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:
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;
+};