Skip to content

Commit

Permalink
[pjs] Add support of for-loop statements
Browse files Browse the repository at this point in the history
  • Loading branch information
pajama-coder committed Nov 20, 2024
1 parent 21254bd commit ed156cd
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 4 deletions.
3 changes: 3 additions & 0 deletions src/pjs/expr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,9 @@ class Compound : public Expr {
out = std::move(m_exprs);
}

auto expression_count() const -> size_t { return m_exprs.size(); }
auto expression(size_t i) const -> Expr* { return m_exprs[i].get(); }

virtual bool is_argument_list() const override;
virtual bool is_comma_ended() const override { return m_is_comma_ended; }
virtual bool eval(Context &ctx, Value &result) override;
Expand Down
29 changes: 29 additions & 0 deletions src/pjs/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1368,8 +1368,37 @@ Stmt* ScriptParser::statement() {
}
return locate(switch_case(cond.release(), std::move(cases)), l);
}
case Token::ID("for"): {
read(l);
bool is_var = false;
std::unique_ptr<Expr> init, cond, step;
if (!read(Token::ID("("), TokenExpected)) return nullptr;
if (read(Token::ID("var"))) is_var = true;
if (!read(Token::ID(";"))) {
auto e = expression();
if (!e) return nullptr;
init.reset(e);
}
if (!read(Token::ID(";"), TokenExpected)) return nullptr;
if (!read(Token::ID(";"))) {
auto e = expression();
if (!e) return nullptr;
cond.reset(e);
}
if (!read(Token::ID(";"), TokenExpected)) return nullptr;
if (!read(Token::ID(")"))) {
auto e = expression();
if (!e) return nullptr;
step.reset(e);
}
if (!read(Token::ID(")"), TokenExpected)) return nullptr;
auto s = statement();
if (!s) return nullptr;
return for_loop(is_var, init.release(), cond.release(), step.release(), s);
}
case Token::ID("break"): {
read(l);
if (peek_eol() || peek_end()) return locate(flow_break(), l);
if (auto name = read_identifier()) {
read_semicolons();
return locate(flow_break(name.release()), l);
Expand Down
103 changes: 102 additions & 1 deletion src/pjs/stmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,107 @@ void Switch::dump(std::ostream &out, const std::string &indent) {
}
}

//
// For
//

bool For::declare(Module *module, Scope &scope, Error &error, bool is_lval) {
if (m_is_var && m_init) {
std::vector<Ref<Str>> names;
if (auto assign = m_init->as<expr::Assignment>()) {
auto lvalue = assign->lvalue();
if (auto id = lvalue->as<expr::Identifier>()) {
names.emplace_back(id->name());
} else {
error.tree = lvalue;
error.message = "illegal left-value in assignment";
return false;
}
} else if (auto comp = m_init->as<expr::Compound>()) {
for (size_t i = 0, n = comp->expression_count(); i < n; i++) {
auto expr = comp->expression(i);
if (auto assign = expr->as<expr::Assignment>()) {
auto lvalue = assign->lvalue();
if (auto id = lvalue->as<expr::Identifier>()) {
names.emplace_back(id->name());
} else {
error.tree = lvalue;
error.message = "illegal left-value in assignment";
return false;
}
}
}
}
auto s = scope.parent();
while (!s->is_root()) s = s->parent();
for (const auto &name : names) {
if (Var::is_fiber(name->str())) {
if (!check_reserved(name->str(), error)) return false;
s->declare_fiber_var(name, module);
} else {
s->declare_var(name);
}
}
}

Tree::Scope s(Tree::Scope::LOOP, &scope);
if (m_init && !m_init->declare(module, s, error, false)) return false;
if (m_cond && !m_cond->declare(module, s, error, false)) return false;
if (m_step && !m_step->declare(module, s, error, false)) return false;
if (m_body && !m_body->declare(module, s, error, false)) return false;

return true;
}

void For::resolve(Module *module, Context &ctx, int l, Tree::LegacyImports *imports) {
if (m_init) m_init->resolve(module, ctx, l, imports);
if (m_cond) m_cond->resolve(module, ctx, l, imports);
if (m_step) m_step->resolve(module, ctx, l, imports);
if (m_body) m_body->resolve(module, ctx, l, imports);
}

void For::execute(Context &ctx, Result &result) {
Value val;
if (m_init && !m_init->eval(ctx, val)) return;
for (;;) {
if (m_cond) {
if (!m_cond->eval(ctx, val)) return;
if (!val.to_boolean()) break;
}
if (m_body) {
m_body->execute(ctx, result);
if (!ctx.ok()) return;
if (result.is_break()) {
if (result.label) return;
break;
}
if (result.is_continue()) {
if (m_step && !m_step->eval(ctx, val)) return;
continue;
}
if (!result.is_done()) return;
}
if (m_step && !m_step->eval(ctx, val)) return;
}
result.set_done();
}

bool For::check_reserved(const std::string &name, Error &error) {
if (!Var::is_reserved(name)) return true;
error.tree = this;
error.message = "reserved variable name '" + name + "'";
return false;
}

void For::dump(std::ostream &out, const std::string &indent) {
auto indent2 = indent + " ";
out << indent << "for" << std::endl;
out << indent << " init" << std::endl; if (m_init) m_init->dump(out, indent2);
out << indent << " cond" << std::endl; if (m_cond) m_cond->dump(out, indent2);
out << indent << " step" << std::endl; if (m_step) m_step->dump(out, indent2);
out << indent << " body" << std::endl; if (m_body) m_body->dump(out, indent2);
}

//
// Break
//
Expand All @@ -463,7 +564,7 @@ bool Break::declare(Module *module, Scope &scope, Error &error, bool is_lval) {
s = s->parent();
}
} else {
while (s && s->kind() != Tree::Scope::SWITCH) {
while (s && s->kind() != Tree::Scope::SWITCH && s->kind() != Tree::Scope::LOOP) {
s = s->parent();
}
}
Expand Down
31 changes: 28 additions & 3 deletions src/pjs/stmt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,9 @@ class Var : public Exportable {
Var(std::vector<std::unique_ptr<Expr>> &&list)
: m_list(std::move(list)) {}

static bool is_fiber(const std::string &name);
static bool is_reserved(const std::string &name);

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;
virtual void execute(Context &ctx, Result &result) override;
Expand All @@ -167,9 +170,6 @@ class Var : public Exportable {
std::vector<expr::Assignment*> m_assignments;

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

static bool is_fiber(const std::string &name);
static bool is_reserved(const std::string &name);
};

//
Expand Down Expand Up @@ -232,6 +232,30 @@ class Switch : public Stmt {
std::list<std::pair<std::unique_ptr<Expr>, std::unique_ptr<Stmt>>> m_cases;
};

//
// For
//

class For : public Stmt {
public:
For(bool is_var, Expr *init, Expr *cond, Expr *step, Stmt *body)
: m_is_var(is_var), m_init(init), m_cond(cond), m_step(step), m_body(body) {}

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;
virtual void execute(Context &ctx, Result &result) override;
virtual void dump(std::ostream &out, const std::string &indent) override;

private:
bool m_is_var;
std::unique_ptr<Expr> m_init;
std::unique_ptr<Expr> m_cond;
std::unique_ptr<Expr> m_step;
std::unique_ptr<Stmt> m_body;

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

//
// Break
//
Expand Down Expand Up @@ -359,6 +383,7 @@ inline Stmt* var(std::vector<std::unique_ptr<Expr>> &&list) { return new stmt::V
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)); }
inline Stmt* for_loop(bool is_var, Expr *init, Expr *cond, Expr *step, Stmt *body) { return new stmt::For(is_var, init, cond, step, body); }
inline Stmt* try_catch(Stmt *try_clause, Stmt *catch_clause, Stmt *finally_clause, Expr *exception_variable) { return new stmt::Try(try_clause, catch_clause, finally_clause, exception_variable); }
inline Stmt* flow_break() { return new stmt::Break(); }
inline Stmt* flow_break(expr::Identifier *label) { return new stmt::Break(label); }
Expand Down

0 comments on commit ed156cd

Please sign in to comment.