hare

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

commit c5b1b81064efd567b4adcb442e29b7426f2cd173
parent 6da2a3fec8f5732e85df7e9050c3090c903a1c08
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 17 Apr 2021 11:12:38 -0400

hare::parse: implement bindings

Diffstat:
Mhare/ast/expr.ha | 26++++++++++++++++----------
Mhare/parse/+test/expr.ha | 10++++++++++
Mhare/parse/expr.ha | 53++++++++++++++++++++++++++++++++++++++++++++++++-----
Mhare/unparse/expr.ha | 23++++++++++++++++++++++-
4 files changed, 96 insertions(+), 16 deletions(-)

diff --git a/hare/ast/expr.ha b/hare/ast/expr.ha @@ -83,14 +83,20 @@ export type binarithm_expr = struct { rvalue: *expr, }; -// let foo: int = bar -export type binding_expr = struct { +// foo: int = bar +export type binding = struct { name: str, _type: nullable *_type, - is_static: bool, init: *expr, }; +// let foo: int = bar, ... +export type binding_expr = struct { + is_static: bool, + is_const: bool, + bindings: []binding, +}; + // break :label export type break_expr = label; @@ -243,7 +249,7 @@ export type unarithm_expr = struct { // An expression export type expr = (access_expr | alloc_expr | append_expr | assert_expr | - assign_expr | binarithm_expr | []binding_expr | break_expr | call_expr | + assign_expr | binarithm_expr | binding_expr | break_expr | call_expr | cast_expr | constant_expr | continue_expr | defer_expr | delete_expr | for_expr | free_expr | if_expr | list_expr | match_expr | len_expr | size_expr | offset_expr | propagate_expr | return_expr | slice_expr | @@ -300,13 +306,13 @@ export fn expr_free(e: (expr | nullable *expr)) void = match (e) { expr_free(b.lvalue); expr_free(b.rvalue); }, - b: []binding_expr => { - for (let i = 0z; i < len(b); i += 1) { - free(b[i].name); - type_free(b[i]._type); - expr_free(b[i].init); + b: binding_expr => { + for (let i = 0z; i < len(b.bindings); i += 1) { + free(b.bindings[i].name); + type_free(b.bindings[i]._type); + expr_free(b.bindings[i].init); }; - free(b); + free(b.bindings); }, b: break_expr => free(b), c: call_expr => { diff --git a/hare/parse/+test/expr.ha b/hare/parse/+test/expr.ha @@ -2,6 +2,16 @@ roundtrip("export fn main() void = void + void * void / void;\n"); }; +@test fn binding() void = { + roundtrip("export fn main() void = { + let x: int = 1337, y = 7331; + const z: int = 42, q: int = 24; + static let p: int = 62893, o = 39826; + static const w: int = 62893, t = 39826; +}; +"); +}; + @test fn call() void = { roundtrip("export fn main() void = test();\n" "export fn main() void = test(void, void, void);\n" diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha @@ -18,7 +18,7 @@ export fn expression(lexer: *lex::lexer) (ast::expr | error) = { ltok::IF => if_expr(lexer), ltok::FOR => abort(), // TODO ltok::BREAK, ltok::CONTINUE, ltok::RETURN => control(lexer), - ltok::LET, ltok::CONST => binding(lexer), + ltok::LET, ltok::CONST => binding(lexer, false), * => abort(), // Invariant }; }; @@ -61,8 +61,37 @@ fn binarithm( return lvalue; }; -fn binding(lexer: *lex::lexer) (ast::expr | error) = { - abort(); // TODO +fn binding(lexer: *lex::lexer, is_static: bool) (ast::expr | error) = { + const is_const = switch (want(lexer, ltok::LET, ltok::CONST)?.0) { + ltok::LET => false, + ltok::CONST => true, + }; + + let bindings: []ast::binding = []; + for (true) { + const name = want(lexer, ltok::NAME)?.1 as str; + const btype: nullable *ast::_type = + if (try(lexer, ltok::COLON)? is lex::token) { + alloc(_type(lexer)?); + } else null; + want(lexer, ltok::EQUAL)?; + const init = alloc(expression(lexer)?); + append(bindings, ast::binding { + name = name, + _type = btype, + init = init, + }); + match (try(lexer, ltok::COMMA)?) { + void => break, + lex::token => void, + }; + }; + + return ast::binding_expr { + is_static = is_static, + is_const = is_const, + bindings = bindings, + }; }; fn builtin(lexer: *lex::lexer) (ast::expr | error) = { @@ -78,7 +107,21 @@ fn builtin(lexer: *lex::lexer) (ast::expr | error) = { ltok::DELETE => abort(), ltok::FREE => abort(), ltok::ASSERT => abort(), - ltok::STATIC => abort(), + ltok::STATIC => { + want(lexer, ltok::STATIC)?; + let tok = match (peek(lexer, ltok::LET, ltok::CONST, + ltok::ASSERT)?) { + tok: lex::token => tok, + // TODO: The following is lame + void => return syntaxerr(tok.2, + "Expected let, const, or assert"), + }; + switch (tok.0) { + ltok::LET, ltok::CONST => binding(lexer, true), + ltok::ASSERT => abort(), // TODO + * => abort(), + }; + }, ltok::SIZE, ltok::LEN, ltok::OFFSET => abort(), ltok::DEFER => abort(), * => abort(), // Invariant @@ -389,7 +432,7 @@ fn unarithm(lexer: *lex::lexer) (ast::expr | error) = { const tok = match (try(lexer, ltok::PLUS, ltok::MINUS, ltok::BNOT, ltok::LNOT, ltok::TIMES, ltok::BAND)) { - void => return postfix(lexer, void), + void => return builtin(lexer), tok: lex::token => tok.0, }; const op = switch (tok) { diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha @@ -58,7 +58,28 @@ export fn expr( z += expr(out, indent, *e.rvalue)?; z; }, - e: []ast::binding_expr => abort(), + e: ast::binding_expr => { + let z = fmt::fprintf(out, "{}{}", + if (e.is_static) "static " else "", + if (e.is_const) "const " else "let ")?; + for (let i = 0z; i < len(e.bindings); i += 1) { + let binding = e.bindings[i]; + z += match (binding._type) { + null => fmt::fprint(out, binding.name)?, + t: *ast::_type => { + fmt::fprintf(out, "{}: ", + binding.name)? + + _type(out, indent, *t)?; + }, + }; + z += fmt::fprint(out, " = ")?; + z += expr(out, indent, *binding.init)?; + if (i + 1 < len(e.bindings)) { + z += fmt::fprint(out, ", ")?; + }; + }; + z; + }, e: ast::break_expr => { let z = fmt::fprint(out, "break")?; // TODO: https://todo.sr.ht/~sircmpwn/hare/380