2024-09-27 15:11:03 +08:00
import type {
2024-10-03 23:55:51 +08:00
LuaExpression ,
LuaLValue ,
LuaStatement ,
2024-09-27 15:11:03 +08:00
} from "$common/space_lua/ast.ts" ;
2024-09-12 23:01:54 +08:00
import { evalPromiseValues } from "$common/space_lua/util.ts" ;
2024-10-10 02:35:07 +08:00
import { luaCall , luaSet } from "$common/space_lua/runtime.ts" ;
2024-09-24 16:15:22 +08:00
import {
2024-10-03 23:55:51 +08:00
type ILuaFunction ,
type ILuaGettable ,
type ILuaSettable ,
LuaBreak ,
LuaEnv ,
LuaFunction ,
luaGet ,
luaLen ,
type LuaLValueContainer ,
LuaMultiRes ,
LuaReturn ,
LuaRuntimeError ,
LuaTable ,
luaToString ,
luaTruthy ,
type LuaValue ,
singleResult ,
2024-09-24 16:15:22 +08:00
} from "./runtime.ts" ;
2024-09-12 23:01:54 +08:00
export function evalExpression (
2024-10-03 23:55:51 +08:00
e : LuaExpression ,
env : LuaEnv ,
2024-09-27 15:11:03 +08:00
) : Promise < LuaValue > | LuaValue {
2024-10-04 23:15:50 +08:00
try {
switch ( e . type ) {
case "String" :
return e . value ;
case "Number" :
return e . value ;
case "Boolean" :
return e . value ;
case "Nil" :
return null ;
case "Binary" : {
const values = evalPromiseValues ( [
evalExpression ( e . left , env ) ,
evalExpression ( e . right , env ) ,
] ) ;
if ( values instanceof Promise ) {
return values . then ( ( [ left , right ] ) = >
luaOp ( e . operator , singleResult ( left ) , singleResult ( right ) )
) ;
} else {
return luaOp (
e . operator ,
singleResult ( values [ 0 ] ) ,
singleResult ( values [ 1 ] ) ,
) ;
}
2024-10-03 23:55:51 +08:00
}
2024-10-04 23:15:50 +08:00
case "Unary" : {
const value = evalExpression ( e . argument , env ) ;
if ( value instanceof Promise ) {
return value . then ( ( value ) = > {
switch ( e . operator ) {
case "-" :
return - singleResult ( value ) ;
case "+" :
return + singleResult ( value ) ;
case "not" :
return ! singleResult ( value ) ;
case "#" :
return luaLen ( singleResult ( value ) ) ;
default :
throw new Error (
` Unknown unary operator ${ e . operator } ` ,
) ;
}
} ) ;
} else {
2024-10-03 23:55:51 +08:00
switch ( e . operator ) {
case "-" :
return - singleResult ( value ) ;
case "+" :
return + singleResult ( value ) ;
case "not" :
return ! singleResult ( value ) ;
case "#" :
return luaLen ( singleResult ( value ) ) ;
default :
throw new Error (
` Unknown unary operator ${ e . operator } ` ,
) ;
}
2024-09-12 23:01:54 +08:00
}
2024-10-03 23:55:51 +08:00
}
2024-10-09 01:53:09 +08:00
2024-10-04 23:15:50 +08:00
case "Variable" :
case "FunctionCall" :
2024-10-09 01:53:09 +08:00
case "TableAccess" :
case "PropertyAccess" :
2024-10-04 23:15:50 +08:00
return evalPrefixExpression ( e , env ) ;
case "TableConstructor" : {
const table = new LuaTable ( ) ;
const promises : Promise < void > [ ] = [ ] ;
for ( const field of e . fields ) {
switch ( field . type ) {
case "PropField" : {
const value = evalExpression ( field . value , env ) ;
if ( value instanceof Promise ) {
promises . push ( value . then ( ( value ) = > {
table . set (
field . key ,
singleResult ( value ) ,
) ;
} ) ) ;
} else {
table . set ( field . key , singleResult ( value ) ) ;
}
break ;
}
case "DynamicField" : {
const key = evalExpression ( field . key , env ) ;
const value = evalExpression ( field . value , env ) ;
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 ) ,
) ;
} ) ,
) ;
} else {
2024-10-03 23:55:51 +08:00
table . set (
2024-10-04 23:15:50 +08:00
singleResult ( key ) ,
2024-10-03 23:55:51 +08:00
singleResult ( value ) ,
2024-09-12 23:01:54 +08:00
) ;
2024-10-04 23:15:50 +08:00
}
break ;
2024-09-12 23:01:54 +08:00
}
2024-10-04 23:15:50 +08:00
case "ExpressionField" : {
const value = evalExpression ( field . value , env ) ;
if ( value instanceof Promise ) {
promises . push ( value . then ( ( value ) = > {
// +1 because Lua tables are 1-indexed
2024-10-03 23:55:51 +08:00
table . set (
2024-10-04 23:15:50 +08:00
table . length + 1 ,
2024-10-03 23:55:51 +08:00
singleResult ( value ) ,
) ;
2024-10-04 23:15:50 +08:00
} ) ) ;
} else {
2024-10-03 23:55:51 +08:00
// +1 because Lua tables are 1-indexed
table . set (
table . length + 1 ,
singleResult ( value ) ,
) ;
2024-10-04 23:15:50 +08:00
}
break ;
2024-09-24 16:15:22 +08:00
}
2024-10-03 23:55:51 +08:00
}
2024-09-24 16:15:22 +08:00
}
2024-10-04 23:15:50 +08:00
if ( promises . length > 0 ) {
return Promise . all ( promises ) . then ( ( ) = > table ) ;
} else {
return table ;
}
2024-10-03 23:55:51 +08:00
}
2024-10-04 23:15:50 +08:00
case "FunctionDefinition" : {
return new LuaFunction ( e . body , env ) ;
2024-10-03 23:55:51 +08:00
}
2024-10-04 23:15:50 +08:00
default :
throw new Error ( ` Unknown expression type ${ e . type } ` ) ;
2024-10-03 23:55:51 +08:00
}
2024-10-04 23:15:50 +08:00
} catch ( err : any ) {
// Repackage any non Lua-specific exceptions with some position information
if ( ! err . constructor . name . startsWith ( "Lua" ) ) {
throw new LuaRuntimeError ( err . message , e . ctx , err ) ;
} else {
throw err ;
2024-09-12 23:01:54 +08:00
}
2024-10-03 23:55:51 +08:00
}
2024-09-12 23:01:54 +08:00
}
function evalPrefixExpression (
2024-10-03 23:55:51 +08:00
e : LuaExpression ,
env : LuaEnv ,
2024-09-27 15:11:03 +08:00
) : Promise < LuaValue > | LuaValue {
2024-10-03 23:55:51 +08:00
switch ( e . type ) {
case "Variable" : {
const value = env . get ( e . name ) ;
if ( value === undefined ) {
2024-10-05 21:37:36 +08:00
return null ;
2024-10-03 23:55:51 +08:00
} else {
return value ;
}
}
case "Parenthesized" :
return evalExpression ( e . expression , env ) ;
2024-10-09 01:53:09 +08:00
// <<expr>>[<<expr>>]
case "TableAccess" : {
const values = evalPromiseValues ( [
evalPrefixExpression ( e . object , env ) ,
evalExpression ( e . key , env ) ,
] ) ;
if ( values instanceof Promise ) {
return values . then ( ( [ table , key ] ) = > {
table = singleResult ( table ) ;
key = singleResult ( key ) ;
2024-10-10 02:35:07 +08:00
return luaGet ( table , key , e . ctx ) ;
2024-10-09 01:53:09 +08:00
} ) ;
} else {
const table = singleResult ( values [ 0 ] ) ;
const key = singleResult ( values [ 1 ] ) ;
2024-10-10 02:35:07 +08:00
return luaGet ( table , singleResult ( key ) , e . ctx ) ;
2024-10-09 01:53:09 +08:00
}
}
// <expr>.property
2024-10-03 23:55:51 +08:00
case "PropertyAccess" : {
const obj = evalPrefixExpression ( e . object , env ) ;
if ( obj instanceof Promise ) {
return obj . then ( ( obj ) = > {
2024-10-10 02:35:07 +08:00
return luaGet ( obj , e . property , e . ctx ) ;
2024-10-03 23:55:51 +08:00
} ) ;
} else {
2024-10-10 02:35:07 +08:00
return luaGet ( obj , e . property , e . ctx ) ;
2024-10-03 23:55:51 +08:00
}
}
case "FunctionCall" : {
2024-10-04 23:15:50 +08:00
let prefixValue = evalPrefixExpression ( e . prefix , env ) ;
if ( ! prefixValue ) {
2024-10-05 21:37:36 +08:00
throw new LuaRuntimeError (
` Attempting to call nil as a function ` ,
e . prefix . ctx ,
) ;
2024-10-04 23:15:50 +08:00
}
if ( prefixValue instanceof Promise ) {
return prefixValue . then ( ( prefixValue ) = > {
if ( ! prefixValue ) {
2024-10-05 21:37:36 +08:00
throw new LuaRuntimeError (
` Attempting to call a nil value ` ,
e . prefix . ctx ,
) ;
2024-10-04 23:15:50 +08:00
}
let selfArgs : LuaValue [ ] = [ ] ;
// Handling a:b() syntax (b is kept in .name)
if ( e . name && ! prefixValue . get ) {
throw new LuaRuntimeError (
2024-10-05 21:37:36 +08:00
` Attempting to index a non-table: ${ prefixValue } ` ,
2024-10-04 23:15:50 +08:00
e . prefix . ctx ,
) ;
} else if ( e . name ) {
// Two things need to happen: the actual function be called needs to be looked up in the table, and the table itself needs to be passed as the first argument
selfArgs = [ prefixValue ] ;
prefixValue = prefixValue . get ( e . name ) ;
}
if ( ! prefixValue . call ) {
2024-10-05 21:37:36 +08:00
throw new LuaRuntimeError (
` Attempting to call ${ prefixValue } as a function ` ,
2024-10-10 02:35:07 +08:00
e . prefix . ctx ,
2024-10-05 21:37:36 +08:00
) ;
2024-10-03 23:55:51 +08:00
}
const args = evalPromiseValues (
e . args . map ( ( arg ) = > evalExpression ( arg , env ) ) ,
) ;
if ( args instanceof Promise ) {
2024-10-10 02:35:07 +08:00
return args . then ( ( args ) = >
luaCall ( prefixValue , [ . . . selfArgs , . . . args ] , e . ctx )
) ;
2024-10-03 23:55:51 +08:00
} else {
2024-10-10 02:35:07 +08:00
return luaCall ( prefixValue , [ . . . selfArgs , . . . args ] , e . ctx ) ;
2024-10-03 23:55:51 +08:00
}
} ) ;
} else {
2024-10-04 23:15:50 +08:00
let selfArgs : LuaValue [ ] = [ ] ;
// Handling a:b() syntax (b is kept in .name)
if ( e . name && ! prefixValue . get ) {
throw new LuaRuntimeError (
2024-10-05 21:37:36 +08:00
` Attempting to index a non-table: ${ prefixValue } ` ,
2024-10-04 23:15:50 +08:00
e . prefix . ctx ,
) ;
} else if ( e . name ) {
// Two things need to happen: the actual function be called needs to be looked up in the table, and the table itself needs to be passed as the first argument
selfArgs = [ prefixValue ] ;
prefixValue = prefixValue . get ( e . name ) ;
}
if ( ! prefixValue . call ) {
throw new LuaRuntimeError (
2024-10-05 21:37:36 +08:00
` Attempting to call ${ prefixValue } as a function ` ,
2024-10-04 23:15:50 +08:00
e . prefix . ctx ,
) ;
2024-09-12 23:01:54 +08:00
}
2024-10-03 23:55:51 +08:00
const args = evalPromiseValues (
e . args . map ( ( arg ) = > evalExpression ( arg , env ) ) ,
) ;
if ( args instanceof Promise ) {
2024-10-10 02:35:07 +08:00
return args . then ( ( args ) = >
luaCall ( prefixValue , [ . . . selfArgs , . . . args ] , e . ctx )
) ;
2024-10-03 23:55:51 +08:00
} else {
2024-10-10 02:35:07 +08:00
return luaCall ( prefixValue , [ . . . selfArgs , . . . args ] , e . ctx ) ;
2024-10-03 23:55:51 +08:00
}
}
2024-09-12 23:01:54 +08:00
}
2024-10-03 23:55:51 +08:00
default :
throw new Error ( ` Unknown prefix expression type ${ e . type } ` ) ;
}
2024-09-12 23:01:54 +08:00
}
2024-10-04 23:15:50 +08:00
// Mapping table of operators meta-methods to their corresponding operator
type LuaMetaMethod = Record < string , {
metaMethod? : string ;
nativeImplementation : ( a : LuaValue , b : LuaValue ) = > LuaValue ;
} > ;
const operatorsMetaMethods : LuaMetaMethod = {
"+" : {
metaMethod : "__add" ,
nativeImplementation : ( a , b ) = > a + b ,
} ,
"-" : {
metaMethod : "__sub" ,
nativeImplementation : ( a , b ) = > a - b ,
} ,
"*" : {
metaMethod : "__mul" ,
nativeImplementation : ( a , b ) = > a * b ,
} ,
"/" : {
metaMethod : "__div" ,
nativeImplementation : ( a , b ) = > a / b ,
} ,
"//" : {
metaMethod : "__idiv" ,
nativeImplementation : ( a , b ) = > Math . floor ( a / b ) ,
} ,
"%" : {
metaMethod : "__mod" ,
nativeImplementation : ( a , b ) = > a % b ,
} ,
"^" : {
metaMethod : "__pow" ,
nativeImplementation : ( a , b ) = > a * * b ,
} ,
".." : {
metaMethod : "__concat" ,
nativeImplementation : ( a , b ) = > luaToString ( a ) + luaToString ( b ) ,
} ,
"==" : {
metaMethod : "__eq" ,
nativeImplementation : ( a , b ) = > a === b ,
} ,
"~=" : {
metaMethod : "__ne" ,
nativeImplementation : ( a , b ) = > a !== b ,
} ,
"!=" : {
metaMethod : "__ne" ,
nativeImplementation : ( a , b ) = > a !== b ,
} ,
"<" : {
metaMethod : "__lt" ,
nativeImplementation : ( a , b ) = > a < b ,
} ,
"<=" : {
metaMethod : "__le" ,
nativeImplementation : ( a , b ) = > a <= b ,
} ,
">" : {
nativeImplementation : ( a , b ) = > ! luaOp ( "<=" , a , b ) ,
} ,
">=" : {
nativeImplementation : ( a , b ) = > ! luaOp ( "<" , a , b ) ,
} ,
and : {
metaMethod : "__and" ,
nativeImplementation : ( a , b ) = > a && b ,
} ,
or : {
metaMethod : "__or" ,
nativeImplementation : ( a , b ) = > a || b ,
} ,
} ;
2024-09-12 23:01:54 +08:00
function luaOp ( op : string , left : any , right : any ) : any {
2024-10-04 23:15:50 +08:00
const operatorHandler = operatorsMetaMethods [ op ] ;
if ( ! operatorHandler ) {
throw new Error ( ` Unknown operator ${ op } ` ) ;
2024-10-03 23:55:51 +08:00
}
2024-10-04 23:15:50 +08:00
if ( operatorHandler . metaMethod ) {
if ( left ? . metatable ? . has ( operatorHandler . metaMethod ) ) {
const fn = left . metatable . get ( operatorHandler . metaMethod ) ;
if ( ! fn . call ) {
throw new Error (
` Meta method ${ operatorHandler . metaMethod } is not callable ` ,
) ;
} else {
return fn . call ( left , right ) ;
}
} else if ( right ? . metatable ? . has ( operatorHandler . metaMethod ) ) {
const fn = right . metatable . get ( operatorHandler . metaMethod ) ;
if ( ! fn . call ) {
throw new Error (
` Meta method ${ operatorHandler . metaMethod } is not callable ` ,
) ;
} else {
return fn . call ( right , left ) ;
}
}
}
return operatorHandler . nativeImplementation ( left , right ) ;
}
async function evalExpressions (
es : LuaExpression [ ] ,
env : LuaEnv ,
) : Promise < LuaValue [ ] > {
return new LuaMultiRes (
await Promise . all ( es . map ( ( e ) = > evalExpression ( e , env ) ) ) ,
) . flatten ( ) . values ;
2024-09-12 23:01:54 +08:00
}
2024-09-27 15:11:03 +08:00
export async function evalStatement (
2024-10-03 23:55:51 +08:00
s : LuaStatement ,
env : LuaEnv ,
2024-09-27 15:11:03 +08:00
) : Promise < void > {
2024-10-03 23:55:51 +08:00
switch ( s . type ) {
case "Assignment" : {
2024-10-04 23:15:50 +08:00
const values = await evalExpressions ( s . expressions , env ) ;
2024-10-03 23:55:51 +08:00
const lvalues = await evalPromiseValues ( s . variables
. map ( ( lval ) = > evalLValue ( lval , env ) ) ) ;
2024-09-27 15:11:03 +08:00
2024-10-03 23:55:51 +08:00
for ( let i = 0 ; i < lvalues . length ; i ++ ) {
2024-10-10 02:35:07 +08:00
luaSet ( lvalues [ i ] . env , lvalues [ i ] . key , values [ i ] , s . ctx ) ;
2024-10-03 23:55:51 +08:00
}
2024-09-27 15:11:03 +08:00
2024-10-03 23:55:51 +08:00
break ;
}
case "Local" : {
2024-10-04 23:15:50 +08:00
if ( s . expressions ) {
const values = await evalExpressions ( s . expressions , env ) ;
for ( let i = 0 ; i < s . names . length ; i ++ ) {
env . setLocal ( s . names [ i ] . name , values [ i ] ) ;
}
} else {
for ( let i = 0 ; i < s . names . length ; i ++ ) {
2024-10-03 23:55:51 +08:00
env . setLocal ( s . names [ i ] . name , null ) ;
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
}
break ;
}
case "Semicolon" :
break ;
case "Label" :
case "Goto" :
throw new Error ( "Labels and gotos are not supported yet" ) ;
case "Block" : {
const newEnv = new LuaEnv ( env ) ;
for ( const statement of s . statements ) {
await evalStatement ( statement , newEnv ) ;
}
break ;
}
case "If" : {
for ( const cond of s . conditions ) {
if ( luaTruthy ( await evalExpression ( cond . condition , env ) ) ) {
return evalStatement ( cond . block , env ) ;
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
}
if ( s . elseBlock ) {
return evalStatement ( s . elseBlock , env ) ;
}
break ;
}
case "While" : {
while ( luaTruthy ( await evalExpression ( s . condition , env ) ) ) {
try {
await evalStatement ( s . block , env ) ;
} catch ( e : any ) {
if ( e instanceof LuaBreak ) {
2024-09-27 15:11:03 +08:00
break ;
2024-10-03 23:55:51 +08:00
} else {
throw e ;
}
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
}
break ;
}
case "Repeat" : {
do {
try {
await evalStatement ( s . block , env ) ;
} catch ( e : any ) {
if ( e instanceof LuaBreak ) {
2024-09-27 15:11:03 +08:00
break ;
2024-10-03 23:55:51 +08:00
} else {
throw e ;
}
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
} while ( ! luaTruthy ( await evalExpression ( s . condition , env ) ) ) ;
break ;
}
case "Break" :
throw new LuaBreak ( ) ;
case "FunctionCallStatement" : {
return evalExpression ( s . call , env ) ;
}
case "Function" : {
let body = s . body ;
let propNames = s . name . propNames ;
if ( s . name . colonName ) {
// function hello:there() -> function hello.there(self) transformation
body = {
. . . s . body ,
parameters : [ "self" , . . . s . body . parameters ] ,
} ;
propNames = [ . . . s . name . propNames , s . name . colonName ] ;
}
let settable : ILuaSettable & ILuaGettable = env ;
for ( let i = 0 ; i < propNames . length - 1 ; i ++ ) {
settable = settable . get ( propNames [ i ] ) ;
if ( ! settable ) {
2024-10-09 01:53:09 +08:00
throw new LuaRuntimeError (
2024-10-03 23:55:51 +08:00
` Cannot find property ${ propNames [ i ] } ` ,
2024-10-09 01:53:09 +08:00
s . name . ctx ,
2024-10-03 23:55:51 +08:00
) ;
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
}
settable . set (
propNames [ propNames . length - 1 ] ,
new LuaFunction ( body , env ) ,
) ;
break ;
}
case "LocalFunction" : {
env . setLocal (
s . name ,
new LuaFunction ( s . body , env ) ,
) ;
break ;
}
case "Return" : {
// A return statement for now is implemented by throwing the value as an exception, this should
// be optimized for the common case later
throw new LuaReturn (
await evalPromiseValues (
s . expressions . map ( ( value ) = > evalExpression ( value , env ) ) ,
) ,
) ;
}
case "For" : {
const start = await evalExpression ( s . start , env ) ;
const end = await evalExpression ( s . end , env ) ;
const step = s . step ? await evalExpression ( s . step , env ) : 1 ;
const localEnv = new LuaEnv ( env ) ;
for (
let i = start ;
step > 0 ? i <= end : i >= end ;
i += step
) {
localEnv . setLocal ( s . name , i ) ;
try {
await evalStatement ( s . block , localEnv ) ;
} catch ( e : any ) {
if ( e instanceof LuaBreak ) {
2024-09-27 15:11:03 +08:00
break ;
2024-10-03 23:55:51 +08:00
} else {
throw e ;
}
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
}
break ;
}
case "ForIn" : {
const iteratorMultiRes = new LuaMultiRes (
await evalPromiseValues (
s . expressions . map ( ( e ) = > evalExpression ( e , env ) ) ,
) ,
) . flatten ( ) ;
const iteratorFunction : ILuaFunction | undefined =
iteratorMultiRes . values [ 0 ] ;
if ( ! iteratorFunction ? . call ) {
console . error ( "Cannot iterate over" , iteratorMultiRes . values [ 0 ] ) ;
throw new LuaRuntimeError (
` Cannot iterate over ${ iteratorMultiRes . values [ 0 ] } ` ,
2024-10-04 23:15:50 +08:00
s . ctx ,
2024-10-03 23:55:51 +08:00
) ;
}
const state : LuaValue = iteratorMultiRes . values [ 1 ] || null ;
const control : LuaValue = iteratorMultiRes . values [ 2 ] || null ;
while ( true ) {
const iterResult = new LuaMultiRes (
await iteratorFunction . call ( state , control ) ,
) . flatten ( ) ;
if (
iterResult . values [ 0 ] === null || iterResult . values [ 0 ] === undefined
) {
break ;
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
const localEnv = new LuaEnv ( env ) ;
for ( let i = 0 ; i < s . names . length ; i ++ ) {
localEnv . setLocal ( s . names [ i ] , iterResult . values [ i ] ) ;
2024-09-27 23:09:25 +08:00
}
2024-10-03 23:55:51 +08:00
try {
await evalStatement ( s . block , localEnv ) ;
} catch ( e : any ) {
if ( e instanceof LuaBreak ) {
break ;
} else {
throw e ;
}
2024-09-27 23:09:25 +08:00
}
2024-10-03 23:55:51 +08:00
}
break ;
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
}
2024-09-27 15:11:03 +08:00
}
function evalLValue (
2024-10-03 23:55:51 +08:00
lval : LuaLValue ,
env : LuaEnv ,
2024-09-27 15:11:03 +08:00
) : LuaLValueContainer | Promise < LuaLValueContainer > {
2024-10-03 23:55:51 +08:00
switch ( lval . type ) {
case "Variable" :
return { env , key : lval.name } ;
case "TableAccess" : {
const objValue = evalExpression (
lval . object ,
env ,
) ;
const keyValue = evalExpression ( lval . key , env ) ;
if (
objValue instanceof Promise ||
keyValue instanceof Promise
) {
return Promise . all ( [
objValue instanceof Promise ? objValue : Promise.resolve ( objValue ) ,
keyValue instanceof Promise ? keyValue : Promise.resolve ( keyValue ) ,
] ) . then ( ( [ objValue , keyValue ] ) = > ( {
env : singleResult ( objValue ) ,
key : singleResult ( keyValue ) ,
} ) ) ;
} else {
return {
env : singleResult ( objValue ) ,
key : singleResult ( keyValue ) ,
} ;
}
}
case "PropertyAccess" : {
const objValue = evalExpression (
lval . object ,
env ,
) ;
if ( objValue instanceof Promise ) {
return objValue . then ( ( objValue ) = > {
return {
env : objValue ,
key : lval.property ,
} ;
} ) ;
} else {
return {
env : objValue ,
key : lval.property ,
} ;
}
2024-09-27 15:11:03 +08:00
}
2024-10-03 23:55:51 +08:00
}
2024-09-27 15:11:03 +08:00
}