diff --git a/src/pjs/expr.hpp b/src/pjs/expr.hpp index 838be217..89a85eca 100644 --- a/src/pjs/expr.hpp +++ b/src/pjs/expr.hpp @@ -1310,7 +1310,8 @@ class Assignment : public Expr { public: Assignment(Expr *l, Expr *r) : m_l(l), m_r(r) {} - auto value() const -> Expr* { return m_r.get(); } + auto lvalue() const -> Expr* { return m_l.get(); } + auto rvalue() const -> Expr* { return m_r.get(); } virtual bool is_argument() const override; virtual void to_arguments(std::vector> &args, std::vector> &vars) const override; diff --git a/src/pjs/parser.cpp b/src/pjs/parser.cpp index d03c67fe..bcce6164 100644 --- a/src/pjs/parser.cpp +++ b/src/pjs/parser.cpp @@ -1301,17 +1301,16 @@ Stmt* ScriptParser::statement() { } case Token::ID("var"): { read(l); - if (peek_eol(MissingIdentifier)) return nullptr; - auto name = read_identifier(MissingIdentifier); - if (!name) return nullptr; - if (read(Token::ID("="))) { - auto e = expression(); - if (!e) return nullptr; - read(Token::ID(";")); - return locate(var(name.release(), e), l); - } + auto e = expression(false); + if (!e) return nullptr; read_semicolons(); - return locate(var(name.release()), l); + std::vector> list; + if (auto comp = e->as()) { + comp->break_down(list); + } else { + list.emplace_back(e); + } + return locate(var(std::move(list)), l); } case Token::ID("function"): { read(l); diff --git a/src/pjs/stmt.cpp b/src/pjs/stmt.cpp index b9d0641d..f9675b38 100644 --- a/src/pjs/stmt.cpp +++ b/src/pjs/stmt.cpp @@ -160,55 +160,112 @@ bool Evaluate::declare_export(Module *module, bool is_default, Error &error) { // bool Var::declare(Module *module, Scope &scope, Error &error, bool is_lval) { - auto name = m_identifier->name(); + std::vector> names; + for (const auto &e : m_list) { + if (auto id = e->as()) { + id->unpack(names); + continue; + } + if (auto assign = e->as()) { + auto l = assign->lvalue(); + if ( + l->is() || + l->is() || + l->is() + ) { + if (l->is_left_value()) { + l->unpack(names); + m_assignments.push_back(assign); + continue; + } + } + } + error.message = "illegal variable declaration"; + error.tree = e.get(); + return false; + } + auto s = scope.parent(); while (!s->is_root()) s = s->parent(); - if (is_fiber(name->str())) { - if (!check_reserved(name->str(), error)) return false; - s->declare_fiber_var(name, module); - } else { - s->declare_var(name); + for (const auto &name : names) { + if (is_fiber(name->str())) { + if (!check_reserved(name->str(), error)) return false; + s->declare_fiber_var(name, module); + } else { + s->declare_var(name); + } } - if (m_expr && !m_expr->declare(module, scope, error)) return false; + + for (auto e : m_assignments) { + if (!e->declare(module, scope, error, false)) { + return false; + } + } + return true; } void Var::resolve(Module *module, Context &ctx, int l, Tree::LegacyImports *imports) { - if (m_expr) { - m_identifier->resolve(module, ctx, l, imports); - m_expr->resolve(module, ctx, l, imports); + for (auto e : m_assignments) { + e->resolve(module, ctx, l, imports); } } void Var::execute(Context &ctx, Result &result) { - if (m_expr) { + for (auto e : m_assignments) { Value val; - if (m_expr->eval(ctx, val) && m_identifier->assign(ctx, val)) { - result.set_done(); - } + if (!e->eval(ctx, val)) return; } + result.set_done(); } bool Var::declare_export(Module *module, bool is_default, Error &error) { - auto name = m_identifier->name(); - if (!check_reserved(name->str(), error)) return false; - if (is_fiber(name->str())) { - error.tree = this; - error.message = "cannot export a fiber variable"; + std::vector> names; + for (const auto &e : m_list) { + if (auto id = e->as()) { + id->unpack(names); + continue; + } + if (auto assign = e->as()) { + auto l = assign->lvalue(); + if (l->is()) { + l->unpack(names); + m_assignments.push_back(assign); + continue; + } + } + error.message = "illegal export"; + error.tree = e.get(); return false; } - if (is_default) { - module->add_export(s_default, name); - } else { - module->add_export(name, name); + + for (const auto &name : names) { + if (!check_reserved(name->str(), error)) return false; + if (is_fiber(name->str())) { + error.tree = this; + error.message = "cannot export a fiber variable"; + return false; + } + if (is_default) { + module->add_export(s_default, name); + } else { + module->add_export(name, name); + } + } + + for (auto e : m_assignments) { + if (!e->declare(module, module->scope(), error, false)) { + return false; + } } - if (m_expr && !m_expr->declare(module, module->scope(), error)) return false; + return true; } void Var::dump(std::ostream &out, const std::string &indent) { - out << indent << "var " << m_identifier->name()->str() << std::endl; - if (m_expr) m_expr->dump(out, indent + " "); + out << indent << "var" << std::endl; + auto indent2 = indent + " "; + for (const auto &e : m_list) e->dump(out, indent2); } bool Var::check_reserved(const std::string &name, Error &error) { diff --git a/src/pjs/stmt.hpp b/src/pjs/stmt.hpp index 3805127a..1a688690 100644 --- a/src/pjs/stmt.hpp +++ b/src/pjs/stmt.hpp @@ -153,8 +153,8 @@ class Evaluate : public Exportable { class Var : public Exportable { public: - Var(expr::Identifier *name, Expr *expr) - : m_identifier(name), m_expr(expr) {} + Var(std::vector> &&list) + : m_list(std::move(list)) {} virtual bool declare(Module *module, Scope &scope, Error &error, bool is_lval) override; virtual void resolve(Module *module, Context &ctx, int l, Tree::LegacyImports *imports) override; @@ -163,9 +163,8 @@ class Var : public Exportable { virtual void dump(std::ostream &out, const std::string &indent) override; private: - std::unique_ptr m_identifier; - std::unique_ptr m_resolved; - std::unique_ptr m_expr; + std::vector> m_list; + std::vector m_assignments; bool check_reserved(const std::string &name, Error &error); @@ -356,7 +355,7 @@ inline Stmt* block() { return new stmt::Block(); } inline Stmt* block(std::list> &&stmts) { return new stmt::Block(std::move(stmts)); } inline Stmt* label(const std::string &name, Stmt *stmt) { return new stmt::Label(name, stmt); } inline Stmt* evaluate(Expr *expr) { return new stmt::Evaluate(expr); } -inline Stmt* var(expr::Identifier *name, Expr *expr = nullptr) { return new stmt::Var(name, expr); } +inline Stmt* var(std::vector> &&list) { return new stmt::Var(std::move(list)); } inline Stmt* function(expr::Identifier *name, Expr *expr) { return new stmt::Function(name, expr); } inline Stmt* if_else(Expr *cond, Stmt *then_clause, Stmt *else_clause = nullptr) { return new stmt::If(cond, then_clause, else_clause); } inline Stmt* switch_case(Expr *cond, std::list, std::unique_ptr>> &&cases) { return new stmt::Switch(cond, std::move(cases)); } diff --git a/src/pjs/tree.cpp b/src/pjs/tree.cpp index fd2e380a..5c101a6b 100644 --- a/src/pjs/tree.cpp +++ b/src/pjs/tree.cpp @@ -61,7 +61,7 @@ void Tree::Scope::declare_arg(Expr *expr) { InitArg init; init.index = index; if (auto assign = dynamic_cast(expr)) { - init.default_value = assign->value(); + init.default_value = assign->rvalue(); } if (m_args[index] == Str::empty) { init.unpack = expr; diff --git a/src/pjs/tree.hpp b/src/pjs/tree.hpp index cad5f034..1e3c4bb2 100644 --- a/src/pjs/tree.hpp +++ b/src/pjs/tree.hpp @@ -157,6 +157,13 @@ class Tree { void init_variables(); }; + // + // Tree types + // + + template bool is() { return as() ? true : false; } + template auto as() -> T* { return dynamic_cast(this); } + // // Tree location in script //