hare

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

commit bc8d0bf21145131f7ce3cb4d399ecc3cd10213a6
parent 6f6067ba16ca3cc21447db4cbe8ec60df81daf9d
Author: Drew DeVault <sir@cmpwn.com>
Date:   Sat, 21 Aug 2021 09:33:41 +0200

hare::unit: new module

This provides some of the initial groundwork for a "check" phase for the
hosted harec toolchain. cmd/harec is updated to advance the pipeline a
little further accordingly.

Signed-off-by: Drew DeVault <sir@cmpwn.com>

Diffstat:
Mcmd/harec/main.ha | 19+++++++++++++++++--
Ahare/unit/check.ha | 32++++++++++++++++++++++++++++++++
Ahare/unit/errors.ha | 4++++
Ahare/unit/scan.ha | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahare/unit/scope.ha | 58++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ahare/unit/unit.ha | 34++++++++++++++++++++++++++++++++++
6 files changed, 199 insertions(+), 2 deletions(-)

diff --git a/cmd/harec/main.ha b/cmd/harec/main.ha @@ -4,6 +4,8 @@ use getopt; use hare::ast; use hare::lex; use hare::parse; +use hare::types; +use hare::unit; use hare::unparse; use io; use os; @@ -38,6 +40,15 @@ export fn main() void = { os::exit(1); }; + // TODO: Use hare::unit resolver + const store = types::store(types::x86_64, null, null); + defer types::store_free(store); + + let subunits: []ast::subunit = []; + defer for (let i = 0z; i < len(subunits); i += 1) { + ast::subunit_free(subunits[i]); + }; + for (let i = 0z; i < len(cmd.args); i += 1) { let input = match (os::open(cmd.args[i])) { f: *io::stream => f, @@ -57,8 +68,12 @@ export fn main() void = { }, u: ast::subunit => u, }; - defer ast::subunit_free(su); + append(subunits, su); + }; - unparse::subunit(os::stdout, su)!; + let unit = match (unit::check(store, [], subunits)) { + unit::error => abort(), // TODO + u: unit::unit => u, }; + defer unit::unit_free(unit); }; diff --git a/hare/unit/check.ha b/hare/unit/check.ha @@ -0,0 +1,32 @@ +use hare::ast; +use hare::types; + +type context = struct { + store: *types::typestore, + unit: *unit, + scope: *scope, +}; + +// Type checks a set of [[hare::ast::subunit]]s and determines if it is +// logically consistent, then returns the corresponding [[unit]], which from +// then on is guaranteed to be compilable. +export fn check( + store: *types::typestore, + ident: ast::ident, + subunits: const []ast::subunit, +) (unit | error) = { + let unit = unit { + ident = ident, + decls = [], + }; + let ctx = context { + store = store, + unit = &unit, + scope = alloc(scope { ... }), + }; + + scan(&ctx, subunits)?; + // TODO: validate + + return unit; +}; diff --git a/hare/unit/errors.ha b/hare/unit/errors.ha @@ -0,0 +1,4 @@ +use hare::types; + +// TODO: More errors +export type error = !(types::error | void); diff --git a/hare/unit/scan.ha b/hare/unit/scan.ha @@ -0,0 +1,54 @@ +use hare::ast; +use hare::types; + +// The scan phase determines the types of all declarations in the unit, and +// handles forward references, so that the next phase (validate) is simplified. +fn scan(ctx: *context, subunits: const []ast::subunit) (void | error) = { + for (let i = 0z; i < len(subunits); i += 1) { + let subunit = &subunits[i]; + assert(len(subunit.imports) == 0); // TODO + + // TODO: Allocate scope + for (let j = 0z; j < len(subunit.decls); j += 1) { + let decl = &subunit.decls[j]; + match (scan_decl(ctx, decl)) { + void => void, + types::deferred => abort(), // TODO + error => abort(), // TODO + }; + }; + }; +}; + +fn scan_decl( + ctx: *context, + decl: *ast::decl, +) (void | types::deferred | error) = { + // TODO: match on &decl.decl + match (decl.decl) { + co: []ast::decl_const => abort(), // TODO + gl: []ast::decl_global => abort(), // TODO + ty: []ast::decl_type => abort(), // TODO + fu: ast::decl_func => scan_func(ctx, decl, fu), + }; +}; + +fn scan_func( + ctx: *context, + decl: *ast::decl, + func: ast::decl_func, +) (void | types::deferred | error) = { + assert(func.attrs & ast::fndecl_attrs::TEST == 0); // TODO + const fntype = match (types::lookup(ctx.store, &func.prototype)) { + err: types::error => return err, + types::deferred => return types::deferred, + fntype: const *types::_type => fntype, + }; + scope_insert(ctx, object { + kind = object_kind::DECL, + // TODO: Add namespace to ident + ident = ast::ident_dup(func.ident), + name = ast::ident_dup(func.ident), + _type = fntype, + }); +}; diff --git a/hare/unit/scope.ha b/hare/unit/scope.ha @@ -0,0 +1,58 @@ +use hare::ast; +use hare::types; +use hash::fnv; +use hash; +use strings; + +export type object_kind = enum { + BIND, + CONST, + DECL, + TYPE, +}; + +export type object = struct { + kind: object_kind, + hash: u64, + + // The fully qualified identifier + ident: ast::ident, + // Local name, if different from the fully qualified identifier + name: ast::ident, + + _type: const *types::_type, + // TODO: store value for constants +}; + +export def SCOPE_BUCKETS: size = 4096; + +export type scope = struct { + parent: nullable *scope, + objects: []object, + hashmap: [SCOPE_BUCKETS][]*object, +}; + +fn scope_push(ctx: *context) *scope = { + let new = alloc(scope { + parent = ctx.scope, + ... + }); + ctx.scope = new; + return new; +}; + +fn scope_insert(ctx: *context, obj: object) void = { + const scope = ctx.scope; + append(scope.objects, obj); + let obj = &scope.objects[len(scope.objects) - 1]; + let hash = fnv::fnv64a(); + defer hash::close(hash); + const zerobuf = [0u8]; + for (let i = len(obj.ident); i > 0; i -= 1) { + hash::write(hash, strings::toutf8(obj.ident[i - 1])); + hash::write(hash, zerobuf[..]); + }; + let hash = fnv::sum64(hash); + obj.hash = hash; + append(scope.hashmap[hash: size % SCOPE_BUCKETS], obj); +}; diff --git a/hare/unit/unit.ha b/hare/unit/unit.ha @@ -0,0 +1,34 @@ +use hare::ast; +use hare::lex; +use hare::types; + +export type fndecl_attrs = enum { + NONE, + FINI, + INIT, + TEST, +}; + +export type decl_func = struct { + symbol: str, + ident: ast::ident, + prototype: const *types::_type, + //body: (expr | void), TODO + attrs: fndecl_attrs, +}; + +export type decl = struct { + exported: bool, + loc: lex::location, + decl: (decl_func | void), // TODO: Other decl types +}; + +export type unit = struct { + ident: ast::ident, + decls: []decl, +}; + +export fn unit_free(unit: unit) void = { + // TODO + return; +};