hare

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

commit a96bd0449c2beb92590c7eea17046498e43853d2
parent 3d06f9d20ff097d7c3a273c7059c8ca6ed4fe6db
Author: Sebastian <sebastian@sebsite.pw>
Date:   Mon,  6 Nov 2023 23:29:29 -0500

hare::*: remove unused import forms

The following forms are removed:

	use foo = bar::{baz}; // alias + members
	use foo::{bar = baz}; // aliased members

This also fixes a memory leak in hare::parse when parsing the alias
form: the identifier slice wasn't freed before being overwritten.

References: https://lists.sr.ht/~sircmpwn/hare-rfc/<CW9K5L4ALVKU.1Y541JKBOIPRU%40notmylaptop>#<CWKB92S81IWF.TY7GT6M2EVYC@notmylaptop>
Signed-off-by: Sebastian <sebastian@sebsite.pw>

Diffstat:
Mhare/ast/import.ha | 54++++++++++++++++++++++++++++--------------------------
Mhare/parse/+test/unit_test.ha | 101++++++++++++++++++++++++++++++++-----------------------------------------------
Mhare/parse/import.ha | 79+++++++++++++++++++++++++++++++++----------------------------------------------
Mhare/unparse/import.ha | 62++++++++++++++++++--------------------------------------------
4 files changed, 120 insertions(+), 176 deletions(-)

