Browse Source

defuck vm.js

master
wes 7 years ago
parent
commit
5057a95252
  1. BIN
      server/.app.js.swp
  2. BIN
      server/.vm.js.swp
  3. 135
      server/cexps.js
  4. 158
      server/closures.js
  5. 5
      server/cps.js
  6. 141
      server/desugar.js
  7. 33
      server/environments.js
  8. 38
      server/errors.js
  9. 94
      server/example.jl
  10. 21
      server/fib.jl
  11. 159
      server/free_vars.js
  12. 844
      server/parse.js
  13. 102
      server/pprint.js
  14. 118
      server/prelude.jl
  15. 404
      server/representation.js
  16. BIN
      server/routes/.index.js.swp
  17. 5
      server/routes/index.js
  18. 149
      server/test.js
  19. 432
      server/tokenize.js
  20. 104
      server/tools.js
  21. 18
      server/typecheck.js
  22. BIN
      server/views/.index.pug.swp
  23. 1
      server/views/index.pug
  24. 87
      server/vm.js

BIN
server/.app.js.swp

Binary file not shown.

BIN
server/.vm.js.swp

Binary file not shown.

135
server/cexps.js

@ -0,0 +1,135 @@
/*
* Defines the data structures associated with Continuation Expressions (cexps)
* The object here is to have an intermediate representation that allows a code generator backend
* to easily create sequential code. In other words we are taking an alegraic/applicative language
* and translating it to something more similar to the "one word at a time" architecture of the von-Neumann
* architecture
*/
var cexp = {
type : "cexp"
};
function record(values_accesspaths,
w,
next_cexp) {
this.values_accesspaths = values_accesspaths;
this.w = w;
this.next_cexp = next_cexp;
return this;
}
record.prototype = cexp;
function select(i, v, w, next_cexp) {
this.i = i;
this.v = v;
this.w = w;
this.next_cexp = next_cexp;
return this;
}
select.prototype = cexp;
function offset(i, v, w, next_cexp) {
this.i = i;
this.v = v;
this.w = w;
this.next_cexp = next_cexp;
return this;
}
offset.prototype = cexp;
function app(k, vs) {
this.k = k;
this.vs = vs;
return this;
}
app.prototype = cexp;
function fix(fs, next_cexp) {
this.fs = fs;
this.next_cexp = next_cexp;
return this;
}
fix.prototype = cexp;
function switchl(v, cexps) {
this.v = v;
this.cexps = cexps;
return this;
}
switchl.prototype = cexp;
function primop(op, vals, vars, next_cexp) {
this.op = op;
this.vals = vals;
this.vars = vars;
this.next_cexp = next_cexp;
return this;
}
primop.prototype = cexp;
function accessPath(offp, selp) {
this.offp = offp;
this.selp = selp;
return this;
}
var primoptype = {
type : "primop",
equal : function(pOp) {
return this.name === pOp.name;
}
};
function Primop(name) {
function ptype() {
this.name = name;
return this;
}
ptype.prototype = primoptype;
return new ptype();
}
var times = Primop("+");
var plus = Primop("+");
var div = Primop("div");
var tilde = Primop("~");
var ieql = Primop("ieql");
var ineq = Primop("ineq");
var lessthan = Primop("<");
var lessoreq = Primop("<=");
var greatthan = Primop(">");
var greatoreq = Primop(">=");
var bang = Primop("!");
var subscript = Primop("subscript");
var ordof = Primop("ordof");
var assign = Primop(":=");
var unboxedassign = Primop("unboxedassign");
var update = Primop("update");
var unboxedupdate = Primop("unboxedupdate");
var store = Primop("store");
var makeref = Primop("makeref");
var makerefunboxed = Primop("makerefunboxed");
var alength = Primop("alength");
var slength = Primop("slength");
var gethdlr = Primop("gethdlr");
var sethdlr = Primop("sethdlr");
var boxed = Primop("boxed");
var fadd = Primop("fadd");
var fsub = Primop("fsub");
var fdiv = Primop("fdiv");
var fmul = Primop("fmul");
var feql = Primop("feql");
var fneq = Primop("fneq");
var fge = Primop("fge");
var fgt = Primop("fgt");
var fle = Primop("fle");
var flt = Primop("flt");
var rshift = Primop("rshift");
var lshift = Primop("lshift");
var orb = Primop("orb");
var andb = Primop("andb");
var xorb = Primop("xorb");
var notb = Primop("notb");

158
server/closures.js

@ -0,0 +1,158 @@
/* Takes an AST and converts all of the functions into "closures"
* A closure is a triple of:
* the bound variables in a function or let
* the free variables in a function or let
* a function body or let body and bound values (if it is an escaping closure only)
* The closure has the property that all of the free variables of the function or let
* are in the environment, or an exception is raised because the variable is not bound
* in the current environment.
* A free variable is simply those that are not in the list of formal parameters or bound variables if it is a let
*
* Therefore in order to call a closure one must first extract the actual function and then
* call the function with the environment associated with it.
* For the purposes of type checking it does not matter how the function gets called, the environment
* is only used for looking up the types of names. Formal parameters are given type variables.
*
* The first phase of closure conversion is not really closure conversion exactly.
* All it does is find out the free variables in scope and tie those up into a data structure with their types later.
* The second phase will be done to the CPS language and closures will actually lambda-lifted out potentially.
*/
var rep = require("./representation.js");
var errors = require("./errors.js");
var parser = require("./parse.js");
var $ = require("./tools.js");
var _ = require("underscore");
var notEmpty = _.compose($.not, _.partial(_.equal, []));
function fvs(stx) {
switch (stx.exprType) {
case "Integer":
return [];
case "Float":
return [];
case "String":
return [];
case "Function":
return [];
case "Nil":
return [];
case "Bool":
return [];
case "Let":
return [];
case "Unary":
return _.flatten([stx.op.ident, fvs(stx.val)]);
case "Definition":
return _.flatten(fvs(stx.val));
case "Application":
var vs = _.flatten(fvs(stx.p));
var f_fvs = _.flatten(fvs(stx.func));
return _.flatten([vs, f_fvs]);
case "If":
if (stx.elseexp) {
var cond_fvs = fvs(stx.condition);
var then_fvs = fvs(stx.thenexp);
var else_fvs = fvs(stx.elseexp);
return _.flatten([cond_fvs, then_fvs, else_fvs]);
}
else {
return _.flatten([fvs(stx.condition), fvs(stx.thenexp)]);
}
break;
case "Name":
return [stx.ident];
}
}
function annotate_fvs(stx) {
/* Takes a stx object that is either
* a lambda
* a let
* and returns a closure wrapped around that stx object
*/
if (stx.exprType !== "Function" &&
stx.exprType !== "Let") {
throw errors.JInternalError(
["Tried to calculate the free variables of",
"something that was not a function or let.\n",
"That something was a: " + stx.exprType +"\n"].reduce(
function (a,b) {
return a+" "+b;
}, ""));
}
var variables, free_variables, bound_vars, stx_type;
switch (stx.exprType) {
case "Let":
bound_vars = stx.pairs.map(
function (stx) {
return stx.ident.ident;
});
var let_fvs = stx.pairs.map(fvs);
var body_fvs = fvs(stx.body);
variables = _.flatten(let_fvs);
$.extend(variables, _.flatten(body_fvs));
break;
case "Function":
bound_vars = [stx.p.ident,];
variables = fvs(stx.body);
break;
}
free_variables = _.difference(_.uniq(variables), bound_vars);
return new rep.Closure(bound_vars, free_variables, stx, []);
}
/*
* This traverse the tree and gathers up all of the free variables of various functions/let bindings
*/
function annotate_fvs_all(stx) {
var closure;
switch (stx.exprType) {
case "Let":
closure = annotate_fvs(stx);
closure.body.pairs = closure.body.pairs.map(annotate_fvs_all);
closure.body = annotate_fvs_all(closure.body.body);
return closure;
case "Function":
closure = annotate_fvs(stx);
closure.body.body = annotate_fvs_all(closure.body.body);
return closure;
case "Unary":
stx.val = annotate_fvs_all(stx.val);
return stx;
case "Application":
stx.func = annotate_fvs_all(stx.func);
stx.p = annotate_fvs_all(stx.p);
return stx;
case "If":
if (stx.elseexp) {
stx.condition = annotate_fvs_all(stx.condition);
stx.thenexp = annotate_fvs_all(stx.thenexp);
stx.elseexp = annotate_fvs_all(stx.elseexp);
return stx;
}
else {
stx.condition = annotate_fvs_all(stx.condition);
stx.thenexp = annotate_fvs_all(stx.thenexp);
return stx;
}
break;
case "Definition":
stx.val = annotate_fvs_all(stx.val);
return stx;
default:
return stx;
}
}
function test(src) {
var ast = parser.parse(src);
}
//console.log(test("if something then if a then if b then c else d else rtrrt else some_other_thing"));
module.exports = {
annotate_fvs : annotate_fvs_all
};

5
server/cps.js

@ -0,0 +1,5 @@
#! /usr/bin/node
var typ = require("representation.js");

141
server/desugar.js

