Commits

firefly  committed 2915fa8

_Actually_ moving pages to separate folder.

  • Participants
  • Parent commits 6bd84e7

Comments (0)

Files changed (22)

File graph.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);
-	
-		/* 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>

File graph.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>

File pages/brainfuck.html

+<!DOCTYPE html>
+<html>
+<head>
+	<title>Brainfuck! - firefly.nu</title>
+	<script src="../src/floop.js"></script>
+	<script src="../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>

File pages/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>

File pages/firelang.css

+/* 
+ * CSS for the FireLang demos.
+ * Author: Jonas Höglund
+ */
+
+body {background: #F5F5F5;}
+* {
+	font-family: monospace;
+	font-size: 10pt;
+}
+h1 {font-size: 18pt;}
+
+#operators {
+	display: block;
+	margin: 0.3em 0;
+}
+
+textarea {
+	display: block;
+}
+
+.wrapper {
+	position: relative;
+	margin-bottom: 1em;
+	padding-bottom: 0.3em;
+	border: 1px solid #E0E5EE;
+	background: #FFF;
+}
+.hider {
+	height: 6px;
+	background: #E0E5EE;
+}
+.hider:hover {background: #C0CAD3;}
+h2 {
+	position: absolute;
+	top: -0.6em;
+	right: 1em;
+	margin: 0;
+	padding: 0.1em 0.3em;
+	background: #FFF;
+	border: 1px solid #E0E5EE;
+}
+pre {margin: 0 0.3em;}
+
+hr {
+	margin: 2em 0;
+	height: 1px;
+	border: 0 none;
+	background: #999;
+}
+
+#footer {
+	display: block;
+	margin-top: 1.5em;
+	text-align: center;
+}

File pages/firelang.css~

+/* 
+ * CSS for the FireLang demos.
+ * Author: Jonas Höglund.
+ */
+
+body {background: #F5F5F5;}
+* {
+	font-family: monospace;
+	font-size: 10pt;
+}
+h1 {font-size: 18pt;}
+
+#operators {
+	display: block;
+	margin: 0.3em 0;
+}
+
+textarea {
+	display: block;
+}
+
+.wrapper {
+	position: relative;
+	margin-bottom: 1em;
+	padding-bottom: 0.3em;
+	border: 1px solid #E0E5EE;
+	background: #FFF;
+}
+.hider {
+	height: 6px;
+	background: #E0E5EE;
+}
+.hider:hover {background: #C0CAD3;}
+h2 {
+	position: absolute;
+	top: -0.6em;
+	right: 1em;
+	margin: 0;
+	padding: 0.1em 0.3em;
+	background: #FFF;
+	border: 1px solid #E0E5EE;
+}
+pre {margin: 0 0.3em;}
+
+hr {
+	margin: 2em 0;
+	height: 1px;
+	border: 0 none;
+	background: #999;
+}
+
+#footer {
+	display: block;
+	margin-top: 1.5em;
+	text-align: center;
+}

