diff options
Diffstat (limited to 'src/interpreter/interpreter.ts')
-rw-r--r-- | src/interpreter/interpreter.ts | 124 |
1 files changed, 124 insertions, 0 deletions
diff --git a/src/interpreter/interpreter.ts b/src/interpreter/interpreter.ts new file mode 100644 index 0000000..ebd605c --- /dev/null +++ b/src/interpreter/interpreter.ts @@ -0,0 +1,124 @@ +import { + type ContinuationExpression, + type PrimitiveOperationExpression, + 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) { + logger.debug(`Evaluating variable: ${value.name}`); + return env.get(value.name); + } + + throw new InvalidStateError(`Invalid value: ${value}`); +}; + +const evaluatePrimitiveOperation = ( + { primitiveOperation }: PrimitiveOperationExpression, + env: Environment, + logger: TracingLogger, +) => { + const { opr, operands, resultBindings, continuations } = primitiveOperation; + if (operands.length !== 2) { + throw new BadArgumentError('Primitive operations must have 2 operands'); + } + + 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); + } + + // return the result of the last continuation + return continuations.reduce((_, continuation, i) => { + const childLogger = logger.createChild(`continuation[${i}]`); + return evaluteContinuationExpression( + continuation, + continuationEnvironment, + childLogger, + ); + }, result); +}; + +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 ('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 ('application' in expr) { + throw new NotImplementedError( + 'Continuation application 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<Denotable> => { + 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); +}; |