Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Initial import

  • Loading branch information...
commit a55e5ca4578e84fdb00af32ef5dac6761c7d4d9a 0 parents
@norm2782 authored
Showing with 564 additions and 0 deletions.
  1. +310 −0 LazyJS.js
  2. +5 −0 README.md
  3. +11 −0 tests.html
  4. +238 −0 tests.js
310 LazyJS.js
@@ -0,0 +1,310 @@
+(function (window) {
+ "use strict";
+
+ var traceOn, traceFn, evalCounter, nodeCounter;
+
+ traceOn = false;
+ traceFn = console.log;
+
+ evalCounter = 0;
+ nodeCounter = 0;
+
+ function enableTrace() {
+ traceOn = true;
+ }
+
+ function trace(m, s) {
+ if (traceOn) {
+ traceFn(m + ": " + s);
+ }
+ }
+
+ function traceX(msg, x) {
+ trace(msg, typeof x + "/" + typeof x.__eOrV__ + ":" + x);
+ }
+
+ // interface to eval
+ function ev(x) {
+ trace("> ev", x);
+
+ if (x !== undefined && x.__eOrV__ !== undefined) {
+ var x_, xx, x_next;
+
+ x_ = x;
+ do {
+ if (typeof x.__eOrV__ === 'function') {
+ traceX(">> ev()", x);
+
+ xx = x.__eOrV__();
+ x.__eOrV__ = xx;
+ x = xx;
+
+ traceX("<< ev()", x);
+ } else {
+ traceX(">> ev", x);
+
+ x = x.__eOrV__;
+
+ traceX("<< ev", x);
+ }
+ } while (x !== undefined && x.__eOrV__ !== undefined);
+
+ while (x_.__eOrV__ !== undefined) {
+ x_next = x_.__eOrV__;
+ x_.__eOrV__ = x;
+ x_ = x_next;
+ }
+ }
+
+ evalCounter += 1;
+
+ trace("< ev", x);
+
+ return x;
+ }
+
+ function App_undersat(f, as) {
+ this.fun = f;
+ this.args = as;
+ nodeCounter += 1;
+ this.nodeCounter = nodeCounter;
+ }
+
+ // Apply node, not enough args
+ App_undersat.prototype = {
+ __aN__ : function (args) {
+ var needs, fun;
+
+ needs = this.needsNrArgs();
+
+ if (args.length < needs) {
+ return new App_undersat(this, args);
+ } else if (args.length === needs) {
+ trace("> App_undersat.__aN__(=sat)", this + "(|args#" + args.length +
+ "=(|" + args + "|)|)");
+
+ return this.fun.__aN__(this.args.concat(args));
+ } else {
+ trace("> App_undersat.__aN__(>sat)", this + "(|args#" + args.length +
+ "=(|" + args.slice(0, needs) + "|)+(|" + args.slice(needs) +
+ "|)|)");
+
+ fun = ev(this.__aN__(args.slice(0, needs)));
+
+ return {
+ __eOrV__ : function () {
+ return fun.__aN__(args.slice(needs));
+ }
+ };
+ }
+ },
+ needsNrArgs : function () {
+ return this.fun.needsNrArgs() - this.args.length;
+ },
+ getName : function () {
+ return "A-" + this.needsNrArgs() + "#" + this.nodeId + "'";
+ },
+ toString : function () {
+ return "(" + this.getName() + "=" + this.fun + "@" + this.args + ")";
+ }
+ };
+
+ function traceFun(msg, fun, args) {
+ trace(msg, fun + "(|args#" + args.length + "=" + args + "|)");
+ }
+
+ function App(f, as) {
+ this.__eOrV__ = function () {
+ traceFun("> App.__eOrV__", f, as);
+
+ var x = f.__aN__(as);
+
+ traceFun("< App.__eOrV__", f, as);
+ trace("< ->", this + " -> " + x);
+
+ return x;
+ };
+
+ this.fun = f;
+ this.args = as;
+ nodeCounter += 1;
+ this.nodeId = nodeCounter;
+ }
+
+ // Apply node, unknown how much is missing or too much
+ App.prototype = {
+ __aN__ : function (args) {
+ var fun = ev(this);
+ return {
+ __eOrV__ : function () {
+ return fun.__aN__(args);
+ }
+ };
+ },
+ getName : function () {
+ return "A" + this.args.length + "#" + this.nodeId + "'"
+ + this.fun.getName();
+ },
+ getVal : function () {
+ return "V#" + this.nodeId + "'" + this.__eOrV__;
+ },
+ toString : function () {
+ if (typeof this.__eOrV__ === 'function') {
+ return "(" + this.getName() + "@args#" + this.args.length + "=(|"
+ + this.args + "|))";
+ } else {
+ return "(" + this.getVal() + ")";
+ }
+ }
+ };
+
+ function LFun(evalN, nm) {
+ this.needs = evalN.length;
+ this.__evN__ = evalN;
+
+ if (nm !== undefined) {
+ this.name = nm;
+ }
+
+ nodeCounter += 1;
+ this.nodeId = nodeCounter;
+ }
+
+ // Function node
+ LFun.prototype = {
+ __aN__ : function (args) {
+ var x, fun, remargs;
+
+ if (args.length < this.needs) {
+ return new App_undersat(this, args);
+ } else if (args.length === this.needs) {
+ traceFun("> LFun.__aN__(=sat)", this, args);
+
+ x = this.__evN__.apply(null, args);
+
+ traceFun("< LFun.__aN__(=sat)", this, args);
+ trace("< ->", x);
+
+ return x;
+ } else {
+ trace("> LFun.__aN__(>sat)", this + "(|needs#" + this.needs + "args#" +
+ args.length + "=" + args + "|)");
+
+ fun = ev(this.__evN__.apply(null, args.slice(0, this.needs)));
+ remargs = args.slice(this.needs);
+
+ trace("< LFun.__aN__(>sat)", fun + "(|needs#" + this.needs + "remargs#"
+ + remargs.length + "=" + remargs + "|)");
+ trace("< ->", fun);
+
+ return {
+ __eOrV__ : function () {
+ return fun.__aN__(remargs);
+ }
+ };
+ }
+ },
+ needsNrArgs : function () {
+ return this.needs;
+ },
+ getName : function () {
+ return "F" + this.needs + "#" + this.nodeId + "'" + this.name;
+ },
+ toString : function () {
+ return "(" + this.getName() + ")";
+ }
+ };
+
+ // function construction wrappers
+ function fn(f) {
+ return new LFun(f);
+ }
+
+ // strict application wrappers
+ function e1(f, a) {
+ return ev(f.__aN__([a]));
+ }
+
+ function e2(f, a, b) {
+ return ev(f.__aN__([a, b]));
+ }
+
+ function e3(f, a, b, c) {
+ return ev(f.__aN__([a, b, c]));
+ }
+
+ function e4(f, a, b, c, d) {
+ return ev(f.__aN__([a, b, c, d]));
+ }
+
+ function e5(f, a, b, c, d, e) {
+ return ev(f.__aN__([a, b, c, d, e]));
+ }
+
+ function eN(f, a) {
+ return ev(f.__aN__(a));
+ }
+
+ // lazy application wrappers
+ function a0(f) {
+ return new App(f, []);
+ }
+
+ function a1(f, a) {
+ return new App(f, [a]);
+ }
+
+ function a2(f, a, b) {
+ return new App(f, [a, b]);
+ }
+
+ function a3(f, a, b, c) {
+ return new App(f, [a, b, c]);
+ }
+
+ function a4(f, a, b, c, d) {
+ return new App(f, [a, b, c, d]);
+ }
+
+ function a5(f, a, b, c, d, e) {
+ return new App(f, [a, b, c, d, e]);
+ }
+
+ function aN(f, a) {
+ return new App(f, a);
+ }
+
+ // indirection
+ function ind() {
+ return new App(function () {
+ throw "ind: attempt to prematurely evaluate indirection";
+ }, []);
+ }
+
+ function indSet(i, x) {
+ i.__eOrV__ = x;
+ }
+
+ window.LFun = LFun;
+ window.App = App;
+ window.ev = ev;
+ window.fn = fn;
+ window.e1 = e1;
+ window.e2 = e2;
+ window.e3 = e3;
+ window.e4 = e4;
+ window.e5 = e5;
+ window.eN = eN;
+ window.a0 = a0;
+ window.a1 = a1;
+ window.a2 = a2;
+ window.a3 = a3;
+ window.a4 = a4;
+ window.a5 = a5;
+ window.aN = aN;
+ window.ind = ind;
+ window.indSet = indSet;
+
+ window.trace = trace;
+ window.enableTrace = enableTrace;
+}(window));
5 README.md
@@ -0,0 +1,5 @@
+Implementation of a simple lambda calculus in JavaScript. It models
+partial function application and lazy evaluation.
+
+The original version of this library was developed by Atze Dijkstra as
+part of the runtime system for the Utrecht Haskell Compiler JavaScript backend.
11 tests.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <title></title>
+ <script type="text/javascript" src="LazyJS.js"></script>
+ <script type="text/javascript" src="tests.js"></script>
+ </head>
+ <body>
+ </body>
+</html>
238 tests.js
@@ -0,0 +1,238 @@
+(function (window) {
+ "use strict";
+
+ var take, filter, nil, sub, id, even, eq, ne, add, sub, mul, div, mod, from,
+ last, list2, take, filter, notMultiple, notMultiple2, sieve, sieve2;
+
+ // setup
+ function init() {
+ }
+
+ function print(x) {
+ document.write(x);
+ }
+
+ function cons(x, y) { return [0, x, y]; }
+ function head(l) { return l[1]; }
+ function tail(l) { return l[2]; }
+ nil = [1];
+ function isNil(x) { return x[0] === 1; }
+
+ function show(x) {
+ print(ev(x));
+ }
+
+ function showList(l) {
+ var list = ev(l);
+ switch (list[0]) {
+ case 0:
+ print(ev(head(list)) + ":");
+ showList(tail(list));
+ break;
+
+ case 1:
+ print("[]");
+ break;
+ }
+ }
+
+ sub = fn(function (a, b) {
+ return ev(a) - ev(b);
+ });
+
+ take = fn(function (n, xs) {
+ var len, list;
+ len = ev(n);
+ list = ev(xs);
+ if (len <= 0 || isNil(list)) {
+ return nil;
+ } else {
+ return cons(head(list), a2(take, a2(sub, len, 1), tail(list)));
+ }
+ });
+
+ filter = fn(function (a, b) {
+ var list, test;
+ list = ev(b);
+ test = e1(a, head(list));
+ if (test) {
+ return cons(head(list), a2(filter, a, tail(list)));
+ } else {
+ return a2(filter, a, tail(list));
+ }
+ });
+
+ id = fn(function (a) {
+ // trace("id: " + a);
+ return a;
+ });
+
+ even = fn(function (a) {
+ // return _ev_(a[0]) % 2 == 0;
+ return a2(eq, a2(mod, a, 2), 0);
+ });
+
+ eq = fn(function (a, b) {
+ return ev(a) === ev(b);
+ });
+
+ ne = fn(function (a, b) {
+ return ev(a) !== ev(b);
+ });
+
+ add = fn(function (a, b) {
+ return ev(a) + ev(b);
+ });
+
+ mul = fn(function (a, b) {
+ return ev(a) * ev(b);
+ });
+
+ div = fn(function (a, b) {
+ return Math.floor(ev(a) / ev(b));
+ });
+
+ mod = fn(function (a, b) {
+ return (ev(a) % ev(b));
+ });
+
+ from = fn(function (a) {
+ return cons(a, a1(from, a2(add, a, 1)));
+ });
+
+ last = fn(function (a) {
+ list = ev(a);
+ switch (list[0]) {
+ case 0:
+ list2 = ev(tail(list));
+ switch (list2[0]) {
+ case 0:
+ return a1(last, tail(list));
+
+ case 1:
+ return head(list);
+ }
+ break;
+
+ case 1:
+ return undefined;
+ }
+ });
+
+ notMultiple = fn(function (a, b) {
+ return a2(ne, a2(mul, a2(div, b, a), a), b);
+ });
+
+ notMultiple2 = fn(function (a, b) {
+ var x, y;
+ x = ev(a);
+ y = ev(b);
+ return (Math.floor(y / x) * x) !== y;
+ });
+
+ sieve = fn(function (a) {
+ var list = ev(a);
+ return cons(head(list), a1(sieve, a2(filter, a1(notMultiple2,
+ head(list)), tail(list))));
+ });
+
+ sieve2 = fn(function (nmz, a) {
+ var list = ev(a);
+ return cons(head(list), a2(sieve2, a1(id, nmz), a2(filter, a1(nmz,
+ head(list)), tail(list))));
+ });
+
+ // test: sieve
+ function testSieve() {
+ var mainSieve, mainSieve2, d, t1, t2, evalCounter, list;
+
+ mainSieve = a2(take, 1000, a1(sieve, a1(from, 2)));
+ mainSieve2 = a2(take, 500, a2(sieve2, a1(id, notMultiple2), a1(from, 2)));
+
+ // running it...
+ evalCounter = 0;
+ d = new Date();
+ t1 = d.getTime();
+ // showList(mainSieve);
+ show(a1(last, mainSieve));
+ d = new Date();
+ t2 = d.getTime() - t1;
+ print("<hr/>time= " + t2 + " ms" + ((evalCounter > 0) ? ", nreval= " +
+ evalCounter + ", ms/ev= " + (t2 / evalCounter) : "") + "<br/>");
+ }
+
+ function testMisc() {
+ var plus, inc1, inc2, two1, two3, arr, x1, x2;
+
+ trace("load & init ok");
+ plus = fn(function (a, b) { return ev(a) + ev(b); });
+ trace("plus: " + plus);
+ inc1 = fn(function (a) {
+ trace("inc: " + a);
+ var x = ev(a);
+ return x+1;
+ });
+ trace("inc1: " + inc1);
+ inc2 = plus.__aN__([10]);
+ trace("inc2: " + inc2);
+ two1 = 2;
+ // var two2 = new AppN_WHNF(2);
+ two3 = new App(new LFun(0, function () { return 2; }), []);
+ arr = [two1];
+ // trace("two2: " + two2);
+ trace("two3: " + two3);
+ trace("two3 eval: " + ev(two3));
+ trace("two3: " + two3);
+ trace("two3 eval: " + ev(two3));
+ trace("arr: " + arr);
+ x1 = inc2.__aN__(arr);
+ trace("inc 2: " + x1);
+ x2 = new App(inc2, arr);
+ trace("inc del 2: " + x2);
+ trace("inc del 2 eval: " + ev(x2));
+ }
+
+ function tryOut() {
+ var f, l;
+ f = function (a, b) {};
+ l = cons(1, nil);
+ // trace(ToPropertyDescriptor(f));
+ // trace(ToPropertyDescriptor(Function));
+ // trace(ToPropertyDescriptor("a"));
+ // trace(ToPropertyDescriptor(String));
+ trace("f " + f.length);
+ }
+
+ function main() {
+ init();
+ // testMisc();
+ // tryOut();
+ testSieve();
+ }
+
+ window.testSieve = testSieve;
+ window.testMisc = testMisc;
+ window.tryOut = tryOut;
+ window.cons = cons;
+ window.nil = nil;
+ window.head = head;
+ window.tail = tail;
+ window.main = main;
+ window.take = take;
+ window.filter = filter;
+ window.id = id;
+ window.even = even;
+ window.eq = eq;
+ window.ne = ne;
+ window.add = add;
+ window.mul = mul;
+ window.div = div;
+ window.mod = mod;
+ window.from = from;
+ window.last = last;
+ window.notMultiple = notMultiple;
+ window.notMultiple2 = notMultiple2;
+ window.sieve = sieve;
+ window.sieve2 = sieve2;
+}(window));
+
Please sign in to comment.
Something went wrong with that request. Please try again.