hare

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

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:
Mhare/ast/expr.ha | 3++-
Mhare/parse/+test.ha | 16++++++++++++++++
Mhare/parse/expr.ha | 126+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mhare/unparse/expr.ha | 60+++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
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(), + }; };