mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 08:25:02 +01:00
allocators and type aliases
This commit is contained in:
parent
60c743d656
commit
f05e5520f3
16 changed files with 283 additions and 121 deletions
|
|
@ -51,6 +51,7 @@ module.exports = {
|
|||
// This lint is horrible with noisy false positives every time there are typescript errors.
|
||||
"@typescript-eslint/no-unsafe-return": "off",
|
||||
"@typescript-eslint/no-unsafe-assignment": "off",
|
||||
"eslint@typescript-eslint/no-unsafe-argument": "off",
|
||||
|
||||
// Useful extra lints that are not on by default:
|
||||
"@typescript-eslint/explicit-module-boundary-types": "warn",
|
||||
|
|
|
|||
36
src/ast.ts
36
src/ast.ts
|
|
@ -144,10 +144,20 @@ export type FunctionArg<P extends Phase> = {
|
|||
|
||||
export type TypeDef<P extends Phase> = {
|
||||
name: string;
|
||||
fields: FieldDef<P>[];
|
||||
type: TypeDefKind<P>;
|
||||
ty?: TyStruct;
|
||||
};
|
||||
|
||||
export type TypeDefKind<P extends Phase> =
|
||||
| {
|
||||
kind: "struct";
|
||||
fields: FieldDef<P>[];
|
||||
}
|
||||
| {
|
||||
kind: "alias";
|
||||
type: Type<P>;
|
||||
};
|
||||
|
||||
export type FieldDef<P extends Phase> = {
|
||||
name: Ident;
|
||||
type: Type<P>;
|
||||
|
|
@ -624,15 +634,29 @@ export function superFoldItem<From extends Phase, To extends Phase>(
|
|||
};
|
||||
}
|
||||
case "type": {
|
||||
const fields = item.node.fields.map(({ name, type }) => ({
|
||||
name,
|
||||
type: folder.type(type),
|
||||
}));
|
||||
const typeKind = item.node.type;
|
||||
let type: TypeDefKind<To>;
|
||||
switch (typeKind.kind) {
|
||||
case "struct": {
|
||||
const fields = typeKind.fields.map(({ name, type }) => ({
|
||||
name,
|
||||
type: folder.type(type),
|
||||
}));
|
||||
type = { kind: "struct", fields };
|
||||
break;
|
||||
}
|
||||
case "alias": {
|
||||
type = {
|
||||
kind: "alias",
|
||||
type: folder.type(typeKind.type),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...item,
|
||||
kind: "type",
|
||||
node: { name: item.node.name, fields },
|
||||
node: { name: item.node.name, type },
|
||||
};
|
||||
}
|
||||
case "import": {
|
||||
|
|
|
|||
|
|
@ -36,8 +36,8 @@ const WASM_PAGE = 65536;
|
|||
|
||||
const DUMMY_IDX = 9999999;
|
||||
|
||||
const ALLOCATE_ITEM: string[] = ["std", "rt", "allocateItem"];
|
||||
const DEALLOCATE_ITEM: string[] = ["std", "rt", "deallocateItem"];
|
||||
const ALLOCATE_ITEM: string[] = ["std", "rt", "alloc", "allocateItem"];
|
||||
const DEALLOCATE_ITEM: string[] = ["std", "rt", "alloc", "deallocateItem"];
|
||||
|
||||
type RelocationKind =
|
||||
| {
|
||||
|
|
|
|||
|
|
@ -79,7 +79,11 @@ export function parseArgs(hardcodedInput: string): Options {
|
|||
}
|
||||
|
||||
input = fs.readFileSync(filename, { encoding: "utf-8" });
|
||||
packageName = path.basename(filename, ".nil");
|
||||
if (filename.endsWith(".mod.nil")) {
|
||||
packageName = path.basename(filename, ".mod.nil");
|
||||
} else {
|
||||
packageName = path.basename(filename, ".nil");
|
||||
}
|
||||
|
||||
const debugArg = process.argv.find((arg) => arg.startsWith("--debug="));
|
||||
if (debugArg !== undefined) {
|
||||
|
|
|
|||
13
src/index.ts
13
src/index.ts
|
|
@ -13,9 +13,14 @@ import { GlobalContext, parseArgs } from "./context";
|
|||
import { loadCrate } from "./loader";
|
||||
|
||||
const INPUT = `
|
||||
type A = { a: Int };
|
||||
type A = struct { a: Int };
|
||||
|
||||
type What = What;
|
||||
|
||||
type Uwu = (Int, Int);
|
||||
|
||||
function main() = (
|
||||
let a: What = 0;
|
||||
uwu();
|
||||
);
|
||||
|
||||
|
|
@ -28,7 +33,7 @@ function uwu() = (
|
|||
/*-1*/
|
||||
);
|
||||
|
||||
type B = {
|
||||
type B = struct {
|
||||
a: (Int, Int, Int, Int, Int),
|
||||
};
|
||||
|
||||
|
|
@ -59,7 +64,9 @@ function main() {
|
|||
() => {
|
||||
const start = Date.now();
|
||||
|
||||
gcx.crateLoader(gcx, "std", Span.startOfFile(file));
|
||||
if (packageName !== "std") {
|
||||
gcx.crateLoader(gcx, "std", Span.startOfFile(file));
|
||||
}
|
||||
|
||||
const tokens = tokenize(file);
|
||||
if (debug.has("tokens")) {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export type DatalessToken =
|
|||
| "extern"
|
||||
| "mod"
|
||||
| "global"
|
||||
| "struct"
|
||||
| "("
|
||||
| ")"
|
||||
| "{"
|
||||
|
|
@ -323,6 +324,7 @@ const KEYOWRDS: DatalessToken[] = [
|
|||
"extern",
|
||||
"mod",
|
||||
"global",
|
||||
"struct",
|
||||
];
|
||||
|
||||
const KEYWORD_SET = new Set<string>(KEYOWRDS);
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ import {
|
|||
ItemId,
|
||||
GlobalItem,
|
||||
StructLiteralField,
|
||||
TypeDefKind,
|
||||
} from "./ast";
|
||||
import { CompilerError, LoadedFile, Span } from "./error";
|
||||
import {
|
||||
|
|
@ -107,32 +108,46 @@ function parseItem(t: State): [State, Item<Parsed>] {
|
|||
let name;
|
||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||
[t] = expectNext(t, "=");
|
||||
[t] = expectNext(t, "{");
|
||||
|
||||
let fields;
|
||||
[t, fields] = parseCommaSeparatedList<FieldDef<Parsed>>(t, "}", (t) => {
|
||||
let name;
|
||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||
[t] = expectNext(t, ":");
|
||||
let type;
|
||||
[t, type] = parseType(t);
|
||||
return [
|
||||
t,
|
||||
{
|
||||
name: {
|
||||
name: name.ident,
|
||||
span: name.span,
|
||||
let type: TypeDefKind<Parsed>;
|
||||
|
||||
let struct;
|
||||
[t, struct] = eat(t, "struct");
|
||||
if (struct) {
|
||||
[t] = expectNext(t, "{");
|
||||
|
||||
let fields;
|
||||
[t, fields] = parseCommaSeparatedList<FieldDef<Parsed>>(t, "}", (t) => {
|
||||
let name;
|
||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||
[t] = expectNext(t, ":");
|
||||
let type;
|
||||
[t, type] = parseType(t);
|
||||
return [
|
||||
t,
|
||||
{
|
||||
name: {
|
||||
name: name.ident,
|
||||
span: name.span,
|
||||
},
|
||||
type,
|
||||
},
|
||||
type,
|
||||
},
|
||||
];
|
||||
});
|
||||
];
|
||||
});
|
||||
|
||||
type = { kind: "struct", fields };
|
||||
} else {
|
||||
let aliased: Type<Parsed>;
|
||||
[t, aliased] = parseType(t);
|
||||
|
||||
type = { kind: "alias", type: aliased };
|
||||
}
|
||||
|
||||
[t] = expectNext(t, ";");
|
||||
|
||||
const def: TypeDef<Parsed> = {
|
||||
name: name.ident,
|
||||
fields,
|
||||
type,
|
||||
};
|
||||
|
||||
return [
|
||||
|
|
|
|||
|
|
@ -63,14 +63,22 @@ function printFunction(func: FunctionDef<AnyPhase>): string {
|
|||
}
|
||||
|
||||
function printTypeDef(type: TypeDef<AnyPhase>): string {
|
||||
const fields = type.fields.map(
|
||||
({ name, type }) => `${ind(1)}${name.name}: ${printType(type)},`,
|
||||
);
|
||||
switch (type.type.kind) {
|
||||
case "struct": {
|
||||
const { fields } = type.type;
|
||||
|
||||
const fieldPart =
|
||||
type.fields.length === 0 ? "{}" : `{\n${fields.join("\n")}\n}`;
|
||||
const fieldStr = fields.map(
|
||||
({ name, type }) => `${ind(1)}${name.name}: ${printType(type)},`,
|
||||
);
|
||||
const fieldPart =
|
||||
fields.length === 0 ? "{}" : `{\n${fieldStr.join("\n")}\n}`;
|
||||
|
||||
return `type ${type.name} = ${fieldPart};`;
|
||||
return `type ${type.name} = ${fieldPart};`;
|
||||
}
|
||||
case "alias": {
|
||||
return `type ${type.name} = ${printType(type.type.type)}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function printImportDef(def: ImportDef<AnyPhase>): string {
|
||||
|
|
|
|||
|
|
@ -175,23 +175,29 @@ export function typeck(
|
|||
return ty;
|
||||
}
|
||||
case "type": {
|
||||
const ty: Ty = {
|
||||
kind: "struct",
|
||||
name: item.node.name,
|
||||
fields: [
|
||||
/*dummy*/
|
||||
],
|
||||
};
|
||||
switch (item.node.type.kind) {
|
||||
case "struct": {
|
||||
const ty: Ty = {
|
||||
kind: "struct",
|
||||
name: item.node.name,
|
||||
fields: [
|
||||
/*dummy*/
|
||||
],
|
||||
};
|
||||
|
||||
itemTys.set(item.id, ty);
|
||||
itemTys.set(item.id, ty);
|
||||
|
||||
const fields = item.node.fields.map<[string, Ty]>(({ name, type }) => [
|
||||
name.name,
|
||||
lowerAstTy(type),
|
||||
]);
|
||||
const fields = item.node.type.fields.map<[string, Ty]>(
|
||||
({ name, type }) => [name.name, lowerAstTy(type)],
|
||||
);
|
||||
|
||||
ty.fields = fields;
|
||||
return ty;
|
||||
ty.fields = fields;
|
||||
return ty;
|
||||
}
|
||||
case "alias": {
|
||||
return lowerAstTy(item.node.type.type);
|
||||
}
|
||||
}
|
||||
}
|
||||
case "mod": {
|
||||
throw new CompilerError(
|
||||
|
|
@ -313,32 +319,51 @@ export function typeck(
|
|||
};
|
||||
}
|
||||
case "type": {
|
||||
const fieldNames = new Set();
|
||||
item.node.fields.forEach(({ name }) => {
|
||||
if (fieldNames.has(name)) {
|
||||
throw new CompilerError(
|
||||
`type ${item.node.name} has a duplicate field: ${name.name}`,
|
||||
name.span,
|
||||
);
|
||||
}
|
||||
fieldNames.add(name);
|
||||
});
|
||||
switch (item.node.type.kind) {
|
||||
case "struct": {
|
||||
const fieldNames = new Set();
|
||||
item.node.type.fields.forEach(({ name }) => {
|
||||
if (fieldNames.has(name)) {
|
||||
throw new CompilerError(
|
||||
`type ${item.node.name} has a duplicate field: ${name.name}`,
|
||||
name.span,
|
||||
);
|
||||
}
|
||||
fieldNames.add(name);
|
||||
});
|
||||
|
||||
const ty = typeOfItem(item.id, item.span) as TyStruct;
|
||||
const ty = typeOfItem(item.id, item.span) as TyStruct;
|
||||
|
||||
return {
|
||||
...item,
|
||||
node: {
|
||||
name: item.node.name,
|
||||
fields: item.node.fields.map((field, i) => ({
|
||||
name: field.name,
|
||||
type: {
|
||||
...field.type,
|
||||
ty: ty.fields[i][1],
|
||||
return {
|
||||
...item,
|
||||
node: {
|
||||
name: item.node.name,
|
||||
type: {
|
||||
kind: "struct",
|
||||
fields: item.node.type.fields.map((field, i) => ({
|
||||
name: field.name,
|
||||
type: {
|
||||
...field.type,
|
||||
ty: ty.fields[i][1],
|
||||
},
|
||||
})),
|
||||
},
|
||||
},
|
||||
})),
|
||||
},
|
||||
};
|
||||
};
|
||||
}
|
||||
case "alias": {
|
||||
return {
|
||||
...item,
|
||||
node: {
|
||||
name: item.node.name,
|
||||
type: {
|
||||
kind: "alias",
|
||||
type: item.node.type.type,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
case "mod": {
|
||||
return {
|
||||
|
|
|
|||
44
std/rt.nil
44
std/rt.nil
|
|
@ -1,44 +0,0 @@
|
|||
// 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");
|
||||
);
|
||||
);
|
||||
|
||||
HEAD_PTR = newHeadPtr;
|
||||
|
||||
actualObjPtr
|
||||
);
|
||||
|
||||
function deallocateItem(ptr: I32, objSize: I32) = (
|
||||
std.println("uwu deawwocate :3");
|
||||
);
|
||||
112
std/rt/alloc.nil
Normal file
112
std/rt/alloc.nil
Normal file
|
|
@ -0,0 +1,112 @@
|
|||
// 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");
|
||||
);
|
||||
);
|
||||
|
||||
HEAD_PTR = newHeadPtr;
|
||||
|
||||
actualObjPtr
|
||||
);
|
||||
|
||||
function deallocateItem(ptr: I32, objSize: I32) = (
|
||||
std.println("uwu deawwocate :3");
|
||||
);
|
||||
|
||||
// Port of https://github.com/CCareaga/heap_allocator
|
||||
//
|
||||
// MIT License
|
||||
//
|
||||
// Copyright (c) 2017 Chris Careaga
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
global HEAP_START: I32 = 2048_I32;
|
||||
// heap size = start+end+bin_t*BIN_COUNT
|
||||
// 4+ 4+ 4* 9 = 8+36=42 (round to 64)
|
||||
global HEAP_REGION_START: I32 = 2112_I32;
|
||||
|
||||
// typedef struct node_t {
|
||||
// uint hole;
|
||||
// uint size;
|
||||
// struct node_t* next;
|
||||
// struct node_t* prev;
|
||||
// } node_t;
|
||||
|
||||
// typedef struct {
|
||||
// node_t *header;
|
||||
// } footer_t;
|
||||
|
||||
// typedef struct {
|
||||
// node_t* head;
|
||||
// } bin_t;
|
||||
|
||||
global SIZEOF_NODE: I32 = 16_I32;
|
||||
global SIZEOF_FOOTER: I32 = 4_I32;
|
||||
|
||||
function initHeap() = (
|
||||
let heap_init_size = 65536_I32 - HEAP_REGION_START;
|
||||
|
||||
__i32_store(HEAP_REGION_START, 1_I32); // START.hole =
|
||||
__i32_store(HEAP_REGION_START + 4_I32, heap_init_size - SIZEOF_NODE - SIZEOF_FOOTER); // START.size =
|
||||
);
|
||||
|
||||
function createFoot(head_node: I32) = (
|
||||
let foot = getFoot(head_node);
|
||||
__i32_store(foot, head_node); // foot.header = head_node
|
||||
);
|
||||
|
||||
function getFoot(node: I32): I32 = (
|
||||
let node_size = __i32_load(node + 4_I32); // node.size
|
||||
node + SIZEOF_NODE + node_size
|
||||
);
|
||||
|
||||
function addNode(bin: I32, node: I32) = ;
|
||||
|
||||
function test() =;
|
||||
1
std/rt/rt.mod.nil
Normal file
1
std/rt/rt.mod.nil
Normal file
|
|
@ -0,0 +1 @@
|
|||
mod alloc;
|
||||
|
|
@ -79,5 +79,5 @@ function abort(message: String) = (
|
|||
);
|
||||
|
||||
function main() = (
|
||||
std.rt.allocateItem(100000000_I32, 8_I32);
|
||||
std.rt.alloc.test();
|
||||
);
|
||||
7
ui-tests/ui/type_alias.nil
Normal file
7
ui-tests/ui/type_alias.nil
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
//@check-pass
|
||||
|
||||
type A = (Int, Int);
|
||||
|
||||
function main() = (
|
||||
let a: A = (0, 0);
|
||||
);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
//@check-pass
|
||||
|
||||
type CustomType = {};
|
||||
type CustomType = struct {};
|
||||
|
||||
function main() = ();
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
"patterns": [
|
||||
{
|
||||
"name": "keyword.control.riverdelta",
|
||||
"match": "\\b(function|let|if|then|else|type|loop|break|import|extern|mod|global)\\b"
|
||||
"match": "\\b(function|let|if|then|else|type|loop|break|import|extern|mod|global|struct)\\b"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue