commit 6efed7de7248d0a56a146a20234bb7a32ab2b26e
parent eecfabb69c6fe45e8aa1643a81931352142e534f
Author: Drew DeVault <sir@cmpwn.com>
Date: Mon, 22 Feb 2021 13:11:28 -0500
hare::ast, hare::parse: new modules
Diffstat:
3 files changed, 167 insertions(+), 0 deletions(-)
diff --git a/hare/ast/types.ha b/hare/ast/types.ha
@@ -0,0 +1,40 @@
+// Identifies a single object, e.g. foo::bar::baz
+export type identifier = struct {
+ name: str,
+ ns: nullable *identifier,
+};
+
+export fn identifier_finish(ident: *identifier) void = {
+ match (ident.ns) {
+ null => void,
+ ns: *identifier => identifier_finish(ns),
+ };
+ free(ident.name);
+};
+
+// A sub-unit, typically representing a single source file
+export type subunit = struct {
+ imports: []import,
+ declarations: []declaration,
+};
+
+// use module;
+export type import_module = identifier;
+
+// use alias = module;
+export type import_alias = struct {
+ ident: identifier,
+ alias: str,
+};
+
+// use module::{foo, bar, baz};
+export type import_objects = struct {
+ ident: identifier,
+ objects: []str,
+};
+
+// An imported module
+export type import = (import_module | import_alias | import_objects);
+
+// TODO
+export type declaration = void;
diff --git a/hare/parse/+test.ha b/hare/parse/+test.ha
@@ -0,0 +1,75 @@
+use bufio;
+use hare::ast;
+use hare::lex;
+use strings;
+
+@test fn identifier() void = {
+ {
+ const in = "foo";
+ let buf = bufio::fixed(strings::to_utf8(in));
+ let lexer = lex::lexer_init(buf, "<test>");
+ let ident = identifier(&lexer) as ast::identifier;
+ defer ast::identifier_finish(&ident);
+ assert(ident.name == "foo");
+ assert(ident.ns == null);
+ assert(lex::lex(&lexer) is io::EOF);
+ };
+
+ {
+ const in = "foo::bar";
+ let buf = bufio::fixed(strings::to_utf8(in));
+ let lexer = lex::lexer_init(buf, "<test>");
+ let ident = identifier(&lexer) as ast::identifier;
+ defer ast::identifier_finish(&ident);
+ assert(ident.name == "foo");
+ match (ident.ns) {
+ null => abort(),
+ ns: *ast::identifier => {
+ assert(ns.name == "bar");
+ assert(ns.ns == null);
+ },
+ };
+ assert(lex::lex(&lexer) is io::EOF);
+ };
+
+ {
+ const in = "foo::bar::baz";
+ let buf = bufio::fixed(strings::to_utf8(in));
+ let lexer = lex::lexer_init(buf, "<test>");
+ let ident = identifier(&lexer) as ast::identifier;
+ defer ast::identifier_finish(&ident);
+ assert(ident.name == "foo");
+ match (ident.ns) {
+ null => abort(),
+ ns: *ast::identifier => {
+ assert(ns.name == "bar");
+ match (ns.ns) {
+ null => abort(),
+ ns: *ast::identifier => {
+ assert(ns.name == "baz");
+ assert(ns.ns == null);
+ },
+ };
+ },
+ };
+ assert(lex::lex(&lexer) is io::EOF);
+ };
+
+ {
+ const in = "foo::bar;";
+ let buf = bufio::fixed(strings::to_utf8(in));
+ let lexer = lex::lexer_init(buf, "<test>");
+ let ident = identifier(&lexer) as ast::identifier;
+ defer ast::identifier_finish(&ident);
+ assert(ident.name == "foo");
+ match (ident.ns) {
+ null => abort(),
+ ns: *ast::identifier => {
+ assert(ns.name == "bar");
+ assert(ns.ns == null);
+ },
+ };
+ let tok = lex::lex(&lexer) as (lex::token, lex::location);
+ assert(tok.0 as lex::btoken == hare::lex::btoken::SEMICOLON);
+ };
+};
diff --git a/hare/parse/parse.ha b/hare/parse/parse.ha
@@ -0,0 +1,52 @@
+use hare::ast;
+use hare::lex;
+
+// All possible error types
+export type error = lex::error;
+
+// Convert an error into a human-friendly string
+export fn errstr(err: error) const str = lex::errstr(err: lex::error);
+
+// Parses a single identifier, i.e. foo::bar::baz
+export fn identifier(lexer: *lex::lexer) (ast::identifier | error) = {
+ let ident = ast::identifier { ... }, cur = &ident;
+ for (true) {
+ let tok = lex::lex(lexer);
+ match (tok) {
+ io::EOF => abort(), // TODO: Syntax error
+ err: lex::error => return err,
+ t: (lex::token, lex::location) => match (t.0) {
+ n: lex::name => cur.name = n: str,
+ * => {
+ lex::unlex(lexer, t);
+ break;
+ },
+ },
+ };
+
+ tok = lex::lex(lexer);
+ match (tok) {
+ io::EOF => break,
+ err: lex::error => return err,
+ t: (lex::token, lex::location) => match (t.0) {
+ b: lex::btoken => switch (b) {
+ hare::lex::btoken::DOUBLE_COLON => {
+ let new = alloc(*ast::identifier,
+ ast::identifier { ... });
+ cur.ns = new;
+ cur = new;
+ },
+ * => {
+ lex::unlex(lexer, t);
+ break;
+ },
+ },
+ * => {
+ lex::unlex(lexer, t);
+ break;
+ },
+ },
+ };
+ };
+ return ident;
+};