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

LuaLS annotations for E2 API #2855

Closed
wants to merge 1 commit 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
46 changes: 25 additions & 21 deletions lua/entities/gmod_wire_expression2/core/core.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,24 @@ end

__e2setcost(1) -- approximation

[nodiscard]
---@nodiscard
e2function number first()
return self.entity.first and 1 or 0
end

[nodiscard]
---@nodiscard
e2function number duped()
return self.entity.duped and 1 or 0
end

[nodiscard, deprecated = "Use the input event instead"]
---@nodiscard
---@deprecated Use the input event instead
e2function number inputClk()
return self.triggerinput and 1 or 0
end

[nodiscard, deprecated = "Use the input event instead"]
---@nodiscard
---@deprecated Use the input event instead
e2function string inputClkName()
return self.triggerinput or ""
end
Expand Down Expand Up @@ -78,19 +80,21 @@ local function dupefinished( TimedPasteData, TimedPasteDataCurrent )
end
hook.Add("AdvDupe_FinishPasting", "E2_dupefinished", dupefinished )

[nodiscard]
---@nodiscard
e2function number dupefinished()
return self.entity.dupefinished and 1 or 0
end

--- Returns 1 if this is the last() execution and caused by the entity being removed.
[nodiscard, deprecated = "Use the removed event instead"]
---@nodiscard
---@deprecated Use the removed event instead
e2function number removing()
return self.entity.removing and 1 or 0
end

--- If <activate> != 0, the chip will run once when it is removed, setting the last() flag when it does.
[nodiscard, deprecated = "Use the removed event instead"]
---@nodiscard
---@deprecated Use the removed event instead
e2function void runOnLast(activate)
if self.data.last then return end
self.data.runOnLast = activate ~= 0
Expand All @@ -106,7 +110,7 @@ e2function void exit()
end

do
[noreturn]
---@noreturn
e2function void error( string reason )
self:forceThrow(reason)
end
Expand All @@ -128,7 +132,7 @@ end

__e2setcost(100) -- approximation

[noreturn]
---@noreturn
e2function void reset()
self.Scope, self.ScopeID, self.Scopes = self.GlobalScope, 0, { [0] = self.GlobalScope }

Expand Down Expand Up @@ -166,43 +170,43 @@ local round = math.Round

__e2setcost(1) -- approximation

[nodiscard]
---@nodiscard
e2function number ops()
return round(self.prfbench)
end

[nodiscard]
---@nodiscard
e2function number entity:ops()
if not IsValid(this) or this:GetClass() ~= "gmod_wire_expression2" or not this.context then return 0 end
return round(this.context.prfbench)
end

[nodiscard]
---@nodiscard
e2function number opcounter()
return ceil(self.prf + self.prfcount)
end

[nodiscard]
---@nodiscard
e2function number cpuUsage()
return self.timebench
end

[nodiscard]
---@nodiscard
e2function number entity:cpuUsage()
if not IsValid(this) or this:GetClass() ~= "gmod_wire_expression2" or not this.context then return 0 end
return this.context.timebench
end

--- If used as a while loop condition, stabilizes the expression around <maxexceed> hardquota used.
[nodiscard]
---@nodiscard
e2function number perf()
if self.prf >= e2_tickquota*0.95-200 then return 0 end
if self.prf + self.prfcount >= e2_hardquota then return 0 end
if self.prf >= e2_softquota*2 then return 0 end
return 1
end

[nodiscard]
---@nodiscard
e2function number perf(number n)
n = math.Clamp(n, 0, 100)
if self.prf >= e2_tickquota*n*0.01 then return 0 end
Expand All @@ -215,7 +219,7 @@ e2function number perf(number n)
return 1
end

[nodiscard]
---@nodiscard
e2function number minquota()
if self.prf < e2_softquota then
return floor(e2_softquota - self.prf)
Expand All @@ -224,7 +228,7 @@ e2function number minquota()
end
end

[nodiscard]
---@nodiscard
e2function number maxquota()
if self.prf < e2_tickquota then
local tickquota = e2_tickquota - self.prf
Expand All @@ -240,17 +244,17 @@ e2function number maxquota()
end
end

[nodiscard]
---@nodiscard
e2function number softQuota()
return e2_softquota
end

[nodiscard]
---@nodiscard
e2function number hardQuota()
return e2_hardquota
end

[nodiscard]
---@nodiscard
e2function number timeQuota()
return e2_timequota
end
Expand Down
109 changes: 105 additions & 4 deletions lua/entities/gmod_wire_expression2/core/extpp.lua
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
-- e2function number foo()
local key, value = unpack(k:Split("="), 1, 2)
attrs[key:lower():Trim()] = value:Trim()
elseif not ValidAttributes[k] then

Check failure on line 116 in lua/entities/gmod_wire_expression2/core/extpp.lua

View workflow job for this annotation

GitHub Actions / lint

"Parse error"

unexpected elseif expecting label, if statement, function definition, numeric for loop, generic for loop, variable definition, function call or return statement
ErrorNoHalt("Invalid attribute fed to ExtPP: " .. k .. " " .. trace .. "\n")
else
attrs[tag:lower():Trim()] = "true"
Expand All @@ -124,6 +124,103 @@
end
end

local annotationHandlers = {}

local function checkAttributes(annotations)
local attributes = annotations.Attributes
if not attributes then
attributes = { legacy = "false" }
annotations.Attributes = attributes
end
return attributes
end

