1*50498135SJose Luis Duran--- 2a42d6f76SBaptiste Daroussin-- SPDX-License-Identifier: MIT 3a42d6f76SBaptiste Daroussin-- 4a42d6f76SBaptiste Daroussin-- Copyright (c) 2017 Dominic Letz dominicletz@exosite.com 5a42d6f76SBaptiste Daroussin 6a42d6f76SBaptiste Daroussinlocal table_print_value 7a42d6f76SBaptiste Daroussintable_print_value = function(value, indent, done) 8a42d6f76SBaptiste Daroussin indent = indent or 0 9a42d6f76SBaptiste Daroussin done = done or {} 10a42d6f76SBaptiste Daroussin if type(value) == "table" and not done [value] then 11a42d6f76SBaptiste Daroussin done [value] = true 12a42d6f76SBaptiste Daroussin 13a42d6f76SBaptiste Daroussin local list = {} 14a42d6f76SBaptiste Daroussin for key in pairs (value) do 15a42d6f76SBaptiste Daroussin list[#list + 1] = key 16a42d6f76SBaptiste Daroussin end 17a42d6f76SBaptiste Daroussin table.sort(list, function(a, b) return tostring(a) < tostring(b) end) 18a42d6f76SBaptiste Daroussin local last = list[#list] 19a42d6f76SBaptiste Daroussin 20a42d6f76SBaptiste Daroussin local rep = "{\n" 21a42d6f76SBaptiste Daroussin local comma 22a42d6f76SBaptiste Daroussin for _, key in ipairs (list) do 23a42d6f76SBaptiste Daroussin if key == last then 24a42d6f76SBaptiste Daroussin comma = '' 25a42d6f76SBaptiste Daroussin else 26a42d6f76SBaptiste Daroussin comma = ',' 27a42d6f76SBaptiste Daroussin end 28a42d6f76SBaptiste Daroussin local keyRep 29a42d6f76SBaptiste Daroussin if type(key) == "number" then 30a42d6f76SBaptiste Daroussin keyRep = key 31a42d6f76SBaptiste Daroussin else 32a42d6f76SBaptiste Daroussin keyRep = string.format("%q", tostring(key)) 33a42d6f76SBaptiste Daroussin end 34a42d6f76SBaptiste Daroussin rep = rep .. string.format( 35a42d6f76SBaptiste Daroussin "%s[%s] = %s%s\n", 36a42d6f76SBaptiste Daroussin string.rep(" ", indent + 2), 37a42d6f76SBaptiste Daroussin keyRep, 38a42d6f76SBaptiste Daroussin table_print_value(value[key], indent + 2, done), 39a42d6f76SBaptiste Daroussin comma 40a42d6f76SBaptiste Daroussin ) 41a42d6f76SBaptiste Daroussin end 42a42d6f76SBaptiste Daroussin 43a42d6f76SBaptiste Daroussin rep = rep .. string.rep(" ", indent) -- indent it 44a42d6f76SBaptiste Daroussin rep = rep .. "}" 45a42d6f76SBaptiste Daroussin 46a42d6f76SBaptiste Daroussin done[value] = false 47a42d6f76SBaptiste Daroussin return rep 48a42d6f76SBaptiste Daroussin elseif type(value) == "string" then 49a42d6f76SBaptiste Daroussin return string.format("%q", value) 50a42d6f76SBaptiste Daroussin else 51a42d6f76SBaptiste Daroussin return tostring(value) 52a42d6f76SBaptiste Daroussin end 53a42d6f76SBaptiste Daroussinend 54a42d6f76SBaptiste Daroussin 55a42d6f76SBaptiste Daroussinlocal table_print = function(tt) 56a42d6f76SBaptiste Daroussin print('return '..table_print_value(tt)) 57a42d6f76SBaptiste Daroussinend 58a42d6f76SBaptiste Daroussin 59a42d6f76SBaptiste Daroussinlocal table_clone = function(t) 60a42d6f76SBaptiste Daroussin local clone = {} 61a42d6f76SBaptiste Daroussin for k,v in pairs(t) do 62a42d6f76SBaptiste Daroussin clone[k] = v 63a42d6f76SBaptiste Daroussin end 64a42d6f76SBaptiste Daroussin return clone 65a42d6f76SBaptiste Daroussinend 66a42d6f76SBaptiste Daroussin 67a42d6f76SBaptiste Daroussinlocal string_trim = function(s, what) 68a42d6f76SBaptiste Daroussin what = what or " " 69a42d6f76SBaptiste Daroussin return s:gsub("^[" .. what .. "]*(.-)["..what.."]*$", "%1") 70a42d6f76SBaptiste Daroussinend 71a42d6f76SBaptiste Daroussin 72a42d6f76SBaptiste Daroussinlocal push = function(stack, item) 73a42d6f76SBaptiste Daroussin stack[#stack + 1] = item 74a42d6f76SBaptiste Daroussinend 75a42d6f76SBaptiste Daroussin 76a42d6f76SBaptiste Daroussinlocal pop = function(stack) 77a42d6f76SBaptiste Daroussin local item = stack[#stack] 78a42d6f76SBaptiste Daroussin stack[#stack] = nil 79a42d6f76SBaptiste Daroussin return item 80a42d6f76SBaptiste Daroussinend 81a42d6f76SBaptiste Daroussin 82a42d6f76SBaptiste Daroussinlocal context = function (str) 83a42d6f76SBaptiste Daroussin if type(str) ~= "string" then 84a42d6f76SBaptiste Daroussin return "" 85a42d6f76SBaptiste Daroussin end 86a42d6f76SBaptiste Daroussin 87a42d6f76SBaptiste Daroussin str = str:sub(0,25):gsub("\n","\\n"):gsub("\"","\\\""); 88a42d6f76SBaptiste Daroussin return ", near \"" .. str .. "\"" 89a42d6f76SBaptiste Daroussinend 90a42d6f76SBaptiste Daroussin 91a42d6f76SBaptiste Daroussinlocal Parser = {} 92a42d6f76SBaptiste Daroussinfunction Parser.new (self, tokens) 93a42d6f76SBaptiste Daroussin self.tokens = tokens 94a42d6f76SBaptiste Daroussin self.parse_stack = {} 95a42d6f76SBaptiste Daroussin self.refs = {} 96a42d6f76SBaptiste Daroussin self.current = 0 97a42d6f76SBaptiste Daroussin return self 98a42d6f76SBaptiste Daroussinend 99a42d6f76SBaptiste Daroussin 100a42d6f76SBaptiste Daroussinlocal exports = {version = "1.2"} 101a42d6f76SBaptiste Daroussin 102a42d6f76SBaptiste Daroussinlocal word = function(w) return "^("..w..")([%s$%c])" end 103a42d6f76SBaptiste Daroussin 104a42d6f76SBaptiste Daroussinlocal tokens = { 105a42d6f76SBaptiste Daroussin {"comment", "^#[^\n]*"}, 106a42d6f76SBaptiste Daroussin {"indent", "^\n( *)"}, 107a42d6f76SBaptiste Daroussin {"space", "^ +"}, 108a42d6f76SBaptiste Daroussin {"true", word("enabled"), const = true, value = true}, 109a42d6f76SBaptiste Daroussin {"true", word("true"), const = true, value = true}, 110a42d6f76SBaptiste Daroussin {"true", word("yes"), const = true, value = true}, 111a42d6f76SBaptiste Daroussin {"true", word("on"), const = true, value = true}, 112a42d6f76SBaptiste Daroussin {"false", word("disabled"), const = true, value = false}, 113a42d6f76SBaptiste Daroussin {"false", word("false"), const = true, value = false}, 114a42d6f76SBaptiste Daroussin {"false", word("no"), const = true, value = false}, 115a42d6f76SBaptiste Daroussin {"false", word("off"), const = true, value = false}, 116a42d6f76SBaptiste Daroussin {"null", word("null"), const = true, value = nil}, 117a42d6f76SBaptiste Daroussin {"null", word("Null"), const = true, value = nil}, 118a42d6f76SBaptiste Daroussin {"null", word("NULL"), const = true, value = nil}, 119a42d6f76SBaptiste Daroussin {"null", word("~"), const = true, value = nil}, 120a42d6f76SBaptiste Daroussin {"id", "^\"([^\"]-)\" *(:[%s%c])"}, 121a42d6f76SBaptiste Daroussin {"id", "^'([^']-)' *(:[%s%c])"}, 122a42d6f76SBaptiste Daroussin {"string", "^\"([^\"]-)\"", force_text = true}, 123a42d6f76SBaptiste Daroussin {"string", "^'([^']-)'", force_text = true}, 124a42d6f76SBaptiste Daroussin {"timestamp", "^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?):(%d%d):(%d%d)%s+(%-?%d%d?):(%d%d)"}, 125a42d6f76SBaptiste Daroussin {"timestamp", "^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?):(%d%d):(%d%d)%s+(%-?%d%d?)"}, 126a42d6f76SBaptiste Daroussin {"timestamp", "^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?):(%d%d):(%d%d)"}, 127a42d6f76SBaptiste Daroussin {"timestamp", "^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?):(%d%d)"}, 128a42d6f76SBaptiste Daroussin {"timestamp", "^(%d%d%d%d)-(%d%d?)-(%d%d?)%s+(%d%d?)"}, 129a42d6f76SBaptiste Daroussin {"timestamp", "^(%d%d%d%d)-(%d%d?)-(%d%d?)"}, 130a42d6f76SBaptiste Daroussin {"doc", "^%-%-%-[^%c]*"}, 131a42d6f76SBaptiste Daroussin {",", "^,"}, 132a42d6f76SBaptiste Daroussin {"string", "^%b{} *[^,%c]+", noinline = true}, 133a42d6f76SBaptiste Daroussin {"{", "^{"}, 134a42d6f76SBaptiste Daroussin {"}", "^}"}, 135a42d6f76SBaptiste Daroussin {"string", "^%b[] *[^,%c]+", noinline = true}, 136a42d6f76SBaptiste Daroussin {"[", "^%["}, 137a42d6f76SBaptiste Daroussin {"]", "^%]"}, 138a42d6f76SBaptiste Daroussin {"-", "^%-", noinline = true}, 139a42d6f76SBaptiste Daroussin {":", "^:"}, 140a42d6f76SBaptiste Daroussin {"pipe", "^(|)(%d*[+%-]?)", sep = "\n"}, 141a42d6f76SBaptiste Daroussin {"pipe", "^(>)(%d*[+%-]?)", sep = " "}, 142a42d6f76SBaptiste Daroussin {"id", "^([%w][%w %-_]*)(:[%s%c])"}, 143a42d6f76SBaptiste Daroussin {"string", "^[^%c]+", noinline = true}, 144a42d6f76SBaptiste Daroussin {"string", "^[^,%]}%c ]+"} 145a42d6f76SBaptiste Daroussin}; 146a42d6f76SBaptiste Daroussinexports.tokenize = function (str) 147a42d6f76SBaptiste Daroussin local token 148a42d6f76SBaptiste Daroussin local row = 0 149a42d6f76SBaptiste Daroussin local ignore 150a42d6f76SBaptiste Daroussin local indents = 0 151a42d6f76SBaptiste Daroussin local lastIndents 152a42d6f76SBaptiste Daroussin local stack = {} 153a42d6f76SBaptiste Daroussin local indentAmount = 0 154a42d6f76SBaptiste Daroussin local inline = false 155a42d6f76SBaptiste Daroussin str = str:gsub("\r\n","\010") 156a42d6f76SBaptiste Daroussin 157a42d6f76SBaptiste Daroussin while #str > 0 do 158a42d6f76SBaptiste Daroussin for i in ipairs(tokens) do 159a42d6f76SBaptiste Daroussin local captures = {} 160a42d6f76SBaptiste Daroussin if not inline or tokens[i].noinline == nil then 161a42d6f76SBaptiste Daroussin captures = {str:match(tokens[i][2])} 162a42d6f76SBaptiste Daroussin end 163a42d6f76SBaptiste Daroussin 164a42d6f76SBaptiste Daroussin if #captures > 0 then 165a42d6f76SBaptiste Daroussin captures.input = str:sub(0, 25) 166a42d6f76SBaptiste Daroussin token = table_clone(tokens[i]) 167a42d6f76SBaptiste Daroussin token[2] = captures 168a42d6f76SBaptiste Daroussin local str2 = str:gsub(tokens[i][2], "", 1) 169a42d6f76SBaptiste Daroussin token.raw = str:sub(1, #str - #str2) 170a42d6f76SBaptiste Daroussin str = str2 171a42d6f76SBaptiste Daroussin 172a42d6f76SBaptiste Daroussin if token[1] == "{" or token[1] == "[" then 173a42d6f76SBaptiste Daroussin inline = true 174a42d6f76SBaptiste Daroussin elseif token.const then 175a42d6f76SBaptiste Daroussin -- Since word pattern contains last char we're re-adding it 176a42d6f76SBaptiste Daroussin str = token[2][2] .. str 177a42d6f76SBaptiste Daroussin token.raw = token.raw:sub(1, #token.raw - #token[2][2]) 178a42d6f76SBaptiste Daroussin elseif token[1] == "id" then 179a42d6f76SBaptiste Daroussin -- Since id pattern contains last semi-colon we're re-adding it 180a42d6f76SBaptiste Daroussin str = token[2][2] .. str 181a42d6f76SBaptiste Daroussin token.raw = token.raw:sub(1, #token.raw - #token[2][2]) 182a42d6f76SBaptiste Daroussin -- Trim 183a42d6f76SBaptiste Daroussin token[2][1] = string_trim(token[2][1]) 184a42d6f76SBaptiste Daroussin elseif token[1] == "string" then 185a42d6f76SBaptiste Daroussin -- Finding numbers 186a42d6f76SBaptiste Daroussin local snip = token[2][1] 187a42d6f76SBaptiste Daroussin if not token.force_text then 188a42d6f76SBaptiste Daroussin if snip:match("^(-?%d+%.%d+)$") or snip:match("^(-?%d+)$") then 189a42d6f76SBaptiste Daroussin token[1] = "number" 190a42d6f76SBaptiste Daroussin end 191a42d6f76SBaptiste Daroussin end 192a42d6f76SBaptiste Daroussin 193a42d6f76SBaptiste Daroussin elseif token[1] == "comment" then 194a42d6f76SBaptiste Daroussin ignore = true; 195a42d6f76SBaptiste Daroussin elseif token[1] == "indent" then 196a42d6f76SBaptiste Daroussin row = row + 1 197a42d6f76SBaptiste Daroussin inline = false 198a42d6f76SBaptiste Daroussin lastIndents = indents 199a42d6f76SBaptiste Daroussin if indentAmount == 0 then 200a42d6f76SBaptiste Daroussin indentAmount = #token[2][1] 201a42d6f76SBaptiste Daroussin end 202a42d6f76SBaptiste Daroussin 203a42d6f76SBaptiste Daroussin if indentAmount ~= 0 then 204a42d6f76SBaptiste Daroussin indents = (#token[2][1] / indentAmount); 205a42d6f76SBaptiste Daroussin else 206a42d6f76SBaptiste Daroussin indents = 0 207a42d6f76SBaptiste Daroussin end 208a42d6f76SBaptiste Daroussin 209a42d6f76SBaptiste Daroussin if indents == lastIndents then 210a42d6f76SBaptiste Daroussin ignore = true; 211a42d6f76SBaptiste Daroussin elseif indents > lastIndents + 2 then 212a42d6f76SBaptiste Daroussin error("SyntaxError: invalid indentation, got " .. tostring(indents) 213a42d6f76SBaptiste Daroussin .. " instead of " .. tostring(lastIndents) .. context(token[2].input)) 214a42d6f76SBaptiste Daroussin elseif indents > lastIndents + 1 then 215a42d6f76SBaptiste Daroussin push(stack, token) 216a42d6f76SBaptiste Daroussin elseif indents < lastIndents then 217a42d6f76SBaptiste Daroussin local input = token[2].input 218a42d6f76SBaptiste Daroussin token = {"dedent", {"", input = ""}} 219a42d6f76SBaptiste Daroussin token.input = input 220a42d6f76SBaptiste Daroussin while lastIndents > indents + 1 do 221a42d6f76SBaptiste Daroussin lastIndents = lastIndents - 1 222a42d6f76SBaptiste Daroussin push(stack, token) 223a42d6f76SBaptiste Daroussin end 224a42d6f76SBaptiste Daroussin end 225a42d6f76SBaptiste Daroussin end -- if token[1] == XXX 226a42d6f76SBaptiste Daroussin token.row = row 227a42d6f76SBaptiste Daroussin break 228a42d6f76SBaptiste Daroussin end -- if #captures > 0 229a42d6f76SBaptiste Daroussin end 230a42d6f76SBaptiste Daroussin 231a42d6f76SBaptiste Daroussin if not ignore then 232a42d6f76SBaptiste Daroussin if token then 233a42d6f76SBaptiste Daroussin push(stack, token) 234a42d6f76SBaptiste Daroussin token = nil 235a42d6f76SBaptiste Daroussin else 236a42d6f76SBaptiste Daroussin error("SyntaxError " .. context(str)) 237a42d6f76SBaptiste Daroussin end 238a42d6f76SBaptiste Daroussin end 239a42d6f76SBaptiste Daroussin 240a42d6f76SBaptiste Daroussin ignore = false; 241a42d6f76SBaptiste Daroussin end 242a42d6f76SBaptiste Daroussin 243a42d6f76SBaptiste Daroussin return stack 244a42d6f76SBaptiste Daroussinend 245a42d6f76SBaptiste Daroussin 246a42d6f76SBaptiste DaroussinParser.peek = function (self, offset) 247a42d6f76SBaptiste Daroussin offset = offset or 1 248a42d6f76SBaptiste Daroussin return self.tokens[offset + self.current] 249a42d6f76SBaptiste Daroussinend 250a42d6f76SBaptiste Daroussin 251a42d6f76SBaptiste DaroussinParser.advance = function (self) 252a42d6f76SBaptiste Daroussin self.current = self.current + 1 253a42d6f76SBaptiste Daroussin return self.tokens[self.current] 254a42d6f76SBaptiste Daroussinend 255a42d6f76SBaptiste Daroussin 256a42d6f76SBaptiste DaroussinParser.advanceValue = function (self) 257a42d6f76SBaptiste Daroussin return self:advance()[2][1] 258a42d6f76SBaptiste Daroussinend 259a42d6f76SBaptiste Daroussin 260a42d6f76SBaptiste DaroussinParser.accept = function (self, type) 261a42d6f76SBaptiste Daroussin if self:peekType(type) then 262a42d6f76SBaptiste Daroussin return self:advance() 263a42d6f76SBaptiste Daroussin end 264a42d6f76SBaptiste Daroussinend 265a42d6f76SBaptiste Daroussin 266a42d6f76SBaptiste DaroussinParser.expect = function (self, type, msg) 267a42d6f76SBaptiste Daroussin return self:accept(type) or 268a42d6f76SBaptiste Daroussin error(msg .. context(self:peek()[1].input)) 269a42d6f76SBaptiste Daroussinend 270a42d6f76SBaptiste Daroussin 271a42d6f76SBaptiste DaroussinParser.expectDedent = function (self, msg) 272a42d6f76SBaptiste Daroussin return self:accept("dedent") or (self:peek() == nil) or 273a42d6f76SBaptiste Daroussin error(msg .. context(self:peek()[2].input)) 274a42d6f76SBaptiste Daroussinend 275a42d6f76SBaptiste Daroussin 276a42d6f76SBaptiste DaroussinParser.peekType = function (self, val, offset) 277a42d6f76SBaptiste Daroussin return self:peek(offset) and self:peek(offset)[1] == val 278a42d6f76SBaptiste Daroussinend 279a42d6f76SBaptiste Daroussin 280a42d6f76SBaptiste DaroussinParser.ignore = function (self, items) 281a42d6f76SBaptiste Daroussin local advanced 282a42d6f76SBaptiste Daroussin repeat 283a42d6f76SBaptiste Daroussin advanced = false 284a42d6f76SBaptiste Daroussin for _,v in pairs(items) do 285a42d6f76SBaptiste Daroussin if self:peekType(v) then 286a42d6f76SBaptiste Daroussin self:advance() 287a42d6f76SBaptiste Daroussin advanced = true 288a42d6f76SBaptiste Daroussin end 289a42d6f76SBaptiste Daroussin end 290a42d6f76SBaptiste Daroussin until advanced == false 291a42d6f76SBaptiste Daroussinend 292a42d6f76SBaptiste Daroussin 293a42d6f76SBaptiste DaroussinParser.ignoreSpace = function (self) 294a42d6f76SBaptiste Daroussin self:ignore{"space"} 295a42d6f76SBaptiste Daroussinend 296a42d6f76SBaptiste Daroussin 297a42d6f76SBaptiste DaroussinParser.ignoreWhitespace = function (self) 298a42d6f76SBaptiste Daroussin self:ignore{"space", "indent", "dedent"} 299a42d6f76SBaptiste Daroussinend 300a42d6f76SBaptiste Daroussin 301a42d6f76SBaptiste DaroussinParser.parse = function (self) 302a42d6f76SBaptiste Daroussin 303a42d6f76SBaptiste Daroussin local ref = nil 304a42d6f76SBaptiste Daroussin if self:peekType("string") and not self:peek().force_text then 305a42d6f76SBaptiste Daroussin local char = self:peek()[2][1]:sub(1,1) 306a42d6f76SBaptiste Daroussin if char == "&" then 307a42d6f76SBaptiste Daroussin ref = self:peek()[2][1]:sub(2) 308a42d6f76SBaptiste Daroussin self:advanceValue() 309a42d6f76SBaptiste Daroussin self:ignoreSpace() 310a42d6f76SBaptiste Daroussin elseif char == "*" then 311a42d6f76SBaptiste Daroussin ref = self:peek()[2][1]:sub(2) 312a42d6f76SBaptiste Daroussin return self.refs[ref] 313a42d6f76SBaptiste Daroussin end 314a42d6f76SBaptiste Daroussin end 315a42d6f76SBaptiste Daroussin 316a42d6f76SBaptiste Daroussin local result 317a42d6f76SBaptiste Daroussin local c = { 318a42d6f76SBaptiste Daroussin indent = self:accept("indent") and 1 or 0, 319a42d6f76SBaptiste Daroussin token = self:peek() 320a42d6f76SBaptiste Daroussin } 321a42d6f76SBaptiste Daroussin push(self.parse_stack, c) 322a42d6f76SBaptiste Daroussin 323a42d6f76SBaptiste Daroussin if c.token[1] == "doc" then 324a42d6f76SBaptiste Daroussin result = self:parseDoc() 325a42d6f76SBaptiste Daroussin elseif c.token[1] == "-" then 326a42d6f76SBaptiste Daroussin result = self:parseList() 327a42d6f76SBaptiste Daroussin elseif c.token[1] == "{" then 328a42d6f76SBaptiste Daroussin result = self:parseInlineHash() 329a42d6f76SBaptiste Daroussin elseif c.token[1] == "[" then 330a42d6f76SBaptiste Daroussin result = self:parseInlineList() 331a42d6f76SBaptiste Daroussin elseif c.token[1] == "id" then 332a42d6f76SBaptiste Daroussin result = self:parseHash() 333a42d6f76SBaptiste Daroussin elseif c.token[1] == "string" then 334a42d6f76SBaptiste Daroussin result = self:parseString("\n") 335a42d6f76SBaptiste Daroussin elseif c.token[1] == "timestamp" then 336a42d6f76SBaptiste Daroussin result = self:parseTimestamp() 337a42d6f76SBaptiste Daroussin elseif c.token[1] == "number" then 338a42d6f76SBaptiste Daroussin result = tonumber(self:advanceValue()) 339a42d6f76SBaptiste Daroussin elseif c.token[1] == "pipe" then 340a42d6f76SBaptiste Daroussin result = self:parsePipe() 341a42d6f76SBaptiste Daroussin elseif c.token.const == true then 342a42d6f76SBaptiste Daroussin self:advanceValue(); 343a42d6f76SBaptiste Daroussin result = c.token.value 344a42d6f76SBaptiste Daroussin else 345a42d6f76SBaptiste Daroussin error("ParseError: unexpected token '" .. c.token[1] .. "'" .. context(c.token.input)) 346a42d6f76SBaptiste Daroussin end 347a42d6f76SBaptiste Daroussin 348a42d6f76SBaptiste Daroussin pop(self.parse_stack) 349a42d6f76SBaptiste Daroussin while c.indent > 0 do 350a42d6f76SBaptiste Daroussin c.indent = c.indent - 1 351a42d6f76SBaptiste Daroussin local term = "term "..c.token[1]..": '"..c.token[2][1].."'" 352a42d6f76SBaptiste Daroussin self:expectDedent("last ".. term .." is not properly dedented") 353a42d6f76SBaptiste Daroussin end 354a42d6f76SBaptiste Daroussin 355a42d6f76SBaptiste Daroussin if ref then 356a42d6f76SBaptiste Daroussin self.refs[ref] = result 357a42d6f76SBaptiste Daroussin end 358a42d6f76SBaptiste Daroussin return result 359a42d6f76SBaptiste Daroussinend 360a42d6f76SBaptiste Daroussin 361a42d6f76SBaptiste DaroussinParser.parseDoc = function (self) 362a42d6f76SBaptiste Daroussin self:accept("doc") 363a42d6f76SBaptiste Daroussin return self:parse() 364a42d6f76SBaptiste Daroussinend 365a42d6f76SBaptiste Daroussin 366a42d6f76SBaptiste DaroussinParser.inline = function (self) 367a42d6f76SBaptiste Daroussin local current = self:peek(0) 368a42d6f76SBaptiste Daroussin if not current then 369a42d6f76SBaptiste Daroussin return {}, 0 370a42d6f76SBaptiste Daroussin end 371a42d6f76SBaptiste Daroussin 372a42d6f76SBaptiste Daroussin local inline = {} 373a42d6f76SBaptiste Daroussin local i = 0 374a42d6f76SBaptiste Daroussin 375a42d6f76SBaptiste Daroussin while self:peek(i) and not self:peekType("indent", i) and current.row == self:peek(i).row do 376a42d6f76SBaptiste Daroussin inline[self:peek(i)[1]] = true 377a42d6f76SBaptiste Daroussin i = i - 1 378a42d6f76SBaptiste Daroussin end 379a42d6f76SBaptiste Daroussin return inline, -i 380a42d6f76SBaptiste Daroussinend 381a42d6f76SBaptiste Daroussin 382a42d6f76SBaptiste DaroussinParser.isInline = function (self) 383a42d6f76SBaptiste Daroussin local _, i = self:inline() 384a42d6f76SBaptiste Daroussin return i > 0 385a42d6f76SBaptiste Daroussinend 386a42d6f76SBaptiste Daroussin 387a42d6f76SBaptiste DaroussinParser.parent = function(self, level) 388a42d6f76SBaptiste Daroussin level = level or 1 389a42d6f76SBaptiste Daroussin return self.parse_stack[#self.parse_stack - level] 390a42d6f76SBaptiste Daroussinend 391a42d6f76SBaptiste Daroussin 392a42d6f76SBaptiste DaroussinParser.parentType = function(self, type, level) 393a42d6f76SBaptiste Daroussin return self:parent(level) and self:parent(level).token[1] == type 394a42d6f76SBaptiste Daroussinend 395a42d6f76SBaptiste Daroussin 396a42d6f76SBaptiste DaroussinParser.parseString = function (self) 397a42d6f76SBaptiste Daroussin if self:isInline() then 398a42d6f76SBaptiste Daroussin local result = self:advanceValue() 399a42d6f76SBaptiste Daroussin 400a42d6f76SBaptiste Daroussin --[[ 401a42d6f76SBaptiste Daroussin - a: this looks 402a42d6f76SBaptiste Daroussin flowing: but is 403a42d6f76SBaptiste Daroussin no: string 404a42d6f76SBaptiste Daroussin --]] 405a42d6f76SBaptiste Daroussin local types = self:inline() 406a42d6f76SBaptiste Daroussin if types["id"] and types["-"] then 407a42d6f76SBaptiste Daroussin if not self:peekType("indent") or not self:peekType("indent", 2) then 408a42d6f76SBaptiste Daroussin return result 409a42d6f76SBaptiste Daroussin end 410a42d6f76SBaptiste Daroussin end 411a42d6f76SBaptiste Daroussin 412a42d6f76SBaptiste Daroussin --[[ 413a42d6f76SBaptiste Daroussin a: 1 414a42d6f76SBaptiste Daroussin b: this is 415a42d6f76SBaptiste Daroussin a flowing string 416a42d6f76SBaptiste Daroussin example 417a42d6f76SBaptiste Daroussin c: 3 418a42d6f76SBaptiste Daroussin --]] 419a42d6f76SBaptiste Daroussin if self:peekType("indent") then 420a42d6f76SBaptiste Daroussin self:expect("indent", "text block needs to start with indent") 421a42d6f76SBaptiste Daroussin local addtl = self:accept("indent") 422a42d6f76SBaptiste Daroussin 423a42d6f76SBaptiste Daroussin result = result .. "\n" .. self:parseTextBlock("\n") 424a42d6f76SBaptiste Daroussin 425a42d6f76SBaptiste Daroussin self:expectDedent("text block ending dedent missing") 426a42d6f76SBaptiste Daroussin if addtl then 427a42d6f76SBaptiste Daroussin self:expectDedent("text block ending dedent missing") 428a42d6f76SBaptiste Daroussin end 429a42d6f76SBaptiste Daroussin end 430a42d6f76SBaptiste Daroussin return result 431a42d6f76SBaptiste Daroussin else 432a42d6f76SBaptiste Daroussin --[[ 433a42d6f76SBaptiste Daroussin a: 1 434a42d6f76SBaptiste Daroussin b: 435a42d6f76SBaptiste Daroussin this is also 436a42d6f76SBaptiste Daroussin a flowing string 437a42d6f76SBaptiste Daroussin example 438a42d6f76SBaptiste Daroussin c: 3 439a42d6f76SBaptiste Daroussin --]] 440a42d6f76SBaptiste Daroussin return self:parseTextBlock("\n") 441a42d6f76SBaptiste Daroussin end 442a42d6f76SBaptiste Daroussinend 443a42d6f76SBaptiste Daroussin 444a42d6f76SBaptiste DaroussinParser.parsePipe = function (self) 445a42d6f76SBaptiste Daroussin local pipe = self:expect("pipe") 446a42d6f76SBaptiste Daroussin self:expect("indent", "text block needs to start with indent") 447a42d6f76SBaptiste Daroussin local result = self:parseTextBlock(pipe.sep) 448a42d6f76SBaptiste Daroussin self:expectDedent("text block ending dedent missing") 449a42d6f76SBaptiste Daroussin return result 450a42d6f76SBaptiste Daroussinend 451a42d6f76SBaptiste Daroussin 452a42d6f76SBaptiste DaroussinParser.parseTextBlock = function (self, sep) 453a42d6f76SBaptiste Daroussin local token = self:advance() 454a42d6f76SBaptiste Daroussin local result = string_trim(token.raw, "\n") 455a42d6f76SBaptiste Daroussin local indents = 0 456a42d6f76SBaptiste Daroussin while self:peek() ~= nil and ( indents > 0 or not self:peekType("dedent") ) do 457a42d6f76SBaptiste Daroussin local newtoken = self:advance() 458a42d6f76SBaptiste Daroussin while token.row < newtoken.row do 459a42d6f76SBaptiste Daroussin result = result .. sep 460a42d6f76SBaptiste Daroussin token.row = token.row + 1 461a42d6f76SBaptiste Daroussin end 462a42d6f76SBaptiste Daroussin if newtoken[1] == "indent" then 463a42d6f76SBaptiste Daroussin indents = indents + 1 464a42d6f76SBaptiste Daroussin elseif newtoken[1] == "dedent" then 465a42d6f76SBaptiste Daroussin indents = indents - 1 466a42d6f76SBaptiste Daroussin else 467a42d6f76SBaptiste Daroussin result = result .. string_trim(newtoken.raw, "\n") 468a42d6f76SBaptiste Daroussin end 469a42d6f76SBaptiste Daroussin end 470a42d6f76SBaptiste Daroussin return result 471a42d6f76SBaptiste Daroussinend 472a42d6f76SBaptiste Daroussin 473a42d6f76SBaptiste DaroussinParser.parseHash = function (self, hash) 474a42d6f76SBaptiste Daroussin hash = hash or {} 475a42d6f76SBaptiste Daroussin local indents = 0 476a42d6f76SBaptiste Daroussin 477a42d6f76SBaptiste Daroussin if self:isInline() then 478a42d6f76SBaptiste Daroussin local id = self:advanceValue() 479a42d6f76SBaptiste Daroussin self:expect(":", "expected semi-colon after id") 480a42d6f76SBaptiste Daroussin self:ignoreSpace() 481a42d6f76SBaptiste Daroussin if self:accept("indent") then 482a42d6f76SBaptiste Daroussin indents = indents + 1 483a42d6f76SBaptiste Daroussin hash[id] = self:parse() 484a42d6f76SBaptiste Daroussin else 485a42d6f76SBaptiste Daroussin hash[id] = self:parse() 486a42d6f76SBaptiste Daroussin if self:accept("indent") then 487a42d6f76SBaptiste Daroussin indents = indents + 1 488a42d6f76SBaptiste Daroussin end 489a42d6f76SBaptiste Daroussin end 490a42d6f76SBaptiste Daroussin self:ignoreSpace(); 491a42d6f76SBaptiste Daroussin end 492a42d6f76SBaptiste Daroussin 493a42d6f76SBaptiste Daroussin while self:peekType("id") do 494a42d6f76SBaptiste Daroussin local id = self:advanceValue() 495a42d6f76SBaptiste Daroussin self:expect(":","expected semi-colon after id") 496a42d6f76SBaptiste Daroussin self:ignoreSpace() 497a42d6f76SBaptiste Daroussin hash[id] = self:parse() 498a42d6f76SBaptiste Daroussin self:ignoreSpace(); 499a42d6f76SBaptiste Daroussin end 500a42d6f76SBaptiste Daroussin 501a42d6f76SBaptiste Daroussin while indents > 0 do 502a42d6f76SBaptiste Daroussin self:expectDedent("expected dedent") 503a42d6f76SBaptiste Daroussin indents = indents - 1 504a42d6f76SBaptiste Daroussin end 505a42d6f76SBaptiste Daroussin 506a42d6f76SBaptiste Daroussin return hash 507a42d6f76SBaptiste Daroussinend 508a42d6f76SBaptiste Daroussin 509a42d6f76SBaptiste DaroussinParser.parseInlineHash = function (self) 510a42d6f76SBaptiste Daroussin local id 511a42d6f76SBaptiste Daroussin local hash = {} 512a42d6f76SBaptiste Daroussin local i = 0 513a42d6f76SBaptiste Daroussin 514a42d6f76SBaptiste Daroussin self:accept("{") 515a42d6f76SBaptiste Daroussin while not self:accept("}") do 516a42d6f76SBaptiste Daroussin self:ignoreSpace() 517a42d6f76SBaptiste Daroussin if i > 0 then 518a42d6f76SBaptiste Daroussin self:expect(",","expected comma") 519a42d6f76SBaptiste Daroussin end 520a42d6f76SBaptiste Daroussin 521a42d6f76SBaptiste Daroussin self:ignoreWhitespace() 522a42d6f76SBaptiste Daroussin if self:peekType("id") then 523a42d6f76SBaptiste Daroussin id = self:advanceValue() 524a42d6f76SBaptiste Daroussin if id then 525a42d6f76SBaptiste Daroussin self:expect(":","expected semi-colon after id") 526a42d6f76SBaptiste Daroussin self:ignoreSpace() 527a42d6f76SBaptiste Daroussin hash[id] = self:parse() 528a42d6f76SBaptiste Daroussin self:ignoreWhitespace() 529a42d6f76SBaptiste Daroussin end 530a42d6f76SBaptiste Daroussin end 531a42d6f76SBaptiste Daroussin 532a42d6f76SBaptiste Daroussin i = i + 1 533a42d6f76SBaptiste Daroussin end 534a42d6f76SBaptiste Daroussin return hash 535a42d6f76SBaptiste Daroussinend 536a42d6f76SBaptiste Daroussin 537a42d6f76SBaptiste DaroussinParser.parseList = function (self) 538a42d6f76SBaptiste Daroussin local list = {} 539a42d6f76SBaptiste Daroussin while self:accept("-") do 540a42d6f76SBaptiste Daroussin self:ignoreSpace() 541a42d6f76SBaptiste Daroussin list[#list + 1] = self:parse() 542a42d6f76SBaptiste Daroussin 543a42d6f76SBaptiste Daroussin self:ignoreSpace() 544a42d6f76SBaptiste Daroussin end 545a42d6f76SBaptiste Daroussin return list 546a42d6f76SBaptiste Daroussinend 547a42d6f76SBaptiste Daroussin 548a42d6f76SBaptiste DaroussinParser.parseInlineList = function (self) 549a42d6f76SBaptiste Daroussin local list = {} 550a42d6f76SBaptiste Daroussin local i = 0 551a42d6f76SBaptiste Daroussin self:accept("[") 552a42d6f76SBaptiste Daroussin while not self:accept("]") do 553a42d6f76SBaptiste Daroussin self:ignoreSpace() 554a42d6f76SBaptiste Daroussin if i > 0 then 555a42d6f76SBaptiste Daroussin self:expect(",","expected comma") 556a42d6f76SBaptiste Daroussin end 557a42d6f76SBaptiste Daroussin 558a42d6f76SBaptiste Daroussin self:ignoreSpace() 559a42d6f76SBaptiste Daroussin list[#list + 1] = self:parse() 560a42d6f76SBaptiste Daroussin self:ignoreSpace() 561a42d6f76SBaptiste Daroussin i = i + 1 562a42d6f76SBaptiste Daroussin end 563a42d6f76SBaptiste Daroussin 564a42d6f76SBaptiste Daroussin return list 565a42d6f76SBaptiste Daroussinend 566a42d6f76SBaptiste Daroussin 567a42d6f76SBaptiste DaroussinParser.parseTimestamp = function (self) 568a42d6f76SBaptiste Daroussin local capture = self:advance()[2] 569a42d6f76SBaptiste Daroussin 570a42d6f76SBaptiste Daroussin return os.time{ 571a42d6f76SBaptiste Daroussin year = capture[1], 572a42d6f76SBaptiste Daroussin month = capture[2], 573a42d6f76SBaptiste Daroussin day = capture[3], 574a42d6f76SBaptiste Daroussin hour = capture[4] or 0, 575a42d6f76SBaptiste Daroussin min = capture[5] or 0, 576a42d6f76SBaptiste Daroussin sec = capture[6] or 0, 577a42d6f76SBaptiste Daroussin isdst = false, 578a42d6f76SBaptiste Daroussin } - os.time{year=1970, month=1, day=1, hour=8} 579a42d6f76SBaptiste Daroussinend 580a42d6f76SBaptiste Daroussin 581a42d6f76SBaptiste Daroussinexports.eval = function (str) 582a42d6f76SBaptiste Daroussin return Parser:new(exports.tokenize(str)):parse() 583a42d6f76SBaptiste Daroussinend 584a42d6f76SBaptiste Daroussin 585a42d6f76SBaptiste Daroussinexports.dump = table_print 586a42d6f76SBaptiste Daroussin 587a42d6f76SBaptiste Daroussinreturn exports 588