mirror of
https://github.com/Noratrieb/riverdelta.git
synced 2026-01-14 16:35:03 +01:00
start structs
This commit is contained in:
parent
924236532c
commit
b0b92dae0f
8 changed files with 331 additions and 30 deletions
15
src/ast.ts
15
src/ast.ts
|
|
@ -270,7 +270,13 @@ export type ExprBreak = {
|
||||||
export type ExprStructLiteral<P extends Phase> = {
|
export type ExprStructLiteral<P extends Phase> = {
|
||||||
kind: "structLiteral";
|
kind: "structLiteral";
|
||||||
name: IdentWithRes<P>;
|
name: IdentWithRes<P>;
|
||||||
fields: [Ident, Expr<P>][];
|
fields: StructLiteralField<P>[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type StructLiteralField<P extends Phase> = {
|
||||||
|
name: Ident;
|
||||||
|
expr: Expr<P>;
|
||||||
|
fieldIdx?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type TupleLiteral<P extends Phase> = {
|
export type TupleLiteral<P extends Phase> = {
|
||||||
|
|
@ -432,7 +438,7 @@ export const BUILTINS = [
|
||||||
"__string_len",
|
"__string_len",
|
||||||
"__memory_size",
|
"__memory_size",
|
||||||
"__memory_grow",
|
"__memory_grow",
|
||||||
"__i32_extend_to_i64_u"
|
"__i32_extend_to_i64_u",
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
export type BuiltinName = (typeof BUILTINS)[number];
|
export type BuiltinName = (typeof BUILTINS)[number];
|
||||||
|
|
@ -789,7 +795,10 @@ export function superFoldExpr<From extends Phase, To extends Phase>(
|
||||||
...expr,
|
...expr,
|
||||||
kind: "structLiteral",
|
kind: "structLiteral",
|
||||||
name: folder.ident(expr.name),
|
name: folder.ident(expr.name),
|
||||||
fields: expr.fields.map(([name, expr]) => [name, folder.expr(expr)]),
|
fields: expr.fields.map(({ name, expr }) => ({
|
||||||
|
name,
|
||||||
|
expr: folder.expr(expr),
|
||||||
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "tupleLiteral": {
|
case "tupleLiteral": {
|
||||||
|
|
|
||||||
15
src/index.ts
15
src/index.ts
|
|
@ -15,10 +15,21 @@ import { Ids } from "./utils";
|
||||||
const INPUT = `
|
const INPUT = `
|
||||||
extern mod std;
|
extern mod std;
|
||||||
|
|
||||||
type A = { a: String };
|
type A = { a: Int };
|
||||||
|
|
||||||
function main() = (
|
function main() = (
|
||||||
std.rt.allocateItem(0_I32, 0_I32);
|
let a = A { a: 100 };
|
||||||
|
printA(a);
|
||||||
|
);
|
||||||
|
|
||||||
|
function printA(a: A) = (
|
||||||
|
print("ABCDEFGH\\n");
|
||||||
|
std.printlnInt(a.a);
|
||||||
|
print("ABCDEFGH\\n");
|
||||||
|
);
|
||||||
|
|
||||||
|
function linkStd() = (
|
||||||
|
std.println("a");
|
||||||
);
|
);
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|
|
||||||
46
src/lower.test.ts
Normal file
46
src/lower.test.ts
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { TY_I32, TY_INT, TyStruct } from "./ast";
|
||||||
|
import { layoutOfStruct } from "./lower";
|
||||||
|
|
||||||
|
it("should compute struct layout correctly", () => {
|
||||||
|
const ty: TyStruct = {
|
||||||
|
kind: "struct",
|
||||||
|
name: "",
|
||||||
|
fields: [
|
||||||
|
["uwu", TY_I32],
|
||||||
|
["owo", TY_INT],
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const layout = layoutOfStruct(ty);
|
||||||
|
|
||||||
|
expect(layout).toMatchInlineSnapshot(`
|
||||||
|
{
|
||||||
|
"align": 8,
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"ty": {
|
||||||
|
"kind": "i32",
|
||||||
|
},
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"type": "i32",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ty": {
|
||||||
|
"kind": "int",
|
||||||
|
},
|
||||||
|
"types": [
|
||||||
|
{
|
||||||
|
"offset": 8,
|
||||||
|
"type": "i64",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"size": 16,
|
||||||
|
}
|
||||||
|
`);
|
||||||
|
});
|
||||||
242
src/lower.ts
242
src/lower.ts
|
|
@ -2,6 +2,7 @@ import {
|
||||||
Crate,
|
Crate,
|
||||||
Expr,
|
Expr,
|
||||||
ExprBlock,
|
ExprBlock,
|
||||||
|
Folder,
|
||||||
FunctionDef,
|
FunctionDef,
|
||||||
GlobalItem,
|
GlobalItem,
|
||||||
ImportDef,
|
ImportDef,
|
||||||
|
|
@ -11,9 +12,13 @@ import {
|
||||||
Resolution,
|
Resolution,
|
||||||
Ty,
|
Ty,
|
||||||
TyFn,
|
TyFn,
|
||||||
|
TyStruct,
|
||||||
TyTuple,
|
TyTuple,
|
||||||
Typecked,
|
Typecked,
|
||||||
findCrateItem,
|
findCrateItem,
|
||||||
|
mkDefaultFolder,
|
||||||
|
superFoldExpr,
|
||||||
|
superFoldItem,
|
||||||
varUnreachable,
|
varUnreachable,
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
import { printTy } from "./printer";
|
import { printTy } from "./printer";
|
||||||
|
|
@ -31,7 +36,7 @@ const WASM_PAGE = 65536;
|
||||||
|
|
||||||
const DUMMY_IDX = 9999999;
|
const DUMMY_IDX = 9999999;
|
||||||
|
|
||||||
const ALLOCATE_SYMBOL = "nil__std__rt__allocateItem";
|
const ALLOCATE_ITEM: string[] = ["std", "rt", "allocateItem"];
|
||||||
|
|
||||||
type RelocationKind =
|
type RelocationKind =
|
||||||
| {
|
| {
|
||||||
|
|
@ -57,6 +62,7 @@ export type Context = {
|
||||||
globalIndices: ComplexMap<Resolution, wasm.GlobalIdx>;
|
globalIndices: ComplexMap<Resolution, wasm.GlobalIdx>;
|
||||||
crates: Crate<Typecked>[];
|
crates: Crate<Typecked>[];
|
||||||
relocations: Relocation[];
|
relocations: Relocation[];
|
||||||
|
knownDefPaths: ComplexMap<string[], ItemId>;
|
||||||
};
|
};
|
||||||
|
|
||||||
function mangleDefPath(defPath: string[]): string {
|
function mangleDefPath(defPath: string[]): string {
|
||||||
|
|
@ -95,6 +101,8 @@ function appendData(cx: Context, newData: Uint8Array): number {
|
||||||
});
|
});
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
console.log("appending", newData);
|
||||||
|
|
||||||
const data = datas[0];
|
const data = datas[0];
|
||||||
const idx = data.init.length;
|
const idx = data.init.length;
|
||||||
const init = new Uint8Array(data.init.length + newData.length);
|
const init = new Uint8Array(data.init.length + newData.length);
|
||||||
|
|
@ -112,7 +120,45 @@ function findItem(cx: Context, id: ItemId): Item<Typecked> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const KNOWN_DEF_PATHS = [ALLOCATE_ITEM];
|
||||||
|
|
||||||
|
function getKnownDefPaths(
|
||||||
|
crates: Crate<Typecked>[]
|
||||||
|
): ComplexMap<string[], ItemId> {
|
||||||
|
const knows = new ComplexMap<string[], ItemId>();
|
||||||
|
|
||||||
|
const folder: Folder<Typecked, Typecked> = {
|
||||||
|
...mkDefaultFolder(),
|
||||||
|
itemInner(item): Item<Typecked> {
|
||||||
|
KNOWN_DEF_PATHS.forEach((path) => {
|
||||||
|
if (JSON.stringify(path) === JSON.stringify(item.defPath)) {
|
||||||
|
knows.set(path, item.id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return superFoldItem(item, this);
|
||||||
|
},
|
||||||
|
expr(expr) {
|
||||||
|
return superFoldExpr(expr, this);
|
||||||
|
},
|
||||||
|
ident(ident) {
|
||||||
|
return ident;
|
||||||
|
},
|
||||||
|
type(type) {
|
||||||
|
return type;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
crates.forEach((crate) =>
|
||||||
|
crate.rootItems.forEach((item) => folder.item(item))
|
||||||
|
);
|
||||||
|
|
||||||
|
return knows;
|
||||||
|
}
|
||||||
|
|
||||||
export function lower(crates: Crate<Typecked>[]): wasm.Module {
|
export function lower(crates: Crate<Typecked>[]): wasm.Module {
|
||||||
|
const knownDefPaths = getKnownDefPaths(crates);
|
||||||
|
|
||||||
const mod: wasm.Module = {
|
const mod: wasm.Module = {
|
||||||
types: [],
|
types: [],
|
||||||
funcs: [],
|
funcs: [],
|
||||||
|
|
@ -145,6 +191,7 @@ export function lower(crates: Crate<Typecked>[]): wasm.Module {
|
||||||
reservedHeapMemoryStart: 0,
|
reservedHeapMemoryStart: 0,
|
||||||
crates,
|
crates,
|
||||||
relocations: [],
|
relocations: [],
|
||||||
|
knownDefPaths,
|
||||||
};
|
};
|
||||||
|
|
||||||
function lowerMod(items: Item<Typecked>[]) {
|
function lowerMod(items: Item<Typecked>[]) {
|
||||||
|
|
@ -304,10 +351,16 @@ type ArgRetAbi = wasm.ValType[];
|
||||||
|
|
||||||
type VarLocation = { localIdx: number; types: wasm.ValType[] };
|
type VarLocation = { localIdx: number; types: wasm.ValType[] };
|
||||||
|
|
||||||
|
type StructFieldLayout = {
|
||||||
|
types: { offset: number; type: wasm.ValType }[];
|
||||||
|
ty: Ty;
|
||||||
|
};
|
||||||
|
|
||||||
type StructLayout = {
|
type StructLayout = {
|
||||||
size: number,
|
size: number;
|
||||||
align: number,
|
align: number;
|
||||||
}
|
fields: StructFieldLayout[];
|
||||||
|
};
|
||||||
|
|
||||||
function lowerFunc(
|
function lowerFunc(
|
||||||
cx: Context,
|
cx: Context,
|
||||||
|
|
@ -776,7 +829,53 @@ function lowerExpr(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "struct": {
|
case "struct": {
|
||||||
todo("struct field accesses");
|
const ty = expr.lhs.ty;
|
||||||
|
const layout = layoutOfStruct(ty);
|
||||||
|
const field = layout.fields[expr.field.fieldIdx!];
|
||||||
|
|
||||||
|
// TODO: SCRATCH LOCALS
|
||||||
|
const ptrLocal = fcx.wasmType.params.length + fcx.wasm.locals.length;
|
||||||
|
fcx.wasm.locals.push("i32");
|
||||||
|
|
||||||
|
// We save the local for getting it later for all the field parts.
|
||||||
|
instrs.push({
|
||||||
|
kind: "local.set",
|
||||||
|
imm: ptrLocal,
|
||||||
|
});
|
||||||
|
|
||||||
|
field.types.forEach((fieldPart) => {
|
||||||
|
instrs.push({
|
||||||
|
kind: "local.get",
|
||||||
|
imm: ptrLocal,
|
||||||
|
});
|
||||||
|
switch (fieldPart.type) {
|
||||||
|
case "i32":
|
||||||
|
instrs.push({
|
||||||
|
kind: "i32.load",
|
||||||
|
imm: {
|
||||||
|
align: sizeOfValtype(fieldPart.type),
|
||||||
|
offset: fieldPart.offset,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "i64":
|
||||||
|
instrs.push({
|
||||||
|
kind: "i64.load",
|
||||||
|
imm: {
|
||||||
|
align: sizeOfValtype(fieldPart.type),
|
||||||
|
offset: fieldPart.offset,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
throw new Error(
|
||||||
|
`unsupported struct content type: ${fieldPart.type}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new Error("invalid field access lhs");
|
throw new Error("invalid field access lhs");
|
||||||
|
|
@ -849,7 +948,70 @@ function lowerExpr(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "structLiteral": {
|
case "structLiteral": {
|
||||||
todo("struct literal");
|
if (expr.ty.kind !== "struct") {
|
||||||
|
throw new Error("struct literal must have struct type");
|
||||||
|
}
|
||||||
|
const layout = layoutOfStruct(expr.ty);
|
||||||
|
|
||||||
|
// std.rt.allocateItem(size, align);
|
||||||
|
instrs.push({ kind: "i32.const", imm: BigInt(layout.size) });
|
||||||
|
instrs.push({ kind: "i32.const", imm: BigInt(layout.align) });
|
||||||
|
const allocate: wasm.Instr = { kind: "call", func: DUMMY_IDX };
|
||||||
|
const allocateItemId = fcx.cx.knownDefPaths.get(ALLOCATE_ITEM);
|
||||||
|
if (!allocateItemId) {
|
||||||
|
throw new Error("std.rt.allocateItem not found");
|
||||||
|
}
|
||||||
|
fcx.cx.relocations.push({
|
||||||
|
kind: "funccall",
|
||||||
|
instr: allocate,
|
||||||
|
res: { kind: "item", id: allocateItemId },
|
||||||
|
});
|
||||||
|
instrs.push(allocate);
|
||||||
|
// TODO: scratch locals...
|
||||||
|
const ptrLocal = fcx.wasmType.params.length + fcx.wasm.locals.length;
|
||||||
|
fcx.wasm.locals.push("i32");
|
||||||
|
instrs.push({ kind: "local.set", imm: ptrLocal });
|
||||||
|
|
||||||
|
// Now, set all fields.
|
||||||
|
expr.fields.forEach((field, i) => {
|
||||||
|
instrs.push({ kind: "local.get", imm: ptrLocal });
|
||||||
|
lowerExpr(fcx, instrs, field.expr);
|
||||||
|
|
||||||
|
const fieldLayout = [...layout.fields[i].types];
|
||||||
|
fieldLayout.reverse();
|
||||||
|
fieldLayout.forEach((fieldPart) => {
|
||||||
|
switch (fieldPart.type) {
|
||||||
|
case "i32":
|
||||||
|
instrs.push({
|
||||||
|
kind: "i32.store",
|
||||||
|
imm: {
|
||||||
|
align: sizeOfValtype(fieldPart.type),
|
||||||
|
offset: fieldPart.offset,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case "i64":
|
||||||
|
instrs.push({
|
||||||
|
kind: "i64.store",
|
||||||
|
imm: {
|
||||||
|
align: sizeOfValtype(fieldPart.type),
|
||||||
|
offset: fieldPart.offset,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
throw new Error(
|
||||||
|
`unsupported struct content type: ${fieldPart.type}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Last, load the pointer and pass that on.
|
||||||
|
instrs.push({ kind: "local.get", imm: ptrLocal });
|
||||||
|
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
case "tupleLiteral": {
|
case "tupleLiteral": {
|
||||||
expr.fields.forEach((field) => lowerExpr(fcx, instrs, field));
|
expr.fields.forEach((field) => lowerExpr(fcx, instrs, field));
|
||||||
|
|
@ -931,7 +1093,7 @@ function argRetAbi(param: Ty): ArgRetAbi {
|
||||||
case "tuple":
|
case "tuple":
|
||||||
return param.elems.flatMap(argRetAbi);
|
return param.elems.flatMap(argRetAbi);
|
||||||
case "struct":
|
case "struct":
|
||||||
todo("struct ABI");
|
return ["i32"];
|
||||||
case "never":
|
case "never":
|
||||||
return [];
|
return [];
|
||||||
case "var":
|
case "var":
|
||||||
|
|
@ -981,7 +1143,7 @@ function wasmTypeForBody(ty: Ty): wasm.ValType[] {
|
||||||
case "fn":
|
case "fn":
|
||||||
todo("fn types");
|
todo("fn types");
|
||||||
case "struct":
|
case "struct":
|
||||||
todo("struct types");
|
return ["i32"];
|
||||||
case "never":
|
case "never":
|
||||||
return [];
|
return [];
|
||||||
case "var":
|
case "var":
|
||||||
|
|
@ -989,6 +1151,70 @@ function wasmTypeForBody(ty: Ty): wasm.ValType[] {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sizeOfValtype(type: wasm.ValType): number {
|
||||||
|
switch (type) {
|
||||||
|
case "i32":
|
||||||
|
case "f32":
|
||||||
|
return 4;
|
||||||
|
case "i64":
|
||||||
|
case "f64":
|
||||||
|
return 8;
|
||||||
|
case "v128":
|
||||||
|
case "funcref":
|
||||||
|
case "externref":
|
||||||
|
throw new Error("types not emitted");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function layoutOfStruct(ty: TyStruct): StructLayout {
|
||||||
|
const fieldWasmTys = ty.fields.map(([, field]) => wasmTypeForBody(field));
|
||||||
|
|
||||||
|
const align = fieldWasmTys.some((field) =>
|
||||||
|
field.some((type) => type === "i64")
|
||||||
|
)
|
||||||
|
? 8
|
||||||
|
: 4;
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
const fields: StructFieldLayout[] = fieldWasmTys.map((field, i) => {
|
||||||
|
const value: StructFieldLayout = {
|
||||||
|
types: [],
|
||||||
|
ty: ty.fields[i][1],
|
||||||
|
};
|
||||||
|
|
||||||
|
const types = field.map((type) => {
|
||||||
|
const size = sizeOfValtype(type);
|
||||||
|
|
||||||
|
if (size === 8 && offset % 8 !== 0) {
|
||||||
|
// padding.
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
const fieldPart = {
|
||||||
|
offset,
|
||||||
|
type,
|
||||||
|
};
|
||||||
|
offset += size;
|
||||||
|
return fieldPart;
|
||||||
|
});
|
||||||
|
|
||||||
|
value.types = types;
|
||||||
|
|
||||||
|
return value;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (align === 8 && offset % 8 !== 0) {
|
||||||
|
offset += 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
size: offset,
|
||||||
|
align,
|
||||||
|
fields,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function blockTypeForBody(cx: Context, ty: Ty): wasm.Blocktype {
|
function blockTypeForBody(cx: Context, ty: Ty): wasm.Blocktype {
|
||||||
const typeIdx = internFuncType(cx, {
|
const typeIdx = internFuncType(cx, {
|
||||||
params: [],
|
params: [],
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,9 @@ import {
|
||||||
ExternItem,
|
ExternItem,
|
||||||
ItemId,
|
ItemId,
|
||||||
GlobalItem,
|
GlobalItem,
|
||||||
|
StructLiteralField,
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
import { CompilerError, DUMMY_SPAN, EOF_SPAN, Span, spanMerge } from "./error";
|
import { CompilerError, EOF_SPAN, Span, spanMerge } from "./error";
|
||||||
import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer";
|
import { BaseToken, Token, TokenIdent, TokenLitString } from "./lexer";
|
||||||
import { ComplexMap, ComplexSet, Ids } from "./utils";
|
import { ComplexMap, ComplexSet, Ids } from "./utils";
|
||||||
|
|
||||||
|
|
@ -540,15 +541,19 @@ function parseStructInit(
|
||||||
[t] = expectNext(t, "{");
|
[t] = expectNext(t, "{");
|
||||||
|
|
||||||
let fields;
|
let fields;
|
||||||
[t, fields] = parseCommaSeparatedList<[Ident, Expr<Parsed>]>(t, "}", (t) => {
|
[t, fields] = parseCommaSeparatedList<StructLiteralField<Parsed>>(
|
||||||
let name;
|
t,
|
||||||
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
"}",
|
||||||
[t] = expectNext(t, ":");
|
(t) => {
|
||||||
let expr;
|
let name;
|
||||||
[t, expr] = parseExpr(t);
|
[t, name] = expectNext<TokenIdent>(t, "identifier");
|
||||||
|
[t] = expectNext(t, ":");
|
||||||
|
let expr;
|
||||||
|
[t, expr] = parseExpr(t);
|
||||||
|
|
||||||
return [t, [{ name: name.ident, span: name.span }, expr]];
|
return [t, { name: { name: name.ident, span: name.span }, expr }];
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return [t, fields];
|
return [t, fields];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -187,7 +187,7 @@ function printExpr(expr: Expr<AnyPhase>, indent: number): string {
|
||||||
}
|
}
|
||||||
case "structLiteral": {
|
case "structLiteral": {
|
||||||
return `${printIdent(expr.name)} { ${expr.fields
|
return `${printIdent(expr.name)} { ${expr.fields
|
||||||
.map(([name, expr]) => `${name.name}: ${printExpr(expr, indent + 1)}`)
|
.map(({ name, expr }) => `${name.name}: ${printExpr(expr, indent + 1)}`)
|
||||||
.join(", ")} }`;
|
.join(", ")} }`;
|
||||||
}
|
}
|
||||||
case "tupleLiteral": {
|
case "tupleLiteral": {
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ import {
|
||||||
ExprUnary,
|
ExprUnary,
|
||||||
foldAst,
|
foldAst,
|
||||||
Folder,
|
Folder,
|
||||||
Ident,
|
|
||||||
IdentWithRes,
|
IdentWithRes,
|
||||||
ItemId,
|
ItemId,
|
||||||
LOGICAL_KINDS,
|
LOGICAL_KINDS,
|
||||||
|
|
@ -30,6 +29,7 @@ import {
|
||||||
TyStruct,
|
TyStruct,
|
||||||
Item,
|
Item,
|
||||||
findCrateItem,
|
findCrateItem,
|
||||||
|
StructLiteralField,
|
||||||
} from "./ast";
|
} from "./ast";
|
||||||
import { CompilerError, Span } from "./error";
|
import { CompilerError, Span } from "./error";
|
||||||
import { printTy } from "./printer";
|
import { printTy } from "./printer";
|
||||||
|
|
@ -943,8 +943,8 @@ export function checkBody(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "structLiteral": {
|
case "structLiteral": {
|
||||||
const fields = expr.fields.map<[Ident, Expr<Typecked>]>(
|
const fields = expr.fields.map<StructLiteralField<Typecked>>(
|
||||||
([name, expr]) => [name, this.expr(expr)]
|
({ name, expr }) => ({ name, expr: this.expr(expr) })
|
||||||
);
|
);
|
||||||
|
|
||||||
const structTy = typeOf(expr.name.res, expr.name.span);
|
const structTy = typeOf(expr.name.res, expr.name.span);
|
||||||
|
|
@ -958,16 +958,20 @@ export function checkBody(
|
||||||
|
|
||||||
const assignedFields = new Set();
|
const assignedFields = new Set();
|
||||||
|
|
||||||
fields.forEach(([name, field]) => {
|
fields.forEach(({ name, expr: field }, i) => {
|
||||||
const fieldTy = structTy.fields.find((def) => def[0] === name.name);
|
const fieldIdx = structTy.fields.findIndex(
|
||||||
if (!fieldTy) {
|
(def) => def[0] === name.name
|
||||||
|
);
|
||||||
|
if (fieldIdx == -1) {
|
||||||
throw new CompilerError(
|
throw new CompilerError(
|
||||||
`field ${name.name} doesn't exist on type ${expr.name.name}`,
|
`field ${name.name} doesn't exist on type ${expr.name.name}`,
|
||||||
name.span
|
name.span
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
const fieldTy = structTy.fields[fieldIdx];
|
||||||
infcx.assign(fieldTy[1], field.ty, field.span);
|
infcx.assign(fieldTy[1], field.ty, field.span);
|
||||||
assignedFields.add(name.name);
|
assignedFields.add(name.name);
|
||||||
|
fields[i].fieldIdx = fieldIdx;
|
||||||
});
|
});
|
||||||
|
|
||||||
const missing: string[] = [];
|
const missing: string[] = [];
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ function printBinaryString(buf: Uint8Array, f: FmtCtx) {
|
||||||
|
|
||||||
buf.forEach((byte) => {
|
buf.forEach((byte) => {
|
||||||
const noEscape =
|
const noEscape =
|
||||||
(byte > 0x30 && byte <= 0x5a) || (byte > 0x61 && byte <= 0x71);
|
(byte > 0x30 && byte <= 0x5a) || (byte >= 0x61 && byte <= 0x7a);
|
||||||
if (noEscape) {
|
if (noEscape) {
|
||||||
parts.push(`${String.fromCharCode(byte)}`);
|
parts.push(`${String.fromCharCode(byte)}`);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue