Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add runString(s) #2380

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 36 additions & 12 deletions lua/entities/gmod_wire_expression2/base/parser.lua
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,18 @@ function Parser:Error(message, token)
end
end

function Parser:Process(tokens, params)
---@alias ParserConfig { each_hook: fun()? }

---@param config ParserConfig
function Parser:Process(tokens, config)
self.tokens = tokens
self.index = 0
self.count = #tokens
self.delta = {}
self.includes = {}

self:NextToken()
local tree = self:Root()
local tree = self:Root(config and config.each_hook)
if parserDebug:GetBool() then
print(E2Lib.AST.dump(tree))
end
Expand Down Expand Up @@ -211,30 +214,51 @@ end

local loopdepth

function Parser:Root()
---@param hook fun()?
function Parser:Root(hook)
loopdepth = 0
return self:Stmts()
end


function Parser:Stmts()
---@param hook fun()?
function Parser:Stmts(hook)
local trace = self:GetTokenTrace()
local stmts = self:Instruction(trace, "seq")

if not self:HasTokens() then return stmts end

while true do
if self:AcceptRoamingToken("com") then
self:Error("Statement separator (,) must not appear multiple times")
if hook then
while true do
if self:AcceptRoamingToken("com") then
self:Error("Statement separator (,) must not appear multiple times")
end

