first commit

pull/6/head
Teoxoy 8 years ago
commit d5ccdbea3b

1
.gitignore vendored

@ -0,0 +1 @@
node_modules

@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright 2015—2016 Tanasoaia Teodor Andrei
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,5 @@
# lua2js
A Lua to JS transpiler
This library is based on the sourcecode of this library: https://github.com/paulcuth/starlight

1
dist/lua2js.js vendored

File diff suppressed because one or more lines are too long

8966
package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -0,0 +1,24 @@
{
"name": "lua2js",
"version": "0.1.0",
"description": "A Lua to JS transpiler",
"main": "dist/lua2js.js",
"module": "src/index.js",
"author": "Teoxoy",
"license": "MIT",
"scripts": {
"build": "webpack"
},
"dependencies": {
"printf": "^0.2.5",
"luaparse": "^0.2.1"
},
"devDependencies": {
"@babel/core": "^7.0.0-beta.42",
"@babel/preset-env": "^7.0.0-beta.42",
"babel-loader": "^8.0.0-beta.2",
"clean-webpack-plugin": "^0.1.19",
"webpack": "^4.4.1",
"webpack-cli": "^2.0.13"
}
}

@ -0,0 +1,7 @@
import * as parser from './parser'
import * as runtime from './runtime'
export {
parser,
runtime
}

