Skip to content
Merged
176 changes: 120 additions & 56 deletions lua/entities/gmod_wire_expression2/base/preprocessor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@
}

function PreProcessor:GetType(tp, trace)
tp = tp:Trim():lower()
tp = self:Trim(tp):lower()
local up = tp:upper()

if tp == "normal" then
Expand All @@ -83,47 +83,110 @@
end
end

function PreProcessor:Trim(line)
local length = #line
local first

for i = 1, length do
local b = string.byte(line, i)

if b ~= 32 and (b < 9 or b > 13) then
first = i
break
end
end

if not first then
return ""
end

local last

for i = length, 1, -1 do
local b = string.byte(line, i)

if b ~= 32 and (b < 9 or b > 13) then
last = i
break
end
end

return string.sub(line, first, last)
end

function PreProcessor:TrimRight(line)
for i = #line, 1, -1 do
local b = string.byte(line, i)

if b ~= 32 and (b < 9 or b > 13) then
return string.sub(line, 1, i)
end
end

-- The line consists only of spaces
return ""
end

function PreProcessor:FindComments(line)
local isinput = not self.blockcomment and not self.multilinestring and line:match("^@inputs") ~= nil
local isoutput = not self.blockcomment and not self.multilinestring and line:match("^@outputs") ~= nil

local ret, count, pos, found = {}, 0, 1
repeat
found = line:find((isinput or isoutput) and '[#"\\A-Z]' or '[#"\\]', pos)
if found then -- We found something
local char = line:sub(found, found)
if (isinput or isoutput) and char:match("[A-Z]") ~= nil then -- we found the start of an input/output variable definition
local varname, endpos = line:match("^([A-Z][A-Za-z0-9_]*)()",found)
count = count + 1
ret[count] = {type = isinput and "inputs" or "outputs", name=varname, pos=found, blockcomment = {}}
pos = endpos
elseif char == "#" then -- We found a comment
local before = line:sub(found - 1, found - 1)
if before == "]" then -- We found an ending
count = count + 1
ret[count] = { type = "end", pos = found - 1 }
pos = found + 1
local ret, count = {}, 0
local len = #line
local i = 1

while i <= len do
local byte = string.byte(line, i)

-- We found the start of an input/output variable definition
if (isinput or isoutput) and byte >= 65 and byte <= 90 then
local start = i
i = i + 1

while i <= len do
local b = string.byte(line, i)

if (b >= 65 and b <= 90) or (b >= 97 and b <= 122) or (b >= 48 and b <= 57) or b == 95 then
i = i + 1
else
local after = line:sub(found + 1, found + 1)
if after == "[" then -- We found a start
count = count + 1
ret[count] = { type = "start", pos = found }
pos = found + 2
else -- We found a normal comment
count = count + 1
ret[count] = { type = "normal", pos = found }
pos = found + 1
end
break
end
elseif char == '"' then -- We found a string
end

local varname = string.sub(line, start, i - 1)
count = count + 1
ret[count] = { type = isinput and "inputs" or "outputs", name = varname, pos = start, blockcomment = {} }
elseif byte == 92 then -- We found an escape character
i = i + 2 -- Skip the escape character and the character following it
elseif byte == 34 then -- We found a string
count = count + 1
ret[count] = { type = "string", pos = i }
i = i + 1
elseif byte == 35 then -- We found a comment
local before = i > 1 and string.byte(line, i - 1)

if before == 93 then -- We found an ending
count = count + 1
ret[count] = { type = "string", pos = found }
pos = found + 1
elseif char == '\\' then -- We found an escape character
pos = found + 2 -- Skip the escape character and the character following it
ret[count] = { type = "end", pos = i - 1 }
i = i + 1
else
local after = i < len and string.byte(line, i + 1)

if after == 91 then -- We found a start
count = count + 1
ret[count] = { type = "start", pos = i }
i = i + 2
else -- We found a normal comment
count = count + 1
ret[count] = { type = "normal", pos = i }
i = i + 1
end
end
else
i = i + 1
end
until (not found)
end

return ret, count
end

Expand Down Expand Up @@ -265,7 +328,7 @@
["persist"] = handleIO("persist"),

["trigger"] = function(self, value, trace)
local trimmed = string.Trim(value)
local trimmed = PreProcessor.Trim(nil, value)
if trimmed == "all" then
if self.directives.trigger[1] ~= nil then
self:Error("Directive (@trigger) conflicts with previous directives", trace)
Expand Down Expand Up @@ -311,7 +374,7 @@
end

if CLIENT then
if #string.Trim(arg) > 0 then
if #PreProcessor.Trim(nil, arg) > 0 then
trace.start_col = trace.end_col + 1
trace.end_line = trace.start_line + 1
trace.end_col = 1
Expand Down Expand Up @@ -348,7 +411,7 @@
-- not a directive?
if not directive then
-- flag as "in code", if that is the case
if string.Trim(line) ~= "" then
if self:Trim(line) ~= "" then
self.incode = true
end
-- don't handle as a directive.
Expand Down Expand Up @@ -387,8 +450,6 @@
self.ifdefStack = {}
self.warnings, self.errors = {}, {}

local lines = string.Explode("\n", buffer)

