Skip to content

Commit

Permalink
linear types are now storable in variables
Browse files Browse the repository at this point in the history
  • Loading branch information
AjaniBilby committed Mar 8, 2024
1 parent 2ac5a06 commit 89fec60
Show file tree
Hide file tree
Showing 11 changed files with 273 additions and 259 deletions.
154 changes: 82 additions & 72 deletions source/compiler/codegen/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ import type { Scope } from "./scope.ts";

import * as banned from "~/compiler/codegen/banned.ts";
import Structure from "~/compiler/structure.ts";
import { IntrinsicType, i16, i8, u16, u8 } from "~/compiler/intrinsic.ts";
import { IntrinsicType, IntrinsicValue, i16, i8, u16, u8 } from "~/compiler/intrinsic.ts";
import { Instruction, AnyInstruction } from "~/wasm/index.ts";
import { AssertUnreachable, Panic } from "~/helper.ts";
import { OperandType } from "~/compiler/codegen/expression/type.ts";
import { CompileExpr } from "~/compiler/codegen/expression/index.ts";
import { none, never } from "~/compiler/intrinsic.ts";
import { Block } from "~/wasm/instruction/control-flow.ts";
import { LinearType } from "~/compiler/codegen/expression/type.ts";
import { IntrinsicVariable } from "~/compiler/codegen/variable.ts";
import { StructVariable } from "~/compiler/codegen/variable.ts";
import { Variable } from "~/compiler/codegen/variable.ts";

export class Context {
file: File;
Expand Down Expand Up @@ -75,108 +79,114 @@ function CompileDeclare(ctx: Context, syntax: Syntax.Term_Declare) {
const type = syntax.value[1].value[0];
const expr = syntax.value[2].value[0];

if (banned.namespaces.includes(name))
Panic(`${colors.red("Error")}: You're not allowed to call a variable ${name}\n`, {
path: ctx.file.path,
name: ctx.file.name,
ref: syntax.value[0].value[0].ref
})
if (banned.namespaces.includes(name)) Panic(
`${colors.red("Error")}: You're not allowed to call a variable ${name}\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.value[0].value[0].ref }
)

if (ctx.scope.hasVariable(name)) Panic(`${colors.red("Error")}: Variable ${name} is already declared\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }
);

let typeRef: Namespace | null = null;
if (type) {
typeRef = ctx.file.get(type.value[0]);

if (typeRef === null || !(typeRef instanceof IntrinsicType) && !(typeRef instanceof Structure))
Panic(`${colors.red("Error")}: Cannot find type\n`, {
path: ctx.file.path,
name: ctx.file.name,
ref: type.ref
})

if (typeRef === i8 || typeRef === u8 || typeRef === i16 || typeRef === u16)
Panic(`${colors.red("Error")}: Cannot explicitly use virtual integer types\n`, {
path: ctx.file.path,
name: ctx.file.name,
ref: type.ref
})
if (typeRef === null || !(typeRef instanceof IntrinsicType) && !(typeRef instanceof Structure)) Panic(
`${colors.red("Error")}: Cannot find type\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: type.ref }
)

if (typeRef === i8 || typeRef === u8 || typeRef === i16 || typeRef === u16) Panic(
`${colors.red("Error")}: Cannot explicitly use virtual integer types\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: type.ref }
)
}

if (!expr) {
if (!typeRef)
Panic(`${colors.red("Error")}: Declared variables must have an explicit or an inferred type\n`, {
path: ctx.file.path,
name: ctx.file.name,
ref: syntax.ref
})

const variable = ctx.scope.registerVariable(name, typeRef, syntax.ref);
if (!variable)
Panic(`${colors.red("Error")}: Variable ${name} is already declared\n`, {
path: ctx.file.path,
name: ctx.file.name,
ref: syntax.ref
});
if (!typeRef) Panic(
`${colors.red("Error")}: Declared variables must have an explicit or an inferred type\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }
)

if (typeRef instanceof Structure) {
typeRef.link();
const alloc = ctx.scope.stack.allocate(typeRef.size, typeRef.align);
const linear = LinearType.make(typeRef, alloc, ctx.file.owner.project.stackBase);

ctx.scope.registerVariable(name, linear, syntax.ref);
} else {
ctx.scope.registerVariable(name, typeRef.value, syntax.ref);
}

return;
}

const value = expr.value[0];
const resolveType = CompileExpr(ctx, value, typeRef || undefined);
if (!typeRef && !resolveType) Panic(
`${colors.red("Error")}: Unable to determine type\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }
);
if (typeRef && resolveType !== typeRef) Panic(
`${colors.red("Error")}: type ${typeRef.name} != type ${resolveType.getTypeName()}\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: type?.ref || syntax.ref }
)

if (!(resolveType instanceof IntrinsicType)) {
// TODO
// Panic(
// `${colors.red("Error")}: Cannot assign variable to non-intrinsic type\n`,
// { path: ctx.file.path, name: ctx.file.name, ref: type?.ref || syntax.ref }
// )
// Check expected, and inferred matches
if (typeRef) {
let baseType = resolveType;

return;
if ( resolveType instanceof LinearType ) baseType = resolveType.type;
if ( resolveType instanceof IntrinsicValue ) baseType = resolveType.type;

if (typeRef !== baseType) Panic(
`${colors.red("Error")}: type ${typeRef.name} != type ${resolveType.getTypeName()}\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: type?.ref || syntax.ref }
)
}

