hare

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

commit d003ed892c49810aacc0f81d418692412bcc835b
parent 3c90d8bc46e9a957bc28559a3de3698fdfecbcb6
Author: Drew DeVault <sir@cmpwn.com>
Date:   Fri, 26 Feb 2021 15:42:15 -0500

hare::module: scan imports

Diffstat:
Mhare/ast/types.ha | 13+++++++++++++
Mhare/lex/+test.ha | 8++++----
Mhare/lex/lex.ha | 2+-
Mhare/module/scan.ha | 57++++++++++++++++++++++++++++++++++++++++++++++-----------
Mhare/module/types.ha | 6+++++-
Mhare/parse/+test.ha | 16++++++++--------
6 files changed, 77 insertions(+), 25 deletions(-)

diff --git a/hare/ast/types.ha b/hare/ast/types.ha @@ -15,6 +15,19 @@ export fn ident_free(ident: ident) void = { free(ident); }; +// Returns true if two idents are identical. +export fn ident_eq(a: ident, b: ident) bool = { + if (len(a) != len(b)) { + return false; + }; + for (let i = 0z; i < len(a); i += 1) { + if (a[i] != b[i]) { + return false; + }; + }; + return true; +}; + // A sub-unit, typically representing a single source file. export type subunit = struct { imports: []import, diff --git a/hare/lex/+test.ha b/hare/lex/+test.ha @@ -6,7 +6,7 @@ use strings; @test fn unget() void = { let buf = bufio::fixed(strings::to_utf8("z"), mode::READ); - let lexer = lexer_init(buf, "<test>"); + let lexer = init(buf, "<test>"); unget(&lexer, 'x'); unget(&lexer, 'y'); assert(next(&lexer) as rune == 'y'); @@ -18,7 +18,7 @@ use strings; }; @test fn unlex() void = { - let lexer = lexer_init(io::empty, "<test>"); + let lexer = init(io::empty, "<test>"); unlex(&lexer, (btoken::IF, location { path = "<test>", line = 1234, @@ -58,7 +58,7 @@ fn liteq(expected: literal, actual: literal) bool = { fn lextest(in: str, expected: [](uint, uint, token)) void = { let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lexer_init(buf, "<test>"); + let lexer = init(buf, "<test>"); for (let i = 0z; i < len(expected); i += 1) { let eline = expected[i].0, ecol = expected[i].1, etok = expected[i].2; @@ -191,7 +191,7 @@ fn lextest(in: str, expected: [](uint, uint, token)) void = { @test fn keywords() void = { let keywords = bmap[..btoken::LAST_KEYWORD+1]; for (let i = 0z; i < len(keywords); i += 1) { - let lexer = lexer_init(bufio::fixed( + let lexer = init(bufio::fixed( strings::to_utf8(keywords[i]), mode::READ), "<test>"); let tl = match (lex(&lexer)) { diff --git a/hare/lex/lex.ha b/hare/lex/lex.ha @@ -33,7 +33,7 @@ export fn errstr(err: error) const str = { }; // Initializes a new lexer for the given input stream. The path is borrowed. -export fn lexer_init(in: *io::stream, path: str) lexer = lexer { +export fn init(in: *io::stream, path: str) lexer = lexer { in = in, path = path, loc = (1, 1), diff --git a/hare/module/scan.ha b/hare/module/scan.ha @@ -1,39 +1,35 @@ use crypto::sha256; use fs; use hare::ast; +use hare::lex; +use hare::parse; use hash; use io; use path; use slice; use strings; -fn hash_file(ctx: *context, path: path::path) ([]u8 | error) = { - let sha = sha256::sha256(); - //defer! hash::close(sha); - let f = fs::open(ctx.fs, path, io::mode::READ)?; - io::copy(hash::writer(sha), f)?; - return hash::finish(sha); -}; - // Scans the files in a directory for eligible build inputs and returns a -// [version] which includes all applicable files. +// [version] which includes all applicable files and their dependencies. export fn scan(ctx: *context, path: path::path) (version | error) = { let sha = sha256::sha256(); //defer! hash::close(sha); let inputs: []input = []; + let deps: []ast::ident = []; let iter = match (fs::iter(ctx.fs, path)) { fs::wrongtype => { let st = fs::stat(ctx.fs, path)?; let in = input { path = path, stat = st, - hash = hash_file(ctx, path)?, + hash = scan_file(ctx, path, &deps)?, ... }; append(inputs, in); hash::write(sha, in.hash); return version { hash = hash::finish(sha), + depends = deps, inputs = inputs, }; }, @@ -51,7 +47,7 @@ export fn scan(ctx: *context, path: path::path) (version | error) = { let in = input { path = p, stat = st, - hash = hash_file(ctx, p)?, + hash = scan_file(ctx, p, &deps)?, ... }; append(inputs, in); @@ -62,6 +58,7 @@ export fn scan(ctx: *context, path: path::path) (version | error) = { }; return version { hash = hash::finish(sha), + depends = deps, inputs = inputs, }; }; @@ -86,3 +83,41 @@ fn eligible(ctx: *context, name: path::path) bool = { }; return false; }; + +fn scan_file( + ctx: *context, + path: path::path, + deps: *[]ast::ident, +) ([]u8 | error) = { + let f = fs::open(ctx.fs, path, io::mode::READ)?; + let sha = sha256::sha256(); + //defer! hash::close(sha); + let tee = io::tee(f, hash::writer(sha)); + defer io::close(tee); + + let lexer = lex::init(tee, path as str); + let imports = parse::imports(&lexer)?; + for (let i = 0z; i < len(imports); i += 1) { + let ident = match (imports[i]) { + m: ast::import_module => m: ast::ident, + a: ast::import_alias => a.ident, + o: ast::import_objects => o.ident, + }; + if (!have_ident(deps, ident)) { + append(*deps, ident); + }; + }; + + io::copy(io::empty, tee)?; // Finish spooling out the file for the SHA + return hash::finish(sha); +}; + +fn have_ident(sl: *[]ast::ident, id: ast::ident) bool = { + // XXX: We shouldn't have to deref sl here + for (let i = 0z; i < len(*sl); i += 1) { + if (ast::ident_eq(sl[i], id)) { + return true; + }; + }; + return false; +}; diff --git a/hare/module/types.ha b/hare/module/types.ha @@ -1,4 +1,6 @@ use fs; +use hare::ast; +use hare::parse; use io; use path; @@ -24,6 +26,7 @@ export type manifest = struct { // A module version: a set of possible input files for that module. export type version = struct { hash: []u8, + depends: []ast::ident, inputs: []input, }; @@ -38,10 +41,11 @@ export type input = struct { export type module_not_found = void!; // All possible error types. -export type error = (fs::error | io::error | module_not_found)!; +export type error = (fs::error | io::error | parse::error | module_not_found)!; export fn errstr(err: error) const str = match (err) { err: fs::error => fs::errstr(err), err: io::error => io::errstr(err), + err: parse::error => parse::errstr(err), module_not_found => "Module not found", }; diff --git a/hare/parse/+test.ha b/hare/parse/+test.ha @@ -9,7 +9,7 @@ use strings; { const in = "foo"; let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lex::lexer_init(buf, "<test>"); + let lexer = lex::init(buf, "<test>"); let ident = ident(&lexer) as ast::ident; defer ast::ident_free(ident); assert(len(ident) == 1); @@ -20,7 +20,7 @@ use strings; { const in = "foo::bar"; let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lex::lexer_init(buf, "<test>"); + let lexer = lex::init(buf, "<test>"); let ident = ident(&lexer) as ast::ident; defer ast::ident_free(ident); assert(len(ident) == 2); @@ -31,7 +31,7 @@ use strings; { const in = "foo::bar::baz"; let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lex::lexer_init(buf, "<test>"); + let lexer = lex::init(buf, "<test>"); let ident = ident(&lexer) as ast::ident; defer ast::ident_free(ident); assert(len(ident) == 3); @@ -43,7 +43,7 @@ use strings; { const in = "foo::bar;"; let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lex::lexer_init(buf, "<test>"); + let lexer = lex::init(buf, "<test>"); let ident = ident(&lexer) as ast::ident; defer ast::ident_free(ident); assert(len(ident) == 2); @@ -57,7 +57,7 @@ use strings; { const in = "use foo;"; let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lex::lexer_init(buf, "<test>"); + let lexer = lex::init(buf, "<test>"); let mods = imports(&lexer) as []ast::import; defer for (let i = 0z; i < len(mods); i += 1) { ast::import_free(mods[i]); @@ -78,7 +78,7 @@ use strings; "use baz::bat;\n\n" "export fn main() void = void;"; let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lex::lexer_init(buf, "<test>"); + let lexer = lex::init(buf, "<test>"); let mods = imports(&lexer) as []ast::import; defer for (let i = 0z; i < len(mods); i += 1) { ast::import_free(mods[i]); @@ -107,7 +107,7 @@ use strings; "use qux = quux::corge;\n" "export fn main() void = void;"; let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lex::lexer_init(buf, "<test>"); + let lexer = lex::init(buf, "<test>"); let mods = imports(&lexer) as []ast::import; defer for (let i = 0z; i < len(mods); i += 1) { ast::import_free(mods[i]); @@ -138,7 +138,7 @@ use strings; "use quux::corge::{grault, garply,};\n" "export fn main() void = void;"; let buf = bufio::fixed(strings::to_utf8(in), mode::READ); - let lexer = lex::lexer_init(buf, "<test>"); + let lexer = lex::init(buf, "<test>"); let mods = imports(&lexer) as []ast::import; defer for (let i = 0z; i < len(mods); i += 1) { ast::import_free(mods[i]);