diff --git a/src/index.ts b/src/index.ts index f7c1cc1..4dbdda3 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,6 @@ import { GlobalContext, parseArgs } from "./context"; import { loadCrate } from "./loader"; const INPUT = ` -extern mod a; type A = { a: Int }; function main() = ( diff --git a/src/loader.ts b/src/loader.ts index 362da1b..a759cda 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -13,9 +13,22 @@ export function loadModuleFile( moduleName: string, span: Span ): LoadedFile { + let searchDir: string; + if (relativeTo.endsWith(".mod.nil")) { + // x/uwu.mod.nil searches in x/ + searchDir = path.dirname(relativeTo); + } else if (relativeTo.endsWith(".nil")) { + throw new CompilerError( + `.nil files cannot have submodules. use .mod.nil in a subdirectory`, + span + ); + } else { + searchDir = relativeTo; + } + const options = [ - path.join(relativeTo, `${moduleName}.nil`), - path.join(relativeTo, moduleName, `${moduleName}.mod.nil`), + path.join(searchDir, `${moduleName}.nil`), + path.join(searchDir, moduleName, `${moduleName}.mod.nil`), ]; let content: string | undefined = undefined; @@ -45,15 +58,17 @@ export const loadCrate: CrateLoader = ( // We really, really want a good algorithm for finding crates. // But right now we just look for files in the CWD. - const existing = gcx.finalizedCrates.find((crate) => crate.packageName === name); + const existing = gcx.finalizedCrates.find( + (crate) => crate.packageName === name + ); if (existing) { return existing; } - const file = loadModuleFile(".", name, span); - return withErrorPrinter( (): DepCrate => { + const file = loadModuleFile(".", name, span); + const crateId = gcx.crateId.next(); const tokens = tokenize(file); diff --git a/src/parser.ts b/src/parser.ts index 3aa8f83..ec4834b 100644 --- a/src/parser.ts +++ b/src/parser.ts @@ -33,7 +33,14 @@ import { StructLiteralField, } from "./ast"; import { CompilerError, LoadedFile, Span } from "./error"; -import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer"; +import { + BaseToken, + Token, + TokenIdent, + TokenLitString, + tokenize, +} from "./lexer"; +import { loadModuleFile } from "./loader"; import { ComplexMap, ComplexSet, Ids } from "./utils"; export type ParseState = { tokens: Token[]; file: LoadedFile }; @@ -46,6 +53,16 @@ export function parse( t: State, crateId: number ): Crate { + const [, items] = parseItems(t); + + const ast: Crate = buildCrate(packageName, items, crateId, t.file); + + validateAst(ast); + + return ast; +} + +function parseItems(t: State): [State, Item[]] { const items: Item[] = []; while (t.tokens.length > 0) { @@ -54,11 +71,7 @@ export function parse( items.push(item); } - const ast: Crate = buildCrate(packageName, items, crateId, t.file); - - validateAst(ast); - - return ast; + return [t, items]; } function parseItem(t: State): [State, Item] { @@ -165,18 +178,32 @@ function parseItem(t: State): [State, Item] { let name; [t, name] = expectNext(t, "identifier"); - [t] = expectNext(t, "("); + let contents: Item[] = []; - const contents: Item[] = []; + let popen = undefined; + [t, popen] = eat(t, "("); + if (popen) { + while (peekKind(t) !== ")") { + let item; + [t, item] = parseItem(t); - while (peekKind(t) !== ")") { - let item; - [t, item] = parseItem(t); + contents.push(item); + } - contents.push(item); + [t] = expectNext(t, ")"); + } else { + if (name.span.file.path === undefined) { + throw new CompilerError( + `no known source file for statement, cannot load file relative to it`, + name.span + ); + } + const file = loadModuleFile(name.span.file.path, name.ident, name.span); + + const tokens = tokenize(file); + [, contents] = parseItems({ file, tokens }); } - [t] = expectNext(t, ")"); [t] = expectNext(t, ";"); const node: ModItem = { diff --git a/std/rt.nil b/std/rt.nil new file mode 100644 index 0000000..9e826a0 --- /dev/null +++ b/std/rt.nil @@ -0,0 +1,38 @@ +// Start the heap at 1024. In practice this could probably be as low as we want. +// TODO: The compiler should set this global to whatever it has calculated the heap +// start to be. But well, 1024 ought to be enough for now. lol. +global HEAD_PTR: I32 = 1024_I32; + +// Every struct has a header of an I32 as a refcount. + +// Allocate a new item. We do not deallocate anything yet. +// lol. +function allocateItem(objSize: I32, align: I32): I32 = ( + if align < 4_I32 then std.abort("invalid alignment"); + + // Include the refcount header. + let actualSize = 4_I32 + objSize; + + // Let's see whether we can fit the refcount into the align bits. + // I happen to know that everything will always be at least 4 bytes aligned. + let alignedPtr = std.alignUp(HEAD_PTR, align); + let actualObjPtr = if (alignedPtr - HEAD_PTR) > align then ( + alignedPtr - 4_I32 + ) else ( + // Take up the next spot. + alignedPtr + align - 4_I32 + ); + + let newHeadPtr = actualObjPtr + actualSize; + + if newHeadPtr > __memory_size() then ( + // 16 pages, very arbitrary. + let result = __memory_grow(16_I32); + // If allocation failed we get -1. We don't have negative numbers yet, lol. + if result > 4294967295_I32 then ( + std.abort("failed to grow memory"); + ); + ); + + actualObjPtr +); \ No newline at end of file diff --git a/std/std.mod.nil b/std/std.mod.nil index 74a4716..8fed608 100644 --- a/std/std.mod.nil +++ b/std/std.mod.nil @@ -1,3 +1,5 @@ +mod rt; + function printlnI32(x: I32) = ( printI32(x); print("\n"); @@ -65,48 +67,6 @@ function println(s: String) = ( print("\n"); ); -mod rt ( - // Start the heap at 1024. In practice this could probably be as low as we want. - global BASE_PTR: I32 = 1024_I32; - global HEAD_PTR: I32 = 1024_I32; - - /* - Every struct has a header of an I32 as a refcount. - */ - - // Allocate a new item. We do not deallocate anything yet. - // lol. - function allocateItem(objSize: I32, align: I32): I32 = ( - if align < 4_I32 then std.abort("invalid alignment"); - - // Include the refcount header. - let actualSize = 4_I32 + objSize; - - // Let's see whether we can fit the refcount into the align bits. - // I happen to know that everything will always be at least 4 bytes aligned. - let alignedPtr = std.alignUp(HEAD_PTR, align); - let actualObjPtr = if (alignedPtr - HEAD_PTR) > align then ( - alignedPtr - 4_I32 - ) else ( - // Take up the next spot. - alignedPtr + align - 4_I32 - ); - - let newHeadPtr = actualObjPtr + actualSize; - - if newHeadPtr > __memory_size() then ( - // 16 pages, very arbitrary. - let result = __memory_grow(16_I32); - // If allocation failed we get -1. We don't have negative numbers yet, lol. - if result > 4294967295_I32 then ( - std.abort("failed to grow memory"); - ); - ); - - actualObjPtr - ); -); - function alignUp(x: I32, align: I32): I32 = (x + (align - 1_I32)) & !(align - 1_I32); function i32ToInt(x: I32): Int = __i32_extend_to_i64_u(x);