import { type ContinuationExpression, type PrimitiveOperationExpression, type ApplicationExpression, type Program, type Value, } from '@/parser'; import { Environment, type Denotable } from '.'; import { BadArgumentError, InvalidStateError, NotImplementedError, type TracingLogger, } from '@/utils'; import { putBuiltinsOnEnvironemtn } from './builtins'; const evaluateValue = ( value: Value, env: Environment, _logger: TracingLogger, ): Denotable => { if (typeof value === 'string') { return { type: 'string', value }; } if ('real' in value) { return { type: 'real', value: value.real }; } if ('int' in value) { return { type: 'int', value: value.int }; } if ('name' in value) { return env.get(value.name); } throw new InvalidStateError(`Invalid value: ${value}`); }; const evaluateApplicationExpression = ( { application }: ApplicationExpression, env: Environment, logger: TracingLogger, ): Denotable => { const { fn, args } = application; const argValues = args.map(arg => evaluateValue(arg, env, logger.createChild('evaluateValue')), ); return env.apply(fn.name, argValues); }; const evaluatePrimitiveOperation = ( { primitiveOperation }: PrimitiveOperationExpression, env: Environment, logger: TracingLogger, ) => { const { opr, operands, resultBindings, continuations } = primitiveOperation; const operandValues = operands.map(operand => evaluateValue(operand, env, logger.createChild('evaluteValue')), ); const result = env.apply(opr, operandValues); const continuationEnvironment = env.createChild(); for (const { name } of resultBindings) { continuationEnvironment.set(name, result); } if (result.type === 'bool') { if (continuations.length > 2) { throw new BadArgumentError( `Expected <= 2 continuations for boolean result, got ${continuations.length}`, ); } if (continuations.length !== 2) { logger.warn( `Expected 2 continuations for boolean result, got ContinuationLength=(${continuations.length})`, ); return result; } const [trueContinuation, falseContinuation] = continuations; const childLogger = logger.createChild('continuation[true]'); const continuation = result.value ? trueContinuation : falseContinuation; return evaluteContinuationExpression( continuation, continuationEnvironment, childLogger, ); } if (continuations.length > 1) { throw new BadArgumentError( `Expected <= 1 continuations for non-boolean result, got ${continuations.length}`, ); } else if (continuations.length === 0) { logger.warn( "Expected 1 continuation for non-boolean result, but there wasn't any. Implicitly returning the result", // technically undefined behavior ); return result; } const [continuation] = continuations; const childLogger = logger.createChild('continuation'); return evaluteContinuationExpression( continuation, continuationEnvironment, childLogger, ); }; const evaluteContinuationExpression = ( expr: ContinuationExpression, env: Environment, logger: TracingLogger, ): Denotable => { if ('primitiveOperation' in expr) { logger.debug('Evaluating primitive operation'); return evaluatePrimitiveOperation( expr, env, logger.createChild('evaluatePrimitiveOperation'), ); } if ('application' in expr) { logger.debug('Evaluating function application'); return evaluateApplicationExpression( expr, env, logger.createChild('evaluateApplicationExpression'), ); } if ('record' in expr) { throw new NotImplementedError('Continuation records are not supported yet'); } if ('select' in expr) { throw new NotImplementedError('Continuation select is not supported yet'); } if ('offset' in expr) { throw new NotImplementedError('Continuation offset is not supported yet'); } if ('switch' in expr) { throw new NotImplementedError('Continuation switch is not supported yet'); } if ('fix' in expr) { throw new NotImplementedError('Continuation fix is not supported yet'); } throw new InvalidStateError(`Invalid continuation expression: ${expr}`); }; export const evaluate = async ( ast: Program, logger: TracingLogger, ): Promise => { const globalEnvironment = putBuiltinsOnEnvironemtn( new Environment(logger.createChild('RootEnv')), ); return ast.reduce((_, continuation, i) => { const exprLogger = logger.createChild(`statement[${i}]`); return evaluteContinuationExpression( continuation as ContinuationExpression, globalEnvironment, exprLogger, ); }, null); };