-- SINGLE-LINE JS INTERPRETER WITH LIVE MODE & CLEARLL (evaluator fix) local PREFIX = "js:" local UNLOAD_KEY = Enum.KeyCode.M local Players = game:GetService("Players") local ReplicatedStorage = game:GetService("ReplicatedStorage") local UserInputService = game:GetService("UserInputService") local player = Players.LocalPlayer -- FULL UNLOAD OLD COPY if _G.JSInterpreterLoaded then if _G.JSUnloadFunc then _G.JSUnloadFunc() end end _G.JSInterpreterLoaded = true -- Interpreter state local codeBuffer = {} local variables = {} local functions = {} local running = false local enabled = true local chatConn, inputConn -- CONSOLE / CHAT API local console = {} local c = console function console.log(msg) print("[JS]", tostring(msg)) end function console.chat(msg) if ReplicatedStorage:FindFirstChild("DefaultChatSystemChatEvents") then pcall(function() ReplicatedStorage.DefaultChatSystemChatEvents.SayMessageRequest:FireServer(tostring(msg),"All") end) else if player then pcall(function() player:Chat(tostring(msg)) end) else warn("[JS] Cannot chat: player missing") end end end -- Random helper table (use m.r(min, max)) local m = {} -- Random helper table (use m.r(min, max [, precision])) local m = {} -- seed once if not _G.JSRandomSeeded then local seed = (tick and tick() or os.time()) % 1e9 math.randomseed(seed) math.random(); math.random(); math.random() _G.JSRandomSeeded = true end -- Helper: round to N decimal places local function roundTo(n, decimals) local mult = 10 ^ (decimals or 0) return math.floor(n * mult + 0.5) / mult end -- m.r(min, max [, precision]) -- - If both min and max are integer-like and precision is nil -> returns integer in [min,max] -- - If precision is provided (non-negative integer) -> returns a number with that many decimals -- * If precision == 0 behaves like integer mode -- * If precision > 0 returns a float with exactly that many decimals (rounded) -- - If precision is nil and either arg is non-integer -> returns a float with full math.random() precision function m.r(minv, maxv, precision) minv = tonumber(minv) or 0 maxv = tonumber(maxv) or 1 if minv > maxv then minv, maxv = maxv, minv end -- If precision explicitly provided if precision ~= nil then precision = tonumber(precision) or 0 if precision <= 0 then -- integer behavior local imin = math.ceil(minv) local imax = math.floor(maxv) if imin > imax then return imin end return math.random(imin, imax) end -- produce float with 'precision' decimal places local scale = 10 ^ precision -- choose integer in scaled range, but avoid exact endpoints if you want open interval local scaledMin = math.ceil(minv * scale) local scaledMax = math.floor(maxv * scale) if scaledMin > scaledMax then -- fallback: produce a single rounded value return roundTo(minv, precision) end local r = math.random(scaledMin, scaledMax) / scale return roundTo(r, precision) end -- No precision provided: previous behavior if math.floor(minv) == minv and math.floor(maxv) == maxv then return math.random(minv, maxv) end -- mixed/float args -> return float in [minv, maxv) return minv + math.random() * (maxv - minv) end -- Strip comments and pasted metadata local function stripComments(line) if not line then return "" end line = line:gsub("//.*", "") line = line:gsub("#.*", "") line = line:gsub("]->", "") line = line:gsub("]->", "") line = line:gsub("<[^>]->", "") return line:match("^%s*(.-)%s*$") or "" end -- Enhanced syntax check local function syntaxValid(line) line = stripComments(line) if line == "" then return false end if line:match("^var%s+[%a_][%w_]*%s*=%s*.+") then return true end if line:match("^[%a_][%w_]*%s*=%s*.+") then return true end if line:match("^function%s+%w+%s*%((.-)%)%s*{.-}$") then return true end if line:match("^console%.") or line:match("^c%.") then return true end if line:match("^if%s*%(.+%)%s*{.-}%s*else%s*{.-}$") then return true end if line:match("^if%s*%(.+%)%s*{.-}$") then return true end if line:match("%f[%w]true%f[%W]") or line:match("%f[%w]false%f[%W]") then return true end return false end -- Convert JS operators and boolean literals to Lua equivalents local function convertMath(expr) if not expr then return expr end -- boolean literals expr = expr:gsub("%f[%w]true%f[%W]","true") expr = expr:gsub("%f[%w]false%f[%W]","false") -- logical operators expr = expr:gsub("&&"," and ") expr = expr:gsub("%|%|"," or ") expr = expr:gsub("!%s*","not ") -- inequality expr = expr:gsub("!=","~=") -- exponent expr = expr:gsub("%*%*","^") return expr end -- Evaluate expression in sandboxed env that resolves names from variables first local function evaluate(expr) if expr == nil then return nil end expr = expr:match("^%s*(.-)%s*$") -- quoted boolean strings local quoted = expr:match('^"(.-)"$') or expr:match("^'(.-)'$") if quoted == "true" then return true end if quoted == "false" then return false end expr = convertMath(expr) -- Build sandbox environment that resolves identifiers from variables table local env = {} env.math = math env.string = string env.tonumber = tonumber env.tostring = tostring env.pairs = pairs env.ipairs = ipairs env.print = print env.m = m -- <--- expose the random helper setmetatable(env, { __index = function(_, k) -- first check interpreter variables if variables[k] ~= nil then return variables[k] end -- then fallback to global return _G[k] end }) -- try as expression first local chunk, err = loadstring("return "..expr) if not chunk then -- try as statement chunk, err = loadstring(expr) end if not chunk then warn("[JS] Eval compile error:", err, "expr:", expr) return nil end -- set environment and run local ok, res -- setfenv is available in Roblox Lua (5.1) pcall(function() setfenv(chunk, env) end) ok, res = pcall(chunk) if not ok then warn("[JS] Eval runtime error:", res, "expr:", expr) return nil end return res end -- Assignment handler (supports "var name = value" and "name = value") local function handleAssignment(line) local name, value = line:match("^var%s+([%a_][%w_]*)%s*=%s*(.+)") if not name then name, value = line:match("^([%a_][%w_]*)%s*=%s*(.+)") end if name and value then value = stripComments(value):match("^%s*(.-)%s*$") local quoted = value:match('^"(.-)"$') or value:match("^'(.-)'$") if quoted == "true" then variables[name] = true; return true end if quoted == "false" then variables[name] = false; return true end local ok, res = pcall(function() return evaluate(value) end) if ok then variables[name] = res else warn("[JS] Assignment eval failed for "..name..":", res) end return true end return false end -- Run a single line (supports nested single-line if/else) local function runLine(line) line = stripComments(line) if line == "" then return end -- assignment if handleAssignment(line) then return end -- function if line:match("^function") then local fname,args,body = line:match("^function%s+(%w+)%s*%((.-)%)%s*{(.-)}$") if fname and args and body then functions[fname] = {args=args, body=body} end return end -- if/else (supports single-line if without else) if line:match("^if%s*%(") then local cond, ifbody, elsebody cond, ifbody, elsebody = line:match("^if%s*%((.-)%)%s*{(.-)}%s*else%s*{(.-)}") if cond then if evaluate(cond) then runLine(ifbody) else runLine(elsebody) end return end cond, ifbody = line:match("^if%s*%((.-)%)%s*{(.-)}") if cond then if evaluate(cond) then runLine(ifbody) end return end warn("[JS] Malformed if statement:", line) return end -- function call resolution for user-defined functions -- (simple inline replacement for single-line functions) if line:match("(%w+)%s*%(") then -- try to detect console calls first if line:match("console%.log") or line:match("c%.l") then local expr = line:match("console%.log%((.+)%)") or line:match("c%.l%((.+)%)") if expr then console.log(evaluate(expr)) end return end if line:match("console%.chat") or line:match("c%.c") then local expr = line:match("console%.chat%((.+)%)") or line:match("c%.c%((.+)%)") if expr then console.chat(evaluate(expr)) end return end end -- fallback: try to evaluate as expression/statement local ok, res = pcall(function() return evaluate(line) end) if not ok then warn("[JS] RunLine eval failed:", res) end end -- Execute program buffer local function executeBuffer() for _,line in ipairs(codeBuffer) do local ok, err = pcall(function() runLine(line) end) if not ok then warn("[JS] Error executing line:", line, err) end end end -- Handle chat input local function handleJSCommand(msg) if not enabled then return end if msg:sub(1,#PREFIX):lower() ~= PREFIX then return end local code = msg:sub(#PREFIX+1):gsub("^%s+","") -- COMMANDS if code == "print" then print("========== JS PROGRAM ==========") for _,line in ipairs(codeBuffer) do print(line) end print("========== END PROGRAM ==========") return elseif code == "clip" then if setclipboard then setclipboard(table.concat(codeBuffer,"\n")) print("[JS] Program copied to clipboard!") else warn("[JS] Clipboard unavailable") end return elseif code == "clear" then codeBuffer = {} variables = {} functions = {} print("[JS] Program cleared") return elseif code == "clearll" then local last = table.remove(codeBuffer) if last then local varName = last:match("^var%s+(%w+)%s*=") or last:match("^([%a_][%w_]*)%s*=") if varName then variables[varName] = nil end local funcName = last:match("^function%s+(%w+)%s*%(") if funcName then functions[funcName] = nil end print("[JS] Last line cleared:", last) else print("[JS] No line to clear") end return elseif code == "run" then for _,line in ipairs(codeBuffer) do if not syntaxValid(line) then warn("[JS] Cannot run: Invalid code in buffer:", line) return end end running = true executeBuffer() print("[JS] Entered running mode — new JS executes instantly!") return elseif code == "stop" then running = false print("[JS] Stopped running code") return end -- SINGLE-LINE STRICT CHECK if not syntaxValid(code) then warn("[JS] Invalid syntax:", code) return end -- Add valid line to buffer table.insert(codeBuffer, code) -- Instantly execute if running if running then local ok, err = pcall(function() runLine(code) end) if not ok then warn("[JS] Runtime error:", err) end end end -- CHAT CONNECTION (Unified) local ChatEvents = game:FindFirstChild("DefaultChatSystemChatEvents", true) if ChatEvents and ChatEvents:IsA("Folder") and ChatEvents:FindFirstChild("OnMessageDoneFiltering") then chatConn = ChatEvents.OnMessageDoneFiltering.OnClientEvent:Connect(function(msgData) if msgData.Message and type(msgData.Message)=="string" then handleJSCommand(msgData.Message) end end) else chatConn = player.Chatted:Connect(handleJSCommand) end -- UNLOAD local function fullUnload() enabled = false codeBuffer = {} variables = {} functions = {} if chatConn then chatConn:Disconnect() end if inputConn then inputConn:Disconnect() end _G.JSInterpreterLoaded = false _G.JSUnloadFunc = nil print("[JS] Fully Unloaded") end _G.JSUnloadFunc = fullUnload inputConn = UserInputService.InputBegan:Connect(function(input,gp) if gp then return end if input.KeyCode == UNLOAD_KEY then fullUnload() end end) print("[JS] Loaded (evaluator fixed)")