hare

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

commit d1986c5d017a318d589b0706c3839203a34e6cdf
parent f3364fcb0e43deaa00b39073d2e94ebe65d6168f
Author: Sebastian <sebastian@sebsite.pw>
Date:   Tue, 17 Oct 2023 22:30:18 -0400

hare::*: support top-level static assertions

Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Mcmd/haredoc/doc/html.ha | 1+
Mcmd/haredoc/doc/sort.ha | 2++
Mcmd/haredoc/main.ha | 1+
Mhare/ast/decl.ha | 16+++++++++++++++-
Mhare/parse/+test/unit_test.ha | 4+++-
Mhare/parse/decl.ha | 12++++++++++++
Mhare/unit/process.ha | 2++
Mhare/unit/scan.ha | 2++
Mhare/unparse/decl.ha | 2++
Mhare/unparse/expr.ha | 67++++++++++++++++++++++++++++++++++++++-----------------------------
10 files changed, 78 insertions(+), 31 deletions(-)

diff --git a/cmd/haredoc/doc/html.ha b/cmd/haredoc/doc/html.ha @@ -204,6 +204,7 @@ fn details(ctx: *context, decl: ast::decl) (void | error) = { yield "def"; case []ast::decl_global => yield "let"; + case ast::assert_expr => abort(); })?; unparse::ident(ctx.out, decl_ident(decl))?; // TODO: Add source URL diff --git a/cmd/haredoc/doc/sort.ha b/cmd/haredoc/doc/sort.ha @@ -79,6 +79,7 @@ export fn sort_decls(decls: []ast::decl) summary = { docs = decl.docs, }); }; + case ast::assert_expr => void; }; }; @@ -115,5 +116,6 @@ fn decl_ident(decl: ast::decl) ast::ident = { case let g: []ast::decl_global => assert(len(g) == 1); return g[0].ident; + case ast::assert_expr => abort(); }; }; diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha @@ -363,6 +363,7 @@ fn has_decl(decl: ast::decl, name: str) bool = { return true; }; }; + case ast::assert_expr => void; }; return false; }; diff --git a/hare/ast/decl.ha b/hare/ast/decl.ha @@ -57,7 +57,8 @@ export type decl = struct { exported: bool, start: lex::location, end: lex::location, - decl: ([]decl_const | []decl_global | []decl_type | decl_func), + decl: ([]decl_const | []decl_global | []decl_type | decl_func | + assert_expr), // Only valid if the lexer has comments enabled docs: str, @@ -101,5 +102,18 @@ export fn decl_finish(d: decl) void = { free(c[i].init); }; free(c); + case let e: assert_expr => + match (e.cond) { + case let e: *expr => + expr_finish(e); + free(e); + case null => void; + }; + match (e.message) { + case let e: *expr => + expr_finish(e); + free(e); + case null => void; + }; }; }; diff --git a/hare/parse/+test/unit_test.ha b/hare/parse/+test/unit_test.ha @@ -136,7 +136,9 @@ fn tup_to_import(tup: import_tuple) ast::import = ast::import { "\tfirst: *const void,\n" "\tsecond: (void | rune | str),\n" "\tthird: size...\n" - ") nullable *const void;\n"); + ") nullable *const void;\n\n" + "static abort();\n\n" + "static assert(true);\n"); }; @test fn docs() void = { diff --git a/hare/parse/decl.ha b/hare/parse/decl.ha @@ -191,6 +191,18 @@ fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = { export fn decl(lexer: *lex::lexer) (ast::decl | error) = { const start = lex::mkloc(lexer); let comment = ""; + if (try(lexer, ltok::STATIC)? is lex::token) { + comment = strings::dup(lex::comment(lexer)); + let expr = assert_expr(lexer, true)?; + want(lexer, ltok::SEMICOLON)?; + return ast::decl { + exported = false, + start = start, + end = expr.end, + decl = expr.expr as ast::assert_expr, + docs = comment, + }; + }; let exported = match (try(lexer, ltok::EXPORT)?) { case void => yield false; diff --git a/hare/unit/process.ha b/hare/unit/process.ha @@ -42,6 +42,8 @@ fn process_decl( abort(); // TODO case let fu: ast::decl_func => return process_func(ctx, decl, &fu); + case let ex: ast::assert_expr => + abort(); // TODO }; }; diff --git a/hare/unit/scan.ha b/hare/unit/scan.ha @@ -40,6 +40,8 @@ fn scan_decl( abort(); // TODO case let fu: ast::decl_func => return scan_func(ctx, decl, &fu); + case let ex: ast::assert_expr => + abort(); // TODO }; }; diff --git a/hare/unparse/decl.ha b/hare/unparse/decl.ha @@ -153,6 +153,8 @@ export fn decl( n += space(&ctx)?; n += _expr(&ctx, syn, e)?; }; + case let e: ast::assert_expr => + n += assert_expr(&ctx, syn, e)?; }; n += syn(&ctx, ";", synkind::PUNCTUATION)?; return n; diff --git a/hare/unparse/expr.ha b/hare/unparse/expr.ha @@ -109,34 +109,7 @@ fn _expr(ctx: *context, syn: *synfunc, e: ast::expr) (size | io::error) = { case let e: ast::append_expr => return append_insert_expr(ctx, syn, e, false); case let e: ast::assert_expr => - let z = 0z; - if (e.is_static) { - z += syn(ctx, "static", synkind::KEYWORD)?; - z += space(ctx)?; - }; - // assert without a condition = abort - match (e.cond) { - case let e: *ast::expr => - z += syn(ctx, "assert", synkind::KEYWORD)?; - z += syn(ctx, "(", synkind::PUNCTUATION)?; - z += _expr(ctx, syn, *e)?; - case null => - z += syn(ctx, "abort", synkind::KEYWORD)?; - z += syn(ctx, "(", synkind::PUNCTUATION)?; - }; - match (e.message) { - case let m: *ast::expr => - match (e.cond) { - case null => void; - case *ast::expr => - z += syn(ctx, ",", synkind::PUNCTUATION)?; - z += space(ctx)?; - }; - z += _expr(ctx, syn, *m)?; - case null => void; - }; - z += syn(ctx, ")", synkind::PUNCTUATION)?; - return z; + return assert_expr(ctx, syn, e); case let e: ast::assign_expr => let z = 0z; z += _expr(ctx, syn, *e.object)?; @@ -890,7 +863,8 @@ fn case_exprs( if (e.cond == null) { // abort() expression z += space(ctx)?; - z += stmt(ctx, syn, *exprs[0])?; + z += assert_expr(ctx, syn, e)?; + z += syn(ctx, ";", synkind::PUNCTUATION)?; return z; }; case let e: ast::value => @@ -1006,6 +980,41 @@ fn is_cast(e: *ast::expr) bool = { return is_unary(e) || (e.expr is ast::cast_expr); }; +fn assert_expr( + ctx: *context, + syn: *synfunc, + e: ast::assert_expr, +) (size | io::error) = { + let z = 0z; + if (e.is_static) { + z += syn(ctx, "static", synkind::KEYWORD)?; + z += space(ctx)?; + }; + // assert without a condition = abort + match (e.cond) { + case let e: *ast::expr => + z += syn(ctx, "assert", synkind::KEYWORD)?; + z += syn(ctx, "(", synkind::PUNCTUATION)?; + z += _expr(ctx, syn, *e)?; + case null => + z += syn(ctx, "abort", synkind::KEYWORD)?; + z += syn(ctx, "(", synkind::PUNCTUATION)?; + }; + match (e.message) { + case let m: *ast::expr => + match (e.cond) { + case null => void; + case *ast::expr => + z += syn(ctx, ",", synkind::PUNCTUATION)?; + z += space(ctx)?; + }; + z += _expr(ctx, syn, *m)?; + case null => void; + }; + z += syn(ctx, ")", synkind::PUNCTUATION)?; + return z; +}; + fn append_insert_expr( ctx: *context, syn: *synfunc,