From 3936e334f1e8ae3d52bd689027e39668afd465b0 Mon Sep 17 00:00:00 2001 From: Sascha Brawer Date: Sat, 1 Aug 2015 19:25:48 +0200 Subject: [PATCH] [feaLib] Resolve references to glyph classes --- Lib/fontTools/feaLib/parser.py | 23 +++++++++++++++++++-- Lib/fontTools/feaLib/parser_test.py | 32 ++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 9 deletions(-) diff --git a/Lib/fontTools/feaLib/parser.py b/Lib/fontTools/feaLib/parser.py index 0f896ed03..9cc4d1dd2 100644 --- a/Lib/fontTools/feaLib/parser.py +++ b/Lib/fontTools/feaLib/parser.py @@ -66,10 +66,19 @@ class Parser(object): def parse_glyphclass_reference_(self): result = set() + if self.next_token_type_ is Lexer.GLYPHCLASS: + self.advance_lexer_() + gc = self.resolve_glyphclass_(self.cur_token_) + if gc is None: + raise ParserError("Unknown glyph class @%s" % self.cur_token_, + self.cur_token_location_) + result.update(gc.glyphs) + return result + self.expect_symbol_("[") while self.next_token_ != "]": - if self.next_token_type_ is Lexer.NAME: - self.advance_lexer_() + self.advance_lexer_() + if self.cur_token_type_ is Lexer.NAME: if self.next_token_ == "-": range_location_ = self.cur_token_location_ range_start = self.cur_token_ @@ -80,6 +89,16 @@ class Parser(object): range_end)) else: result.add(self.cur_token_) + elif self.cur_token_type_ is Lexer.GLYPHCLASS: + gc = self.resolve_glyphclass_(self.cur_token_) + if gc is None: + raise ParserError( + "Unknown glyph class @%s" % self.cur_token_, + self.cur_token_location_) + result.update(gc.glyphs) + else: + raise ParserError("Expected glyph name, range, or reference", + self.cur_token_location_) self.expect_symbol_("]") return result diff --git a/Lib/fontTools/feaLib/parser_test.py b/Lib/fontTools/feaLib/parser_test.py index 40ae89183..80187215a 100644 --- a/Lib/fontTools/feaLib/parser_test.py +++ b/Lib/fontTools/feaLib/parser_test.py @@ -24,11 +24,26 @@ class ParserTest(unittest.TestCase): self.assertEqual(gc.name, "dash") self.assertEqual(gc.glyphs, {"endash", "emdash", "figuredash"}) + def test_glyphclass_bad(self): + self.assertRaisesRegex( + ParserError, "Expected glyph name, range, or reference", + self.parse, "@bad = [a 123];") + def test_glyphclass_duplicate(self): self.assertRaisesRegex( ParserError, "Glyph class @dup already defined", self.parse, "@dup = [a b]; @dup = [x];") + def test_glyphclass_empty(self): + [gc] = self.parse("@empty_set = [];").statements + self.assertEqual(gc.name, "empty_set") + self.assertEqual(gc.glyphs, set()) + + def test_glyphclass_equality(self): + [foo, bar] = self.parse("@foo = [a b]; @bar = @foo;").statements + self.assertEqual(foo.glyphs, {"a", "b"}) + self.assertEqual(bar.glyphs, {"a", "b"}) + def test_glyphclass_range_uppercase(self): [gc] = self.parse("@swashes = [X.swash-Z.swash];").statements self.assertEqual(gc.name, "swashes") @@ -72,13 +87,16 @@ class ParserTest(unittest.TestCase): "a", "foo.09", "foo.10", "foo.11", "X.sc", "Y.sc", "Z.sc" }) - # TODO: self.parse("@foo = [a b]; @bar = [@foo];") - # TODO: self.parse("@foo = [a b]; @bar = @foo;") - - def test_glyphclass_empty(self): - [gc] = self.parse("@empty_set = [];").statements - self.assertEqual(gc.name, "empty_set") - self.assertEqual(gc.glyphs, set()) + def test_glyphclass_reference(self): + [vowels_lc, vowels_uc, vowels] = self.parse( + "@Vowels.lc = [a e i o u]; @Vowels.uc = [A E I O U]; " + + "@Vowels = [@Vowels.lc @Vowels.uc y Y];").statements + self.assertEqual(vowels_lc.glyphs, set(list("aeiou"))) + self.assertEqual(vowels_uc.glyphs, set(list("AEIOU"))) + self.assertEqual(vowels.glyphs, set(list("aeiouyAEIOUY"))) + self.assertRaisesRegex( + ParserError, "Unknown glyph class @unknown", + self.parse, "@bad = [@unknown];") def test_languagesystem(self): [langsys] = self.parse("languagesystem latn DEU;").statements