-- JSON Script Runtime Executor -- Executes script.json definitions at runtime without code generation local M = {} -- Reference resolver - resolves $ref:path.to.value local function resolve_ref(ref, context) if type(ref) ~= "string" or not ref:match("^%$ref:") then return ref end local path = ref:sub(6) -- Remove "$ref:" local parts = {} for part in path:gmatch("[^.]+") do table.insert(parts, part) end local value = context for _, part in ipairs(parts) do if type(value) ~= "table" then return nil end value = value[part] end return value end -- Expression evaluator local function eval_expression(expr, context) if type(expr) ~= "table" then return resolve_ref(expr, context) end local expr_type = expr.type -- Literal values if expr_type == "string_literal" then return expr.value elseif expr_type == "number_literal" then return expr.value elseif expr_type == "boolean_literal" then return expr.value elseif expr_type == "null_literal" then return nil end -- Template literals if expr_type == "template_literal" then local template = expr.template -- Simple interpolation ${variable} return template:gsub("%${([^}]+)}", function(varPath) local ref = "$ref:" .. varPath local value = resolve_ref(ref, context) return tostring(value or "") end) end -- Binary expressions if expr_type == "binary_expression" then local left = eval_expression(expr.left, context) local right = eval_expression(expr.right, context) local op = expr.operator if op == "+" then return left + right elseif op == "-" then return left - right elseif op == "*" then return left * right elseif op == "/" then return left / right elseif op == "%" then return left % right elseif op == "==" or op == "===" then return left == right elseif op == "!=" or op == "~=" or op == "!==" then return left ~= right elseif op == "<" then return left < right elseif op == ">" then return left > right elseif op == "<=" then return left <= right elseif op == ">=" then return left >= right end end -- Logical expressions (short-circuit evaluation) if expr_type == "logical_expression" then local op = expr.operator if op == "&&" or op == "and" then local left = eval_expression(expr.left, context) if not left then return left end -- Short-circuit return eval_expression(expr.right, context) elseif op == "||" or op == "or" then local left = eval_expression(expr.left, context) if left then return left end -- Short-circuit return eval_expression(expr.right, context) elseif op == "??" then local left = eval_expression(expr.left, context) if left ~= nil then return left end -- Null coalescing return eval_expression(expr.right, context) end end -- Unary expressions if expr_type == "unary_expression" then local arg = eval_expression(expr.argument, context) local op = expr.operator if op == "!" or op == "not" then return not arg elseif op == "-" then return -arg elseif op == "+" then return arg elseif op == "~" then return ~arg -- Bitwise NOT (Lua 5.3+) end end -- Conditional expression (ternary) if expr_type == "conditional_expression" then local test = eval_expression(expr.test, context) if test then return eval_expression(expr.consequent, context) else return eval_expression(expr.alternate, context) end end -- Member access if expr_type == "member_access" then local obj = eval_expression(expr.object, context) if type(obj) == "table" then -- Computed property access if expr.computed then local prop = eval_expression(expr.property, context) return obj[prop] else return obj[expr.property] end end return nil end -- Call expression if expr_type == "call_expression" then local callee = resolve_ref(expr.callee, context) local args = {} if expr.args then for _, arg in ipairs(expr.args) do table.insert(args, eval_expression(arg, context)) end end if type(callee) == "function" then return callee(table.unpack(args)) end end -- Object literal if expr_type == "object_literal" then local obj = {} if expr.properties then for key, value in pairs(expr.properties) do obj[key] = eval_expression(value, context) end end return obj end -- Array literal if expr_type == "array_literal" then local arr = {} if expr.elements then for _, elem in ipairs(expr.elements) do table.insert(arr, eval_expression(elem, context)) end end return arr end return nil end -- Statement executor local function execute_statement(stmt, context) local stmt_type = stmt.type -- Variable declarations if stmt_type == "const_declaration" or stmt_type == "let_declaration" then local name = stmt.name local value = eval_expression(stmt.value, context) context.local_vars = context.local_vars or {} context.local_vars[name] = value return nil end -- Assignment if stmt_type == "assignment" then local target = stmt.target local value = eval_expression(stmt.value, context) -- Simple variable assignment if type(target) == "string" then context.local_vars = context.local_vars or {} context.local_vars[target] = value end return nil end -- If statement if stmt_type == "if_statement" then local condition = eval_expression(stmt.condition, context) if condition then if stmt.then then for _, s in ipairs(stmt.then) do local result = execute_statement(s, context) if result and result.type == "return" then return result end end end elseif stmt.else then for _, s in ipairs(stmt.else) do local result = execute_statement(s, context) if result and result.type == "return" then return result end end end return nil end -- Return statement if stmt_type == "return" then return { type = "return", value = eval_expression(stmt.value, context) } end -- Try-catch if stmt_type == "try_catch" then local success, err = pcall(function() if stmt.try then for _, s in ipairs(stmt.try) do local result = execute_statement(s, context) if result and result.type == "return" then return result end end end end) if not success and stmt.catch then context.catch = context.catch or {} context.catch.error = err for _, s in ipairs(stmt.catch.body) do local result = execute_statement(s, context) if result and result.type == "return" then return result end end end if stmt.finally then for _, s in ipairs(stmt.finally) do execute_statement(s, context) end end return nil end -- Call expression statement if stmt_type == "call_expression" then eval_expression(stmt, context) return nil end -- For-each loop (ipairs/for...of) if stmt_type == "for_each_loop" then local iterable = eval_expression(stmt.iterable, context) if type(iterable) == "table" then context.local_vars = context.local_vars or {} for _, item in ipairs(iterable) do context.local_vars[stmt.iterator] = item for _, s in ipairs(stmt.body or {}) do local result = execute_statement(s, context) if result and result.type == "return" then return result end end end end return nil end -- Comment (ignored at runtime) if stmt_type == "comment" then return nil end return nil end -- Execute function from script.json function M.execute_function(script_json, function_name, args) -- Find function definition local func_def = nil for _, fn in ipairs(script_json.functions or {}) do if fn.name == function_name then func_def = fn break end end if not func_def then error("Function not found: " .. function_name) end -- Build context local context = { params = {}, local_vars = {}, constants = {}, imports = {}, functions = {} } -- Load constants for _, const in ipairs(script_json.constants or {}) do context.constants[const.name] = const.value end -- Set parameters with defaults args = args or {} for i, param in ipairs(func_def.params or {}) do local value = args[i] -- Handle default values if value == nil and param.default ~= nil then if type(param.default) == "string" and param.default:match("^%$ref:") then value = resolve_ref(param.default, context) elseif type(param.default) == "table" then value = eval_expression(param.default, context) else value = param.default end end context.params[param.name] = value end -- Execute function body for _, stmt in ipairs(func_def.body or {}) do local result = execute_statement(stmt, context) if result and result.type == "return" then return result.value end end return nil end -- Load and execute script.json file function M.load_and_execute(json_path, function_name, args) local file = io.open(json_path, "r") if not file then error("Cannot open file: " .. json_path) end local content = file:read("*all") file:close() -- Parse JSON (assuming json module available) local json = require("json") local script_json = json.decode(content) return M.execute_function(script_json, function_name, args) end return M