def get_expansion(s): if '$' in s: expansion = data.Expansion() # for an input like e.g. "foo $(bar) baz", # _vars.split returns ["foo", "bar", "baz"] # every other element is a variable name. for i, element in enumerate(_vars.split(s)): if i % 2: expansion.appendfunc( functions.VariableRef( None, data.StringExpansion(element, None))) elif element: expansion.appendstr(element) return expansion return data.StringExpansion(s, None)
def parsemakesyntax(d, offset, stopon, iterfunc): """ Given Data, parse it into a data.Expansion. @param stopon (sequence) Indicate characters where toplevel parsing should stop. @param iterfunc (generator function) A function which is used to iterate over d, yielding (char, offset, loc) @see iterdata @see itermakefilechars @see itercommandchars @return a tuple (expansion, token, offset). If all the data is consumed, token and offset will be None """ assert callable(iterfunc) stacktop = ParseStackFrame(_PARSESTATE_TOPLEVEL, None, data.Expansion(loc=d.getloc(d.lstart)), tokenlist=stopon + ('$', ), openbrace=None, closebrace=None) tokeniterator = _alltokens.finditer(d.s, offset, d.lend) di = iterfunc(d, offset, stacktop.tokenlist, tokeniterator) while True: # this is not a for loop because `di` changes during the function assert stacktop is not None try: s, token, tokenoffset, offset = next(di) except StopIteration: break stacktop.expansion.appendstr(s) if token is None: continue parsestate = stacktop.parsestate if token[0] == '$': if tokenoffset + 1 == d.lend: # an unterminated $ expands to nothing break loc = d.getloc(tokenoffset) c = token[1] if c == '$': assert len(token) == 2 stacktop.expansion.appendstr('$') elif c in ('(', '{'): closebrace = _matchingbrace[c] if len(token) > 2: fname = token[2:].rstrip() fn = functions.functionmap[fname](loc) e = data.Expansion() if len(fn) + 1 == fn.maxargs: tokenlist = (c, closebrace, '$') else: tokenlist = (',', c, closebrace, '$') stacktop = ParseStackFrame(_PARSESTATE_FUNCTION, stacktop, e, tokenlist, function=fn, openbrace=c, closebrace=closebrace) else: e = data.Expansion() tokenlist = (':', c, closebrace, '$') stacktop = ParseStackFrame(_PARSESTATE_VARNAME, stacktop, e, tokenlist, openbrace=c, closebrace=closebrace, loc=loc) else: assert len(token) == 2 e = data.Expansion.fromstring(c, loc) stacktop.expansion.appendfunc(functions.VariableRef(loc, e)) elif token in ('(', '{'): assert token == stacktop.openbrace stacktop.expansion.appendstr(token) stacktop = ParseStackFrame(_PARSESTATE_PARENMATCH, stacktop, stacktop.expansion, (token, stacktop.closebrace, '$'), openbrace=token, closebrace=stacktop.closebrace, loc=d.getloc(tokenoffset)) elif parsestate == _PARSESTATE_PARENMATCH: assert token == stacktop.closebrace stacktop.expansion.appendstr(token) stacktop = stacktop.parent elif parsestate == _PARSESTATE_TOPLEVEL: assert stacktop.parent is None return stacktop.expansion.finish(), token, offset elif parsestate == _PARSESTATE_FUNCTION: if token == ',': stacktop.function.append(stacktop.expansion.finish()) stacktop.expansion = data.Expansion() if len(stacktop.function) + 1 == stacktop.function.maxargs: tokenlist = (stacktop.openbrace, stacktop.closebrace, '$') stacktop.tokenlist = tokenlist elif token in (')', '}'): fn = stacktop.function fn.append(stacktop.expansion.finish()) fn.setup() stacktop = stacktop.parent stacktop.expansion.appendfunc(fn) else: assert False, "Not reached, _PARSESTATE_FUNCTION" elif parsestate == _PARSESTATE_VARNAME: if token == ':': stacktop.varname = stacktop.expansion stacktop.parsestate = _PARSESTATE_SUBSTFROM stacktop.expansion = data.Expansion() stacktop.tokenlist = ('=', stacktop.openbrace, stacktop.closebrace, '$') elif token in (')', '}'): fn = functions.VariableRef(stacktop.loc, stacktop.expansion.finish()) stacktop = stacktop.parent stacktop.expansion.appendfunc(fn) else: assert False, "Not reached, _PARSESTATE_VARNAME" elif parsestate == _PARSESTATE_SUBSTFROM: if token == '=': stacktop.substfrom = stacktop.expansion stacktop.parsestate = _PARSESTATE_SUBSTTO stacktop.expansion = data.Expansion() stacktop.tokenlist = (stacktop.openbrace, stacktop.closebrace, '$') elif token in (')', '}'): # A substitution of the form $(VARNAME:.ee) is probably a mistake, but make # parses it. Issue a warning. Combine the varname and substfrom expansions to # make the compatible varname. See tests/var-substitutions.mk SIMPLE3SUBSTNAME _log.warning( "%s: Variable reference looks like substitution without =", stacktop.loc) stacktop.varname.appendstr(':') stacktop.varname.concat(stacktop.expansion) fn = functions.VariableRef(stacktop.loc, stacktop.varname.finish()) stacktop = stacktop.parent stacktop.expansion.appendfunc(fn) else: assert False, "Not reached, _PARSESTATE_SUBSTFROM" elif parsestate == _PARSESTATE_SUBSTTO: assert token in (')', '}'), "Not reached, _PARSESTATE_SUBSTTO" fn = functions.SubstitutionRef(stacktop.loc, stacktop.varname.finish(), stacktop.substfrom.finish(), stacktop.expansion.finish()) stacktop = stacktop.parent stacktop.expansion.appendfunc(fn) else: assert False, "Unexpected parse state %s" % stacktop.parsestate if stacktop.parent is not None and iterfunc == itercommandchars: di = itermakefilechars(d, offset, stacktop.tokenlist, tokeniterator, ignorecomments=True) else: di = iterfunc(d, offset, stacktop.tokenlist, tokeniterator) if stacktop.parent is not None: raise errors.SyntaxError("Unterminated function call", d.getloc(offset)) assert stacktop.parsestate == _PARSESTATE_TOPLEVEL return stacktop.expansion.finish(), None, None