commit 5f490faead1af543eca2a29277454cbc63aba9d5
parent 12a6b4f88bced73878d1c68af59f0bc2c69ff5f2
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 5 Apr 2021 09:33:49 -0400
hare::parse: implement binary arithmetic exprs
Diffstat:
4 files changed, 195 insertions(+), 10 deletions(-)
diff --git a/hare/ast/expr.ha b/hare/ast/expr.ha
@@ -52,8 +52,9 @@ export type assign_expr = struct {
indirect: bool,
};
-// TODO: Rehome this with the checked AST?
+// A binary arithmetic operator
export type binarithm_op = enum {
+ // TODO: Rehome this with the checked AST?
BAND, // &
BOR, // |
DIV, // /
diff --git a/hare/parse/+test.ha b/hare/parse/+test.ha
@@ -190,3 +190,19 @@ use strio;
defer free(s);
assert(s == in);
};
+
+@test fn binarithm() void = {
+ const in = "export fn main() void = void + void * void / void;\n";
+ let buf = bufio::fixed(strings::toutf8(in), mode::READ);
+ let lexer = lex::init(buf, "<test>");
+ let u = ast::subunit {
+ imports = [],
+ decls = decls(&lexer) as []ast::decl,
+ };
+ defer ast::subunit_free(u);
+ let out = strio::dynamic();
+ unparse::subunit(out, u) as size;
+ let s = strio::finish(out);
+ defer free(s);
+ assert(s == in);
+};
diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha
@@ -2,11 +2,126 @@ use hare::ast;
use hare::lex;
use hare::lex::{btoken};
+fn cast(lexer: *lex::lexer) (ast::expr | error) = {
+ want_btoken(lexer, btoken::VOID)?; // TODO
+ return void;
+};
+
+fn binarithm(
+ lexer: *lex::lexer,
+ lvalue: (ast::expr | void),
+ i: int,
+) (ast::expr | error) = {
+ // Precedence climbing parser
+ // https://en.wikipedia.org/wiki/Operator-precedence_parser
+ let lvalue = match (lvalue) {
+ void => cast(lexer)?,
+ expr: ast::expr => expr,
+ };
+
+ let tok = match (lex::lex(lexer)?) {
+ io::EOF => return syntaxerr(mkloc(lexer),
+ "Unexpected EOF, was expecting simple expression"),
+ t: (lex::token, lex::location) => t,
+ };
+
+ let j = precedence(tok);
+ for (j >= i; j = precedence(tok)) {
+ const op = binop_for_tok(tok);
+
+ let rvalue = cast(lexer)?;
+ tok = match (lex::lex(lexer)?) {
+ io::EOF => return syntaxerr(mkloc(lexer),
+ "Unexpected EOF, expecting simple expression"),
+ t: (lex::token, lex::location) => t,
+ };
+
+ let k = precedence(tok);
+ for (k > j; k = precedence(tok)) {
+ lex::unlex(lexer, tok);
+ rvalue = binarithm(lexer, rvalue, k)?;
+ tok = match (lex::lex(lexer)?) {
+ io::EOF => return syntaxerr(mkloc(lexer),
+ "Unexpected EOF, expecting simple expression"),
+ t: (lex::token, lex::location) => t,
+ };
+ };
+
+ let expr = ast::binarithm_expr {
+ op = op,
+ lvalue = alloc(lvalue),
+ rvalue = alloc(rvalue),
+ };
+ lvalue = expr;
+ };
+
+ lex::unlex(lexer, tok);
+ return lvalue;
+};
+
+fn binop_for_tok(tok: (lex::token, lex::location)) ast::binarithm_op = {
+ let tok = match (tok.0) {
+ b: btoken => b,
+ * => abort(),
+ };
+ return switch (tok) {
+ btoken::BAND => ast::binarithm_op::BAND,
+ btoken::BOR => ast::binarithm_op::BOR,
+ btoken::BXOR => ast::binarithm_op::BXOR,
+ btoken::DIV => ast::binarithm_op::DIV,
+ btoken::GREATER => ast::binarithm_op::GT,
+ btoken::GREATEREQ => ast::binarithm_op::GTEQ,
+ btoken::LAND => ast::binarithm_op::LAND,
+ btoken::LEQUAL => ast::binarithm_op::LEQUAL,
+ btoken::LESS => ast::binarithm_op::LESS,
+ btoken::LESSEQ => ast::binarithm_op::LESSEQ,
+ btoken::LOR => ast::binarithm_op::LOR,
+ btoken::LSHIFT => ast::binarithm_op::LSHIFT,
+ btoken::LXOR => ast::binarithm_op::LXOR,
+ btoken::MINUS => ast::binarithm_op::MINUS,
+ btoken::MODULO => ast::binarithm_op::MODULO,
+ btoken::NEQUAL => ast::binarithm_op::NEQUAL,
+ btoken::PLUS => ast::binarithm_op::PLUS,
+ btoken::RSHIFT => ast::binarithm_op::RSHIFT,
+ btoken::TIMES => ast::binarithm_op::TIMES,
+ * => abort(),
+ };
+};
+
+fn precedence(tok: (lex::token, lex::location)) int = {
+ let tok = match (tok.0) {
+ b: btoken => b,
+ * => return -1,
+ };
+ return switch (tok) {
+ btoken::LOR => 0,
+ btoken::LXOR => 1,
+ btoken::LAND => 2,
+ btoken::LEQUAL,
+ btoken::NEQUAL => 3,
+ btoken::LESS,
+ btoken::LESSEQ,
+ btoken::GREATER,
+ btoken::GREATEREQ => 4,
+ btoken::BOR => 5,
+ btoken::BXOR => 6,
+ btoken::BAND => 7,
+ btoken::LSHIFT,
+ btoken::RSHIFT => 8,
+ btoken::PLUS,
+ btoken::MINUS => 9,
+ btoken::TIMES,
+ btoken::DIV,
+ btoken::MODULO => 10,
+ * => -1,
+ };
+};
+
// Parses a compound-expression
export fn compound_expression(lexer: *lex::lexer) (ast::expr | error) = {
let tok = match (lex::lex(lexer)?) {
io::EOF => return syntaxerr(mkloc(lexer),
- "Expected EOF, expected compound expression"),
+ "Unexpected EOF, expected compound expression"),
t: (lex::token, lex::location) => t,
};
@@ -25,14 +140,13 @@ export fn compound_expression(lexer: *lex::lexer) (ast::expr | error) = {
};
// Parses a simple-expression
-export fn simple_expression(lexer: *lex::lexer) (ast::expr | error) = {
- want_btoken(lexer, btoken::VOID)?; // TODO
- return void;
-};
+export fn simple_expression(lexer: *lex::lexer) (ast::expr | error) =
+ binarithm(lexer, void, 0);
// Parses a complex-expression
export fn complex_expression(lexer: *lex::lexer) (ast::expr | error) = {
- return simple_expression(lexer); // TODO
+ // TODO: if, for, switch, match
+ return simple_expression(lexer);
};
fn expression_list(lexer: *lex::lexer) (ast::expr | error) = {
diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha
@@ -8,7 +8,61 @@ export fn expr(
indent: size,
t: ast::expr
) (size | io::error) = {
- let e = t as ast::constant_expr;
- assert(e is void);
- return fmt::fprint(out, "void")?;
+ return match (t) {
+ e: ast::access_expr => abort(),
+ e: ast::alloc_expr => abort(),
+ e: ast::append_expr => abort(),
+ e: ast::assert_expr => abort(),
+ e: ast::assign_expr => abort(),
+ e: ast::binarithm_expr => {
+ let z = expr(out, indent, *e.lvalue)?;
+ z += fmt::fprintf(out, " {} ", switch (e.op) {
+ ast::binarithm_op::BAND => "&",
+ ast::binarithm_op::BOR => "|",
+ ast::binarithm_op::DIV => "/",
+ ast::binarithm_op::GT => ">",
+ ast::binarithm_op::GTEQ => ">=",
+ ast::binarithm_op::LAND => "&&",
+ ast::binarithm_op::LEQUAL => "==",
+ ast::binarithm_op::LESS => "<",
+ ast::binarithm_op::LESSEQ => "<=",
+ ast::binarithm_op::LOR => "||",
+ ast::binarithm_op::LSHIFT => "<<",
+ ast::binarithm_op::LXOR => "^^",
+ ast::binarithm_op::MINUS => "-",
+ ast::binarithm_op::MODULO => "%",
+ ast::binarithm_op::NEQUAL => "!=",
+ ast::binarithm_op::PLUS => "+",
+ ast::binarithm_op::RSHIFT => ">>",
+ ast::binarithm_op::TIMES => "*",
+ ast::binarithm_op::BXOR => "^",
+ })?;
+ z += expr(out, indent, *e.rvalue)?;
+ z;
+ },
+ e: []ast::binding_expr => abort(),
+ e: ast::break_expr => abort(),
+ e: ast::call_expr => abort(),
+ e: ast::cast_expr => abort(),
+ e: ast::constant_expr => {
+ assert(e is void);
+ fmt::fprint(out, "void")?;
+ },
+ e: ast::continue_expr => abort(),
+ e: ast::defer_expr => abort(),
+ e: ast::delete_expr => abort(),
+ e: ast::for_expr => abort(),
+ e: ast::free_expr => abort(),
+ e: ast::if_expr => abort(),
+ e: ast::list_expr => abort(),
+ e: ast::match_expr => abort(),
+ e: ast::len_expr => abort(),
+ e: ast::size_expr => abort(),
+ e: ast::offset_expr => abort(),
+ e: ast::propagate_expr => abort(),
+ e: ast::return_expr => abort(),
+ e: ast::slice_expr => abort(),
+ e: ast::switch_expr => abort(),
+ e: ast::unarithm_expr => abort(),
+ };
};