@ -0,0 +1,141 @@
/*
* This module takes a parse tree in a surface format
* and transforms it into the "core" language which is
* much simpler and easier to type-check, optimize, and evaluate
*/
var typ = require("./representation.js");
var errors = require("./errors.js");
var _ = require("underscore");
// Lists get desugared to nested function calls
// i.e. (cons (cons (cons ...)))
function desugarList(lst) {
if (lst.xs.length <= 0) {
return new typ.Nil();
}
else {
var x = desugar(lst.xs[0]);
var rest = lst.xs.slice(1);
return new typ.App(new typ.App(new typ.Name("(:)"), x), desugarList(new typ.ListT(rest)));
}
}
function desugarDefFunc(def) {
return new typ.Def(def.ident,
curryFunc(def.params,
def.body));
}
function curryFunc(ps, body) {
var result;
if (_.isEmpty(ps)) {
return desugar(body);
}
else {
result = new typ.FuncT(desugar(_.first(ps)),
curryFunc(_.rest(ps), body));
result.charnum = ps.charnum;
result.linenum = ps.linenum;
return result;
}
}
function desugarLet(stx) {
var values = stx.pairs.map(desugar);
return new typ.LetExp(values, desugar(stx.body));
}
function sugarTypeDecl(stx) {
var type;
var expression;
type = stx.p;
expression = desugar(stx.func.p);
expression.linenum = stx.linenum;
expression.charnum = stx.charnum;
return new typ.TypeDecl(expression, type);
}
function desugarDefType(stx, typeEnv) {
var result;
var rhs = desugar(stx.rhs);
var name = stx.lhs.name;
typeEnv[name] = rhs;
result = new typ.DefType(stx.lhs, desugar(stx.rhs));
result.linenum = stx.linenum;
result.charnum = stx.charnum;
return result;
}
function desugar(stx, typeEnv) {
var typeExpTest;
switch (stx.exprType) {
case "If":
if (stx.elseexp) {
return new typ.If(desugar(stx.condition, typeEnv), desugar(stx.thenexp, typeEnv), desugar(stx.elseexp, typeEnv));
}
return new typ.If(desugar(stx.condition, typeEnv), desugar(stx.thenexp, typeEnv));
case "FunctionDefinition":
return desugarDefFunc(stx);
case "Definition":
return new typ.Def(stx.ident, desugar(stx.val, typeEnv));
case "TypeDefinition":
return desugarDefType(stx, typeEnv);
case "Name":
return stx;
case "Application":
if ((stx.func.func !== undefined ) &&
(stx.func.func.ident === "::")) {
/* It's a type declaration probably (will be verified later)
* In this case we actually *add* syntax here to differentiate type declarations
* from normal function application
*/
typeExpTest = typ.isTypeExpr(stx.p);
if (typeExpTest.failed !== undefined &&
typeExpTest.failed) {
throw errors.JInternalError(
"Type declaration error near line " + stx.linenum + " at character #"+stx.charnum +
"\n"+typeExpTest.stx.exprType+" (" + typeExpTest.stx.val + ") found where a type operator or type application was expected");
}
return sugarTypeDecl(stx);
}
if ((stx.func.ident === "-" ||
stx.func.ident === "+") &&
stx.p) {
return new typ.UnaryOp(desugar(stx.func, typeEnv), desugar(stx.p, typeEnv));
}
if (stx.p) {
return new typ.App(desugar(stx.func, typeEnv), desugar(stx.p, typeEnv));
}
return new typ.App(stx.func);
case "Function":
return curryFunc(stx.p, stx.body);
case "List":
return desugarList(stx);
case "Bool":
return stx;
case "String":
return stx;
case "Float":
return stx;
case "Integer":
return stx;
case "Let":
return desugarLet(stx);
default:
return stx;
}
}
module.exports = { desugar : desugar };
//var test = typ.ListT([1,2,3]);
//console.log(desugarList(test));

33
server/environments.js

@ -0,0 +1,33 @@
/*
* An environment is just an object that maps identifiers to JLambda expressions
* with a few built-in (a standard Prelude environment)
*/
var errors = require("./errors.js");
var rep = require("./representation.js");
// creates a new environment initialized with the pairs in values
function makeEnv(name, values) {
var env = {};
env.name = name;
env.bindings = {};
for (var i = 0; i < values.length; i++) {
name = values[i][0];
var val = values[i][1];
env.bindings[name] = val;
}
return env;
}
function lookup(name, env) {
var value = env.bindings[name];
if (!value) {
throw errors.JUnboundError(name, env.name);
}
return value;
}
module.exports = {
lookup : lookup,
makeEnv : makeEnv
};

38
server/errors.js

@ -0,0 +1,38 @@
/*
* This file defines common error objects
* for reporting on syntax errors, type errors,
* and perhaps runtime exceptions although I have
* not thought about how that will work much
*/
function JSyntaxError(linenum, charnum, message) {
this.linenum = linenum;
this.charnum = charnum;
this.errormessage = message;
this.stxerror = function() {
console.log("Syntax Error\n",
"Line #", this.linenum,"\n",
"Near character #", this.charnum, "\n",
this.errormessage);
};
return this;
}
function JTypeError(linenum, charnum, token, message) {
this.linenum = linenum;
this.charnum = charnum;
this.errormessage = message;
this.token = token;
return this;
}
function JInternalError(message) {
this.errormessage = message;
return this;
}
module.exports =
{JSyntaxError : JSyntaxError,
JTypeError : JTypeError,
JInternalError : JInternalError
};

94
server/example.jl

@ -0,0 +1,94 @@
def foo# 3
deftype Foo (A -> B)
;; here is a comment
; here is another comment
deftype (Foo a b)
(a -> b)
(qat :: A -> b)
def tdeftype (lambda a b c -> (a + b))
def (add a b)
(a + b)
def wat [[1,2,3], [4,5,6]]
def (catstrs strs)
(foldr f
(head strs)
(tail strs))
def strs ["aa", "bb"]
def (mymap f xs)
if ((length xs) == 0)
then
xs
else
((f (head xs))
: (mymap f (tail xs)))
def empty []
def getFile
(readFile "./parse.js")
;;def fileLines
;; (getFile >>=
;; ((mapM_ putStrLn) . lines))
def (testUnary n)
((-n) + n)
def (foo bar)
let {
lol = [1,
(lambda qwerty blah ->
[qerty, blah,
(lambda whatever -> whatever)])]
}
if bar
then [lol,(- 1.2),"lulz",lol]
else if something
then [,]
else
somethingelse
def (splitHelp acc xs ys)
if (null xs)
then ((reverse acc), ys)
else if (null (tail xs))
then ((reverse acc), ys)
else
(splitHelp ((head ys) : acc)
(tail (tail xs))
(tail ys))
def (splitxs xs)
(splitHelp [] xs xs)
def r def
{
a = 4
}
a
def main
let {
(f a) -> a
unary = (print (testUnary 6))
splitted = def {
xs = (fst (splitxs [12,3,4,56]))
} (xs ++ [0,9])
}
if False
then superduper
else
(unary +
fileLines +
(print splitted))
def blah (3 / 4)

21
server/fib.jl

@ -0,0 +1,21 @@
def (fib n)
if (n == 0)
then 0
else if (n == 1)
then 1
else
((fib (n-1)) +
(fib (n-2)))
def (fact n)
if (n == 0)
then 1
else
((fact (n-1)) * n)
def ns [1,2,3,4,5]
def main
((print (fib 19)) >>
(print (ns >>= (return . fact))))

159
server/free_vars.js

