hare

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

commit 8293b7f79076b9524ce1415a7d1fba653c9b5334
parent a353678c4ba764ee6185458a29439ae330f8cb30
Author: Alexey Yerin <yyp@disroot.org>
Date:   Mon, 19 Apr 2021 20:18:49 -0400

hare/parse,unparse: add match expressions

Co-authored-by: Eyal Sawady <ecs@d2evs.net>

Diffstat:
Mhare/parse/+test/expr.ha | 20++++++++++++++++++++
Mhare/parse/expr.ha | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mhare/unparse/expr.ha | 46+++++++++++++++++++++++++++++++++++++++++++++-
3 files changed, 135 insertions(+), 2 deletions(-)

diff --git a/hare/parse/+test/expr.ha b/hare/parse/+test/expr.ha @@ -210,6 +210,26 @@ "); }; +@test fn match_expr() void = { + roundtrip("export fn main() void = { + match (x) { + i: size => y, + _: str => { + z; + }, + null => void, + }; + match (x) { + s: matchdata => y, + _: str => { + z; + }, + * => q, + }; +}; +"); +}; + @test fn unarithm() void = { roundtrip("export fn main() void = +void;\n" "export fn main() void = -void;\n" diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha @@ -36,7 +36,7 @@ export fn expression(lexer: *lex::lexer) (ast::expr | error) = { _: void => binarithm(lexer, void, 0)?, tok: lex::token => switch (tok.0) { ltok::LBRACE => expression_list(lexer)?, - ltok::MATCH => abort(), // TODO + ltok::MATCH => match_expr(lexer)?, ltok::SWITCH => switch_expr(lexer)?, ltok::IF => if_expr(lexer)?, ltok::LABEL, ltok::FOR => for_expr(lexer)?, @@ -815,6 +815,75 @@ fn switch_expr(lexer: *lex::lexer) (ast::expr | error) = { }; }; +fn match_case(lexer: *lex::lexer) (ast::match_case | error) = { + let tok = want(lexer, ltok::NAME, ltok::UNDERSCORE, ltok::NULL)?; + let nt = switch (tok.0) { + ltok::NAME => { + let name = tok.1 as str; + want(lexer, ltok::COLON)?; + (name, _type(lexer)?); + }, + ltok::UNDERSCORE => { + want(lexer, ltok::COLON)?; + ("", _type(lexer)?); + }, + ltok::NULL => ("", ast::_type { + loc = tok.2, + flags = 0, + _type = ast::builtin_type::NULL, + }), + }; + want(lexer, ltok::CASE)?; + let expr = expression(lexer)?; + + return ast::match_case { + name = nt.0, + _type = alloc(nt.1), + value = alloc(expr), + }; +}; + +fn match_expr(lexer: *lex::lexer) (ast::expr | error) = { + want(lexer, ltok::MATCH)?; + want(lexer, ltok::LPAREN)?; + const value = expression(lexer)?; + want(lexer, ltok::RPAREN)?; + want(lexer, ltok::LBRACE)?; + + let cases: []ast::match_case = []; + let default: nullable *ast::expr = null; + for (true) { + match (try(lexer, ltok::TIMES)?) { + _: void => append(cases, match_case(lexer)?), + t: lex::token => { + want(lexer, ltok::CASE)?; + if (default != null) { + return syntaxerr(t.2, "More than one default match case"); + }; + default = alloc(expression(lexer)?); + }, + }; + + match (try(lexer, ltok::COMMA)?) { + _: void => { + want(lexer, ltok::RBRACE)?; + break; + }, + _: lex::token => void, + }; + match (try(lexer, ltok::RBRACE)?) { + _: void => void, + _: lex::token => break, + }; + }; + + return ast::match_expr { + value = alloc(value), + cases = cases, + default = default, + }; +}; + fn unarithm(lexer: *lex::lexer) (ast::expr | error) = { const tok = match (try(lexer, ltok::PLUS, ltok::MINUS, ltok::BNOT, diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha @@ -244,7 +244,7 @@ export fn expr( z += fmt::fprintf(out, "}}")?; z; }, - e: ast::match_expr => abort(), + e: ast::match_expr => match_expr(out, indent, e)?, e: ast::len_expr => { let z = fmt::fprint(out, "len(")?; z += expr(out, indent, *e)?; @@ -444,3 +444,47 @@ fn switch_expr( z += fmt::fprint(out, "}")?; return z; }; + +fn match_expr( + out: *io::stream, + indent: size, + e: ast::match_expr, +) (size | io::error) = { + let z = fmt::fprint(out, "match (")?; + z += expr(out, indent, *e.value)?; + z += fmt::fprint(out, ") {")?; + indent += 1; + + for (let i = 0z; i < len(e.cases); i += 1) { + z += newline(out, indent)?; + const case = e.cases[i]; + const is_null = match (case._type._type) { + b: ast::builtin_type => b == ast::builtin_type::NULL, + * => false, + }; + if (len(case.name) > 0) { + z += fmt::fprintf(out, "{}: ", case.name)?; + } else if (!is_null) { + z += fmt::fprintf(out, "_: ")?; + }; + z += _type(out, indent, *case._type)?; + z += fmt::fprint(out, " => ")?; + z += expr(out, indent, *case.value)?; + z += fmt::fprint(out, ",")?; + }; + + match (e.default) { + e: *ast::expr => { + z += newline(out, indent)?; + z += fmt::fprint(out, "* => ")?; + z += expr(out, indent, *e)?; + z += fmt::fprint(out, ",")?; + }, + null => void, + }; + + indent -= 1; + z += newline(out, indent)?; + z += fmt::fprint(out, "}")?; + return z; +};