Skip to content

Commit 368f2a0

Browse files
committed
Initial support for lua runtime
1 parent 2760338 commit 368f2a0

5 files changed

+246
-0
lines changed

bin/write-lua-bytecode.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env node
2+
// Node CLI to write bytecode for TurtleScript parser, bytecode compiler,
3+
// startup code and standard library, as a Lua file.
4+
5+
// this is just the thunk to run it under node.
6+
var turtlescript = require('../');
7+
turtlescript.write_lua_bytecode();

bin/write-lua-ops.js

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/env node
2+
// Node CLI to write out the opcode mapping from bytecode-table.js
3+
// as a Lua file.
4+
5+
// this is just the thunk to run it under node.
6+
var turtlescript = require('../');
7+
turtlescript.write_lua_ops();

nodemain.js

+8
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,11 @@ module.exports.write_php_ops = function() {
5252
// This module has side-effects when imported.
5353
requirejs('./write-php-ops');
5454
};
55+
module.exports.write_lua_bytecode = function() {
56+
// This module has side-effects when imported.
57+
requirejs('./write-lua-bytecode');
58+
};
59+
module.exports.write_lua_ops = function() {
60+
// This module has side-effects when imported.
61+
requirejs('./write-lua-ops');
62+
};

write-lua-bytecode.js

