Patchwork [1,of,7] fileset: add phase to transform parsed tree

login
register
mail settings
Submitter Yuya Nishihara
Date Aug. 3, 2018, 3:01 p.m.
Message ID <78c669de8c435c0594ab.1533308498@mimosa>
Download mbox | patch
Permalink /patch/33151/
State Accepted
Headers show

Comments

Yuya Nishihara - Aug. 3, 2018, 3:01 p.m.
# HG changeset patch
# User Yuya Nishihara <yuya@tcha.org>
# Date 1532157096 -32400
#      Sat Jul 21 16:11:36 2018 +0900
# Node ID 78c669de8c435c0594ab4f849f591aacad074097
# Parent  d814bbd229467f7e5d866fd3af41df8beb131b9a
fileset: add phase to transform parsed tree

This isn't strictly necessary, but I decided to just follow the strategy
of the revset parsing.

Patch

diff --git a/mercurial/debugcommands.py b/mercurial/debugcommands.py
--- a/mercurial/debugcommands.py
+++ b/mercurial/debugcommands.py
@@ -901,6 +901,7 @@  def debugfileset(ui, repo, expr, **opts)
 
     stages = [
         ('parsed', pycompat.identity),
+        ('analyzed', filesetlang.analyze),
     ]
     stagenames = set(n for n, f in stages)
 
diff --git a/mercurial/fileset.py b/mercurial/fileset.py
--- a/mercurial/fileset.py
+++ b/mercurial/fileset.py
@@ -528,6 +528,7 @@  def _intree(funcs, tree):
 def match(ctx, expr, badfn=None):
     """Create a matcher for a single fileset expression"""
     tree = filesetlang.parse(expr)
+    tree = filesetlang.analyze(tree)
     mctx = matchctx(ctx, _buildstatus(ctx, tree), badfn=badfn)
     return getmatch(mctx, tree)
 
diff --git a/mercurial/filesetlang.py b/mercurial/filesetlang.py
--- a/mercurial/filesetlang.py
+++ b/mercurial/filesetlang.py
@@ -131,5 +131,41 @@  def getargs(x, min, max, err):
         raise error.ParseError(err)
     return l
 
+def _analyze(x):
+    if x is None:
+        return x
+
+    op = x[0]
+    if op in {'string', 'symbol'}:
+        return x
+    if op == 'kindpat':
+        getsymbol(x[1])  # kind must be a symbol
+        t = _analyze(x[2])
+        return (op, x[1], t)
+    if op in {'group', 'not', 'negate'}:
+        t = _analyze(x[1])
+        return (op, t)
+    if op in {'and', 'minus'}:
+        ta = _analyze(x[1])
+        tb = _analyze(x[2])
+        return (op, ta, tb)
+    if op in {'list', 'or'}:
+        ts = tuple(_analyze(y) for y in x[1:])
+        return (op,) + ts
+    if op == 'func':
+        getsymbol(x[1])  # function name must be a symbol
+        ta = _analyze(x[2])
+        return (op, x[1], ta)
+    raise error.ProgrammingError('invalid operator %r' % op)
+
+def analyze(x):
+    """Transform raw parsed tree to evaluatable tree which can be fed to
+    getmatch()
+
+    All pseudo operations should be mapped to real operations or functions
+    defined in methods or symbols table respectively.
+    """
+    return _analyze(x)
+
 def prettyformat(tree):
     return parser.prettyformat(tree, ('string', 'symbol'))
diff --git a/mercurial/minifileset.py b/mercurial/minifileset.py
--- a/mercurial/minifileset.py
+++ b/mercurial/minifileset.py
@@ -89,4 +89,5 @@  def compile(text):
     root except for "bin/README".
     """
     tree = filesetlang.parse(text)
+    tree = filesetlang.analyze(tree)
     return _compile(tree)
diff --git a/tests/test-fileset.t b/tests/test-fileset.t
--- a/tests/test-fileset.t
+++ b/tests/test-fileset.t
@@ -169,6 +169,18 @@  Show parsed tree at stages:
         (func
           (symbol 'clean')
           None))))
+  * analyzed:
+  (or
+    (symbol 'a1')
+    (symbol 'a2')
+    (group
+      (and
+        (func
+          (symbol 'grep')
+          (string 'b'))
+        (func
+          (symbol 'clean')
+          None))))
   * matcher:
   <unionmatcher matchers=[
     <patternmatcher patterns='(?:a1$)'>,