You've already forked factorio-blueprint-editor
mirror of
https://github.com/teoxoy/factorio-blueprint-editor.git
synced 2025-11-23 22:15:01 +02:00
1801 lines
50 KiB
JavaScript
1801 lines
50 KiB
JavaScript
|
|
/**
|
||
|
|
@license Copyright 2011 Maximilian Herkender
|
||
|
|
|
||
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
you may not use this file except in compliance with the License.
|
||
|
|
You may obtain a copy of the License at
|
||
|
|
|
||
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
|
||
|
|
Unless required by applicable law or agreed to in writing, software
|
||
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
See the License for the specific language governing permissions and
|
||
|
|
limitations under the License.
|
||
|
|
*/
|
||
|
|
|
||
|
|
var lua_print = function () {
|
||
|
|
try {
|
||
|
|
console.log.apply(console, arguments);
|
||
|
|
} catch (e) {
|
||
|
|
// do nothing
|
||
|
|
}
|
||
|
|
return [];
|
||
|
|
};
|
||
|
|
|
||
|
|
function lua_load(chunk, chunkname) {
|
||
|
|
if (!lua_parser) {
|
||
|
|
throw new Error("Lua parser not available, perhaps you're not using the lua+parser.js version of the library?");
|
||
|
|
}
|
||
|
|
|
||
|
|
var fn;
|
||
|
|
eval(
|
||
|
|
"fn = function " + (chunkname || "load") + "() {\n" +
|
||
|
|
"return (function () {\n" +
|
||
|
|
lua_parser.parse(chunk) + "\n" +
|
||
|
|
"})()[0];\n" +
|
||
|
|
"};");
|
||
|
|
return fn;
|
||
|
|
}
|
||
|
|
|
||
|
|
/*
|
||
|
|
Copyright 2011 Maximilian Herkender
|
||
|
|
|
||
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
|
you may not use this file except in compliance with the License.
|
||
|
|
You may obtain a copy of the License at
|
||
|
|
|
||
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
||
|
|
|
||
|
|
Unless required by applicable law or agreed to in writing, software
|
||
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
|
See the License for the specific language governing permissions and
|
||
|
|
limitations under the License.
|
||
|
|
*/
|
||
|
|
|
||
|
|
// slice that works in as3 and js on arguments
|
||
|
|
function slice(arr, start) {
|
||
|
|
if (arr.slice) {
|
||
|
|
return arr.slice(start);
|
||
|
|
} else {
|
||
|
|
return Array.prototype.slice.call(arr, start);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// not supported call
|
||
|
|
function not_supported() {
|
||
|
|
throw new Error("Not supported");
|
||
|
|
}
|
||
|
|
|
||
|
|
function check_string(s) {
|
||
|
|
var type = typeof s;
|
||
|
|
if (type == "string") {
|
||
|
|
return s;
|
||
|
|
} else if (type == "number") {
|
||
|
|
return s.toString();
|
||
|
|
} else {
|
||
|
|
throw new Error("Input not string");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/** @constructor */
|
||
|
|
function ReturnValues(vars) {
|
||
|
|
this.vars = vars || [];
|
||
|
|
}
|
||
|
|
|
||
|
|
// methods used by generated lua code
|
||
|
|
function lua_true(op) {
|
||
|
|
return op != null && op !== false;
|
||
|
|
}
|
||
|
|
function lua_not(op) {
|
||
|
|
return op == null || op === false;
|
||
|
|
}
|
||
|
|
function lua_and(op1, op2) {
|
||
|
|
return op1 == null || op1 === false ? op1 : op2();
|
||
|
|
}
|
||
|
|
function lua_or(op1, op2) {
|
||
|
|
return op1 != null && op1 !== false ? op1 : op2();
|
||
|
|
}
|
||
|
|
function lua_assertfloat(n) {
|
||
|
|
var result = parseFloat(n);
|
||
|
|
if (isNaN(result)) {
|
||
|
|
throw new Error("Invalid number: " + n);
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
function lua_newtable(autoIndexList) {
|
||
|
|
var result = {str: {}, uints: [], floats: {}, bool: {}, objs: []};
|
||
|
|
for (var i = 1; i < arguments.length - 1; i += 2) {
|
||
|
|
var value = arguments[i + 1];
|
||
|
|
if (value == null) {
|
||
|
|
continue;
|
||
|
|
}
|
||
|
|
var key = arguments[i];
|
||
|
|
switch (typeof key) {
|
||
|
|
case "string":
|
||
|
|
result.str[key] = value;
|
||
|
|
break;
|
||
|
|
case "number":
|
||
|
|
if (key != key) {
|
||
|
|
throw new Error("Table index is NaN");
|
||
|
|
}
|
||
|
|
if (key > 0 && (key | 0) == key) {
|
||
|
|
result.uints[key - 1] = value;
|
||
|
|
} else {
|
||
|
|
result.floats[key] = value;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case "boolean":
|
||
|
|
result.bool[key] = value;
|
||
|
|
break;
|
||
|
|
case "object":
|
||
|
|
if (key == null) {
|
||
|
|
throw new Error("Table index is nil");
|
||
|
|
}
|
||
|
|
var bFound = value == null;
|
||
|
|
for (var i in result.objs) {
|
||
|
|
if (result.objs[i][0] === key) {
|
||
|
|
if (value == null) {
|
||
|
|
result.objs.splice(i, 1); // remove element [i]
|
||
|
|
} else {
|
||
|
|
bFound = true;
|
||
|
|
// modify/overwrite existing entry
|
||
|
|
// (could happen that same key is used twice in autoIndexList)
|
||
|
|
result.objs[i][1] = value;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!bFound) {
|
||
|
|
result.objs.push([key, value]); // add new entry
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
throw new Error("Unsupported type for table: " + (typeof key));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (autoIndexList) {
|
||
|
|
if (result.uints.length == 0) {
|
||
|
|
result.uints = autoIndexList;
|
||
|
|
} else {
|
||
|
|
i = autoIndexList.length;
|
||
|
|
while (i-- > 0) {
|
||
|
|
result.uints[i] = autoIndexList[i];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
}
|
||
|
|
function lua_newtable2(str) {
|
||
|
|
var str_copy = {};
|
||
|
|
for (var i in str) {
|
||
|
|
str_copy[i] = str[i];
|
||
|
|
}
|
||
|
|
return {str: str_copy, uints: [], floats: {}, bool: {}, objs: {}};
|
||
|
|
}
|
||
|
|
function lua_len(op) {
|
||
|
|
if (typeof op == "string") {
|
||
|
|
return op.length;
|
||
|
|
} else if (typeof op == "object" && op != null) {
|
||
|
|
if (op.length == null) {
|
||
|
|
var index = 0;
|
||
|
|
while (op.uints[index++] != null) {};
|
||
|
|
return op.length = index - 1;
|
||
|
|
} else {
|
||
|
|
return op.length;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
var h = op.metatable && op.metatable.str["__len"];
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("Length of <" + op + "> not supported");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_rawcall(func, args) {
|
||
|
|
try {
|
||
|
|
return func.apply(null, args);
|
||
|
|
} catch (e) {
|
||
|
|
if (e.constructor == ReturnValues) {
|
||
|
|
return e.vars;
|
||
|
|
}
|
||
|
|
// This breaks the stack on Chrome
|
||
|
|
// <http://code.google.com/p/chromium/issues/detail?id=60240>
|
||
|
|
throw e;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// could be replaced by lua_call(lua_tableget(table, key), args)
|
||
|
|
// but this gives better error messages
|
||
|
|
function lua_tablegetcall(table, key, args) {
|
||
|
|
var func = lua_tableget(table, key);
|
||
|
|
if (typeof func == "function") {
|
||
|
|
return lua_rawcall(func, args);
|
||
|
|
} else {
|
||
|
|
if (func == null) {
|
||
|
|
throw new Error("attempt to call field '" + key + "' (a nil value)");
|
||
|
|
}
|
||
|
|
var h = func.metatable && func.metatable.str["__call"];
|
||
|
|
if (h != null) {
|
||
|
|
return lua_rawcall(h, [func].concat(args));
|
||
|
|
} else {
|
||
|
|
throw new Error("Could not call " + func + " as function");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_call(func, args) {
|
||
|
|
if (typeof func == "function") {
|
||
|
|
return lua_rawcall(func, args);
|
||
|
|
} else {
|
||
|
|
if (func == null) {
|
||
|
|
throw new Error("attempt to call function (a nil value)");
|
||
|
|
}
|
||
|
|
var h = func.metatable && func.metatable.str["__call"];
|
||
|
|
if (h != null) {
|
||
|
|
return lua_rawcall(h, [func].concat(args));
|
||
|
|
} else {
|
||
|
|
throw new Error("Could not call " + func + " as function");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_mcall(obj, methodname, args) {
|
||
|
|
var func = lua_tableget(obj, methodname);
|
||
|
|
if (func == null) {
|
||
|
|
throw new Error("attempt to call method '" + methodname + "' (a nil value)");
|
||
|
|
}
|
||
|
|
return lua_call(func, [obj].concat(args));
|
||
|
|
}
|
||
|
|
function lua_eq(op1, op2) {
|
||
|
|
if (typeof op1 != typeof op2) {
|
||
|
|
if (op1 == null && op2 == null) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (op1 == op2) {
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
if (op1 == null || op2 == null) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
var h = op1.metatable && op1.metatable.str["__eq"];
|
||
|
|
if (h && h == (op2.metatable && op2.metatable.str["__eq"])) {
|
||
|
|
return lua_true(lua_rawcall(h, [op1, op2])[0]);
|
||
|
|
} else {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_lt(op1, op2) {
|
||
|
|
if (typeof op1 == "number" && typeof op2 == "number") {
|
||
|
|
return op1 < op2;
|
||
|
|
} else if (typeof op1 == "string" && typeof op2 == "string") {
|
||
|
|
// TODO: not sure how similar lua/javascript string comparison is
|
||
|
|
return op1 < op2;
|
||
|
|
} else {
|
||
|
|
var h = op1.metatable && op1.metatable.str["__lt"];
|
||
|
|
if (h && h == (op2.metatable && op2.metatable.str["__lt"])) {
|
||
|
|
return lua_true(lua_rawcall(h, [op1, op2])[0]);
|
||
|
|
} else {
|
||
|
|
throw new Error("Unable to compare " + op1 + " and " + op2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_lte(op1, op2) {
|
||
|
|
if (typeof op1 == "number" && typeof op2 == "number") {
|
||
|
|
return op1 <= op2;
|
||
|
|
} else if (typeof op1 == "string" && typeof op2 == "string") {
|
||
|
|
// TODO: not sure how similar lua/javascript string comparison is
|
||
|
|
return op1 <= op2;
|
||
|
|
} else {
|
||
|
|
var h = op1.metatable && op1.metatable.str["__le"];
|
||
|
|
if (h && h == (op2.metatable && op2.metatable.str["__le"])) {
|
||
|
|
return lua_true(lua_rawcall(h, [op1, op2])[0]);
|
||
|
|
} else {
|
||
|
|
var h = op1.metatable && op1.metatable.str["__lt"];
|
||
|
|
if (h && h == (op2.metatable && op2.metatable.str["__lt"])) {
|
||
|
|
return lua_not(lua_rawcall(h, [op2, op1])[0]);
|
||
|
|
} else {
|
||
|
|
throw new Error("Unable to compare " + op1 + " and " + op2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_unm(op) {
|
||
|
|
var o = parseFloat(op);
|
||
|
|
if (!isNaN(o)) {
|
||
|
|
return -o;
|
||
|
|
} else {
|
||
|
|
var h = op.metatable && op.metatable.str["__unm"];
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("Inverting <" + op + "> not supported");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_add(op1, op2) {
|
||
|
|
var o1 = parseFloat(op1), o2 = parseFloat(op2);
|
||
|
|
if (isNaN(o1) || isNaN(o2)) {
|
||
|
|
var h = (op1.metatable && op1.metatable.str["__add"]) || (op2.metatable && op2.metatable.str["__add"]);
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op1, op2])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("Adding <" + op1 + "> and <" + op2 + "> not supported");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return o1 + o2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_subtract(op1, op2) {
|
||
|
|
var o1 = parseFloat(op1), o2 = parseFloat(op2);
|
||
|
|
if (isNaN(o1) || isNaN(o2)) {
|
||
|
|
var h = (op1.metatable && op1.metatable.str["__sub"]) || (op2.metatable && op2.metatable.str["__sub"]);
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op1, op2])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("Subtracting <" + op1 + "> and <" + op2 + "> not supported");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return o1 - o2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_divide(op1, op2) {
|
||
|
|
var o1 = parseFloat(op1), o2 = parseFloat(op2);
|
||
|
|
if (isNaN(o1) || isNaN(o2)) {
|
||
|
|
var h = (op1.metatable && op1.metatable.str["__div"]) || (op2.metatable && op2.metatable.str["__div"]);
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op1, op2])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("Dividing <" + op1 + "> and <" + op2 + "> not supported");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return o1 / o2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_multiply(op1, op2) {
|
||
|
|
var o1 = parseFloat(op1), o2 = parseFloat(op2);
|
||
|
|
if (isNaN(o1) || isNaN(o2)) {
|
||
|
|
var h = (op1.metatable && op1.metatable.str["__mul"]) || (op2.metatable && op2.metatable.str["__mul"]);
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op1, op2])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("Multiplying <" + op1 + "> and <" + op2 + "> not supported");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return o1 * o2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_power(op1, op2) {
|
||
|
|
var o1 = parseFloat(op1), o2 = parseFloat(op2);
|
||
|
|
if (isNaN(o1) || isNaN(o2)) {
|
||
|
|
var h = (op1.metatable && op1.metatable.str["__pow"]) || (op2.metatable && op2.metatable.str["__pow"]);
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op1, op2])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("<" + op1 + "> to the power of <" + op2 + "> not supported");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
return Math.pow(o1, o2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_mod(op1, op2) {
|
||
|
|
var o1 = parseFloat(op1), o2 = parseFloat(op2);
|
||
|
|
if (isNaN(o1) || isNaN(o2)) {
|
||
|
|
var h = (op1.metatable && op1.metatable.str["__mod"]) || (op2.metatable && op2.metatable.str["__mod"]);
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op1, op2])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("Modulo <" + op1 + "> and <" + op2 + "> not supported");
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (o1 >= 0) {
|
||
|
|
if (o2 >= 0) {
|
||
|
|
return o1 % o2;
|
||
|
|
} else {
|
||
|
|
return (o2 + (o1 % o2)) % o2;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (o2 >= 0) {
|
||
|
|
return (o2 + (o1 % o2)) % o2;
|
||
|
|
} else {
|
||
|
|
return o1 % o2;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_rawget(table, key) {
|
||
|
|
switch (typeof key) {
|
||
|
|
case "string":
|
||
|
|
return table.str[key];
|
||
|
|
case "number":
|
||
|
|
if (key != key) {
|
||
|
|
throw new Error("Table index is NaN");
|
||
|
|
}
|
||
|
|
if (key > 0 && (key | 0) == key) {
|
||
|
|
return table.uints[key - 1];
|
||
|
|
} else {
|
||
|
|
return table.floats[key];
|
||
|
|
}
|
||
|
|
case "boolean":
|
||
|
|
return table.bool[key];
|
||
|
|
case "object":
|
||
|
|
if (key == null) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
for (var i in table.objs) {
|
||
|
|
if (table.objs[i][0] == key) {
|
||
|
|
return table.objs[i][1];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
throw new Error("Unsupported key for table: " + (typeof key));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_rawset(table, key, value) {
|
||
|
|
delete table.length;
|
||
|
|
switch (typeof key) {
|
||
|
|
case "string":
|
||
|
|
if (value == null) {
|
||
|
|
delete table.str[key];
|
||
|
|
} else {
|
||
|
|
table.str[key] = value;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case "number":
|
||
|
|
if (key != key) {
|
||
|
|
throw new Error("Table index is NaN");
|
||
|
|
}
|
||
|
|
if (key > 0 && (key | 0) == key) {
|
||
|
|
if (value == null) {
|
||
|
|
delete table.uints[key - 1];
|
||
|
|
} else {
|
||
|
|
table.uints[key - 1] = value;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
if (value == null) {
|
||
|
|
delete table.floats[key];
|
||
|
|
} else {
|
||
|
|
table.floats[key] = value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case "boolean":
|
||
|
|
if (value == null) {
|
||
|
|
delete table.bool[key];
|
||
|
|
} else {
|
||
|
|
table.bool[key] = value;
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
case "object":
|
||
|
|
if (key == null) {
|
||
|
|
throw new Error("Table index is nil");
|
||
|
|
}
|
||
|
|
var bFound = value == null;
|
||
|
|
for (var i in table.objs) {
|
||
|
|
if (table.objs[i][0] == key) {
|
||
|
|
if (value == null) {
|
||
|
|
table.objs.splice(i, 1); // remove element [i]
|
||
|
|
} else {
|
||
|
|
bFound = true;
|
||
|
|
table.objs[i][1] = value; // modifiy/overwrite existing entry
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (!bFound) {
|
||
|
|
table.objs.push([key, value]); // add new entry
|
||
|
|
}
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
throw new Error("Unsupported key for table: " + (typeof key));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_tabletoJson(data) {
|
||
|
|
let dataOut
|
||
|
|
//is table
|
||
|
|
if (typeof data === 'object') {
|
||
|
|
// both
|
||
|
|
if (Object.keys(data.str).length !== 0 && data.uints.length !== 0) {
|
||
|
|
dataOut = []
|
||
|
|
for (let i = 0; i < data.uints.length; i++) {
|
||
|
|
dataOut.push(lua_tabletoJson(data.uints[i]))
|
||
|
|
}
|
||
|
|
for (let k in data.str) {
|
||
|
|
if (data.str.hasOwnProperty(k)) {
|
||
|
|
// ignore keys
|
||
|
|
dataOut.push(lua_tabletoJson(data.str[k]))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
// obj
|
||
|
|
if (Object.keys(data.str).length !== 0) {
|
||
|
|
dataOut = {}
|
||
|
|
for (let k in data.str) {
|
||
|
|
if (data.str.hasOwnProperty(k)) {
|
||
|
|
dataOut[k] = lua_tabletoJson(data.str[k])
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// array
|
||
|
|
if (data.uints.length !== 0) {
|
||
|
|
dataOut = []
|
||
|
|
for (let i = 0; i < data.uints.length; i++) {
|
||
|
|
dataOut.push(lua_tabletoJson(data.uints[i]))
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
dataOut = data
|
||
|
|
}
|
||
|
|
return dataOut
|
||
|
|
}
|
||
|
|
function lua_tableget(table, key) {
|
||
|
|
if (table == null) {
|
||
|
|
throw new Error("attempt to index field '" + key + "' in a nil value");
|
||
|
|
}
|
||
|
|
if (typeof table == "object") {
|
||
|
|
var v = lua_rawget(table, key);
|
||
|
|
if (v != null) {
|
||
|
|
return v;
|
||
|
|
}
|
||
|
|
var h = table.metatable && table.metatable.str["__index"];
|
||
|
|
if (h == null) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
var h = table.metatable && table.metatable.str["__index"];
|
||
|
|
if (h == null) {
|
||
|
|
throw new Error("Unable to index key " + key + " from " + table);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (typeof h == "function") {
|
||
|
|
return lua_rawcall(h, [table, key])[0];
|
||
|
|
} else {
|
||
|
|
return lua_tableget(h, key);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_tableset(table, key, value) {
|
||
|
|
if (table == null) {
|
||
|
|
throw new Error("attempt to set field '" + key + "' in a nil value");
|
||
|
|
}
|
||
|
|
if (typeof table == "object") {
|
||
|
|
var v = lua_rawget(table, key);
|
||
|
|
if (v != null) {
|
||
|
|
lua_rawset(table, key, value);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
var h = table.metatable && table.metatable.str["__newindex"];
|
||
|
|
if (h == null) {
|
||
|
|
lua_rawset(table, key, value);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
var h = table.metatable && table.metatable.str["__newindex"];
|
||
|
|
if (h == null) {
|
||
|
|
throw new Error("Unable to set key " + key + " in table " + table);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
if (typeof h == "function") {
|
||
|
|
lua_rawcall(h, [table, key, value]);
|
||
|
|
} else {
|
||
|
|
lua_tableset(h, key, value);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_concat(op1, op2) {
|
||
|
|
if (typeof op1 == "number" && typeof op2 == "number") {
|
||
|
|
throw new Error("number concat not supported yet");
|
||
|
|
} else if ((typeof op1 == "string" || typeof op1 == "number") && (typeof op2 == "string" || typeof op2 == "number")) {
|
||
|
|
return op1 + op2;
|
||
|
|
} else {
|
||
|
|
var h = (op1.metatable && op1.metatable.str["__concat"]) || (op2.metatable && op2.metatable.str["__concat"]);
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [op1, op2])[0];
|
||
|
|
} else {
|
||
|
|
throw new Error("Unable to concat " + op1 + " and " + op2);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
function lua_tonumber(e, base) {
|
||
|
|
var type = typeof e;
|
||
|
|
if (type == "number") {
|
||
|
|
return e;
|
||
|
|
}
|
||
|
|
if (type == "string" && e.indexOf('0x') != -1) {
|
||
|
|
return parseInt(e, 16);
|
||
|
|
}
|
||
|
|
if (type != "string" || e.search("[^0-9\. -]") != -1) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
var num;
|
||
|
|
if (base === 10 || base == null) {
|
||
|
|
num = parseFloat(e);
|
||
|
|
} else {
|
||
|
|
num = parseInt(e, base);
|
||
|
|
}
|
||
|
|
if (isNaN(num)) {
|
||
|
|
num = null;
|
||
|
|
}
|
||
|
|
return num;
|
||
|
|
}
|
||
|
|
|
||
|
|
// core lua functions
|
||
|
|
function _ipairs_next(table, index) {
|
||
|
|
var entry;
|
||
|
|
entry = table.uints[index];
|
||
|
|
if (entry == null) {
|
||
|
|
return [null, null];
|
||
|
|
}
|
||
|
|
return [index + 1, entry];
|
||
|
|
}
|
||
|
|
var lua_libs = {};
|
||
|
|
var lua_core = {
|
||
|
|
"assert": function (value, message) {
|
||
|
|
if (arguments.length < 1) {
|
||
|
|
message = "assertion failed!";
|
||
|
|
}
|
||
|
|
if (value != null && value !== false) {
|
||
|
|
return [value];
|
||
|
|
} else {
|
||
|
|
throw new Error(message);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"collectgarbage": function () {},// no-op
|
||
|
|
"dofile": function () {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"error": function (message, level) {
|
||
|
|
// TODO: "level" is currently ignored
|
||
|
|
throw new Error(message);
|
||
|
|
},
|
||
|
|
"getfenv": function (func, table) {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"getmetatable": function (op) {
|
||
|
|
return [op.metatable && (op.metatable.str["__metatable"] || op.metatable)];
|
||
|
|
},
|
||
|
|
"ipairs": function (table) {
|
||
|
|
return [_ipairs_next, table, 0];
|
||
|
|
},
|
||
|
|
"load": function (func, chunkname) {
|
||
|
|
var script = "", chunk;
|
||
|
|
while ((chunk = func()) != null && chunk != "") {
|
||
|
|
script += chunk;
|
||
|
|
}
|
||
|
|
try {
|
||
|
|
return [lua_load(script, chunkname)];
|
||
|
|
} catch (e) {
|
||
|
|
return [null, e.message];
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"loadfile": function () {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"loadstring": function (string, chunkname) {
|
||
|
|
try {
|
||
|
|
return [lua_load(string, chunkname)];
|
||
|
|
} catch (e) {
|
||
|
|
return [null, e.message];
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"next": function () {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"pairs": function (table) {
|
||
|
|
var props = [], i;
|
||
|
|
for (i in table.str) {
|
||
|
|
props.push(i);
|
||
|
|
}
|
||
|
|
var j = table.uints.length;
|
||
|
|
while (j-- > 0) {
|
||
|
|
if (table.uints[j] != null) {
|
||
|
|
props.push(j + 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
for (i in table.floats) {
|
||
|
|
props.push(parseFloat(i));
|
||
|
|
}
|
||
|
|
for (i in table.bool) {
|
||
|
|
props.push(i === "true" ? true : false);
|
||
|
|
}
|
||
|
|
for (i in table.objs) {
|
||
|
|
props.push(table.objs[i][0]);
|
||
|
|
}
|
||
|
|
|
||
|
|
// okay, so I'm faking it here
|
||
|
|
// regardless of what key is given, this function will return the next value
|
||
|
|
// not sure how to do it the "right way" right now
|
||
|
|
i = 0;
|
||
|
|
return [function (table, key) {
|
||
|
|
var entry;
|
||
|
|
do {
|
||
|
|
if (i >= props.length) {
|
||
|
|
return [null, null];
|
||
|
|
}
|
||
|
|
key = props[i++];
|
||
|
|
entry = lua_rawget(table, key);
|
||
|
|
} while (entry == null);
|
||
|
|
return [key, entry];
|
||
|
|
}, table, null];
|
||
|
|
},
|
||
|
|
"pcall": function (func) {
|
||
|
|
try {
|
||
|
|
return [true].concat(func.apply(null, slice(arguments, 1)));
|
||
|
|
} catch (e) {
|
||
|
|
return [false, e.message];
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"print": lua_print,
|
||
|
|
"rawequal": function (op1, op2) {
|
||
|
|
return [(op1 == op2) || (op1 == null && op2 == null)];
|
||
|
|
},
|
||
|
|
"rawget": function (table, key) {
|
||
|
|
if (typeof table == "object" && table != null) {
|
||
|
|
return [lua_rawget(table, key)];
|
||
|
|
}
|
||
|
|
throw new Error("Unable to index key " + key + " from " + table);
|
||
|
|
},
|
||
|
|
"rawset": function (table, key, value) {
|
||
|
|
if (typeof table == "object" && table != null && key != null) {
|
||
|
|
lua_rawset(table, key, value);
|
||
|
|
return [table];
|
||
|
|
}
|
||
|
|
throw new Error("Unable set key " + key + " in " + table);
|
||
|
|
},
|
||
|
|
"select": function (n) {
|
||
|
|
if (n === "#") {
|
||
|
|
return [arguments.length - 1];
|
||
|
|
} else {
|
||
|
|
n = lua_assertfloat(n);
|
||
|
|
if (n >= 1) {
|
||
|
|
return slice(arguments, lua_assertfloat(n));
|
||
|
|
} else {
|
||
|
|
throw new Error("Index out of range");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"setfenv": function (func, table) {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"setmetatable": function (table, metatable) {
|
||
|
|
if (typeof table != "object" || table == null) {
|
||
|
|
throw new Error("table expected, got " + table);
|
||
|
|
}
|
||
|
|
if (metatable == null) {
|
||
|
|
delete table.metatable;
|
||
|
|
} else if (typeof metatable === "object") {
|
||
|
|
table.metatable = metatable;
|
||
|
|
} else {
|
||
|
|
throw new Error("table or nil expected, got " + metatable);
|
||
|
|
}
|
||
|
|
return [table]
|
||
|
|
},
|
||
|
|
"tonumber": function (e, base) {
|
||
|
|
return [lua_tonumber(e, base)];
|
||
|
|
},
|
||
|
|
"tostring": function (e) {
|
||
|
|
if (e == null) {
|
||
|
|
return ["nil"];
|
||
|
|
}
|
||
|
|
var h = e.metatable && e.metatable.str["__tostring"];
|
||
|
|
if (h) {
|
||
|
|
return lua_rawcall(h, [e]);
|
||
|
|
} else {
|
||
|
|
switch (typeof e) {
|
||
|
|
case "number":
|
||
|
|
case "boolean":
|
||
|
|
return [e.toString()];
|
||
|
|
case "string":
|
||
|
|
return [e];
|
||
|
|
case "object":
|
||
|
|
return ["table"];
|
||
|
|
case "function":
|
||
|
|
return ["function"];
|
||
|
|
default:
|
||
|
|
return ["nil"];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"type": function (v) {
|
||
|
|
switch (typeof v) {
|
||
|
|
case "number":
|
||
|
|
return ["number"];
|
||
|
|
case "string":
|
||
|
|
return ["string"];
|
||
|
|
case "boolean":
|
||
|
|
return ["boolean"];
|
||
|
|
case "function":
|
||
|
|
return ["function"];
|
||
|
|
case "object":
|
||
|
|
return [v === null ? "nil" : "table"];
|
||
|
|
case "undefined":
|
||
|
|
return ["nil"];
|
||
|
|
default:
|
||
|
|
throw new Error("Unexpected value of type " + typeof v);
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"unpack": function (list, i, j) {
|
||
|
|
if (list.length != null) {
|
||
|
|
j = list.length;
|
||
|
|
} else {
|
||
|
|
j = 0;
|
||
|
|
while (list.uints[j++] != null) {};
|
||
|
|
list.length = --j;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (i == null || i < 1) {
|
||
|
|
i = 1;
|
||
|
|
}
|
||
|
|
if (j == null) {
|
||
|
|
j = list.length;
|
||
|
|
}
|
||
|
|
throw new ReturnValues(list.uints.slice(i - 1, j));
|
||
|
|
},
|
||
|
|
"_VERSION": "Lua 5.1",
|
||
|
|
"xpcall": function () {
|
||
|
|
not_supported();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// coroutine
|
||
|
|
var _lua_coroutine = lua_libs["coroutine"] = {};
|
||
|
|
_lua_coroutine["resume"] = _lua_coroutine["running"] = _lua_coroutine["status"] = _lua_coroutine["wrap"] = _lua_coroutine["yield"] = _lua_coroutine["create"] = function () {
|
||
|
|
not_supported();
|
||
|
|
};
|
||
|
|
|
||
|
|
// debug
|
||
|
|
var _lua_debug = lua_libs["debug"] = {
|
||
|
|
"getmetatable": function (obj) {
|
||
|
|
return [obj.metatable];
|
||
|
|
}
|
||
|
|
};
|
||
|
|
_lua_debug["traceback"] = _lua_debug["getfenv"] = _lua_debug["gethook"] = _lua_debug["getinfo"] = _lua_debug["getlocal"] = _lua_debug["getregistry"] = _lua_debug["getupvalue"] = _lua_debug["setfenv"] = _lua_debug["sethook"] = _lua_debug["setlocal"] = _lua_debug["setupvalue"] = _lua_debug["debug"] = function () {
|
||
|
|
not_supported();
|
||
|
|
};
|
||
|
|
|
||
|
|
// io
|
||
|
|
var _lua_write_buffer = "";
|
||
|
|
var _lua_io = lua_libs["io"] = {
|
||
|
|
"write": function () {
|
||
|
|
_lua_write_buffer += Array.prototype.join.call(arguments, "");
|
||
|
|
var lines = _lua_write_buffer.split("\n");
|
||
|
|
while (lines.length > 1) {
|
||
|
|
_lua_print(lines.shift());
|
||
|
|
}
|
||
|
|
_lua_write_buffer = lines[0];
|
||
|
|
return [];
|
||
|
|
},
|
||
|
|
"flush": function () {},// no-op
|
||
|
|
"stderr": null,
|
||
|
|
"stdin": null,
|
||
|
|
"stdout": null
|
||
|
|
};
|
||
|
|
_lua_io["close"] = _lua_io["input"] = _lua_io["lines"] = _lua_io["output"] = _lua_io["popen"] = _lua_io["read"] = _lua_io["tmpfile"] = _lua_io["type"] = _lua_io["open"] = function () {
|
||
|
|
not_supported();
|
||
|
|
};
|
||
|
|
|
||
|
|
// math
|
||
|
|
var _lua_randmax = 0x100000000;
|
||
|
|
var _lua_randseed = (Math.random() * _lua_randmax) & (_lua_randmax - 1);
|
||
|
|
lua_libs["math"] = {
|
||
|
|
"abs": function (x) {
|
||
|
|
return [Math.abs(x)];
|
||
|
|
},
|
||
|
|
"acos": function (x) {
|
||
|
|
return [Math.acos(x)];
|
||
|
|
},
|
||
|
|
"asin": function (x) {
|
||
|
|
return [Math.asin(x)];
|
||
|
|
},
|
||
|
|
"atan": function (x) {
|
||
|
|
return [Math.atan(x)];
|
||
|
|
},
|
||
|
|
"atan2": function (y, x) {
|
||
|
|
return [Math.atan2(y, x)];
|
||
|
|
},
|
||
|
|
"ceil": function (x) {
|
||
|
|
return [Math.ceil(x)];
|
||
|
|
},
|
||
|
|
"cos": function (x) {
|
||
|
|
return [Math.cos(x)];
|
||
|
|
},
|
||
|
|
"cosh": function (x) {
|
||
|
|
return [(Math.exp(x) + Math.exp(-x)) / 2];
|
||
|
|
},
|
||
|
|
"deg": function (x) {
|
||
|
|
return [x * (180 / Math.PI)];
|
||
|
|
},
|
||
|
|
"exp": function (x) {
|
||
|
|
return [Math.exp(x)];
|
||
|
|
},
|
||
|
|
"floor": function (x) {
|
||
|
|
return [Math.floor(x)];
|
||
|
|
},
|
||
|
|
"fmod": function (x, y) {
|
||
|
|
return [x % y];
|
||
|
|
},
|
||
|
|
"frexp": function (m, e) {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"huge": Infinity,
|
||
|
|
"ldexp": function (m, e) {
|
||
|
|
return [m * Math.pow(2, e)];
|
||
|
|
},
|
||
|
|
"log": function (x) {
|
||
|
|
return [Math.log(x)];
|
||
|
|
},
|
||
|
|
"log10": function (x) {
|
||
|
|
return [Math.log(x) / Math.LN10];
|
||
|
|
},
|
||
|
|
"max": function () {
|
||
|
|
return [Math.max.apply(null, arguments)];
|
||
|
|
},
|
||
|
|
"min": function () {
|
||
|
|
return [Math.min.apply(null, arguments)];
|
||
|
|
},
|
||
|
|
"modf": function (x) {
|
||
|
|
var frac = x % 1;
|
||
|
|
return [x - frac, frac];
|
||
|
|
},
|
||
|
|
"pi": Math.PI,
|
||
|
|
"pow": function (x, y) {
|
||
|
|
return [Math.pow(x, y)];
|
||
|
|
},
|
||
|
|
"rad": function (x) {
|
||
|
|
return [x * (Math.PI / 180)];
|
||
|
|
},
|
||
|
|
"sin": function (x) {
|
||
|
|
return [Math.sin(x)];
|
||
|
|
},
|
||
|
|
"sinh": function (x) {
|
||
|
|
return [(Math.exp(x) - Math.exp(-x)) / 2];
|
||
|
|
},
|
||
|
|
"sqrt": function (x) {
|
||
|
|
return [Math.sqrt(x)];
|
||
|
|
},
|
||
|
|
"tan": function (x) {
|
||
|
|
return [Math.tan(x)];
|
||
|
|
},
|
||
|
|
"tanh": function (x) {
|
||
|
|
var a = Math.exp(x);
|
||
|
|
var b = Math.exp(-x);
|
||
|
|
return [(a - b) / (a + b)];
|
||
|
|
},
|
||
|
|
"random": function (m, n) {
|
||
|
|
// Based on the 32 bit mix function found here:
|
||
|
|
// http://www.concentric.net/~Ttwang/tech/inthash.htm
|
||
|
|
_lua_randseed = ~_lua_randseed + (_lua_randseed << 15); // _lua_randseed = (_lua_randseed << 15) - _lua_randseed - 1;
|
||
|
|
_lua_randseed = _lua_randseed ^ (_lua_randseed >>> 12);
|
||
|
|
_lua_randseed = _lua_randseed + (_lua_randseed << 2);
|
||
|
|
_lua_randseed = _lua_randseed ^ (_lua_randseed >>> 4);
|
||
|
|
_lua_randseed = _lua_randseed * 2057; // _lua_randseed = (_lua_randseed + (_lua_randseed << 3)) + (_lua_randseed << 11);
|
||
|
|
_lua_randseed = _lua_randseed ^ (_lua_randseed >>> 16);
|
||
|
|
|
||
|
|
var val;
|
||
|
|
if (_lua_randseed < 0) {
|
||
|
|
val = ((_lua_randseed + _lua_randmax) / _lua_randmax) % 1;
|
||
|
|
} else {
|
||
|
|
val = (_lua_randseed / _lua_randmax) % 1;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (arguments.length >= 2) {
|
||
|
|
m = m | 0;
|
||
|
|
n = n | 0;
|
||
|
|
if (m >= n) {
|
||
|
|
throw new Error("Invalid range");
|
||
|
|
}
|
||
|
|
return [Math.floor(val * (n - m + 1) + m)];
|
||
|
|
} else if (arguments.length == 1) {
|
||
|
|
m = m | 0;
|
||
|
|
return [Math.floor(val * m + 1)];
|
||
|
|
} else {
|
||
|
|
return [val];
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"randomseed": function (x) {
|
||
|
|
_lua_randseed = x & (_lua_randmax - 1);
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// os
|
||
|
|
// TODO: this should be different for each script, I think?
|
||
|
|
var _lua_clock_start = (new Date()).getTime() / 1000;
|
||
|
|
lua_libs["os"] = {
|
||
|
|
"clock": function () {
|
||
|
|
// This function is supposed to return the time the script has been executing
|
||
|
|
// not the time since it started, but I don't know of a way to do this.
|
||
|
|
return [(((new Date()).getTime()) / 1000) - _lua_clock_start];
|
||
|
|
},
|
||
|
|
"date": function (format, time) {
|
||
|
|
// TODO
|
||
|
|
return ["[" + time + "]" + format];
|
||
|
|
},
|
||
|
|
"difftime": function (t2, t1) {
|
||
|
|
return [t2 - t1];
|
||
|
|
},
|
||
|
|
"execute": function () {
|
||
|
|
return 0;// all commands fail
|
||
|
|
},
|
||
|
|
"exit": function () {
|
||
|
|
//window.close();
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"getenv": function (varname) {
|
||
|
|
return [null];
|
||
|
|
},
|
||
|
|
"remove": function () {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"rename": function () {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"setlocale": function () {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"time": function (table) {
|
||
|
|
if (table) {
|
||
|
|
not_supported();
|
||
|
|
} else {
|
||
|
|
return [Math.floor(new Date().getTime() / 1000)];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// package
|
||
|
|
var lua_packages = lua_newtable();
|
||
|
|
function lua_createmodule(G, name, options) {
|
||
|
|
var t = lua_tableget(lua_packages, name) || lua_tableget(G, name) || lua_newtable();
|
||
|
|
lua_tableset(G, name, t);
|
||
|
|
lua_tableset(lua_packages, name, t);
|
||
|
|
lua_tableset(t, "_NAME", name);
|
||
|
|
lua_tableset(t, "_M", t);
|
||
|
|
lua_tableset(t, "_PACKAGE", name.split(".").slice(0, -1).join("."));
|
||
|
|
|
||
|
|
for (var i = 0; i < options.length; i++) {
|
||
|
|
lua_call(options[i], [t]);
|
||
|
|
}
|
||
|
|
return t;
|
||
|
|
}
|
||
|
|
function lua_module(name) {
|
||
|
|
var t = lua_tableget(lua_packages, name);
|
||
|
|
if (t == null) {
|
||
|
|
throw new Error("Module " + name + " not found. Module must be loaded before use.");
|
||
|
|
}
|
||
|
|
return t;
|
||
|
|
}
|
||
|
|
function lua_require(G, name) {
|
||
|
|
var t = lua_module(name);
|
||
|
|
var pkg = G;
|
||
|
|
var names = name.split(".");
|
||
|
|
for (var i = 0; i < names.length - 1; i++) {
|
||
|
|
if (!lua_tableget(pkg, names[i])) {
|
||
|
|
var newPkg = lua_newtable();
|
||
|
|
lua_tableset(pkg, names[i], newPkg);
|
||
|
|
pkg = newPkg;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
lua_tableset(pkg, names[names.length - 1], t);
|
||
|
|
return t;
|
||
|
|
}
|
||
|
|
lua_libs["package"] = {
|
||
|
|
"path": "",
|
||
|
|
"cpath": "",
|
||
|
|
"loaded": lua_packages,
|
||
|
|
"loaders": lua_newtable(),// not used
|
||
|
|
"preload": lua_newtable(),// not used
|
||
|
|
"loadlib": function () {
|
||
|
|
not_supported();
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
function lua_pattern_to_regex(pattern) {
|
||
|
|
var notsupportedPatterns = [
|
||
|
|
// hyphen quantifier
|
||
|
|
/%[aAcCdDgGlLpPsSuUwW]-/g, // after a chracter class
|
||
|
|
/(]|\))-/g, // after a set or parenthesis
|
||
|
|
/[^%]-(\)|%)/g, // not escaped, before a parenthesis or a character class
|
||
|
|
|
||
|
|
/%(g|G)/g, // all printable characters except space.
|
||
|
|
|
||
|
|
/(^[^%]+%b|%b.{2}.+$)/g, // a balanced pattern with something before or after it
|
||
|
|
|
||
|
|
/%[0-9]{1}/g, // capture index
|
||
|
|
];
|
||
|
|
|
||
|
|
for (var i in notsupportedPatterns) {
|
||
|
|
if (pattern.search(notsupportedPatterns[i]) != -1) {
|
||
|
|
not_supported();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
var replacements = {
|
||
|
|
"%f\\[([^\\]]+)\\]": "[^$1]{1}[$1]{1}", // frontier pattern
|
||
|
|
|
||
|
|
// character classes
|
||
|
|
"%a": "[a-zA-Z\u00C0-\u017F]", // all letters with accented characters À to ſ (shouldn't the down limit be much lower ?)
|
||
|
|
"%A": "[^a-zA-Z\u00C0-\u017F]",
|
||
|
|
|
||
|
|
"%c": "[\u0000-\u001F]", // Control characters
|
||
|
|
"%C": "[^\u0000-\u001F]",
|
||
|
|
|
||
|
|
"%d": "\\d", // all digit
|
||
|
|
"%D": "\\D",
|
||
|
|
|
||
|
|
"%l": "[a-z\u00E0-\u00FF]", // lowercase letters + à to ÿ (below character 00FF, upper case and lowercase characters are mixed)
|
||
|
|
"%L": "[^a-z\u00E0-\u00FF]",
|
||
|
|
|
||
|
|
"%p": "[,\?;\.:/\\!\(\)\[\]\{\}\"'#|%$`^@~&+*<>-]", // all punctuation
|
||
|
|
"%P": "[^,\?;\.:/\\!\(\)\[\]\{\}\"'#|%$`^@~&+*<>-]",
|
||
|
|
|
||
|
|
"%s": "\\s", // all space characters
|
||
|
|
"%S": "\\S",
|
||
|
|
|
||
|
|
"%u": "[A-Z\u00C0-\u00DF]", // uppercase letter + À to ß
|
||
|
|
"%U": "[^A-Z\u00C0-\u00DF]",
|
||
|
|
|
||
|
|
"%w": "\\w", // all alphanum characters
|
||
|
|
"%W": "\\W",
|
||
|
|
|
||
|
|
// escape special characters
|
||
|
|
"%\\.": "\\.",
|
||
|
|
"%\\^": "\\^",
|
||
|
|
"%\\$": "\\$",
|
||
|
|
"%\\(": "\\(",
|
||
|
|
"%\\)": "\\)",
|
||
|
|
"%\\[": "\\[",
|
||
|
|
"%\\]": "\\]",
|
||
|
|
"%\\*": "\\*",
|
||
|
|
"%\\+": "\\+",
|
||
|
|
"%\\-": "\\-",
|
||
|
|
"%\\?": "\\?",
|
||
|
|
"%%": "%",
|
||
|
|
};
|
||
|
|
|
||
|
|
for (var luaExp in replacements) {
|
||
|
|
pattern = pattern.replace(new RegExp(luaExp, "g"), replacements[luaExp]);
|
||
|
|
}
|
||
|
|
|
||
|
|
return pattern;
|
||
|
|
}
|
||
|
|
|
||
|
|
function get_balanced_match(s, pattern) {
|
||
|
|
var match = pattern.search(/%b.{1}.{1}/); // lua_pattern_to_regex() will leave balanced pattern untouched in the returned regex
|
||
|
|
if (match !== -1) {
|
||
|
|
var startChar = pattern[2];
|
||
|
|
var endChar = pattern[3];
|
||
|
|
var level = -1;
|
||
|
|
var startIndex = -1;
|
||
|
|
var startIndexes = [];
|
||
|
|
var endIndex = -1;
|
||
|
|
|
||
|
|
for (var i in s) {
|
||
|
|
i = parseInt(i);
|
||
|
|
var _char = s[i];
|
||
|
|
if (_char === startChar) {
|
||
|
|
startIndexes.push(i);
|
||
|
|
if (level < 0) {
|
||
|
|
startIndex = i;
|
||
|
|
level = 0; // in case one or more endChar were encountered first
|
||
|
|
}
|
||
|
|
level++;
|
||
|
|
} else if (_char === endChar) {
|
||
|
|
level--;
|
||
|
|
endIndex = i;
|
||
|
|
if (level === 0) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if (level > 0) { // there was more startChar than endChar
|
||
|
|
startIndex = startIndexes[level];
|
||
|
|
}
|
||
|
|
if (startIndex >= 0 && endIndex >= 0) {
|
||
|
|
return s.substring(startIndex, endIndex + 1);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
// string
|
||
|
|
lua_libs["string"] = {
|
||
|
|
"byte": function (s, i, j) {
|
||
|
|
s = check_string(s);
|
||
|
|
i = lua_tonumber(i);
|
||
|
|
j = lua_tonumber(j);
|
||
|
|
if (i == null) {
|
||
|
|
i = 1;
|
||
|
|
}
|
||
|
|
if (j == null) {
|
||
|
|
j = i;
|
||
|
|
}
|
||
|
|
i--;
|
||
|
|
j--;
|
||
|
|
var result = [];
|
||
|
|
while (i >= 0 && i <= j && i < s.length) {
|
||
|
|
result.push(s.charCodeAt(i++));
|
||
|
|
}
|
||
|
|
return result;
|
||
|
|
},
|
||
|
|
"char": function () {
|
||
|
|
return [String.fromCharCode.apply(null, arguments)];
|
||
|
|
},
|
||
|
|
"dump": function (func) {
|
||
|
|
not_supported();
|
||
|
|
},
|
||
|
|
"find": function (s, pattern, index, plain) {
|
||
|
|
s = check_string(s);
|
||
|
|
index = lua_tonumber(index);
|
||
|
|
if (index == null) {
|
||
|
|
index = 1;
|
||
|
|
} else if (index < 0) {
|
||
|
|
index = s.length + index;
|
||
|
|
}
|
||
|
|
index--; // -1 because Lua's arrays index starts at 1 instead of 0
|
||
|
|
s = s.substr(index);
|
||
|
|
|
||
|
|
if (plain == null || plain === false) {
|
||
|
|
pattern = lua_pattern_to_regex(pattern);
|
||
|
|
var match = get_balanced_match(s, pattern);
|
||
|
|
if (match !== null) {
|
||
|
|
pattern = match;
|
||
|
|
} else {
|
||
|
|
var matches = s.match(pattern);
|
||
|
|
if (matches !== null) {
|
||
|
|
pattern = matches[0];
|
||
|
|
} else {
|
||
|
|
return [null];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// pattern is now the matched string
|
||
|
|
}
|
||
|
|
|
||
|
|
var start = s.indexOf(pattern);
|
||
|
|
var returnValues = [null];
|
||
|
|
if (start != -1) {
|
||
|
|
returnValues = [start + index + 1, start + index + pattern.length];
|
||
|
|
if (matches != null && matches[1] != null) { // string.find() returns the capture(s) (if any) after the indexes
|
||
|
|
returnValues = returnValues.concat(matches.slice(1));
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return returnValues;
|
||
|
|
},
|
||
|
|
"format": function () {
|
||
|
|
// sprintf.js, forked to match Lua's string.format() behavior and for use in lua.js
|
||
|
|
/* Copyright (c) 2007-2013, Alexandru Marasteanu <hello [at) alexei (dot] ro>
|
||
|
|
All rights reserved.
|
||
|
|
|
||
|
|
Redistribution and use in source and binary forms, with or without
|
||
|
|
modification, are permitted provided that the following conditions are met:
|
||
|
|
* Redistributions of source code must retain the above copyright
|
||
|
|
notice, this list of conditions and the following disclaimer.
|
||
|
|
* Redistributions in binary form must reproduce the above copyright
|
||
|
|
notice, this list of conditions and the following disclaimer in the
|
||
|
|
documentation and/or other materials provided with the distribution.
|
||
|
|
* Neither the name of this software nor the names of its contributors may be
|
||
|
|
used to endorse or promote products derived from this software without
|
||
|
|
specific prior written permission.
|
||
|
|
|
||
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||
|
|
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||
|
|
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||
|
|
DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
|
||
|
|
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||
|
|
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||
|
|
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||
|
|
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||
|
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
||
|
|
var sprintf = function() {
|
||
|
|
if (!sprintf.cache.hasOwnProperty(arguments[0])) {
|
||
|
|
sprintf.cache[arguments[0]] = sprintf.parse(arguments[0]);
|
||
|
|
}
|
||
|
|
return sprintf.format.call(null, sprintf.cache[arguments[0]], arguments);
|
||
|
|
};
|
||
|
|
sprintf.cache = {};
|
||
|
|
|
||
|
|
sprintf.format = function(parse_tree, argv) {
|
||
|
|
var cursor = 1;
|
||
|
|
var tree_length = parse_tree.length;
|
||
|
|
var node_type = '';
|
||
|
|
var arg;
|
||
|
|
var output = [];
|
||
|
|
var i;
|
||
|
|
var k;
|
||
|
|
var match;
|
||
|
|
var pad;
|
||
|
|
var pad_character;
|
||
|
|
var pad_length;
|
||
|
|
for (i = 0; i < tree_length; i++) {
|
||
|
|
node_type = get_type(parse_tree[i]);
|
||
|
|
if (node_type === 'string') {
|
||
|
|
output.push(parse_tree[i]);
|
||
|
|
} else if (node_type === 'array') {
|
||
|
|
match = parse_tree[i]; // convenience purposes only
|
||
|
|
if (match[2]) { // keyword argument
|
||
|
|
arg = argv[cursor];
|
||
|
|
for (k = 0; k < match[2].length; k++) {
|
||
|
|
if (!arg.hasOwnProperty(match[2][k])) {
|
||
|
|
throw new Error('[string.format()] property "'+match[2][k]+'" does not exist');
|
||
|
|
}
|
||
|
|
arg = arg[match[2][k]];
|
||
|
|
}
|
||
|
|
} else if (match[1]) { // positional argument (explicit)
|
||
|
|
arg = argv[match[1]];
|
||
|
|
} else { // positional argument (implicit)
|
||
|
|
arg = argv[cursor++];
|
||
|
|
}
|
||
|
|
|
||
|
|
if (/[^sq]/.test(match[8]) && (get_type(arg) != 'number')) {
|
||
|
|
throw new Error('[string.format()] expecting number but found '+get_type(arg));
|
||
|
|
}
|
||
|
|
switch (match[8]) {
|
||
|
|
case 'c':
|
||
|
|
arg = String.fromCharCode(arg);
|
||
|
|
break;
|
||
|
|
case 'i':
|
||
|
|
case 'd': // int
|
||
|
|
arg = parseInt(arg, 10);
|
||
|
|
break;
|
||
|
|
case 'e':
|
||
|
|
arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(6);
|
||
|
|
break;
|
||
|
|
case 'E':
|
||
|
|
arg = match[7] ? arg.toExponential(match[7]).toUpperCase() : arg.toExponential(6).toUpperCase();
|
||
|
|
break;
|
||
|
|
case 'f': // float
|
||
|
|
arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg).toFixed(6);
|
||
|
|
break;
|
||
|
|
case 'g':
|
||
|
|
case 'G':
|
||
|
|
arg = match[7] ? parseFloat(arg).toFixed(match[7] - 1) : parseFloat(arg).toFixed(5);
|
||
|
|
// in practice, g or G always return a float with 1 less digits after the coma than asked for (by default it's 5 instead of 6)
|
||
|
|
break;
|
||
|
|
case 'o': // octal
|
||
|
|
if (arg < 0) {
|
||
|
|
arg = 0xFFFFFFFF + arg + 1;
|
||
|
|
}
|
||
|
|
arg = arg.toString(8);
|
||
|
|
break;
|
||
|
|
case 'u': // unsigned integer
|
||
|
|
arg = arg >>> 0;
|
||
|
|
break;
|
||
|
|
case 'x': // hexadecimal
|
||
|
|
if (arg < 0) {
|
||
|
|
arg = 0xFFFFFFFF + arg + 1;
|
||
|
|
}
|
||
|
|
arg = arg.toString(16);
|
||
|
|
break;
|
||
|
|
case 'X':
|
||
|
|
if (arg < 0) {
|
||
|
|
arg = 0xFFFFFFFF + arg + 1;
|
||
|
|
}
|
||
|
|
arg = arg.toString(16).toUpperCase();
|
||
|
|
break;
|
||
|
|
case 'q':
|
||
|
|
arg = '"'+((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg)+'"';
|
||
|
|
break;
|
||
|
|
case 's':
|
||
|
|
arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg);
|
||
|
|
break;
|
||
|
|
default:
|
||
|
|
not_supported();
|
||
|
|
}
|
||
|
|
if (/[eE]/.test(match[8])) {
|
||
|
|
//make the exponent (exp[2]) always at least 3 digit (ie : 3.14E+003)
|
||
|
|
var exp = /^(.+\+)(\d+)$/.exec(arg);
|
||
|
|
if (exp != null) {
|
||
|
|
if (exp[2].length == 1) {
|
||
|
|
arg = exp[1]+"00"+exp[2];
|
||
|
|
} else if (exp[2].length == 2) {
|
||
|
|
arg = exp[1]+"0"+exp[2];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
arg = (/[dieEf]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
|
||
|
|
pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
|
||
|
|
pad_length = match[6] - String(arg).length;
|
||
|
|
pad = match[6] ? str_repeat(pad_character, pad_length) : '';
|
||
|
|
output.push(match[5] ? arg + pad : pad + arg);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return output.join('');
|
||
|
|
};
|
||
|
|
|
||
|
|
sprintf.parse = function(fmt) { // fmt = format string
|
||
|
|
var _fmt = fmt;
|
||
|
|
var match = [];
|
||
|
|
var parse_tree = [];
|
||
|
|
var arg_names = 0;
|
||
|
|
while (_fmt) {
|
||
|
|
// \x25 = %
|
||
|
|
if ((match = /^[^\x25]+/.exec(_fmt)) !== null) { // no % found
|
||
|
|
parse_tree.push(match[0]);
|
||
|
|
} else if ((match = /^\x25{2}/.exec(_fmt)) !== null) { // 2 consecutive % found
|
||
|
|
parse_tree.push('%');
|
||
|
|
} else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([cdeEfgGiouxXqs])/.exec(_fmt)) !== null) {
|
||
|
|
if (match[2]) {
|
||
|
|
arg_names |= 1;
|
||
|
|
var field_list = [];
|
||
|
|
var replacement_field = match[2];
|
||
|
|
var field_match = [];
|
||
|
|
if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
|
||
|
|
field_list.push(field_match[1]);
|
||
|
|
while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
|
||
|
|
if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
|
||
|
|
field_list.push(field_match[1]);
|
||
|
|
} else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
|
||
|
|
field_list.push(field_match[1]);
|
||
|
|
} else {
|
||
|
|
throw new Error('[string.format()] No field_match found in replacement_field 1.');
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
throw new Error('[string.format()] No field_match found in replacement_field 2.');
|
||
|
|
}
|
||
|
|
match[2] = field_list;
|
||
|
|
} else {
|
||
|
|
arg_names |= 2;
|
||
|
|
}
|
||
|
|
if (arg_names === 3) {
|
||
|
|
throw new Error('[string.format()] mixing positional and named placeholders is not (yet) supported');
|
||
|
|
}
|
||
|
|
parse_tree.push(match);
|
||
|
|
} else {
|
||
|
|
throw new Error('[string.format()] Format string "'+fmt+'" not recognized.');
|
||
|
|
}
|
||
|
|
_fmt = _fmt.substring(match[0].length);
|
||
|
|
}
|
||
|
|
return parse_tree;
|
||
|
|
};
|
||
|
|
|
||
|
|
function get_type(variable) {
|
||
|
|
var type = typeof variable;
|
||
|
|
if (type == "object" && Object.prototype.toString.call(variable) == "[object Array]") {
|
||
|
|
type = "array";
|
||
|
|
}
|
||
|
|
return type;
|
||
|
|
}
|
||
|
|
|
||
|
|
function str_repeat(input, multiplier) {
|
||
|
|
for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
|
||
|
|
return output.join('');
|
||
|
|
}
|
||
|
|
|
||
|
|
if (arguments.length > 0) {
|
||
|
|
arguments[0] = check_string(arguments[0]);
|
||
|
|
}
|
||
|
|
return [sprintf.apply(this, arguments)];
|
||
|
|
},
|
||
|
|
"gmatch": function (s, pattern) {
|
||
|
|
var lua_gmatch_next = function(data) {
|
||
|
|
var match = get_balanced_match(data.s, data.pattern);
|
||
|
|
if (match === null) {
|
||
|
|
var matches = data.s.match(data.pattern);
|
||
|
|
if (matches === null) {
|
||
|
|
return [null];
|
||
|
|
} else {
|
||
|
|
if (matches[1] != null) { // if there was a capture, match[0] is the whole matched expression, match[1] the first capture
|
||
|
|
match = matches[1];
|
||
|
|
} else {
|
||
|
|
match = matches[0];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
data.s = data.s.substr(data.s.search(match) + match.length);
|
||
|
|
return [match];
|
||
|
|
};
|
||
|
|
|
||
|
|
// an object is used to keep the modifs to the string accross calls to lua_gmatch_next()
|
||
|
|
return [lua_gmatch_next, {"s":check_string(s), "pattern":lua_pattern_to_regex(pattern)}];
|
||
|
|
},
|
||
|
|
"gsub": function (s, pattern, replacement, n) {
|
||
|
|
s = check_string(s);
|
||
|
|
n = lua_tonumber(n);
|
||
|
|
|
||
|
|
pattern = lua_pattern_to_regex(pattern);
|
||
|
|
var regex = new RegExp(pattern);
|
||
|
|
|
||
|
|
var replacementCount = 0;
|
||
|
|
var replacementType = typeof replacement;
|
||
|
|
if (replacementType == "string") { // replacement can be a function
|
||
|
|
replacement = replacement.replace(/%([0-9]+)/g, "$$$1");
|
||
|
|
}
|
||
|
|
|
||
|
|
var newS = "";
|
||
|
|
var processMatch = function(match) {
|
||
|
|
var matchEndIndex = s.search(regex) + match.length;
|
||
|
|
var matchChunk = s.substr(0, matchEndIndex);
|
||
|
|
var newMatchChunk = "";
|
||
|
|
|
||
|
|
if (replacementType == "string") {
|
||
|
|
newMatchChunk = matchChunk.replace(regex, replacement);
|
||
|
|
} else if (replacementType == "function") {
|
||
|
|
var result = null;
|
||
|
|
// match is the whole expression matched by the pattern, now get captures
|
||
|
|
var matches = match.match(pattern); // not global to get the captures !
|
||
|
|
|
||
|
|
if (matches[1] != null) {
|
||
|
|
matches = matches.slice(1);
|
||
|
|
result = replacement.apply(null, matches)[0];
|
||
|
|
} else {
|
||
|
|
result = replacement(match)[0]; // the function always returns an array
|
||
|
|
}
|
||
|
|
|
||
|
|
if (result == null) {
|
||
|
|
newMatchChunk = matchChunk;
|
||
|
|
} else {
|
||
|
|
newMatchChunk = matchChunk.replace(regex, result);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
newS += newMatchChunk;
|
||
|
|
s = s.substr(matchEndIndex);
|
||
|
|
|
||
|
|
replacementCount++;
|
||
|
|
if (n !== null && replacementCount >= n) {
|
||
|
|
return false; // break
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
};
|
||
|
|
|
||
|
|
var match = get_balanced_match(s, pattern);
|
||
|
|
if (match !== null) {
|
||
|
|
var startChar = pattern[2];
|
||
|
|
var endChar = pattern[3];
|
||
|
|
// escape start and end char if they are special regex chars
|
||
|
|
var specialChars = ["[","]","(",")","{","}"]; // in this context it's not necessarily usefull to add others characters
|
||
|
|
if (specialChars.indexOf(startChar) !== -1) {
|
||
|
|
startChar = "\\"+startChar;
|
||
|
|
}
|
||
|
|
if (specialChars.indexOf(endChar) !== -1) {
|
||
|
|
endChar = "\\"+endChar;
|
||
|
|
}
|
||
|
|
|
||
|
|
do {
|
||
|
|
match = get_balanced_match(s, pattern);
|
||
|
|
if (match === null) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
regex = match.replace(new RegExp(startChar, "g"), startChar);
|
||
|
|
regex = regex.replace(new RegExp(endChar, "g"), endChar);
|
||
|
|
regex = new RegExp(regex);
|
||
|
|
} while (processMatch(match));
|
||
|
|
} else {
|
||
|
|
var matches = s.match(new RegExp(pattern , 'g'));
|
||
|
|
|
||
|
|
for (var i in matches) {
|
||
|
|
if (!processMatch(matches[i])) {
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
newS += s;
|
||
|
|
return [newS, replacementCount];
|
||
|
|
},
|
||
|
|
"len": function (s) {
|
||
|
|
return [check_string(s).length];
|
||
|
|
},
|
||
|
|
"lower": function (s) {
|
||
|
|
return [check_string(s).toLowerCase()];
|
||
|
|
},
|
||
|
|
"match": function (s, pattern, index) {
|
||
|
|
s = check_string(s);
|
||
|
|
index = lua_tonumber(index);
|
||
|
|
if (index === null) {
|
||
|
|
index = 1;
|
||
|
|
} else if (index < 0) {
|
||
|
|
index = s.length + index;
|
||
|
|
}
|
||
|
|
index--;
|
||
|
|
s = s.substr(index);
|
||
|
|
|
||
|
|
pattern = lua_pattern_to_regex(pattern);
|
||
|
|
var match = get_balanced_match(s, pattern);
|
||
|
|
if (match === null) {
|
||
|
|
var matches = s.match(pattern);
|
||
|
|
if (matches !== null) {
|
||
|
|
if (matches[1] != null) {
|
||
|
|
match = matches[1];
|
||
|
|
} else {
|
||
|
|
match = matches[0];
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return [match];
|
||
|
|
},
|
||
|
|
"rep": function (s, n) {
|
||
|
|
s = check_string(s);
|
||
|
|
n = lua_tonumber(n);
|
||
|
|
if (n !== null) {
|
||
|
|
var result = [];
|
||
|
|
while (n-- > 0) {
|
||
|
|
result.push(s);
|
||
|
|
}
|
||
|
|
return [result.join("")];
|
||
|
|
} else {
|
||
|
|
throw new Error("Input not string and number");
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"reverse": function (s) {
|
||
|
|
return [check_string(s).split("").reverse().join("")];
|
||
|
|
},
|
||
|
|
"sub": function (s, i, j) {
|
||
|
|
i = lua_tonumber(i);
|
||
|
|
j = lua_tonumber(j);
|
||
|
|
// thanks to ghoulsblade for pointing out the bugs in string.sub
|
||
|
|
i = i < 0 ? (i + s.length + 1) : (i >= 0 ? i : 0)
|
||
|
|
if (j == null) {
|
||
|
|
j = -1;
|
||
|
|
}
|
||
|
|
j = j < 0 ? (j + s.length + 1) : (j >= 0 ? j : 0)
|
||
|
|
if (i < 1) {
|
||
|
|
i = 1;
|
||
|
|
}
|
||
|
|
if (j > s.length) {
|
||
|
|
j = s.length;
|
||
|
|
}
|
||
|
|
if (i <= j) {
|
||
|
|
return [s.substr(i - 1, j - i + 1)];
|
||
|
|
} else {
|
||
|
|
return [""];
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"upper": function (s) {
|
||
|
|
return [check_string(s).toUpperCase()];
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// add string functions to every string
|
||
|
|
String.prototype["metatable"] = lua_newtable(null, "__index", lua_newtable2(lua_libs["string"]));
|
||
|
|
|
||
|
|
// table
|
||
|
|
lua_libs["table"] = {
|
||
|
|
"concat": function (table, sep, i, j) {
|
||
|
|
if (sep == null) {
|
||
|
|
sep = "";
|
||
|
|
}
|
||
|
|
if (i != null) {
|
||
|
|
if (j == null) {
|
||
|
|
j = table.uints.length;
|
||
|
|
}
|
||
|
|
return [table.uints.slice(i - 1, j).join(sep)];
|
||
|
|
} else {
|
||
|
|
return [table.uints.join(sep)];
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"insert": function (table, pos, value) {
|
||
|
|
if (arguments.length == 2) {
|
||
|
|
value = pos;
|
||
|
|
pos = table.uints.length + 1;
|
||
|
|
}
|
||
|
|
pos--;
|
||
|
|
if (table.uints[pos] == null) {
|
||
|
|
table.uints[pos] = value;
|
||
|
|
} else {
|
||
|
|
table.uints.splice(pos, 0, value);
|
||
|
|
}
|
||
|
|
if (table.length != null) {
|
||
|
|
table.length++;
|
||
|
|
}
|
||
|
|
return [];
|
||
|
|
},
|
||
|
|
"maxn": function (table) {
|
||
|
|
var max = 0;
|
||
|
|
for (var i in table.uints) {
|
||
|
|
var val = parseFloat(i);
|
||
|
|
if (val > max) {
|
||
|
|
max = val;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return [max];
|
||
|
|
},
|
||
|
|
"remove": function (table, pos) {
|
||
|
|
if (pos == null) {
|
||
|
|
pos = table.uints.length;
|
||
|
|
} else {
|
||
|
|
pos = lua_assertfloat(pos);
|
||
|
|
}
|
||
|
|
if (table.uints.length) {
|
||
|
|
var value = table.uints[pos - 1];
|
||
|
|
table.uints.splice(pos - 1, 1);
|
||
|
|
if (table.length != null) {
|
||
|
|
table.length--;
|
||
|
|
}
|
||
|
|
return [value];
|
||
|
|
} else {
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"sort": function (table, comp) {
|
||
|
|
if (comp) {
|
||
|
|
table.uints.sort(function (a, b) {
|
||
|
|
return comp(a, b)[0] ? -1 : 1;
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
table.uints.sort(function (a, b) {
|
||
|
|
return lua_lt(a, b) ? -1 : 1;
|
||
|
|
});
|
||
|
|
}
|
||
|
|
return [];
|
||
|
|
}
|
||
|
|
};
|
||
|
|
|
||
|
|
// bit (based on BitOp <http://bitop.luajit.org/>)
|
||
|
|
lua_libs["bit"] = {
|
||
|
|
"tobit": function (x) {
|
||
|
|
return [x << 0]
|
||
|
|
},
|
||
|
|
"tohex": function (x, n) {
|
||
|
|
if (n > 0) {
|
||
|
|
var str = x.toString(16).substr(-n);
|
||
|
|
while (str.length < n) {
|
||
|
|
str = "0" + str;
|
||
|
|
}
|
||
|
|
return [str];
|
||
|
|
} else if (n < 0) {
|
||
|
|
var str = x.toString(16).substr(n).toUpperCase();
|
||
|
|
while (str.length < -n) {
|
||
|
|
str = "0" + str;
|
||
|
|
}
|
||
|
|
return [str];
|
||
|
|
} else {
|
||
|
|
return [x.toString(16)]
|
||
|
|
}
|
||
|
|
},
|
||
|
|
"bnot": function (x) {
|
||
|
|
return [~x];
|
||
|
|
},
|
||
|
|
"bor": function (x) {
|
||
|
|
x = lua_assertfloat(x);
|
||
|
|
for (var i = 1; i < arguments.length; i++) {
|
||
|
|
x |= arguments[i];
|
||
|
|
}
|
||
|
|
return [x];
|
||
|
|
},
|
||
|
|
"band": function (x) {
|
||
|
|
x = lua_assertfloat(x);
|
||
|
|
for (var i = 1; i < arguments.length; i++) {
|
||
|
|
x &= arguments[i];
|
||
|
|
}
|
||
|
|
return [x];
|
||
|
|
},
|
||
|
|
"bxor": function (x) {
|
||
|
|
x = lua_assertfloat(x);
|
||
|
|
for (var i = 1; i < arguments.length; i++) {
|
||
|
|
x ^= arguments[i];
|
||
|
|
}
|
||
|
|
return [x];
|
||
|
|
},
|
||
|
|
"lshift": function (x, n) {
|
||
|
|
return [x << n];
|
||
|
|
},
|
||
|
|
"rshift": function (x, n) {
|
||
|
|
return [x >>> n];
|
||
|
|
},
|
||
|
|
"arshift": function (x, n) {
|
||
|
|
return [x >> n];
|
||
|
|
},
|
||
|
|
"rol": function (x, n) {
|
||
|
|
n &= 0xf;
|
||
|
|
return [(x << n) | (x >>> -n)];
|
||
|
|
},
|
||
|
|
"ror": function (x, n) {
|
||
|
|
n &= 0xf;
|
||
|
|
return [(x >>> n) | (x << -n)];
|
||
|
|
},
|
||
|
|
"bswap": function (x) {
|
||
|
|
// from Bit Twiddling hacks <http://graphics.stanford.edu/~seander/bithacks.html>
|
||
|
|
x = ((x >> 1) & 0x55555555) | ((x & 0x55555555) << 1);
|
||
|
|
x = ((x >> 2) & 0x33333333) | ((x & 0x33333333) << 2);
|
||
|
|
x = ((x >> 4) & 0x0F0F0F0F) | ((x & 0x0F0F0F0F) << 4);
|
||
|
|
x = ((x >> 8) & 0x00FF00FF) | ((x & 0x00FF00FF) << 8);
|
||
|
|
x = (x >> 16) | (x << 16);
|
||
|
|
return [x];
|
||
|
|
}
|
||
|
|
};
|