Skip to content

Commit

Permalink
[pjs] Support multiple declarations and destructing assignments in a …
Browse files Browse the repository at this point in the history
…single var statement
  • Loading branch information
pajama-coder committed Nov 16, 2024
1 parent 4c08d2b commit 21254bd
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 44 deletions.
3 changes: 2 additions & 1 deletion src/pjs/expr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ref<Str>> &args, std::vector<Ref<Str>> &vars) const override;
Expand Down
19 changes: 9 additions & 10 deletions src/pjs/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::unique_ptr<Expr>> list;
if (auto comp = e->as<expr::Compound>()) {
comp->break_down(list);
} else {
list.emplace_back(e);
}
return locate(var(std::move(list)), l);
}
case Token::ID("function"): {
read(l);
Expand Down
109 changes: 83 additions & 26 deletions src/pjs/stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<Ref<Str>> names;
for (const auto &e : m_list) {
if (auto id = e->as<expr::Identifier>()) {
id->unpack(names);
continue;
}
if (auto assign = e->as<expr::Assignment>()) {
auto l = assign->lvalue();
if (
l->is<expr::Identifier>() ||
l->is<expr::ObjectLiteral>() ||
l->is<expr::ArrayLiteral>()
) {
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<Ref<Str>> names;
for (const auto &e : m_list) {
if (auto id = e->as<expr::Identifier>()) {
id->unpack(names);
continue;
}
if (auto assign = e->as<expr::Assignment>()) {
auto l = assign->lvalue();
if (l->is<expr::Identifier>()) {
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) {
Expand Down
11 changes: 5 additions & 6 deletions src/pjs/stmt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::unique_ptr<Expr>> &&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;
Expand All @@ -163,9 +163,8 @@ class Var : public Exportable {
virtual void dump(std::ostream &out, const std::string &indent) override;

private:
std::unique_ptr<expr::Identifier> m_identifier;
std::unique_ptr<Expr> m_resolved;
std::unique_ptr<Expr> m_expr;
std::vector<std::unique_ptr<Expr>> m_list;
std::vector<expr::Assignment*> m_assignments;

bool check_reserved(const std::string &name, Error &error);

Expand Down Expand Up @@ -356,7 +355,7 @@ inline Stmt* block() { return new stmt::Block(); }
inline Stmt* block(std::list<std::unique_ptr<Stmt>> &&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<std::unique_ptr<Expr>> &&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::pair<std::unique_ptr<Expr>, std::unique_ptr<Stmt>>> &&cases) { return new stmt::Switch(cond, std::move(cases)); }
Expand Down
2 changes: 1 addition & 1 deletion src/pjs/tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ void Tree::Scope::declare_arg(Expr *expr) {
InitArg init;
init.index = index;
if (auto assign = dynamic_cast<expr::Assignment*>(expr)) {
init.default_value = assign->value();
init.default_value = assign->rvalue();
}
if (m_args[index] == Str::empty) {
init.unpack = expr;
Expand Down
7 changes: 7 additions & 0 deletions src/pjs/tree.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,13 @@ class Tree {
void init_variables();
};

//
// Tree types
//

template<class T> bool is() { return as<T>() ? true : false; }
template<class T> auto as() -> T* { return dynamic_cast<T*>(this); }

//
// Tree location in script
//
Expand Down

0 comments on commit 21254bd

Please sign in to comment.