xref: /freebsd/libexec/nuageinit/yaml.lua (revision 504981357aa36365784458cfe8d9e23097bfac7b)
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