File pages/graph.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);
+	
+		/* 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>

File pages/graph.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);
+	
+		/* 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>

File pages/index.html

+<!DOCTYPE html>
+<html>
+<head>
+	<title>FireLang2 - firefly.nu</title>
+</head>
+<body>
+	<h1>FireLang2</h1>
+	<ul>
+		<li><a href="brainfuck.html">brainfuck.html</li>
+		<li><a href="graph.html">graph.html</li>
+		<li><a href="interpreter.html">interpreter.html</li>
+		<li><a href="parts.html">parts.html</li>
+	</ul>
+</body>
+</html>

File pages/index.html~

+<!DOCTYPE html>
+<html>
+<head>
+	<title>FireLang2 - firefly.nu</title>
+</head>
+<body>
+	<h1>FireLang2</h1>
+	<ul>
+		<li><a href="brainfuck.html">brainfuck.html</li>
+		<li><a href="graph.html">graph.html</li>
+		<li><a href="interpreter.html">interpreter.html</li>
+		<li><a href="parts.html">parts.html</li>
+	</ul>
+</body>
+</html>

File pages/interpreter.html

+<!DOCTYPE html>
+<html>
+<head>
+	<title>FireLang - firefly.nu</title>
+	<meta charset="UTF-8" />
+	<link rel="stylesheet" type="text/css" href="firelang.css" />
+	<script src="../src/floop.js"></script>
+	<script src="../src/firelang.js"></script>
+	<script>
+	function output(str) {
+		var space = "";
+	//	for (var i=0; i<indentation; i++) space += "   ";
+		document.getElementById('debug').appendChild(document.createTextNode(
+				"" + str + "\n"));
+	}
+	
+	function firelangOutput(str, isError) {
+		var el = document.createTextNode(str + "\n");
+		var span = document.createElement('span');
+		
+		span.appendChild(el);
+		if (isError) span.style.color = "#C00";
+		
+		document.getElementById('stdout').appendChild(span);
+	}
+	
+	function showHide(id) {
+		var st = document.getElementById(id).style;
+		
+		if (st.display == 'none') st.display = 'block';
+		else st.display = 'none';
+	}
+	
+	function hide() {
+		for (var i=0; i<arguments.length; i++) {
+			document.getElementById(arguments[i]).style.display = 'none';
+		}
+	}
+	
+		/* ---------- FireLang ---------- */
+	var Syntax = FireLang;
+	var FireLang = function(stdout) {
+		this.namespace = {};
+		
+		this.namespace["print"] = {
+			type: "function-builtin",
+			execute: function(str) {
+				stdout(str.value);
+			}
+		};
+	};
+	
+	for (var k in Syntax) FireLang[k] = Syntax[k];
+	
+	(function() {
+			/* Helper functions */
+		function isArray(obj) {
+			return typeof(obj) == 'object' && obj !== null && obj instanceof Array;
+		}
+		
+		function execute(interp, stm) {
+			var func = stm[0];
+			var args = stm.slice(1).map(function(a, i) {
+				switch (func.floop.arguments[i].type) {
+					case 'value':
+						if (isArray(a)) var value = execute(interp, a);
+						else var value = a;
+						
+						if (typeof(value) == 'string') {
+							if (value in interp.namespace) {
+								return interp.namespace[value];
+							} else {
+								throw new Error("Undefined variable: '" + value + "'");
+							}
+						} else {
+							return value;
+						}
+					case 'tree': case 'literal':
+						return a;
+					default:
+						throw new Error("Invalid argument type: '" + 
+								func.floop.arguments[i].type + "'");
+				}
+			});
+			
+			return func.apply({
+				namespace: interp.namespace,
+				execute: function(stm) {
+					return execute(interp, stm);
+				}
+			}, args);
+		}
+		
+		FireLang.prototype.interpret = function(str) {
+			var stm = FireLang.grouper.group(FireLang.tokenizer.tokenize(str));
+			execute(this, stm);
+		};
+	})();
+	
+	var interpreter = new FireLang(firelangOutput);
+		/* ---------- Init ---------- */
+	function interpret() {
+		document.getElementById('debug').innerHTML = "";
+		document.getElementById('stdout').innerHTML = "";
+		document.getElementById('error').innerHTML = "";
+		
+		try {
+			var code = document.getElementById('input').value;
+			interpreter.interpret(code);
+			
+			output(" ----");
+			for (var k in interpreter.namespace) {
+				output(k + ": " + interpreter.namespace[k]);
+			}
+		} catch (err) {
+			document.getElementById('error').innerHTML = "Error: " + err.message;
+		}
+	}
+	
+	function init() {
+	//	hide("debug");
+		interpret();
+	}
+	
+	window.onload = init;
+	</script>
+</head>
+<body>
+	<h1>FireLang</h1>
+	
+	<div class="wrapper">
+		<h2>Input</h2>
+		<div class="hider" onclick="showHide('input')"></div>
+		<textarea id="input" rows="10" cols="120" onkeyup="interpret()">
+foo = 42 + 3;
+bar = function(x, y) {x + y};
+baz = bar(2, 3);
+hei = "Foo";
+print("Hello, world!");
+print(hei + " " + baz + " " + foo);</textarea>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Output</h2>
+		<div class="hider" onclick="showHide('stdout')"></div>
+		<pre id="stdout"></pre>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Debug</h2>
+		<div class="hider" onclick="showHide('debug')"></div>
+		<pre id="debug"></pre>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Error</h2>
+		<div class="hider" onclick="showHide('error')"></div>
+		<pre id="error"></pre>
+	</div>
+	
+	<span id="footer">&copy; Jonas Höglund 2010</span>
+</body>
+</html>