@ -0,0 +1,159 @@
/* Takes an AST and converts all of the functions into "closures"
* A closure is a triple of:
* the bound variables in a function or let
* the free variables in a function or let
* a function body or let body and bound values (if it is an escaping closure only)
* The closure has the property that all of the free variables of the function or let
* are in the environment, or an exception is raised because the variable is not bound
* in the current environment.
* A free variable is simply those that are not in the list of formal parameters or bound variables if it is a let
*
* Therefore in order to call a closure one must first extract the actual function and then
* call the function with the environment associated with it.
* For the purposes of type checking it does not matter how the function gets called, the environment
* is only used for looking up the types of names. Formal parameters are given type variables.
*
* The first phase of closure conversion is not really closure conversion exactly.
* All it does is find out the free variables in scope and tie those up into a data structure with their types later.
* The second phase will be done to the CPS language and closures will actually lambda-lifted out potentially.
*/
var rep = require("./representation.js");
var errors = require("./errors.js");
var parser = require("./parse.js");
var $ = require("./tools.js");
var _ = require("underscore");
var notEmpty = _.compose($.not, _.partial(_.equal, []));
function fvs(stx) {
switch (stx.exprType) {
case "Integer":
return [];
case "Float":
return [];
case "String":
return [];
case "Function":
return [];
case "Nil":
return [];
case "Bool":
return [];
case "Let":
return [];
case "Unary":
return _.flatten([stx.op.ident, fvs(stx.val)]);
case "Definition":
return _.flatten(fvs(stx.val));
case "Application":
var vs = _.flatten(fvs(stx.p));
var f_fvs = _.flatten(fvs(stx.func));
return _.flatten([vs, f_fvs]);
case "If":
if (stx.elseexp) {
var cond_fvs = fvs(stx.condition);
var then_fvs = fvs(stx.thenexp);
var else_fvs = fvs(stx.elseexp);
return _.flatten([cond_fvs, then_fvs, else_fvs]);
}
else {
return _.flatten([fvs(stx.condition), fvs(stx.thenexp)]);
}
break;
case "Name":
return [stx.ident];
}
}
function annotate_fvs(stx) {
/* Takes a stx object that is either
* a lambda
* a let
* and returns a closure wrapped around that stx object
*/
if (stx.exprType !== "Function" &&
stx.exprType !== "Let") {
throw errors.JInternalError(
["Tried to calculate the free variables of",
"something that was not a function or let.\n",
"That something was a: " + stx.exprType +"\n"].reduce(
function (a,b) {
return a+" "+b;
}, ""));
}
var variables, free_variables, bound_vars, stx_type;
switch (stx.exprType) {
case "Let":
bound_vars = stx.pairs.map(
function (stx) {
return stx.ident.ident;
});
var let_fvs = stx.pairs.map(fvs);
var body_fvs = fvs(stx.body);
variables = _.flatten(let_fvs);
$.extend(variables, _.flatten(body_fvs));
break;
case "Function":
bound_vars = [stx.p.ident,];
variables = fvs(stx.body);
break;
}
free_variables = _.difference(_.uniq(variables), bound_vars);
return new rep.Closure(bound_vars, free_variables, stx, []);
}
/*
* This traverse the tree and gathers up all of the free variables of various functions/let bindings
*/
function annotate_fvs_all(stx) {
var closure;
switch (stx.exprType) {
case "Let":
closure = annotate_fvs(stx);
closure.body.pairs = closure.body.pairs.map(annotate_fvs_all);
closure.body = annotate_fvs_all(closure.body.body);
return closure;
case "Function":
closure = annotate_fvs(stx);
closure.body.body = annotate_fvs_all(closure.body.body);
return closure;
case "Unary":
stx.val = annotate_fvs_all(stx.val);
return stx;
case "Application":
stx.func = annotate_fvs_all(stx.func);
stx.p = annotate_fvs_all(stx.p);
return stx;
case "If":
if (stx.elseexp) {
stx.condition = annotate_fvs_all(stx.condition);
stx.thenexp = annotate_fvs_all(stx.thenexp);
stx.elseexp = annotate_fvs_all(stx.elseexp);
return stx;
}
else {
stx.condition = annotate_fvs_all(stx.condition);
stx.thenexp = annotate_fvs_all(stx.thenexp);
return stx;
}
break;
case "Definition":
stx.val = annotate_fvs_all(stx.val);
return stx;
default:
return stx;
}
}
function test(src) {
var ast = parser.parse(src);
}
//console.log(test("if something then if a then if b then c else d else rtrrt else some_other_thing"));
module.export = {
test : test,
annotate_fvs: annotate_fvs_all
};

844
server/parse.js

