hare

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

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:
Ahare/ast/types.ha | 40++++++++++++++++++++++++++++++++++++++++
Ahare/parse/+test.ha | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahare/parse/parse.ha | 52++++++++++++++++++++++++++++++++++++++++++++++++++++
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; +};