Only allow one attribute per function, and represent lack of symbol as void,
not as an empty string.
Breaking-Change: hare::ast::, hare::unit::
Signed-off-by: Bor Grošelj Simić <bgs@turminal.net>
---
This implements the spec change that I've just sent.
cmd/haredoc/doc/sort.ha | 2 +-
cmd/haredoc/main.ha | 24 ++++++++++++++++--------
hare/ast/decl.ha | 14 +++++++++-----
hare/parse/decl.ha | 26 ++++++++++++--------------
hare/unit/process.ha | 4 ++--
hare/unit/scan.ha | 2 +-
hare/unit/unit.ha | 4 ++--
hare/unparse/decl.ha | 38 ++++++++++++++++++++++++++------------
8 files changed, 69 insertions(+), 45 deletions(-)
diff --git a/cmd/haredoc/doc/sort.ha b/cmd/haredoc/doc/sort.ha
index 66febc47..a64c6f79 100644
--- a/cmd/haredoc/doc/sort.ha
+++ b/cmd/haredoc/doc/sort.ha
@@ -33,7 +33,7 @@ export fn sort_decls(decls: []ast::decl) summary = {
ident = f.ident,
prototype = f.prototype,
body = null,
- attrs = f.attrs,
+ attr = f.attr,
},
docs = decl.docs,
})!;
diff --git a/cmd/haredoc/main.ha b/cmd/haredoc/main.ha
index f301d697..da1cdbb1 100644
--- a/cmd/haredoc/main.ha
+++ b/cmd/haredoc/main.ha
@@ -397,22 +397,30 @@ fn has_decl(decl: ast::decl, name: str) bool = {
if (len(d.ident) == 1 && d.ident[0] == name) {
return true;
};
- let tok = strings::rtokenize(d.symbol, ".");
- match (strings::next_token(&tok)) {
- case done => void;
+ match (d.symbol) {
+ case void => void;
case let s: str =>
- return s == name;
+ let tok = strings::rtokenize(s, ".");
+ match (strings::next_token(&tok)) {
+ case done => void;
+ case let s: str =>
+ return s == name;
+ };
};
case let globals: []ast::decl_global =>
for (let d .. globals) {
if (len(d.ident) == 1 && d.ident[0] == name) {
return true;
};
- let tok = strings::rtokenize(d.symbol, ".");
- match (strings::next_token(&tok)) {
- case done => void;
+ match (d.symbol) {
+ case void => void;
case let s: str =>
- return s == name;
+ let tok = strings::rtokenize(s, ".");
+ match (strings::next_token(&tok)) {
+ case done => void;
+ case let s: str =>
+ return s == name;
+ };
};
};
case let types: []ast::decl_type =>
diff --git a/hare/ast/decl.ha b/hare/ast/decl.ha
index ba9c2fd9..85daed8a 100644
--- a/hare/ast/decl.ha
+++ b/hare/ast/decl.ha
@@ -19,7 +19,7 @@ export type decl_const = struct {
export type decl_global = struct {
is_const: bool,
is_threadlocal: bool,
- symbol: str,
+ symbol: (str | void),
ident: ident,
_type: nullable *_type,
init: nullable *expr,
@@ -45,11 +45,11 @@ export type fndecl_attr = enum {
//
// fn main() void = void;
export type decl_func = struct {
- symbol: str,
+ symbol: (str | void),
ident: ident,
prototype: *_type,
body: nullable *expr,
- attrs: fndecl_attr,
+ attr: fndecl_attr,
};
// A Hare declaration.
@@ -70,7 +70,9 @@ export fn decl_finish(d: decl) void = {
match (d.decl) {
case let g: []decl_global =>
for (let i = 0z; i < len(g); i += 1) {
- free(g[i].symbol);
+ if (g[i].symbol is str) {
+ free(g[i].symbol: str);
+ };
ident_free(g[i].ident);
type_finish(g[i]._type);
free(g[i]._type);
@@ -86,7 +88,9 @@ export fn decl_finish(d: decl) void = {
};
free(t);
case let f: decl_func =>
- free(f.symbol);
+ if (f.symbol is str) {
+ free(f.symbol: str);
+ };
ident_free(f.ident);
type_finish(f.prototype);
free(f.prototype);
diff --git a/hare/parse/decl.ha b/hare/parse/decl.ha
index 85cd106d..2f4d76f3 100644
--- a/hare/parse/decl.ha
+++ b/hare/parse/decl.ha
@@ -67,12 +67,12 @@ fn decl_global(
const (symbol, threadlocal) = match (try(lexer,
ltok::ATTR_SYMBOL, ltok::ATTR_THREADLOCAL)?) {
case void =>
- yield ("", false);
+ yield (void: (void | str), false);
case let t: lex::token =>
yield if (t.0 == ltok::ATTR_SYMBOL) {
- yield (attr_symbol(lexer)?, false);
+ yield (attr_symbol(lexer)?: (void | str), false);
} else {
- yield ("", true);
+ yield (void: (void | str), true);
};
};
const ident = ident(lexer)?;
@@ -130,24 +130,22 @@ fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = {
ltok::ATTR_FINI, ltok::ATTR_INIT, ltok::ATTR_TEST,
ltok::ATTR_SYMBOL
];
- for (true) match (try(lexer, attrs...)?) {
+ let (sym, attr) = match (try(lexer, attrs...)?) {
case void =>
- break;
+ yield (void: (str | void), ast::fndecl_attr::NONE);
case let t: lex::token =>
- synassert(t.2, t.0 == ltok::ATTR_SYMBOL || attr == 0,
- "Only one of @init, @fini, or @test may be provided")?;
- switch (t.0) {
+ yield switch (t.0) {
case ltok::ATTR_FINI =>
- attr = ast::fndecl_attr::FINI;
+ yield (void, ast::fndecl_attr::FINI);
case ltok::ATTR_INIT =>
- attr = ast::fndecl_attr::INIT;
+ yield (void, ast::fndecl_attr::INIT);
case ltok::ATTR_TEST =>
- attr = ast::fndecl_attr::TEST;
+ yield (void, ast::fndecl_attr::TEST);
case ltok::ATTR_SYMBOL =>
- sym = attr_symbol(lexer)?;
+ yield (attr_symbol(lexer)?, ast::fndecl_attr::NONE);
case =>
abort("unreachable");
- };
+ }: ((void | str), ast::fndecl_attr);
};
want(lexer, ltok::FN)?;
@@ -182,7 +180,7 @@ fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = {
repr = prototype,
})!,
body = body,
- attrs = attr,
+ attr = attr,
};
};
diff --git a/hare/unit/process.ha b/hare/unit/process.ha
index da77dc44..b18ed4d8 100644
--- a/hare/unit/process.ha
+++ b/hare/unit/process.ha
@@ -52,7 +52,7 @@ fn process_func(
adecl: *ast::decl,
func: *ast::decl_func,
) (decl | error) = {
- assert(func.attrs & ast::fndecl_attr::TEST == 0); // TODO
+ assert(func.attr != ast::fndecl_attr::TEST); // TODO
const afndecl = adecl.decl as ast::decl_func;
const prototype = types::lookup(ctx.store, func.prototype)!;
const fntype = prototype.repr as types::func;
@@ -78,7 +78,7 @@ fn process_func(
prototype = prototype,
body = body,
// TODO: We should make these enums inherited
- attrs = afndecl.attrs: ast::fndecl_attr,
+ attr = afndecl.attr,
},
};
};
diff --git a/hare/unit/scan.ha b/hare/unit/scan.ha
index a40a7119..ca607b0a 100644
--- a/hare/unit/scan.ha
+++ b/hare/unit/scan.ha
@@ -50,7 +50,7 @@ fn scan_func(
decl: *ast::decl,
func: *ast::decl_func,
) (void | types::deferred | error) = {
- assert(func.attrs & ast::fndecl_attr::TEST == 0); // TODO
+ assert(func.attr != ast::fndecl_attr::TEST); // TODO
const fntype = match (types::lookup(ctx.store, func.prototype)) {
case let err: types::error =>
return err;
diff --git a/hare/unit/unit.ha b/hare/unit/unit.ha
index 8e47ca87..e1572618 100644
--- a/hare/unit/unit.ha
+++ b/hare/unit/unit.ha
@@ -8,11 +8,11 @@ use hare::types;
// A function declaration.
export type decl_func = struct {
- symbol: str,
+ symbol: (str | void),
ident: ast::ident,
prototype: const *types::_type,
body: nullable *expr,
- attrs: ast::fndecl_attr,
+ attr: ast::fndecl_attr,
};
// A declaration within a unit.
diff --git a/hare/unparse/decl.ha b/hare/unparse/decl.ha
index 0e7b6e87..1f25efa5 100644
--- a/hare/unparse/decl.ha
+++ b/hare/unparse/decl.ha
@@ -58,12 +58,15 @@ export fn decl(
synkind::KEYWORD)?;
n += space(&ctx)?;
for (let i = 0z; i < len(g); i += 1) {
- if (len(g[i].symbol) != 0) {
+ match (g[i].symbol) {
+ case let s: str =>
n += syn(&ctx, "@symbol(", synkind::ATTRIBUTE)?;
- n += literal(&ctx, syn, g[i].symbol)?;
+ n += literal(&ctx, syn, s)?;
n += syn(&ctx, ")", synkind::ATTRIBUTE)?;
n += space(&ctx)?;
- } else if (g[i].is_threadlocal) {
+ case => void;
+ };
+ if (g[i].is_threadlocal) {
n += syn(&ctx, "@threadlocal",
synkind::ATTRIBUTE)?;
n += space(&ctx)?;
@@ -119,7 +122,7 @@ export fn decl(
ctx.stack = stack.up;
};
- switch (f.attrs) {
+ switch (f.attr) {
case ast::fndecl_attr::NONE => void;
case ast::fndecl_attr::FINI =>
n += syn(&ctx, "@fini", synkind::ATTRIBUTE)?;
@@ -132,11 +135,13 @@ export fn decl(
n += space(&ctx)?;
};
let p = f.prototype.repr as ast::func_type;
- if (len(f.symbol) != 0) {
+ match (f.symbol) {
+ case let s: str =>
n += syn(&ctx, "@symbol(", synkind::ATTRIBUTE)?;
- n += literal(&ctx, syn, f.symbol)?;
+ n += literal(&ctx, syn, s)?;
n += syn(&ctx, ")", synkind::ATTRIBUTE)?;
n += space(&ctx)?;
+ case => void;
};
n += syn(&ctx, "fn", synkind::KEYWORD)?;
n += space(&ctx)?;
@@ -234,7 +239,7 @@ fn decl_test(d: *ast::decl, expected: str) bool = {
ast::decl_global {
is_const = false,
is_threadlocal = false,
- symbol = "",
+ symbol = void,
ident = ["foo", "bar"],
_type = &type_int,
init = &expr_void,
@@ -242,7 +247,7 @@ fn decl_test(d: *ast::decl, expected: str) bool = {
ast::decl_global {
is_const = false,
is_threadlocal = true,
- symbol = "",
+ symbol = void,
ident = ["boo"],
_type = &type_int,
init = &expr_void,
@@ -288,9 +293,18 @@ fn decl_test(d: *ast::decl, expected: str) bool = {
ident = ["foo"],
prototype = &type_fn,
body = null,
- attrs = ast::fndecl_attr::FINI,
+ attr = ast::fndecl_attr::NONE,
+ };
+ assert(decl_test(&d, "@symbol(\"foo\") fn foo(foo: int, bar: int...) int;"));
+
+ d.decl = ast::decl_func {
+ symbol = void,
+ ident = ["foo"],
+ prototype = &type_fn,
+ body = null,
+ attr = ast::fndecl_attr::FINI,
};
- assert(decl_test(&d, "@fini @symbol(\"foo\") fn foo(foo: int, bar: int...) int;"));
+ assert(decl_test(&d, "@fini fn foo(foo: int, bar: int...) int;"));
type_fn.repr = ast::func_type {
result = &type_int,
@@ -312,11 +326,11 @@ fn decl_test(d: *ast::decl, expected: str) bool = {
],
};
d.decl = ast::decl_func {
- symbol = "",
+ symbol = void,
ident = ["foo"],
prototype = &type_fn,
body = &expr_void,
- attrs = 0,
+ attr = ast::fndecl_attr::NONE,
};
assert(decl_test(&d, "fn foo(int = 4) int = void;"));
};
--
2.47.1