-- This file is part of Reno desktop. -- -- Reno desktop is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. -- -- Reno desktop is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License along with Reno desktop. If not, see . -- Various utilitiy parsers local parsers = {} local function split_strings(text) -- probably the cleanest function to split by strings i've written to date local split = {} while text:find("\"") do local strstart = text:find("\"") while text:sub(strstart-1,strstart-1) == "\\" do strstart = text:find("\"",strstart+1) end local strend = text:find("\"",strstart+1) while text:sub(strend-1,strend-1) == "\\" do strend = text:find("\"",strend+1) end if not strend then return nil, "String not closed at "..strstart end local before_string = text:sub(1,strstart-1) local string = text:sub(strstart,strend):gsub("\\\"","\"") text = text:sub(strend+1,-1) table.insert(split,before_string) table.insert(split,string) end table.insert(split,text) return split end parsers.fast_split_yaml = function(cfgtext) -- Fast yaml splitter - incomplete parsing, only first layer is parsed -- Used within timers to find objects while decreasing CPU usage local items = {} local replacements = 1 cfgtext = cfgtext:gsub("^%s*","") while replacements > 0 do cfgtext,replacements = cfgtext:gsub("^(.-\n)(%S+)",function(struct,n) table.insert(items,struct) return ""..n end) end table.insert(items,cfgtext) return items end parsers.yaml_pseudo = function(cfgtext) -- Somewhat yaml-like structure used by pactl local struct = {} local lines = {} cfgtext:gsub("(%s*)([^\n]*)",function(spacing,line) table.insert(lines, { spacing:len(), -- key line:match("^([^:=]-)%s*[:=]") or line, -- value line:match(":%s*(.-)%s*$") or line:match("=%s*(.-)%s*$") } ) end) local history = {struct} local spacing_width = 0 for k,v in pairs(lines) do if v[1] > spacing_width then history[#history][lines[k-1][2]] = { [lines[k-1][2]] = lines[k-1][3] } history[#history+1] = history[#history][lines[k-1][2]] elseif v[1] < spacing_width then history[#history] = nil end if v[3] and v[3]:match("^%s*\".*\"%s*$") then history[#history][v[2]] = v[3]:match("^%s*\"(.*)\"%s*$") else history[#history][v[2]] = v[3] end spacing_width = v[1] end return struct end local function quotestrip(txt) if (txt:sub(1,1):match("['\"]")) and (txt:sub(-1,-1) == txt:sub(1,1)) then return txt:sub(2,-2) else return txt end end parsers.conf = function(cfgtext) -- Conf style parser (not exactly TOML) cfgtext = cfgtext:gsub("#[^\n]*","") local split_by_strings,err = split_strings(cfgtext) if not split_by_strings then error(err) end local full_split = {{}} local current_line = full_split[1] -- tokenizer for _,v in pairs(split_by_strings) do v = v:match("^[ \t]*(.*)[ \t]*$") if (not (v == "")) then if not v:match("^\".*\"$") then v:gsub("[^ \t]+",function(text) while text:match("\n") do local before,after = text:match("([^\n]*)\n(.*)") if before ~= "" then table.insert(current_line,before) end if #current_line > 0 then table.insert(full_split,{}) current_line = full_split[#full_split] end text = after end if text ~= "" then table.insert(current_line,text) end end) else table.insert(current_line,v) end end end table.remove(full_split,#full_split) local struct = {global = {}} local block = "global" -- parser for _,line in pairs(full_split) do if line[1] and line[1]:match("^%[[^%]]+%]$") then -- block block = line[1]:match("^%[([^%]]+)%]$") struct[block] = {} elseif #line == 3 then -- assignment if (line[3]:sub(1,1):match("['\"]")) -- string and (line[3]:sub(-1,-1) == line[3]:sub(1,1)) then struct[block][quotestrip(line[1])] = quotestrip(line[3]) elseif line[3]:match("^%d+$") then -- number struct[block][quotestrip(line[1])] = tonumber(line[3]) elseif (line[3] == "true") or (line[3] == "false") then -- boolean struct[block][quotestrip(line[1])] = (line[3] == "true") else error("Invalid assignment expression: "..line[3]) end else -- invalid local textline = "" for _,v in pairs(line) do textline = textline..v.." " end error("Invalid config expression: "..textline:sub(1,-2)) end end return struct end return parsers