Anonymous avatar Anonymous committed 9bf441e

Really added firelang.js. Also added brainfuck.html and graph.html (really thought I had these added).
Improved firelang.js; made function calling possible with single argument. (No args still broken.)

Comments (0)

Files changed (6)

+<!DOCTYPE html>
+<html>
+<head>
+	<title>Tree grapher - firefly.nu</title>
+	<script src="src/floop.js"></script>
+	<script src="src/firelang.js"></script>
+	<script>
+	var indentation = 0;
+	function output() {};
+	
+	function fun(name, func) {
+		func.ffName = name;
+		return func;
+	}
+	
+		/* Brainfuck */
+	var Brainfuck = {
+		tokenizer: new Floop.Tokenizer(),
+		grouper: new Floop.Grouper(function() {
+			return false;
+		}, function(c) {
+			return c;
+		})
+	};
+	
+	function isArray(obj) {
+		return typeof(obj) == 'object' && obj !== null && obj instanceof Array;
+	}
+	
+	Brainfuck.tokenizer.add("command", function(c) {
+		return "+-<>.,[]".indexOf(c) >= 0;
+	}, function() {return false;});
+	
+	Brainfuck.grouper.add(["+"], fun("+", function() {}), 1);
+	Brainfuck.grouper.add(["-"], fun("-", function() {}), 2);
+	Brainfuck.grouper.add(["<"], fun("<", function() {}), 3);
+	Brainfuck.grouper.add([">"], fun(">", function() {}), 4);
+	Brainfuck.grouper.add([","], fun(",", function() {}), 5);
+	Brainfuck.grouper.add(["."], fun(".", function() {}), 6);
+	Brainfuck.grouper.add(["[", {a: "tree"}, "]"], fun("[]", function() {}), 7);
+	Brainfuck.grouper.add([{a: "value"}, {b: "value"}], fun("then", function () {}), 8);
+	
+		/* Grapher start */
+	function text(x, y, str, style) {
+		var cont = document.getElementById('canvas-strings');
+		
+		var el = document.createElement('span');
+		el.style.top = y + "px";
+		el.style.left = x + "px";
+		
+		if (style) {
+			for (var k in style) {
+				el.style[k] = style[k];
+			}
+		}
+		
+		el.appendChild(document.createTextNode("" + str));
+		
+		cont.appendChild(el);
+	}
+	
+	function drawBlob(ctx, x, y, r, color, str) {
+		ctx.fillStyle = color;
+		
+		var str = "" + str;
+		var scale = 1 + str.length / 5;
+		
+		ctx.beginPath();
+		ctx.save();
+		ctx.scale(scale, 1);
+		ctx.moveTo(x/scale + r, y);
+		ctx.arc(x/scale, y, r, 0, Math.PI*2, false);
+		ctx.restore();
+		ctx.closePath();
+		
+		ctx.fill();
+		ctx.stroke();
+		
+		var fontSize = (r*0.9 - 4);
+		if (fontSize < 0) fontSize = 0.1;
+		
+		text(x - r*str.length/2, y - r*1.1, str, {
+			display: "block",
+			width: r*str.length + "px",
+			textAlign: "center",
+			lineHeight: r*2 + "px",
+			fontSize: fontSize + "pt"
+		});
+	}
+	
+	function isArray(obj) {
+		return typeof(obj) == 'object' && obj !== null && obj instanceof Array;
+	}
+	
+	function graph(ctx, tree, color, offset, step) {
+		var off = offset || {x: 0, y: 0};
+		var step = step || 1;
+		
+		var width = 800, height = 600;
+		var r = 25;
+		var offY = 200 / step;
+		
+		var tx = width/2 + off.x;
+		var ty = offY/2 + off.y;
+		
+		var wideEach = width / (tree.length * step);
+		var start = off.x - width / (step * 2);
+		
+		for (var i=1; i<tree.length; i++) {
+			var dx = start + i*wideEach, dy = off.y + offY;
+			var ex = width/2 + dx, ey = offY/4 + dy;
+			
+			ctx.beginPath();
+			ctx.moveTo(tx, ty);
+			ctx.lineTo(ex, ey);
+			ctx.closePath();
+			ctx.stroke();
+			
+			if (isArray(tree[i])) {
+				graph(ctx, tree[i], color, {x: dx, y: dy}, step+1);
+			} else {
+				drawBlob(ctx, ex, ey, r-(step+1)*2, color, tree[i], step+1);
+			}
+		}
+		
+		drawBlob(ctx, tx, ty, r-step*2, color, tree[0]);
+	}
+	
+	function funcToName(arr) {
+		arr[0] = arr[0].ffName;
+		
+		for (var i=1; i<arr.length; i++) {
+			if (isArray(arr[i])) {
+				funcToName(arr[i]);
+			} else if (typeof(arr[i]) == 'object') {
+				arr[i] = arr[i].value;
+			}
+		}
+	}
+	
+	function init() {
+		doGraph(document.getElementById('input'));
+	}
+	
+	function doGraph(el) {
+		var lang = FireLang;
+		var str = el.value;
+		
+		try {
+			var tree = lang.grouper.group(lang.tokenizer.tokenize(str));
+			funcToName(tree);
+		
+			var ctx = document.getElementById('canvas').getContext('2d');
+			var strEl = document.getElementById('canvas-strings');
+			
+			while (strEl.hasChildNodes()) strEl.removeChild(strEl.childNodes[0]);
+			
+			ctx.clearRect(0, 0, 800, 600);
+			document.getElementById('err').innerHTML = "";
+			graph(ctx, tree, "#FC0");
+		} catch (ex) {
+			document.getElementById('err').innerHTML = "Error: " + ex.message;
+		}
+	}
+	
+	window.onload = init;
+	</script>
+	<style>
+	#canvas {
+		border: 1px solid #CCC;
+	}
+	
+	#canvas-strings {
+		position: relative;
+		margin: 1px;
+		z-index: 10;
+		top: -600px;
+		
+		width: 800px;
+		height: 600px;
+	}
+	#canvas-strings * {
+		position: absolute;
+	}
+	
+	input {
+		display: block;
+		width: 790px;
+		margin: 0;
+		padding: 4px 5px;
+		font-family: monospace;
+		font-size: 10pt;
+		
+		background: #F5F5F5;
+		color: #666;
+		border: 1px solid #CCC;
+		border-bottom-width: 0;
+	}
+	input:focus {
+		color: #222;
+		background: #FFF;
+	}
+	
+	#err {
+		position: absolute;
+		top: 610px;
+		left: 10px;
+	}
+	</style>
+</head>
+<body>
+	<input id="input" type="text" value="foo = function(a, b) {a+b}; bar = foo(2, 3) + 5" onkeyup="doGraph(this)" />
+	<canvas id="canvas" width="800" height="600"></canvas>
+	<div id="canvas-strings"></div>
+	<span id="err"></span>
+</body>
+</html>
+<!DOCTYPE html>
+<html>
+<head>
+	<title>Tree grapher - firefly.nu</title>
+	<script src="src/floop.js"></script>
+	<script src="src/firelang.js"></script>
+	<script>
+	var indentation = 0;
+	function output() {};
+	
+	function fun(name, func) {
+		func.ffName = name;
+		return func;
+	}
+	
+		/* Brainfuck */
+	var Brainfuck = {
+		tokenizer: new Floop.Tokenizer(),
+		grouper: new Floop.Grouper(function() {
+			return false;
+		}, function(c) {
+			return c;
+		})
+	};
+	
+	function isArray(obj) {
+		return typeof(obj) == 'object' && obj !== null && obj instanceof Array;
+	}
+	
+	Brainfuck.tokenizer.add("command", function(c) {
+		return "+-<>.,[]".indexOf(c) >= 0;
+	}, function() {return false;});
+	
+	Brainfuck.grouper.add(["+"], fun("+", function() {}), 1);
+	Brainfuck.grouper.add(["-"], fun("-", function() {}), 2);
+	Brainfuck.grouper.add(["<"], fun("<", function() {}), 3);
+	Brainfuck.grouper.add([">"], fun(">", function() {}), 4);
+	Brainfuck.grouper.add([","], fun(",", function() {}), 5);
+	Brainfuck.grouper.add(["."], fun(".", function() {}), 6);
+	Brainfuck.grouper.add(["[", {a: "tree"}, "]"], fun("[]", function() {}), 7);
+	Brainfuck.grouper.add([{a: "value"}, {b: "value"}], fun("then", function () {}), 8);
+	
+		/* FireLang */
+	/*
+	var FireLang = {
+		tokenizer: new Floop.Tokenizer(),
+		grouper: new Floop.Grouper(function(str) {
+			return typeof(str) == 'string' && (str.match(/^".*"$/) ||
+					str.match(/^[A-Za-z\d_$]\w*$/) || str.match(/^\d+/));
+		}, function(obj) {
+			if (typeof(obj) != 'string') {
+				return obj;
+			} else if (obj.match(/^"(?:[^"]|(?=<\\)")*"$/)) {
+				return {value: obj.substr(1, obj.length-2)};
+			} else if (obj.match(/\d+/)) {
+				return {value: parseInt(obj)};
+			} else {
+				return obj;
+			}	
+		})
+	};
+	
+	function beginsIdentifier(c) {
+		return c >= "a" && c <= "z" || c >= "A" && c <= "Z" ||
+				c == "$" || c == "_";
+	}
+	function isNumber(c) {
+		return c >= "0" && c <= "9";
+	}
+	function isWhitespace(c) {
+		return c == " " || c == "\t" || c == "\n" || c == "\r";
+	}
+	function isIdentifier(c) {
+		return beginsIdentifier(c) || isNumber(c);
+	}
+	
+	FireLang.tokenizer.add("identifier", beginsIdentifier, isIdentifier);
+	FireLang.tokenizer.add("number", isNumber, isNumber);
+	FireLang.tokenizer.add("string", function(c) {
+		return c == '"';
+	}, function(c, i, str) {
+		return i == 1 || (str.charAt(i-1) != '"' && str.charAt(i-2) != "\\");
+	});
+	FireLang.tokenizer.add("whitespace", isWhitespace, isWhitespace, true);
+	FireLang.tokenizer.add("operator", function() {return true;}, function() {return false;});
+	
+	with (FireLang) {
+		grouper.add(["(", {a: "value"}, ")"], fun("id", function(a) {return a;}), 1, true);
+		grouper.add(["function", {a: "tree"}, "{", {code: "tree"}, "}"], fun("fun", function() {}), 2);
+		
+		grouper.add([{a: "value"}, "/", {b: "value"}], fun("/", function() {}), 4.1);
+		grouper.add([{a: "value"}, "*", {b: "value"}], fun("*", function() {}), 4.2);
+		grouper.add([{a: "value"}, "-", {b: "value"}], fun("-", function() {}), 4.3);
+		grouper.add([{a: "value"}, "+", {b: "value"}], fun("+", function() {}), 4.4);
+		
+		grouper.add([{a: "value"}, {b: "value"}], fun("call", function() {}), 8);
+		grouper.add([{a: "value"}, ",", {b: "value"}], fun("tuple", function() {}), 9);
+		grouper.add([{x: "literal"}, "=", {a: "value"}], fun("assign", function() {}), 15);
+		
+		grouper.add([{a: "value"}, ";", {b: "value"}], fun("sep", function() {}), 16);
+		grouper.add([{a: "value"}, ";"], fun("sep", function() {}), 16.1);
+		grouper.add([";", {a: "value"}], fun("sep", function() {}), 16.2);
+	}
+	*/
+	
+		/* Grapher start */
+	function text(x, y, str, style) {
+		var cont = document.getElementById('canvas-strings');
+		
+		var el = document.createElement('span');
+		el.style.top = y + "px";
+		el.style.left = x + "px";
+		
+		if (style) {
+			for (var k in style) {
+				el.style[k] = style[k];
+			}
+		}
+		
+		el.appendChild(document.createTextNode("" + str));
+		
+		cont.appendChild(el);
+	}
+	
+	function drawBlob(ctx, x, y, r, color, str) {
+		ctx.fillStyle = color;
+		
+		var str = "" + str;
+		var scale = 1 + str.length / 5;
+		
+		ctx.beginPath();
+		ctx.save();
+		ctx.scale(scale, 1);
+		ctx.moveTo(x/scale + r, y);
+		ctx.arc(x/scale, y, r, 0, Math.PI*2, false);
+		ctx.restore();
+		ctx.closePath();
+		
+		ctx.fill();
+		ctx.stroke();
+		
+		var fontSize = (r*0.9 - 4);
+		if (fontSize < 0) fontSize = 0.1;
+		
+		text(x - r*str.length/2, y - r*1.1, str, {
+			display: "block",
+			width: r*str.length + "px",
+			textAlign: "center",
+			lineHeight: r*2 + "px",
+			fontSize: fontSize + "pt"
+		});
+	}
+	
+	function isArray(obj) {
+		return typeof(obj) == 'object' && obj !== null && obj instanceof Array;
+	}
+	
+	function graph(ctx, tree, color, offset, step) {
+		var off = offset || {x: 0, y: 0};
+		var step = step || 1;
+		
+		var width = 800, height = 600;
+		var r = 25;
+		var offY = 200 / step;
+		
+		var tx = width/2 + off.x;
+		var ty = offY/2 + off.y;
+		
+		var wideEach = width / (tree.length * step);
+		var start = off.x - width / (step * 2);
+		
+		for (var i=1; i<tree.length; i++) {
+			var dx = start + i*wideEach, dy = off.y + offY;
+			var ex = width/2 + dx, ey = offY/4 + dy;
+			
+			ctx.beginPath();
+			ctx.moveTo(tx, ty);
+			ctx.lineTo(ex, ey);
+			ctx.closePath();
+			ctx.stroke();
+			
+			if (isArray(tree[i])) {
+				graph(ctx, tree[i], color, {x: dx, y: dy}, step+1);
+			} else {
+				drawBlob(ctx, ex, ey, r-(step+1)*2, color, tree[i], step+1);
+			}
+		}
+		
+		drawBlob(ctx, tx, ty, r-step*2, color, tree[0]);
+	}
+	
+	function funcToName(arr) {
+		arr[0] = arr[0].ffName;
+		
+		for (var i=1; i<arr.length; i++) {
+			if (isArray(arr[i])) {
+				funcToName(arr[i]);
+			} else if (typeof(arr[i]) == 'object') {
+				arr[i] = arr[i].value;
+			}
+		}
+	}
+	
+	function init() {
+		doGraph(document.getElementById('input'));
+	}
+	
+	function doGraph(el) {
+		var lang = FireLang;
+		var str = el.value;
+		
+		try {
+			var tree = lang.grouper.group(lang.tokenizer.tokenize(str));
+			funcToName(tree);
+		
+			var ctx = document.getElementById('canvas').getContext('2d');
+			var strEl = document.getElementById('canvas-strings');
+			
+			while (strEl.hasChildNodes()) strEl.removeChild(strEl.childNodes[0]);
+			
+			ctx.clearRect(0, 0, 800, 600);
+			document.getElementById('err').innerHTML = "";
+			graph(ctx, tree, "#FC0");
+		} catch (ex) {
+			document.getElementById('err').innerHTML = "Error: " + ex.message;
+		}
+	}
+	
+	window.onload = init;
+	</script>
+	<style>
+	#canvas {
+		border: 1px solid #CCC;
+	}
+	
+	#canvas-strings {
+		position: relative;
+		margin: 1px;
+		z-index: 10;
+		top: -600px;
+		
+		width: 800px;
+		height: 600px;
+	}
+	#canvas-strings * {
+		position: absolute;
+	}
+	
+	input {
+		display: block;
+		width: 790px;
+		margin: 0;
+		padding: 4px 5px;
+		font-family: monospace;
+		font-size: 10pt;
+		
+		background: #F5F5F5;
+		color: #666;
+		border: 1px solid #CCC;
+		border-bottom-width: 0;
+	}
+	input:focus {
+		color: #222;
+		background: #FFF;
+	}
+	
+	#err {
+		position: absolute;
+		top: 610px;
+		left: 10px;
+	}
+	</style>
+</head>
+<body>
+	<input id="input" type="text" value="foo = function(a, b) {a+b}; bar = foo(2, 3) + 5" onkeyup="doGraph(this)" />
+	<canvas id="canvas" width="800" height="600"></canvas>
+	<div id="canvas-strings"></div>
+	<span id="err"></span>
+</body>
+</html>