@ -0,0 +1,844 @@
#! /usr/bin/node
var fs = require("fs");
var typ = require("./representation.js");
var $ = require("./tools.js");
var _ = require("underscore");
var tokenizer = require("./tokenize.js");
var desugarer = require("./desugar.js");
var pprint = require("./pprint.js");
var error = require("./errors.js");
var closure = require("./closures.js");
var print = console.log;
function sourcePos(tokens, linenum, charnum) {
if (!tokens || tokens.length === 0) {
return { linenum : linenum,
charnum : charnum
};
}
return {
linenum : fst(tokens)[3],
charnum : fst(tokens)[2]
};
}
function addSrcPos(stx, tokens, linenum, charnum) {
var pos = sourcePos(tokens, linenum, charnum);
stx.linenum = pos.linenum;
stx.charnum = pos.charnum;
return stx;
}
/* Gets the first token from the right side of the stack */
function fst(ts) {
return ts[ts.length-1];
}
/* Gets the second token from the right side of the stack */
function snd(ts) {
return ts[ts.length-2];
}
/*Checks if the next token is not followed by any of ``checks'' */
function notFollowedBy(tokens, checks, linenum, charnum) {
if (!fst(tokens)) {
throw error.JSyntaxError(0,0,"unexpected end of source");
}
var nextT = fst(tokens)[0];
if (checks.some(function (x) {
return x === nextT;
}))
return false;
else
return true;
}
/* returns a function that takes a parameter and
checks if it is in the array ``props''*/
function makeChecker(props) {
return function(x) {
return x && props.some(function (y) {return y(x);});
};
}
function tokTypeCheck(name) {
return function(tok) {
return tok[0] === name;
};
}
function formTypeCheck(stxtype) {
return function(stx) {
return stx.exprType === stxtype;
};
}
/*Tries to parse until the prediction ``valid'' fails or the wrong type is parsed
Collects the results into an array and returns it*/
function parseMany(parse, exprType, valid, tokens, charnum, linenum) {
if (!fst(tokens)) {
throw error.JSyntaxError(charnum,
linenum,
"Unexpected end of source");
}
var current = fst(tokens)[0];
var results = [];
var parsed;
if (valid(fst(tokens))) {
parsed = parse(tokens);
}
else {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"Unexpected token: ``"+fst(tokens)[0]+"''");
}
results.push(parsed);
//make sure there are at least 2 tokens to parse
if (tokens.length > 1 && fst(tokens) && valid(fst(tokens))) {
while (valid(snd(tokens))) {
if (!(valid(fst(tokens)))) {
break;
}
results.push(parse(tokens));
if (!exprType(fst(results).exprType)) {
break;
}
if (fst(tokens)) {
current = fst(tokens)[0];
}
else {
throw error.JSyntaxError(linenum,
charnum,
"Unexpected end of source");
}
if (tokens.length <= 1) {
break;
}
}
}
//do the same validity check as before and in the loop
if (!fst(tokens)) {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"unexpected end of source");
}
if (valid(fst(tokens))) {
results.push(parse(tokens));
}
return results;
}
/* Tries to parse exprType separated by the token between
* e.g. <identifier>,<identifier>,...
*/
function parseBetween(exprType, between, tokens, charnum, linenum) {
var first = parse(tokens);
var items;
var parsed;
if (!exprType(first)) {
throw error.JSyntaxError(fst(tokens)[2], fst(tokens)[3], "Unexpected token: ``"+fst(tokens)[0]+"''");
}
items = [first];
if (tokens.length > 1 && fst(tokens)[0] === between) {
while (fst(tokens)[0] === between) {
tokens.pop();
parsed = parse(tokens);
if (!fst(tokens))
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"Missing terminator: "+between);
items.push(parsed);
}
return items;
}
return items;
}
function parseList(tokens, linenum, charnum) {
var xs;
var result;
if (fst(tokens)[0] === "right_square") {
xs = [];
}
else if (fst(tokens)[0] === "comma") {
tokens.pop();
xs = [];
}
else {
xs = parseBetween(function (x) {
return true;
}, "comma", tokens, fst(tokens)[3], fst(tokens)[2]);
}
if (!fst(tokens) || fst(tokens)[0] !== "right_square") {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"list must be terminated by ]");
}
tokens.pop();
result = addSrcPos(new typ.ListT(xs), tokens, linenum, charnum);
return result;
}
function parseDefFunction(tokens, linenum, charnum) {
var fname = parse(tokens);
var parameters;
var result;
var body;
if (fname.exprType !== "Name") {
throw error.JSyntaxError(linenum,
charnum,
"Expected an identifier in function definition");
}
if (fst(tokens)[0] === "right_paren") {
parameters = [];
}
else {
parameters = parseMany(parse,
validName,
validFormPar,
tokens,
charnum,
linenum);
}
if (!tokens || (fst(tokens)[0]) !== "right_paren") {
throw error.JSyntaxError(linenum,
charnum,
"Formal parameters must be followed by )");
}
tokens.pop();
body = parse(tokens);
result = addSrcPos(new typ.DefFunc(fname, parameters, body), tokens, body.linenum, body.charnum);
return result;
}
validLet = makeChecker(["Definition", "FunctionDefinition"].map(formTypeCheck));
letEnd = _.compose($.not, makeChecker(["right_brace"].map(tokTypeCheck)));
function parseLetForm(tokens, linenum, charnum) {
var result;
var pairs;
var body;
if (!fst(tokens)) {
error.JSyntaxError(linenum,
charnum,
"Unexpected end of source");
}
pairs = parseMany(parseLetItem,
validLet,
letEnd,
tokens,
charnum,
linenum);
if (fst(tokens) && fst(tokens)[0] !== "right_brace") {
throw error.JSyntaxError(fst(tokens)[2],
fst(tokens)[3],
"let/def form must have a closing }");
}
if (!fst(tokens)) {
throw error.JSyntaxError(_.last(pairs).linenum,
_.last(pairs).charnum,
"Unexpected end of source");
}
tokens.pop();
if (tokens.length <= 0) {
throw error.JSyntaxError(_.last(pairs).linenum,
_.last(pairs).charnum,
"let/def form must have a body");
}
body = parse(tokens);
if (body.exprType === "Definition" ||
body.exprType === "FunctionDefinition") {
throw error.JSyntaxError(body.linenum,
body.charnum,
"Body of a let/def expression cannot be a definition");
}
result = addSrcPos(new typ.LetExp(pairs, body), tokens, body.linenum, body.charnum);
return result;
}
function parseLetFunction(tokens, linenum, charnum) {
var fname = parse(tokens);
var parameters;
var result;
var body;
if (fname.exprType != "Name") {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"Expected an identifier in function definition");
}
if (fst(tokens)[0] === "right_paren") {
parameters = [];
}
else {
parameters = parseMany(parse,
validName,
validFormPar,
tokens,
charnum,
linenum);
}
if ((fst(tokens)[0]) !== "right_paren") {
throw error.JSyntaxError(linenum,
charnum,
"Formal parameters must be followed by )");
}
tokens.pop();
if (fst(tokens)[1] !== "->") {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"Function parameters in let/def form must be followed by ->");
}
tokens.pop();
body = parse(tokens);
result = addSrcPos(new typ.DefFunc(fname, parameters, body), tokens, body.linenum, body.charnum);
return result;
}
function parseLetBinding(tokens, linenum, charnum) {
var name = parse(tokens);
var result;
var bound;
if (name.exprType != "Name") {
throw error.JSyntaxError(name.linenum,
name.charnum,
"Expected an identifier in let/def binding");
}
if (!fst(tokens) || fst(tokens)[1] !== "=") {
throw error.JSyntaxError(name.linenum,
name.charnum,
"An identifier in a let/def binding must be followed by ``=''");
}
tokens.pop();
if (!notFollowedBy(tokens,
["comma", "arrow", "right_brace", "right_square"],
name.linenum,
name.charnum)) {
throw error.JSyntaxError(name.linenum,
name.charnum,
"The binding of " + identifier.val + " must not be followed by " + fst(tokens)[0]);
}
bound = parse(tokens);
if (bound.exprType === "Definition" ||
bound.exprType === "FunctionDefinition") {
throw error.JSyntaxError(bound.linenum,
bound.charnum,
"A definition cannot be the value of a binding");
}
result = addSrcPos(new typ.Def(name, bound), tokens, bound.linenum, bound.charnum);
return result;
}
function parseLetItem(tokens) {
var linenum = fst(tokens)[3];
var charnum = fst(tokens)[2];
if (fst(tokens) && fst(tokens)[0] === "left_paren") {
tokens.pop();
return parseLetFunction(tokens,
linenum,
charnum);
}
else {
return parseLetBinding(tokens,
linenum,
charnum);
}
}
function parseDataType(tokens, linenum, charnum) {
var typeName = parse(tokens, linenum, charnum);
var typeParams;
var typeBody;
var result;
if (typeName.exprType !== "TypeOperator") {
throw error.JSyntaxError(typeName.linenum,
typeName.charnum,
"Expected a type operator in data type definition");
}
if (fst(tokens)[0] !== "right_paren") {
parameters = parseMany(parse,
validName,
validFormPar,
tokens,
charnum,
linenum);
}
else {
parameters = [];
}
if (!tokens || (fst(tokens)[0]) !== "right_paren") {
throw error.JSyntaxError(_.last(parameters).linenum,
_.last(parameters).charnum,
"Data type parameters must be followed by )");
}
tokens.pop();
typeBody = parse(tokens);
result = addSrcPos(new typ.DataType(typeName, parameters, typeBody), tokens, typeBody.linenum, typeBody.charnum);
return result;
}
function parseDefType(tokens, linenum, charnum) {
var result;
var rhs;
var lhs;
if (tokens.length < 2) {
/* Minimal number of tokens required is 2
* because it could be 'deftype Foo Blah'
*/
throw error.JSyntaxError(linenum,
charnum,
"Unexpected end of source");
}
if (fst(tokens)[0] === "left_paren") {
/* It's an actual data type definition
* i.e. not just an alias
*/
tokens.pop();
return parseDataType(tokens, linenum, charnum);
}
if (notFollowedBy(tokens, ["constructor"], linenum, charnum)) {
throw error.JSyntaxError(linenum,
charnum,
"deftype must be followed by a single constructor" +
" if it is not a data type definition with type variables");
}
else {
lhs = parse(tokens, linenum, charnum);
if (!tokens) {
throw error.JSyntaxError(lhs.linenum,
lhs.charnum,
"Unexpected end of source");
}
if (lhs.exprType !== "TypeOperator") {
throw error.JSyntaxError(lhs.linenum,
lhs.charnum,
"left-hand side of type alias was not a type operator");
}
rhs = parse(tokens, linenum, charnum);
if (rhs.exprType !== "Application" &&
rhs.exprType !== "TypeOperator") {
throw error.JSyntaxError(rhs.linenum,
rhs.charnum,
"was expecting an application or type operator on the right-hand side of a type alias");
}
result = addSrcPos(new typ.DefType(lhs, rhs), tokens, rhs.linenum, rhs.charnum);
return result;
}
}
function parseDef(tokens, linenum, charnum) {
var result;
var identifier;
var bound;
if (tokens.length < 2) {
/* Minimal number of tokens required is 2
* because it could be 'def foo blah'
*/
throw error.JSyntaxError(linenum,
charnum,
"Unexpected end of source");
}
if (fst(tokens)[0] === "left_paren") {
/* It's a function definition */
tokens.pop();
return parseDefFunction(tokens, linenum, charnum);
}
if (fst(tokens)[0] === "left_brace") {
/* It's a let/def form */
tokens.pop();
return parseLetForm(tokens,
fst(tokens)[3],
fst(tokens)[2]);
}
if (notFollowedBy(tokens, ["identifier"], linenum, charnum)) {
throw error.JSyntaxError(linenum,
charnum,
"def must be followed by identifier, not "+fst(tokens)[0]);
}
else {
identifier = parse(tokens);
if (!fst(tokens))
throw error.JSyntaxError(identifier.linenum,
identifier.charnum,
"Unexpected end of source");
if (!notFollowedBy(tokens,
["comma", "arrow", "right_brace", "right_square"],
identifier.linenum,
identifier.charnum)) {
throw error.JSyntaxError(identifier.linenum,
identifier.charnum,
"def " + identifier.val + " must not be followed by " + fst(tokens)[0]);
}
bound = parse(tokens);
if (bound.exprType === "Definition" ||
bound.exprType === "FunctionDefinition") {
throw error.JSyntaxError(bound.linenum,
bound.charnum,
"A definition cannot be the value of a binding");
}
result = addSrcPos(new typ.Def(identifier, bound), tokens, bound.linenum, bound.charnum);
return result;
}
}
function parseDefOp(tokens, linenum, charnum) {
var result;
var names;
var pattern;
if (fst(tokens)[0] !== "integer" ||
fst(tokens)[1] < 1) {
throw error.JSyntaxError(linenum,
charnum,
"defop must be followed by integer precedence >= 1");
}
tokens.pop();
if (fst(tokens)[1] !== "Left" && fst(tokens)[1] !== "Right") {
throw error.JSyntaxError(linenum,
charnum,
"defop must be followed by precedence and then either Left or Right");
}
tokens.pop();
if (fst(tokens)[0] !== "left_paren") {
throw error.JSyntaxError(linenum,
charnum,
"defop arguments must start with (");
}
tokens.pop();
if (!(tokens.slice(tokens.length-3,
tokens.length).every(function(x) {
return x[0] === "identifier";
}))) {
throw error.JSyntaxError(linenum,
charnum,
"defop must be surrounded by exactly 3 identifiers");
}
pattern = tokens.slice(tokens.length-3,
tokens.length);
tokens.pop(); tokens.pop(); tokens.pop();
if (fst(tokens)[0] !== "right_paren") {
throw error.JSyntaxError(linenum,
charnum,
"defop pattern must be terminated with )");
}
tokens.pop();
names = [new typ.Name(pattern[1][1]),
new typ.Name(pattern[0][1]),
new typ.Name(pattern[2][1])];
names.map(function(name) {
name.linenum = linenum;
name.charnum = charnum;
return name;
});
result = addSrcPos(new typ.DefFunc(names[0],
names.slice(1,3),
parse(tokens)),
tokens,
_.last(names).linenum,
_.last(names).charnum);
return result;
}
function parseIf(tokens, linenum, charnum) {
var result;
var ifC;
var thenC;
var elseC;
if (!notFollowedBy(tokens,
["def","comma","lambda"],
linenum,
charnum)) {
throw error.JSyntaxError(linenum,
charnum,
"``if'' cannot be followed by "+fst(tokens)[0]) ;
}
else {
ifC = parse(tokens);
if (!fst(tokens) || fst(tokens)[0] !== "thenexp") {
throw error.JSyntaxError(ifC.linenum,
ifC.charnum,
"if ``exp'' must be folowed by ``then'' exp, not "+snd(tokens)[0]);
}
else {
tokens.pop();
thenC = parse(tokens);
if (fst(tokens) && fst(tokens)[0] === "elsexp") {
tokens.pop();
if (_.size(tokens) < 1) {
throw error.JSyntaxError(thenC.linenum,
thenC.charnum,
"Unexpected end of source");
}
else {
elseC = parse(tokens);
result = addSrcPos(new typ.If(ifC, thenC, elseC), tokens, elseC.linenum, elseC.charnum);
return result;
}
}
else {
throw error.JSyntaxError(thenC.linenum,
thenC.charnum,
"If expression must include an else variant");
}
}
}
}
var validName = makeChecker(["Name"].map(formTypeCheck));
function validFormPar(tok) {
return tok[0] === "identifier" &&
tok[1] !== "->";
}
function parseLambda(tokens, linenum, charnum) {
var result;
var parameters = parseMany(parse,
validName,
validFormPar,
tokens,
charnum,
linenum);
if (fst(tokens)[1] !== "->") {
throw error.JSyntaxError(_.last(parameters).linenum,
_.last(parameters).charnum,
"arrow must follow parameters in lambda, not "+fst(tokens)[0]);
}
tokens.pop();
var body = parse(tokens);
result = addSrcPos(new typ.FuncT(parameters, body), tokens, body.linenum, body.charnum);
return result;
}
var invalidArguments = ["def", "comma", "right_paren", "right_square", "right_brace", "left_brace", "right_brace"].map(tokTypeCheck);
var validArgument = _.compose($.not, makeChecker(invalidArguments));
var validArgTypes = _.compose($.not, makeChecker(["Definition"].map(formTypeCheck)));
/* Parses function application (either infix or prefix) */
function computeApp(tokens, charnum, linenum) {
var lhs = parse(tokens);
var next;
var result;
var parameters;
if (fst(tokens)) {
next = fst(tokens);
}
else {
throw error.JSyntaxError(lhs.linenum,
lhs.charnum,
"Unexpected end of source");
}
if (typ.OPInfo[next[1]]) {
/* it's an infix expression */
result = parseInfix(tokens, 1, lhs, lhs.linenum, lhs.charnum);
if (!fst(tokens) || fst(tokens)[0] !== "right_paren") {
throw error.JSyntaxError(lhs.linenum,
lhs.charnum,
"Mismatched parentheses or missing parenthesis on right-hand side");
}
else {
tokens.pop();
return result;
}
}
else {
/* it's a prefix application */
if (fst(tokens)[0] !== "right_paren") {
parameters = parseMany(parse,
validArgTypes,
validArgument,
tokens,
fst(tokens)[2],
fst(tokens)[3]);
}
else {
parameters = [];
}
if ((!fst(tokens)) || fst(tokens)[0] !== "right_paren") {
throw error.JSyntaxError(fst(tokens)[3],
fst(tokens)[2],
"Mismatched parentheses or missing parenthesis on right-hand side");
}
else {
tokens.pop();
return addSrcPos(typ.makeApp(lhs, parameters), tokens, linenum, charnum);
}
}
}
/*Parses infix expressions by precedence climbing
* console.log(stx);
See this for more info and an implementation in python
http://eli.thegreenplace.net/2012/08/02/parsing-expressions-by-precedence-climbing/
*/
function parseInfix(tokens, minPrec, lhs, linenum, charnum) {
if (!lhs) {
lhs = parse(tokens);
}
while (true) {
var cur = fst(tokens);
if (!cur) {
throw error.JSyntaxError(linenum,
charnum,
"Unexpected end of source");
}
var opinfo = typ.OPInfo[cur[1]];
if (!opinfo || opinfo[0] < minPrec)
break;
var op = addSrcPos(new typ.Name(cur[1]), tokens, linenum, charnum);
var prec = opinfo[0];
var assoc = opinfo[1];
var nextMinPrec = assoc === "Left" ? prec + 1 : prec;
tokens.pop();
/*remove the operator token*/
var rhs = parseInfix(tokens, nextMinPrec);
lhs = addSrcPos(typ.makeApp(op, [lhs, rhs]), tokens, rhs.linenum, rhs.charnum);
}
return lhs;
}
function parse(tokens) {
var charnum = fst(tokens)[2];
var linenum = fst(tokens)[3];
var toktype;
var result;
if (fst(tokens)) {
toktype = fst(tokens)[0];
}
else {
process.exit(code=1);
}
var token = fst(tokens)[1];
tokens.pop();
if (toktype === "stringlit") {
result = addSrcPos(new typ.StrT(token), tokens, linenum, charnum);
return result;
}
else if (toktype === "left_square") {
return parseList(tokens, linenum, charnum);
}
else if (toktype === "lambda") {
return parseLambda(tokens, linenum, charnum);
}
else if (toktype === "integer") {
result = addSrcPos(new typ.IntT(token), tokens, linenum, charnum);
return result;
}
else if (toktype === "float") {
result = addSrcPos(new typ.FloatT(token), tokens, linenum, charnum);
return result;
}
else if (toktype === "identifier") {
result = addSrcPos(new typ.Name(token), tokens, linenum, charnum);
return result;
}
else if (toktype === "constructor") {
result = addSrcPos(new typ.TypeOp(token), tokens, linenum, charnum);
return result;
}
else if (toktype === "truelit" || toktype === "falselit") {
result = addSrcPos(new typ.BoolT(token), tokens, linenum, charnum);
return result;
}
else if (toktype === "def" ||
toktype === "let") {
return parseDef(tokens, linenum, charnum);
}
else if (toktype === "deftype") {
return parseDefType(tokens, linenum, charnum);
}
else if (toktype === "defop") {
return parseDefOp(tokens, linenum, charnum);
}
else if (toktype === "ifexp") {
return parseIf(tokens, linenum, charnum);
}
else if (toktype === "left_paren") {
if (fst(tokens)[0] === "lambda") {
tokens.pop();
var parsed = parseLambda(tokens, linenum, charnum);
tokens.pop();
return parsed;
}
else
return computeApp(tokens, linenum, charnum);
}
else {
throw error.JSyntaxError(linenum,
charnum,
"Unexpected token: ``" + toktype+"''");
}
}
function parseFull(tokenized) {
var ast = [];
var typeBindings = {};
var current;
try {
while (tokenized.length > 0) {
current = closure.annotate_fvs(desugarer.desugar(parse(tokenized), typeBindings));
ast.push(current);
}
return {
"ast" : ast,
"types" : typeBindings
};
} catch (e) {
if (e.stxerror !== undefined) {
e.stxerror();
process.exit(1);
}
else {
console.log(e.errormessage);
process.exit(1);
}
}
}
module.exports = { parse : function(str) {
return parseFull(tokenizer.tokenize(str));
},
tokenize : tokenizer.tokenize,
parseFull : parseFull,
};
//var istr = fs.readFileSync('/dev/stdin').toString();
//var testParse = parseFull(tokenizer.tokenize(istr));
//console.log(testParse[1]);
//console.log(testParse[0].map(pprint.pprint));

