builtin functions: improved parser
Parser rewritten to use pushdown automaton, now it can correctly parse
e.g. the following:
[variables]
cores1=2-5
isolated_cores=${f:cpulist_pack:${f:cpulist_unpack:${cores1}}},${f:cpulist_unpack:6-8}
I.e. nested functions are no problem now, multiple functions on the same
level also works and it correctly skips unexpanded variables. It's also
possible to prevent function expansion by escaping:
v=\${f:f1\}
Variables expansion could be now probably also moved to this automaton and
all could be expanded in one step, but for now keeping it as is (i.e. two
steps expansion).
Signed-off-by: Jaroslav Škarvada <jskarvad@redhat.com>
This commit is contained in:
parent
b2cc6189f8
commit
9a9afb6ef6
1 changed files with 61 additions and 20 deletions
|
|
@ -17,32 +17,73 @@ class Functions():
|
|||
|
||||
def __init__(self):
|
||||
self._repository = repository.Repository()
|
||||
self._parse_init()
|
||||
|
||||
def sub_func(self, mo):
|
||||
sorig = mo.string[mo.start():mo.end()]
|
||||
if mo.lastindex != 1:
|
||||
return sorig
|
||||
s = mo.string[mo.start(1):mo.end(1)]
|
||||
if len(s) == 0:
|
||||
return sorig
|
||||
sl = re.split(r'(?<!\\):', s)
|
||||
def _parse_init(self, s = ""):
|
||||
self._cnt = 0
|
||||
self._str = s
|
||||
self._len = len(s)
|
||||
self._stack = []
|
||||
self._esc = False
|
||||
|
||||
def _curr_char(self):
|
||||
return self._str[self._cnt]
|
||||
|
||||
def _curr_substr(self, _len):
|
||||
return self._str[self._cnt:self._cnt + _len]
|
||||
|
||||
def _push_pos(self):
|
||||
self._stack.append(self._cnt)
|
||||
|
||||
def _sub(self, a, b, s):
|
||||
self._str = self._str[:a] + s + self._str[b + 1:]
|
||||
self._len = len(self._str)
|
||||
self._cnt += len(s) - (b - a + 1)
|
||||
if self._cnt < 0:
|
||||
self._cnt = 0
|
||||
|
||||
def _process_func(self, _from):
|
||||
sl = re.split(r'(?<!\\):', self._str[_from:self._cnt])
|
||||
if sl[0] != "${f":
|
||||
return
|
||||
sl = map(lambda v: str(v).replace("\:", ":"), sl)
|
||||
if not re.match(r'\w+$', sl[0]):
|
||||
log.error("invalid function name '%s'" % sl[0])
|
||||
return sorig
|
||||
if not re.match(r'\w+$', sl[1]):
|
||||
log.error("invalid function name '%s'" % sl[1])
|
||||
return
|
||||
try:
|
||||
f = self._repository.load_func(sl[0])
|
||||
f = self._repository.load_func(sl[1])
|
||||
except ImportError:
|
||||
log.error("function '%s' not implemented" % sl[0])
|
||||
return sorig
|
||||
s = f.execute(sl[1:])
|
||||
log.error("function '%s' not implemented" % sl[1])
|
||||
return
|
||||
s = f.execute(sl[2:])
|
||||
if s is None:
|
||||
return sorig
|
||||
return s
|
||||
return
|
||||
self._sub(_from, self._cnt, s)
|
||||
|
||||
def _process(self, s):
|
||||
self._parse_init(s)
|
||||
while self._cnt < self._len:
|
||||
if self._esc:
|
||||
self._esc = False
|
||||
else:
|
||||
if self._curr_char() == "\\":
|
||||
self._esc = True
|
||||
elif self._curr_char() == "}":
|
||||
try:
|
||||
_from = self._stack.pop()
|
||||
except IndexError:
|
||||
log.error("invalid variable syntax, non pair '}' in: '%s'" % s)
|
||||
return self._str
|
||||
self._process_func(_from)
|
||||
elif self._curr_substr(2) == "${":
|
||||
self._push_pos()
|
||||
self._cnt += 1
|
||||
if len(self._stack):
|
||||
log.error("invalid varialbe syntax, non pair '{' in: '%s'" % s)
|
||||
return self._str
|
||||
|
||||
def expand(self, s):
|
||||
if s is None:
|
||||
if s is None or s == "":
|
||||
return s
|
||||
r = re.compile(r'(?<!\\)\${f:(.*)}')
|
||||
# expand functions and convert all \${f:*} to ${f:*} (unescape)
|
||||
return re.sub(r'\\(\${f:.*})', r'\1', r.sub(self.sub_func, s))
|
||||
return re.sub(r'\\(\${f:.*\\})', r'\1', self._process(s))
|
||||
|
|
|
|||
Loading…
Reference in a new issue