if not directives then
self.directives = {
name = nil,
Expand All @@ -403,25 +464,28 @@
self.ignorestuff = true
end

-- to avoid big hangs, 2 regex changed from 500 to 10000 to avoid false positives
local regex_limits = {[0] = 50000000, 15000, 10000, 150, 70, 40}
local timeout = SysTime() + 0.5
local lines

for i, line in ipairs(lines) do
self.readline = i
if #buffer > 5000000 then -- 5mb
self:Error("Code is too big (5mb max)")
lines = {}
else
-- to avoid big hangs
local timeout = SysTime() + 0.5
lines = string.Explode("\n", buffer)

local ok = pcall(function() WireLib.CheckRegex(line, "^(.-)%s*$", regex_limits) end)
if not ok then self:Error("Line strip regex is too complex!") goto cont end
for i, line in ipairs(lines) do
self.readline = i

line = string.TrimRight(line)
line = self:RemoveComments(line)
line = self:ParseDirectives(line)
lines[i] = line
::cont::
line = self:TrimRight(line)
line = self:RemoveComments(line)
line = self:ParseDirectives(line)
lines[i] = line

if SysTime() > timeout then
self:Error("Preprocessing took too long!")
break
if SysTime() > timeout then
self:Error("Preprocessing took too long")
break
end
end
end

Expand Down Expand Up @@ -463,14 +527,14 @@
if not i then
-- no -> malformed variable name
self:Error("Variable name (" .. E2Lib.limitString(key, 10) .. ") must start with an uppercase letter", tr, { { at = tr, replace = key:sub(1, 1):upper() .. key:sub(2) } })
goto cont

Check warning on line 530 in lua/entities/gmod_wire_expression2/base/preprocessor.lua

View workflow job for this annotation

GitHub Actions / lint

"Goto"

Don't use labels and gotos unless you're jumping out of multiple loops.
else
-- yes -> add all variables.
for column2, var in namestring:gmatch("()([^,]+)") do
column2 = column + column2
local tr = Trace.new(self.readline, column2, self.readline, column2 + #var)

var = string.Trim(var)
var = self:Trim(var)
-- skip empty entries
if var ~= "" then
-- error on malformed variable names
Expand Down Expand Up @@ -504,7 +568,7 @@

if vtype ~= vtype:lower() then
self:Error("Variable type [" .. E2Lib.limitString(vtype, 10) .. "] must be lowercase", tr, { { at = tr, replace = vtype:lower() } })
goto cont

Check warning on line 571 in lua/entities/gmod_wire_expression2/base/preprocessor.lua

View workflow job for this annotation

GitHub Actions / lint

"Goto"

Don't use labels and gotos unless you're jumping out of multiple loops.
elseif vtype == "number" then
vtype = "normal"
elseif vtype == "normal" then
Expand All @@ -516,7 +580,7 @@
else
-- invalid -> raise an error
self:Error("Variable declaration (" .. E2Lib.limitString(key, 10) .. ") contains invalid characters", tr)
goto cont

Check warning on line 583 in lua/entities/gmod_wire_expression2/base/preprocessor.lua

View workflow job for this annotation

GitHub Actions / lint

"Goto"

Don't use labels and gotos unless you're jumping out of multiple loops.
end

-- fill in the missing types
Expand Down Expand Up @@ -615,7 +679,7 @@
local state = table.remove(self.ifdefStack)
if state == nil then self:Error("Found #else outside #ifdef/#ifndef block", trace) end

if args:Trim() ~= "" then self:Error("Must not pass an argument to #else", trace) end
if self:Trim(args) ~= "" then self:Error("Must not pass an argument to #else", trace) end

if self:Disabled() then
table.insert(self.ifdefStack, false)
Expand All @@ -628,7 +692,7 @@
local state = table.remove(self.ifdefStack)
if state == nil then self:Error("Found #endif outside #ifdef/#ifndef block", trace) end

if args:Trim() ~= "" then self:Error("Must not pass an argument to #endif", trace) end
if self:Trim(args) ~= "" then self:Error("Must not pass an argument to #endif", trace) end
end

function PreProcessor:PP_error(args, trace)
Expand Down
30 changes: 25 additions & 5 deletions lua/entities/gmod_wire_expression2/core/string.lua
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,33 @@ end
__e2setcost(2)

e2function string string:trim()
return this:Trim()
local ok, ret = pcall(function() WireLib.CheckRegex(this, "^%s*(.-)%s*$") return string.Trim(this) end)

if not ok then
return self:throw(ret)
else
return ret
end
end

e2function string string:trimLeft()
return this:TrimLeft()
local ok, ret = pcall(function() WireLib.CheckRegex(this, "^%s*(.+)$") return string.TrimLeft(this) end)

if not ok then
return self:throw(ret)
else
return ret
end
end

e2function string string:trimRight()
return this:TrimRight()
local ok, ret = pcall(function() WireLib.CheckRegex(this, "^(.-)%s*$") return string.TrimRight(this) end)

if not ok then
return self:throw(ret)
else
return ret
end
end

--[[******************************************************************************]]--
Expand All @@ -250,8 +268,9 @@ __e2setcost(10)
--- Returns the 1st occurrence of the string <pattern>, returns 0 if not found. Prints malformed string errors to the chat area.
e2function number string:findRE(string pattern)
local ok, ret = pcall(function() WireLib.CheckRegex(this, pattern) return string_find(this, pattern) end)

if not ok then
return self:throw(ret, 0)
return self:throw(ret)
else
return ret or 0
end
Expand All @@ -260,8 +279,9 @@ end
--- Returns the 1st occurrence of the string <pattern> starting at <start> and going to the end of the string, returns 0 if not found. Prints malformed string errors to the chat area.
e2function number string:findRE(string pattern, start)
local ok, ret = pcall(function() WireLib.CheckRegex(this, pattern) return string_find(this, pattern, start) end)

if not ok then
return self:throw(ret, 0)
return self:throw(ret)
else
return ret or 0
end
Expand Down
Loading