--[[ SaferLua [safer_lua] ==================== Copyright (C) 2017-2020 Joachim Stolberg AGPL v3 See LICENSE.txt for more information scanner.lua: ]]-- local function trim(s) return (s:gsub("^%s*(.-)%s*$", "%1")) end function safer_lua:word(ch, pttrn) local word = "" while ch:match(pttrn) do word = word .. ch self.pos = self.pos + 1 ch = self.line:sub(self.pos, self.pos) end return word end function safer_lua:string(pttrn) self.pos = self.pos + 1 local ch = self.line:sub(self.pos, self.pos) while not ch:match(pttrn) and self.pos < #self.line do if ch == "\\" then self.pos = self.pos + 1 end self.pos = self.pos + 1 ch = self.line:sub(self.pos, self.pos) end self.pos = self.pos + 1 -- result is not needed end local function lines(str) local t = {} local function helper(line) table.insert(t, line) return "" end helper((str:gsub("(.-)\r?\n", helper))) return t end function safer_lua:scanner(text) local lToken = {} for idx, line in ipairs(lines(text)) do self.line = line self.pos = 1 self.line = trim(self.line) self.line = string.split(self.line, "--", true, 1)[1] table.insert(lToken, idx) -- line number if self.line then -- devide line in tokens while true do if self.pos > #self.line then break end local ch = self.line:sub(self.pos, self.pos) if ch:match("[%u%l_]") then -- identifier? table.insert(lToken, self:word(ch, "[%w_]")) elseif ch:match("[%d]") then -- number? table.insert(lToken, self:word(ch, "[%d%xx]")) elseif ch:match("'") then -- string? self:string("'") elseif ch:match('"') then -- string? self:string('"') elseif ch:match("[%s]") then -- Space? self.pos = self.pos + 1 elseif ch:match("[:{}]") then -- critical tokens? table.insert(lToken,ch) self.pos = self.pos + 1 else self.pos = self.pos + 1 end end end end return lToken end local InvalidKeywords = { ["while"] = true, ["repeat"] = true, ["until"] = true, ["for"] = true, ["range"] = true, --["function"] = true, ["_G"] = true, ["__load"] = true, ["__dump"] = true, } local InvalidChars = { [":"] = true, ["{"] = true, ["["] = true, ["]"] = true, ["}"] = true, } function safer_lua:check(pos, text, label, err_clbk) local lToken = self:scanner(text) local lineno = 0 local errno = 0 for idx,token in ipairs(lToken) do if type(token) == "number" then lineno = token elseif InvalidKeywords[token] then if token == "for" then -- invalid for statement? if lToken[idx + 3] == "in" and lToken[idx + 5] == "next" then -- elseif lToken[idx + 2] == "in" and lToken[idx + 3] == "range" then -- else err_clbk(pos, label..":"..lineno..": Invalid use of 'for'") errno = errno + 1 end elseif token == "range" then if lToken[idx - 1] ~= "in" then err_clbk(pos, label..":"..lineno..": Invalid use of 'range'") errno = errno + 1 end else err_clbk(pos, label..":"..lineno..": Invalid keyword '"..token.."'") errno = errno + 1 end elseif InvalidChars[token] then err_clbk(pos, label..":"..lineno..": Invalid character '"..token.."'") errno = errno + 1 end end return errno end