commit b75132a5ba95f1206a4a9abae8a31ef31358ac4d
parent 10c5e27f2f64f11a50bf994a18428ea8f9ea88a2
Author: Eyal Sawady <ecs@d2evs.net>
Date: Wed, 24 Mar 2021 01:30:02 -0400
hare::parse: implement decls parsing
Diffstat:
11 files changed, 621 insertions(+), 106 deletions(-)
diff --git a/hare/parse/+test.ha b/hare/parse/+test.ha
@@ -2,8 +2,10 @@ use bufio;
use fmt;
use hare::ast;
use hare::lex;
+use hare::unparse;
use io::{mode};
use strings;
+use strio;
@test fn ident() void = {
{
@@ -165,3 +167,26 @@ use strings;
};
};
};
+
+@test fn decls() void = {
+ const in = "export type foo::bar = *int, baz = const void;\n"
+ "type foo = ...bar;\n"
+ "type foo = nullable *fn(x: rune, _: int) void;\n"
+ "export let @symbol(\"_\") foo::bar: int = void, baz: int = void;\n"
+ "def foo::bar: int = void;\n"
+ "@symbol(\".f9$oo\") fn foo(bar: int, baz: int...) void;\n"
+ "@test fn foo(_: int, ...) void;\n"
+ "export fn main() void = void;\n";
+ let buf = bufio::fixed(strings::to_utf8(in), mode::READ);
+ let lexer = lex::init(buf, "<test>");
+ let u = ast::subunit {
+ imports = [],
+ decls = decls(&lexer) as []ast::decl,
+ };
+ defer ast::subunit_free(u);
+ let out = strio::dynamic();
+ unparse::subunit(out, u) as size;
+ let s = strio::finish(out);
+ defer free(s);
+ assert(s == in);
+};
diff --git a/hare/parse/decl.ha b/hare/parse/decl.ha
@@ -0,0 +1,191 @@
+use ascii;
+use hare::lex;
+use hare::lex::{btoken};
+use hare::ast;
+use strings;
+
+fn attr_symbol(lexer: *lex::lexer) (str | error) = {
+ want_btoken(lexer, btoken::LPAREN)?;
+ let s: (str, lex::location) = match (lex::lex(lexer)?) {
+ io::EOF => return syntaxerr(mkloc(lexer),
+ "Expected string, got EOF"),
+ t: (lex::token, lex::location) => match (t.0) {
+ l: lex::literal => match (l) {
+ s: str => (s, t.1),
+ * => return syntaxerr(t.1,
+ "Expected string, got <something else>"),
+ },
+ * => return syntaxerr(t.1,
+ "Expected string, got <something else>"),
+ },
+ };
+ let d = strings::iter(s.0);
+ match (strings::next(&d)) {
+ void => void,
+ r: rune => synassert(s.1,
+ ascii::isalpha(r) || r == '.' || r == '_',
+ "Invalid symbol")?,
+ };
+ for (true) match (strings::next(&d)) {
+ void => break,
+ r: rune => synassert(s.1,
+ ascii::isalnum(r) || r == '$' || r == '.' || r == '_',
+ "Invalid symbol")?,
+ };
+ want_btoken(lexer, btoken::RPAREN)?;
+ return s.0;
+};
+
+fn decl_global(
+ lexer: *lex::lexer,
+ tok: btoken
+) ([]ast::decl_global | error) = {
+ let decl: []ast::decl_global = [];
+ for (true) {
+ let symbol = if (tok == btoken::CONST || tok == btoken::LET) {
+ match (try_btoken(lexer, btoken::ATTR_SYMBOL)?) {
+ void => "",
+ lex::btoken => attr_symbol(lexer)?,
+ };
+ } else "";
+ let ident = ident(lexer)?;
+ want_btoken(lexer, btoken::COLON)?;
+ let _type = _type(lexer)?;
+ if (tok == btoken::CONST) {
+ _type.flags |= ast::type_flags::CONST;
+ };
+ want_btoken(lexer, btoken::EQUAL)?;
+ let init = expr(lexer)?;
+ let btok = try_btoken(lexer, btoken::COMMA)?;
+ append(decl, ast::decl_global {
+ is_const = tok == btoken::DEF,
+ symbol = symbol,
+ ident = ident,
+ _type = _type,
+ init = init,
+ });
+ match (btok) {
+ void => break,
+ * => void,
+ };
+ };
+ return decl;
+};
+
+fn decl_type(lexer: *lex::lexer) ([]ast::decl_type | error) = {
+ let decl: []ast::decl_type = [];
+ for (true) {
+ let ident = ident(lexer)?;
+ want_btoken(lexer, btoken::EQUAL)?;
+ let _type = _type(lexer)?;
+ let btok = try_btoken(lexer, btoken::COMMA)?;
+ append(decl, ast::decl_type {
+ ident = ident,
+ _type = _type,
+ });
+ match (btok) {
+ void => break,
+ * => void,
+ };
+ };
+ return decl;
+};
+
+fn decl_func(lexer: *lex::lexer) (ast::decl_func | error) = {
+ let attr = ast::fndecl_attrs::NONE, noreturn = false, sym = "";
+ let attrs = [
+ btoken::ATTR_FINI, btoken::ATTR_INIT, btoken::ATTR_TEST,
+ btoken::ATTR_NORETURN, btoken::ATTR_SYMBOL
+ ];
+ for (true) match (try_btoken(lexer, attrs...)?) {
+ void => break,
+ b: lex::btoken => switch (b) {
+ btoken::ATTR_FINI => attr = ast::fndecl_attrs::FINI,
+ btoken::ATTR_INIT => attr = ast::fndecl_attrs::INIT,
+ btoken::ATTR_TEST => attr = ast::fndecl_attrs::TEST,
+ btoken::ATTR_NORETURN => noreturn = true,
+ btoken::ATTR_SYMBOL => sym = attr_symbol(lexer)?,
+ * => abort("unreachable"),
+ },
+ };
+
+ want_btoken(lexer, btoken::FN)?;
+ let ident_loc = mkloc(lexer);
+ let ident = ident(lexer)?;
+ let proto_loc = mkloc(lexer);
+ let prototype = prototype(lexer)?;
+ if (noreturn) {
+ prototype.attrs |= ast::func_attrs::NORETURN;
+ };
+
+ let tok = want_btoken(lexer, btoken::EQUAL, btoken::SEMICOLON)?;
+ let body = switch (tok) {
+ btoken::EQUAL => {
+ synassert(ident_loc, len(ident) == 1,
+ "Expected name, found identifier")?;
+ const params = prototype.params;
+ for (let i = 0z; i < len(params); i += 1) {
+ synassert(params[i].loc,
+ len(params[i].name) > 0,
+ "Expected parameter name in function declaration")?;
+ };
+ expr(lexer)?;
+ },
+ // We don't care about the location
+ btoken::SEMICOLON => lex::unlex(lexer, (tok, proto_loc)),
+ };
+
+ return ast::decl_func {
+ symbol = sym,
+ ident = ident,
+ prototype = ast::_type {
+ loc = proto_loc,
+ flags = ast::type_flags::CONST,
+ _type = prototype,
+ },
+ body = body,
+ attrs = attr,
+ };
+};
+
+// Parses the declarations for a sub-unit
+export fn decls(lexer: *lex::lexer) ([]ast::decl | error) = {
+ let decls: []ast::decl = [];
+ for (true) {
+ let exported = match (try_btoken(lexer, btoken::EXPORT)?) {
+ void => false,
+ lex::btoken => true,
+ };
+ let tok = match(lex::lex(lexer)?) {
+ io::EOF => return syntaxerr(mkloc(lexer),
+ "Expected declaration, found EOF"),
+ t: (lex::token, lex::location) => t,
+ };
+ let btok = match (tok.0) {
+ b: lex::btoken => b,
+ // TODO: Use fmt+lex::tokstr here:
+ * => return syntaxerr(mkloc(lexer),
+ "Expected declaration, found <something else>"),
+ };
+ let decl = switch (btok) {
+ btoken::CONST, btoken::LET, btoken::DEF =>
+ decl_global(lexer, btok)?,
+ btoken::TYPE => decl_type(lexer)?,
+ * => {
+ lex::unlex(lexer, tok);
+ decl_func(lexer)?;
+ },
+ };
+ append(decls, ast::decl {
+ exported = exported,
+ loc = mkloc(lexer),
+ decl = decl,
+ });
+ want_btoken(lexer, btoken::SEMICOLON)?;
+ match (lex::lex(lexer)?) {
+ io::EOF => break,
+ t: (lex::token, lex::location) => lex::unlex(lexer, t),
+ };
+ };
+ return decls;
+};
diff --git a/hare/parse/expr.ha b/hare/parse/expr.ha
@@ -0,0 +1,10 @@
+use hare::ast;
+use hare::lex;
+use hare::lex::{btoken};
+
+// Parses an expression
+// TODO: Actually implement this
+export fn expr(lexer: *lex::lexer) (ast::expr | error) = {
+ want_btoken(lexer, btoken::VOID)?;
+ return void;
+};
diff --git a/hare/parse/ident.ha b/hare/parse/ident.ha
@@ -0,0 +1,34 @@
+use hare::ast;
+use hare::lex;
+use hare::lex::{btoken};
+
+fn ident_trailing(lexer: *lex::lexer) ((ast::ident, bool) | error) = {
+ let ident: []str = [];
+ let z = 0z;
+ for (true) {
+ let name = match (try_name(lexer)?) {
+ n: lex::name => n,
+ void => return (ident: ast::ident, true),
+ };
+ append(ident, name: str);
+ z += len(name);
+ match (try_btoken(lexer, btoken::DOUBLE_COLON)?) {
+ void => break,
+ * => void, // Grab the next ident
+ };
+ z += 1;
+ };
+ if (z > ast::IDENT_MAX) {
+ ast::ident_free(ident: ast::ident);
+ return syntaxerr(mkloc(lexer),
+ "Identifier exceeds maximum length");
+ };
+ return (ident: ast::ident, false);
+};
+
+// Parses a single identifier, i.e. foo::bar::baz
+export fn ident(lexer: *lex::lexer) (ast::ident | error) = {
+ let ident = ident_trailing(lexer)?;
+ synassert(mkloc(lexer), !ident.1, "Unexpected trailing :: in ident")?;
+ return ident.0;
+};
diff --git a/hare/parse/import.ha b/hare/parse/import.ha
@@ -0,0 +1,64 @@
+use hare::ast;
+use hare::lex;
+use hare::lex::{btoken};
+
+fn name_list(lexer: *lex::lexer) ([]str | error) = {
+ let names: []str = [];
+ for (true) {
+ append(names, want_name(lexer)?: str);
+ switch (want_btoken(lexer, btoken::COMMA, btoken::RBRACE)?) {
+ btoken::COMMA => match (try_btoken(lexer, btoken::RBRACE)?) {
+ void => void,
+ * => return names,
+ },
+ btoken::RBRACE => return names,
+ * => abort(), // Unreachable
+ };
+ };
+ abort();
+};
+
+// Parses the import list for a sub-unit
+export fn imports(lexer: *lex::lexer) ([]ast::import | error) = {
+ let imports: []ast::import = [];
+ for (true) {
+ match (try_btoken(lexer, btoken::USE)?) {
+ void => break,
+ * => void,
+ };
+
+ let name = ident_trailing(lexer)?;
+
+ switch (want_btoken(lexer, btoken::SEMICOLON, btoken::LBRACE,
+ btoken::EQUAL)?) {
+ btoken::SEMICOLON => {
+ synassert(mkloc(lexer), !name.1,
+ "Unexpected trailing :: in ident")?;
+ append(imports, name.0: ast::import_module);
+ },
+ btoken::LBRACE => {
+ synassert(mkloc(lexer), name.1,
+ "Expected trailing :: in ident")?;
+ let objects = name_list(lexer)?;
+ append(imports, ast::import_objects {
+ ident = name.0,
+ objects = objects,
+ });
+ want_btoken(lexer, btoken::SEMICOLON)?;
+ },
+ btoken::EQUAL => {
+ synassert(mkloc(lexer),
+ len(name.0) == 1 && !name.1,
+ "Expected name, not ident")?;
+ let ident = ident(lexer)?;
+ append(imports, ast::import_alias {
+ ident = ident,
+ alias = name.0[0],
+ });
+ want_btoken(lexer, btoken::SEMICOLON)?;
+ },
+ * => abort(), // Unreachable
+ };
+ };
+ return imports;
+};
diff --git a/hare/parse/parse.ha b/hare/parse/parse.ha
@@ -1,96 +0,0 @@
-use hare::ast;
-use hare::lex;
-use hare::lex::{btoken};
-use slice;
-
-fn ident_trailing(lexer: *lex::lexer) ((ast::ident, bool) | error) = {
- let ident: []str = [];
- let z = 0z;
- for (true) {
- let name = match (try_name(lexer)?) {
- n: lex::name => n,
- void => return (ident: ast::ident, true),
- };
- append(ident, name: str);
- z += len(name);
- match (try_btoken(lexer, btoken::DOUBLE_COLON)?) {
- void => break,
- * => void, // Grab the next ident
- };
- z += 1;
- };
- if (z > ast::IDENT_MAX) {
- ast::ident_free(ident: ast::ident);
- return syntaxerr(mkloc(lexer),
- "Identifier exceeds maximum length");
- };
- return (ident: ast::ident, false);
-};
-
-// Parses a single identifier, i.e. foo::bar::baz
-export fn ident(lexer: *lex::lexer) (ast::ident | error) = {
- let ident = ident_trailing(lexer)?;
- synassert(mkloc(lexer), !ident.1, "Unexpected trailing :: in ident")?;
- return ident.0;
-};
-
-fn parse_name_list(lexer: *lex::lexer) ([]str | error) = {
- let names: []str = [];
- for (true) {
- append(names, want_name(lexer)?: str);
- switch (want_btoken(lexer, btoken::COMMA, btoken::RBRACE)?) {
- btoken::COMMA => match (try_btoken(lexer, btoken::RBRACE)?) {
- void => void,
- * => return names,
- },
- btoken::RBRACE => return names,
- * => abort(), // Unreachable
- };
- };
- abort();
-};
-
-// Parses the import list for a sub-unit
-export fn imports(lexer: *lex::lexer) ([]ast::import | error) = {
- let imports: []ast::import = [];
- for (true) {
- match (try_btoken(lexer, btoken::USE)?) {
- void => break,
- * => void,
- };
-
- let name = ident_trailing(lexer)?;
-
- switch (want_btoken(lexer, btoken::SEMICOLON, btoken::LBRACE,
- btoken::EQUAL)?) {
- btoken::SEMICOLON => {
- synassert(mkloc(lexer), !name.1,
- "Unexpected trailing :: in ident")?;
- append(imports, name.0: ast::import_module);
- },
- btoken::LBRACE => {
- synassert(mkloc(lexer), name.1,
- "Expected trailing :: in ident")?;
- let objects = parse_name_list(lexer)?;
- append(imports, ast::import_objects {
- ident = name.0,
- objects = objects,
- });
- want_btoken(lexer, btoken::SEMICOLON)?;
- },
- btoken::EQUAL => {
- synassert(mkloc(lexer),
- len(name.0) == 1 && !name.1,
- "Expected name, not ident")?;
- let ident = ident(lexer)?;
- append(imports, ast::import_alias {
- ident = ident,
- alias = name.0[0],
- });
- want_btoken(lexer, btoken::SEMICOLON)?;
- },
- * => abort(), // Unreachable
- };
- };
- return imports;
-};
diff --git a/hare/parse/type.ha b/hare/parse/type.ha
@@ -0,0 +1,256 @@
+use hare::ast;
+use hare::ast::{builtin_type};
+use hare::lex;
+use hare::lex::{btoken};
+
+fn prototype(lexer: *lex::lexer) (ast::func_type | error) = {
+ let variadism = ast::variadism::NONE;
+ let params: []ast::func_param = [];
+ want_btoken(lexer, btoken::LPAREN)?;
+ for (try_btoken(lexer, btoken::RPAREN)? is void) {
+ let loc = mkloc(lexer);
+ match (try_btoken(lexer, btoken::ELLIPSIS)?) {
+ void => void,
+ lex::btoken => {
+ synassert(loc, len(params) > 0,
+ "Expected at least one non-variadic parameter for C-style variadism")?;
+ variadism = ast::variadism::C;
+ try_btoken(lexer, btoken::COMMA)?;
+ want_btoken(lexer, btoken::RPAREN)?;
+ break;
+ },
+ };
+ let name = match (try_btoken(lexer, btoken::UNDERSCORE)?) {
+ void => want_name(lexer)?: str,
+ lex::btoken => "",
+ };
+ want_btoken(lexer, btoken::COLON);
+ append(params, ast::func_param {
+ loc = loc,
+ name = name,
+ _type = alloc(_type(lexer)?),
+ });
+ match (try_btoken(lexer, btoken::ELLIPSIS)?) {
+ void => void,
+ lex::btoken => {
+ variadism = ast::variadism::HARE;
+ try_btoken(lexer, btoken::COMMA)?;
+ want_btoken(lexer, btoken::RPAREN)?;
+ break;
+ },
+ };
+ match (try_btoken(lexer, btoken::COMMA)?) {
+ void => {
+ want_btoken(lexer, btoken::RPAREN)?;
+ break;
+ },
+ lex::btoken => void,
+ };
+ };
+ let t = _type(lexer)?;
+ return ast::func_type {
+ result = alloc(t),
+ attrs = 0,
+ variadism = variadism,
+ params = params,
+ };
+};
+
+fn integer_type(
+ lexer: *lex::lexer,
+) (builtin_type | error) = switch (want_btoken(lexer)?) {
+ btoken::CHAR => builtin_type::CHAR,
+ btoken::I16 => builtin_type::I16,
+ btoken::I32 => builtin_type::I32,
+ btoken::I64 => builtin_type::I64,
+ btoken::I64 => builtin_type::I64,
+ btoken::I8 => builtin_type::I8,
+ btoken::INT => builtin_type::INT,
+ btoken::SIZE => builtin_type::SIZE,
+ btoken::U16 => builtin_type::U16,
+ btoken::U32 => builtin_type::U32,
+ btoken::U64 => builtin_type::U64,
+ btoken::U64 => builtin_type::U64,
+ btoken::U8 => builtin_type::U8,
+ btoken::UINT => builtin_type::UINT,
+ btoken::UINTPTR => builtin_type::UINTPTR,
+ * => syntaxerr(mkloc(lexer), "Expected integer type"),
+};
+
+fn primitive_type(lexer: *lex::lexer) (ast::_type | error) = {
+ let t = match (lex::lex(lexer)?) {
+ io::EOF => return syntaxerr(mkloc(lexer),
+ "Expected primitive type, got EOF"),
+ t: (lex::token, lex::location) => t,
+ };
+ let b = match (t.0) {
+ b: lex::btoken => b,
+ * => return syntaxerr(mkloc(lexer),
+ "Expected primitive type, got <something else>"),
+ };
+ let builtin = switch (b) {
+ btoken::CHAR, btoken::I16, btoken::I32, btoken::I64,
+ btoken::I64, btoken::I8, btoken::INT, btoken::SIZE, btoken::U16,
+ btoken::U32, btoken::U64, btoken::U64, btoken::U8, btoken::UINT,
+ btoken::UINTPTR => {
+ lex::unlex(lexer, t);
+ integer_type(lexer)?;
+ },
+ btoken::RUNE => builtin_type::RUNE,
+ btoken::STR => builtin_type::STR,
+ btoken::F32 => builtin_type::F32,
+ btoken::F64 => builtin_type::F64,
+ btoken::BOOL => builtin_type::BOOL,
+ btoken::VOID => builtin_type::VOID,
+ btoken::NULL => builtin_type::NULL,
+ * => return syntaxerr(mkloc(lexer), "Expected primitive type"),
+ };
+ return ast::_type {
+ loc = mkloc(lexer),
+ flags = 0,
+ _type = builtin,
+ };
+};
+
+fn alias_type(lexer: *lex::lexer) (ast::_type | error) = {
+ let loc = mkloc(lexer);
+ let unwrap = match (try_btoken(lexer, btoken::ELLIPSIS)?) {
+ void => false,
+ * => true,
+ };
+ return ast::_type {
+ loc = loc,
+ flags = 0,
+ _type = ast::alias_type {
+ unwrap = unwrap,
+ ident = ident(lexer)?,
+ },
+ };
+};
+
+fn pointer_type(lexer: *lex::lexer) (ast::_type | error) = {
+ let loc = mkloc(lexer);
+ let flags = match (try_btoken(lexer, btoken::NULLABLE)?) {
+ void => 0: ast::pointer_flags,
+ * => ast::pointer_flags::NULLABLE,
+ };
+ want_btoken(lexer, btoken::TIMES)?;
+ return ast::_type {
+ loc = loc,
+ flags = 0,
+ _type = ast::pointer_type {
+ referent = alloc(_type(lexer)?),
+ flags = flags,
+ },
+ };
+};
+
+fn tagged_type(lexer: *lex::lexer, first: ast::_type) (ast::_type | error) = {
+ let loc = mkloc(lexer);
+ let tagged: ast::tagged_type = [];
+ append(tagged, alloc(first));
+ for (try_btoken(lexer, btoken::RPAREN)? is void) {
+ append(tagged, alloc(_type(lexer)?));
+ match (try_btoken(lexer, btoken::BOR)?) {
+ void => {
+ want_btoken(lexer, btoken::RPAREN)?;
+ break;
+ },
+ lex::btoken => void,
+ };
+ };
+ return ast::_type {
+ loc = loc,
+ flags = 0,
+ _type = tagged,
+ };
+};
+
+fn tuple_type(lexer: *lex::lexer, first: ast::_type) (ast::_type | error) = {
+ let loc = mkloc(lexer);
+ let tuple: ast::tuple_type = [];
+ append(tuple, alloc(first));
+ for (try_btoken(lexer, btoken::RPAREN)? is void) {
+ append(tuple, alloc(_type(lexer)?));
+ match (try_btoken(lexer, btoken::COMMA)?) {
+ void => {
+ want_btoken(lexer, btoken::RPAREN)?;
+ break;
+ },
+ lex::btoken => void,
+ };
+ };
+ return ast::_type {
+ loc = loc,
+ flags = 0,
+ _type = tuple,
+ };
+};
+
+fn fn_type(lexer: *lex::lexer) (ast::_type | error) = {
+ let loc = mkloc(lexer);
+ let attrs = match (try_btoken(lexer, btoken::ATTR_NORETURN)?) {
+ void => 0: ast::func_attrs,
+ * => ast::func_attrs::NORETURN,
+ };
+ want_btoken(lexer, btoken::FN)?;
+ let proto = prototype(lexer)?;
+ proto.attrs |= attrs;
+ return ast::_type {
+ loc = loc,
+ flags = ast::type_flags::CONST,
+ _type = proto,
+ };
+};
+
+// Parses a type
+export fn _type(lexer: *lex::lexer) (ast::_type | error) = {
+ let flags: ast::type_flags = match (try_btoken(lexer, btoken::CONST)?) {
+ void => 0,
+ * => ast::type_flags::CONST,
+ };
+ let t = match (lex::lex(lexer)?) {
+ io::EOF => return syntaxerr(mkloc(lexer),
+ "Expected type, found EOF"),
+ t: (lex::token, lex::location) => t,
+ };
+ lex::unlex(lexer, t);
+ let typ: ast::_type = match (t.0) {
+ b: lex::btoken => switch (b) {
+ btoken::CHAR, btoken::I16, btoken::I32, btoken::I64,
+ btoken::I64, btoken::I8, btoken::INT, btoken::SIZE,
+ btoken::U16, btoken::U32, btoken::U64, btoken::U64,
+ btoken::U8, btoken::UINT, btoken::UINTPTR, btoken::RUNE,
+ btoken::STR, btoken::F32, btoken::F64, btoken::BOOL,
+ btoken::VOID, btoken::NULL => primitive_type(lexer)?,
+ btoken::ENUM => abort(), // TODO
+ btoken::NULLABLE, btoken::TIMES => pointer_type(lexer)?,
+ btoken::STRUCT, btoken::UNION => abort(), // TODO
+ btoken::LBRACKET => abort(), // TODO
+ btoken::LPAREN => {
+ want_btoken(lexer, btoken::LPAREN)?;
+ let t = _type(lexer)?;
+ switch (want_btoken(lexer, btoken::BOR,
+ btoken::COMMA)?) {
+ btoken::BOR => tagged_type(lexer, t)?,
+ btoken::COMMA => tuple_type(lexer, t)?,
+ * => abort("unreachable"),
+ };
+ },
+ btoken::ATTR_NORETURN, btoken::FN => fn_type(lexer)?,
+ btoken::ELLIPSIS => alias_type(lexer)?,
+ * => return syntaxerr(mkloc(lexer), "Expected type"),
+ },
+ lex::name => alias_type(lexer)?,
+ * => return syntaxerr(mkloc(lexer),
+ "Expected type, found <something>"),
+ };
+
+ match (try_btoken(lexer, btoken::LNOT)?) {
+ void => void,
+ * => flags |= ast::type_flags::ERROR,
+ };
+
+ typ.flags |= flags;
+ return typ;
+};
diff --git a/hare/parse/unit.ha b/hare/parse/unit.ha
@@ -0,0 +1,11 @@
+use hare::ast;
+use hare::lex;
+
+export fn subunit(lexer: *lex::lexer) (ast::subunit | error) = {
+ let i = imports(lexer)?;
+ let d = decls(lexer)?;
+ return ast::subunit {
+ imports = i,
+ decls = d,
+ };
+};
diff --git a/hare/parse/util.ha b/hare/parse/util.ha
@@ -39,6 +39,9 @@ fn want_btoken(
"Expected name, found EOF"),
t: (lex::token, lex::location) => match (t.0) {
b: lex::btoken => {
+ if (len(want) == 0) {
+ return b;
+ };
for (let i = 0z; i < len(want); i += 1) {
if (b == want[i]) {
return b;
diff --git a/scripts/gen-stdlib b/scripts/gen-stdlib
@@ -294,14 +294,19 @@ hare_module() {
manifest.ha
gen_ssa hare::module \
hare::ast hare::lex hare::parse hare::unparse strio fs io strings hash \
- crypto::sha256 dirs bytes encoding::utf8 ascii fmt time
+ crypto::sha256 dirs bytes encoding::utf8 ascii fmt time slice
}
gensrcs_hare_parse() {
gen_srcs hare::parse \
+ decl.ha \
+ expr.ha \
+ ident.ha \
+ import.ha \
+ type.ha \
types.ha \
+ unit.ha \
util.ha \
- parse.ha \
$*
}
@@ -310,11 +315,13 @@ hare_parse() {
if [ $testing -eq 0 ]
then
gensrcs_hare_parse
+ gen_ssa hare::parse hare::ast hare::lex
else
gensrcs_hare_parse \
+test.ha
+ gen_ssa hare::parse bufio fmt hare::ast hare::lex hare::unparse io \
+ strings strio
fi
- gen_ssa hare::parse hare::ast hare::lex slice
}
hash() {
diff --git a/stdlib.mk b/stdlib.mk
@@ -383,7 +383,7 @@ stdlib_hare_module_srcs= \
$(STDLIB)/hare/module/scan.ha \
$(STDLIB)/hare/module/manifest.ha
-$(HARECACHE)/hare/module/hare_module.ssa: $(stdlib_hare_module_srcs) $(stdlib_rt) $(stdlib_hare_ast) $(stdlib_hare_lex) $(stdlib_hare_parse) $(stdlib_hare_unparse) $(stdlib_strio) $(stdlib_fs) $(stdlib_io) $(stdlib_strings) $(stdlib_hash) $(stdlib_crypto_sha256) $(stdlib_dirs) $(stdlib_bytes) $(stdlib_encoding_utf8) $(stdlib_ascii) $(stdlib_fmt) $(stdlib_time)
+$(HARECACHE)/hare/module/hare_module.ssa: $(stdlib_hare_module_srcs) $(stdlib_rt) $(stdlib_hare_ast) $(stdlib_hare_lex) $(stdlib_hare_parse) $(stdlib_hare_unparse) $(stdlib_strio) $(stdlib_fs) $(stdlib_io) $(stdlib_strings) $(stdlib_hash) $(stdlib_crypto_sha256) $(stdlib_dirs) $(stdlib_bytes) $(stdlib_encoding_utf8) $(stdlib_ascii) $(stdlib_fmt) $(stdlib_time) $(stdlib_slice)
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/hare/module
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nhare::module \
@@ -391,11 +391,16 @@ $(HARECACHE)/hare/module/hare_module.ssa: $(stdlib_hare_module_srcs) $(stdlib_rt
# hare::parse
stdlib_hare_parse_srcs= \
+ $(STDLIB)/hare/parse/decl.ha \
+ $(STDLIB)/hare/parse/expr.ha \
+ $(STDLIB)/hare/parse/ident.ha \
+ $(STDLIB)/hare/parse/import.ha \
+ $(STDLIB)/hare/parse/type.ha \
$(STDLIB)/hare/parse/types.ha \
- $(STDLIB)/hare/parse/util.ha \
- $(STDLIB)/hare/parse/parse.ha
+ $(STDLIB)/hare/parse/unit.ha \
+ $(STDLIB)/hare/parse/util.ha
-$(HARECACHE)/hare/parse/hare_parse.ssa: $(stdlib_hare_parse_srcs) $(stdlib_rt) $(stdlib_hare_ast) $(stdlib_hare_lex) $(stdlib_slice)
+$(HARECACHE)/hare/parse/hare_parse.ssa: $(stdlib_hare_parse_srcs) $(stdlib_rt) $(stdlib_hare_ast) $(stdlib_hare_lex)
@printf 'HAREC \t$@\n'
@mkdir -p $(HARECACHE)/hare/parse
@HARECACHE=$(HARECACHE) $(HAREC) $(HAREFLAGS) -o $@ -Nhare::parse \
@@ -1030,7 +1035,7 @@ testlib_hare_module_srcs= \
$(STDLIB)/hare/module/scan.ha \
$(STDLIB)/hare/module/manifest.ha
-$(TESTCACHE)/hare/module/hare_module.ssa: $(testlib_hare_module_srcs) $(testlib_rt) $(testlib_hare_ast) $(testlib_hare_lex) $(testlib_hare_parse) $(testlib_hare_unparse) $(testlib_strio) $(testlib_fs) $(testlib_io) $(testlib_strings) $(testlib_hash) $(testlib_crypto_sha256) $(testlib_dirs) $(testlib_bytes) $(testlib_encoding_utf8) $(testlib_ascii) $(testlib_fmt) $(testlib_time)
+$(TESTCACHE)/hare/module/hare_module.ssa: $(testlib_hare_module_srcs) $(testlib_rt) $(testlib_hare_ast) $(testlib_hare_lex) $(testlib_hare_parse) $(testlib_hare_unparse) $(testlib_strio) $(testlib_fs) $(testlib_io) $(testlib_strings) $(testlib_hash) $(testlib_crypto_sha256) $(testlib_dirs) $(testlib_bytes) $(testlib_encoding_utf8) $(testlib_ascii) $(testlib_fmt) $(testlib_time) $(testlib_slice)
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/hare/module
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nhare::module \
@@ -1038,12 +1043,17 @@ $(TESTCACHE)/hare/module/hare_module.ssa: $(testlib_hare_module_srcs) $(testlib_
# hare::parse
testlib_hare_parse_srcs= \
+ $(STDLIB)/hare/parse/decl.ha \
+ $(STDLIB)/hare/parse/expr.ha \
+ $(STDLIB)/hare/parse/ident.ha \
+ $(STDLIB)/hare/parse/import.ha \
+ $(STDLIB)/hare/parse/type.ha \
$(STDLIB)/hare/parse/types.ha \
+ $(STDLIB)/hare/parse/unit.ha \
$(STDLIB)/hare/parse/util.ha \
- $(STDLIB)/hare/parse/parse.ha \
$(STDLIB)/hare/parse/+test.ha
-$(TESTCACHE)/hare/parse/hare_parse.ssa: $(testlib_hare_parse_srcs) $(testlib_rt) $(testlib_hare_ast) $(testlib_hare_lex) $(testlib_slice)
+$(TESTCACHE)/hare/parse/hare_parse.ssa: $(testlib_hare_parse_srcs) $(testlib_rt) $(testlib_bufio) $(testlib_fmt) $(testlib_hare_ast) $(testlib_hare_lex) $(testlib_hare_unparse) $(testlib_io) $(testlib_strings) $(testlib_strio)
@printf 'HAREC \t$@\n'
@mkdir -p $(TESTCACHE)/hare/parse
@HARECACHE=$(TESTCACHE) $(HAREC) $(TESTHAREFLAGS) -o $@ -Nhare::parse \