const variable = ctx.scope.registerVariable(name, typeRef || resolveType, syntax.ref);
if (!variable)
Panic(`${colors.red("Error")}: Variable ${name} is already declared\n`, {
path: ctx.file.path,
name: ctx.file.name,
ref: syntax.ref
});
if (!(resolveType instanceof IntrinsicValue || resolveType instanceof LinearType)) Panic(
`${colors.red("Error")}: Cannot assign to non-runtime type ${resolveType.getTypeName()}\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }
)

const variable = ctx.scope.registerVariable(name, resolveType, syntax.ref);
variable.markDefined();

ctx.block.push(Instruction.local.set(variable.register.ref));
if (variable instanceof IntrinsicVariable) {
ctx.block.push(Instruction.local.set(variable.register.ref));
} else if (variable instanceof StructVariable) {
// No-op for struct as value is already written to stack allocator addr
} else AssertUnreachable(variable);
}

function CompileAssign(ctx: Context, syntax: Syntax.Term_Assign) {
const name = syntax.value[0].value[0].value;
const value = syntax.value[1];

const variable = ctx.scope.getVariable(name, false);
if (!variable)
Panic(`${colors.red("Error")}: Undeclared variable ${name}\n`, {
path: ctx.file.path,
name: ctx.file.name,
ref: syntax.ref
});

const resolveType = CompileExpr(ctx, value, variable.type);
if (resolveType !== variable.type) Panic(
`${colors.red("Error")}: type ${variable.name} != type ${resolveType.getTypeName()}\n`,
if (!variable) Panic(
`${colors.red("Error")}: Undeclared variable ${name}\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }
);

if (!(resolveType instanceof IntrinsicType)) Panic(
`${colors.red("Error")}: Cannot assign variable to non-intrinsic type\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }
)
const resolveType = CompileExpr(ctx, value, variable.getBaseType());

// Check expected, and inferred match
if (variable.type) {
let baseType = resolveType;

if ( resolveType instanceof LinearType ) baseType = resolveType.type;
if ( resolveType instanceof IntrinsicValue ) baseType = resolveType.type;

if (variable.type !== baseType) Panic(
`${colors.red("Error")}: type ${variable.type.getTypeName()} != type ${resolveType.getTypeName()}\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }
)
}

if (variable instanceof IntrinsicVariable) {
ctx.block.push(Instruction.local.set(variable.register.ref));
} else if (variable instanceof StructVariable) {
// TODO: drop previous value
// TODO: move operation
console.warn(`Warn: Unimplemented struct re-assign causing unsafe no-op`);
} else AssertUnreachable(variable);


ctx.block.push(Instruction.local.set(variable.register.ref));
variable.markDefined();
}

Expand Down
74 changes: 74 additions & 0 deletions source/compiler/codegen/expression/container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import * as colors from "https://deno.land/[email protected]/fmt/colors.ts";

import type * as Syntax from "~/bnf/syntax.d.ts";
import Structure from "~/compiler/structure.ts";
import { IsContainerType, LinearType, SolidType, OperandType } from "~/compiler/codegen/expression/type.ts";
import { Panic, LatentOffset } from "~/helper.ts";
import { IntrinsicValue } from "~/compiler/intrinsic.ts";
import { CompileExpr } from "~/compiler/codegen/expression/index.ts";
import { Instruction } from "~/wasm/index.ts";
import { Context } from "~/compiler/codegen/context.ts";
import { Store } from "~/compiler/codegen/expression/helper.ts";
import { SourceView } from "~/parser.ts";

