Cleanup eval a bit
parent
5ca0e98300
commit
538032ea94
|
@ -38,6 +38,91 @@ import {
|
||||||
import { luaValueToJS } from "$common/space_lua/runtime.ts";
|
import { luaValueToJS } from "$common/space_lua/runtime.ts";
|
||||||
import { jsToLuaValue } from "$common/space_lua/runtime.ts";
|
import { jsToLuaValue } from "$common/space_lua/runtime.ts";
|
||||||
|
|
||||||
|
function handleVarargSync(env: LuaEnv): LuaValue[] | Promise<LuaValue[]> {
|
||||||
|
const varargs = env.get("...");
|
||||||
|
if (varargs instanceof Promise) {
|
||||||
|
return handleVarargAsync(varargs);
|
||||||
|
}
|
||||||
|
if (varargs instanceof LuaTable) {
|
||||||
|
const args = [];
|
||||||
|
for (let i = 1; i <= varargs.length; i++) {
|
||||||
|
const val = varargs.get(i);
|
||||||
|
if (val instanceof Promise) {
|
||||||
|
return handleVarargAsync(varargs);
|
||||||
|
}
|
||||||
|
args.push(val);
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleVarargAsync(
|
||||||
|
varargs: Promise<LuaValue> | LuaTable,
|
||||||
|
): Promise<LuaValue[]> {
|
||||||
|
const resolvedVarargs = await varargs;
|
||||||
|
if (resolvedVarargs instanceof LuaTable) {
|
||||||
|
const args = [];
|
||||||
|
for (let i = 1; i <= resolvedVarargs.length; i++) {
|
||||||
|
args.push(await resolvedVarargs.get(i));
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleTableFieldSync(
|
||||||
|
table: LuaTable,
|
||||||
|
field: any,
|
||||||
|
env: LuaEnv,
|
||||||
|
sf: LuaStackFrame,
|
||||||
|
): void | Promise<void> {
|
||||||
|
switch (field.type) {
|
||||||
|
case "PropField": {
|
||||||
|
const value = evalExpression(field.value, env, sf);
|
||||||
|
if (value instanceof Promise) {
|
||||||
|
return value.then((v) => table.set(field.key, singleResult(v), sf));
|
||||||
|
}
|
||||||
|
table.set(field.key, singleResult(value), sf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "DynamicField": {
|
||||||
|
const key = evalExpression(field.key, env, sf);
|
||||||
|
const value = evalExpression(field.value, env, sf);
|
||||||
|
if (key instanceof Promise || value instanceof Promise) {
|
||||||
|
return Promise.all([
|
||||||
|
key instanceof Promise ? key : Promise.resolve(key),
|
||||||
|
value instanceof Promise ? value : Promise.resolve(value),
|
||||||
|
]).then(([k, v]) => {
|
||||||
|
table.set(singleResult(k), singleResult(v), sf);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
table.set(singleResult(key), singleResult(value), sf);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case "ExpressionField": {
|
||||||
|
if (field.value.type === "Variable" && field.value.name === "...") {
|
||||||
|
const varargs = handleVarargSync(env);
|
||||||
|
if (varargs instanceof Promise) {
|
||||||
|
return varargs.then((args) => {
|
||||||
|
args.forEach((val, i) => table.set(i + 1, val, sf));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
varargs.forEach((val, i) => table.set(i + 1, val, sf));
|
||||||
|
} else {
|
||||||
|
const value = evalExpression(field.value, env, sf);
|
||||||
|
if (value instanceof Promise) {
|
||||||
|
return value.then((v) =>
|
||||||
|
table.set(table.length + 1, singleResult(v), sf)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
table.set(table.length + 1, singleResult(value), sf);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function evalExpression(
|
export function evalExpression(
|
||||||
e: LuaExpression,
|
e: LuaExpression,
|
||||||
env: LuaEnv,
|
env: LuaEnv,
|
||||||
|
@ -138,132 +223,36 @@ export function evalExpression(
|
||||||
return evalPrefixExpression(e, env, sf);
|
return evalPrefixExpression(e, env, sf);
|
||||||
case "TableConstructor": {
|
case "TableConstructor": {
|
||||||
const table = new LuaTable();
|
const table = new LuaTable();
|
||||||
|
|
||||||
if (
|
if (
|
||||||
e.fields.length === 1 &&
|
e.fields.length === 1 &&
|
||||||
e.fields[0].type === "ExpressionField" &&
|
e.fields[0].type === "ExpressionField" &&
|
||||||
e.fields[0].value.type === "Variable" &&
|
e.fields[0].value.type === "Variable" &&
|
||||||
e.fields[0].value.name === "..."
|
e.fields[0].value.name === "..."
|
||||||
) {
|
) {
|
||||||
const varargs = env.get("...");
|
const varargs = handleVarargSync(env);
|
||||||
if (varargs instanceof Promise) {
|
if (varargs instanceof Promise) {
|
||||||
return varargs.then((resolvedVarargs) => {
|
return varargs.then((args) => {
|
||||||
if (resolvedVarargs instanceof LuaTable) {
|
args.forEach((val, i) => table.set(i + 1, val, sf));
|
||||||
const newTable = new LuaTable();
|
|
||||||
for (let i = 1; i <= resolvedVarargs.length; i++) {
|
|
||||||
newTable.set(i, resolvedVarargs.get(i), sf);
|
|
||||||
}
|
|
||||||
return newTable;
|
|
||||||
}
|
|
||||||
return table;
|
return table;
|
||||||
});
|
});
|
||||||
} else if (varargs instanceof LuaTable) {
|
|
||||||
for (let i = 1; i <= varargs.length; i++) {
|
|
||||||
table.set(i, varargs.get(i), sf);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
varargs.forEach((val, i) => table.set(i + 1, val, sf));
|
||||||
return table;
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
const promises: Promise<void>[] = [];
|
const promises: Promise<void>[] = [];
|
||||||
for (const field of e.fields) {
|
for (const field of e.fields) {
|
||||||
switch (field.type) {
|
const result = handleTableFieldSync(table, field, env, sf);
|
||||||
case "PropField": {
|
if (result instanceof Promise) {
|
||||||
const value = evalExpression(field.value, env, sf);
|
promises.push(result);
|
||||||
if (value instanceof Promise) {
|
|
||||||
promises.push(value.then((value) => {
|
|
||||||
table.set(
|
|
||||||
field.key,
|
|
||||||
singleResult(value),
|
|
||||||
sf,
|
|
||||||
);
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
table.set(field.key, singleResult(value), sf);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "DynamicField": {
|
|
||||||
const key = evalExpression(field.key, env, sf);
|
|
||||||
const value = evalExpression(field.value, env, sf);
|
|
||||||
if (
|
|
||||||
key instanceof Promise || value instanceof Promise
|
|
||||||
) {
|
|
||||||
promises.push(
|
|
||||||
Promise.all([
|
|
||||||
key instanceof Promise ? key : Promise.resolve(key),
|
|
||||||
value instanceof Promise ? value : Promise.resolve(value),
|
|
||||||
]).then(([key, value]) => {
|
|
||||||
table.set(
|
|
||||||
singleResult(key),
|
|
||||||
singleResult(value),
|
|
||||||
sf,
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
table.set(
|
|
||||||
singleResult(key),
|
|
||||||
singleResult(value),
|
|
||||||
sf,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case "ExpressionField": {
|
|
||||||
const value = evalExpression(field.value, env, sf);
|
|
||||||
if (value instanceof Promise) {
|
|
||||||
promises.push(value.then(async (value) => {
|
|
||||||
if (
|
|
||||||
field.value.type === "Variable" &&
|
|
||||||
field.value.name === "..."
|
|
||||||
) {
|
|
||||||
// Special handling for {...}
|
|
||||||
const varargs = await Promise.resolve(env.get("..."));
|
|
||||||
if (varargs instanceof LuaTable) {
|
|
||||||
// Copy all values from varargs table
|
|
||||||
for (let i = 1; i <= varargs.length; i++) {
|
|
||||||
const val = await Promise.resolve(varargs.get(i));
|
|
||||||
table.set(i, val, sf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Normal case
|
|
||||||
table.set(table.length + 1, singleResult(value), sf);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
if (
|
|
||||||
field.value.type === "Variable" && field.value.name === "..."
|
|
||||||
) {
|
|
||||||
// Special handling for {...}
|
|
||||||
const varargs = env.get("...");
|
|
||||||
if (varargs instanceof LuaTable) {
|
|
||||||
for (let i = 1; i <= varargs.length; i++) {
|
|
||||||
const val = varargs.get(i);
|
|
||||||
if (val instanceof Promise) {
|
|
||||||
promises.push(
|
|
||||||
Promise.resolve(val).then((val) => {
|
|
||||||
table.set(i, val, sf);
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
table.set(i, val, sf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Normal case
|
|
||||||
table.set(table.length + 1, singleResult(value), sf);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (promises.length > 0) {
|
if (promises.length > 0) {
|
||||||
return Promise.all(promises).then(() => table);
|
return Promise.all(promises).then(() => table);
|
||||||
} else {
|
|
||||||
return table;
|
|
||||||
}
|
}
|
||||||
|
return table;
|
||||||
}
|
}
|
||||||
case "FunctionDefinition": {
|
case "FunctionDefinition": {
|
||||||
return new LuaFunction(e.body, env);
|
return new LuaFunction(e.body, env);
|
||||||
|
@ -600,25 +589,19 @@ function luaOp(
|
||||||
ctx: ASTCtx,
|
ctx: ASTCtx,
|
||||||
sf: LuaStackFrame,
|
sf: LuaStackFrame,
|
||||||
): any {
|
): any {
|
||||||
const operatorHandler = operatorsMetaMethods[op];
|
const handler = operatorsMetaMethods[op];
|
||||||
if (!operatorHandler) {
|
if (!handler) {
|
||||||
throw new LuaRuntimeError(`Unknown operator ${op}`, sf.withCtx(ctx));
|
throw new LuaRuntimeError(`Unknown operator ${op}`, sf.withCtx(ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (operatorHandler.metaMethod) {
|
if (handler.metaMethod) {
|
||||||
const result = evalMetamethod(
|
const metaResult = evalMetamethod(left, right, handler.metaMethod, ctx, sf);
|
||||||
left,
|
if (metaResult !== undefined) {
|
||||||
right,
|
return metaResult;
|
||||||
operatorHandler.metaMethod,
|
|
||||||
ctx,
|
|
||||||
sf,
|
|
||||||
);
|
|
||||||
if (result !== undefined) {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return operatorHandler.nativeImplementation(left, right, ctx, sf);
|
return handler.nativeImplementation(left, right, ctx, sf);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function evalExpressions(
|
async function evalExpressions(
|
||||||
|
|
Loading…
Reference in New Issue