hare

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

commit 88a09d7c961e5425165d4ef4b9bc6e648eaf4acc
parent 57a2a1ca713a8b30de316d24f000cbd49977c21a
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sun, 18 Apr 2021 12:16:41 -0400

hare::parse: implement assignment

This one is a bit of a doozy.

Diffstat:
Mhare/ast/expr.ha | 2+-
Mhare/parse/+test/expr.ha | 21++++++++++++++++++++-
Mhare/parse/expr.ha | 83++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------
Mhare/unparse/expr.ha | 27++++++++++++++++++++++++++-
4 files changed, 115 insertions(+), 18 deletions(-)

diff --git a/hare/ast/expr.ha b/hare/ast/expr.ha @@ -46,7 +46,7 @@ export type assert_expr = struct { // foo = bar export type assign_expr = struct { - op: binarithm_op, + op: (binarithm_op | void), object: *expr, value: *expr, indirect: bool, diff --git a/hare/parse/+test/expr.ha b/hare/parse/+test/expr.ha @@ -1,5 +1,24 @@ +@test fn assignment() void = { + roundtrip("export fn main() void = { + x = y; + *x = *y + 10; + *x = *foo(); + x += 10; + x -= 10; + x *= 10; + x /= 10; + x %= 10; + x &= 10; + x |= 10; + x ^= 10; + x >>= 10; + x <<= 10; +}; +"); +}; + @test fn binarithm() void = { - roundtrip("export fn main() void = void + void * void / void;\n"); + roundtrip("export fn main() void = *void + void * void / void;\n"); }; @test fn binding() void = { diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha @@ -5,21 +5,75 @@ 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::LABEL, ltok::FOR, ltok::BREAK, - ltok::CONTINUE, ltok::RETURN, ltok::LET, ltok::CONST)?) { - void => return binarithm(lexer, void, 0), - tok: lex::token => tok, + const indirect = match (try(lexer, ltok::TIMES)?) { + void => false, + lex::token => true, + }; + + // All assignment-op tokens + const atoks: []ltok = [ + ltok::EQUAL, ltok::ANDEQ, ltok::BXOREQ, ltok::DIVEQ, + ltok::LSHIFTEQ, ltok::MINUSEQ, ltok::MODEQ, ltok::OREQ, + ltok::PLUSEQ, ltok::RSHIFTEQ, ltok::TIMESEQ, + ]; + + const expr: ast::expr = if (indirect) { + const expr = unarithm(lexer)?; + // Disambiguate between + // * unary-expression assignment-op expression + // and + // binary-expression + if (peek(lexer, atoks...)? is lex::token) { + expr; + } else return binarithm(lexer, ast::unarithm_expr { + op = ast::unarithm_op::DEREF, + operand = alloc(expr), + }, 0); + } else match (peek(lexer, ltok::LBRACE, ltok::MATCH, + ltok::SWITCH, ltok::IF, ltok::LABEL, ltok::FOR, + ltok::BREAK, ltok::CONTINUE, ltok::RETURN, ltok::LET, + ltok::CONST)?) { + void => binarithm(lexer, void, 0)?, + tok: lex::token => switch (tok.0) { + ltok::LBRACE => expression_list(lexer)?, + ltok::MATCH => abort(), // TODO + ltok::SWITCH => switch_expr(lexer)?, + ltok::IF => if_expr(lexer)?, + ltok::LABEL, ltok::FOR => for_expr(lexer)?, + ltok::BREAK, + ltok::CONTINUE, + ltok::RETURN => control(lexer)?, + ltok::LET, ltok::CONST => binding(lexer, false)?, + * => abort(), // Invariant + }, }; - return switch (tok.0) { - ltok::LBRACE => expression_list(lexer), - ltok::MATCH => abort(), // TODO - ltok::SWITCH => switch_expr(lexer), - ltok::IF => if_expr(lexer), - ltok::LABEL, ltok::FOR => for_expr(lexer), - ltok::BREAK, ltok::CONTINUE, ltok::RETURN => control(lexer), - ltok::LET, ltok::CONST => binding(lexer, false), - * => abort(), // Invariant + + const tok = match (try(lexer, atoks...)?) { + tok: lex::token => tok, + * => return expr, + }; + + synassert(mkloc(lexer), + expr is ast::access_expr || expr is ast::slice_expr, + "Expected an object-selector or slice for assignment target")?; + + return ast::assign_expr { + op = switch (tok.0) { + ltok::EQUAL => void, + ltok::ANDEQ => ast::binarithm_op::BAND, + ltok::BXOREQ => ast::binarithm_op::BXOR, + ltok::DIVEQ => ast::binarithm_op::DIV, + ltok::LSHIFTEQ => ast::binarithm_op::LSHIFT, + ltok::MINUSEQ => ast::binarithm_op::MINUS, + ltok::MODEQ => ast::binarithm_op::MODULO, + ltok::OREQ => ast::binarithm_op::BOR, + ltok::PLUSEQ => ast::binarithm_op::PLUS, + ltok::RSHIFTEQ => ast::binarithm_op::RSHIFT, + ltok::TIMESEQ => ast::binarithm_op::TIMES, + }, + object = alloc(expr), + value = alloc(expression(lexer)?), + indirect = indirect, }; }; @@ -30,7 +84,6 @@ fn binarithm( ) (ast::expr | error) = { // Precedence climbing parser // https://en.wikipedia.org/wiki/Operator-precedence_parser - // TODO: Incorporate assignment into this? let lvalue = match (lvalue) { void => cast(lexer, void)?, expr: ast::expr => expr, diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha @@ -36,7 +36,32 @@ export fn expr( e: ast::alloc_expr => abort(), e: ast::append_expr => abort(), e: ast::assert_expr => abort(), - e: ast::assign_expr => abort(), + e: ast::assign_expr => { + let z = 0z; + if (e.indirect) { + z += fmt::fprint(out, "*")?; + }; + z += expr(out, indent, *e.object)?; + const op = match (e.op) { + void => "=", + op: ast::binarithm_op => switch (op) { + ast::binarithm_op::BAND => "&=", + ast::binarithm_op::BOR => "|=", + ast::binarithm_op::DIV => "/=", + ast::binarithm_op::LSHIFT => "<<=", + ast::binarithm_op::MINUS => "-=", + ast::binarithm_op::MODULO => "%=", + ast::binarithm_op::PLUS => "+=", + ast::binarithm_op::RSHIFT => ">>=", + ast::binarithm_op::TIMES => "*=", + ast::binarithm_op::BXOR => "^=", + * => abort(), + }, + }; + z += fmt::fprintf(out, " {} ", op)?; + z += expr(out, indent, *e.value)?; + z; + }, e: ast::binarithm_expr => { let z = expr(out, indent, *e.lvalue)?; z += fmt::fprintf(out, " {} ", switch (e.op) {