hook()
stmts[#stmts + 1] = self:Stmt1()

if not self:HasTokens() then break end

if not self:AcceptRoamingToken("com") then
if self.readtoken[3] == false then
self:Error("Statements must be separated by comma (,) or whitespace")
end
end
end
else
while true do
if self:AcceptRoamingToken("com") then
self:Error("Statement separator (,) must not appear multiple times")
end

stmts[#stmts + 1] = self:Stmt1()
stmts[#stmts + 1] = self:Stmt1()

if not self:HasTokens() then break end
if not self:HasTokens() then break end

if not self:AcceptRoamingToken("com") then
if self.readtoken[3] == false then
self:Error("Statements must be separated by comma (,) or whitespace")
if not self:AcceptRoamingToken("com") then
if self.readtoken[3] == false then
self:Error("Statements must be separated by comma (,) or whitespace")
end
end
end
end
Expand Down
55 changes: 42 additions & 13 deletions lua/entities/gmod_wire_expression2/base/tokenizer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@ function Tokenizer:Error(message, offset)
error(message .. " at line " .. self.tokenline .. ", char " .. (self.tokenchar + (offset or 0)), 0)
end

function Tokenizer:Process(buffer, params)
---@alias TokenizerConfig { each_hook: fun()? }

---@param config TokenizerConfig
function Tokenizer:Process(buffer, config)
self.buffer = buffer
self.length = buffer:len()
self.position = 0
Expand All @@ -32,26 +35,52 @@ function Tokenizer:Process(buffer, params)
local tokenname, tokendata, tokenspace
self.tokendata = ""

while self.character do
tokenspace = self:NextPattern("%s+") and true or false

if not self.character then break end
if config and config.each_hook then
local hook = config.each_hook
while self.character do
tokenspace = self:NextPattern("%s+") and true or false

self.tokenline = self.readline
self.tokenchar = self.readchar
self.tokendata = ""
if not self.character then break end

tokenname, tokendata = self:NextSymbol()
self.tokenline = self.readline
self.tokenchar = self.readchar
self.tokendata = ""

if tokenname == nil then
tokenname, tokendata = self:NextOperator()
tokenname, tokendata = self:NextSymbol()

if tokenname == nil then
self:Error("Unknown character found (" .. self.character .. ")")
tokenname, tokendata = self:NextOperator()

if tokenname == nil then
self:Error("Unknown character found (" .. self.character .. ")")
end
end

hook()
tokens[#tokens + 1] = { tokenname, tokendata, tokenspace, self.tokenline, self.tokenchar }
end
else
while self.character do
tokenspace = self:NextPattern("%s+") and true or false

if not self.character then break end

self.tokenline = self.readline
self.tokenchar = self.readchar
self.tokendata = ""

tokens[#tokens + 1] = { tokenname, tokendata, tokenspace, self.tokenline, self.tokenchar }
tokenname, tokendata = self:NextSymbol()

if tokenname == nil then
tokenname, tokendata = self:NextOperator()

if tokenname == nil then
self:Error("Unknown character found (" .. self.character .. ")")
end
end

tokens[#tokens + 1] = { tokenname, tokendata, tokenspace, self.tokenline, self.tokenchar }
end
end

return tokens
Expand Down
62 changes: 62 additions & 0 deletions lua/entities/gmod_wire_expression2/core/selfaware.lua
Original file line number Diff line number Diff line change
Expand Up @@ -243,3 +243,65 @@ end
e2function number hash( string str )
return getHash( self, str )
end

local PreProcessor, Tokenizer, Parser, Optimizer, Compiler = E2Lib.PreProcessor.Execute, E2Lib.Tokenizer.Execute, E2Lib.Parser.Execute, E2Lib.Optimizer.Execute, E2Lib.Compiler.Execute
local raise_exception = E2Lib.raiseException

--- Runs E2 code from a string. Be sure to use try {} catch() {} if you want to handle the errors
local fmt = string.format

__e2setcost(1000)
e2function void runString(string code)
local chip = self.entity

-- Have a check just in case tick quota isn't enough (servers have it really high)
self.data.runstring_stack = (self.data.runstring_stack or 0) + 1
if self.data.runstring_stack >= 30 then
error("runString stack overflow")
end

local function raise(msg, level, trace)
self.data.runstring_stack = self.data.runstring_stack - 1
raise_exception(msg, level, trace)
end

self.data.runstring_config = self.data.runstring_config or {
each_hook = function()
self.prf = self.prf + 50
end
}

local default_config = self.data.runstring_config

self.prf = self.prf + #code / 2

local status, directives, code = PreProcessor(code, nil, self)
if not status then return raise( fmt("Preprocessor Error [%s]", directives), 2, self.trace) end

local status, tokens = Tokenizer(code, default_config)
if not status then return raise( fmt("Tokenizer Error [%s]", tokens), 2, self.trace) end

local status, tree, dvars = Parser(tokens, default_config)
if not status then return raise( fmt("Parser Error [%s]", tree), 2, self.trace) end

self.prf = self.prf + #tree * 20

status, tree = Optimizer(tree)
if not status then return raise( fmt("Optimizer Error [%s]", tree), 2, self.trace) end
self.prf = self.prf + #tree * 10

local status, script, inst = Compiler(tree, chip.inports[3], chip.outports[3], chip.persists and chip.persists[3] or {}, dvars, chip.includes)
if not status then return raise( fmt("Compiler Error [%s]", script), 2, self.trace) end

self:PushScope()
-- pcall so we can pop the scope
local success, why = pcall( script[1], self, script )
self:PopScope()

self.data.runstring_stack = self.data.runstring_stack - 1

if not success then
-- Failed, throw the error back to the error handler
error(why, 0)
end
end
1 change: 1 addition & 0 deletions lua/wire/client/e2descriptions.lua
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,7 @@ E2Helper.Descriptions["ioOutputEntities(s)"] = "Returns an array of all entities
E2Helper.Descriptions["runOnLast(n)"] = "If set to 1, the chip will run once when it is removed, setting the last() flag when it does"
E2Helper.Descriptions["selfDestruct()"] = "Removes the expression"
E2Helper.Descriptions["selfDestructAll()"] = "Removes the expression and all constrained props"
E2Helper.Descriptions["runString(s)"] = "Runs E2 code from a string, in a local scope. It still has access to all of your functions and directive vars, so do not trust user input with this!"

-- Debug
E2Helper.Descriptions["playerCanPrint()"] = "Returns whether or not the next print-message will be printed or omitted by antispam"
Expand Down