Parse locations as .yml files. Fixes #12
parent
d51928f8b2
commit
04ba3f1029
21
DOCS.md
21
DOCS.md
|
@ -1,17 +1,18 @@
|
|||
# Magic Compass DOCS
|
||||
|
||||
### Create new locations
|
||||
Every icon in the menu is a location. Locations must be declared in a .txt document inside the `locations` folder like so:
|
||||
Every icon in the menu is a location. Locations must be declared in one or more .yml files inside the `locations` folder like so:
|
||||
```yml
|
||||
ID: # must be a number, don't write "ID"
|
||||
description: Red Forest # the name you want to show in the menu when hovering the icon
|
||||
icon: magiccompass_redforest.png # the associated texture
|
||||
teleport_to: 90.5, 20.0, -30.5 # where the player will be teleported
|
||||
cooldown: 5 # (optional) cooldown before being able to use it again. None by default
|
||||
requires: fast # (optional) privileges required in order to use it. Use ", " to separate them or it won't work
|
||||
hidden_by_default: true # (optional) whether to hide the icon to players who don't have the required privileges
|
||||
```
|
||||
Red Forest -- the name you want to show in the menu when hovering the icon
|
||||
magiccompass_redforest.png -- the associated texture
|
||||
-3.5, 5.0, -20.5 -- where the player will be teleported
|
||||
5 -- (optional) cooldown before being able to use it again. Leave empty or put -1 for none
|
||||
interact, myrpg_lv10 -- (optional) privileges required in order to use it. Use ", " to separate them or it won't work
|
||||
HIDE -- (optional) whether to hide the icon to players who don't have the required privileges
|
||||
```
|
||||
The file name is important too, as it must start with a number followed by an underscore like `5_whatever name.txt`.
|
||||
The number indicates the position of the associated item in the grid (which scales according to the highest number declared), and empty spaces are generated automatically if the numbers of the items don't represent a full sequence.
|
||||
|
||||
The ID indicates the position of the associated item in the grid (which scales according to the highest number declared), and empty spaces are generated automatically if the numbers of the items don't represent a full sequence.
|
||||
|
||||
### Callbacks
|
||||
If you want to run additional code from an external mod of yours, there a few callbacks coming in handy:
|
||||
|
|
|
@ -1,6 +0,0 @@
|
|||
Do you see me? That's because you have the 'fast' privilege. People who don't, they simply can't!
|
||||
magiccompass_example6.png
|
||||
90.5, 20.0, -30.5
|
||||
-1
|
||||
fast
|
||||
HIDE
|
|
@ -1,6 +0,0 @@
|
|||
And if you can't see ME (you need the password privilege), being the only one in the row, you won't see this row nor all the empty rows before it. THIS IS THE TRUE POWER OF ADMINS, CAN YOU FEEL IT?!
|
||||
magiccompass_example6.png
|
||||
150.5, 100.0, -90.5
|
||||
-1
|
||||
password
|
||||
HIDE
|
|
@ -1,3 +0,0 @@
|
|||
Check the Locations folder inside the mod to edit us!
|
||||
magiccompass_example.png
|
||||
10.5, 30.0, 20.5
|
|
@ -1,6 +0,0 @@
|
|||
So as you can see we can create empty rows too, not declaring anything in a line and having something LIKE ME after
|
||||
magiccompass_example7.png
|
||||
-180.5, 210.0, -20.5
|
||||
-1
|
||||
settime
|
||||
HIDE
|
|
@ -1,4 +0,0 @@
|
|||
Every single icon can have a cooldown. Like me, you can spam-click how much you want, I dare you
|
||||
magiccompass_example2.png
|
||||
-3.5, 5.0, -20.5
|
||||
10
|
|
@ -1,3 +0,0 @@
|
|||
And now some cute animals in Italian to please you with a search on the internet: basettino, pangolino, coniglio ariete. Don't thank me
|
||||
magiccompass_example3.png
|
||||
60.5, 70.0, 10.5
|
|
@ -1,3 +0,0 @@
|
|||
Also remember to change the textures in Textures
|
||||
magiccompass_example4.png
|
||||
55.5, 30.0, -40.5
|
|
@ -1,5 +0,0 @@
|
|||
And how about privileges? We have those too. Like this, which doesn't work without the 'fly' and 'shout' privilege
|
||||
magiccompass_example5.png
|
||||
40.5, 40.0, 1.5
|
||||
-1
|
||||
fly, shout
|
|
@ -0,0 +1,51 @@
|
|||
1:
|
||||
description: Check the Locations folder inside the mod to edit us!
|
||||
icon: magiccompass_example.png
|
||||
teleport_to: 10.5, 30.0, 20.5
|
||||
|
||||
3:
|
||||
description: Every single icon can have a cooldown. Like me, you can spam-click how much you want, I dare you
|
||||
icon: magiccompass_example2.png
|
||||
teleport_to: -3.5, 5.0, -20.5
|
||||
cooldown: 10
|
||||
|
||||
5:
|
||||
description: And now some cute animals in Italian to please you with a search on the internet: basettino, pangolino, coniglio ariete. Don't thank me
|
||||
icon: magiccompass_example3.png
|
||||
teleport_to: 60.5, 70.0, 10.5
|
||||
|
||||
7:
|
||||
description: Also remember to change the textures in Textures
|
||||
icon: magiccompass_example4.png
|
||||
teleport_to: 55.5, 30.0, -40.5
|
||||
|
||||
9:
|
||||
description: And how about privileges? We have those too. Like this, which doesn't work without the 'fly' and 'shout' privilege
|
||||
icon: magiccompass_example5.png
|
||||
teleport_to: 40.5, 40.0, 1.5
|
||||
requires: fly, shout
|
||||
|
||||
10:
|
||||
description: Do you see me? That's because you have the 'fast' privilege. People who don't, they simply can't!
|
||||
icon: magiccompass_example6.png
|
||||
teleport_to: 90.5, 20.0, -30.5
|
||||
requires: fast
|
||||
hidden_by_default: true
|
||||
|
||||
# ------------ #
|
||||
# admins only
|
||||
# ------------ #
|
||||
|
||||
18:
|
||||
description: And if you can't see ME (you need the password privilege), being the only one in the row, you won't see this row nor all the empty rows before it. THIS IS THE TRUE POWER OF ADMINS, CAN YOU FEEL IT?!
|
||||
icon: magiccompass_example6.png
|
||||
teleport_to: 150.5, 100.0, -90.5
|
||||
requires: password
|
||||
hidden_by_default: true
|
||||
|
||||
28:
|
||||
description: So as you can see we can create empty rows too, like the one before mine
|
||||
icon: magiccompass_example7.png
|
||||
teleport_to: -180.5, 210.0, -20.5
|
||||
requires: settime
|
||||
hidden_by_default: true
|
|
@ -1,47 +1,40 @@
|
|||
magic_compass.items = {}
|
||||
|
||||
local yaml = dofile(minetest.get_modpath("magic_compass") .. "/src/yaml_parser.lua")
|
||||
local S = minetest.get_translator("magic_compass")
|
||||
|
||||
local locations_dir = minetest.get_modpath("magic_compass") .. "/locations"
|
||||
local locations_content = minetest.get_dir_list(locations_dir)
|
||||
|
||||
for _, file_name in pairs(locations_content) do
|
||||
|
||||
-- estrapolo le info dal file
|
||||
local file = io.open(locations_dir .. "/" .. file_name, "r")
|
||||
local data = string.split(file:read("*all"), "\n")
|
||||
|
||||
file:close()
|
||||
local function load_locations()
|
||||
|
||||
local i_ID = string.match(file_name, "(%d+)_")
|
||||
local i_desc = data[1]
|
||||
local i_texture = data[2]
|
||||
local i_pos = data[3]
|
||||
local i_cooldown
|
||||
local i_privileges
|
||||
local i_hide
|
||||
local dir = minetest.get_modpath("magic_compass") .. "/locations"
|
||||
local content = minetest.get_dir_list(dir)
|
||||
|
||||
if data[4] and tonumber(data[4]) ~= -1 then
|
||||
i_cooldown = tonumber(data[4])
|
||||
for _, f_name in pairs(content) do
|
||||
if f_name:sub(-4) == ".yml" or f_name:sub(-5) == ".yaml" then
|
||||
local file = io.open(dir .. "/" .. f_name, "r")
|
||||
local locs = yaml.parse(file:read("*all"))
|
||||
|
||||
for ID, loc in pairs(locs) do
|
||||
|
||||
assert(type(ID) == "number", "[MAGIC_COMPASS] Invalid location ID '" .. ID .. "': numbers only!")
|
||||
assert(loc.description, "[MAGIC_COMPASS] Location #" .. ID .. " has no description!")
|
||||
assert(loc.icon, "[MAGIC_COMPASS] Location #" .. ID .. " has no icon!")
|
||||
assert(loc.teleport_to, "[MAGIC_COMPASS] Location #" .. ID .. " has no teleport coordinates!")
|
||||
|
||||
minetest.register_tool("magic_compass:" .. ID, {
|
||||
description = S(loc.description),
|
||||
inventory_image = loc.icon,
|
||||
groups = {not_in_creative_inventory = 1, oddly_breakable_by_hand = 2}
|
||||
})
|
||||
|
||||
magic_compass.items[ID] = {desc = loc.description, pos = loc.teleport_to, cooldown = loc.cooldown, privs = loc.requires, hide = loc.hidden_by_default}
|
||||
end
|
||||
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
if data[5] then
|
||||
i_privileges = data[5]
|
||||
end
|
||||
|
||||
if data[6] and data[6] == "HIDE" then
|
||||
i_hide = true
|
||||
end
|
||||
|
||||
-- creo l'oggetto
|
||||
minetest.register_tool("magic_compass:" .. i_ID, {
|
||||
|
||||
description = S(i_desc),
|
||||
inventory_image = i_texture,
|
||||
groups = {not_in_creative_inventory = 1, oddly_breakable_by_hand = 2}
|
||||
|
||||
})
|
||||
|
||||
magic_compass.items[tonumber(i_ID)] = {desc = i_desc, pos = i_pos, cooldown = i_cooldown, privs = i_privileges, hide = i_hide}
|
||||
|
||||
end
|
||||
|
||||
load_locations()
|
||||
|
|
|
@ -60,8 +60,6 @@ function magic_compass.get_formspec(p_name)
|
|||
last_pointed_row = i
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
|
||||
local shown_rows = rows - hidden_rows
|
||||
|
|
|
@ -0,0 +1,475 @@
|
|||
-- YAMLParserLite = class("YAMLParserLite")
|
||||
|
||||
-- function YAMLParserLite:initialize()
|
||||
-- end
|
||||
|
||||
-- function YAMLParserLite:parse(yaml)
|
||||
-- local lines = {}
|
||||
-- for line in string.gmatch(yaml..'\n', '(.-)\n') do
|
||||
-- table.insert(lines, line)
|
||||
-- end
|
||||
|
||||
-- local docs = parse_documents(lines)
|
||||
-- if #docs == 1 then
|
||||
-- return docs[1]
|
||||
-- end
|
||||
-- return docs
|
||||
-- end
|
||||
|
||||
-- 以上是为了 配合个人已有结构
|
||||
|
||||
local schar = string.char
|
||||
local ssub, gsub = string.sub, string.gsub
|
||||
local sfind, smatch = string.find, string.match
|
||||
local tinsert, tremove = table.insert, table.remove
|
||||
|
||||
local UNESCAPES = {
|
||||
['0'] = "\x00", z = "\x00", N = "\x85",
|
||||
a = "\x07", b = "\x08", t = "\x09",
|
||||
n = "\x0a", v = "\x0b", f = "\x0c",
|
||||
r = "\x0d", e = "\x1b", ['\\'] = '\\',
|
||||
}
|
||||
|
||||
-- help function
|
||||
local function select(list, pred)
|
||||
local selected = {}
|
||||
for i = 0, #list do
|
||||
local v = list[i]
|
||||
if v and pred(v, i) then
|
||||
tinsert(selected, v)
|
||||
end
|
||||
end
|
||||
return selected
|
||||
end
|
||||
|
||||
-- return: indent_count, left_string
|
||||
local function count_indent(line)
|
||||
local _, j = sfind(line, '^%s+')
|
||||
if not j then
|
||||
return 0, line
|
||||
end
|
||||
return j, ssub(line, j+1)
|
||||
end
|
||||
|
||||
local function trim(str)
|
||||
return string.gsub(str, "^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
||||
local function ltrim(str)
|
||||
return smatch(str, "^%s*(.-)$")
|
||||
end
|
||||
|
||||
local function rtrim(str)
|
||||
return smatch(str, "^(.-)%s*$")
|
||||
end
|
||||
|
||||
local function isemptyline(line)
|
||||
return line == '' or sfind(line, '^%s*$') or sfind(line, '^%s*#')
|
||||
end
|
||||
|
||||
local function startswith(haystack, needle)
|
||||
return ssub(haystack, 1, #needle) == needle
|
||||
end
|
||||
|
||||
local function startswithline(line, needle)
|
||||
return startswith(line, needle) and isemptyline(ssub(line, #needle+1))
|
||||
end
|
||||
|
||||
-- class
|
||||
local class = {__meta={}}
|
||||
function class.__meta.__call(cls, ...)
|
||||
local self = setmetatable({}, cls)
|
||||
if cls.__init then
|
||||
cls.__init(self, ...)
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
function class.def(base, type, cls)
|
||||
base = base or class
|
||||
local mt = {__metatable=base, __index=base}
|
||||
for k, v in pairs(base.__meta) do mt[k] = v end
|
||||
cls = setmetatable(cls or {}, mt)
|
||||
cls.__index = cls
|
||||
cls.__metatable = cls
|
||||
cls.__type = type
|
||||
cls.__meta = mt
|
||||
return cls
|
||||
end
|
||||
|
||||
local types = {
|
||||
null = class:def('null'),
|
||||
map = class:def('map'),
|
||||
seq = class:def('seq'),
|
||||
}
|
||||
|
||||
local Null = types.null
|
||||
function Null.__tostring() return 'yaml.null' end
|
||||
function Null.isnull(v)
|
||||
if v == nil then return true end
|
||||
if type(v) == 'table' and getmetatable(v) == Null then return true end
|
||||
return false
|
||||
end
|
||||
local null = Null()
|
||||
|
||||
-- implement function
|
||||
local function parse_string(line, stopper)
|
||||
|
||||
stopper = stopper or ''
|
||||
local q = ssub(line, 1, 1)
|
||||
if q == ' ' or q == '\t' then
|
||||
return parse_string(ssub(line, 2))
|
||||
end
|
||||
|
||||
if q == "'" then
|
||||
local i = sfind(line, "'", 2, true)
|
||||
if not i then
|
||||
return nil, line
|
||||
end
|
||||
return ssub(line, 2, i-1), ssub(line, i+1)
|
||||
end
|
||||
|
||||
if q == '"' then
|
||||
local i, buf = 2, ''
|
||||
while i < #line do
|
||||
local c = ssub(line, i, i)
|
||||
if c == '\\' then
|
||||
local n = ssub(line, i+1, i+1)
|
||||
if UNESCAPES[n] ~= nil then
|
||||
buf = buf..UNESCAPES[n]
|
||||
elseif n == 'x' then
|
||||
local h = ssub(i+2,i+3)
|
||||
if sfind(h, '^[0-9a-fA-F]$') then
|
||||
buf = buf..schar(tonumber(h, 16))
|
||||
i = i + 2
|
||||
else
|
||||
buf = buf..'x'
|
||||
end
|
||||
else
|
||||
buf = buf..n
|
||||
end
|
||||
i = i + 1
|
||||
elseif c == q then
|
||||
break
|
||||
else
|
||||
buf = buf..c
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
return buf, ssub(line, i+1)
|
||||
end
|
||||
|
||||
if q == '-' or q == ':' then
|
||||
if ssub(line, 2, 2) == ' ' or #line == 1 then
|
||||
return nil, line
|
||||
end
|
||||
end
|
||||
|
||||
local buf = ''
|
||||
while #line > 0 do
|
||||
local c = ssub(line, 1, 1)
|
||||
if sfind(stopper, c, 1, true) then
|
||||
break
|
||||
elseif c == ':' and (ssub(line, 2, 2) == ' ' or #line == 1) then
|
||||
break
|
||||
elseif c == '#' and (ssub(buf, #buf, #buf) == ' ') then
|
||||
break
|
||||
else
|
||||
buf = buf..c
|
||||
end
|
||||
line = ssub(line, 2)
|
||||
end
|
||||
return rtrim(buf), line
|
||||
end
|
||||
|
||||
local function parse_flowstyle(line, lines)
|
||||
local stack = {}
|
||||
while true do
|
||||
if #line == 0 then
|
||||
if #lines == 0 then
|
||||
break
|
||||
else
|
||||
line = tremove(lines, 1)
|
||||
end
|
||||
end
|
||||
local c = ssub(line, 1, 1)
|
||||
if c == '#' then
|
||||
line = ''
|
||||
elseif c == ' ' or c == '\t' or c == '\r' or c == '\n' then
|
||||
line = ssub(line, 2)
|
||||
elseif c == '{' or c == '[' then
|
||||
tinsert(stack, {v={},t=c})
|
||||
line = ssub(line, 2)
|
||||
elseif c == ':' then
|
||||
local s = tremove(stack)
|
||||
tinsert(stack, {v=s.v, t=':'})
|
||||
line = ssub(line, 2)
|
||||
elseif c == ',' then
|
||||
local value = tremove(stack)
|
||||
if value.t == ':' or value.t == '{' or value.t == '[' then error() end
|
||||
if stack[#stack].t == ':' then
|
||||
-- map
|
||||
local key = tremove(stack)
|
||||
stack[#stack].v[key.v] = value.v
|
||||
elseif stack[#stack].t == '{' then
|
||||
-- set
|
||||
stack[#stack].v[value.v] = true
|
||||
elseif stack[#stack].t == '[' then
|
||||
-- seq
|
||||
tinsert(stack[#stack].v, value.v)
|
||||
end
|
||||
line = ssub(line, 2)
|
||||
elseif c == '}' then
|
||||
if stack[#stack].t == '{' then
|
||||
if #stack == 1 then break end
|
||||
stack[#stack].t = '}'
|
||||
line = ssub(line, 2)
|
||||
else
|
||||
line = ','..line
|
||||
end
|
||||
elseif c == ']' then
|
||||
if stack[#stack].t == '[' then
|
||||
if #stack == 1 then break end
|
||||
stack[#stack].t = ']'
|
||||
line = ssub(line, 2)
|
||||
else
|
||||
line = ','..line
|
||||
end
|
||||
else
|
||||
local s, rest = parse_string(line, ',{}[]')
|
||||
if not s then
|
||||
error('invalid flowstyle line: '..line)
|
||||
end
|
||||
tinsert(stack, {v=s, t='s'})
|
||||
line = rest
|
||||
end
|
||||
end
|
||||
return stack[1].v, line
|
||||
end
|
||||
|
||||
local function parse_scalar(line, lines)
|
||||
|
||||
line = ltrim(line)
|
||||
line = gsub(line, '%s*#.*$', '')
|
||||
|
||||
if line == '' or line == '~' then
|
||||
return null
|
||||
end
|
||||
|
||||
if startswith(line, '{') or startswith(line, '[') then
|
||||
return parse_flowstyle(line, lines)
|
||||
end
|
||||
|
||||
local s, _ = parse_string(line)
|
||||
if s and s ~= line then
|
||||
return s
|
||||
end
|
||||
|
||||
-- Special cases
|
||||
if sfind('\'"!$', ssub(line, 1, 1), 1, true) then
|
||||
error('unsupported line: '..line)
|
||||
end
|
||||
|
||||
if startswithline(line, '{}') then
|
||||
return {}
|
||||
end
|
||||
if startswithline(line, '[]') then
|
||||
return {}
|
||||
end
|
||||
|
||||
-- Regular unquoted string
|
||||
local v = line
|
||||
if v == 'null' or v == 'Null' or v == 'NULL'then
|
||||
return null
|
||||
elseif v == 'true' or v == 'True' or v == 'TRUE' then
|
||||
return true
|
||||
elseif v == 'false' or v == 'False' or v == 'FALSE' then
|
||||
return false
|
||||
elseif v == '.inf' or v == '.Inf' or v == '.INF' then
|
||||
return math.huge
|
||||
elseif v == '+.inf' or v == '+.Inf' or v == '+.INF' then
|
||||
return math.huge
|
||||
elseif v == '-.inf' or v == '-.Inf' or v == '-.INF' then
|
||||
return -math.huge
|
||||
elseif v == '.nan' or v == '.NaN' or v == '.NAN' then
|
||||
return 0 / 0
|
||||
elseif sfind(v, '^[%+%-]?[0-9]+$') or sfind(v, '^[%+%-]?[0-9]+%.$')then
|
||||
return tonumber(v)
|
||||
elseif sfind(v, '^[%+%-]?[0-9]+%.[0-9]+$') then
|
||||
return tonumber(v)
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local parse_map
|
||||
|
||||
local function parse_seq(line, lines, indent)
|
||||
local seq = setmetatable({}, types.seq)
|
||||
if line ~= '' then
|
||||
error()
|
||||
end
|
||||
while #lines > 0 do
|
||||
line = lines[1]
|
||||
|
||||
local level = count_indent(line)
|
||||
if level < indent and indent ~= -1 then
|
||||
return seq
|
||||
elseif level > indent and indent ~= -1 then
|
||||
error("found bad indenting in line: ".. line)
|
||||
end
|
||||
|
||||
local i, j = sfind(line, '%-%s+')
|
||||
if not i then
|
||||
i, j = sfind(line, '%-$')
|
||||
if not i then
|
||||
return seq
|
||||
end
|
||||
end
|
||||
local rest = ssub(line, j+1)
|
||||
|
||||
if sfind(rest, '^[^\'\"%s]*:') then
|
||||
local indent2 = j
|
||||
lines[1] = string.rep(' ', indent2)..rest
|
||||
tinsert(seq, parse_map('', lines, indent2))
|
||||
elseif isemptyline(rest) then
|
||||
tremove(lines, 1)
|
||||
if #lines == 0 then
|
||||
tinsert(seq, null)
|
||||
return seq
|
||||
end
|
||||
if sfind(lines[1], '^%s*%-') then
|
||||
local nextline = lines[1]
|
||||
local indent2 = count_indent(nextline)
|
||||
if indent2 == indent then
|
||||
tinsert(seq, null)
|
||||
else
|
||||
tinsert(seq, parse_seq('', lines, indent2))
|
||||
end
|
||||
else
|
||||
local nextline = lines[1]
|
||||
local indent2 = count_indent(nextline)
|
||||
tinsert(seq, parse_map('', lines, indent2))
|
||||
end
|
||||
elseif rest then
|
||||
tremove(lines, 1)
|
||||
local tmp = parse_scalar(rest, lines)
|
||||
tinsert(seq, tmp)
|
||||
end
|
||||
end
|
||||
return seq
|
||||
end
|
||||
|
||||
function parse_map(line, lines, indent)
|
||||
if not isemptyline(line) then
|
||||
error('not map line: '..line)
|
||||
end
|
||||
local map = setmetatable({}, types.map)
|
||||
while #lines > 0 do
|
||||
line = lines[1]
|
||||
|
||||
local level, _ = count_indent(line)
|
||||
if level < indent then
|
||||
return map
|
||||
elseif level > indent then
|
||||
error("found bad indenting in line: ".. line)
|
||||
end
|
||||
|
||||
local key
|
||||
local s, rest = parse_string(line)
|
||||
if s and startswith(rest, ':') then
|
||||
local sc = parse_scalar(s, {})
|
||||
if sc and type(sc) ~= 'string' then
|
||||
key = sc
|
||||
else
|
||||
key = s
|
||||
end
|
||||
line = ssub(rest, 2)
|
||||
else
|
||||
error("failed to classify line: "..line)
|
||||
end
|
||||
|
||||
if map[key] ~= nil then
|
||||
print("found a duplicate key '"..key.."' in line: "..line)
|
||||
local suffix = 1
|
||||
while map[key..'__'..suffix] do
|
||||
suffix = suffix + 1
|
||||
end
|
||||
key = key ..'_'..suffix
|
||||
end
|
||||
|
||||
line = ltrim(line)
|
||||
|
||||
if not isemptyline(line) then
|
||||
tremove(lines, 1)
|
||||
line = ltrim(line)
|
||||
map[key] = parse_scalar(line, lines)
|
||||
else
|
||||
tremove(lines, 1)
|
||||
if #lines == 0 then
|
||||
map[key] = null
|
||||
return map;
|
||||
end
|
||||
if sfind(lines[1], '^%s*%-') then
|
||||
local indent2 = count_indent(lines[1])
|
||||
map[key] = parse_seq('', lines, indent2)
|
||||
else
|
||||
local indent2 = count_indent(lines[1])
|
||||
if indent >= indent2 then
|
||||
map[key] = null
|
||||
else
|
||||
map[key] = parse_map('', lines, indent2)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return map
|
||||
end
|
||||
|
||||
local function parse_documents(lines)
|
||||
lines = select(lines, function(s) return not isemptyline(s) end)
|
||||
|
||||
if #lines == 1 and not sfind(lines[1], '^%s*%-') then
|
||||
local line = lines[1]
|
||||
line = ltrim(line)
|
||||
return parse_scalar(line, lines)
|
||||
end
|
||||
|
||||
local root = {}
|
||||
while #lines > 0 do
|
||||
local line = lines[1]
|
||||
if sfind(line, '^%s*%-') then
|
||||
tinsert(root, parse_seq('', lines, -1))
|
||||
elseif sfind(line, '^%s*[^%s]') then
|
||||
local level = count_indent(line)
|
||||
tinsert(root, parse_map('', lines, level))
|
||||
else
|
||||
error('parse error: '..line)
|
||||
end
|
||||
end
|
||||
|
||||
if #root > 1 and Null.isnull(root[1]) then
|
||||
tremove(root, 1)
|
||||
return root
|
||||
end
|
||||
|
||||
return root
|
||||
|
||||
end
|
||||
|
||||
local function parse(yaml)
|
||||
local lines = {}
|
||||
for line in string.gmatch(yaml..'\n', '(.-)\n') do
|
||||
table.insert(lines, line)
|
||||
end
|
||||
|
||||
local docs = parse_documents(lines)
|
||||
if #docs == 1 then
|
||||
return docs[1]
|
||||
end
|
||||
return docs
|
||||
end
|
||||
|
||||
return {
|
||||
null = null,
|
||||
parse = parse,
|
||||
}
|
Loading…
Reference in New Issue