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:
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,