102
server/pprint.js

@ -0,0 +1,102 @@
function pprintName(ident) {
return pprint(ident.val);
}
function pprintFunc(func) {
if (func.p.exprType === "Name")
return "(lambda " + pprint(func.p) + " -> " + pprint(func.body) + ")";
else
return "(lambda " + func.p.map(pprint).join(" ") + " -> " + pprint(func.body) + ")";
}
function pprintApp(app) {
if (!app.p || app.p === undefined)
return pprint(app.func);
return "((" + pprint(app.func) + ") " + pprint(app.p) + ")";
}
function pprintDef(def) {
return pprint(def.ident) + " = " + pprint(def.val);
}
function pprintIf(ifexp) {
return ("(if " + pprint(ifexp.condition) +
" then " + pprint(ifexp.thenexp) +
" else " + pprint(ifexp.elseexp) + ")");
}
function pprintDefType(stx) {
return pprint(stx.lhs) + " = " + pprint(stx.rhs);
}
function pprintTypeFunc(stx) {
return "(" + stx.name.name + " " + stx.params.map(pprint).join(" ") + ") = " + pprint(stx.type);
}
function pprint(expr) {
if (expr.exprType === "Name") {
return expr.val;
}
else if (expr.exprType === "Bool") {
if (expr.val) {
return "True";
}
else {
return "False";
}
}
else if (expr.exprType === "Integer") {
return "("+expr.val+")";
}
else if (expr.exprType === "Float") {
return "("+expr.val+")";
}
else if (expr.exprType === "String") {
return '"'+expr.val+'"';
}
else if (expr.exprType === "Name") {
return expr.val;
}
else if (expr.exprType === "Application") {
return pprintApp(expr);
}
else if (expr.exprType === "Definition") {
return pprintDef(expr);
}
else if (expr.exprType === "TypeDefinition") {
return pprintDefType(expr);
}
else if (expr.exprType === "TypeFuncDefinition") {
return pprintTypeFunc(expr);
}
else if (expr.exprType === "If") {
return pprintIf(expr);
}
else if (expr.exprType === "Function") {
return pprintFunc(expr);
}
else if (expr.exprType === "Nil") {
return "[]";
}
else if (expr.exprType === "Unary") {
return "("+expr.op.ident+" "+pprint(expr.val)+")";
}
else if (expr.exprType === "Let") {
return "let {" + expr.pairs.map(
function (v) {
return pprint(v);
}).join(" ; ") + "} in " + pprint(expr.body);
}
else if (expr.exprType === "TypeOperator") {
return "("+expr.val+")";
}
else if (expr.exprType === "TypeVar") {
return "("+expr.name+")";
}
else if (expr.exprType === "TypeDeclaration") {
return "( " + pprint(expr.expression) + " :: " + pprint(expr.type) + " )";
}
}
module.exports = {pprint : pprint};

118
server/prelude.jl

