1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
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
|