A bevy of functions. Also simplified the function setup() since all the functions just call expectargs.
authorBenjamin Smedberg <benjamin@smedbergs.us>
Tue, 10 Feb 2009 11:54:01 -0500
changeset 79 2b377534d9fda5a9c9067abcb4280a46cad23c83
parent 78 b6cacff1e1f3f437cc19c261a5b672b3e626e2fc
child 80 474b11cf02430c73856ea8ad80067e38509e8ccb
push id43
push userbsmedberg@mozilla.com
push dateTue, 10 Feb 2009 18:18:59 +0000
A bevy of functions. Also simplified the function setup() since all the functions just call expectargs.
pymake/functions.py
pymake/parser.py
tests/functions.mk
--- a/pymake/functions.py
+++ b/pymake/functions.py
@@ -21,16 +21,19 @@ class Function(object):
     """
     def __init__(self, loc):
         self._arguments = []
         self.loc = loc
 
     def __getitem__(self, key):
         return self._arguments[key]
 
+    def setup(self):
+        self.expectargs(self.expectedargs)
+
     def append(self, arg):
         assert isinstance(arg, data.Expansion)
         self._arguments.append(arg)
 
     def expectargs(self, argc):
         if len(self._arguments) < argc:
             raise DataError("Not enough arguments to function %s" % self.name, self.loc)
         if len(self._arguments) > argc:
@@ -89,133 +92,115 @@ class SubstitutionRef(Function):
             f = data.Pattern('%' + substfrom)
             substto = '%' + substto
 
         return " ".join((f.subst(substto, word, False)
                          for word in words))
 
 class SubstFunction(Function):
     name = 'subst'
-
-    def setup(self):
-        self.expectargs(3)
+    expectedargs = 3
 
     def resolve(self, variables, setting):
         s = self._arguments[0].resolve(variables, setting)
         r = self._arguments[1].resolve(variables, setting)
         d = self._arguments[2].resolve(variables, setting)
         return d.replace(s, r)
 
 class PatSubstFunction(Function):
     name = 'patsubst'
-
-    def setup(self):
-        self.expectargs(3)
+    expectedargs = 3
 
     def resolve(self, variables, setting):
         s = self._arguments[0].resolve(variables, setting)
         r = self._arguments[1].resolve(variables, setting)
         d = self._arguments[2].resolve(variables, setting)
 
         p = data.Pattern(s)
         return ' '.join((p.subst(r, word, False)
                          for word in data.splitwords(d)))
 
 class StripFunction(Function):
     name = 'strip'
-
-    def setup(self):
-        self.expectargs(1)
+    expectedargs = 1
 
     def resolve(self, variables, setting):
         return ' '.join(data.splitwords(self._arguments[0].resolve(variables, setting)))
 
 class FindstringFunction(Function):
     name = 'findstring'
-
-    def setup(self):
-        self.expectargs(2)
+    expectedargs = 2
 
     def resolve(self, variables, setting):
         s = self._arguments[0].resolve(variables, setting)
         r = self._arguments[1].resolve(variables, setting)
         if r.find(s) == -1:
             return ''
         return s
 
 class FilterFunction(Function):
     name = 'filter'
-
-    def setup(self):
-        self.expectargs(2)
+    expectedargs = 2
 
     def resolve(self, variables, setting):
         ps = self._arguments[0].resolve(variables, setting)
         d = self._arguments[1].resolve(variables.setting)
         plist = [data.Pattern(p) for p in ps]
         r = []
         for w in data.splitwords(d):
             for p in plist:
                 if p.match(w) is not None:
                     r.append(w)
                     break
 
         return ' '.join(r)
 
 class FilteroutFunction(Function):
     name = 'filter-out'
-
-    def setup(self):
-        self.expectargs(2)
+    expectedargs = 2
 
     def resolve(self, variables, setting):
         ps = self._arguments[0].resolve(variables, setting)
         d = self._arguments[1].resolve(variables, setting)
         plist = [data.Pattern(p) for p in ps]
         r = []
         for w in data.splitwords(d):
             for p in plist:
                 if p.match(w) is not None:
                     break
                 r.append(w)
 
         return ' '.join(r)
 
 class SortFunction(Function):
     name = 'sort'
-
-    def setup(self):
-        self.expectargs(1)
+    expectedargs = 1
 
     def resolve(self, variables, setting):
         d = self._arguments[0].resolve(variables, setting)
         w = data.splitwords(w)
         w.sort()
         return data.withoutdups(w)
 
 class WordFunction(Function):
     name = 'word'
-
-    def setup(self):
-        self.expectargs(2)
+    expectedargs = 2
 
     def resolve(self, variables, setting):
         n = self._arguments[0].resolve(variables, setting)
         # TODO: provide better error if this doesn't convert
         n = int(n)
         words = data.splitwords(self._arguments[1].resolve(variables, setting))
         if n < 1 or n > len(words):
             return ''
         return words[n - 1]
 
 class WordlistFunction(Function):
     name = 'wordlist'
-
-    def setup(self):
-        self.expectargs(3)
+    expectedargs = 3
 
     def resolve(self, variables, setting):
         nfrom = self._arguments[0].resolve(variables, setting)
         nto = self._arguments[1].resolve(variables, setting)
         # TODO: provide better errors if this doesn't convert
         nfrom = int(nfrom)
         nto = int(nto)
 
@@ -223,33 +208,83 @@ class WordlistFunction(Function):
 
         if nfrom < 1:
             nfrom = 1
         if nto < 1:
             nto = 1
 
         return ' '.join(words[nfrom - 1:nto])
 
+class WordsFunction(Function):
+    name = 'words'
+    expectedargs = 1
+
+    def resolve(self, variables, setting):
+        return str(len(data.splitwords(self._arguments[0].resolve(variables, setting))))
+
+class FirstWordFunction(Function):
+    name = 'firstword'
+    expectedargs = 1
+
+    def resolve(self, variables, setting):
+        wl = data.splitwords(self._arguments[0].resolve(variables, setting))
+        if len(wl) == 0:
+            return ''
+        return wl[0]
+
+class LastWordFunction(Function):
+    name = 'lastword'
+    expectedargs = 1
+
+    def resolve(self, variables, setting):
+        wl = data.splitwords(self._arguments[0].resolve(variables, setting))
+        if len(wl) == 0:
+            return ''
+        return wl[0]
+
+def pathsplit(path):
+    """
+    Splits a path into dirpart, filepart on the last slash. If there is no slash, dirpart
+    is ./
+    """
+    dir, slash, file = path.rpartition('/')
+    if dir == '':
+        return './', file
+
+    return dir + slash, file
+
+class DirFunction(Function):
+    name = 'dir'
+    expectedargs = 1
+
+    def resolve(self, variables, setting):
+        return ' '.join((pathsplit(path)[0]
+                         for path in data.splitwords(self._arguments[0].resolve(variables, setting))))
+
+class NotDirFunction(Function):
+    name = 'notdir'
+    expectedargs = 1
+
+    def resolve(self, variables, setting):
+        return ' '.join((pathsplit(path)[1]
+                         for path in data.splitwords(self._arguments[0].resolve(variables, setting))))
+
 class ValueFunction(Function):
     name = 'value'
-
-    def setup(self):
-        self.expectargs(1)
+    expectedargs = 1
 
     def resolve(self, variables, setting):
         varname = self._arguments[0].resolve(variables, setting)
 
         flavor, source, value = variables.get(varname, expand=False)
         return value
 
 class FlavorFunction(Function):
     name = 'flavor'
-
-    def setup(self):
-        self.expectargs(1)
+    expectedargs = 1
 
     def resolve(self, variables, setting):
         varname = self._arguments[0].resolve(variables, setting)
 
         
         flavor, source, value = variables.get(varname)
         if flavor is None:
             return 'undefined'
@@ -258,80 +293,72 @@ class FlavorFunction(Function):
             return 'recursive'
         elif flavor == data.Variables.FLAVOR_SIMPLE:
             return 'simple'
 
         assert False, "Neither simple nor recursive?"
 
 class ShellFunction(Function):
     name = 'shell'
-
-    def setup(self):
-        self.expectargs(1)
+    expectedargs = 1
 
     def resolve(self, variables, setting):
         cline = self._arguments[0].resolve(variables, setting)
 
         p = subprocess.Popen(cline, shell=True, stdout=subprocess.PIPE)
         stdout, stderr = p.communicate()
 
         stdout.replace('\r\n', '\n')
         if len(stdout) > 1 and stdout[-1] == '\n':
             stdout = stdout[:-1]
         stdout.replace('\n', ' ')
 
         return stdout
 
 class ErrorFunction(Function):
     name = 'error'
-
-    def setup(self):
-        self.expectargs(1)
+    expectedargs = 1
 
     def resolve(self, variables, setting):
         v = self._arguments[0].resolve(variables, setting)
         raise data.DataError(v, self.loc)
 
 class WarningFunction(Function):
     name = 'warning'
-
-    def setup(self):
-        self.expectargs(1)
+    expectedargs = 1
 
     def resolve(self, variables, setting):
         v = self._arguments[0].resolve(variables, setting)
         log.warning(v)
         return ''
 
 class InfoFunction(Function):
     name = 'info'
-
-    def setup(self):
-        self.expectargs(1)
+    expectedargs = 1
 
     def resolve(self, variables, setting):
         v = self._arguments[0].resolve(variables, setting)
         log.info(v)
         return ''
 
 functionmap = {
     'subst': SubstFunction,
     'patsubst': PatSubstFunction,
     'strip': StripFunction,
     'findstring': FindstringFunction,
     'filter': FilterFunction,
     'filter-out': FilteroutFunction,
     'sort': SortFunction,
     'word': WordFunction,
     'wordlist': WordlistFunction,
-    'words': None,
-    'firstword': None,
-    'lastword': None,
-    'dir': None,
-    'notdir': None,
+    'words': WordsFunction,
+    'firstword': FirstWordFunction,
+    'lastword': LastWordFunction,
+    'dir': DirFunction,
+    'notdir': NotDirFunction,
     'suffix': None,
     'basename': None,
     'addsuffix': None,
     'addprefix': None,
     'join': None,
     'wildcard': None,
     'realpath': None,
     'abspath': None,
--- a/pymake/parser.py
+++ b/pymake/parser.py
@@ -702,16 +702,17 @@ class ParseStackFrame(object):
     def __init__(self, parsestate, expansion, stopon, **kwargs):
         self.parsestate = parsestate
         self.expansion = expansion
         self.stopon = stopon
         for key, value in kwargs.iteritems():
             setattr(self, key, value)
 
 functiontokens = [k for k in functions.functionmap.iterkeys()]
+functiontokens.sort(key=len, reverse=True)
 
 def parsemakesyntax(d, startat, stopon, iterfunc):
     """
     Given Data, parse it into a data.Expansion.
 
     @param stopon (sequence)
         Indicate characters where toplevel parsing should stop.
 
--- a/tests/functions.mk
+++ b/tests/functions.mk
@@ -1,7 +1,13 @@
 all:
 	test "$(subst e,EE,hello)" = "hEEllo"
 	test "$(strip $(NULL)  test data  )" = "test data"
 	test "$(word 1, hello )" = "hello"
 	test "$(word 2, hello )" = ""
 	test "$(wordlist 1, 2, foo bar baz )" = "foo bar"
+	test "$(words 1 2 3)" = "3"
+	test "$(words )" = "0"
+	test "$(firstword $(NULL) foo bar baz)" = "foo"
+	test "$(firstword )" = ""
+	test "$(dir foo.c path/foo.o dir/dir2/)" = "./ path/ dir/dir2/"
+	test "$(notdir foo.c path/foo.o dir/dir2/)" = "foo.c foo.o "
 	@echo TEST-PASS