@ -0,0 +1,118 @@
;; This file declares the various types used by intrinsic/prelude definitions
;; It is sort of special in that it doesn't care whether there are any associated definitions
;; just that there are type definitions for that particular binding's name
;; Type definitions
deftype String (Vector Char)
deftype (Int) Intrinsic
deftype (Float) Intrinsic
deftype (Char) Intrinsic
deftype (Byte) Intrinsic
deftype (Void) Intrinsic
deftype (IO a) Intrinsic
deftype (Vector a) Intrinsic
deftype (List a)
(Empty |
(Cons a (List a)))
deftype (Bottom)
Undefined
deftype (Maybe a)
(Nothing |
(Just a))
deftype (Either a b)
((Left a) |
(Right b))
;; List functions
(: :: (a -> (List a) -> (List a)))
(map :: ((a -> b) -> (List a) -> (List b)))
(head :: ((List a) -> a))
(tail :: ((List a) -> (List a)))
(!! :: (Int -> (List a) -> a))
(take :: (Int -> (List a) -> (Maybe (List a))))
(drop :: (Int -> (List a) -> (Maybe (List a))))
;; Optional functions
(maybe :: (b -> (a -> b) -> (Maybe a) -> b))
(either :: ((b -> c) -> (b -> c) -> (Either a b) -> c))
;; I/O functions
(print :: (String -> (IO Void)))
;; Operator definitions
defop 1 Left (a + b)
(add a b)
defop 1 Left (a - b)
(minus a b)
defop 2 Left (a * b)
(mul a b)
defop 2 Left (a / b)
(div a b)
defop 2 Right (a ^ b)
(pow a b)
defop 3 Left (a ++ b)
(listConcat a b)
defop 3 Left (a == b)
(eq a b)
defop 3 Left (a > b)
(gt a b)
defop 3 Left (a >= b)
(gte a b)
defop 3 Left (a < b)
(lt a b)
defop 3 Left (a <= b)
(lte a b)
defop 3 Left (a && b)
(and a b)
defop 3 Left (a || b)
(or a b)
defop 4 Left (x : xs)
(cons x xs)
defop 5 Left (f $ x)
(fapply f x)
defop 5 Left (f . g)
(compose f g)
defop 3 Left (a | b)
(bitwiseOr a b)
defop 3 Left (a & b)
(bitwiseAnd a b)

404
server/representation.js

@ -0,0 +1,404 @@
var errors = require("./errors.js");
var _ = require("underscore");
var Expression = {
display :
function() {
return this.exprType + " " + this.val;
},
type :
function () {
return this.exprType;
},
linenum : 0,
charnum : 0
};
var TypeExpression = {
unify :
function (t) {
if (this.expr === t.expr) {
return t.expr;
}
else {
console.log("Could not unify " + this.expr + " with " + t.expr);
}
},
isTypeExpr : true,
linenum : 0,
charnum : 0
};
function isTypeExpr(x) {
return x.isTypeExpr !== undefined;
}
function isIrregularTypeOp(x) {
return (x === "->");
}
function flattenTypeDecl(stx) {
if (isTypeExpr(stx)) {
return true;
}
if (stx.exprType === "Application") {
/* it might be a type application so recursively check it */
if (stx.p !== undefined) {
return _.flatten([flattenTypeDecl(stx.p), flattenTypeDecl(stx.func)]);
}
else {
return _.flatten([flattenTypeDecl(stx.func)]);
}
}
if (stx.exprType === "Name") {
/*
* Either it is a type operator
* or we assume it is a type variable
* since it was not capitalized
*/
return true;
}
return {
failed : true,
stx : stx
};
}
function isTypeExprRec(stx) {
var flattened = flattenTypeDecl(stx);
for(var i = 0; i < flattened.length; i++) {
if (flattened[i].failed !== undefined &&
flattened[i].failed) {
return flattened[i];
}
}
return true;
}
function App(func, p) {
this.func = func;
this.exprType = "Application";
if (p)
this.p = p;
return this;
}
function Closure(bound_vars, free_vars, body, env) {
this.bound_vars = bound_vars;
this.free_vars = free_vars;
this.body = body;
this.env = env;
this.exprType = "Closure";
return this;
}
function LetExp(pairs, body) {
if (!pairs.every(function(x) {
return (x.exprType === "Definition" ||
x.exprType === "FunctionDefinition");
})) {
throw errors.JInternalError(
"let can only be used to bind things to names or functions"
);
}
this.exprType = "Let";
this.val = [pairs, body];
this.pairs = pairs;
this.body = body;
return this;
}
LetExp.prototype = Expression;
function UnaryOp(op, v) {
this.exprType = "Unary";
this.val = v;
this.op = op;
return this;
}
UnaryOp.prototype = Expression;
function IntT(v) {
this.exprType = "Integer";
this.val = parseInt(v, 10);
return this;
}
IntT.prototype = Expression;
function FloatT(v) {
this.exprType = "Float";
this.val = parseFloat(v, 10);
return this;
}
FloatT.prototype = Expression;
function StrT(v) {
this.exprType = "String";
this.val = v;
return this;
}
StrT.prototype = Expression;
function BoolT(b) {
if (b === "true") {
this.val = true;
}
else {
this.val = false;
}
this.exprType = "Bool";
return this;
}
BoolT.prototype = Expression;
function ListT(xs) {
this.xs = xs;
this.val = xs;
this.exprType = "List";
return this;
}
ListT.prototype = Expression;
function Nil() {
this.exprType = "Nil";
return this;
}
Nil.prototype = Expression;
function FuncT(p, body) {
this.p = p;
this.body = body;
this.val = [p, body];
this.exprType = "Function";
return this;
}
FuncT.prototype = Expression;
//Wrapper for function objects
function OpT(operator) {
this.op = operator;
this.val = this.op;
this.exprType = "Function";
return this;
}
OpT.prototype = Expression;
// Applications separate from other types
function App(func, p) {
this.func = func;
this.exprType = "Application";
if (p)
this.p = p;
return this;
}
App.prototype = Expression;
// Names are not types
function Name(identifier) {
this.ident = identifier;
this.val = this.ident;
this.exprType = "Name";
return this;
}
Name.prototype = Expression;
function Def(ident, exp) {
this.ident = ident;
this.val = exp;
this.exprType = "Definition";
return this;
}
Def.prototype = Expression;
function DefFunc(ident, params, body) {
this.ident = ident;
this.val = this.ident;
this.params = params;
this.body = body;
this.exprType = "FunctionDefinition";
return this;
}
DefFunc.prototype = Expression;
function If(condition, thenexp, elseexp) {
this.condition = condition;
this.thenexp = thenexp;
this.elseexp = elseexp;
this.exprType = "If";
return this;
}
If.prototype = Expression;
function TypeVar(name) {
this.exprtype = "TypeVar";
this.name = name;
this.exprType = "TypeVar";
return this;
}
TypeVar.prototype = TypeExpression;
function TypeOp(name) {
this.name = name;
this.val = name;
this.exprType = "TypeOperator";
return this;
}
TypeOp.prototype = TypeExpression;
function isTypeExpr(expr) {
if (!expr.exprType) {
throw errors.JInternalError(expr);
}
return ((expr.exprType === "TypeOperator") ||
(expr.exprType === "TypeVar") ||
(expr.exprType === "TypeDeclaration"));
}
function TypeDecl(expression, type) {
if (isTypeExprRec(expression) &&
expression.exprType !== "Name") {
throw errors.JSyntaxError(
expression.linenum,
expression.charnum,
"Left-hand-side of type declaration must not be in the type language"
);
}
if (isTypeExprRec(type).failed) {
throw errors.JInternalError(
"Right-hand-side of type declaration must be a type expression"
);
}
this.expression = expression;
this.type = type;
this.exprType = "TypeDeclaration";
return this;
}
TypeDecl.prototype = TypeExpression;
function DefType(lhs, rhs) {
/* Both rhs and lhs are expected
* to be fully desugared already
*/
if (isTypeExprRec(rhs).failed ||
!isTypeExpr(lhs)) {
throw errors.JSyntaxError(
rhs.linenum,
rhs.charnum,
"Illegal type definition, both sides must be valid type expressions");
}
this.rhs = rhs;
this.lhs = lhs;
this.exprType = "TypeDefinition";
return this;
}
DefType.prototype = Expression;
function checkName(exp) {
if (exp.exprType !== "Name") {
throw errors.JSyntaxError(
exp.linenum,
exp.charnum,
"Expected a type variable (an identifier starting with a lowercase character), got " + exp.val);
}
}
function DataType(name, params, type) {
/* Params is a list of type variables
* type is a type expression
*/
if (name.exprType !== "TypeOperator") {
throw errors.JSyntaxError(
name.linenum,
name.charnum,
"First element in a data type definition must be its name " +
"which is a type operator");
}
_.each(params, checkName);
if (isTypeExprRec(type).failed) {
throw errors.JSyntaxError(
type.linenum,
type.charnum,
"Body of a type definition must be a valid type expression");
}
this.name = name;
this.params = params;
this.type = type;
this.exprType = "TypeFuncDefinition";
return this;
}
/* Applies the function ``name'' to the list of parameters */
function makeApp(name, parameters) {
if (parameters) {
return parameters.slice(1).reduce(function(f, ident) {
return new App(f, ident);
}, new App(name, parameters[0]));
}
else {
return new App(name);
}
}
function makeGensym() {
var n = 0;
return function() {
var x = "G"+n;
n = n + 1;
return x;
};
}
var gensym = makeGensym();
OPInfo = {
"::" : [2, "Left"],
"," : [1, "Left"],
"->" : [1, "Right"]
};
module.exports =
{
IntT : IntT,
FloatT : FloatT,
StrT : StrT,
BoolT : BoolT,
ListT : ListT,
FuncT : FuncT,
App : App,
Name : Name,
Def : Def,
OpT : OpT,
OPInfo : OPInfo,
makeApp : makeApp,
If : If,
DefFunc : DefFunc,
UnaryOp : UnaryOp,
Nil : Nil,
LetExp : LetExp,
gensym : gensym,
TypeVar : TypeVar,
TypeOp : TypeOp,
TypeDecl : TypeDecl,
Closure : Closure,
isTypeExpr : isTypeExprRec,
DefType : DefType,
DataType : DataType
};