annotationHandlers.deprecated = function(annotations, msg)
local attrs = checkAttributes(annotations)
msg = msg:Trim()
if msg ~= "" then
attrs.deprecated = string.format("%q", msg)
else
attrs.deprecated = "true"
end
end

annotationHandlers.nodiscard = function(annotations)
checkAttributes(annotations).nodiscard = "true"
end

annotationHandlers.noreturn = function(annotations)
checkAttributes(annotations).noreturn = "true"
end

annotationHandlers.cost = function(annotations, cost)
annotations.Cost = tonumber(cost:Trim()) -- Will be nil if invalid number, won't be processed
end

---@param contents string
---@param endPos number
---@return table
local function parseFunctionAnnotations(contents, endPos, trace)
local output = {}
local annotationLines = {}
-- Funky reverse find
do
local lineEnd = endPos - 1
local checkedAttributes = false
local pastFirstLine = false -- This is here because I don't want to assumed every e2function follows a newline, yet most do
for i = lineEnd, 1, -1 do
if contents:sub(i, i) == "\n" then
local line = contents:sub(i + 1, lineEnd)
if line:sub(1, 3) == "---" then
table.insert(annotationLines, 1, line:sub(4))
lineEnd = i - 1
elseif not checkedAttributes then
local oldAttributes = line:match("%b[]")
if oldAttributes then
output.AttributesBegin = i + 1
output.Attributes = parseAttributes(oldAttributes, trace)
elseif pastFirstLine then
break
end
checkedAttributes = pastFirstLine
else
if pastFirstLine then break end
end
pastFirstLine = true
end
end
end
if #annotationLines ~= 0 then
for _, v in ipairs(annotationLines) do
if v:sub(1, 1) == "@" then
local param = v:match("^%S*", 2)
if param and annotationHandlers[param] then
annotationHandlers[param](output, v:sub(#param + 2))
end
--[[ -- Just kidding, let's not try to completely change how E2 descriptions are handled... yet
else -- Annotation comment

local comments = output.Comment ---@diagnostic disable-line: param-type-mismatch
if not comments then
comments = {}
output.Comment = comments
end
v = v:gsub("<br/?>", "\n"):gsub("\\n", "\n") -- Normalize newline escapes
comments[#comments + 1] = ((v:match("^[ \t]*(.-)[ \t\n\r]*$") or v) .. (v:sub(-1, -1) == "\n" and "" or " ")):gsub("\n", "\\n"):Trim()
]]
end
end

--[[
if output.Comment then
output.Comment = table.concat(output.Comment) ---@diagnostic disable-line: param-type-mismatch
end
]]
end
output.LineCount = #annotationLines
return output
end

--- Compact lua code to a single line to avoid changing lua's tracebacks.
local function compact(lua)
return (lua:Trim():gsub("\n\t*", " "))
Expand All @@ -147,7 +244,7 @@

-- This flag helps determine whether the preprocessor changed, so we can tell the environment about it.
local changed = false
for a_begin, attributes, h_begin, ret, thistype, colon, name, args, whitespace, equals, h_end in contents:gmatch("()(%[?[%w,_ =\"]*%]?)[\r\n\t ]*()e2function%s+(" .. p_typename .. ")%s+([a-z0-9]-)%s*(:?)%s*(" .. p_func_operator .. ")%(([^)]*)%)(%s*)(=?)%s*()") do
for h_begin, ret, thistype, colon, name, args, whitespace, equals, h_end in contents:gmatch("()e2function%s+(" .. p_typename .. ")%s+([a-z0-9]-)%s*(:?)%s*(" .. p_func_operator .. ")%(([^)]*)%)(%s*)(=?)%s*()") do
local _, line = contents:sub(1, h_begin):gsub("\n", "")

local trace = "(at line " .. line .. ")" .. (E2Lib.currentextension and (" @" .. filename) or "")
Expand All @@ -170,7 +267,8 @@
error("PP syntax error: Invalid return type: '" .. ret .. "' " .. trace)
elseif RemovedOperators[name] then -- Old operator that no longer is needed.
ErrorNoHalt("Warning: Operator " .. name .. " is now redundant. Ignoring registration. " .. trace .. "\n")
local pivot = parseAttributes(attributes, trace) and a_begin - 1 or h_begin - 1
local annotations = parseAnnotations(contents, h_begin, trace)
local pivot = annotations.AttributesBegin and annotations.AttributesBegin - 1 or h_begin - 1
table.insert(output, contents:sub(lastpos, pivot)) -- Insert code from before header.
changed, lastpos = true, h_end -- Mark as changed and remove function header.
table.insert(output, "local _ = function() ") -- Insert dummy lambda function to substitute for function declaration.
Expand All @@ -193,7 +291,10 @@

local params, has_vararg, vartbl_name = parseParameters(args, trace)

local attributes = parseAttributes(attributes, trace)
local annotations = parseFunctionAnnotations(contents, h_begin, trace)

local attributes = annotations.Attributes
local a_begin = annotations.AttributesBegin or h_begin

local attr_str
if attributes then
Expand Down Expand Up @@ -250,7 +351,7 @@
"]] .. param_sig .. (has_vararg and "..." or "") .. [[",
"]] .. ret_typeid .. [[",
registeredfunctions.]] .. mangled .. [[,
tempcosts.]] .. mangled .. [[,
]] .. (annotations.Cost and tostring(annotations.Cost) or ([[tempcosts.]] .. mangled)) .. [[,
]] .. "{" .. table.concat(param_names_quot, ",", thistype ~= "" and 2 or 1) .. "}" .. [[,
]] .. attr_str .. [[
)
Expand Down
Loading