--[[ Copyright (c) 2012-2015 Kaarle Ritvanen See LICENSE file for license details --]] local M = {} local object = require('aconf.object') local class = object.class local map = require('aconf.util').map M.Special = class() function M.Special:tostring() return self.symbol end local Up = class(M.Special) Up.symbol = '..' local Wildcard = class(M.Special) Wildcard.symbol = '*' M.Syntax = class() M.Syntax.up = Up() M.Syntax.wildcard = Wildcard() function M.Syntax:get_special(comp) return ({['..']=M.Syntax.up, ['*']=M.Syntax.wildcard})[comp] end function M.Syntax:is_absolute(path) return path:sub(1, 1) == '/' end function M.Syntax:escape(comp) if type(comp) == 'table' then if object.isinstance(object.toinstance(comp), M.Special) then return comp:tostring() end end if type(comp) == 'number' then return tostring(comp) end local res = comp:gsub('([\\/])', '\\%1') return (self:get_special(res) or tonumber(res)) and '\\'..res or res end function M.Syntax:rawjoin(p1, p2, ...) if not p2 then return p1 end if not self:is_absolute(p2) then p2 = '/'..p2 end return self:rawjoin((p1 == '/' and '' or p1)..p2, ...) end function M.Syntax:join(parent, ...) local args = map(function(c) return self:escape(c) end, {...}) if parent > '' then table.insert(args, 1, parent) end return self:rawjoin(table.unpack(args)) end function M.Syntax:split(path) local res = {} local comp = '' local escaped local function merge(s) if s > '' then table.insert( res, not escaped and (self:get_special(s) or tonumber(s)) or s ) end end while true do local prefix, sep, suffix = path:match('([^\\/]*)([\\/])(.*)') if not prefix then merge(comp..path) return res end comp = comp..prefix if sep == '\\' then comp = comp..suffix:sub(1, 1) escaped = true path = suffix:sub(2, -1) else merge(comp) comp = '' escaped = false path = suffix end end end function M.Syntax:is_unique(path) for _, comp in ipairs(self:split(path)) do if comp == self.wildcard then return false end end return true end function M.Syntax:is_subordinate(p1, p2) p1 = self:split(p1) for i, comp in ipairs(self:split(p2)) do if p1[i] ~= comp then return false end end return true end function M.Syntax:to_absolute(path, base) if not self:is_absolute(path) then path = base..(base ~= '/' and '/' or '')..path end local comps = self:split(path) local i = 1 while i <= #comps do if comps[i] == self.up then if i == 1 then error('Invalid path: '..path) end table.remove(comps, i - 1) table.remove(comps, i - 1) i = i - 1 else i = i + 1 end end return self:join('/', table.unpack(comps)) end function M.Syntax:parent(path) local comps = self:split(path) table.remove(comps) return self:join('/', table.unpack(comps)) end function M.Syntax:name(path) local comps = self:split(path) return comps[#comps] end function M.Syntax:export(mod) if not mod then mod = {} end for k, v in pairs(M.Syntax) do if k ~= 'export' then mod[k] = type(v) == 'function' and function(...) return v(self, ...) end or v end end return mod end return M