BIN
server/routes/.index.js.swp

Binary file not shown.

5
server/routes/index.js

@ -1,9 +1,12 @@
var express = require('express');
var router = express.Router();
var vm = require("../vm.js");
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
var query_params = req.query;
var evaluated = vm.evaluate(query_params.source);
res.render('index', { title: 'Express', output: JSON.stringify(evaluated)});
});
module.exports = router;

149
server/test.js

@ -0,0 +1,149 @@
#! /usr/bin/node
var parser = require("./parse.js");
var cexps = require("./cexps.js");
var closures = require("./closure_conversion.js");
var desugar = require("./desugar.js");
var environments = require("./environments.js");
var errors = require("./errors.js");
var tokens = require("./tokenize.js");
var tools = require("./tools.js");
var typecheck = require("./typecheck.js");
var representation = require("./representation.js");
var _ = require("underscore");
var qc = require("quickcheck");
var assert = require("assert");
/* my own generators */
function arbChars(n, max, min) {
return function () {
return _.invoke(_.times(_.random(1, n),
_.partial(arbChar, max, min)),
"call");
};
}
function arbChar(max, min) {
return function() {
return String.fromCharCode(_.random(max, min));
};
}
function arbCharRanges(ranges, max) {
return function() {
return _.flatten(
_.shuffle(
_.map(ranges,
function(bound) {
return _.sample(arbChars(max, bound[0], bound[1])(),
bound[1] - bound[0]);
}))).join("");
};
}
var arbName = arbCharRanges([[33, 33],
[35, 39],
[42,43],
[45, 122],
[124, 126]],
200);
var arbCapital = arbChar(65, 90);
function arbArray(gen) {
return qc.arbArray(gen);
}
function arbStrings() {
return qc.arbArray(qc.arbString);
}
function arbPair(gen) {
return function() {
return [gen(), gen()];
};
}
function arbArrayofPairs() {
return arbArray(function() {
return arbArray(arbPair(qc.arbString));
});
}
function arbPairs() {
return arbArray(arbPair(qc.arbString));
}
/* Tests for misc tools */
function emptyProp(xs) {
return (tools.empty(xs) === tools.empty(xs) &&
((tools.empty(xs) === true) ||
(tools.empty(xs) === false)));
}
function dictProp(pairs) {
var dict = tools.dict(pairs);
var result = _.map(pairs,
function(pair) {
if ((_.size(pair) < 2) ||
(_.size(pair[0]) < 1) ||
(_.size(pair[1]) < 1)) {
return true;
}
return dict[pair[0]] === pair[1];
});
if (_.every(result, _.identity)) {
return true;
}
return false;
}
function opMatchProp(strings) {
var match = tools.opMatch(strings);
var result = _.every(_.map(strings,
function (str) {
if (str.replace(/ /g,'').length < 1) {
return true;
}
var res = match(str);
if (res !== false) {
console.log(str);
return true;
}
return false;
}),
_.identity);
return result;
}
function extendProp(pair) {
if (pair.length < 2) {
// empty lists or lists with one item are undefined
// so just return true because extend can't handle them
return true;
}
var x = _.first(pair);
var y = _.first(_.rest(pair));
var extended = tools.extend(x,y);
return x.length + y.length === extended.length;
}
/* Tokenizer tests */
function toolsTests() {
assert.equal(true, tools.empty([]));
assert.equal(true, qc.forAll(dictProp, arbArrayofPairs));
assert.equal(true, qc.forAll(extendProp, arbPairs));
//assert.equal(true, qc.forAll(opMatchProp, arbStrings));
}
console.log(arbName());
//toolsTests();

432
server/tokenize.js