@ -0,0 +1,508 @@
import luaparse from 'luaparse'
class MemExpr extends String {
constructor(base, property) {
super();
this.base = base;
this.property = property;
}
get() {
return `Tget(${this.base}, ${this.property})`;
}
set(value) {
return `Tset(${this.base}, ${this.property}, ${value})`;
}
toString() {
return this.get();
}
valueOf() {
return this.get();
}
}
let scopeIndex = 1;
let functionIndex = 0;
let forLoopIndex = 0;
const UNI_OP_MAP = {
'-': 'unm',
'not': 'not',
'#': 'len'
};
const BIN_OP_MAP = {
'..': 'concat',
'+': 'add',
'-': 'sub',
'*': 'mul',
'/': 'div',
'%': 'mod',
'==': 'eq',
'~=': 'neq',
'<': 'lt',
'>': 'gt',
'<=': 'lte',
'>=': 'gte',
'^': 'pow'
};
const GENERATORS = {
AssignmentStatement(node, scope) {
let assignments = node.variables.map((variable, index) => {
const name = scoped(variable, scope);
if (name instanceof MemExpr) {
return name.set(`__star_tmp[${index}]`);
} else {
const [match, args] = [].concat(name.match(/^\$get\((.*)\)$/));
if (!match) {
throw new Error('Unhandled');
}
return `$set(${args}, __star_tmp[${index}])`;
}
}).join(';\n');
const values = parseExpressionList(node.init, scope).join(', ');
return `__star_tmp = [${values}];${assignments}`;
},
BinaryExpression(node, scope) {
let left = scoped(node.left, scope);
let right = scoped(node.right, scope);
let operator = BIN_OP_MAP[node.operator];
if (isCallExpression(node.left)) {
left += '[0]';
}
if (isCallExpression(node.right)) {
right += '[0]';
}
if (!operator) {
console.info(node);
throw new Error(`Unhandled binary operator: ${node.operator}`);
}
return `__star_op_${operator}(${left}, ${right})`;
},
BooleanLiteral(node) {
return node.value ? 'true' : 'false';
},
BreakStatement(node) {
return 'break';
},
CallStatement(node, scope) {
return generate(node.expression, scope);
},
CallExpression(node, scope) {
let functionName = scoped(node.base, scope);
const args = parseExpressionList(node.arguments, scope);
if (isCallExpression(node.base)) {
args.unshift(`${functionName}[0]`);
} else {
if (functionName instanceof MemExpr && node.base.indexer === ':') {
args.unshift(functionName.base);
}
args.unshift(`${functionName}`);
}
return `__star_call(${args})`;
},
Chunk(node, scope) {
let output = node.body.map(statement => generate(statement, scope) + ';');
return output.join('\n');
},
DoStatement(node, outerScope) {
let { scope, scopeDef } = extendScope(outerScope);
let body = this.Chunk(node, scope);
scopeDef = scopeDef.replace(',', ';');
return `${scopeDef}\n${body}\n$=$${outerScope};`;
},
ElseClause(node, scope) {
let body = this.Chunk(node, scope);
return `{\n${body}\n}`;
},
ElseifClause(node, scope) {
return this.IfClause(node, scope);
},
ForNumericStatement(node, outerScope) {
let { scope, scopeDef } = extendScope(outerScope);
let variableName = generate(node.variable, outerScope);
let start = scoped(node.start, outerScope);
let end = scoped(node.end, outerScope);
let step = node.step === null ? 1 : generate(node.step, outerScope);
let operator = step > 0 ? '<=' : '>=';
let body = this.Chunk(node, scope);
let loopIndex = ++forLoopIndex;
let init = `$${outerScope}._forLoop${loopIndex} = ${start}`;
let cond = `$${outerScope}._forLoop${loopIndex} ${operator} ${end}`;
let after = `$${outerScope}._forLoop${loopIndex} += ${step}`;
let varInit = `$${scope}.setLocal('${variableName}',$${outerScope}._forLoop${loopIndex});`;
return `for (${init}; ${cond}; ${after}) {\n${scopeDef}\n${varInit}\n${body}\n}`;
},
ForGenericStatement(node, outerScope) {
const { scope, scopeDef } = extendScope(outerScope);
const { scope: iterationScope, scopeDef: iterationScopeDef } = extendScope(scope);
const iterators = parseExpressionList(node.iterators, outerScope).join(', ');
const body = this.Chunk(node, iterationScope);
const variables = node.variables.map((variable, index) => {
const name = generate(variable, scope);
return `$setLocal($, '${name}', __star_tmp[${index}])`;
}).join(';\n');
const defs = scopeDef.split(', ');
return `${defs[0]};\n[$${scope}._iterator, $${scope}._table, $${scope}._next] = [${iterators}];\nwhile((__star_tmp = __star_call($${scope}._iterator, $${scope}._table, $${scope}._next)),__star_tmp[0] !== undefined) {\n${iterationScopeDef}\$${scope}._next = __star_tmp[0]\n${variables}\n${body}\n}`;
},
FunctionDeclaration(node, outerScope) {
let { scope, scopeDef } = extendScope(outerScope);
let isAnonymous = !node.identifier;
let identifier = isAnonymous ? '' : generate(node.identifier, outerScope);
let isMemberExpr = identifier instanceof MemExpr;
let params = node.parameters.map((param, index) => {
let name = generate(param, scope);
if (name === '...$.getVarargs()') {
return `$.setVarargs(args)`;
} else {
return `$setLocal($, '${name}', __star_shift(args))`;
}
});
let name;
if (isMemberExpr) {
name = identifier.property.replace(/'/g, '');
if (node.identifier.indexer === ':') {
params.unshift("$setLocal($, 'self', __star_shift(args))");
}
} else {
name = identifier;
}
let paramStr = params.join(';\n');
let body = this.Chunk(node, scope);
let prefix = isAnonymous? '' : 'func$';
let funcDef = `(__star_tmp = function ${prefix}${name}(...args){${scopeDef}\n${paramStr};\n${body} return [];}, __star_tmp.toString=()=>'function: 0x${(++functionIndex).toString(16)}', __star_tmp)`;
if (isAnonymous) {
return funcDef;
} else if (isMemberExpr) {
return identifier.set(funcDef);
} else {
const local = node.isLocal ? 'Local' : '';
return `$set${local}($, '${identifier}', ${funcDef})`;
}
},
Identifier(node, scope) {
return node.name;
},
IfClause(node, scope) {
let condition = scoped(node.condition, scope);
if (isCallExpression(node.condition)) {
condition += '[0]';
}
let body = this.Chunk(node, scope);
return `if (__star_op_bool(${condition})) {\n${body}\n}`;
},
IfStatement(node, scope) {
let clauses = node.clauses.map((clause) => generate(clause, scope));
return clauses.join (' else ');
},
IndexExpression(node, scope) {
let base = scoped(node.base, scope);
let index = scoped(node.index, scope);
if (isCallExpression(node.base)) {
base += '[0]';
}
if (isCallExpression(node.index)) {
index += '[0]';
}
return new MemExpr(base, index);
},
LocalStatement(node, scope) {
let assignments = node.variables.map((variable, index) => {
let name = generate(variable, scope);
return `$setLocal($, '${name}', __star_tmp[${index}])`;
}).join(';\n');
const values = parseExpressionList(node.init, scope).join(', ');
return `__star_tmp = [${values}];${assignments}`;
},
LogicalExpression(node, scope) {
let left = scoped(node.left, scope);
let right = scoped(node.right, scope);
let operator = node.operator;
if (isCallExpression(node.left)) {
left += '[0]';
}
if (isCallExpression(node.right)) {
right += '[0]';
}
if (operator === 'and') {
return `(!__star.op.bool(${left})?${left}:${right})`
} else if (operator === 'or') {
return `(__star.op.bool(${left})?${left}:${right})`
} else {
console.info(node);
throw new Error(`Unhandled logical operator: ${node.operator}`);
}
},
MemberExpression(node, scope) {
let base = scoped(node.base, scope);
let identifier = generate(node.identifier, scope);
if (isCallExpression(node.base)) {
base += '[0]';
}
return new MemExpr(base, `'${identifier}'`);
},
NilLiteral(node) {
return 'undefined';
},
NumericLiteral(node) {
return node.value.toString();
},
RepeatStatement(node, outerScope) {
let { scope, scopeDef } = extendScope(outerScope);
let condition = scoped(node.condition, outerScope);
let body = this.Chunk(node, scope);
return `do{\n${scopeDef}\n${body}\n}while(!(${condition}))`;
},
ReturnStatement(node, scope) {
const args = parseExpressionList(node.arguments, scope).join(', ');
return `return [${args}];`;
},
StringCallExpression(node, scope) {
node.arguments = node.argument;
return this.TableCallExpression(node, scope);
},
StringLiteral(node) {
let raw = node.raw;
if (/^\[\[[^]*]$/m.test(raw)) {
return `\`${raw.substr(2, raw.length - 4).replace(/\\/g, '\\\\')}\``;
} else {
raw = raw.replace(/([^\\])\\(\d{1,3})/g, (_, pre, dec) => `${pre}\\u${('000' + parseInt(dec, 10).toString(16)).substr(-4)}`);
return raw;
}
},
TableCallExpression(node, scope) {
let functionName = scoped(node.base, scope);
let args = [generate(node.arguments, scope)];
if (isCallExpression(node.base)) {
return `__star_call(${functionName}[0],${args})`;
} else {
if (functionName instanceof MemExpr && node.base.indexer === ':') {
args.unshift(functionName.base);
}
return `__star_call(${functionName},${args})`;
}
},
TableConstructorExpression(node, scope) {
let fields = node.fields.map((field, index, arr) => {
if (field.type == 'TableValue') {
const isLastItem = index === arr.length - 1;
return this.TableValue(field, scope, isLastItem);
}
return generate(field, scope);
}).join(';\n');
return `new __star_T(t => {${fields}})`;
},
TableKeyString(node, scope) {
let name = generate(node.key, scope);
let value = scoped(node.value, scope);
return `Tset(t, '${name}', ${value})`;
},
TableKey(node, scope) {
let name = scoped(node.key, scope);
let value = scoped(node.value, scope);
return `Tset(t, ${name}, ${value})`;
},
TableValue(node, scope, isLastItem) {
let value = scoped(node.value, scope);
if (isCallExpression(node.value)) {
value = isLastItem ? `...${value}` : `${value}[0]`;
}
return `Tins(t, ${value})`;
},
UnaryExpression(node, scope) {
let operator = UNI_OP_MAP[node.operator];
let argument = scoped(node.argument, scope);
if (isCallExpression(node.argument)) {
argument += '[0]';
}
if (!operator) {
console.info(node);
throw new Error(`Unhandled unary operator: ${node.operator}`);
}
return `__star_op_${operator}(${argument})`;
},
VarargLiteral(node) {
return '...$.getVarargs()';
},
WhileStatement(node, outerScope) {
let { scope, scopeDef } = extendScope(outerScope);
let condition = scoped(node.condition, outerScope);
let body = this.Chunk(node, scope);
return `while(${condition}) {\n${scopeDef}\n${body}\n}`;
},
};
function parseExpressionList(expressionNodeArray, scope) {
return expressionNodeArray.map((node, index, arr) => {
let value = scoped(node, scope);
if (isCallExpression(node)) {
if (index == arr.length - 1) {
return `...${value}`;
}
return `${value}[0]`;
}
return value;
});
}
function extendScope(outerIndex) {
let scope = scopeIndex++;
let scopeDef = `let $${scope} = $${outerIndex}.extend(), $ = $${scope};`;
return { scope, scopeDef };
}
function scoped(node, scope) {
let value = generate(node, scope);
return node.type === 'Identifier' ? `$get($, '${value}')` : value;
}
function isCallExpression(node) {
return !!node.type.match(/CallExpression$/);
}
function generate(ast, scope) {
let generator = GENERATORS[ast.type];
if (!generator) {
console.info(ast);
throw new Error(`No generator found for: ${ast.type}`);
}
return generator.call(GENERATORS, ast, scope);
}
export function parse(data) {
let ast = luaparse.parse(data);
let js = generate(ast, 0);
return `
"use strict"; if (typeof global === \'undefined\' && typeof window !== \'undefined\') { window.global = window; }
let __star = global.starlight.runtime, $0 = __star.globalScope, $ = $0, __star_tmp;
let __star_call = __star.call, __star_T = __star.T, __star_op_bool = __star.op.bool;
let __star_op_unm = __star.op.unm, __star_op_not = __star.op.not, __star_op_len = __star.op.len, __star_op_concat = __star.op.concat, __star_op_add = __star.op.add, __star_op_sub = __star.op.sub, __star_op_mul = __star.op.mul, __star_op_div = __star.op.div, __star_op_mod = __star.op.mod, __star_op_eq = __star.op.eq, __star_op_neq = __star.op.neq, __star_op_lt = __star.op.lt, __star_op_gt = __star.op.gt, __star_op_lte = __star.op.lte, __star_op_gte = __star.op.gte, __star_op_pow = __star.op.pow;
let __star_op_and = __star.op.and, __star_op_or = __star.op.or;
let Tget, Tset, Tins, $get, $set, $setLocal, __star_shift;
(()=>{
let call = Function.prototype.call, bind = call.bind.bind(call), Tproto = __star_T.prototype, $proto = __star.globalScope.constructor.prototype;
Tget = bind(Tproto.get), Tset = bind(Tproto.set), Tins = bind(Tproto.insert);
$get = bind($proto.get), $set = bind($proto.set), $setLocal = bind($proto.setLocal);
__star_shift = bind(Array.prototype.shift);
})();
${js}
`
}

@ -0,0 +1,10 @@
export default class LuaError extends Error {
constructor(message) {
super();
this.message = message;
}
toString() {
return `LuaError: ${this.message}`;
}
}

@ -0,0 +1,44 @@
const hasOwnProperty = Object.prototype.hasOwnProperty;
export default class Scope {
constructor(variables = {}) {
this._variables = variables;
}
get(key) {
return this._variables[key];
}
set(key, value) {
let vars = this._variables;
if (hasOwnProperty.call(this._variables, key) || !this.parent) {
vars[key] = value;
} else {
this.parent.set(key, value);
}
}
setLocal(key, value) {
this._variables[key] = value;
}
setVarargs(value) {
this._varargs = value;
}
getVarargs() {
return this._varargs || this.parent && this.parent.getVarargs();
}
add(key, value) {
this._variables[key] += value;
}
extend(outerScope) {
let innerVars = Object.create(this._variables);
let scope = new Scope(innerVars);
scope.parent = this;
return scope;
}
}

@ -0,0 +1,214 @@
import { default as LuaError } from './LuaError';
import { type } from './lib/globals';
let count = 0;
let stringLib, getn;
export function registerLibs(libs) {
// Can't import directly because they'll create a circular dependencies. :(
stringLib = libs.string;
getn = libs.getn;
};
export default class Table {
constructor(initialiser) {
this.index = ++count;
this.numValues = [undefined];
this.strValues = {};
this.keys = [];
this.values = [];
this.metatable = null;
if (!initialiser) {
// noop
} else if (typeof initialiser === 'function') {
initialiser(this);
} else {
let isArr = initialiser instanceof Array;
for (let i in initialiser) {
if (initialiser.hasOwnProperty(i)) {
let value = initialiser[i];
if (value === null) value = undefined;
let key = isArr? parseInt(i, 10) + 1: i;
let iterate = (typeof value == 'object' && value.constructor === Object) || value instanceof Array;
this.set(key, iterate? new Table(value) : value);
}
}
}
}
get(key) {
if (!(this instanceof Table)) {
if (type(this) == 'string') {
return stringLib.get(key);
} else if (
type(this) === 'userdata'
|| (type(this) === 'function' && key === 'new') // exception for DOMAPI compat with Moonshine
) {
if (key in this) {
return this[key];
}
}
throw new LuaError(`attempt to index a ${type(this)} value`);
}
let value = this.rawget(key);
if (value === void 0) {
let mt, mm;
if (
(mt = this.metatable)
&& (mm = mt.get('__index'))
) {
switch (mm.constructor) {
case Table: return mm.get(key);
case Function:
value = mm.call(undefined, this, key);
return (value instanceof Array)? value[0] : value;
}
}
}
return value;
}
rawget(key) {
switch (typeof key) {
case 'string': return Object.prototype.hasOwnProperty.call(this.strValues, key) ? this.strValues[key] : void 0;
case 'number':
if (key > 0 && key == key >> 0) {
return this.numValues[key];
}
/* fallthrough */
default:
let index = this.keys.indexOf(key);
return (index >= 0) ? this.values[index] : void 0;
}
}
set(key, value) {
if (!(this instanceof Table)) {
if (type(this) == 'userdata') {
this[key] = value;
return;
}
throw new LuaError(`attempt to index a ${type(this)} value`);
}
let mt, mm;
if (
(mt = this.metatable)
&& (mm = mt.get('__newindex'))
) {
let oldValue;
switch (typeof key) {
case 'string':
oldValue = this.strValues[key];
break;
case 'number':
let positiveIntegerKey = key > 0 && key == key >> 0;
if (positiveIntegerKey) {
oldValue = this.numValues[key];
break;
}
default:
let keys = this.keys;
let index = keys.indexOf(key);
oldValue = index == -1? undefined : this.values[index];
}
if (oldValue === undefined) {
switch (mm.constructor) {
case Table: return mm.set(key, value);
case Function: return mm(this, key, value);
}
}
}
this.rawset(key, value);
}
rawset(key, value) {
if (value instanceof Array) {
value = value[0];
}
switch (typeof key) {
case 'string':
this.strValues[key] = value;
break;
case 'number':
let positiveIntegerKey = key > 0 && key == key >> 0;
if (positiveIntegerKey) {
this.numValues[key] = value;
break;
}
default:
let keys = this.keys;
let index = keys.indexOf(key);
if (index < 0) {
index = keys.length;
keys[index] = key;
}
this.values[index] = value;
}
}
insert(...values) {
this.numValues.push(...values);
}
toString() {
let mt, mm;
if (
(mt = this.metatable)
&& (mm = mt.get('__tostring'))
) {
return mm(this)[0];
} else {
return 'table: 0x' + this.index.toString(16);
}
}
toObject() {
const isArr = getn(this) > 0;
const result = isArr? [] : {};
const numValues = this.numValues;
const strValues = this.strValues;
let i;
const l = numValues.length;
for (i = 1; i < l; i++) {
const propValue = numValues[i];
result[i - 1] = (propValue instanceof Table)? propValue.toObject() : propValue;
}
for (i in strValues) {
if (strValues.hasOwnProperty(i)) {
const propValue = strValues[i];
result[i] = (propValue instanceof Table)? propValue.toObject() : propValue;
}
}
return result;
}
};

@ -0,0 +1,78 @@
import { default as Scope } from './Scope';
import { default as globals, type } from './lib/globals';
import { default as operators } from './operators';
import { default as Table, registerLibs } from './Table';
import { default as LuaError } from './LuaError';
function ensureArray(value) {
return (value instanceof Array) ? value : [value];
}
function call(f, ...args) {
if (!(f instanceof Function)) {
if (f instanceof Table) {
let mt, mm;
if (
(mt = f.metatable)
&& (mm = mt.rawget('__call'))
) {
args.unshift(f);
f = mm;
}
}
if (!(f instanceof Function)) {
let typ = type(f);
throw new LuaError(`attempt to call a ${typ} value`);
}
}
return ensureArray(f(...args));
}
let namespace = global.starlight = global.starlight || {};
let _G = globals;
function init () {
let userEnv = namespace.config && namespace.config.env;
if (userEnv) {
for (let key in userEnv) {
globals.set(key, userEnv[key]);
}
}
}
init();
let runtime = namespace.runtime = {
globalScope: new Scope(globals.strValues),
_G,
op: operators,
T: Table,
LuaError,
call
};
// The following should be configurable
import { default as math } from './lib/math';
_G.set('math', math);
import { default as table, getn } from './lib/table';
_G.set('table', table);
import { default as string } from './lib/string';
_G.set('string', string);
import { default as os } from './lib/os';
_G.set('os', os);
import { default as _package } from './lib/package';
_G.set('package', _package);
registerLibs({ string, getn });

@ -0,0 +1,454 @@
import { default as T } from '../Table';
import { default as LuaError } from '../LuaError';
import { default as stringLib, metatable as stringMetatable } from './string';
import { getn } from './table';
import {
stdout,
coerceToNumber,
coerceToString,
coerceToBoolean,
coerceArgToNumber,
coerceArgToString,
coerceArgToTable
} from '../utils';
const CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
function getPackageMethods() {
let packageLib = global.starlight.runtime._G.rawget('package');
if (packageLib === void 0) {
throw new LuaError('error during require(), package lib not found.');
}
let methods = [packageLib.rawget('preload'), packageLib.rawget('loaded')];
getPackageMethods = ()=>methods;
return methods;
}
function ipairsIterator(table, index) {
if (index === void 0) {
throw new LuaError('Bad argument #2 to ipairs() iterator');
}
var nextIndex = index + 1,
numValues = table.numValues;
if (!numValues.hasOwnProperty(nextIndex) || numValues[nextIndex] === void 0) return void 0;
return [nextIndex, numValues[nextIndex]];
}
export const _VERSION = 'Lua 5.1';
export function assert(v, m) {
if (!coerceToBoolean(v)) {
m = coerceArgToString(m, 'assert', 2);
throw new LuaError(m || 'Assertion failed!');
}
return [v, m];
}
// TODO dofile([filename])
export function error(message) {
if (
typeof message !== 'string'
&& typeof message !== 'number'
) {
message = '(error object is not a string)';
}
throw new LuaError(message);
}
// TODO getfenv([f])
export function getmetatable(table) {
if (table && table instanceof T) {
let mt = table.metatable;
if (mt) {
let mm;
return (mm = mt.rawget('__metatable')) ? mm : mt;
}
} else if (typeof table == 'string') {
return stringMetatable;
}
}
export function ipairs(t) {
t = coerceArgToTable(t, 'ipairs', 1);
const mt = getmetatable(t);
const mm = mt && mt.get('__ipairs');
return mm ? mm(t).slice(0, 3) : [ipairsIterator, t, 0];
}
// TODO load(func [, chunkname])
// TODO loadfile([filename])
export function loadstring(str, chunkname) {
str = coerceArgToString(str, 'loadstring', 1);
let parser = global.starlight.parser;
if (!parser) {
throw new Error('Starlight parser not found in call to loadstring(). The parser is required to execute Lua strings at runtime.');
}
try {
return parser.parse(str);
} catch (e) {
return [undefined, e.message];
}
}
export function next(table, index) {
table = coerceArgToTable(table, 'next', 1);
// SLOOOOOOOW...
let found = (index === void 0),
key, value,
i, l;
if (found || (typeof index == 'number' && index > 0 && index == index >> 0)) {
let numValues = table.numValues;
if ('keys' in Object) {
// Use Object.keys, if available.
let keys = Object['keys'](numValues);
if (found) {
// First pass
i = 1;
} else if (i = keys.indexOf('' + index) + 1) {
found = true;
}
if (found) {
while ((key = keys[i]) !== void 0 && (value = numValues[key]) === void 0) i++;
if (value !== void 0) return [key >>= 0, value];
}
} else {
// Else use for-in (faster than for loop on tables with large holes)
for (l in numValues) {
i = l >> 0;
if (!found) {
if (i === index) found = true;
} else if (numValues[i] !== void 0) {
return [i, numValues[i]];
}
}
}
}
for (i in table.strValues) {
if (table.strValues.hasOwnProperty(i)) {
if (!found) {
if (i == index) found = true;
} else if (table.strValues[i] !== void 0) {
return [i, table.strValues[i]];
}
}
}
for (i in table.keys) {
if (table.keys.hasOwnProperty(i)) {
let key = table.keys[i];
if (!found) {
if (key === index) found = true;
} else if (table.values[i] !== void 0) {
return [key, table.values[i]];
}
}
}
return [];
}
export function pairs(table) {
table = coerceArgToTable(table, 'pairs', 1);
const mt = getmetatable(table);
const mm = mt && mt.get('__pairs');
return mm ? mm(table).slice(0, 3) : [next, table];
}
export function pcall(func, ...args) {
let result;
try {
if (typeof func == 'function') {
result = func(...args);
} else {
throw new LuaError('Attempt to call non-function');
}
} catch (e) {
return [false, e && e.toString()];
}
result = [].concat(result);
return [true, ...result];
}
export function print(...args) {
let output = args.map(arg => tostring(arg)).join('\t');
stdout.writeln(output);
}
export function rawequal(v1, v2) {
return v1 === v2;
}
export function rawget(table, index) {
table = coerceArgToTable(table, 'rawget', 1);
return table.rawget(index);
}
export function rawset(table, index, value) {
table = coerceArgToTable(table, 'rawset', 1);
if (index === void 0) throw new LuaError('table index is nil');
table.rawset(index, value);
return table;
}
export function _require(modname) {
modname = coerceArgToString(modname, 'require', 1);
modname = modname.replace(/\//g, '.');
let [preload, loaded] = getPackageMethods();
let mod = loaded.rawget(modname);
if (mod) {
return mod;
}
let modinit = preload.rawget(modname);
if (modinit === void 0) {
throw new LuaError(`module '${modname}' not found:\n\tno field package.preload['${modname}']`);
}
let modResult = modinit(modname);
mod = (modResult instanceof Array) ? modResult[0] : modResult;
loaded.rawset(modname, mod !== void 0 ? mod : true);
return mod;
}
export function select(index, ...args) {
if (index === '#') {
return args.length;
} else if (index = parseInt(index, 10)) {
return args.slice(index - 1);
} else {
let typ = type(index);
throw new LuaError(`bad argument #1 to 'select' (number expected, got ${typ})`);
}
}
// TODO setfenv(f, table)
export function setmetatable(table, metatable) {
table = coerceArgToTable(table, 'setmetatable', 1);
if (metatable !== void 0) {
metatable = coerceArgToTable(metatable, 'setmetatable', 2);
}
let mt;
if (
(mt = table.metatable)
&& mt.rawget('__metatable')
) {
throw new LuaError('cannot change a protected metatable');
}
table.metatable = metatable;
return table;
}
export function tonumber(e, base = 10) {
base = coerceArgToNumber(base, 'tonumber', 2);
if (e === '') return;
if (base < 2 || base > 36) {
throw new LuaError(`bad argument #2 to 'tonumber' (base out of range)`);
}
if (base == 10 && (e === Infinity || e === -Infinity || (typeof e == 'number' && global.isNaN(e)))) {
return e;
}
if (base != 10 && e === void 0) {
throw new LuaError("bad argument #1 to 'tonumber' (string expected, got nil)");
}
e = `${e}`.trim();
// If using base 10, use normal coercion.
if (base === 10) {
return coerceToNumber(e);
}
e = coerceToString(e);
// If using base 16, ingore any "0x" prefix
let match;
if (
base === 16
&& (match = e.match(/^(\-)?0[xX](.+)$/))
) {
e = `${match[1] || ''}${match[2]}`;
}
let pattern = new RegExp('^[' + CHARS.substr(0, base) + ']*$', 'gi');
if (!pattern.test(e)) return; // Invalid
return parseInt(e, base);
}
export function tostring(e) {
let mt, mm;
if (
e !== void 0
&& e instanceof T
&& (mt = e.metatable)
&& (mm = mt.rawget('__tostring'))
) {
return mm.call(mm, e);
}
if (e instanceof T) {
return e.toString();
} else if (e instanceof Function) {
return e.hasOwnProperty('toString')? `${e}` : 'function: [host code]';
}
return coerceToString(e);
}
export function type(v) {
let t = typeof v;
switch (t) {
case 'undefined':
return 'nil';
case 'number':
case 'string':
case 'boolean':
case 'function':
return t;
case 'object':
if (v.constructor === T) return 'table';
if (v && v instanceof Function) return 'function';
return 'userdata';
}
}
export function unpack(table, i = 1, j) {
table = coerceArgToTable(table, 'unpack', 1);
i = coerceArgToNumber(i, 'unpack', 2);
if (j === void 0) {
j = getn(table);
} else {
j = coerceArgToNumber(j, 'unpack', 3);
}
return table.numValues.slice(i, j + 1);
}
export function xpcall(func, err) {
let result, success, invalid;
try {
if (typeof func === 'function') {
result = func();
} else {
invalid = true;
}
success = true;
} catch (e) {
result = err(void 0, true)[0];
success = false;
}
if (invalid) throw new LuaError('Attempt to call non-function');
if (!(result && result instanceof Array)) result = [result];
result.unshift(success);
return result;
}
export const _G = new T({
_VERSION,
assert,
error,
getmetatable,
ipairs,
loadstring,
next,
pairs,
pcall,
print,
rawequal,
rawget,
rawset,
require: _require,
select,
setmetatable,
tonumber,
tostring,
type,
unpack,
xpcall,
});
_G.rawset('_G', _G);
export default _G;

@ -0,0 +1,253 @@
import { default as T } from '../Table';
import { coerceArgToNumber } from '../utils';
const RANDOM_MULTIPLIER = 16807;
const RANDOM_MODULUS = 2147483647;
let randomSeed = 1;
function getRandom () {
randomSeed = (RANDOM_MULTIPLIER * randomSeed) % RANDOM_MODULUS;
return randomSeed / RANDOM_MODULUS;
}
export function abs(x) {
x = coerceArgToNumber(x, 'abs', 1);
return Math.abs(x);
}
export function acos(x) {
x = coerceArgToNumber(x, 'acos', 1);
return Math.acos(x);
}
export function asin(x) {
x = coerceArgToNumber(x, 'asin', 1);
return Math.asin(x);
}
export function atan(x) {
x = coerceArgToNumber(x, 'atan', 1);
return Math.atan(x);
}
export function atan2(y, x) {
y = coerceArgToNumber(y, 'atan2', 1);
x = coerceArgToNumber(x, 'atan2', 2);
return Math.atan2(y, x);
}
export function ceil(x) {
x = coerceArgToNumber(x, 'ceil', 1);
return Math.ceil(x);
}
export function cos(x) {
x = coerceArgToNumber(x, 'cos', 1);
return Math.cos(x);
}
export function cosh(x) {
x = coerceArgToNumber(x, 'cosh', 1);
return (exp(x) + exp(-x)) / 2;
}
export function deg(x) {
x = coerceArgToNumber(x, 'deg', 1);
return x * 180 / Math.PI;
}
export function exp(x) {
x = coerceArgToNumber(x, 'exp', 1);
return Math.exp(x);
}
export function floor(x) {
x = coerceArgToNumber(x, 'floor', 1);
return Math.floor(x);
}
export function fmod(x, y) {
x = coerceArgToNumber(x, 'fmod', 1);
y = coerceArgToNumber(y, 'fmod', 2);
return x % y;
}
export function frexp(x) {
x = coerceArgToNumber(x, 'frexp', 1);
if (x === 0) {
return [0, 0];
}
let delta = x > 0? 1 : -1;
x *= delta;
let exponent = Math.floor(Math.log(x) / Math.log(2)) + 1;
let mantissa = x / Math.pow(2, exponent);
return [mantissa * delta, exponent];
}
export const huge = Infinity;
export function ldexp(m, e) {
m = coerceArgToNumber(m, 'ldexp', 1);
e = coerceArgToNumber(e, 'ldexp', 2);
return m * Math.pow(2, e);
}
export function log(x, base) {
x = coerceArgToNumber(x, 'log', 1);
if (base === void 0) {
return Math.log(x);
} else {
y = coerceArgToNumber(y, 'log', 2);
return Math.log(x) / Math.log(base);
}
}
export function log10(x) {
x = coerceArgToNumber(x, 'log10', 1);
// v5.2: shine.warn ('math.log10 is deprecated. Use math.log with 10 as its second argument instead.');
return Math.log(x) / Math.log(10);
}
export function max(...args) {
return Math.max(...args);
}
export function min(...args) {
return Math.min(...args);
}
export function modf(x) {
x = coerceArgToNumber(x, 'modf', 1);
let intValue = Math.floor(x);
let mantissa = x - intValue;
return [intValue, mantissa];
}
export const pi = Math.PI;
export function pow(x, y) {
x = coerceArgToNumber(x, 'pow', 1);
y = coercArgToNumber(y, 'pow', 2);
return Math.pow(x, y);
}
export function rad(x) {
x = coerceArgToNumber(x, 'rad', 1);
return (Math.PI / 180) * x;
}
export function random(min, max) {
if (min === void 0 && max === void 0) return getRandom();
min = coerceArgToNumber(min, 'random', 1);
if (max === void 0) {
max = min;
min = 1;
} else {
max = coerceArgToNumber(max, 'random', 2);
}
if (min > max) throw new shine.Error("bad argument #2 to 'random' (interval is empty)");
return Math.floor(getRandom() * (max - min + 1) + min);
}
export function randomseed(x) {
x = coerceArgToNumber(x, 'randomseed', 1);
randomSeed = x;
}
export function sin(x) {
x = coerceArgToNumber(x, 'sin', 1);
return Math.sin(x);
}
export function sinh(x) {
x = coerceArgToNumber(x, 'sinh', 1);
return (exp(x) - exp(-x)) / 2;
}
export function sqrt(x) {
x = coerceArgToNumber(x, 'sqrt', 1);
return Math.sqrt(x);
}
export function tan(x) {
x = coerceArgToNumber(x, 'tan', 1);
return Math.tan(x);
}
export function tanh(x) {
x = coerceArgToNumber(x, 'tanh', 1);
return (exp(x) - exp(-x))/(exp(x) + exp(-x));
}
export default new T({
abs,
acos,
asin,
atan,
atan2,
ceil,
cos,
cosh,
deg,
exp,
floor,
fmod,
frexp,
huge,
ldexp,
log,
log10,
max,
min,
modf,
pi,
pow,
rad,
random,
randomseed,
sin,
sinh,
sqrt,
tan,
tanh
});

@ -0,0 +1,115 @@
import { default as T } from '../Table';
const DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
const MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
const DAYS_IN_MONTH = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
const DATE_FORMAT_HANDLERS = {
'%a': (d, utc) => DAYS[d['get' + (utc? 'UTC' : '') + 'Day']()].substr(0, 3),
'%A': (d, utc) => DAYS[d['get' + (utc? 'UTC' : '') + 'Day']()],
'%b': (d, utc) => MONTHS[d['get' + (utc? 'UTC' : '') + 'Month']()].substr(0, 3),
'%B': (d, utc) => MONTHS[d['get' + (utc? 'UTC' : '') + 'Month']()],
'%c': (d, utc) => d['to' + (utc? 'UTC' : '') + 'LocaleString'](),
'%d': (d, utc) => ('0' + d['get' + (utc? 'UTC' : '') + 'Date']()).substr(-2),
'%H': (d, utc) => ('0' + d['get' + (utc? 'UTC' : '') + 'Hours']()).substr(-2),
'%I': (d, utc) => ('0' + ((d['get' + (utc? 'UTC' : '') + 'Hours']() + 11) % 12 + 1)).substr(-2),
'%j': (d, utc) => {
let result = d['get' + (utc? 'UTC' : '') + 'Date']();
let m = d['get' + (utc? 'UTC' : '') + 'Month']();
for (let i = 0; i < m; i++) {
result += DAYS_IN_MONTH[i];
}
if (m > 1 && d['get' + (utc? 'UTC' : '') + 'FullYear']() % 4 === 0) {
result +=1;
}
return ('00' + result).substr(-3);
},
'%m': (d, utc) => ('0' + (d['get' + (utc? 'UTC' : '') + 'Month']() + 1)).substr(-2),
'%M': (d, utc) => ('0' + d['get' + (utc? 'UTC' : '') + 'Minutes']()).substr(-2),
'%p': (d, utc) => (d['get' + (utc? 'UTC' : '') + 'Hours']() < 12)? 'AM' : 'PM',
'%S': (d, utc) => ('0' + d['get' + (utc? 'UTC' : '') + 'Seconds']()).substr(-2),
'%U': (d, utc) => getWeekOfYear(d, 0, utc),
'%w': (d, utc) => '' + (d['get' + (utc? 'UTC' : '') + 'Day']()),
'%W': (d, utc) => getWeekOfYear(d, 1, utc),
'%x': (d, utc) => DATE_FORMAT_HANDLERS['%m'](d, utc) + '/' + DATE_FORMAT_HANDLERS['%d'](d, utc) + '/' + DATE_FORMAT_HANDLERS['%y'](d, utc),
'%X': (d, utc) => DATE_FORMAT_HANDLERS['%H'](d, utc) + ':' + DATE_FORMAT_HANDLERS['%M'](d, utc) + ':' + DATE_FORMAT_HANDLERS['%S'](d, utc),
'%y': (d, utc) => DATE_FORMAT_HANDLERS['%Y'](d, utc).substr (-2),
'%Y': (d, utc) => '' + d['get' + (utc? 'UTC' : '') + 'FullYear'](),
'%Z': (d, utc) => { let m; return (utc && 'UTC') || ((m = d.toString().match(/[A-Z][A-Z][A-Z]/)) && m[0]); },
'%%': () => '%'
}
function isDST(date) {
let year = date.getFullYear();
let jan = new Date(year, 0);
// ASSUMPTION: If the time offset of the date is the same as it would be in January of the same year, DST is not in effect.
return (date.getTimezoneOffset() !== jan.getTimezoneOffset());
}
function getWeekOfYear (d, firstDay, utc) {
let dayOfYear = parseInt(DATE_FORMAT_HANDLERS['%j'](d), 10);
let jan1 = new Date(d.getFullYear (), 0, 1, 12);
let offset = (8 - jan1['get' + (utc? 'UTC' : '') + 'Day']() + firstDay) % 7;
return ('0' + (Math.floor((dayOfYear - offset) / 7) + 1)).substr(-2);
}
export function date(format = '%c', time) {
let utc,
date = new Date();
if (time) {
date.setTime(time * 1000);
}
if (format.substr(0, 1) === '!') {
format = format.substr(1);
utc = true;
}
if (format === '*t') {
return new T ({
year: parseInt(DATE_FORMAT_HANDLERS['%Y'](date, utc), 10),
month: parseInt(DATE_FORMAT_HANDLERS['%m'](date, utc), 10),
day: parseInt(DATE_FORMAT_HANDLERS['%d'](date, utc), 10),
hour: parseInt(DATE_FORMAT_HANDLERS['%H'](date, utc), 10),
min: parseInt(DATE_FORMAT_HANDLERS['%M'](date, utc), 10),
sec: parseInt(DATE_FORMAT_HANDLERS['%S'](date, utc), 10),
wday: parseInt(DATE_FORMAT_HANDLERS['%w'](date, utc), 10) + 1,
yday: parseInt(DATE_FORMAT_HANDLERS['%j'](date, utc), 10),
isdst: isDST(date)
});
}
for (let i in DATE_FORMAT_HANDLERS) {
if (DATE_FORMAT_HANDLERS.hasOwnProperty(i) && format.indexOf(i) >= 0) {
format = format.replace(i, DATE_FORMAT_HANDLERS[i](date, utc));
}
}
return format;
}
export function exit(code = 0) {
var process = global.process;
if (process && process.exit) {
process.exit(code);
} else {
throw new Error(`Exit with code: ${code}`);
}
}
export default new T({
date,
exit
});

@ -0,0 +1,7 @@
import { default as T } from '../Table';
export default new T({
preload: new T(),
loaded: new T()
});

@ -0,0 +1,319 @@
import { default as T } from '../Table';
import { default as LuaError } from '../LuaError';
import { tostring } from './string';
import printf from 'printf';
import {
coerceToNumber,
coerceToString,
coerceToBoolean,
coerceArgToNumber,
coerceArgToString,
coerceArgToFunction
} from '../utils';
const ROSETTA_STONE = {
'([^a-zA-Z0-9%(])-': '$1*?',
'([^%])-([^a-zA-Z0-9?])': '$1*?$2',
'(.)-$': '$1*?',
'%a': '[a-zA-Z]',
'%A': '[^a-zA-Z]',
'%c': '[\x00-\x1f]',
'%C': '[^\x00-\x1f]',
'%d': '\\d',
'%D': '[^\d]',
'%l': '[a-z]',
'%L': '[^a-z]',
'%p': '[\.\,\"\'\?\!\;\:\#\$\%\&\(\)\*\+\-\/\<\>\=\@\\[\\]\\\\^\_\{\}\|\~]',
'%P': '[^\.\,\"\'\?\!\;\:\#\$\%\&\(\)\*\+\-\/\<\>\=\@\\[\\]\\\\^\_\{\}\|\~]',
'%s': '[ \\t\\n\\f\\v\\r]',
'%S': '[^ \t\n\f\v\r]',
'%u': '[A-Z]',
'%U': '[^A-Z]',
'%w': '[a-zA-Z0-9]',
'%W': '[^a-zA-Z0-9]',
'%x': '[a-fA-F0-9]',
'%X': '[^a-fA-F0-9]',
'%([^a-zA-Z])': '\\$1'
};
function translatePattern (pattern) {
// TODO Add support for balanced character matching (not sure this is easily achieveable).
pattern = '' + pattern;
// Replace single backslash with double backslashes
pattern = pattern.replace(new RegExp('\\\\', 'g'), '\\\\');
for (let i in ROSETTA_STONE) {
if (ROSETTA_STONE.hasOwnProperty(i)) {
pattern = pattern.replace(new RegExp(i, 'g'), ROSETTA_STONE[i]);
}
}
let l = pattern.length;
let n = 0;
for (let i = 0; i < l; i++) {
const character = pattern.substr(i, 1);
if (i && pattern.substr(i - 1, 1) == '\\') {
continue;
}
let addSlash = false;
if (character == '[') {
if (n) addSlash = true;
n++;
} else if (character == ']' && pattern.substr(i - 1, 1) !== '\\') {
n--;
if (n) addSlash = true;
}
if (addSlash) {
pattern = pattern.substr(0, i) + pattern.substr(i++ + 1);
l++;
}
}
return pattern;
}
export function byte(s, i = 1, j) {
s = coerceArgToString(s, 'byte', 1);
i = coerceArgToNumber(i, 'byte', 2);
if (j === void 0) {
j = i;
} else {
j = coerceArgToNumber(j, 'byte', 3);
}
return s.substring(i - 1, j).split('').map(c => c.charCodeAt(0));
}
export function char(...bytes) {
return bytes.map((b, i) => {
b = coerceArgToNumber(b, 'char', i);
return String.fromCharCode(b);
}).join('');
}
export function dump(func) {
func = coerceArgToFunction(func, 'dump', 1);
throw new LuaError('string.dump() is not supported');
}
export function find(s, pattern, init = 1, plain = false) {
s = coerceArgToString(s, 'find', 1);
pattern = coerceArgToString(pattern, 'find', 2);
init = coerceArgToNumber(init, 'find', 3);
plain = coerceToBoolean(plain);
// Regex
if (!plain) {
pattern = translatePattern(pattern);
let reg = new RegExp(pattern);
let index = s.substr(init - 1).search(reg);
if (index < 0) return;
let match = s.substr(init - 1).match(reg);
let result = [index + init, index + init + match[0].length - 1];
match.shift();
return result.concat(match);
}
// Plain
let index = s.indexOf(pattern, init - 1);
return (index === -1)? void 0 : [index + 1, index + pattern.length];
}
// TODO string.format (formatstring, ···)
export function format(formatstring, ...args) {
return printf(formatstring, ...args);
}
export function gmatch(s, pattern) {
s = coerceArgToString(s, 'gmatch', 1);
pattern = coerceArgToString(pattern, 'gmatch', 2);
pattern = translatePattern(pattern);
let reg = new RegExp(pattern, 'g'),
matches = s.match(reg);
return () => {
let match = matches.shift(),
groups = new RegExp(pattern).exec(match);
if (match === void 0) {
return;
}
groups.shift();
return groups.length? groups : match;
};
}
export function gsub(s, pattern, repl, n = Infinity) {
s = coerceArgToString(s, 'gsub', 1);
pattern = coerceArgToString(pattern, 'gsub', 2);
n = coerceArgToNumber(n, 'gsub', 3);
pattern = translatePattern('' + pattern);
let replIsFunction = (typeof repl == 'function');
let count = 0,
result = '',
str,
prefix,
match,
lastMatch;
while (
count < n
&& s
&& (match = s.match(pattern))
) {
if (replIsFunction) {
str = repl(match[0]);
if (str instanceof Array) str = str[0];
if (str === void 0) str = match[0];
} else if (repl instanceof T) {
str = repl.get(match[0]);
} else if (typeof repl == 'object') {
str = repl[match];
} else {
str = `${repl}`.replace(/%([0-9])/g, (m, i) => match[i]);
}
if (match[0].length === 0) {
if (lastMatch === void 0) {
prefix = '';
} else {
prefix = s.substr(0, 1);
}
} else {
prefix = s.substr(0, match.index);
}
lastMatch = match[0];
result += `${prefix}${str}`;
s = s.substr(`${prefix}${lastMatch}`.length);
count++;
}
return [`${result}${s}`, count];
}
export function len(s) {
s = coerceArgToString(s, 'len', 1);
return s.length;
}
export function lower (s) {
s = coerceArgToString(s, 'lower', 1);
return s.toLowerCase();
}
export function match(s, pattern, init = 0) {
s = coerceArgToString(s, 'match', 1);
pattern = coerceArgToString(pattern, 'match', 2);
init = coerceArgToNumber(init, 'match', 3);
s = s.substr(init);
let matches = s.match(new RegExp(translatePattern (pattern)));
if (!matches) {
return;
} else if (!matches[1]) {
return matches[0];
}
matches.shift();
return matches;
}
export function rep(s, n) {
s = coerceArgToString(s, 'rep', 1);
n = coerceArgToNumber(n, 'rep', 2);
return Array(n + 1).join(s);
}
export function reverse(s) {
s = coerceArgToString(s, 'reverse', 1);
return Array.prototype.map.call(s, l => l).reverse().join('');
}
export function sub(s, i = 1, j) {
s = coerceArgToString(s, 'sub', 1);
i = coerceArgToNumber(i, 'sub', 2);
if (j === void 0) {
s.length;
} else {
j = coerceArgToNumber(j, 'sub', 3);
}
if (i > 0) {
i = i - 1;
} else if (i < 0) {
i = s.length + i;
}
if (j < 0) {
j = s.length + j + 1;
}
return s.substring(i, j);
}
export function upper(s) {
s = coerceArgToString(s, 'upper', 1);
return s.toUpperCase();
}
const string = new T({
byte,
char,
dump,
find,
format,
gmatch,
gsub,
len,
lower,
match,
rep,
reverse,
sub,
upper
});
export default string;
export const metatable = new T({ __index: string });

@ -0,0 +1,136 @@
import { default as T } from '../Table';
import { default as LuaError } from '../LuaError';
import {
coerceToNumber,
coerceToBoolean,
coerceArgToNumber,
coerceArgToString,
coerceArgToTable,
coerceArgToFunction
} from '../utils';
export function concat(table, sep = '', i = 1, j) {
table = coerceArgToTable(table, 'concat', 1);
sep = coerceArgToString(sep, 'concat', 2);
i = coerceArgToNumber(i, 'concat', 3);
if (j === void 0) {
j = maxn(table);
} else {
j = coerceArgToNumber(j, 'concat', 4);
}
return [].concat(table.numValues).splice(i, j - i + 1).join(sep);
}
export function getn(table) {
table = coerceArgToTable(table, 'getn', 1);
let vals = table.numValues,
keys = [],
j = 0;
for (let i in vals) {
if (vals.hasOwnProperty(i)) {
keys[i] = true;
}
}
while (keys[j + 1]) j++;
// Following translated from ltable.c (http://www.lua.org/source/5.1/ltable.c.html)
if (j > 0 && vals[j] === void 0) {
/* there is a boundary in the array part: (binary) search for it */
let i = 0;
while (j - i > 1) {
let m = Math.floor((i + j) / 2);
if (vals[m] === void 0) {
j = m;
} else {
i = m;
}
}
return i;
}
return j;
}
export function insert(table, index, obj) {
table = coerceArgToTable(table, 'insert', 1);
if (obj === void 0) {
obj = index;
index = table.numValues.length;
} else {
index = coerceArgToNumber(index, 'insert', 2);
}
table.numValues.splice(index, 0, void 0);
table.set(index, obj);
}
export function maxn(table) {
table = coerceArgToTable(table, 'maxn', 1);
return table.numValues.length - 1;
}
export function remove(table, index) {
table = coerceArgToTable(table, 'remove', 1);
index = coerceArgToNumber(index, 'remove', 2);
let max = getn(table);
let vals = table.numValues;
if (index > max) {
return;
}
if (index === void 0) {
index = max;
}
let result = vals.splice(index, 1);
while (index < max && vals[index] === void 0) {
delete vals[index++];
}
return result;
}
export function sort(table, comp) {
table = coerceArgToTable(table, 'sort', 1);
let sortFunc;
let arr = table.numValues;
if (comp) {
comp = coerceArgToFunction(comp, 'sort', 2);
sortFunc = (a, b) => coerceToBoolean(comp(a, b)[0])? -1 : 1;
} else {
sortFunc = (a, b) => a < b? -1 : 1;
}
arr.shift();
arr.sort(sortFunc).unshift(void 0);
}
export default new T({
concat,
getn,
insert,
maxn,
remove,
sort
});

@ -0,0 +1,157 @@
import { default as T } from './Table';
import { coerceToNumber, coerceToBoolean, coerceToString } from './utils';
import { getn } from './lib/table';
import { default as LuaError } from './LuaError';
function binaryArithmetic(left, right, metaMethodName, callback) {
let mt, f;
if ((left && left instanceof T && (mt = left.metatable) && (f = mt.rawget(metaMethodName)))
|| (right && right instanceof T && (mt = right.metatable) && (f = mt.rawget(metaMethodName)))) {
return f(left, right)[0];
}
if (typeof left !== 'number') {
left = coerceToNumber(left, 'attempt to perform arithmetic on a %type value');
}
if (typeof right !== 'number') {
right = coerceToNumber(right, 'attempt to perform arithmetic on a %type value');
}
return callback(left, right);
}
function binaryStringArithmetic(left, right, metaMethodName, callback) {
if (typeof left == 'string' && typeof right == 'string') {
return callback(left, right);
}
return binaryArithmetic(left, right, metaMethodName, callback);
}
function concat(left, right) {
let mt, f;
if (
(left && left instanceof T && (mt = left.metatable) && (f = mt.rawget('__concat')))
|| (right && right instanceof T && (mt = right.metatable) && (f = mt.rawget('__concat')))
) {
return f(left, right)[0];
} else {
right = coerceToString(right, 'attempt to concatenate a %type value');
left = coerceToString(left, 'attempt to concatenate a %type value');
return `${left}${right}`;
}
}
function equal(left, right) {
var mtl, mtr, f, result;
if (right !== left
&& left && left instanceof T
&& right && right instanceof T
&& (mtl = left.metatable)
&& (mtr = right.metatable)
&& mtl === mtr
&& (f = mtl.rawget('__eq'))
) {
return !!f(left, right)[0];
}
return (left === right);
}
function mod(left, right) {
if (
right === 0
|| right === -Infinity
|| right === Infinity
|| global.isNaN(left)
|| global.isNaN(right)
) {
return NaN;
}
let absRight = Math.abs(right);
let result = Math.abs(left) % absRight;
if (left * right < 0) result = absRight - result;
if (right < 0) result *= -1;
return result;
}
function len(value) {
let length, i;
if (value === undefined) throw new LuaError('attempt to get length of a nil value');
if (value instanceof T) return getn(value);
if (typeof value == 'object') {
let length = 0;
for (let key in value) {
if (value.hasOwnProperty(key)) {
length++;
}
}
return length;
}
return value.length;
}
function unaryMinus(value) {
var mt, f, result;
if (value && value instanceof T && (mt = value.metatable) && (f = mt.rawget('__unm'))) {
return f(value)[0];
}
if (typeof value !== 'number') {
value = coerceToNumber(value, 'attempt to perform arithmetic on a %type value');
}
return -value;
}
const op = {
concat,
len,
eq: equal,
unm: unaryMinus,
bool: coerceToBoolean,
neq: (...args) => !equal(...args),
not: (...args) => !coerceToBoolean(...args),
add: (left, right) => binaryArithmetic(left, right, '__add', (l, r) => l + r),
sub: (left, right) => binaryArithmetic(left, right, '__sub', (l, r) => l - r),
mul: (left, right) => binaryArithmetic(left, right, '__mul', (l, r) => l * r),
div: (left, right) => {
if (right === undefined) throw new LuaError('attempt to perform arithmetic on a nil value');
return binaryArithmetic(left, right, '__div', (l, r) => l / r);
},
mod: (left, right) => binaryArithmetic(left, right, '__mod', mod),
pow: (left, right) => binaryArithmetic(left, right, '__pow', Math.pow),
lt: (left, right) => binaryStringArithmetic(left, right, '__lt', (l, r) => l < r),
lte: (left, right) => binaryStringArithmetic(left, right, '__le', (l, r) => l <= r),
gt(left, right) {
return !op.lte(left, right);
},
gte(left, right) {
return !op.lt(left, right);
}
};
export default op;

@ -0,0 +1,173 @@
import { type } from './lib/globals';
import { default as LuaError } from './LuaError';
import { default as T } from './Table';
/**
* Pattern to identify a float string value that can validly be converted to a number in Lua.
* @type RegExp
* @constant
*/
const FLOATING_POINT_PATTERN = /^[-+]?[0-9]*\.?([0-9]+([eE][-+]?[0-9]+)?)?$/;
/**
* Pattern to identify a hex string value that can validly be converted to a number in Lua.
* @type RegExp
* @constant
*/
const HEXIDECIMAL_CONSTANT_PATTERN = /^(\-)?0x([0-9a-fA-F]*)\.?([0-9a-fA-F]*)$/;
function defaultWriteln (...args) {
console.log(...args);
}
/********************
* Stdout
********************/
export const stdout = {
writeln: (() => {
let namespace, config, stdout;
return (namespace = global.starlight)
&& (config = namespace.config)
&& (stdout = config.stdout)
&& stdout.writeln
|| defaultWriteln;
})()
};
/********************
* Coercion
********************/
/**
* Thows an error with the type of a variable included in the message
* @param {Object} val The value whise type is to be inspected.
* @param {String} errorMessage The error message to throw.
* @throws {LuaError}
*/
function throwCoerceError (val, errorMessage) {
if (!errorMessage) return;
errorMessage = ('' + errorMessage).replace(/\%type/gi, type(val));
throw new LuaError(errorMessage);
}
/**
* Coerces a value from its current type to a boolean in the same manner as Lua.
* @param {Object} val The value to be converted.
* @returns {Boolean} The converted value.
*/
export function coerceToBoolean(val) {
return !(val === false || val === void 0);
}
/**
* Coerces a value from its current type to a number in the same manner as Lua.
* @param {Object} val The value to be converted.
* @param {String} [errorMessage] The error message to throw if the conversion fails.
* @returns {Number} The converted value.
*/
export function coerceToNumber(val, errorMessage) {
let n, match, mantissa;
switch (true) {
case typeof val == 'number': return val;
case val === void 0: return;
case val === 'inf': return Infinity;
case val === '-inf': return -Infinity;
case val === 'nan': return NaN;
default:
if (('' + val).match(FLOATING_POINT_PATTERN)) {
n = parseFloat(val);
} else if (match = ('' + val).match(HEXIDECIMAL_CONSTANT_PATTERN)) {
mantissa = match[3];
if ((n = match[2]) || mantissa) {
n = parseInt(n, 16) || 0;
if (mantissa) n += parseInt(mantissa, 16) / Math.pow(16, mantissa.length);
if (match[1]) n *= -1;
}
}
if (n === void 0) {
throwCoerceError(val, errorMessage);
}
return n;
}
}
/**
* Coerces a value from its current type to a string in the same manner as Lua.
* @param {Object} val The value to be converted.
* @param {String} [errorMessage] The error message to throw if the conversion fails.
* @returns {String} The converted value.
*/
export function coerceToString(val, errorMessage) {
switch(true) {
case typeof val == 'string':
return val;
case val === void 0:
case val === null:
return 'nil';
case val === Infinity:
return 'inf';
case val === -Infinity:
return '-inf';
case typeof val == 'number':
case typeof val == 'boolean':
return global.isNaN(val)? 'nan' : `${val}`;
default:
return throwCoerceError(val, errorMessage) || 'userdata';
}
}
function coerceArg(value, coerceFunc, typ, funcName, index) {
return coerceFunc(value, `bad argument #${index} to '${funcName}' (${typ} expected, got %type)`);
}
export function coerceArgToNumber(value, funcName, index) {
return coerceArg(value, coerceToNumber, 'number', funcName, index);
}
export function coerceArgToString(value, funcName, index) {
return coerceArg(value, coerceToString, 'string', funcName, index);
}
export function coerceArgToTable(value, funcName, index) {
if (value instanceof T) {
return value;
} else {
let typ = type(value);
throw new LuaError(`bad argument #${index} to '${funcName}' (table expected, got ${typ})`);
}
}
export function coerceArgToFunction(value, funcName, index) {
if (value instanceof Function) {
return value;
} else {
let typ = type(value);
throw new LuaError(`bad argument #${index} to '${funcName}' (function expected, got ${typ})`);
}
}

@ -0,0 +1,41 @@
'use strict'
const path = require('path')
const CleanWebpackPlugin = require('clean-webpack-plugin')
module.exports = {
mode: 'production',
target: 'node',
entry: './src/index',
output: {
path: path.resolve(__dirname, './dist'),
filename: 'lua2js.js',
library: 'lua2js',
libraryTarget:'umd'
},
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: [
["@babel/preset-env", {
targets: {
node: "current"
}
}]
]
}
}
]
}
]
},
plugins: [
new CleanWebpackPlugin(['dist'])
]
}
Loading…
Cancel
Save