File pages/interpreter.html~

+<!DOCTYPE html>
+<html>
+<head>
+	<title>FireLang - firefly.nu</title>
+	<meta charset="UTF-8" />
+	<link rel="stylesheet" type="text/css" href="firelang.css" />
+	<script src="floop.js"></script>
+	<script src="firelang.js"></script>
+	<script>
+	function output(str) {
+		var space = "";
+	//	for (var i=0; i<indentation; i++) space += "   ";
+		document.getElementById('debug').appendChild(document.createTextNode(
+				"" + str + "\n"));
+	}
+	
+	function firelangOutput(str, isError) {
+		var el = document.createTextNode(str + "\n");
+		var span = document.createElement('span');
+		
+		span.appendChild(el);
+		if (isError) span.style.color = "#C00";
+		
+		document.getElementById('stdout').appendChild(span);
+	}
+	
+	function showHide(id) {
+		var st = document.getElementById(id).style;
+		
+		if (st.display == 'none') st.display = 'block';
+		else st.display = 'none';
+	}
+	
+	function hide() {
+		for (var i=0; i<arguments.length; i++) {
+			document.getElementById(arguments[i]).style.display = 'none';
+		}
+	}
+	
+		/* ---------- FireLang ---------- */
+	var Syntax = FireLang;
+	var FireLang = function(stdout) {
+		this.namespace = {};
+		
+		this.namespace["print"] = {
+			type: "function-builtin",
+			execute: function(str) {
+				stdout(str.value);
+			}
+		};
+	};
+	
+	for (var k in Syntax) FireLang[k] = Syntax[k];
+	
+	(function() {
+			/* Helper functions */
+		function isArray(obj) {
+			return typeof(obj) == 'object' && obj !== null && obj instanceof Array;
+		}
+		
+		function execute(interp, stm) {
+			var func = stm[0];
+			var args = stm.slice(1).map(function(a, i) {
+				switch (func.floop.arguments[i].type) {
+					case 'value':
+						if (isArray(a)) var value = execute(interp, a);
+						else var value = a;
+						
+						if (typeof(value) == 'string') {
+							if (value in interp.namespace) {
+								return interp.namespace[value];
+							} else {
+								throw new Error("Undefined variable: '" + value + "'");
+							}
+						} else {
+							return value;
+						}
+					case 'tree': case 'literal':
+						return a;
+					default:
+						throw new Error("Invalid argument type: '" + 
+								func.floop.arguments[i].type + "'");
+				}
+			});
+			
+			return func.apply({
+				namespace: interp.namespace,
+				execute: function(stm) {
+					return execute(interp, stm);
+				}
+			}, args);
+		}
+		
+		FireLang.prototype.interpret = function(str) {
+			var stm = FireLang.grouper.group(FireLang.tokenizer.tokenize(str));
+			execute(this, stm);
+		};
+	})();
+	
+	var interpreter = new FireLang(firelangOutput);
+		/* ---------- Init ---------- */
+	function interpret() {
+		document.getElementById('debug').innerHTML = "";
+		document.getElementById('stdout').innerHTML = "";
+		document.getElementById('error').innerHTML = "";
+		
+		try {
+			var code = document.getElementById('input').value;
+			interpreter.interpret(code);
+			
+			output(" ----");
+			for (var k in interpreter.namespace) {
+				output(k + ": " + interpreter.namespace[k]);
+			}
+		} catch (err) {
+			document.getElementById('error').innerHTML = "Error: " + err.message;
+		}
+	}
+	
+	function init() {
+	//	hide("debug");
+		interpret();
+	}
+	
+	window.onload = init;
+	</script>
+</head>
+<body>
+	<h1>FireLang</h1>
+	
+	<div class="wrapper">
+		<h2>Input</h2>
+		<div class="hider" onclick="showHide('input')"></div>
+		<textarea id="input" rows="10" cols="120" onkeyup="interpret()">
+foo = 42 + 3;
+bar = function(x, y) {x + y};
+baz = bar(2, 3);
+hei = "Foo";
+print("Hello, world!");
+print(hei + " " + baz + " " + foo);</textarea>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Output</h2>
+		<div class="hider" onclick="showHide('stdout')"></div>
+		<pre id="stdout"></pre>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Debug</h2>
+		<div class="hider" onclick="showHide('debug')"></div>
+		<pre id="debug"></pre>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Error</h2>
+		<div class="hider" onclick="showHide('error')"></div>
+		<pre id="error"></pre>
+	</div>
+	
+	<span id="footer">&copy; Jonas Höglund 2010</span>
+</body>
+</html>