src/brainfuck.html

+<!DOCTYPE html>
+<html>
+<head>
+	<title>Brainfuck! - firefly.nu</title>
+	<script src="floop.js"></script>
+	<script src="lib/json.js"></script>
+	<script>
+	var indentation = 0;
+	function output(str) {
+		document.getElementById('output').appendChild(
+				document.createTextNode("" + str + "\n"));
+	}
+	
+	function init() {
+		var grouper = new Floop.Grouper(function() {
+			return false;
+		}, function(c) {
+			return c;
+		});
+		
+		var state = {
+			memory: [],
+			mp: 0,
+			input: "92\000"
+		}
+		
+		for (var i=0; i<20; i++) state.memory[i] = 0;
+		
+		function fun(name, func) {
+			func.name = name;
+			return func;
+		}
+		
+		function execute(op) {
+			var func = op[0];
+			var args = op.slice(1);
+			
+			func.apply(state, args);
+		}
+		state.execute = execute;
+		
+		grouper.add(["+"], fun("+", function() {
+			this.memory[this.mp]++;
+		}), 1);
+		grouper.add(["-"], fun("-", function() {
+			this.memory[this.mp]--;
+		}), 2);
+		grouper.add(["<"], fun("<", function() {
+			this.mp--;
+		}), 3);
+		grouper.add([">"], fun(">", function() {
+			this.mp++;
+		}), 4);
+		grouper.add([","], fun(",", function() {
+			this.memory[this.mp] = this.input.charCodeAt(0);
+			this.input = this.input.substr(1);
+		}), 5);
+		grouper.add(["."], fun(".", function() {
+		//	output("Output: " + String.fromCharCode(this.memory[this.mp]));
+		}), 6);
+		grouper.add(["[", {a: "tree"}, "]"], fun("loop", function(a) {
+			while (this.memory[this.mp] != 0)
+				this.execute(a);
+		}), 7);
+		grouper.add([{a: "value"}, {b: "value"}], fun("t", function(a, b) {
+			this.execute(a);
+			this.execute(b);
+		}), 8);
+		
+		var tokenizer = new Floop.Tokenizer();
+		tokenizer.add("command", function(c) {
+			return "+-<>.,[]".indexOf(c) >= 0;
+		}, function() {return false;});
+		
+		var tokens = tokenizer.tokenize(",>,>++++++[-<--------<-------->>]<<[>[->+>+<<]>[-<<-" +
+				"[>]>>>[<[>>>-<<<[-]]>>]<<]>>>+<<[-<<+>>]<<<]>[-]>>>>[-<<<<<+>>>>>]<<<<++++++[-<++++++++>]<.");
+		var tree = grouper.group(tokens);
+		
+		output("Tokens: " + tokens);
+		output("Grouped: " + anyExpr(tree));
+		
+		var time = (new Date()).getTime();
+		for (var i=0; i<100; i++) {
+			execute(tree);
+			
+			for (var j=0; j<20; j++) state.memory[i] = 0;
+			state.mp = 0;
+			state.input = "92\000";
+		}
+		var time2 = (new Date()).getTime();
+		
+		output("Memory: " + anyExpr(state.memory));
+		output("Time: " + (time2 - time) + "ms");
+	}
+	
+	window.onload = init;
+	</script>
+</head>
+<body>
+	<pre id="output"></pre>
+</body>
+</html>

