summaryrefslogtreecommitdiffstats
path: root/aconf/path
diff options
context:
space:
mode:
Diffstat (limited to 'aconf/path')
-rw-r--r--aconf/path/address/init.lua33
-rw-r--r--aconf/path/address/special.lua35
-rw-r--r--aconf/path/base.lua159
-rw-r--r--aconf/path/init.lua6
4 files changed, 233 insertions, 0 deletions
diff --git a/aconf/path/address/init.lua b/aconf/path/address/init.lua
new file mode 100644
index 0000000..c62487f
--- /dev/null
+++ b/aconf/path/address/init.lua
@@ -0,0 +1,33 @@
+--[[
+Copyright (c) 2012-2015 Kaarle Ritvanen
+See LICENSE file for license details
+--]]
+
+local object = require('aconf.object')
+local class = object.class
+
+local special = require('aconf.path.address.special')
+local base = require('aconf.path.base')
+
+
+local M = {
+ special={
+ enum_keys=special.EnumKeys(),
+ value=special.Value(),
+ value_equals=special.ValueEquals
+ }
+}
+
+local _special = {['#']=M.special.enum_keys, ['&']=M.special.value}
+
+
+local AddressSyntax = class(base.Syntax)
+
+function AddressSyntax:get_special(comp)
+ if comp:sub(1, 1) == '@' then
+ return special.ValueEquals(#comp > 1 and comp:sub(2, -1) or nil)
+ end
+ return _special[comp] or object.super(self, AddressSyntax):get_special(comp)
+end
+
+return AddressSyntax():export(M)
diff --git a/aconf/path/address/special.lua b/aconf/path/address/special.lua
new file mode 100644
index 0000000..ab94fce
--- /dev/null
+++ b/aconf/path/address/special.lua
@@ -0,0 +1,35 @@
+--[[
+Copyright (c) 2012-2015 Kaarle Ritvanen
+See LICENSE file for license details
+--]]
+
+local M = {}
+
+
+local object = require('aconf.object')
+local class = object.class
+
+
+M.SpecialMode = class(require('aconf.path.base').Special)
+
+M.Value = class(M.SpecialMode)
+M.Value.symbol = '&'
+
+M.Selector = class(M.SpecialMode)
+
+M.EnumKeys = class(M.Selector)
+M.EnumKeys.symbol = '#'
+
+
+M.ValueEquals = class(M.Selector)
+
+M.ValueEquals.symbol = '@'
+
+function M.ValueEquals:init(key) self.key = key end
+
+function M.ValueEquals:tostring()
+ return object.super(self, M.ValueEquals):tostring()..(self.key or '')
+end
+
+
+return M
diff --git a/aconf/path/base.lua b/aconf/path/base.lua
new file mode 100644
index 0000000..4e2bf8f
--- /dev/null
+++ b/aconf/path/base.lua
@@ -0,0 +1,159 @@
+--[[
+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
diff --git a/aconf/path/init.lua b/aconf/path/init.lua
new file mode 100644
index 0000000..5ac9642
--- /dev/null
+++ b/aconf/path/init.lua
@@ -0,0 +1,6 @@
+--[[
+Copyright (c) 2012-2015 Kaarle Ritvanen
+See LICENSE file for license details
+--]]
+
+return require('aconf.path.base').Syntax():export()