Skip to content

Instantly share code, notes, and snippets.

@MarioAriasC
Created January 23, 2023 08:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save MarioAriasC/b1717ed42f4866cf82ddb547758de57e to your computer and use it in GitHub Desktop.
Save MarioAriasC/b1717ed42f4866cf82ddb547758de57e to your computer and use it in GitHub Desktop.
function evaluateNode(node, env) {
if (node instanceof Identifier) {
const value = env.get(node.value);
if (value === null) {
const builtin = builtins.get(node.value);
if (builtin !== undefined) {
return builtin;
}
return new MError(`identifier not found: ${node.value}`);
}
return value;
}
if (node instanceof IntegerLiteral) {
return new MInteger(node.value);
}
if (node instanceof InfixExpression) {
return ifNotError(evaluateNode(node.left, env), (left) => {
return ifNotError(evaluateNode(node.right, env), (right) => {
return evalInfixExpression(node.operator, left, right);
});
});
}
if (node instanceof BlockStatement) {
return evalBlockStatement(node, env);
}
if (node instanceof ExpressionStatement) {
return evaluateNode(node.expression, env);
}
if (node instanceof IfExpression) {
function isTruthy(condition) {
if (condition === NULL) {
return false;
}
if (condition === TRUE) {
return true;
}
return condition !== FALSE;
}
return ifNotError(evaluateNode(node.condition, env), (condition) => {
if (isTruthy(condition)) {
return evaluateNode(node.consequence, env);
}
if (node.alternative !== null) {
return evalBlockStatement(node.alternative, env);
}
return NULL;
});
}
if (node instanceof CallExpression) {
return ifNotError(evaluateNode(node.fun, env), (fun) => {
const args = evalExpressions(node.args, env);
if (args.length === 1 && isError(args[0])) {
return args[0];
}
return applyFunction(fun, args);
});
}
if (node instanceof ReturnStatement) {
return ifNotError(evaluateNode(node.returnValue, env), (value) => {
return new MReturnValue(value);
});
}
if (node instanceof PrefixExpression) {
return ifNotError(evaluateNode(node.right, env), (right) => {
switch (node.operator) {
case '!':
return evalBangOperatorExpression(right);
case '-':
return evalMinusPrefixOperatorExpression(right);
default:
return new MError(`Unknown operator: ${node.operator}${typeDesc(right)}`);
}
});
}
if (node instanceof BooleanLiteral) {
return toMonkey(node.value);
}
if (node instanceof LetStatement) {
return ifNotError(evaluateNode(node.value, env), (value) => {
return env.put(node.name.value, value);
});
}
if (node instanceof FunctionLiteral) {
return new MFunction(node.parameters, node.body, env);
}
if (node instanceof StringLiteral) {
return new MString(node.value);
}
if (node instanceof IndexExpression) {
const left = evaluateNode(node.left, env);
if (isError(left)) {
return left;
}
const index = evaluateNode(node.index, env);
if (isError(index)) {
return index;
}
if (left instanceof MArray && index instanceof MInteger) {
return evalArrayIndexExpression(left, index);
}
if (left instanceof MHash) {
return evalHashIndexExpression(left, index);
}
return new MError(`index operator not supported: ${typeDesc(left)}`);
}
if (node instanceof HashLiteral) {
const pairs = new Map();
for (const [keyNode, valueNode] of node.pairs) {
const key = evaluateNode(keyNode, env);
if (isError(key)) {
return key;
}
if (key instanceof MValue) {
const value = evaluateNode(valueNode, env);
if (isError(value)) {
return value;
}
pairs.set(key.hashKey(), new HashPair(key, value));
}
else {
return new MError(`unusable as hash key: ${typeDesc(key)}`);
}
}
return new MHash(pairs);
}
if (node instanceof ArrayLiteral) {
const elements = evalExpressions(node.elements, env);
if (elements.length == 1 && isError(elements[0])) {
return elements[0];
}
return new MArray(elements);
}
throw new Error(`${node?.toString()} => ${node?.constructor.name}`);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment