1
0
Fork 0

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:
Jaroslav Škarvada 2017-04-04 13:52:51 +02:00
parent b2cc6189f8
commit 9a9afb6ef6
No known key found for this signature in database
GPG key ID: D8E1C00E076E840B

View file

@ -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))