@ -0,0 +1,432 @@
#! /usr/bin/node
var fs = require("fs");
var rep = require("./representation.js");
var $ = require("./tools.js");
var error = require("./errors.js");
var operators = Object.keys(rep.OPInfo);
var _ = require("underscore");
function isDigit(c) {
if (!c)
return false;
var code = c.charCodeAt();
if (isNaN(code)) {
return false;
}
return (47 < code) && (code < 58)
}
function isWhitespace(c) {
if (!c)
return true;
var code = c.charCodeAt();
if (isNaN(code)) {
return true;
}
return (code === 9 ||
code === 32 ||
code === 10 ||
code === 13 ||
code === 11);
}
function isIdentifier(c) {
var code = c.charCodeAt();
return (!isNaN(code) &&
code !== 41 &&
code !== 40 &&
code !== 125 &&
code !== 123 &&
code !== 93 &&
code !== 91 &&
code !== 44 &&
code !== 34 &&
code > 32);
}
function isUpper(c) {
var code = c.charCodeAt();
return (!isNaN(code) &&
(code >= 65) &&
(code <= 90));
}
function tokenizeNum(tokstream, charnum, linenum) {
var number = [];
var code = tokstream[0].charCodeAt();
var isFloat = false;
var n = 0;
// + -
// might want to remove this since it probably won't ever get run?
if (code === 43 || code === 45) { // + or -
number.push(tokstream[0]);
tokstream = tokstream.substr(1);
n++;
}
else if (code === 46) { // .
tokstream = tokstream.substr(1);
n++;
charnum++;
number.push('0');
number.push('.');
isFloat = true;
}
while (isDigit(tokstream[0]) && tokstream.length !== 0) {
number.push(tokstream[0]);
tokstream = tokstream.substr(1);
charnum++;
n++;
}
if (tokstream[0] === '.' && isDigit(tokstream[1])) {
number.push('.');
number.push(tokstream[1]);
tokstream = tokstream.substr(2);
charnum++; charnum++;
n++; n++;
while (isDigit(tokstream[0]) && tokstream.length !== 0) {
number.push(tokstream[0]);
tokstream = tokstream.substr(1);
n++;
charnum++;
}
return [n, ["float", parseFloat(number.join(''), 10), charnum, linenum]];
}
if (!isFloat)
return [n, ["integer", parseInt(number.join(''), 10), charnum, linenum]];
else
return [n, ["float", parseFloat(number.join(''), 10), charnum, linenum]];
}
/* Split up the tokenized identifier if an operator appears in it
* prefer longer identifiers that start with the same character(s) as shorter ones
* e.g. ++ over +
* Everything after the operator goes back on to the token stream
*/
function tokenizeIdent(tokstream,
matchop,
charnum,
linenum) {
var identifier = [];
var n = 0;
while ((!isWhitespace(tokstream[0])) && isIdentifier(tokstream[0]) && !matchop(tokstream)) {
identifier.push(tokstream[0]);
tokstream = tokstream.substr(1);
n++;
charnum++;
}
identifier = identifier.join('');
return [[n, ["identifier", identifier, charnum, linenum]]];
}
function tokenizeCtor(tokstream,
matchop,
charnum,
linenum) {
var ident = tokenizeIdent(tokstream,
matchop,
charnum,
linenum);
ident[0][1][0] = "constructor";
return ident;
}
function tokenizeStr(tokstream, charnum, linenum) {
var stringlit = [];
var n = 1;
var new_charnum = charnum;
tokstream = tokstream.substr(1);
while (tokstream[0].charCodeAt() !== 34) {
stringlit.push(tokstream[0]);
tokstream = tokstream.substr(1);
n++;
new_charnum++;
if (tokstream.length < 1) {
throw error.JSyntaxError(linenum, charnum, "Error: missing quotation mark");
}
}
n++;
return [n, ["stringlit", stringlit.join(''), new_charnum, linenum]];
}
function tokenizeT(tokstream, charnum, linenum) {
if (tokstream.length < 4)
return false;
var next4 = tokstream.substr(0,4);
if (next4 === "then")
return ["thenexp", "then"];
else if (next4 === "true")
return ["truelit", "true"];
return false;
}
function peek(tokstream, toktype, word, charnum, linenum) {
var n = word.length;
if (tokstream.length < n)
return false;
var nextN = tokstream.substr(0, n);
if (nextN == word) {
return [toktype, word];
}
return false;
}
function tokenize(tokstream, matchop) {
var tokens = [];
var charnum = 1;
var linenum = 1;
var i, result, lambda, num, comment;
while (tokstream) {
switch (tokstream[0].charCodeAt()) {
/* falls through */
case 59: // ;
while (tokstream[0].charCodeAt() !== 10) {
tokstream = tokstream.substr(1);
}
break;
case 9: // '\t'
charnum++;
tokens.push(["whitespace", '\t', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 32: // ' '
charnum++;
tokens.push(["whitespace", ' ', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 10: // '\n'
linenum++;
charnum = 1; /* Reset the character number for each line to 1 */
tokens.push(["whitespace", '\n', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 44: // ','
charnum++;
tokens.push(["comma", ",", charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 40: // '('
charnum++;
tokens.push(["left_paren", '(', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 41: // ')'
charnum++;
tokens.push(["right_paren", ')', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 123: // '{'
charnum++;
tokens.push(["left_brace", '{', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 125: // '}'
charnum++;
tokens.push(["right_brace", '}', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 91: // '['
charnum++;
tokens.push(["left_square", '[', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 93: // ']'
charnum++;
tokens.push(["right_square", ']', charnum, linenum]);
tokstream = tokstream.substr(1);
break;
/* falls through */
case 34: // '"'
result = tokenizeStr(tokstream, charnum, linenum);
var str = result[1];
i = result[0];
tokens.push(str);
tokstream = tokstream.substr(i);
break;
/* falls through */
case 46: // '.'
if (isDigit(tokstream[1])) {
result = tokenizeNum(tokstream, charnum, linenum);
num = result[1];
i = result[0];
if (!isNaN(num[1])) {
tokens.push(num);
}
tokstream = tokstream.substr(i);
break;
}
/* falls through */
case 116: // 't'
result = tokenizeT(tokstream);
if (result) {
tokens.push($.extend(result, [charnum, linenum]));
tokstream = tokstream.substr(4); // 4 = length of either token
break;
}
/* falls through */
case 105: // 'i'
var ifexp = peek(tokstream, "ifexp", "if");
if (ifexp) {
tokens.push($.extend(ifexp, [charnum, linenum]));
tokstream = tokstream.substr(2);
break;
}
var inkeyword = peek(tokstream, "in", "in ");
if (inkeyword) {
tokens.push($.extend(inkeyword, [charnum, linenum]));
tokstream = tokstream.substr(3);
break;
}
/* falls through */
case 100: // 'd'
var defop = peek(tokstream, "defop", "defop");
if (defop) {
tokens.push(["defop", "defop", charnum, linenum]);
tokstream = tokstream.substr(5);
break;
}
var deftype = peek(tokstream, "deftype", "deftype");
if (deftype) {
tokens.push(["deftype", "deftype", charnum, linenum]);
tokstream = tokstream.substr(7);
break;
}
var def = peek(tokstream, "def", "def");
if (def) {
tokens.push(["def", "def", charnum, linenum]);
tokstream = tokstream.substr(3);
break;
}
/* falls through */
case 101: // e
result = peek(tokstream, "elsexp", "else");
if (result) {
tokens.push($.extend(result, [charnum, linenum]));
tokstream = tokstream.substr(4);
break;
}
/* falls through */
case 102: // f
result = peek(tokstream, "falselit", "false");
if (result) {
tokens.push($.extend(result, [charnum, linenum]));
tokstream = tokstream.substr(5);
break;
}
/* falls through */
case 108: // l
lambda = peek(tokstream, "lambda", "lambda");
if (lambda) {
tokens.push($.extend(lambda, [charnum, linenum]));
tokstream = tokstream.substr(6);
break;
}
var letexp = peek(tokstream, "let", "let");
if (letexp) {
tokens.push($.extend(letexp, [charnum, linenum]));
tokstream = tokstream.substr(3);
break;
}
/* falls through */
default:
if (isDigit(tokstream[0])) {
result = tokenizeNum(tokstream, charnum, linenum);
num = result[1];
i = result[0];
if (!isNaN(num[1])) {
tokens.push(num);
}
tokstream = tokstream.substr(i);
break;
}
var op = matchop(tokstream);
if (op) {
var l = op.length;
charnum = charnum + l;
tokstream = tokstream.substr(l);
tokens.push(["identifier", op, charnum, linenum]);
}
else {
if (isUpper(tokstream[0])) {
result = tokenizeCtor(tokstream, matchop, charnum, linenum);
}
else {
result = tokenizeIdent(tokstream, matchop, charnum, linenum);
}
for(var index = 0; index < result.length; index++) {
charnum++;
tokens.push(result[index][1]);
tokstream = tokstream.substr(result[index][0]);
}
}
}
}
return tokens;
}
function tokenizeHelp(input, matchop, strip_whitespace) {
try {
return tokenize(input, matchop).reverse().filter(function(x) {
if (strip_whitespace) {
return x[0] !== "whitespace";
}
else {
return true;
}
});
} catch (e) {
console.log(e.stxerror());
process.exit(1);
}
}
var defop_pattern = ["defop", "integer", "constructor",
"left_paren", "identifier",
"identifier", "identifier", "right_paren"];
function checkPattern(x, i) {
return x === defop_pattern[i];
}
function tokenizeFull(input) {
var preludeSrc = fs.readFileSync("./prelude.jl");
var matchop;
input = [preludeSrc, input].join("");
var initialPass = tokenizeHelp(input, _.constant(false), true).reverse();
for (var i = 0; i < initialPass.length; i++) {
if (initialPass.slice(i, i+8).
map(_.first).
every(checkPattern)) {
rep.OPInfo[initialPass[i+5][1]] =
[parseInt(initialPass[i+1][1], 10),
initialPass[i+2][1]];
}
}
operators = Object.keys(rep.OPInfo);
matchop = $.opMatch(operators);
return tokenizeHelp(input, matchop, true);
}
module.exports = {tokenize : tokenizeFull,
isIdentifier : isIdentifier};

104
server/tools.js

@ -0,0 +1,104 @@
var _ = require("underscore");
function empty(xs) {
return _.size(xs) < 1;
}
function not(x) {
return !x;
}
function min(a, b) {
if (a < b) {
return 1;
}
else if (a > b) {
return -1;
}
else {
return 0;
}
}
function max(a, b) {
if (a > b) {
return 1;
}
else if (a < b) {
return -1;
}
else {
return 0;
}
}
function groupOps(ops) {
return _.groupBy(ops.sort(), _.isEqual);
}
function find(f, haystack) {
for(var i = 0; i < haystack.length; i++) {
if (f(haystack[i])) {
return i;
}
}
return false;
}
function dict(pairs) {
var o = {};
pairs.map(function(p) {
o[p[0]] = p[1];
});
return o;
}
function extend(xs, ys) {
var result = _.clone(xs);
result.push.apply(result, ys);
return result;
}
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
function operatorMatch(ops) {
ops = _.filter(ops,
function (op) {
return op.length > 0;
});
ops.sort(min);
var rstring = ops.reduce(
function(acc, x) {
if (!x || x.length < 1) {
return "";
}
return acc + "(" + RegExp.escape(x) + ")|";
}, "");
var reg = new RegExp(rstring);
return function(x) {
var matched = reg.exec(x);
if ((!(_.isNull(matched))) && matched[0]) {
return matched[0];
}
else {
return false;
}
};
}
function debugPrint(stx) {
console.log("%j\n", stx);
}
module.exports = {
not : not,
groupOps : groupOps,
opMatch : operatorMatch,
dict: dict,
extend : extend,
empty : empty,
debugPrint : debugPrint
};

18
server/typecheck.js

@ -0,0 +1,18 @@
/*
* Typecheck an AST with a given environment
* the environment maps variables to types
* a variable can either be bound or free
* when we say a variable is free that means that it is either
* unbound (which causes an exception to be raised immediately)
* or it is bound in the outer scope
*
* So the AST must first be converted to a form where each function body is tied
* to an environment mapping identifiers to types
*/
var rep = require("./representation.js");
var env = require("./environments.js");
/*
* Map all bindings with explicit type annotations in the environment
*/

BIN
server/views/.index.pug.swp

Binary file not shown.

1
server/views/index.pug

@ -3,3 +3,4 @@ extends layout
block content
h1= title
p Welcome to #{title}
p #{output}

87
server/vm.js

@ -0,0 +1,87 @@
#! /usr/bin/node
var typ = require("./representation.js");
var parse = require("./parse.js");
var tokenizer = require("./tokenize.js");
var pprint = require("./pprint.js");
var env = require("./environments.js");
var fs = require("fs");
var istr = fs.readFileSync('/dev/stdin').toString();
var ast = parse.parseFull(tokenizer.tokenize(istr));
var testenv = env.makeEnv("toplevel",
[
["+", function(a) { return function(b) { return a + b; } }],
["*", function(a) { return function(b) { return a * b; } }],
["-", function(a) { return function(b) { return a - b; } }],
["a", 2],
["b", 3]]);
console.log(JSON.stringify(evaluate(ast.ast[ast.ast.length-1], testenv)));
function lookup(ident, env) {
var func = evaluate(env.bindings[ident], env);
return func;
}
function evaluateString(input) {
var parsed = parse.parseFull(tokenizer.tokenize(input)).ast;
var evaluated = evaluateAll(parsed, testenv);
return evaluated;
}
function apply(func, p) {
return func(evaluate(p));
}
function evaluateAll(ast, environment) {
var l = ast.length;
var evaled = [];
for (var i = 0; i < l; i++) {
// should look for closures?
evaled.push(evaluate(ast[i], environment));
}
return evaled;
}
function evaluate(ast, environment) {
if (ast.exprType == "Application") {
var func = evaluate(ast.func, environment);
return apply(
func,
evaluate(ast.p, environment));
}
else if (ast.exprType === "Unary") {
/* Unary function application */
var func = evaluate(ast.op, environment);
return apply(
func,
evaluate(ast.val, environment));
}
else if (ast.exprType === "Name") {
return lookup(ast.ident, environment);
}
else if (ast.exprType === "If") {
if (evaluate(ast.condition, environment)) {
return evaluate(ast.thenexp, environment);
}
else {
return evaluate(ast.elseexp, environment);
}
}
else if (ast.exprType === "Definition") {
return; /* XXX */
}
else if (ast.exprType === "Integer") {
return ast.val;
}
else {
return ast;
}
}
module.exports = {
"evaluate" : evaluateString
};
Loading…
Cancel
Save