hare

The Hare programming language
git clone https://git.torresjrjr.com/hare.git
Log | Files | Refs | README | LICENSE

commit 8fd2614695b0d216e0e773b9e6bbd61b3026c4d8
parent 4b064c0a701a7139b6e883327d959ce2d5b38fd1
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 18 Apr 2021 08:10:27 -0400

hare::parse: implement for loops

Diffstat:
Mhare/parse/+test/expr.ha | 21+++++++++++++++++++++
Mhare/parse/expr.ha | 50+++++++++++++++++++++++++++++++++++++++++++++++---
Mhare/unparse/expr.ha | 27++++++++++++++++++++++++++-
3 files changed, 94 insertions(+), 4 deletions(-)

diff --git a/hare/parse/+test/expr.ha b/hare/parse/+test/expr.ha @@ -54,6 +54,27 @@ "); }; +@test fn for_expr() void = { + roundtrip("export fn main() void = { + for (true) { + x; + }; + :label for (true) { + x; + }; + for (let x = 0; x < 10) { + x; + }; + for (x < 10; x) { + x; + }; + for (let x = 10; x < 10; x) { + x; + }; +}; +"); +}; + @test fn if_expr() void = { roundtrip("export fn main() void = { if (x == y) { diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha @@ -6,8 +6,8 @@ use fmt; // Parses an expression. export fn expression(lexer: *lex::lexer) (ast::expr | error) = { const tok = match (peek(lexer, ltok::LBRACE, ltok::MATCH, ltok::SWITCH, - ltok::IF, ltok::FOR, ltok::BREAK, ltok::CONTINUE, - ltok::RETURN, ltok::LET, ltok::CONST)?) { + ltok::IF, ltok::LABEL, ltok::FOR, ltok::BREAK, + ltok::CONTINUE, ltok::RETURN, ltok::LET, ltok::CONST)?) { void => return binarithm(lexer, void, 0), tok: lex::token => tok, }; @@ -16,7 +16,7 @@ export fn expression(lexer: *lex::lexer) (ast::expr | error) = { ltok::MATCH => abort(), // TODO ltok::SWITCH => abort(), // TODO ltok::IF => if_expr(lexer), - ltok::FOR => abort(), // TODO + ltok::LABEL, ltok::FOR => for_expr(lexer), ltok::BREAK, ltok::CONTINUE, ltok::RETURN => control(lexer), ltok::LET, ltok::CONST => binding(lexer, false), * => abort(), // Invariant @@ -237,6 +237,50 @@ fn expression_list(lexer: *lex::lexer) (ast::expr | error) = { return items; }; +fn for_expr(lexer: *lex::lexer) (ast::expr | error) = { + let tok = want(lexer, ltok::FOR, ltok::LABEL)?; + let label: ast::label = switch (tok.0) { + ltok::LABEL => { + want(lexer, ltok::FOR)?; + tok.1 as str; + }, + * => "", + }; + + want(lexer, ltok::LPAREN)?; + + let bindings: nullable *ast::expr = match (peek( + lexer, ltok::LET, ltok::CONST)?) { + void => null, + lex::token => { + let bindings = alloc(binding(lexer, false)?); + want(lexer, ltok::SEMICOLON)?; + bindings; + }, + }; + + let cond = alloc(expression(lexer)?); + + let afterthought: nullable *ast::expr = match (peek( + lexer, ltok::SEMICOLON)) { + void => null, + lex::token => { + want(lexer, ltok::SEMICOLON)?; + alloc(expression(lexer)?); + }, + }; + + want(lexer, ltok::RPAREN)?; + + return ast::for_expr { + label = label, + bindings = bindings, + cond = cond, + afterthought = afterthought, + body = alloc(expression(lexer)?), + }; +}; + fn if_expr(lexer: *lex::lexer) (ast::expr | error) = { want(lexer, ltok::IF)?; want(lexer, ltok::LPAREN)?; diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha @@ -123,7 +123,7 @@ export fn expr( }, e: ast::defer_expr => abort(), e: ast::delete_expr => abort(), - e: ast::for_expr => abort(), + e: ast::for_expr => for_expr(out, indent, e)?, e: ast::free_expr => abort(), e: ast::if_expr => { let z = fmt::fprint(out, "if (")?; @@ -240,3 +240,28 @@ fn constant( }, }; }; + +fn for_expr( + out: *io::stream, + indent: size, + e: ast::for_expr, +) (size | io::error) = { + let z = 0z; + if (e.label != "") { + z += fmt::fprintf(out, ":{} ", e.label)?; + }; + z += fmt::fprintf(out, "for (")?; + z += match (e.bindings) { + null => 0z, + e: *ast::expr => expr(out, indent, *e)? + + fmt::fprint(out, "; ")?, + }; + z += expr(out, indent, *e.cond)?; + z += match (e.afterthought) { + null => 0z, + e: *ast::expr => fmt::fprint(out, "; ")? + + expr(out, indent, *e)?, + }; + z += fmt::fprintf(out, ") ")?; + return z + expr(out, indent, *e.body)?; +};