Initial commit default tip

Fri, 15 Oct 2010 15:17:17 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Fri, 15 Oct 2010 15:17:17 +0100
changeset 0
b2e55f320d48

Initial commit

js2lua.lua file | annotate | diff | comparison | revisions
jslextest.lua file | annotate | diff | comparison | revisions
lib/js2lua.lua file | annotate | diff | comparison | revisions
lib/jslex.lua file | annotate | diff | comparison | revisions
test.js file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/js2lua.lua	Fri Oct 15 15:17:17 2010 +0100
@@ -0,0 +1,25 @@
+#!/usr/bin/env lua
+
+local jslex = require "lib.jslex"
+local js2lua = require "lib.js2lua"
+
+local stream = jslex.new_stream(io.open(arg[1]));
+
+local list = {};
+
+local i, token_type, token_value = 0, stream.get_token();
+while token_type do
+	i = i + 1;
+	list[i] = { type = token_type, value = token_value };
+	
+--	print("Line "..(stream.line or 1)..":", token_type, token_value);
+	token_type, token_value = stream.get_token();
+end
+
+io.stderr:write("js2lua: Translating...\n");
+local d = {};
+local function w(t) table.insert(d, t); end
+js2lua(list, w);
+io.stderr:write("===== Result ======\n", table.concat(d), "\n===== ====== =====\n");
+io.stderr:write("js2lua: Running...\n");
+assert(loadstring(table.concat(d)))();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jslextest.lua	Fri Oct 15 15:17:17 2010 +0100
@@ -0,0 +1,9 @@
+require "jslex"
+
+local stream = jslex.new_stream(io.open(arg[1]));
+
+local token_type, token_value = stream.get_token();
+while token_type do
+	print("Token:", token_type, token_value);
+	token_type, token_value = stream.get_token();
+end
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/js2lua.lua	Fri Oct 15 15:17:17 2010 +0100
@@ -0,0 +1,113 @@
+local t = { op = {}, keyword = {}, name = {}, eos = {}, string = {}, regex = {} };
+
+local debug = function (...) io.stderr:write(table.concat({...}, "\t")); io.stderr:write"\n";end;
+local debug = function () end
+
+local function read_balanced(tokens, start, left, right)
+	local c = 0;
+	for i=start,#tokens do
+		if tokens[i].type == "op" then
+			local token = tokens[i].value;
+			if token == left then
+				c = c + 1;
+			elseif token == right then
+				c = c - 1;
+			end
+		end
+		if c == 0 then return i; end
+	end
+	return start;
+end
+
+local pair_op = { ["("] = ")", ["{"] = "}", ["["] = "]" };
+local function read_balanced_to(tokens, start, to)
+	local c = 0;
+	local i = start;
+	while i <= #tokens do
+		if tokens[i].type == "op" or tokens[i].type == "eos" then
+			local token = tokens[i].value;
+			if token == to then return i; end
+			
+			if pair_op[token] then
+				i = read_balanced(tokens, i, token, pair_op[token]);
+			end
+		end
+		i = i + 1;
+	end
+	return nil;
+end
+
+function js2lua(tokens, write)
+
+-- Scan
+	local i = 1;
+	while i <= #tokens do
+		local token = tokens[i];
+		debug(token.type, token.value);
+		if token.type == "keyword" and token.value == "function" then
+			local j = read_balanced_to(tokens, i+3, ")"); -- Find matching )
+			j = j + 1; -- j now points to {
+			local k = read_balanced_to(tokens, j+1, "}");
+			table.remove(tokens, j); -- Remove {
+			tokens[k-1].type, tokens[k-1].value = "keyword", "end";
+		elseif token.type == "keyword" and token.value == "if" then
+			table.remove(tokens, i+1); -- Remove (
+			local j = read_balanced_to(tokens, i, ")");
+			tokens[j].type, tokens[j].value = "keyword", "then";
+
+			-- Make sure to end a single-statement block
+			if tokens[j+1].type ~= "op" or tokens[j+1].value ~= "{" then
+				local eos = read_balanced_to(tokens, j+1, ";");
+				if tokens[eos+1].type ~= "keyword" or tokens[eos+1].value ~= "else" then
+					table.insert(tokens, eos+1, { type = "keyword", value = "end" });
+				end
+			end
+		elseif token.type == "keyword" and token.value == "else" then
+			if tokens[i+1].type == "keyword" and tokens[i+1].value == "if" then
+				token.value = "elseif";
+				table.remove(tokens, i+1);
+
+				table.remove(tokens, i+1); -- Remove (
+				local j = read_balanced_to(tokens, i, ")");
+				tokens[j].type, tokens[j].value = "keyword", "then";
+			end
+
+			-- Make sure to end a single-statement block
+			if tokens[i+1].type ~= "op" or tokens[i+1].value ~= "{" then
+				local eos = read_balanced_to(tokens, i+1, ";");
+				if tokens[eos+1].type ~= "keyword" or tokens[eos+1].value ~= "else" then
+					table.insert(tokens, eos+1, { type = "keyword", value = "end" });
+				end
+			end			
+		elseif token.type == "op" and token.value == "+" and tokens[i-1].type == "string" then
+			token.value = "..";
+		elseif token.type == "op" and token.value == "{" then
+		elseif token.type == "keyword" and token.value == "var" then
+			token.value = "local";
+		end
+		i = i + 1;
+	end
+
+-- Serialize
+	local last_token_type;
+	for _, token in ipairs(tokens) do
+		if token.type == "string" then
+			write("\"");
+		elseif token.type == "name" then
+			if last_token_type == "keyword" or last_token_type == "name" or last_token_type == "number" then
+				write(" ");
+			end
+		elseif token.type == "keyword" then
+			if last_token_type == "keyword" or last_token_type == "name" or last_token_type == "number" then
+				write(" ");
+			end
+		end
+		write(token.value);
+		if token.type == "string" then
+			write("\"");
+		end
+		last_token_type = token.type;
+	end
+end
+
+return js2lua;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/jslex.lua	Fri Oct 15 15:17:17 2010 +0100
@@ -0,0 +1,175 @@
+module("jslex", package.seeall);
+
+function peek_char(stream)
+	return stream.next_char;
+end
+
+function read_char(stream, param)
+	local c = stream.next_char;
+	stream.next_char = stream.file:read(1);
+	if c == "\n" then stream.line = (stream.line or 1) + 1; end
+	return c;
+end
+
+function read_until(stream, char)
+	local r = {};
+	while stream.next_char and stream.next_char ~= char do
+		r[#r+1] = read_char(stream);
+	end
+	return table.concat(r);
+end
+
+function read_until_match(stream, pattern)
+	local r = {};
+	while stream.next_char and not stream.next_char:match(pattern) do
+		r[#r+1] = read_char(stream);
+	end
+	return table.concat(r);
+end
+
+local keywords = {
+	"abstract", "as", "break", "case", "catch",
+	"class", "const", "continue", "debugger",
+	"default", "delete", "do", "else", "enum",
+	"export", "extends", "false", "final",
+	"finally", "for", "function", "goto", "if",
+	"implements", "import", "in", "instanceof",
+	"interface", "is", "namespace", "native",
+	"new", "null", "package", "private",
+	"protected", "public", "return", "static",
+	"super", "switch", "synchronized", "this",
+	"throw", "throws", "transient", "true",
+	"try", "typeof", "use", "var", "volatile",
+	"while", "with"
+};
+
+for _, k in ipairs(keywords) do keywords[k] = true; end
+local function is_keyword(name)
+	return keywords[name];
+end
+
+local operators = "+-/*(),={}.&|<>![]:?";
+
+local function push_token(stream, token_type, token_value)
+	stream.last_token_type = token_type;
+	coroutine.yield(token_type, token_value);
+end
+
+local handlers = {
+	-- Whitespace
+	function (stream)
+		local c = peek_char(stream);
+		if c:match("%s") then
+			read_until_match(stream, "%S");
+			return true;
+		end
+	end;
+	-- Strings
+	function (stream)
+		local c = peek_char(stream);
+		if c == [["]] or c == [[']] then
+			read_char(stream); -- Use up the string marker
+			push_token(stream, "string", read_until(stream, c));
+			read_char(stream); -- Use up the string terminator
+			return true;
+		end
+	end;
+	-- Identifiers
+	function (stream)
+		local c = peek_char(stream);
+		if c:match("[_a-zA-Z$]") then
+			local name = read_until_match(stream, "[^a-zA-Z0-9_$]");
+			if is_keyword(name) then
+				push_token(stream, "keyword", name);
+			else
+				push_token(stream, "name", name);
+			end
+			return true;
+		end
+	end;
+	--Numbers
+	function (stream)
+		local c = peek_char(stream);
+		if c:match("%d") then
+			push_token(stream, "number", read_until_match(stream, "[^%d%.]"));
+			return true;
+		end
+	end;
+	-- Operators (and comments!)
+	function (stream)
+		local c = peek_char(stream);
+		if operators:match("%"..c) then
+			local op, c = read_char(stream), peek_char(stream);
+			if op == "/" and (c == "/" or c == "*") then -- A comment
+				if c == "/" then -- Comment until end of line
+					read_until(stream, "\n");
+					return true;
+				else
+					while true do
+						read_until(stream, "*");
+						read_char(stream); -- Read "*"
+						if peek_char(stream) == "/" then
+							read_char(stream); -- Read /
+							return true;
+						elseif peek_char(stream) == nil then
+							return true;
+						end
+					end
+				end
+			elseif op == "/" and stream.last_token_type == "op" then -- Regex
+				local regex = read_until(stream, "/");
+				read_char(stream);
+				local flags = read_until_match(stream, "%A");
+				push_token(stream, "regex", op..regex.."/"..flags);
+				read_char(stream);
+			elseif op == "=" and (c == "=") then -- Equality
+				op = "==";
+				read_char(stream);
+				if peek_char(stream) == "=" then
+					op = "===";
+					read_char(stream);
+				end
+			elseif op:match("[&|]") and c == op then
+				op = op:rep(2);
+				read_char(stream);
+			elseif op == "!" and (c == "=") then
+				op = "!=";
+				read_char(stream);
+			end
+			push_token(stream, "op", op);
+			return true;
+		end
+	end;
+	-- Semi-colons
+	function (stream)
+		local c = peek_char(stream);
+		if c == ";" then
+			read_char(stream);
+			push_token(stream, "eos", c);
+			return true;
+		end
+	end;
+};
+
+
+function new_stream(file)
+	local stream = { file = file; };
+	stream.next_char = stream.file:read(1);
+
+	stream.get_token = coroutine.wrap(
+	function ()
+		while stream.next_char do
+			local handled;
+			for _, handler in ipairs(handlers) do
+				handled = handler(stream) or handled;
+				if not stream.next_char then break; end
+			end
+			if not handled then error("Unexpected character on line "..(stream.line or 1)..": "..stream.next_char); end
+		end
+	end);
+
+
+	return stream;
+end
+
+return _M;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test.js	Fri Oct 15 15:17:17 2010 +0100
@@ -0,0 +1,15 @@
+
+function hello(i)
+{
+	var foo = i;
+	if(foo == 1)
+		return "Hello world! " + foo;
+	else if(foo == 2)
+		return "Haha!";
+	else
+		return "Goodbye!";
+}
+
+print(hello(1));
+print(hello(2));
+print(hello(3));

mercurial