Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

First, functioning version of Try Lambda.

  • Loading branch information...
commit a39665aca27e9e268fa7f6b91ee48348e0a68d91 0 parents
@mike-burns authored
1  .gitignore
@@ -0,0 +1 @@
+*swp
86 index.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+
+<html>
+ <head>
+ <title>Try Lambda Calculus!</title>
+ <script type="text/javascript" src="jquery-console/jquery-1.4.2.min.js"></script>
+ <script type="text/javascript" src="jquery-console/jquery.console.js"></script>
+ <script type="text/javascript" src="lc_parser.js"></script>
+ <script type="text/javascript" src="lc_evaluator.js"></script>
+ <script type="text/javascript">
+ function lcEvaluate(line) {
+ var expr = parser.parse(line);
+ return evaluator.eval(expr, {});
+ }
+
+ $(document).ready(function(){
+ var lcConsole = $('.lcConsole');
+ $('body').append(lcConsole);
+ var controller2 = lcConsole.console({
+ promptLabel: '> ',
+ commandValidate: function(line){
+ if (line == "") return false;
+ else return true;
+ },
+ commandHandle: lcEvaluate,
+ animateScroll: true,
+ });
+ });
+ </script>
+ <style type="text/css" media="screen">
+ div.lcConsole { word-wrap: break-word; }
+ div.lcConsole { font-size: 14px; margin-top:1em }
+ div.lcConsole div.jquery-console-inner {
+ width: 40em;
+ height: 25em;
+ background:#efefef;
+ padding:0.5em;
+ overflow:auto
+ }
+ div.lcConsole div.jquery-console-prompt-box {
+ color:#444; font-family:monospace;
+ }
+ div.lcConsole div.jquery-console-focus span.jquery-console-cursor {
+ background:#333;
+ color:#eee;
+ font-weight:bold
+ }
+ div.lcConsole div.jquery-console-message-error {
+ color: #ef0505;
+ font-family: sans-serif;
+ font-weight: bold;
+ padding: 0.1em;
+ }
+ div.lcConsole div.jquery-console-message-success {
+ color:#187718;
+ font-family:monospace;
+ padding:0.1em;
+ }
+ div.lcConsole span.jquery-console-prompt-label {
+ color: red;
+ }
+
+ code { background:#efefef; }
+ #by-line {bottom: 1em; position: absolute;}
+ </style>
+ </head>
+ <body>
+ <h1>Try Lambda Calculus!</h1>
+ <p>Have two minutes? Give the Lambda Calculus a shot right now!</p>
+ <p>If you're a beginner you can start with some basics:</p>
+ <dl>
+ <dt>Variables!</dt>
+ <dd><code>x</code>, <code>y</code>, <code>a</code>, and other letters!</dd>
+ <dt>Functions!</dt>
+ <dd><code>\x.x</code>, <code>\x.\y.x</code>, and more!</dd>
+ <dt>Application!</dt>
+ <dd><code>(\x.x) \z.z</code>, <code>(\x.\y.x) z q</code>, and even <code>(\x.x x) (\y.y y)</code>!</dd>
+ </dl>
+
+ <div class="lcConsole"></div>
+
+ <div id="by-line">
+ <p>Hacked out in a night by <a href="http://mike-burns.com/">Mike Burns</a>. Thanks to <a href="http://tryruby.org/">Try Ruby</a> and <a href="http://tryhaskell.org/">Try Haskell</a>; and to <a href="http://zaach.github.com/jison/">jison</a> and <a href="https://github.com/chrisdone/jquery-console">jquery-console</a>. Learn more about <a href="http://en.wikipedia.org/wiki/Lambda_Calculus">the Lambda Calculus at Wikipedia</a>.</p>
+ </div>
+ </body>
+</html>
1  jquery-console
@@ -0,0 +1 @@
+Subproject commit 055c2c212944349ddbb045e8536ebfb2192acb80
84 lc_evaluator.js
@@ -0,0 +1,84 @@
+var evaluator = (function() { return {
+ eval: function(expr) {
+ return this.stringify(this.evalWithEnvironment(expr, {}))
+ },
+
+ evalWithEnvironment: function(expr, env) {
+ var tag = expr[0];
+ if (tag == "VarExpr") {
+ var value = this.lookUpInEnv(expr[1], env);
+ var valueTag = value[0];
+ if (valueTag == "VarExpr") {
+ return value;
+ } else {
+ return this.evalWithEnvironment(value, env);
+ }
+ } else if (tag == "LambdaExpr") {
+ return expr;
+ var variable = expr[1];
+ var body = expr[2];
+ return ["LambdaExpr", variable, this.evalWithEnvironment(body, env)]
+ } else if (tag == "ApplyExpr") {
+ var f1 = expr[1];
+ var f2 = expr[2];
+ return this.betaReduce(this.evalWithEnvironment(f1, env), f2, env);
+ }
+ },
+
+ betaReduce: function(lambdaExpr, arg, env) {
+ var tag = lambdaExpr[0];
+ if (tag == "LambdaExpr") {
+ var variable = lambdaExpr[1];
+ var body = lambdaExpr[2];
+ var newEnv = this.extendEnv(env, variable, arg);
+ return this.evalWithEnvironment(this.applyNewEnv(newEnv, body), newEnv);
+ } else {
+ return ["Error", "tried to apply a non-lambda expression: " + this.stringify(lambdaExpr)];
+ }
+ },
+
+ applyNewEnv: function(env, expr) {
+ var tag = expr[0];
+ if (tag == "VarExpr") {
+ return this.lookUpInEnv(expr[1], env);
+ } else if (tag == "LambdaExpr") {
+ return expr;
+ } else if (tag == "ApplyExpr") {
+ return ["ApplyExpr",
+ this.applyNewEnv(env, expr[1]),
+ this.applyNewEnv(env, expr[2])];
+ }
+ },
+
+ lookUpInEnv: function(variable, env) {
+ var value = env[variable];
+ if (value == undefined) { // free var
+ return ["VarExpr", variable];
+ } else {
+ return value;
+ }
+ },
+
+ extendEnv: function(env, variable, value) {
+ var newEnv = env;
+ newEnv[variable] = value;
+ return newEnv;
+ },
+
+ stringify: function(expr) {
+ var tag = expr[0];
+ if (tag == "VarExpr") {
+ return expr[1];
+ } else if (tag == "LambdaExpr") {
+ var variable = expr[1];
+ var body = expr[2];
+ return '\\' + variable + '.' + this.stringify(body);
+ } else if (tag == "ApplyExpr") {
+ var f1 = expr[1];
+ var f2 = expr[2];
+ return this.stringify(f1) + ' ' + this.stringify(f2);
+ } else if (tag == "Error") {
+ return "Error: " + expr[1];
+ }
+ }
+}})();
392 lc_parser.js
@@ -0,0 +1,392 @@
+/* Jison generated parser */
+var parser = (function(){
+
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"file":3,"expr":4,"EOF":5,"LAMBDA":6,"var_list":7,".":8,"SEP":9,"var":10,"(":11,")":12,"VAR":13,"$accept":0,"$end":1},
+terminals_: {2:"error",5:"EOF",6:"LAMBDA",8:".",9:"SEP",11:"(",12:")",13:"VAR"},
+productions_: [0,[3,2],[4,4],[4,3],[4,1],[4,3],[7,2],[7,1],[10,1]],
+performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1: return $$[$0-1];
+break;
+case 2:
+ var temp = ["LambdaExpr", $$[$0-2].shift(), $$[$0]];
+ $$[$0-2].forEach(function (v) {
+ temp = ["LambdaExpr", v, temp];
+ });
+ this.$ = temp;
+
+break;
+case 3: this.$ = ["ApplyExpr", $$[$0-2], $$[$0]];
+break;
+case 4: this.$ = ["VarExpr", $$[$0]];
+break;
+case 5: this.$ = $$[$0-1];
+break;
+case 6: this.$ = $$[$0-1]; this.$.unshift($$[$0]);
+break;
+case 7: this.$ = [$$[$0]];
+break;
+case 8: this.$ = yytext;
+break;
+}
+},
+table: [{3:1,4:2,6:[1,3],10:4,11:[1,5],13:[1,6]},{1:[3]},{5:[1,7],9:[1,8]},{7:9,10:10,13:[1,6]},{5:[2,4],9:[2,4],12:[2,4]},{4:11,6:[1,3],10:4,11:[1,5],13:[1,6]},{5:[2,8],8:[2,8],9:[2,8],12:[2,8],13:[2,8]},{1:[2,1]},{4:12,6:[1,3],10:4,11:[1,5],13:[1,6]},{8:[1,13],10:14,13:[1,6]},{8:[2,7],13:[2,7]},{9:[1,8],12:[1,15]},{5:[2,3],9:[2,3],12:[2,3]},{4:16,6:[1,3],10:4,11:[1,5],13:[1,6]},{8:[2,6],13:[2,6]},{5:[2,5],9:[2,5],12:[2,5]},{5:[2,2],9:[1,8],12:[2,2]}],
+defaultActions: {7:[2,1]},
+parseError: function parseError(str, hash) {
+ throw new Error(str);
+},
+parse: function parse(input) {
+ var self = this,
+ stack = [0],
+ vstack = [null], // semantic value stack
+ lstack = [], // location stack
+ table = this.table,
+ yytext = '',
+ yylineno = 0,
+ yyleng = 0,
+ recovering = 0,
+ TERROR = 2,
+ EOF = 1;
+
+ //this.reductionCount = this.shiftCount = 0;
+
+ this.lexer.setInput(input);
+ this.lexer.yy = this.yy;
+ this.yy.lexer = this.lexer;
+ if (typeof this.lexer.yylloc == 'undefined')
+ this.lexer.yylloc = {};
+ var yyloc = this.lexer.yylloc;
+ lstack.push(yyloc);
+
+ if (typeof this.yy.parseError === 'function')
+ this.parseError = this.yy.parseError;
+
+ function popStack (n) {
+ stack.length = stack.length - 2*n;
+ vstack.length = vstack.length - n;
+ lstack.length = lstack.length - n;
+ }
+
+ function lex() {
+ var token;
+ token = self.lexer.lex() || 1; // $end = 1
+ // if token isn't its numeric value, convert
+ if (typeof token !== 'number') {
+ token = self.symbols_[token] || token;
+ }
+ return token;
+ }
+
+ var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
+ while (true) {
+ // retreive state number from top of stack
+ state = stack[stack.length-1];
+
+ // use default actions if available
+ if (this.defaultActions[state]) {
+ action = this.defaultActions[state];
+ } else {
+ if (symbol == null)
+ symbol = lex();
+ // read action for current state and first input
+ action = table[state] && table[state][symbol];
+ }
+
+ // handle parse error
+ _handle_error:
+ if (typeof action === 'undefined' || !action.length || !action[0]) {
+
+ if (!recovering) {
+ // Report error
+ expected = [];
+ for (p in table[state]) if (this.terminals_[p] && p > 2) {
+ expected.push("'"+this.terminals_[p]+"'");
+ }
+ var errStr = '';
+ if (this.lexer.showPosition) {
+ errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ');
+ } else {
+ errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
+ (symbol == 1 /*EOF*/ ? "end of input" :
+ ("'"+(this.terminals_[symbol] || symbol)+"'"));
+ }
+ this.parseError(errStr,
+ {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
+ }
+
+ // just recovered from another error
+ if (recovering == 3) {
+ if (symbol == EOF) {
+ throw new Error(errStr || 'Parsing halted.');
+ }
+
+ // discard current lookahead and grab another
+ yyleng = this.lexer.yyleng;
+ yytext = this.lexer.yytext;
+ yylineno = this.lexer.yylineno;
+ yyloc = this.lexer.yylloc;
+ symbol = lex();
+ }
+
+ // try to recover from error
+ while (1) {
+ // check for error recovery rule in this state
+ if ((TERROR.toString()) in table[state]) {
+ break;
+ }
+ if (state == 0) {
+ throw new Error(errStr || 'Parsing halted.');
+ }
+ popStack(1);
+ state = stack[stack.length-1];
+ }
+
+ preErrorSymbol = symbol; // save the lookahead token
+ symbol = TERROR; // insert generic error symbol as new lookahead
+ state = stack[stack.length-1];
+ action = table[state] && table[state][TERROR];
+ recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
+ }
+
+ // this shouldn't happen, unless resolve defaults are off
+ if (action[0] instanceof Array && action.length > 1) {
+ throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
+ }
+
+ switch (action[0]) {
+
+ case 1: // shift
+ //this.shiftCount++;
+
+ stack.push(symbol);
+ vstack.push(this.lexer.yytext);
+ lstack.push(this.lexer.yylloc);
+ stack.push(action[1]); // push state
+ symbol = null;
+ if (!preErrorSymbol) { // normal execution/no error
+ yyleng = this.lexer.yyleng;
+ yytext = this.lexer.yytext;
+ yylineno = this.lexer.yylineno;
+ yyloc = this.lexer.yylloc;
+ if (recovering > 0)
+ recovering--;
+ } else { // error just occurred, resume old lookahead f/ before error
+ symbol = preErrorSymbol;
+ preErrorSymbol = null;
+ }
+ break;
+
+ case 2: // reduce
+ //this.reductionCount++;
+
+ len = this.productions_[action[1]][1];
+
+ // perform semantic action
+ yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
+ // default location, uses first token for firsts, last for lasts
+ yyval._$ = {
+ first_line: lstack[lstack.length-(len||1)].first_line,
+ last_line: lstack[lstack.length-1].last_line,
+ first_column: lstack[lstack.length-(len||1)].first_column,
+ last_column: lstack[lstack.length-1].last_column
+ };
+ r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
+
+ if (typeof r !== 'undefined') {
+ return r;
+ }
+
+ // pop off stack
+ if (len) {
+ stack = stack.slice(0,-1*len*2);
+ vstack = vstack.slice(0, -1*len);
+ lstack = lstack.slice(0, -1*len);
+ }
+
+ stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
+ vstack.push(yyval.$);
+ lstack.push(yyval._$);
+ // goto new state = table[STATE][NONTERMINAL]
+ newState = table[stack[stack.length-2]][stack[stack.length-1]];
+ stack.push(newState);
+ break;
+
+ case 3: // accept
+ return true;
+ }
+
+ }
+
+ return true;
+}};/* Jison generated lexer */
+var lexer = (function(){
+
+var lexer = ({EOF:1,
+parseError:function parseError(str, hash) {
+ if (this.yy.parseError) {
+ this.yy.parseError(str, hash);
+ } else {
+ throw new Error(str);
+ }
+ },
+setInput:function (input) {
+ this._input = input;
+ this._more = this._less = this.done = false;
+ this.yylineno = this.yyleng = 0;
+ this.yytext = this.matched = this.match = '';
+ this.conditionStack = ['INITIAL'];
+ this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
+ return this;
+ },
+input:function () {
+ var ch = this._input[0];
+ this.yytext+=ch;
+ this.yyleng++;
+ this.match+=ch;
+ this.matched+=ch;
+ var lines = ch.match(/\n/);
+ if (lines) this.yylineno++;
+ this._input = this._input.slice(1);
+ return ch;
+ },
+unput:function (ch) {
+ this._input = ch + this._input;
+ return this;
+ },
+more:function () {
+ this._more = true;
+ return this;
+ },
+pastInput:function () {
+ var past = this.matched.substr(0, this.matched.length - this.match.length);
+ return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+ },
+upcomingInput:function () {
+ var next = this.match;
+ if (next.length < 20) {
+ next += this._input.substr(0, 20-next.length);
+ }
+ return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
+ },
+showPosition:function () {
+ var pre = this.pastInput();
+ var c = new Array(pre.length + 1).join("-");
+ return pre + this.upcomingInput() + "\n" + c+"^";
+ },
+next:function () {
+ if (this.done) {
+ return this.EOF;
+ }
+ if (!this._input) this.done = true;
+
+ var token,
+ match,
+ col,
+ lines,
+ index;
+ if (!this._more) {
+ this.yytext = '';
+ this.match = '';
+ }
+ var rules = this._currentRules();
+ for (var i=0;i < rules.length; i++) {
+ index = rules[i];
+ match = this._input.match(this.rules[index]);
+ if (match) {
+ lines = match[0].match(/\n.*/g);
+ if (lines !== null) this.yylineno += lines.length;
+ this.yylloc = {first_line: this.yylloc.last_line,
+ last_line: this.yylineno+1,
+ first_column: this.yylloc.last_column,
+ last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
+ this.yytext += match[0];
+ this.match += match[0];
+ this.matches = match;
+ this.yyleng = this.yytext.length;
+ this._more = false;
+ this._input = this._input.slice(match[0].length);
+ this.matched += match[0];
+ token = this.performAction.call(this, this.yy, this, index,this.conditionStack[this.conditionStack.length-1]);
+ if (token) return token;
+ else return;
+ }
+ }
+ if (this._input === "") {
+ return this.EOF;
+ } else {
+ this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
+ {text: "", token: null, line: this.yylineno});
+ }
+ },
+lex:function lex() {
+ var r = this.next();
+ if (typeof r !== 'undefined') {
+ return r;
+ } else {
+ return this.lex();
+ }
+ },
+begin:function begin(condition) {
+ this.conditionStack.push(condition);
+ },
+popState:function popState() {
+ return this.conditionStack.pop();
+ },
+_currentRules:function _currentRules() {
+ return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
+ },
+topState:function () {
+ return this.conditionStack[this.conditionStack.length-2];
+ },
+pushState:function begin(condition) {
+ this.begin(condition);
+ }});
+lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+
+var YYSTATE=YY_START
+switch($avoiding_name_collisions) {
+case 0:/* ignore */
+break;
+case 1: return 11;
+break;
+case 2: return 12;
+break;
+case 3: return 6;
+break;
+case 4: return 8;
+break;
+case 5: return 13;
+break;
+case 6: return 9;
+break;
+case 7: return 5;
+break;
+}
+};
+lexer.rules = [/^\s*\n\s*/,/^\(/,/^\)/,/^\\|λ/,/^\.\s?/,/^[a-zA-Z]/,/^\s+/,/^$/];
+lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7],"inclusive":true}};return lexer;})()
+parser.lexer = lexer;
+return parser;
+})();
+if (typeof require !== 'undefined' && typeof exports !== 'undefined') {
+exports.parser = parser;
+exports.parse = function () { return parser.parse.apply(parser, arguments); }
+exports.main = function commonjsMain(args) {
+ if (!args[1])
+ throw new Error('Usage: '+args[0]+' FILE');
+ if (typeof process !== 'undefined') {
+ var source = require("fs").readFileSync(require("path").join(process.cwd(), args[1]), "utf8");
+ } else {
+ var cwd = require("file").path(require("file").cwd());
+ var source = cwd.join(args[1]).read({charset: "utf-8"});
+ }
+ return exports.parser.parse(source);
+}
+if (typeof module !== 'undefined' && require.main === module) {
+ exports.main(typeof process !== 'undefined' ? process.argv.slice(1) : require("system").args);
+}
+}
Please sign in to comment.
Something went wrong with that request. Please try again.