use :node;
var Node = module.require('../Node').Node;
fn ExistentialExpression(argument)
extends Node { this.type = 'ExistentialExpression'; this.argument = argument; this.argument.parent = this;
}
ExistentialExpression.prototype.codegen = () -> {
if !super.codegen() { return; } var isArgumentCallExpression = this.argument.hasCallExpression?() ?? false; this.argument = this.argument.codegen(); // If the argument has a function call (e.g: a().b) // then store its value in a separate variable to avoid // calling the function twice. if isArgumentCallExpression { var context = this.getContext(); var id = { "type": "Identifier", "name": ExistentialExpression.getNextVariableName() }; context.node.body.splice(context.position + (ExistentialExpression.existentialIndex - 2), 0, { "type": "VariableDeclaration", "declarations": [ { "type": "VariableDeclarator", "id": id, "init": this.argument } ], "kind": "let", "codeGenerated": true }); this.argument = id; } // The generated syntax looks like: // argument === null var nullCheck = { "type": "BinaryExpression", "operator": "!==", "left": this.argument, "right": { "type": "Literal", "value": null, "raw": "null" } }; // Add undefined check this.type = 'LogicalExpression'; this.operator = '&&'; this.left = { "type": "BinaryExpression", "operator": "!==", "left": { "type": "UnaryExpression", "operator": "typeof", "argument": this.argument, "prefix": true }, "right": { "type": "Literal", "value": "undefined", "raw": "'undefined'" } }; this.right = nullCheck; return this;
};
ExistentialExpression.prototype.hasCallExpression = () -> {
return this.argument?.hasCallExpression?() ?? false;
};
ExistentialExpression.getNextVariableName = () -> {
if !this.existentialIndex? { this.existentialIndex = 0; } return "existential" + this.existentialIndex++;
};
ExistentialExpression.resetVariableNames = () -> {
this.existentialIndex = 0;
};
exports.ExistentialExpression = ExistentialExpression;