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:
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) {