src/brainfuck.html~

+<!DOCTYPE html>
+<html>
+<head>
+	<title>Brainfuck! - firefly.nu</title>
+	<script src="floop.js"></script>
+	<script src="lib/json.js"></script>
+	<script>
+	var indentation = 0;
+	function output(str) {
+		document.getElementById('output').appendChild(
+				document.createTextNode("" + str + "\n"));
+	}
+	
+	function init() {
+		var grouper = new Floop.Grouper(function() {
+			return false;
+		}, function(c) {
+			return c;
+		});
+		
+		var state = {
+			memory: [],
+			mp: 0,
+			input: "92\000"
+		}
+		
+		for (var i=0; i<20; i++) state.memory[i] = 0;
+		
+		function fun(name, func) {
+			func.name = name;
+			return func;
+		}
+		
+		function execute(op) {
+			var func = op[0];
+			var args = op.slice(1);
+			
+			func.apply(state, args);
+		}
+		state.execute = execute;
+		
+		grouper.add(["+"], fun("+", function() {
+			this.memory[this.mp]++;
+		}), 1);
+		grouper.add(["-"], fun("-", function() {
+			this.memory[this.mp]--;
+		}), 2);
+		grouper.add(["<"], fun("<", function() {
+			this.mp--;
+		}), 3);
+		grouper.add([">"], fun(">", function() {
+			this.mp++;
+		}), 4);
+		grouper.add([","], fun(",", function() {
+			this.memory[this.mp] = this.input.charCodeAt(0);
+			this.input = this.input.substr(1);
+		}), 5);
+		grouper.add(["."], fun(".", function() {
+		//	output("Output: " + String.fromCharCode(this.memory[this.mp]));
+		}), 6);
+		grouper.add(["[", {a: "tree"}, "]"], fun("loop", function(a) {
+			while (this.memory[this.mp] != 0)
+				this.execute(a);
+		}), 7);
+		grouper.add([{a: "value"}, {b: "value"}], fun("", function(a, b) {
+			this.execute(a);
+			this.execute(b);
+		}), 8);
+		
+		var tokenizer = new Floop.Tokenizer();
+		tokenizer.add("command", function(c) {
+			return "+-<>.,[]".indexOf(c) >= 0;
+		}, function() {return false;});
+		
+		var tokens = tokenizer.tokenize(",>,>++++++[-<--------<-------->>]<<[>[->+>+<<]>[-<<-" +
+				"[>]>>>[<[>>>-<<<[-]]>>]<<]>>>+<<[-<<+>>]<<<]>[-]>>>>[-<<<<<+>>>>>]<<<<++++++[-<++++++++>]<.");
+		var tree = grouper.group(tokens);
+		
+		output("Tokens: " + tokens);
+		output("Grouped: " + anyExpr(tree));
+		
+		var time = (new Date()).getTime();
+		for (var i=0; i<100; i++) {
+			execute(tree);
+			
+			for (var j=0; j<20; j++) state.memory[i] = 0;
+			state.mp = 0;
+			state.input = "92\000";
+		}
+		var time2 = (new Date()).getTime();
+		
+		output("Memory: " + anyExpr(state.memory));
+		output("Time: " + (time2 - time) + "ms");
+	}
+	
+	window.onload = init;
+	</script>
+</head>
+<body>
+	<pre id="output"></pre>
+</body>
+</html>
+var FireLang = {};
+
+(function() {
+	function createFireObject(value, type, props) {
+		var out = props || {};
+		
+		if (typeof(type) != 'string') {
+			switch (typeof(value)) {
+				case 'number': case 'string': case 'function':
+					var type = typeof(value);
+					break;
+				default:
+					throw new Error("Internal error: Couldn't create FireObject; " +
+							"couldn't find fitting type (" + value + ").");
+			}
+		}
+		
+		out.type = type;
+		out.value = value;
+		
+		if (out.type == 'function') {
+			out.call = function() {
+				this.execute(value);
+			};
+		}
+		
+		return out;
+	}
+	
+		/* Grammar functions */
+	function isValue(str) {
+		return typeof(str) == 'string' && (str.match(/^".*"$/) ||
+				str.match(/^[A-Za-z\d_$]\w*$/) || str.match(/^\d+/));
+	};
+	function getValue(obj) {
+		if (typeof(obj) != 'string') {
+			return obj;
+		} else if (obj.match(/^"(?:[^"]|(?=<\\)")*"$/)) {
+			return createFireObject(obj.substr(1, obj.length-2));
+		} else if (obj.match(/\d+/)) {
+			return createFireObject(parseInt(obj));
+		} else {
+			return obj;
+		}	
+	};
+	
+	function beginsIdentifier(c) {
+		return c >= "a" && c <= "z" || c >= "A" && c <= "Z" ||
+				c == "$" || c == "_";
+	}
+	function isNumber(c) {
+		return c >= "0" && c <= "9";
+	}
+	function isWhitespace(c) {
+		return c == " " || c == "\t" || c == "\n" || c == "\r";
+	}
+	function isIdentifier(c) {
+		return beginsIdentifier(c) || isNumber(c);
+	}
+	function beginsString(c) {
+		return c == '"';
+	}
+	function isString(c, i, str) {
+			return i == 1 || (str.charAt(i-1) != '"' && str.charAt(i-2) != "\\");
+	}
+	
+	FireLang.tokenizer = new Floop.Tokenizer();
+	FireLang.grouper = new Floop.Grouper(isValue, getValue);
+	
+	with (FireLang.tokenizer) {
+		add("identifier", beginsIdentifier, isIdentifier);
+		add("number", isNumber, isNumber);
+		add("string", beginsString, isString);
+		add("whitespace", isWhitespace, isWhitespace, true);
+		add("operator", function() {return true;}, function() {return false;});
+	}
+	
+		/* Helper functions */
+	function fun(name, func) {
+		func.ffName = name;
+		return func;
+	}
+	
+	function unwrap(tuple) {
+		if (typeof(tuple) == 'string') {
+			return [tuple];
+		} else if (typeof(tuple[1]) == 'object') {
+			return unwrap(tuple[1]).concat(tuple[2]);
+		} else {
+			return [tuple[1], tuple[2]];
+		}
+	}
+	
+	with (FireLang.grouper) {
+		add(["(", {a: "value"}, ")"], fun("id", function(a) {
+			return a;
+		}), 1, true);
+		add(["function", {a: "tree"}, "{", {code: "tree"}, "}"], fun("fun", function(a, code) {
+			var args = unwrap(a);
+			
+			output("Defining function!");
+			output("  args: " + anyExpr(args));
+			output("  code: " + anyExpr(code));
+			
+			return createFireObject(code, "function", {args: args, code: code});
+		}), 2);
+		
+		add([{a: "value"}, "/", {b: "value"}], fun("/", function(a, b) {
+			return createFireObject(a.value / b.value);
+		}), 4.1);
+		add([{a: "value"}, "*", {b: "value"}], fun("*", function(a, b) {
+			return createFireObject(a.value * b.value);
+		}), 4.2);
+		add([{a: "value"}, "-", {b: "value"}], fun("-", function(a, b) {
+			return createFireObject(a.value - b.value);
+		}), 4.3);
+		add([{a: "value"}, "+", {b: "value"}], fun("+", function(a, b) {
+			return createFireObject(a.value + b.value);
+		}), 4.4);
+		
+		add([{func: "value"}, {args: "value"}], fun("call", function(func, _args) {
+			if (!func || (func.type != "function" && func.type != "function-builtin"))
+				throw new Error("Tried to call non-function '" + func + "'");
+			
+			switch (_args.type) {
+				case 'tuple':
+					var args = _args.value; break;
+				default:
+					var args = [_args];
+			}
+			
+			if (func.type == "function-builtin") {
+				func.execute.apply(this, args);
+			} else {
+				for (var i=0; i<func.args.length; i++) {
+					output("sätter " + func.args[i]);
+					this.namespace[func.args[i]] = args[i] || createFireObject("GAH undefined");
+				}
+				
+				return this.execute(func.code);
+			}
+		}), 8);
+		add([{a: "value"}, ",", {b: "value"}], fun("tuple", function(a, b) {
+			return createFireObject([a, b], "tuple");
+		}), 9);
+		add([{x: "literal"}, "=", {a: "value"}], fun("assign", function(name, value) {
+			output("Assigning value! " + name + ": " + anyExpr(value));
+			this.namespace[name] = value;
+		}), 15);
+		
+		add([{a: "value"}, ";", {b: "value"}], fun("sep", function(a, b) {
+			return b;
+		}), 16);
+		add([{a: "value"}, ";"], fun("sep", function(a) {
+			return a;
+		}), 16.1);
+		add([";", {a: "value"}], fun("sep", function(a) {
+			return a;
+		}), 16.2);
+	}
+})();
+var FireLang = {};
+
+(function() {
+	function createFireObject(value, type, props) {
+		var out = props || {};
+		
+		if (typeof(type) != 'string') {
+			switch (typeof(value)) {
+				case 'number': case 'string': case 'function':
+					var type = typeof(value);
+					break;
+				default:
+					throw new Error("Internal error: Couldn't create FireObject; " +
+							"couldn't find fitting type (" + value + ").");
+			}
+		}
+		
+		out.type = type;
+		out.value = value;
+		
+		if (out.type == 'function') {
+			out.call = function() {
+				this.execute(value);
+			};
+		}
+		
+		return out;
+	}
+	
+		/* Grammar functions */
+	function isValue(str) {
+		return typeof(str) == 'string' && (str.match(/^".*"$/) ||
+				str.match(/^[A-Za-z\d_$]\w*$/) || str.match(/^\d+/));
+	};
+	function getValue(obj) {
+		if (typeof(obj) != 'string') {
+			return obj;
+		} else if (obj.match(/^"(?:[^"]|(?=<\\)")*"$/)) {
+			return createFireObject(obj.substr(1, obj.length-2));
+		} else if (obj.match(/\d+/)) {
+			return createFireObject(parseInt(obj));
+		} else {
+			return obj;
+		}	
+	};
+	
+	function beginsIdentifier(c) {
+		return c >= "a" && c <= "z" || c >= "A" && c <= "Z" ||
+				c == "$" || c == "_";
+	}
+	function isNumber(c) {
+		return c >= "0" && c <= "9";
+	}
+	function isWhitespace(c) {
+		return c == " " || c == "\t" || c == "\n" || c == "\r";
+	}
+	function isIdentifier(c) {
+		return beginsIdentifier(c) || isNumber(c);
+	}
+	function beginsString(c) {
+		return c == '"';
+	}
+	function isString(c, i, str) {
+			return i == 1 || (str.charAt(i-1) != '"' && str.charAt(i-2) != "\\");
+	}
+	
+	FireLang.tokenizer = new Floop.Tokenizer();
+	FireLang.grouper = new Floop.Grouper(isValue, getValue);
+	
+	with (FireLang.tokenizer) {
+		add("identifier", beginsIdentifier, isIdentifier);
+		add("number", isNumber, isNumber);
+		add("string", beginsString, isString);
+		add("whitespace", isWhitespace, isWhitespace, true);
+		add("operator", function() {return true;}, function() {return false;});
+	}
+	
+		/* Helper functions */
+	function fun(name, func) {
+		func.ffName = name;
+		return func;
+	}
+	
+	function unwrap(tuple) {
+		if (typeof(tuple) == 'string') {
+			return [tuple];
+		} else if (typeof(tuple[1]) == 'object') {
+			return unwrap(tuple[1]).concat(tuple[2]);
+		} else {
+			return [tuple[1], tuple[2]];
+		}
+	}
+	
+	with (FireLang.grouper) {
+		add(["(", {a: "value"}, ")"], fun("id", function(a) {
+			return a;
+		}), 1, true);
+		add(["function", {a: "tree"}, "{", {code: "tree"}, "}"], fun("fun", function(a, code) {
+			var args = unwrap(a);
+			
+			output("Defining function!");
+			output("  args: " + anyExpr(args));
+			output("  code: " + anyExpr(code));
+			
+			return createFireObject(code, "function", {args: args, code: code});
+		}), 2);
+		
+		add([{a: "value"}, "/", {b: "value"}], fun("/", function(a, b) {
+			return createFireObject(a.value / b.value);
+		}), 4.1);
+		add([{a: "value"}, "*", {b: "value"}], fun("*", function(a, b) {
+			return createFireObject(a.value * b.value);
+		}), 4.2);
+		add([{a: "value"}, "-", {b: "value"}], fun("-", function(a, b) {
+			return createFireObject(a.value - b.value);
+		}), 4.3);
+		add([{a: "value"}, "+", {b: "value"}], fun("+", function(a, b) {
+			return createFireObject(a.value + b.value);
+		}), 4.4);
+		
+		add([{func: "value"}, {args: "value"}], fun("call", function(func, _args) {
+			if (!func || (func.type != "function" && func.type != "function-builtin"))
+				throw new Error("Tried to call non-function '" + func + "'");
+			
+			switch (_args.type) {
+				case 'tuple':
+					var args = _args.value; break;
+				default:
+					var args = [_args];
+			}
+			
+			if (func.type == "function-builtin") {
+				func.execute.apply(this, args);
+			} else {
+				for (var i=0; i<func.args.length; i++) {
+					output("sätter " + func.args[i]);
+					this.namespace[func.args[i]] = args[i] || createFireObject("GAH undefined");
+				}
+				
+				return this.execute(func.code);
+			}
+		}), 8);
+		add([{a: "value"}, ",", {b: "value"}], fun("tuple", function(a, b) {
+			return createFireObject([a, b], "tuple");
+		}), 9);
+		add([{x: "literal"}, "=", {a: "value"}], fun("assign", function(name, value) {
+			output("Assigning value! " + name + ": " + anyExpr(value));
+			this.namespace[name] = value;
+		}), 15);
+		
+		add([{a: "value"}, ";", {b: "value"}], fun("sep", function(a, b) {
+			return b;
+		}), 16);
+		add([{a: "value"}, ";"], fun("sep", function(a) {
+			return a;
+		}), 16.1);
+		add([";", {a: "value"}], fun("sep", function(a) {
+			return a;
+		}), 16.2);
+	}
+})();
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.