Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 31 additions & 12 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ export const BytecodeDefault: ParseTreeTable = {
not: (out, node) => (visitParseNode(out, node.expr), pushBytecode(out, node.token, { type: 'not' })),

evalfunc: (out, node) => (node.typeArgs.map(x => writeMeta(out, x)), visitAll(out, node.args), pushBytecode(out, node.token, { type: "evalfunc", func: node.func })),
concurrency: (out, node) => (visitAll(out, node.fns), pushBytecode(out, node.token, { type: 'concurrency', count: node.fns.length })),

note: (out, node) => {
if (node.expr instanceof ParseCall) {
Expand Down Expand Up @@ -122,11 +123,13 @@ export const BytecodeDefault: ParseTreeTable = {
visitAll(out, node.typeArgs)
visitAll(out, node.args);
if (node.left instanceof ParseValue) { // Internal desugar results in a fixed value sometimes
pushBytecode(out, node.token, { type: "callobj", callable: node.left.value, count: node.args.length, tcount: node.typeArgs.length })
visitParseNode(out, node.left)
pushBytecode(out, node.token, { type: "callobj", count: node.args.length, tcount: node.typeArgs.length })
return;
}
if (node.left instanceof ParseIdentifier) {
pushBytecode(out, node.token, { type: "call", name: node.left.token.value, count: node.args.length, tcount: node.typeArgs.length });
if (node.left instanceof ParseIdentifier || node.left instanceof ParseFreshIden) {
const name = node.left instanceof ParseFreshIden ? node.left.freshBindingToken.identifier : node.left.token.value
pushBytecode(out, node.token, { type: "call", name, count: node.args.length, tcount: node.typeArgs.length });
return;
}
if (node.left instanceof ParseCompilerIden) {
Expand All @@ -137,6 +140,11 @@ export const BytecodeDefault: ParseTreeTable = {
compilerAssert(false, "Call with field not implemented yet", { node })
return;
}
if (node.left instanceof ParseFunction) {
visitParseNode(out, node.left)
pushBytecode(out, node.token, { type: "callobj", count: node.args.length, tcount: node.typeArgs.length })
return;
}
compilerAssert(false, "Call with non-identifier not implemented yet", { left: node.left})
},
postcall: (out, node) => {
Expand Down Expand Up @@ -298,6 +306,7 @@ export const BytecodeSecondOrder: ParseTreeTable = {
},

evalfunc: (out, node) => (node.typeArgs.map(x => writeMeta(out, x)), visitAll(out, node.args), pushBytecode(out, node.token, { type: "evalfunc", func: node.func })),
concurrency: (out, node) => (visitAll(out, node.fns), pushBytecode(out, node.token, { type: 'concurrency', count: node.fns.length })),

note: (out, node) => {
if (node.expr instanceof ParseCall) {
Expand Down Expand Up @@ -474,8 +483,9 @@ export const BytecodeSecondOrder: ParseTreeTable = {
const name = new ParseFreshIden(node.token, new FreshBindingToken('tmpfn'))
visitParseNode(out, new ParseLetConst(node.token, name, node.left))
pushBytecode(out, node.token, { type: 'pop' })
node.typeArgs.forEach(x => writeMeta(out, x));
visitAll(out, node.args)
pushBytecode(out, node.token, { type: "callast", name: name.freshBindingToken.identifier, count: node.args.length, tcount: 0 });
pushBytecode(out, node.token, { type: "callast", name: name.freshBindingToken.identifier, count: node.args.length, tcount: node.typeArgs.length });
return
}
compilerAssert(false, "Call with non-identifier not implemented yet", { left: node.left})
Expand Down Expand Up @@ -586,9 +596,10 @@ export function createBytecodeVmAndExecuteTask(ctx: TaskContext, subCompilerStat
return (
TaskDef(executeVmTask, { vm })
.mapRejected(error => {
if (error.info) {
if (!(error.info as any).location) (error.info as any).location = vm.location;
(error.info as any).subCompilerState = subCompilerState
const info = error.info as any
if (info) {
if (!info.location) info.location = vm.location;
if (!info.subCompilerState) info.subCompilerState = subCompilerState
}
return error
})
Expand Down Expand Up @@ -728,7 +739,7 @@ const letLocalAst = (vm: Vm, name: string, type: Type | null, value: Ast | null)
compilerAssert(type || value, "Expected type or initial value for let binding $name", { name });
compilerAssert(!Object.hasOwn(vm.scope, name), `Already defined $name`, { name });
let inferType = type || value!.type
compilerAssert(inferType !== VoidType, "Expected type for local $name but got $inferType", { name, inferType })
compilerAssert(inferType !== VoidType, "Expected type for local $name but got $inferType", { name, inferType, value })
inferType = propagateLiteralType(inferType, value)
const binding = new Binding(name, inferType)
setScopeValueAndResolveEvents(vm.scope, name, binding) // This is for globals usually. Locals should be in order
Expand Down Expand Up @@ -889,6 +900,11 @@ const instructions: InstructionMapping = {
vm.stack.push(new IfAst(resultType, vm.location, cond, trueBody, falseBody))
},
evalfunc: (vm, { func }) => { return func(vm) },
concurrency: (vm, { count }) => {
const values = popValues(vm, count).reverse()
const fnctx: CompilerFunctionCallContext = { location: vm.location, compilerState: vm.context.subCompilerState, resultAst: undefined, typeCheckResult: undefined }
return Task.concurrency(values.map(x => createCallAstFromValue(fnctx, x, [], [])))
},
listast: (vm, { count }) => {
const values = expectAsts(popValues(vm, count))
const elementType = getCommonType(values.map(x => x.type))
Expand Down Expand Up @@ -1032,6 +1048,7 @@ const instructions: InstructionMapping = {
return TaskDef(resolveScope, vm.scope, name).chainFn((task, binding) => {
compilerAssert(binding instanceof Binding, "Expected binding got $binding", { binding })
const ast = propagatedLiteralAst(expectAst(popStack(vm)))
compilerAssert(binding.type === ast.type, "Type mismatch got $got expected $expected", { got: ast.type, expected: binding.type })
vm.stack.push(new SetAst(VoidType, vm.location, binding, ast))
return Task.success()
});
Expand Down Expand Up @@ -1220,7 +1237,8 @@ const instructions: InstructionMapping = {
})
)
},
callobj: (vm, { callable, count, tcount }) => {
callobj: (vm, { count, tcount }) => {
const callable = popStack(vm)
const values = popValues(vm, count);
const typeArgs = popValues(vm, tcount || 0);
return TaskDef(callFunctionFromValueTask, vm, callable, typeArgs, values)
Expand Down Expand Up @@ -1333,7 +1351,10 @@ function executeVmTask(ctx: TaskContext, { vm } : { vm: Vm }, p: void): Task<Uni
try {
res = instr(vm, current);
} catch(ex) {
if (ex instanceof CompilerError) Object.assign(ex.info, { ip: vm.ip, current, location: vm.location, subCompilerState: vm.context.subCompilerState })
if (ex instanceof CompilerError) {
if (!ex.info) ex.info = {}
Object.assign(ex.info, { ip: vm.ip, current, location: vm.location, subCompilerState: vm.context.subCompilerState }, ex.info)
}
throw ex;
}

Expand Down Expand Up @@ -1698,7 +1719,6 @@ export const runTopLevelTask = (ctx: TaskContext, stmts: ParseStatements, module

const createInitializerFunctionTask = (ctx: TaskContext) => {
const decl: ParserFunctionDecl = {
id: undefined,
debugName: `<initializer>`,
token: createAnonymousToken(''), functionMetaName: null, name: null, typeParams: [], params: [],
keywords: [], anonymous: true, returnType: null, body: null, annotations: [], variadic: false
Expand All @@ -1724,7 +1744,6 @@ const createInitializerFunctionTask = (ctx: TaskContext) => {

const createEntryFunctionTask = (ctx: TaskContext) => {
const decl: ParserFunctionDecl = {
id: undefined,
debugName: `<entry>`,
token: createAnonymousToken(''), functionMetaName: null, name: null, typeParams: [], params: [],
keywords: [], anonymous: true, returnType: null, body: null, annotations: [], variadic: false
Expand Down
15 changes: 9 additions & 6 deletions src/compiler_functions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,20 @@ import { Task, TaskDef, Unit } from "./tasks";


export const insertFunctionDefinition = (compilerState: GlobalCompilerState, decl: ParserFunctionDecl) => {
if (decl.id !== undefined) return compilerState.functionDefinitions[decl.id];
const existing = compilerState.functionDefinitionsByDeclaration.get(decl)
if (existing) return existing

decl.id = compilerState.functionDefinitions.length;
const id = compilerState.functionDefinitions.length;
const keywords = decl.keywords.map(x => x instanceof ParseNote ? x.expr.token.value : x.token.value)
const inline = !!decl.anonymous || keywords.includes('inline')
const funcDef = new FunctionDefinition(
decl.id, decl.debugName,
id, decl.debugName,
decl.name, decl.typeParams, decl.params,
decl.returnType, decl.body,
inline, decl.annotations)
funcDef.variadic = decl.variadic
funcDef.keywords.push(...keywords)
compilerState.functionDefinitionsByDeclaration.set(decl, funcDef)

if (funcDef.keywords.includes("external")) {
compilerAssert(decl.name, "Expected name")
Expand Down Expand Up @@ -302,8 +304,9 @@ function functionInlineTask(ctx: TaskContext, { location, func, typeArgs, parent

func.typeParams.forEach((typeParam, i) => {
compilerAssert(typeParam instanceof ParseIdentifier, "Not implemented")
const typeArg = typeArgs[i] || result.substitutions[typeParam.token.value]
compilerAssert(typeArg, "Type arg not found $name", { name: typeParam.token.value, typeArgs })
let typeArg = typeArgs[i]
if (typeArg === undefined) typeArg = result.substitutions[typeParam.token.value]
compilerAssert(typeArg !== undefined, "Type arg not found $name", { name: typeParam.token.value, typeArgs })
templateScope[typeParam.token.value] = typeArg
});

Expand All @@ -312,7 +315,7 @@ function functionInlineTask(ctx: TaskContext, { location, func, typeArgs, parent
.chainFn((task, ast) => {
compilerAssert(isAst(ast), "Expected ast got $ast", { ast });

ctx.globalCompiler.logger.log(textColors.cyan(`Compiled inline ${func.debugName}`))
// ctx.globalCompiler.logger.log(textColors.cyan(`Compiled inline ${func.debugName}`))
// TODO: This is basically what endblockast instruction does. can we fuse it?
let type = propagatedLiteralAst(ast).type
if (breakBlock.type) {
Expand Down
Loading