added interpreter

This commit is contained in:
2025-08-31 23:06:26 -06:00
parent dd4f5b9ee6
commit 126524a9e9
5 changed files with 478 additions and 4 deletions

View File

@@ -6,6 +6,7 @@ import { TypeAliasResolver } from "../types/type_alias_resolution";
import { TypeSystem } from "../types/type_system";
import { TypeChecker } from "../types/type_checker";
import { TypeResolver } from "../types/type_resolver";
import { TreeWalkInterpreter } from "../interpreter";
export const run = defineCommand({
name: "run",
@@ -33,7 +34,9 @@ export const run = defineCommand({
typeChecker.withModule(aliasResolvedAst, typeSystem);
typeSystem.solve();
const typeResolvedAst = typeResolver.withModule(aliasResolvedAst, typeSystem);
console.log(JSON.stringify(typeResolvedAst, null, 2));
const interpreter = new TreeWalkInterpreter();
const result = interpreter.withModule(typeResolvedAst);
console.log(JSON.stringify(result, null, 2));
// console.log(JSON.stringify(aliasResolvedAst, null, 2));
} else {

View File

@@ -0,0 +1,92 @@
import { Module } from "../parse/ast";
import { Context, FunctionRef } from "./context";
export function contextFromModule(module: Module): Context {
const ctx: Context = {
environment: {},
currentModule: module,
};
ctx.environment["i8"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["i16"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["i32"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["i64"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["f8"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["f16"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["f32"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["f64"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["String"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["Void"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
ctx.environment["Never"] = { namedEntity: "NamedType", isA: "Scalar", fields: {}, impls: [] };
// add functions, structs, and traits to the context
for (const item of module.items) {
if (item.moduleItem === "StructTypeDeclaration") {
ctx.environment[item.name.text] = {
namedEntity: "NamedType",
isA: "Struct",
fields: Object.fromEntries(item.fields.map((field) => [field.name.text, field.type])),
impls: [],
};
}
if (item.moduleItem === "TraitTypeDeclaration") {
ctx.environment[item.name.text] = {
namedEntity: "NamedType",
isA: "Trait",
fields: {},
impls: [
{
trait: item.name.text,
functions: Object.fromEntries(
item.functions.map((fn) => {
return [
fn.name,
{
functionType: "UserFunction",
function: fn,
},
];
}),
),
},
],
};
}
if (item.moduleItem === "Function") {
ctx.environment[item.declaration.name.text] = {
namedEntity: "Variable",
value: {
value: "FunctionValue",
partial: [],
ref: {
functionType: "UserFunction",
function: item,
},
},
};
}
}
// now that structs and traits are added, add impls
for (const item of module.items) {
if (item.moduleItem === "Impl") {
const struct = ctx.environment[item.struct.name.text];
if (!struct || struct.namedEntity !== "NamedType" || struct.isA !== "Struct") {
throw Error("Impl for non-struct");
}
const functions: Record<string, FunctionRef> = {};
for (const fn of item.functions) {
if (
fn.declaration.arguments.length &&
fn.declaration.arguments[0].type.typeUsage == "NamedTypeUsage" &&
fn.declaration.arguments[0].type.name.text === item.struct.name.text
) {
functions[fn.declaration.name.text] = { functionType: "UserFunction", function: fn };
}
}
struct.impls.push({
trait: item.trait?.name.text ?? null,
functions: functions,
});
}
}
return ctx;
}

View File

@@ -0,0 +1,74 @@
import { TypeUsage, Function, Module } from "../parse/ast";
export interface NumericValue {
value: "NumericValue";
number: number;
}
export interface BoolValue {
value: "BoolValue";
bool: boolean;
}
export interface StringValue {
value: "StringValue";
string: string;
}
export interface UserFunction {
functionType: "UserFunction";
function: Function;
}
export interface BuiltinFunction {
functionType: "BuiltinFunction";
function: (value: Value[]) => Value;
}
export type FunctionRef = UserFunction | BuiltinFunction;
export interface FunctionValue {
value: "FunctionValue";
partial: Value[];
ref: FunctionRef;
}
export interface UnitValue {
value: "UnitValue";
}
export interface StructValue {
value: "StructValue";
source: NamedType;
fields: Record<string, Value>;
}
export type Value =
| NumericValue
| BoolValue
| StringValue
| FunctionValue
| StructValue
| UnitValue;
export interface Variable {
namedEntity: "Variable";
value: Value;
}
export interface EnvImpl {
trait: string | null;
functions: Record<string, FunctionRef>;
}
export interface NamedType {
namedEntity: "NamedType";
isA: "Scalar" | "Trait" | "Struct";
fields: Record<string, TypeUsage>;
impls: EnvImpl[];
}
export interface Context {
environment: Record<string, NamedType | Variable>;
currentModule: Module;
}

View File

@@ -0,0 +1,305 @@
import {
AssignmentStatement,
Block,
Expression,
Function,
FunctionCall,
LetStatement,
Module,
Operation,
ReturnStatement,
StructGetter,
StructTypeDeclaration,
TypeUsage,
} from "../parse/ast";
import { contextFromModule } from "./builtins";
import { Context, Value } from "./context";
interface ExpressionResultValue {
resultType: "Value";
value: Value;
}
interface ExpressionResultReturn {
resultType: "Return";
value: Value;
}
type ExpressionResult = ExpressionResultValue | ExpressionResultReturn;
export class TreeWalkInterpreter {
withModule = (module: Module) => {
const ctx = contextFromModule(module);
const main = ctx.environment["main"];
if (
!main ||
!(main.namedEntity === "Variable") ||
!(main.value.value === "FunctionValue") ||
!(main.value.ref.functionType === "UserFunction")
) {
throw Error("No main function");
}
return this.withFunction(ctx, main.value.ref.function);
};
withFunction = (ctx: Context, fn: Function): Value => {
let result = this.withBlock(ctx, fn.block);
return result.value;
};
withBlock = (ctx: Context, block: Block): ExpressionResult => {
let last: ExpressionResult = { resultType: "Value", value: { value: "UnitValue" } };
for (const statement of block.statements) {
if (statement.statementType === "AssignmentStatement") {
last = this.withAssignmentStatement(ctx, statement);
}
if (statement.statementType === "LetStatement") {
last = this.withLetStatement(ctx, statement);
}
if (statement.statementType === "Expression") {
last = this.withExpression(ctx, statement);
}
if (statement.statementType === "ReturnStatement") {
last = this.withReturnStatement(ctx, statement);
}
if (last.resultType === "Return") {
return last;
}
}
return last;
};
withAssignmentStatement = (ctx: Context, statement: AssignmentStatement): ExpressionResult => {
let result = this.withExpression(ctx, statement.expression);
if (result.resultType === "Return") {
return result;
}
if (statement.source.expressionType == "VariableUsage") {
ctx.environment[statement.source.name.text] = {
namedEntity: "Variable",
value: result.value,
};
}
if (statement.source.expressionType == "StructGetter") {
let source = this.withStructGetter(ctx, statement.source);
if (source.resultType === "Return") {
return source;
}
if (source.value.value !== "StructValue") {
throw Error("set attr on nonstruct, should never happen due to type system");
}
source.value.fields[statement.source.attribute.text] = result.value;
}
return { resultType: "Value", value: { value: "UnitValue" } };
};
withLetStatement = (ctx: Context, statement: LetStatement): ExpressionResult => {
let result = this.withExpression(ctx, statement.expression);
if (result.resultType === "Return") {
return result;
}
ctx.environment[statement.variableName.text] = {
namedEntity: "Variable",
value: result.value,
};
return { resultType: "Value", value: { value: "UnitValue" } };
};
withReturnStatement = (ctx: Context, statement: ReturnStatement): ExpressionResult => {
let result = this.withExpression(ctx, statement.source);
if (result.resultType === "Return") {
return result;
}
return { resultType: "Return", value: result.value };
};
withExpression = (ctx: Context, expression: Expression): ExpressionResult => {
if (expression.subExpression.expressionType === "LiteralInt") {
return {
resultType: "Value",
value: { value: "NumericValue", number: parseInt(expression.subExpression.value) },
};
}
if (expression.subExpression.expressionType === "LiteralFloat") {
return {
resultType: "Value",
value: { value: "NumericValue", number: parseFloat(expression.subExpression.value) },
};
}
if (expression.subExpression.expressionType === "LiteralString") {
return {
resultType: "Value",
value: { value: "StringValue", string: expression.subExpression.value },
};
}
if (expression.subExpression.expressionType === "LiteralBool") {
return {
resultType: "Value",
value: { value: "BoolValue", bool: expression.subExpression.value === "true" },
};
}
if (expression.subExpression.expressionType === "LiteralStruct") {
const def = ctx.environment[expression.subExpression.name.text];
if (def.namedEntity !== "NamedType") {
throw Error("Not a struct");
}
const fields: Record<string, Value> = {};
for (const field of expression.subExpression.fields) {
const fieldResult = this.withExpression(ctx, field.expression);
if (fieldResult.resultType === "Return") {
return fieldResult;
}
fields[field.name.text] = fieldResult.value;
}
return {
resultType: "Value",
value: {
value: "StructValue",
source: def,
fields: fields,
},
};
}
if (expression.subExpression.expressionType === "FunctionCall") {
return this.withFunctionCall(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "VariableUsage") {
const variableValue = ctx.environment[expression.subExpression.name.text];
if (!variableValue || variableValue.namedEntity !== "Variable") {
throw Error(`not found: ${expression.subExpression.name.text}`);
}
return { resultType: "Value", value: variableValue.value };
}
if (expression.subExpression.expressionType === "IfExpression") {
const condition = this.withExpression(ctx, expression.subExpression.condition);
if (condition.resultType === "Return") {
return condition;
}
if (condition.value.value === "BoolValue" && condition.value.bool === true) {
return this.withBlock(ctx, expression.subExpression.block);
} else {
if (expression.subExpression.else) {
return this.withBlock(ctx, expression.subExpression.else);
} else {
return { resultType: "Value", value: { value: "UnitValue" } };
}
}
}
if (expression.subExpression.expressionType === "StructGetter") {
return this.withStructGetter(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "Block") {
return this.withBlock(ctx, expression.subExpression);
}
if (expression.subExpression.expressionType === "Operation") {
return this.withOperation(ctx, expression.subExpression);
}
// not actually possible, but makes the type system happy
return { resultType: "Value", value: { value: "UnitValue" } };
};
withFunctionCall = (ctx: Context, fnCall: FunctionCall): ExpressionResult => {
const source = this.withExpression(ctx, fnCall.source);
if (source.resultType === "Return") {
return source;
}
const argValues: Value[] = [];
for (const arg of fnCall.arguments) {
const argValue = this.withExpression(ctx, arg);
if (argValue.resultType === "Return") {
return argValue;
}
argValues.push(argValue.value);
}
if (source.value.value !== "FunctionValue") {
throw Error("type error: function call source must be a function");
}
if (source.value.ref.functionType === "UserFunction") {
const fn = source.value.partial;
const fnCtx = contextFromModule(ctx.currentModule);
let i = 0;
for (const arg of source.value.partial) {
fnCtx.environment[source.value.ref.function.declaration.arguments[i].name.text] = {
namedEntity: "Variable",
value: arg,
};
i = i + 1;
}
for (const arg of argValues) {
fnCtx.environment[source.value.ref.function.declaration.arguments[i].name.text] = {
namedEntity: "Variable",
value: arg,
};
i = i + 1;
}
return { resultType: "Value", value: this.withFunction(fnCtx, source.value.ref.function) };
}
// builtin
let allValues = source.value.partial.concat(argValues);
return { resultType: "Value", value: source.value.ref.function(allValues) };
};
withStructGetter = (ctx: Context, structGetter: StructGetter): ExpressionResult => {
const source = this.withExpression(ctx, structGetter.source);
if (source.resultType === "Return") {
return source;
}
if (source.value.value !== "StructValue") {
throw Error("get attr of non-struct");
}
if (source.value.fields[structGetter.attribute.text]) {
return {
resultType: "Value",
value: source.value.fields[structGetter.attribute.text],
};
}
for (const impl of source.value.source.impls) {
for (const [name, method] of Object.entries(impl.functions)) {
if (name === structGetter.attribute.text) {
return {
resultType: "Value",
value: { value: "FunctionValue", partial: [source.value], ref: method },
};
}
}
}
// not actually possible, but makes the type system happy
return { resultType: "Value", value: { value: "UnitValue" } };
};
withOperation = (ctx: Context, op: Operation): ExpressionResult => {
const left = this.withExpression(ctx, op.left);
if (left.resultType === "Return") {
return left;
}
const right = this.withExpression(ctx, op.left);
if (right.resultType === "Return") {
return right;
}
if (left.value.value !== "NumericValue" || right.value.value !== "NumericValue") {
throw Error("Operation on a Nan");
}
if (op.op === "+") {
return {
resultType: "Value",
value: { value: "NumericValue", number: left.value.number + right.value.number },
};
}
if (op.op === "-") {
return {
resultType: "Value",
value: { value: "NumericValue", number: left.value.number - right.value.number },
};
}
if (op.op === "*") {
return {
resultType: "Value",
value: { value: "NumericValue", number: left.value.number * right.value.number },
};
}
return {
resultType: "Value",
value: { value: "NumericValue", number: left.value.number / right.value.number },
};
};
}