export function StructBuilder(ctx: Context, syntax: Syntax.Term_Container, expect?: SolidType): OperandType {
if (!(expect instanceof Structure)) Panic(
`${colors.red("Error")}: Unable to infer struct type\n`, {
path: ctx.file.path, name: ctx.file.name, ref: syntax.ref
});
if (expect instanceof Structure) expect.link();

const alloc = ctx.scope.stack.allocate(expect.size, expect.align);
const linear = LinearType.make(expect, alloc, ctx.file.owner.project.stackBase);

function* iterator() {
const base = syntax.value[0].value[0];
if (!base) return;

yield base.value[0]; // first
for (const next of base.value[1].value) yield next.value[0]; // comma chained

return;
}

for (const item of iterator()) {
const elm = item.value[0];
if (elm.type === "container_value") {
console.error(
`${colors.red("Error")}: Unexpected array value as struct member\n`
+ SourceView(ctx.file.path, ctx.file.name, elm.ref)
);
ctx.file.markFailure();
continue;
}

const name = elm.value[0].value[0].value;
const attr = expect.get(name);
if (!attr) Panic(
`${colors.red("Error")}: Unknown attribute ${name} in struct ${expect.name}\n`, {
path: ctx.file.path, name: ctx.file.name, ref: elm.ref
});

ctx.block.push(Instruction.const.i32(0));
const expr = CompileExpr(ctx, elm.value[1], attr.type);
if (expr instanceof IntrinsicValue) {
Store(ctx, expr.type, new LatentOffset(alloc.getOffset(), attr.offset));
} else Panic(
`${colors.red("Error")}: Only intrinsics are currently supported\n`, {
path: ctx.file.path, name: ctx.file.name, ref: elm.ref
});
}

return linear;
}

export function ArrayBuilder(ctx: Context, syntax: Syntax.Term_Container, expect?: SolidType): OperandType {
if (!expect) Panic(
`${colors.red("Error")}: Unsupported untyped container creation\n`, {
path: ctx.file.path, name: ctx.file.name, ref: syntax.ref
});

Panic(`${colors.red("Error")}: Arrays are currently unsupported\n`,
{ path: ctx.file.path, name: ctx.file.name, ref: syntax.ref }
);
}
24 changes: 12 additions & 12 deletions source/compiler/codegen/expression/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@ export function Store(ctx: Context, type: SolidType, offset: number | LatentOffs
if (!(type instanceof IntrinsicType)) Panic("Unimplemented");

switch (type.name) {
case "u32": case "i32": ctx.block.push(Instruction.i32.store(offset, 1)); break;
case "u64": case "i64": ctx.block.push(Instruction.i64.store(offset, 1)); break;
case "u8": case "i8": ctx.block.push(Instruction.i32.store8(offset, 1)); break;
case "u16": case "i16": ctx.block.push(Instruction.i32.store16(offset, 1)); break;
case "u32": case "i32": ctx.block.push(Instruction.i32.store(offset, 0)); break;
case "u64": case "i64": ctx.block.push(Instruction.i64.store(offset, 0)); break;
case "u8": case "i8": ctx.block.push(Instruction.i32.store8(offset, 0)); break;
case "u16": case "i16": ctx.block.push(Instruction.i32.store16(offset, 0)); break;
case "f32": ctx.block.push(Instruction.f32.store(offset, 1)); break;
case "f64": ctx.block.push(Instruction.f64.store(offset, 1)); break;

Expand All @@ -25,14 +25,14 @@ export function Load(ctx: Context, type: SolidType, offset: number | LatentOffse
if (!(type instanceof IntrinsicType)) Panic("Unimplemented");

switch (type.name) {
case "u32": case "i32": ctx.block.push(Instruction.i32.load(offset, 1)); break;
case "u64": case "i64": ctx.block.push(Instruction.i64.load(offset, 1)); break;
case "u8": ctx.block.push(Instruction.i32.load8_u(offset, 1)); break;
case "i8": ctx.block.push(Instruction.i32.load8_s(offset, 1)); break;
case "u16": ctx.block.push(Instruction.i32.load16_u(offset, 1)); break;
case "i16": ctx.block.push(Instruction.i32.load16_s(offset, 1)); break;
case "f32": ctx.block.push(Instruction.f32.load(offset, 1)); break;
case "f64": ctx.block.push(Instruction.f64.load(offset, 1)); break;
case "u32": case "i32": ctx.block.push(Instruction.i32.load(offset, 0)); break;
case "u64": case "i64": ctx.block.push(Instruction.i64.load(offset, 0)); break;
case "u8": ctx.block.push(Instruction.i32.load8_u(offset, 0)); break;
case "i8": ctx.block.push(Instruction.i32.load8_s(offset, 0)); break;
case "u16": ctx.block.push(Instruction.i32.load16_u(offset, 0)); break;
case "i16": ctx.block.push(Instruction.i32.load16_s(offset, 0)); break;
case "f32": ctx.block.push(Instruction.f32.load(offset, 0)); break;
case "f64": ctx.block.push(Instruction.f64.load(offset, 0)); break;

default: Panic(`Unhandled store type ${type.name}`);
}
Expand Down
Loading

0 comments on commit 89fec60

Please sign in to comment.