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:
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;
+};