~sircmpwn/hare-dev

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH hare] hare::parse: parse attributes better

Details
Message ID
<20250117042420.14885-1-bgs@turminal.net>
Sender timestamp
1737091406
DKIM signature
pass
Download raw message
Patch: +69 -45
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
Details
Message ID
<D78QGWZO1E9X.R5VZK91QMOT1@d2evs.net>
In-Reply-To
<20250117042420.14885-1-bgs@turminal.net> (view parent)
Sender timestamp
1737562413
DKIM signature
pass
Download raw message
On Fri Jan 17, 2025 at 4:23 AM UTC, Bor Grošelj Simić wrote:
> diff --git a/hare/ast/decl.ha b/hare/ast/decl.ha
> @@ -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);

i'd prefer `as` rather than `:` here; in the future, we can use if (let) 
instead

> @@ -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);

ditto

other than that, this lgtm
Reply to thread Export thread (mbox)