File pages/parts.html

+<!DOCTYPE html>
+<html>
+<head>
+	<title>Floop - firefly.nu</title>
+	<meta charset="UTF-8" />
+	<link rel="stylesheet" type="text/css" href="firelang.css" />
+	<script src="../src/lib/json.js"></script>
+	<script src="../src/floop.js"></script>
+	<script>
+	var indentation = 0;
+	function output(str) {
+		var space = "";
+		for (var i=0; i<indentation; i++) space += "   ";
+		document.getElementById('output').appendChild(document.createTextNode(
+				space + str + "\n"));
+	}
+	
+	var Data = {};
+	
+	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 {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);
+	}
+	
+	function updateOperators(el) {
+		var ops = el.value.split(/\n/g);
+		
+		Data.grouper = new Floop.Grouper(isValue, getValue);
+		var none = function() {};
+		
+		function isIdentifier(str) {
+			return str.match(/^[A-Za-z\d_$]\w*$/);
+		}
+		
+		var tkz = new Floop.Tokenizer();
+		tkz.add("identifier", beginsIdentifier, isIdentifier);
+		tkz.add("number", isNumber, isNumber);
+		tkz.add("string", function(c) {
+			return c == '"';
+		}, function(c, i, str) {
+			return i == 1 || (str.charAt(i-1) != '"' && str.charAt(i-2) != "\\");
+		});
+		tkz.add("whitespace", isWhitespace, isWhitespace, true);
+		tkz.add("operator", function() {return true;}, function() {return false;});
+		
+		for (var i=0; i<ops.length; i++) {
+			var tmp = ops[i].match(/^\s*([\d.]+)\s*{\s*([^}]*)\s*}\s*"([^"]*)"\s*$/);
+			if (!tmp) continue;
+			
+			var toks = tkz.tokenize(tmp[2]);
+			
+			toks = toks.map(function(a) {
+				if (isIdentifier(a)) {
+					var out = {};
+					
+					if (a.charAt(0) == "$") {
+						out[a.substr(1)] = "literal";
+					} else {
+						out[a] = "value";
+					}
+					
+					return out;
+				} else {
+					return a;
+				}
+			});
+			
+			(function() {
+				var name = tmp[3];
+				Data.grouper.add(toks, function() {return name;}, tmp[1]);
+			})();
+		}
+		
+		document.getElementById('output2').innerHTML = Data.grouper.operators.join("\n");
+		updateField(document.getElementById('input'));
+	}
+	
+	function updateField(el) {
+		var stms = el.value.split(/;/g);
+		document.getElementById('output').innerHTML = "";
+		
+		document.getElementById('output3').innerHTML = stms.map(function(a) {
+			var tokens = Floop.tokenize(a);
+			
+			try {
+				var gr = Data.grouper.group(tokens);
+				return "" + sExpr(gr) != "<BUG!>" ? sExpr(gr) : FF.Utils.toJson(gr);
+			} catch (err) {
+				return "Error: " + err.message;
+			}
+		}).join("\n");
+	}
+	
+	function showHide(id) {
+		var st = document.getElementById(id).style;
+		
+		if (st.display == 'none') st.display = 'block';
+		else st.display = 'none';
+	}
+	
+	function hide() {
+		for (var i=0; i<arguments.length; i++) {
+			document.getElementById(arguments[i]).style.display = 'none';
+		}
+	}
+	
+	function init() {
+		updateOperators(document.getElementById('operators'));
+		hide("operators", "output", "output2");
+	}
+	
+	window.onload = init;
+	</script>
+</head>
+<body>
+	<h1>FireLang: Floop</h1>
+	
+	<div class="wrapper">
+		<h2>Operators</h2>
+		<div class="hider" onclick="showHide('operators')"></div>
+		<textarea id="operators" onkeyup="updateOperators(this)" rows="10" cols="120">
+0 {( a )} "id"
+
+5.1 {a / b} "/"
+5.2 {a * b} "*"
+5.3 {a - b} "-"
+5.4 {a + b} "+"
+
+7 {a "in" b} "in"
+8 {a ? b : c} "iif"
+
+9 {a , b} "tuple"
+10 {$x = a} "assign"</textarea>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Statements (';' separated)</h2>
+		<div class="hider" onclick="showHide('input')"></div>
+		<textarea id="input" onkeyup="updateField(this)" rows="10" cols="120">
+foo + bar = 42;
+foo = 2 +;
+foo = 2 + +;
+foo = (1 + 2, 2*3 + 4, 3 + 4*5);
+1 ? 2 + 3 : 4</textarea>
+	</div>
+	
+	<hr />
+	
+	<div class="wrapper">
+		<h2>Debug</h2>
+		<div class="hider" onclick="showHide('output')"></div>
+		<pre id="output"></pre>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Parsed Operators</h2>
+		<div class="hider" onclick="showHide('output2')"></div>
+		<pre id="output2"></pre>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Syntax Tree</h2>
+		<div class="hider" onclick="showHide('output3')"></div>
+		<pre id="output3"></pre>
+	</div>
+	
+	<span id="footer">&copy; Jonas Höglund 2010</span>
+</body>
+</html>

