commit
d5ccdbea3b
@ -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
|
||||||
File diff suppressed because one or more lines are too long
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…
Reference in new issue