/* eslint-disable import/order */ /* eslint-disable import/no-duplicates */ import { Scope } from './Scope' import { createG } from './lib/globals' import { operators } from './operators' import { Table } from './Table' import { LuaError } from './LuaError' import { libMath } from './lib/math' import { libTable } from './lib/table' import { libString, metatable as stringMetatable } from './lib/string' import { getLibOS } from './lib/os' import { getLibPackage } from './lib/package' import { LuaType, ensureArray, Config } from './utils' import { parse as parseScript } from './parser' interface Script { exec: () => LuaType } const call = (f: Function | Table, ...args: LuaType[]): LuaType[] => { if (f instanceof Function) return ensureArray(f(...args)) const mm = f instanceof Table && f.getMetaMethod('__call') if (mm) return ensureArray(mm(f, ...args)) throw new LuaError(`attempt to call an uncallable type`) } const stringTable = new Table() stringTable.metatable = stringMetatable const get = (t: Table | string, v: LuaType): LuaType => { if (t instanceof Table) return t.get(v) if (typeof t === 'string') return stringTable.get(v) throw new LuaError(`no table or metatable found for given type`) } const execChunk = (_G: Table, chunk: string, chunkName?: string): LuaType[] => { const exec = new Function('__lua', chunk) const globalScope = new Scope(_G.strValues).extend() if (chunkName) globalScope.setVarargs([chunkName]) const res = exec({ globalScope, ...operators, Table, call, get }) return res === undefined ? [undefined] : res } function createEnv( config: Config = {} ): { parse: (script: string) => Script parseFile: (path: string) => Script loadLib: (name: string, value: Table) => void } { const cfg: Config = { LUA_PATH: './?.lua', stdin: '', stdout: console.log, ...config } const _G = createG(cfg, execChunk) const { libPackage, _require } = getLibPackage( (content, moduleName) => execChunk(_G, parseScript(content), moduleName)[0], cfg ) const loaded = libPackage.get('loaded') as Table const loadLib = (name: string, value: Table): void => { _G.rawset(name, value) loaded.rawset(name, value) } loadLib('_G', _G) loadLib('package', libPackage) loadLib('math', libMath) loadLib('table', libTable) loadLib('string', libString) loadLib('os', getLibOS(cfg)) _G.rawset('require', _require) const parse = (code: string): Script => { const script = parseScript(code) return { exec: () => execChunk(_G, script)[0] } } const parseFile = (filename: string): Script => { if (!cfg.fileExists) throw new LuaError('parseFile requires the config.fileExists function') if (!cfg.loadFile) throw new LuaError('parseFile requires the config.loadFile function') if (!cfg.fileExists(filename)) throw new LuaError('file not found') return parse(cfg.loadFile(filename)) } return { parse, parseFile, loadLib } } // eslint-disable-next-line import/first import * as utils from './utils' export { createEnv, Table, LuaError, utils }