hopper
Version:
An interpreter for the Grace programming language
830 lines (631 loc) • 20.2 kB
JavaScript
// The abstract syntax tree of Grace. Consists primarily of constructors.
"use strict";
var Task, util;
Task = require("./task");
util = require("./util");
// (this : Request | Signature).name() : String
// Builds a name from the signature of a Request or Method.
function buildName() {
var i, l, part, parts, signature, value;
signature = this.parts;
if (signature.length === 1) {
part = signature[0];
value = part.name;
if (value.isOperator ||
(part.parameters || part.arguments).length === 0) {
return value.value;
}
}
parts = [];
for (i = 0, l = signature.length; i < l; i += 1) {
parts.push(signature[i].name + "()");
}
return parts.join(" ");
}
// commas(left : String, list : [Object], right : String) : String
// Build a comma separated list separated by the given arguments, or an empty
// string if there is nothing in the list.
function commas(left, list, right) {
return list.length === 0 ? "" : left + list.join(", ") + right;
}
// acceptAll(nodes : [Node], visitor : Visitor) : Task
// Call the accept method on all of the nodes with the given visitor.
function acceptAll(nodes, visitor) {
return Task.each(nodes, function (node) {
return node.accept(visitor);
});
}
// Top-level Node type, used as a type in Grace.
function Node(token) {
this.location = token.location;
}
// Abstract expression constructor, used as a type in Grace.
function Expression(token) {
Node.call(this, token);
}
util.inherits(Expression, Node);
// new Dialect(path : String)
// A dialect directive.
function Dialect(path, token) {
Node.call(this, token);
this.path = path;
}
util.inherits(Dialect, Node);
Dialect.prototype.accept = function (visitor) {
return visitor.visitDialect(this);
};
Dialect.prototype.toString = function () {
return "dialect " + this.path;
};
// new Import(path : String, ident : Identifier)
// An import directive.
function Import(path, ident, token) {
Node.call(this, token);
this.path = path;
this.identifier = ident;
}
util.inherits(Import, Node);
Import.prototype.accept = function (visitor) {
var self = this;
return visitor.visitImport(this).then(function () {
return self.identifier.accept(visitor);
});
};
Import.prototype.toString = function () {
return "import " + this.path + " as " + this.identifier;
};
// new Identifier(value : String, isOperator : Boolean = false)
// An identifier.
function Identifier(value, isOperator, token) {
Node.call(this, token);
this.value = value;
this.isOperator = isOperator === true;
}
util.inherits(Identifier, Node);
Identifier.prototype.accept = function (visitor) {
return visitor.visitIdentifier(this);
};
Identifier.prototype.toString = function () {
return this.value;
};
// An abstract Request constructor, used as a type in Grace.
function Request(signature, node) {
Expression.call(this, node);
this.parts = signature;
}
util.inherits(Request, Expression);
Request.prototype.name = buildName;
Request.prototype.toString = function () {
return this.parts.join(" ");
};
// new UnqualifiedRequest(signature : [RequestPart])
// A variable lookup or method request without a receiver.
function UnqualifiedRequest(signature) {
Request.call(this, signature, signature[0]);
}
util.inherits(UnqualifiedRequest, Request);
UnqualifiedRequest.prototype.accept = function (visitor) {
var self = this;
return visitor.visitUnqualifiedRequest(this).then(function () {
return acceptAll(self.parts, visitor);
});
};
// new Request(receiver : Expression, signature : [RequestPart])
// A method request or variable lookup.
function QualifiedRequest(receiver, signature) {
Request.call(this, signature, receiver);
this.receiver = receiver;
}
util.inherits(QualifiedRequest, Request);
QualifiedRequest.prototype.isBinaryOperator = function () {
var name = this.parts[0].name;
return name.isOperator && name.value.substring(0, 6) !== "prefix";
};
QualifiedRequest.prototype.isPrefixOperator = function () {
var name = this.parts[0].name;
return name.isOperator && name.value.substring(0, 6) === "prefix";
};
QualifiedRequest.prototype.accept = function (visitor) {
var self = this;
return visitor.visitQualifiedRequest(this).then(function () {
return self.receiver.accept(visitor);
}).then(function () {
return acceptAll(self.parts, visitor);
});
};
QualifiedRequest.prototype.toString = function () {
var parts, receiver;
receiver = this.receiver;
parts = this.parts;
if (this.isBinaryOperator()) {
return (receiver.constructor === Request && receiver.isBinaryOperator() ?
"(" + receiver + ")" : receiver) + " " + parts[0];
}
if (this.isPrefixOperator()) {
return parts[0].name.value.substring(6) +
(receiver.constructor === Request && receiver.isPrefixOperator() ?
"(" + receiver + ")" : receiver);
}
return (receiver === null ? "" : receiver + ".") + parts.join(" ");
};
// new RequestPart(name : String,
// generics : [Expression], arguments : [Expression])
// A part of a request's signature.
function RequestPart(name, generics, args) {
Node.call(this, name);
this.name = name;
this.generics = generics;
this.arguments = args;
}
util.inherits(RequestPart, Node);
RequestPart.prototype.accept = function (visitor) {
var self = this;
return visitor.visitRequestPart(this).then(function () {
return acceptAll(self.generics, visitor);
}).then(function () {
return acceptAll(self.arguments, visitor);
});
};
RequestPart.prototype.toString = function () {
var arg, args, name;
name = this.name;
args = this.arguments;
if (name.isOperator) {
// This can't come up unless toString is called directly on the part.
if (name.value.substring(0, 6) === "prefix") {
return name.value;
}
arg = args[0];
if (arg.constructor === Request && arg.isBinaryOperator()) {
args = " (" + args + ")";
} else {
args = " " + arg;
}
} else {
args = commas("(", args, ")");
}
return name + commas("<", this.generics, ">") + args;
};
// new BooleanLiteral(value : Boolean)
// A boolean literal, from a JavaScript boolean.
function BooleanLiteral(value, token) {
Node.call(this, token);
this.value = value;
}
util.inherits(BooleanLiteral, Expression);
BooleanLiteral.prototype.name = function () {
return this.value.toString();
};
BooleanLiteral.prototype.accept = function (visitor) {
return visitor.visitBooleanLiteral(this);
};
BooleanLiteral.prototype.toString = function () {
return this.value.toString();
};
// new NumberLiteral(value : Number)
// A number literal from a JavaScript number.
function NumberLiteral(value, token) {
Node.call(this, token);
this.value = value;
}
util.inherits(NumberLiteral, Expression);
NumberLiteral.prototype.accept = function (visitor) {
return visitor.visitNumberLiteral(this);
};
NumberLiteral.prototype.toString = function () {
return this.value.toString();
};
// new StringLiteral(value : String)
// An object wrapping a string literal.
function StringLiteral(value, token) {
Node.call(this, token);
this.value = value;
}
util.inherits(StringLiteral, Expression);
StringLiteral.prototype.accept = function (visitor) {
return visitor.visitStringLiteral(this);
};
StringLiteral.prototype.toString = function () {
return '"' + this.value.replace(new RegExp('"', "g"), '\\"') + '"';
};
// An abstract constructor for variable declarations.
function Declaration(token) {
Node.call(this, token);
}
util.inherits(Declaration, Node);
Declaration.prototype.patternOrIfAbsent = function (onAbsent) {
if (this.pattern === null) {
return onAbsent.apply();
}
return this.pattern;
};
Declaration.prototype.accept = function (visitor) {
var self = this;
return self.name.accept(visitor).then(function () {
if (self.pattern !== null) {
return self.pattern.accept(visitor);
}
}).then(function () {
return acceptAll(self.annotations, visitor);
}).then(function () {
return self.value.accept(visitor);
});
};
// new Def(name : Identifier, pattern : Expression,
// annotations : [Expression], value : Expression)
// A definition declaration.
function Def(name, pattern, annotations, value, token) {
Declaration.call(this, token);
this.name = name;
this.pattern = pattern;
this.annotations = annotations;
this.value = value;
}
util.inherits(Def, Declaration);
Def.prototype.accept = function (visitor) {
var self = this;
return visitor.visitDef(self).then(function () {
return Declaration.prototype.accept.call(self, visitor);
});
};
Def.prototype.toString = function () {
var pattern = this.pattern;
return "def " + this.name + (pattern === null ? "" : " : " + pattern) +
commas(" is ", this.annotations, "") + " = " + this.value;
};
// new Var(name : Identifier, pattern : Expression,
// annotations : [Expression], value : Expression)
// A variable declaration.
function Var(name, pattern, annotations, value, token) {
Declaration.call(this, token);
this.name = name;
this.pattern = pattern;
this.annotations = annotations;
this.value = value;
}
util.inherits(Var, Declaration);
Var.prototype.accept = function (visitor) {
var self = this;
return visitor.visitVar(self).then(function () {
return Declaration.prototype.accept.call(self, visitor);
});
};
Var.prototype.toString = function () {
var pattern, value;
pattern = this.pattern;
value = this.value;
return "var " + this.name + (pattern === null ? "" : " : " + pattern) +
commas(" is ", this.annotations, "") +
(value === null ? "" : " := " + value);
};
// new ObjectConstructor(annotations : [Expression],
// body : [Statement | Method])
// An object constructor.
function ObjectConstructor(annotations, body, token) {
Node.call(this, token);
this.annotations = annotations;
this.body = body;
}
util.inherits(ObjectConstructor, Expression);
ObjectConstructor.prototype.accept = function (visitor) {
var self = this;
return visitor.visitObjectConstructor(self).then(function () {
return acceptAll(self.body, visitor);
});
};
ObjectConstructor.prototype.toString = function () {
var body = this.body;
return "object" + commas(" is ", this.annotations, "") +
" {" + (body.length === 0 ? "" : "\n " + body.join("\n ") + "\n") + "}";
};
// new Method(signature : Signature,
// annotations : [Expression], body: [Statement])
function Method(signature, annotations, body, token) {
Node.call(this, token);
this.signature = signature;
this.annotations = annotations;
this.body = body;
}
util.inherits(Method, Node);
Method.prototype.accept = function (visitor) {
var self = this;
return visitor.visitMethod(self).then(function () {
return self.signature.accept(visitor);
}).then(function () {
return acceptAll(self.annotations, visitor);
}).then(function () {
return acceptAll(self.body, visitor);
});
};
Method.prototype.toString = function () {
var body, braceSep;
body = this.body;
braceSep = body.length > 0 ? "\n" : "";
return "method " + this.signature + commas(" is ", this.annotations, "") +
" {" + braceSep + body.join("\n") + braceSep + "}";
};
// new Class(name : Identifier, signature : Signature,
// annotations : [Expression], body : [Statement])
function Class(name, signature, annotations, body, token) {
Node.call(this, token);
this.name = name;
this.signature = signature;
this.annotations = annotations;
this.body = body;
}
util.inherits(Class, Node);
Class.prototype.accept = function (visitor) {
var self = this;
return visitor.visitClass(self).then(function () {
return self.name.accept(visitor);
}).then(function () {
return self.signature.accept(visitor);
}).then(function () {
return acceptAll(self.annotations, visitor);
}).then(function () {
return acceptAll(self.body, visitor);
});
};
Class.prototype.toString = function () {
var body, braceSep;
body = this.body;
braceSep = body.length > 0 ? "\n" : "";
return "class " + this.name + "." + this.signature +
commas(" is ", this.annotations, "") +
" {" + braceSep + body.join("\n") + braceSep + "}";
};
// new Signature(parts : [SignaturePart], pattern : Expression)
// A list of signature parts combined with an optional return pattern.
function Signature(parts, pattern, token) {
Node.call(this, token);
this.parts = parts;
this.pattern = pattern;
}
util.inherits(Signature, Node);
Signature.prototype.name = buildName;
Signature.prototype.patternOrIfAbsent = function (onAbsent) {
if (this.pattern === null) {
return onAbsent.apply();
}
return this.pattern;
};
Signature.prototype.accept = function (visitor) {
var self = this;
return visitor.visitSignature(self).then(function () {
return acceptAll(self.parts, visitor);
}).then(function () {
if (self.pattern !== null) {
return self.pattern.accept(visitor);
}
});
};
Signature.prototype.toString = function () {
var pattern = this.pattern;
return this.parts.join(" ") + (pattern ? " -> " + pattern : "");
};
// new SignaturePart(name : Identifier,
// generics : [Identifier], parameters : [Parameter])
// A part of a method's signature.
function SignaturePart(name, generics, parameters) {
Node.call(this, name);
this.name = name;
this.generics = generics;
this.parameters = parameters;
}
util.inherits(SignaturePart, Node);
SignaturePart.prototype.accept = function (visitor) {
var self = this;
return visitor.visitSignaturePart(self).then(function () {
return self.name.accept(visitor);
}).then(function () {
return acceptAll(self.generics, visitor);
}).then(function () {
return acceptAll(self.parameters, visitor);
});
};
SignaturePart.prototype.toString = function () {
return this.name + commas("<", this.generics, ">") +
commas("(", this.parameters, ")");
};
// new Parameter(name : Identifier, pattern : Expression, isVarArg : Boolean)
// A parameter in a method signature.
function Parameter(name, pattern, isVarArg, token) {
Node.call(this, token);
this.name = name;
this.pattern = pattern;
this.isVarArg = isVarArg;
}
util.inherits(Parameter, Node);
Parameter.prototype.patternOrIfAbsent = function (onAbsent) {
if (this.pattern === null) {
return onAbsent.apply();
}
return this.pattern;
};
Parameter.prototype.accept = function (visitor) {
var self = this;
return visitor.visitParameter(self).then(function () {
return self.name.accept(visitor);
}).then(function () {
if (self.pattern !== null) {
return self.pattern.accept(visitor);
}
});
};
Parameter.prototype.toString = function () {
var pattern = this.pattern;
return (this.isVarArg ? "*" : "") + this.name +
(pattern === null ? "" : " : " + pattern);
};
// new Block(parameters : [Parameter], body : [Statement])
// A block literal.
function Block(parameters, body, token) {
Node.call(this, token);
this.parameters = parameters;
this.body = body;
}
util.inherits(Block, Expression);
Block.prototype.accept = function (visitor) {
var self = this;
return visitor.visitBlock(self).then(function () {
return acceptAll(self.parameters, visitor);
}).then(function () {
return acceptAll(self.body, visitor);
});
};
Block.prototype.toString = function () {
var body, braceSep, newline;
body = this.body;
newline = body.length > 1;
braceSep = body.length === 0 ? "" : newline ? "\n" : " ";
return "{" + commas("", this.parameters, " ->") +
braceSep + body.join("\n") + braceSep + "}";
};
// new Return(expression : Expression)
// A return statement with an optional expression.
function Return(expression, token) {
Node.call(this, token);
this.expression = expression;
}
util.inherits(Return, Node);
Return.prototype.accept = function (visitor) {
var self = this;
return visitor.visitReturn(self).then(function () {
return self.expression.accept(visitor);
});
};
Return.prototype.toString = function () {
var expression = this.expression;
return "return" + (expression === null ? "" : " " + expression);
};
// new Inherits(request : Request)
// An inherits statement with a required super-object request.
function Inherits(request, token) {
Node.call(this, token);
this.request = request;
}
util.inherits(Inherits, Node);
Inherits.prototype.accept = function (visitor) {
var self = this;
return visitor.visitInherits(self).then(function () {
return self.request.accept(visitor);
});
};
Inherits.prototype.toString = function () {
return "inherits " + this.request;
};
// new Type(signatures : [Signature])
// A type literal of method signatures.
function Type(signatures, token) {
Node.call(this, token);
this.signatures = signatures;
}
util.inherits(Type, Expression);
Type.prototype.nameOf = function (i) {
return buildName.call(this.signatures[i]);
};
Type.prototype.accept = function (visitor) {
var self = this;
return visitor.visitType(self).then(function () {
return acceptAll(self.signatures, visitor);
});
};
Type.prototype.toString = function () {
var sep, signatures;
signatures = this.signatures;
sep = signatures.length === 0 ? "" : " ";
return "type {" + sep + signatures.join("; ") + sep + "}";
};
// new TypeDeclaration(name : Identifier, generics : [Type],
// annotations : [Expression], value : Type)
// A new type declaration.
function TypeDeclaration(name, generics, annotations, value, token) {
Node.call(this, token);
this.name = name;
this.generics = generics;
this.annotations = annotations;
this.value = value;
}
util.inherits(TypeDeclaration, Node);
TypeDeclaration.prototype.accept = function (visitor) {
var self = this;
return visitor.visitTypeDeclaration(self).then(function () {
return self.name.accept(visitor);
}).then(function () {
return acceptAll(self.generics, visitor);
}).then(function () {
return acceptAll(self.annotations, visitor);
}).then(function () {
return self.value.accept(visitor);
});
};
TypeDeclaration.prototype.toString = function () {
return "type " + this.name + commas("<", this.generics, ">") +
commas(" is ", this.annotations, "") + " = " + this.value;
};
// new Self()
// A reference to the local self value.
function Self(token) {
Node.call(this, token);
}
util.inherits(Self, Expression);
Self.prototype.accept = function (visitor) {
return visitor.visitSelf(this);
};
Self.prototype.toString = function () {
return "self";
};
// new Super()
// The receiver of a request on super. Only appropriate in that context: this
// is not an expression.
function Super(token) {
Node.call(this, token);
}
util.inherits(Super, Node);
Super.prototype.accept = function (visitor) {
return visitor.visitSuper(this);
};
Super.prototype.toString = function () {
return "super";
};
// new Outer()
// The receiver of a request on outer. Only appropriate in that context: this
// is not as expression.
function Outer(token) {
Node.call(this, token);
}
util.inherits(Outer, Node);
Outer.prototype.accept = function (visitor) {
return visitor.visitOuter(this);
};
Outer.prototype.toString = function () {
return "outer";
};
exports.Node = Node;
exports.Expression = Expression;
exports.Dialect = Dialect;
exports.Identifier = Identifier;
exports.Import = Import;
exports.Request = Request;
exports.UnqualifiedRequest = UnqualifiedRequest;
exports.QualifiedRequest = QualifiedRequest;
exports.RequestPart = RequestPart;
exports.BooleanLiteral = BooleanLiteral;
exports.NumberLiteral = NumberLiteral;
exports.StringLiteral = StringLiteral;
exports.Def = Def;
exports.Var = Var;
exports.ObjectConstructor = ObjectConstructor;
exports.Signature = Signature;
exports.SignaturePart = SignaturePart;
exports.Method = Method;
exports.Class = Class;
exports.Parameter = Parameter;
exports.Block = Block;
exports.Return = Return;
exports.Inherits = Inherits;
exports.Type = Type;
exports.TypeDeclaration = TypeDeclaration;
exports.Self = Self;
exports.Super = Super;
exports.Outer = Outer;