Matt Mackall wrote:
You must be new here. There's nothing we care about being
backward-compatible with. Mercurial is not a library and explicitly has
no stable API. We only care about Mercurial's internal API being
friendly for Mercurial's future development. If you decide to link to
Mercurial internals, then you have to expect things to break on a
regular basis because we're not going to stop improving the core any
time soon.
There are three kinds of backward compatibility in the world:
- the kind offered by Mercurial's command line API where you should
expect things to work forever or get lots of advanced notice that
they'll be breaking
This was exactly what I was talking about. Keeping this without any
required extra effort is a good thing (for one example it has less
opportunity to introduce bugs). I don't care about the backward
compatibility inside the codebase, just at the CLI level.
step 4: eval this statement
eval is banned from use in hg. And regex parsing is insufficient to
properly handle the quoting and nesting requirements here.
It doesn't have to be eval, that was just the easy way out. you could
almost as easily push the method calls onto a stack and use getattr
instead.
without regexes (though I think they could have been used just as
easily as the simple tokenizer below) or eval, still not a formal
grammer of any kind (again the usual warnings about untested code and
the fact that python isn't a language I feel strong with):
class revfinder(object):
unquoteddelims = "()\t\n \"'"
def __init__(self, repo, query):
self.repo=repo
"""goal:
turn:
descendant(parent2(1.0)) and ancestor(2.0)
and author(george) and sorted('date') and reversed()
into:
[reversed, [sorted, [andf, [andf, [descendant, parent2,
"1.0"],
ancestor, "2.0"], author, "george"], "date"]]
"""
charstack = []
delimiters = unquoteddelims
tokens = []
for char in query:
if delimiters.find(char) != -1:
charstack.append(char)
else
token = chartoken = ''.join(charstack)
if chartoken:
charstack = []
if token=='and' or token=='or':
token = token+'f'
if delimiters == unquoteddelims:
try:
token = getattr(self, token)
except AttributeError:
pass
if isinstance(string, tokens[-1]):
tokens[-1] = ' '.join([tokens[-1], chartoken])
else:
# could use tags on the functions to determine
which case here
# doing so would allow plugins to extend
(examples: xor, except, ...)
if chartoken=='and' or chartoken=='or':
# infix operation
tokens = [token].append(tokens)
elif chartoken=='sorted' or
chartoken=='reversed':
# whole-list nonfiltering operations
tokens = [token, tokens[1]]
else:
# other
tokens.append(token)
if char=='"':
if token[-1]=="\\":
charstack = [chartoken, char]
elif delimiters = char:
delimiters = unquoteddelims
else:
delimiters = char
elif char=="'":
if delimiters = char:
delimiters = unquoteddelims
else:
delimiters=char
self.statement = tokens
def evalstmt(self):
tokens = self.statement
return self.run(tokens[0], tokens[1:])
def run(self, func, args):
if isinstance(list, args[-1]):
args[-1] = self.run(args[-1][0], args[-1][1:])
if len(args) > 2:
if isinstance(list, args[1]):
args[1] = self.run(args[1][0], args[1][1:])
while len(args) >= 2 and callable(args[-2]):
# heres hoping lists and strings are not callable
args[-2:-1] = args[-2](args[-1])
return func(*args)
# does self need to be a parameter here somewhere
# or is it implicitly included because of the getattr done?
def andf(self, set1, set2):
pass #not interested in writing these right now
def orf(self, set1, set2):
pass
def reversed(self, changes):
pass
def sorted(self, changes, key):
pass
...