hare

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

commit 69c15619b04c2c992110ac93e5ae370ba12c1be9
parent 87a1dec5bd77d010c79a56a51c76464bed124781
Author: Drew DeVault <sir@cmpwn.com>
Date:   Mon, 19 Apr 2021 11:05:11 -0400

hare::parse: implement struct literals

Diffstat:
Mhare/parse/+test/expr.ha | 30++++++++++++++++++++++++++++++
Mhare/parse/expr.ha | 108++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Mhare/unparse/expr.ha | 40+++++++++++++++++++++++++++++++++++++++-
3 files changed, 173 insertions(+), 5 deletions(-)

diff --git a/hare/parse/+test/expr.ha b/hare/parse/+test/expr.ha @@ -82,6 +82,36 @@ [1, 2, 3, 4]; [1, 2, 3, 4...]; (1, 2, 3); + struct { + x: int = 10, + y: int = 20, + }; + coords { + x: int = 10, + y: int = 20, + }; + coords { + x = 10, + y = 20, + }; + struct { + x: int = 10, + struct { + y: int = 20, + }, + }; + struct { + x: int = 10, + coords { + y: int = 20, + }, + }; + struct { + x: int = 10, + namespace::coords { + y: int = 20, + }, + }; }; "); }; diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha @@ -1,7 +1,7 @@ use hare::ast; use hare::lex::{ltok}; use hare::lex; -use fmt; +use strings; // Parses an expression. export fn expression(lexer: *lex::lexer) (ast::expr | error) = { @@ -539,7 +539,7 @@ fn plain_expression(lexer: *lex::lexer) (ast::expr | error) = { ltok::TRUE, ltok::FALSE, ltok::NULL, ltok::VOID => constant(lexer), ltok::LBRACKET => plain_array(lexer)?, - ltok::STRUCT => abort(), // TODO: Struct literal + ltok::STRUCT => plain_struct(lexer, [])?, ltok::LPAREN => { want(lexer, ltok::LPAREN)?; let ex = expression(lexer)?; @@ -551,9 +551,9 @@ fn plain_expression(lexer: *lex::lexer) (ast::expr | error) = { }, ltok::NAME => { let id = ident(lexer)?; - return match (try(lexer, ltok::LBRACE)?) { + return match (peek(lexer, ltok::LBRACE)?) { void => id: ast::access_identifier, - lex::token => abort(), // TODO: Struct literal + lex::token => plain_struct(lexer, id)?, }; }, * => syntaxerr(mkloc(lexer), @@ -598,6 +598,106 @@ fn plain_array(lexer: *lex::lexer) (ast::expr | error) = { }; }; +fn plain_struct(lexer: *lex::lexer, alias: ast::ident) (ast::expr | error) = { + if (len(alias) == 0) { + want(lexer, ltok::STRUCT)?; + }; + want(lexer, ltok::LBRACE)?; + + let autofill = false; + let fields: [](ast::struct_value | *ast::struct_constant) = []; + for (true) { + const tok = want(lexer, ltok::ELLIPSIS, + ltok::NAME, ltok::STRUCT)?; + switch (tok.0) { + ltok::ELLIPSIS => { + synassert(mkloc(lexer), len(alias) != 0, + "Cannot use auto-fill with anonymous struct"); + autofill = true; + try(lexer, ltok::COMMA)?; + want(lexer, ltok::RBRACE)?; + break; + }, + ltok::NAME, ltok::STRUCT => { + lex::unlex(lexer, tok); + append(fields, struct_field(lexer)?); + }, + }; + + switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) { + ltok::COMMA => { + if (try(lexer, ltok::RBRACE) is lex::token) { + break; + }; + }, + ltok::RBRACE => break, + }; + }; + + return ast::struct_constant { + autofill = autofill, + alias = alias, + fields = fields, + }; +}; + +fn struct_field( + lexer: *lex::lexer, +) (ast::struct_value | *ast::struct_constant | error) = { + const tok = want(lexer, ltok::NAME, ltok::STRUCT)?; + switch (tok.0) { + ltok::NAME => { + const name = strings::dup(tok.1 as str); + const tok = match (try(lexer, ltok::COLON, + ltok::DOUBLE_COLON, ltok::EQUAL)?) { + tok: lex::token => tok, + void => { + let id: ast::ident = alloc([name]); + const expr = plain_struct(lexer, id)?; + const expr = expr as ast::constant_expr; + const expr = expr as ast::struct_constant; + return alloc(expr); + }, + }; + return switch (tok.0) { + ltok::COLON => { + const _type = alloc(_type(lexer)?); + want(lexer, ltok::EQUAL)?; + const init = alloc(expression(lexer)?); + ast::struct_value { + name = name, + _type = _type, + init = init, + }; + }, + ltok::DOUBLE_COLON => { + let id: ast::ident = alloc([name]); + let rest = ident(lexer)?; + append(id, rest...); + const expr = plain_struct(lexer, id)?; + const expr = expr as ast::constant_expr; + const expr = expr as ast::struct_constant; + return alloc(expr); + }, + ltok::EQUAL => ast::struct_value { + name = name, + _type = null, + init = alloc(expression(lexer)?), + }, + * => abort(), // Invariant + }; + }, + ltok::STRUCT => { + lex::unlex(lexer, tok); + const expr = plain_struct(lexer, [])?; + const expr = expr as ast::constant_expr; + const expr = expr as ast::struct_constant; + return alloc(expr); + }, + * => abort(), // Invariant + }; +}; + fn plain_tuple(lexer: *lex::lexer, ex: ast::expr) (ast::expr | error) = { let values: []*ast::expr = []; append(values, alloc(ex)); diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha @@ -329,7 +329,7 @@ fn constant( z + fmt::fprintf(out, "{}]", if (ac.expand) "..." else "")?; }, - ast::struct_constant => abort(), // TODO + sc: ast::struct_constant => struct_constant(out, indent, sc)?, tu: ast::tuple_constant => { let z = fmt::fprint(out, "(")?; for (let i = 0z; i < len(tu); i += 1) { @@ -343,6 +343,44 @@ fn constant( }; }; +fn struct_constant( + out: *io::stream, + indent: size, + sc: ast::struct_constant, +) (size | io::error) = { + let z = 0z; + z += if (len(sc.alias) != 0) { + ident(out, sc.alias)?; + } else { + fmt::fprint(out, "struct")?; + }; + z += fmt::fprint(out, " {")?; + indent += 1; + for (let i = 0z; i < len(sc.fields); i += 1) { + newline(out, indent)?; + match (sc.fields[i]) { + sv: ast::struct_value => { + z += match (sv._type) { + null => fmt::fprintf(out, "{}", sv.name)?, + t: *ast::_type => + fmt::fprintf(out, "{}: ", sv.name)? + + _type(out, indent, *t)?, + }; + z += fmt::fprint(out, " = ")?; + z += expr(out, indent, *sv.init)?; + }, + sc: *ast::struct_constant => { + z += constant(out, indent, *sc)?; + }, + }; + z += fmt::fprint(out, ",")?; + }; + indent -= 1; + newline(out, indent)?; + fmt::fprint(out, "}"); + return z; +}; + fn for_expr( out: *io::stream, indent: size,