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