+182
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
// Utility to write bytecode for TurtleScript parser, bytecode compiler,
2+
// startup code and standard library, as a Lua file.
3+
//
4+
// Run it under `node` with the CLI in `bin/write-lua-bytecode.js`
5+
define(['./parse', './bcompile', './bytecode-table', './top-level', './str-escape', './tests', './stdlib', './extensions'], function(parse, bcompile, bytecode_table, top_level, str_escape, tests, stdlib) {
6+
var fake_require =
7+
"var __modules__ = {};\n"+
8+
"define = function _define(name, deps, init_func) {\n"+
9+
" var d = deps.map(function(m) { return __modules__[m]; });\n"+
10+
" __modules__[name] = init_func.apply(this, d);\n"+
11+
"};\n";
12+
var make_compile_from_source = function(parse, bcompile, TOP_LEVEL) {
13+
var compile_from_source = function (source, as_object) {
14+
source = source || '{ return 1+2; }';
15+
var tree = parse(source, TOP_LEVEL);
16+
var bc = bcompile(tree);
17+
var result = as_object ? bc : bc.encode();
18+
return result;
19+
};
20+
compile_from_source.make_repl = function() {
21+
var state = null;
22+
return function(source) {
23+
var rv = parse.repl(state, source, TOP_LEVEL);
24+
state = rv.state;
25+
return bcompile(rv.tree).encode();
26+
};
27+
};
28+
return compile_from_source;
29+
};
30+
var cfs_source = make_compile_from_source.toSource ?
31+
make_compile_from_source.toSource() :
32+
make_compile_from_source.toString();
33+
cfs_source = 'define("compile_from_source", ["parse","bcompile","top-level"], '+
34+
cfs_source + ');';
35+
var top_level_source = 'define("top-level", [], function() { return ' +
36+
str_escape(top_level) + '; });';
37+
var source = '{\n'+
38+
stdlib.source()+'\n'+
39+
fake_require +
40+
tests.lookup("tokenize")+"\n"+
41+
tests.lookup("parse")+"\n"+
42+
tests.lookup("bytecode-table")+"\n"+
43+
tests.lookup("bcompile")+"\n"+
44+
top_level_source+"\n"+
45+
cfs_source + '\n' +
46+
/*
47+
"var test_nan = function() { return NaN; };\n" +
48+
"var test_inf = function() { return Infinity; };\n" +
49+
"var test_neg_inf = function() { return -Infinity; };\n" +
50+
*/
51+
"return __modules__['compile_from_source']; }\n";
52+
53+
/* XXX Hacks for initial tests:
54+
source = "{ console.log('Hello,', 'world!'); }";
55+
source = "{ var fib=function(n){return (n<2)?1:fib(n-1)+fib(n-2);}; return fib(10); }";
56+
source = '{ return 1+2; }';
57+
source = "{ return 0+'x'; }";
58+
*/
59+
60+
var compile_from_source = make_compile_from_source(parse, bcompile, top_level);
61+
var bc = compile_from_source(source, true/*as object*/);
62+
63+
var lua_esc = function(str) {
64+
// Escape string for PHP -- note UTF-16 to UTF-8 conversion.
65+
// XXX this isn't turtlescript...
66+
var re = /[\uD800-\uDBFF][\uDC00-\uDFFF]|[^A-Za-z0-9_ !#-\/:-@\[\]^`{|}~]/g;
67+
return '"' + str.replace(re, function(c) {
68+
var code = c.charCodeAt(0);
69+
if (c.length===2) {
70+
var next = c.charCodeAt(1);
71+
code = ((code - 0xD800) * 0x400) +
72+
(next - 0xDC00) + 0x10000;
73+
}
74+
// Convert code to UTF-8
75+
var esc = function(d) {
76+
var s = d.toString(10);
77+
while (s.length < 3) { s = '0' + s; }
78+
return "\\" + s;
79+
};
80+
if (code < 0x80) {
81+
return esc(code);
82+
}
83+
var prefix, s='', repeat;
84+
if (code < 0x800) {
85+
prefix = esc(0xC0 | (code >>> 6));
86+
repeat = 1;
87+
} else if (code < 0x10000) {
88+
prefix = esc(0xE0 | (code >>> 12));
89+
repeat = 2;
90+
} else {
91+
prefix = esc(0xF0 | (code >>> 18));
92+
repeat = 3;
93+
}
94+
while (repeat > 0) {
95+
s = esc(0x80 | (code & 0x3F)) + s;
96+
code = code >>> 6;
97+
repeat--;
98+
}
99+
return prefix + s;
100+
}) + '"';
101+
};
102+
103+
var pad = function(s, width) {
104+
if (width===undefined) { width = 10; }
105+
while (s.length < width) { s += ' '; }
106+
return s;
107+
};
108+
109+
// ## Output module functions.
110+
console.log('-- generated by TurtleScript write-lua-bytecode.js');
111+
console.log('local jsval = require("luaturtle.jsval")');
112+
console.log('local ifunc = require("luaturtle.ifunc")');
113+
console.log('');
114+
console.log('local startup = {}');
115+
console.log('');
116+
console.log('-- Populate the function and literal arrays with the precompiled');
117+
console.log('-- startup code, including the compiler and standard library.');
118+
console.log('');
119+
console.log('startup.functions = {');
120+
var mkComma = function(len) {
121+
len--;
122+
return function(i) { return (i < len) ? ',' : ''; };
123+
};
124+
var comma = mkComma(bc.functions.length);
125+
bc.functions.forEach(function(f, i) {
126+
var name = f.name ? (' -- '+JSON.stringify(f.name)) : '';
127+
console.log(' ifunc.Function:new{' + name);
128+
name = f.name ? lua_esc(f.name) : 'nil';
129+
console.log(' name = ' + name + ',');
130+
console.log(' id = ' + f.id + ',');
131+
console.log(' nargs = ' + f.nargs + ',');
132+
console.log(' max_stack = ' + f.max_stack + ',');
133+
console.log(' bytecode = {');
134+
var j = 0;
135+
while (j < f.bytecode.length) {
136+
var pc = j;
137+
var bc = bytecode_table.for_num(f.bytecode[j]);
138+
var a = f.bytecode.slice(j, j+=bc.args+1);
139+
a = a.map(function(b) {
140+
if (typeof(b) !== 'number') { b = b.label; }
141+
return ''+b;
142+
});
143+
var b = a.slice(1);
144+
a = a.join(', ') + ((j < f.bytecode.length) ? ',' : '');
145+
b = bc.name + ( b.length ? ( '(' + b.join(',') + ')' ) : '' );
146+
console.log(' ' + pad(a) + '-- ' + pc + ': ' + b);
147+
}
148+
console.log(' }');
149+
console.log(' }' + comma(i));
150+
});
151+
console.log('}');
152+
153+
// ## Output module literals.
154+
console.log('-- literals');
155+
console.log('startup.literals = {');
156+
comma = mkComma(bc.literals.length);
157+
bc.literals.forEach(function(lv, i) {
158+
var str;
159+
if (typeof(lv) === "number" ) {
160+
str = lv.toString();
161+
if (isNaN(lv)) { str = '0/0'; }
162+
else if (!isFinite(lv)) { str = lv > 0 ? '1/0' : '-1/0'; }
163+
str = 'jsval.newNumber(' + str + ')';
164+
} else if (typeof(lv) === "string") {
165+
str = lua_esc(lv); // Note: UTF8!
166+
str = 'jsval.newString(' + str + ')'; // convert to UTF16
167+
} else if (typeof(lv) === "boolean") {
168+
str = 'jsval.' + (lv ? "True" : "False");
169+
} else if (lv === null) {
170+
str = "jsval.Null";
171+
} else if (lv === undefined) {
172+
str = "jsval.Undefined";
173+
} else {
174+
console.assert(false);
175+
}
176+
console.log(' ' + pad(str + comma(i)) + ' -- ' + i);
177+
});
178+
console.log('}');
179+
console.log('');
180+
181+
console.log('return startup');
182+
});

write-lua-ops.js

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Utility to write out the opcode mapping from `bytecode-table.js`
2+
// as a Lua file
3+
//
4+
// Run it under `node` with the CLI in `bin/write-lua-ops.js`
5+
define(['./bytecode-table'], function(bytecode_table) {
6+
var bops = [];
7+
while(true) {
8+
var bc = bytecode_table.for_num(bops.length);
9+
if (!bc) { break; }
10+
bops.push(bc);
11+
}
12+
var comma = function(i) { return (i < (bops.length-1)) ? ',' : ''; };
13+
var luaName = function(bc) {
14+
var name = bc.name.toUpperCase();
15+
if (name==='2DUP') { return 'DUP2'; }
16+
return name;
17+
};
18+
19+
console.log('-- generated by TurtleScript write-lua-ops.js');
20+
console.log('');
21+
console.log('local ops = {}');
22+
console.log('');
23+
24+
// ## Emit `Op` enumeration.
25+
console.log('ops.byname = {');
26+
bops.forEach(function(bc, i) {
27+
console.log(' ["' + luaName(bc) + '"] = ' + i + comma(i));
28+
});
29+
console.log('}');
30+
console.log('');
31+
32+
console.log('ops.bynum = {}');
33+
console.log('');
34+
console.log('-- invert byname into bynum, and combine both into root export');
35+
console.log('for name,val in pairs(ops.byname) do');
36+
console.log(' ops.bynum[val] = name');
37+
console.log(' ops[name] = val');
38+
console.log('end');
39+
console.log('');
40+
41+
console.log('return ops');
42+
});

0 commit comments

Comments
 (0)