Repository: googlefonts/glyphsLib
Pull request #900 (2023-04-30 17:20:14)

Some tokens fixes


Patch:
diff --git a/Lib/glyphsLib/builder/tokens.py b/Lib/glyphsLib/builder/tokens.py
index 23980373..16faea7d 100644
--- a/Lib/glyphsLib/builder/tokens.py
+++ b/Lib/glyphsLib/builder/tokens.py
@@ -1,12 +1,11 @@
 import re
+import fnmatch
 from collections import OrderedDict
 
 
 def _like(got, expected):
-    expected = expected.replace("?", ".")
-    expected = expected.replace("*", ".*")
-    # Technically we should be a bit stricter than this
-    return re.match(expected, str(got))
+    # LIKE is similar to Unix shell-style wildcards supported by fnmatch
+    return fnmatch.fnmatchcase(str(got), expected)
 
 
 class TokenExpander:
@@ -213,7 +212,7 @@ class TokenExpander:
         return list(glyphs.keys())
 
     def _parse_optional_not(self):
-        m = re.match(r"^\s*not\s+", self.glyph_predicate)
+        m = re.match(r"(?i)^\s*(not|!)\s+", self.glyph_predicate)
         if m:
             self.glyph_predicate = self.glyph_predicate[len(m[0]) :]
             return True
@@ -294,7 +293,17 @@ class TokenExpander:
                 "Unknown glyph property '%s' at position %i" % (value, self.position)
             ) from e
 
+    gsglyph_attr_getters = {
+        "colorIndex": lambda g: g.color,
+        "countOfUnicodes": lambda g: len(g.unicodes),
+        "countOfLayers": lambda g: len(g.layers),
+    }
+
     def _get_value_for_glyph(self, g, value):
+        getter = self.gsglyph_attr_getters.get(value, None)
+        if getter:
+            return getter(g)
+
         try:
             return getattr(g, value)
         except AttributeError as exc:
@@ -313,6 +322,8 @@ class TokenExpander:
         "!=": lambda got, exp: got != exp,
         ">=": lambda got, exp: got >= exp,
         "<=": lambda got, exp: got <= exp,
+        ">": lambda got, exp: got > exp,
+        "<": lambda got, exp: got < exp,
         "between": lambda got, exp: got >= exp[0] and got <= exp[1],
         "in": lambda got, exp: got in exp,
     }


Test patch:
diff --git a/tests/data/TokenTest.glyphs b/tests/data/TokenTest.glyphs
index 856b2bd0..abf6592a 100644
--- a/tests/data/TokenTest.glyphs
+++ b/tests/data/TokenTest.glyphs
@@ -142,9 +142,31 @@ width = 600;
 note = "I love it!";
 },
 {
+glyphname = space;
+lastChange = "2023-04-30 16:25:25 +0000";
+layers = (
+{
+layerId = m01;
+width = 200;
+},
+{
+layerId = "11119B89-ADF6-486B-9172-E51437C3592A";
+width = 200;
+},
+{
+associatedMasterId = m01;
+layerId = "54D50A9B-7A3E-4CD6-AC9C-62E91FCBECBA";
+name = Test;
+width = 200;
+}
+);
+unicode = (32,160);
+},
+{
 category = Separator;
+color = 5;
 glyphname = Sacute;
-lastChange = "2023-04-28 17:52:32 +0000";
+lastChange = "2023-04-28 22:20:21 +0000";
 layers = (
 {
 layerId = m01;
diff --git a/tests/tokens_test.py b/tests/tokens_test.py
index fd30c234..4f9815f5 100644
--- a/tests/tokens_test.py
+++ b/tests/tokens_test.py
@@ -63,18 +63,40 @@ expander = TokenExpander(TESTFONT, master)
             "A.sc",
             False,
         ),  # will expand to all glyph names that end in ".sc"
-        ("$[not name endswith '.sc']", "A Sacute", False),
-        ("$[name endswith '.sc' or not name endswith '.sc']", "A.sc A Sacute", False),
+        ("$[not name endswith '.sc']", "A space Sacute", False),
+        ("$[NOT name endswith '.sc']", "A space Sacute", False),
+        ("$[! name endswith '.sc']", "A space Sacute", False),
+        (
+            "$[name endswith '.sc' or not name endswith '.sc']",
+            "A.sc A space Sacute",
+            False,
+        ),
+        (
+            "$[name ENDSWITH '.sc' OR NOT name ENDSWITH '.sc']",
+            "A.sc A space Sacute",
+            False,
+        ),
+        (
+            "$[name endswith '.sc' || ! name endswith '.sc']",
+            "A.sc A space Sacute",
+            False,
+        ),
         ("$[name endswith '.sc' and not name endswith '.sc']", "", False),
+        ("$[name ENDSWITH '.sc' AND NOT name ENDSWITH '.sc']", "", False),
+        ("$[name endswith '.sc' && ! name endswith '.sc']", "", False),
+        ("$[name like 'A']", "A", False),
+        ("$[name like 'Sacut']", "", False),
+        ("$[name like 'S?cute']", "Sacute", False),
+        ("$[name like 'S*']", "Sacute", False),
         # ('$[layer0.width < 500]', "", False), # layer0 = first master
         # ('$[layers.count > 1]', "", False), # compare numbers with: == != <= >= < >
         # ('$[direction == 2]', "", False), # 0=LTR, 1=BiDi, 2=RTL
-        # ('$[colorIndex == 5]', "", False),
+        ("$[colorIndex == 5]", "Sacute", False),
         # ('$[case == smallCaps]', "", False),
         # predefined constants: noCase, upper, lower, smallCaps, minor, other
         (
             '$[name matches "S|s.*"]',
-            "A.sc Sacute",
+            "A.sc space Sacute",
             False,
         ),  # "matches": regular expression
         # ('$[leftMetricsKey like "*"]', "", False), # "like": wildcard search
@@ -85,8 +107,8 @@ expander = TokenExpander(TESTFONT, master)
         ('$[rightKerningGroup like "L"]', "A", False),
         ('$[unicode beginswith "41"]', "A", False),  # beginswith, endswith, contains
         ('$[note contains "love it"]', "A.sc", False),  # glyph note
-        # ('$[countOfUnicodes > 1]', "", False),
-        # ('$[countOfLayers > 1]', "", False),
+        ("$[countOfUnicodes > 1]", "space", False),
+        ("$[countOfLayers > 2]", "space", False),
         ('$[subCategory like "Arrow"]', "Sacute", False),
         # ('$[hasHints == 0]', "", False), # boolean: false, no, 0 versus true, yes, 1
         # ('$[isColorGlyph == true]', "", False),