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:
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();