diff --git a/Lib/glyphsLib/builder/tokens.py b/Lib/glyphsLib/builder/tokens.py index 23980373b..16faea7d2 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 @@ def _parse_glyph_predicate_to_array(self): 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 @@ def _get_value_for_layer(self, layer, value): "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 @@ def _get_value_for_glyph(self, g, value): "!=": 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, } diff --git a/tests/data/TokenTest.glyphs b/tests/data/TokenTest.glyphs index 856b2bd06..abf6592a6 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 fd30c2344..4f9815f56 100644 --- a/tests/tokens_test.py +++ b/tests/tokens_test.py @@ -63,18 +63,40 @@ "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 @@ ('$[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),