File pages/parts.html~

+<!DOCTYPE html>
+<html>
+<head>
+	<title>Floop - firefly.nu</title>
+	<meta charset="UTF-8" />
+	<link rel="stylesheet" type="text/css" href="firelang.css" />
+	<script src="../src/lib/json.js"></script>
+	<script src="../src/floop.js"></script>
+	<script>
+	var indentation = 0;
+	function output(str) {
+		var space = "";
+		for (var i=0; i<indentation; i++) space += "   ";
+		document.getElementById('output').appendChild(document.createTextNode(
+				space + str + "\n"));
+	}
+	
+	var Data = {};
+	
+	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 {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);
+	}
+	
+	function updateOperators(el) {
+		var ops = el.value.split(/\n/g);
+		
+		Data.grouper = new Floop.Grouper(isValue, getValue);
+		var none = function() {};
+		
+		function isIdentifier(str) {
+			return str.match(/^[A-Za-z\d_$]\w*$/);
+		}
+		
+		var tkz = new Floop.Tokenizer();
+		tkz.add("identifier", beginsIdentifier, isIdentifier);
+		tkz.add("number", isNumber, isNumber);
+		tkz.add("string", function(c) {
+			return c == '"';
+		}, function(c, i, str) {
+			return i == 1 || (str.charAt(i-1) != '"' && str.charAt(i-2) != "\\");
+		});
+		tkz.add("whitespace", isWhitespace, isWhitespace, true);
+		tkz.add("operator", function() {return true;}, function() {return false;});
+		
+		for (var i=0; i<ops.length; i++) {
+			var tmp = ops[i].match(/^\s*([\d.]+)\s*{\s*([^}]*)\s*}\s*"([^"]*)"\s*$/);
+			if (!tmp) continue;
+			
+			var toks = tkz.tokenize(tmp[2]);
+			
+			toks = toks.map(function(a) {
+				if (isIdentifier(a)) {
+					var out = {};
+					
+					if (a.charAt(0) == "$") {
+						out[a.substr(1)] = "literal";
+					} else {
+						out[a] = "value";
+					}
+					
+					return out;
+				} else {
+					return a;
+				}
+			});
+			
+			(function() {
+				var name = tmp[3];
+				Data.grouper.add(toks, function() {return name;}, tmp[1]);
+			})();
+		}
+		
+		document.getElementById('output2').innerHTML = Data.grouper.operators.join("\n");
+		updateField(document.getElementById('input'));
+	}
+	
+	function updateField(el) {
+		var stms = el.value.split(/;/g);
+		document.getElementById('output').innerHTML = "";
+		
+		document.getElementById('output3').innerHTML = stms.map(function(a) {
+			var tokens = Floop.tokenize(a);
+			
+			try {
+				var gr = Data.grouper.group(tokens);
+				return "" + sExpr(gr) != "<BUG!>" ? sExpr(gr) : FF.Utils.toJson(gr);
+			} catch (err) {
+				return "Error: " + err.message;
+			}
+		}).join("\n");
+	}
+	
+	function showHide(id) {
+		var st = document.getElementById(id).style;
+		
+		if (st.display == 'none') st.display = 'block';
+		else st.display = 'none';
+	}
+	
+	function hide() {
+		for (var i=0; i<arguments.length; i++) {
+			document.getElementById(arguments[i]).style.display = 'none';
+		}
+	}
+	
+	function init() {
+		updateOperators(document.getElementById('operators'));
+		hide("operators", "output", "output2");
+	}
+	
+	window.onload = init;
+	</script>
+</head>
+<body>
+	<h1>FireLang: Floop</h1>
+	
+	<div class="wrapper">
+		<h2>Operators</h2>
+		<div class="hider" onclick="showHide('operators')"></div>
+		<textarea id="operators" onkeyup="updateOperators(this)" rows="10" cols="120">
+0 {( a )} "id"
+
+5.1 {a / b} "/"
+5.2 {a * b} "*"
+5.3 {a - b} "-"
+5.4 {a + b} "+"
+
+7 {a "in" b} "in"
+8 {a ? b : c} "iif"
+
+9 {a , b} "tuple"
+10 {$x = a} "assign"</textarea>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Statements (';' separated)</h2>
+		<div class="hider" onclick="showHide('input')"></div>
+		<textarea id="input" onkeyup="updateField(this)" rows="10" cols="120">
+foo + bar = 42;
+foo = 2 +;
+foo = 2 + +;
+foo = (1 + 2, 2*3 + 4, 3 + 4*5);
+1 ? 2 + 3 : 4</textarea>
+	</div>
+	
+	<hr />
+	
+	<div class="wrapper">
+		<h2>Debug</h2>
+		<div class="hider" onclick="showHide('output')"></div>
+		<pre id="output"></pre>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Parsed Operators</h2>
+		<div class="hider" onclick="showHide('output2')"></div>
+		<pre id="output2"></pre>
+	</div>
+	
+	<div class="wrapper">
+		<h2>Syntax Tree</h2>
+		<div class="hider" onclick="showHide('output3')"></div>
+		<pre id="output3"></pre>
+	</div>
+	
+	<span id="footer">&copy; Jonas Höglund 2010</span>
+</body>
+</html>

File 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>

File 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() {