diff --git a/hare/ast/import.ha b/hare/ast/import.ha @@ -2,44 +2,46 @@ // (c) Hare authors <https://harelang.org> use hare::lex; - -// Variants of the import statement -export type import_mode = enum uint { - // use module; - IDENT = 0, - // use alias = module; - ALIAS = 1 << 0, - // use module::{foo, bar, baz}; - MEMBERS = 1 << 1, - // use module::*; - WILDCARD = 1 << 2, -}; +use strings; // An imported module. +// +// use foo; +// use foo = bar; +// use foo::{bar, baz}; +// use foo::*; export type import = struct { start: lex::location, end: lex::location, - mode: import_mode, ident: ident, - alias: str, - objects: []((str | void), str), + bindings: (void | import_alias | import_members | import_wildcard), }; +// An import alias. +// +// use foo = bar; +export type import_alias = str; + +// An import members list. +// +// use foo::{bar, baz}; +export type import_members = []str; + +// An import wildcard. +// +// use foo::*; +export type import_wildcard = void; + // Frees resources associated with an [[import]]. export fn import_finish(import: import) void = { ident_free(import.ident); - if (import.mode == import_mode::ALIAS) { - free(import.alias); - }; - for (let i = 0z; i < len(import.objects); i += 1) { - free(import.objects[i].1); - match (import.objects[i].0) { - case void => void; - case let s: str => - free(s); - }; + match (import.bindings) { + case let alias: import_alias => + free(alias); + case let objects: import_members => + strings::freeall(objects); + case => void; }; - free(import.objects); }; // Frees resources associated with each [[import]] in a slice, and then diff --git a/hare/parse/+test/unit_test.ha b/hare/parse/+test/unit_test.ha @@ -10,40 +10,47 @@ use strings; use types; fn import_eq(i1: ast::import, i2: ast::import) bool = { - if (i1.mode != i2.mode) { - return false; - }; if (!ast::ident_eq(i1.ident, i2.ident)) { return false; }; - let mode = i1.mode; - if (mode & ast::import_mode::ALIAS != 0 && i1.alias != i2.alias) { - return false; - }; - if (mode & ast::import_mode::MEMBERS != 0) { - for (let i = 0z; i < len(i1.objects); i += 1) { - let o1 = i1.objects[i], o2 = i2.objects[i]; - if (o1.0 is void ^^ o2.0 is void) { - return false; - }; - if (o1.0 is str && o1.0: str != o2.0: str) { - return false; - }; - if (o1.1 != o2.1) { + + let (o1, o2) = match (i1.bindings) { + case void => + return i2.bindings is void; + case let s1: ast::import_alias => + match (i2.bindings) { + case let s2: ast::import_alias => + return s1 == s2; + case => + return false; + }; + case let o1: ast::import_members => + yield match (i2.bindings) { + case let o2: ast::import_members => + if (len(o1) != len(o2)) { return false; }; + yield (o1, o2); + case => + return false; + }; + case ast::import_wildcard => + return i2.bindings is ast::import_wildcard; + }; + for (let i = 0z; i < len(o1); i += 1) { + if (o1[i] != o2[i]) { + return false; }; }; return true; }; -type import_tuple = (ast::import_mode, ast::ident, str, []((str | void), str)); +type import_tuple = (ast::ident, (void | ast::import_alias | + ast::import_members | ast::import_wildcard)); fn tup_to_import(tup: import_tuple) ast::import = ast::import { - mode = tup.0, - ident = tup.1, - alias = tup.2, - objects = tup.3, + ident = tup.0, + bindings = tup.1, ... }; @@ -65,14 +72,6 @@ fn tup_to_import(tup: import_tuple) ast::import = ast::import { "use baz::{bat, qux};\n" "use quux::corge::{grault, garply,};\n" - "use quux::{alias = grault};\n" - "use quux::{alias = grault,};\n" - "use quux::{alias = grault, garply};\n" - "use quux::{alias = grault, alias2 = garply};\n" - - "use alias = quux::corge::{grault, garply,};\n" - "use modalias = quux::{alias = grault, alias2 = garply};\n" - "export fn main() void = void;"; let buf = memio::fixed(strings::toutf8(in)); let sc = bufio::newscanner(&buf, types::SIZE_MAX); @@ -82,29 +81,18 @@ fn tup_to_import(tup: import_tuple) ast::import = ast::import { defer ast::imports_finish(mods); let expected: [_]import_tuple = [ - (ast::import_mode::IDENT, ["foo"], "", []), - (ast::import_mode::IDENT, ["bar"], "", []), - (ast::import_mode::IDENT, ["baz", "bat"], "", []), - (ast::import_mode::ALIAS, ["bar"], "foo", []), - (ast::import_mode::ALIAS, ["bat"], "baz", []), - (ast::import_mode::ALIAS, ["quux", "corge"], "qux", []), - (ast::import_mode::WILDCARD, ["foo"], "", []), - (ast::import_mode::WILDCARD, ["foo", "bar", "quux"], "", []), - (ast::import_mode::MEMBERS, ["foo"], "", [(void, "bar")]), - (ast::import_mode::MEMBERS, ["foo"], "", [(void, "bar")]), - (ast::import_mode::MEMBERS, ["baz"], "", [(void, "bat"), (void, "qux")]), - (ast::import_mode::MEMBERS, - ["quux", "corge"], "", [(void, "grault"), (void, "garply")]), - (ast::import_mode::MEMBERS, ["quux"], "", [("alias", "grault")]), - (ast::import_mode::MEMBERS, ["quux"], "", [("alias", "grault")]), - (ast::import_mode::MEMBERS, - ["quux"], "", [("alias", "grault"), (void, "garply")]), - (ast::import_mode::MEMBERS, - ["quux"], "", [("alias", "grault"), ("alias2", "garply")]), - (ast::import_mode::MEMBERS | ast::import_mode::ALIAS, - ["quux", "corge"], "alias", [(void, "grault"), (void, "garply")]), - (ast::import_mode::MEMBERS | ast::import_mode::ALIAS, - ["quux"], "modalias", [("alias", "grault"), ("alias2", "garply")]), + (["foo"], void), + (["bar"], void), + (["baz", "bat"], void), + (["bar"], "foo"), + (["bat"], "baz"), + (["quux", "corge"], "qux"), + (["foo"], ast::import_wildcard), + (["foo", "bar", "quux"], ast::import_wildcard), + (["foo"], ["bar"]), + (["foo"], ["bar"]), + (["baz"], ["bat", "qux"]), + (["quux", "corge"], ["grault", "garply"]), ]; assert(len(mods) == len(expected)); @@ -114,13 +102,6 @@ fn tup_to_import(tup: import_tuple) ast::import = ast::import { let tok = lex::lex(&lexer) as lex::token; assert(tok.0 == lex::ltok::EXPORT); - - const in = "use a::{b = c = d};\n"; - let buf = memio::fixed(strings::toutf8(in)); - let sc = bufio::newscanner(&buf, types::SIZE_MAX); - defer bufio::finish(&sc); - let lexer = lex::init(&sc, "<test>"); - assert(imports(&lexer) is error); }; @test fn decls() void = { diff --git a/hare/parse/import.ha b/hare/parse/import.ha @@ -4,21 +4,12 @@ use fmt; use hare::ast; use hare::lex; - use hare::lex::{ltok}; -fn name_list(lexer: *lex::lexer) ([]((str | void), str) | error) = { - let names: []((str | void), str) = []; +fn name_list(lexer: *lex::lexer) (ast::import_members | error) = { + let names: []str = []; for (true) { - append(names, (void, want(lexer, ltok::NAME)?.1 as str)); - match (try(lexer, ltok::EQUAL)?) { - case void => void; - case => - let name = &names[len(names) - 1]; - name.0 = name.1; - name.1 = want(lexer, ltok::NAME)?.1 as str; - }; - + append(names, want(lexer, ltok::NAME)?.1 as str); switch (want(lexer, ltok::COMMA, ltok::RBRACE)?.0) { case ltok::COMMA => match (try(lexer, ltok::RBRACE)?) { @@ -43,42 +34,38 @@ export fn imports(lexer: *lex::lexer) ([]ast::import | error) = { case => void; }; - append(imports, ast::import { ... }); + append(imports, ast::import { + bindings = void, + ... + }); let import = &imports[len(imports) - 1]; import.start = lex::mkloc(lexer); - for (true) { - let name = ident_trailing(lexer)?; - import.ident = name.0; - switch (want(lexer, ltok::SEMICOLON, ltok::LBRACE, - ltok::EQUAL, ltok::TIMES)?.0) { - case ltok::SEMICOLON => - synassert(lex::mkloc(lexer), !name.1, - "Unexpected trailing :: in ident")?; - break; - case ltok::LBRACE => - synassert(lex::mkloc(lexer), name.1, - "Expected trailing :: in ident")?; - import.mode |= ast::import_mode::MEMBERS; - import.objects = name_list(lexer)?; - want(lexer, ltok::SEMICOLON)?; - break; - case ltok::EQUAL => - synassert(lex::mkloc(lexer), - len(name.0) == 1 && !name.1, - "Expected name, not ident")?; - import.alias = name.0[0]; - import.mode |= ast::import_mode::ALIAS; - case ltok::TIMES => - synassert(lex::mkloc(lexer), name.1, - "Expected trailing :: in ident")?; - synassert(lex::mkloc(lexer), - import.mode & ast::import_mode::ALIAS == 0, - "Unexpected * after aliased import")?; - import.mode |= ast::import_mode::WILDCARD; - want(lexer, ltok::SEMICOLON)?; - break; - case => abort(); // Unreachable - }; + let (name, trailing) = ident_trailing(lexer)?; + import.ident = name; + switch (want(lexer, ltok::SEMICOLON, ltok::LBRACE, + ltok::EQUAL, ltok::TIMES)?.0) { + case ltok::SEMICOLON => + synassert(lex::mkloc(lexer), !trailing, + "Unexpected trailing :: in ident")?; + case ltok::LBRACE => + synassert(lex::mkloc(lexer), trailing, + "Expected trailing :: in ident")?; + import.bindings = name_list(lexer)?; + want(lexer, ltok::SEMICOLON)?; + case ltok::EQUAL => + synassert(lex::mkloc(lexer), + len(name) == 1 && !trailing, + "Expected name, not ident")?; + import.bindings = name[0]; + free(name); + import.ident = ident(lexer)?; + want(lexer, ltok::SEMICOLON)?; + case ltok::TIMES => + synassert(lex::mkloc(lexer), trailing, + "Expected trailing :: in ident")?; + import.bindings = ast::import_wildcard; + want(lexer, ltok::SEMICOLON)?; + case => abort(); // Unreachable }; import.end = lex::mkloc(lexer); }; diff --git a/hare/unparse/import.ha b/hare/unparse/import.ha @@ -23,36 +23,31 @@ export fn import( }; n += syn(&ctx, "use", synkind::KEYWORD)?; n += space(&ctx)?; - if (import.mode & ast::import_mode::ALIAS != 0) { - n += syn(&ctx, import.alias, synkind::IMPORT_ALIAS)?; + match (import.bindings) { + case void => + n += _ident(&ctx, syn, import.ident, synkind::IDENT)?; + case let alias: ast::import_alias => + n += syn(&ctx, alias, synkind::IMPORT_ALIAS)?; n += space(&ctx)?; n += syn(&ctx, "=", synkind::OPERATOR)?; n += space(&ctx)?; - }; - n += _ident(&ctx, syn, import.ident, synkind::IDENT)?; - if (import.mode & ast::import_mode::MEMBERS != 0) { + n += _ident(&ctx, syn, import.ident, synkind::IDENT)?; + case let objects: ast::import_members => + n += _ident(&ctx, syn, import.ident, synkind::IDENT)?; n += syn(&ctx, "::", synkind::IDENT)?; n += syn(&ctx, "{", synkind::PUNCTUATION)?; - for (let i = 0z; i < len(import.objects); i += 1) { - match (import.objects[i].0) { - case let s: str => - n += syn(&ctx, s, synkind::IMPORT_ALIAS)?; - n += space(&ctx)?; - n += syn(&ctx, "=", synkind::OPERATOR)?; - n += space(&ctx)?; - case void => void; - }; - n += syn(&ctx, import.objects[i].1, - synkind::SECONDARY)?; - if (i + 1 < len(import.objects)) { + for (let i = 0z; i < len(objects); i += 1) { + n += syn(&ctx, objects[i], synkind::SECONDARY)?; + if (i + 1 < len(objects)) { n += syn(&ctx, ",", synkind::PUNCTUATION)?; n += space(&ctx)?; }; }; n += syn(&ctx, "}", synkind::PUNCTUATION)?; - } else if (import.mode & ast::import_mode::WILDCARD != 0) { + case ast::import_wildcard => + n += _ident(&ctx, syn, import.ident, synkind::IDENT)?; n += syn(&ctx, "::", synkind::IDENT)?; - n += syn(&ctx, "*", synkind::IDENT)?; + n += syn(&ctx, "*", synkind::PUNCTUATION)?; }; n += syn(&ctx, ";", synkind::PUNCTUATION)?; return n; @@ -62,45 +57,24 @@ export fn import( let tests: [_](ast::import, str) = [ (ast::import { ident = ["foo", "bar", "baz"], + bindings = void, ... }, "use foo::bar::baz;"), (ast::import { - mode = ast::import_mode::ALIAS, ident = ["foo"], - alias = "bar", + bindings = "bar", ... }, "use bar = foo;"), (ast::import { - mode = ast::import_mode::MEMBERS, ident = ["foo"], - objects = [(void, "bar"), (void, "baz")], + bindings = ["bar", "baz"], ... }, "use foo::{bar, baz};"), (ast::import { - mode = ast::import_mode::WILDCARD, ident = ["foo", "bar"], + bindings = ast::import_wildcard, ... }, "use foo::bar::*;"), - (ast::import { - mode = ast::import_mode::MEMBERS | ast::import_mode::ALIAS, - ident = ["foo"], - alias = "quux", - objects = [(void, "bar"), (void, "baz")], - ... - }, "use quux = foo::{bar, baz};"), - (ast::import { - mode = ast::import_mode::MEMBERS, - ident = ["foo"], - objects = [("alias", "bar"), (void, "baz")], - ... - }, "use foo::{alias = bar, baz};"), - (ast::import { - mode = ast::import_mode::MEMBERS | ast::import_mode::ALIAS, - ident = ["foo"], - alias = "quux", - objects = [("alias1", "bar"), ("alias2", "baz")], - ... - }, "use quux = foo::{alias1 = bar, alias2 = baz};"), ]; for (let i = 0z; i < len(tests); i += 1) { let buf = memio::dynamic();