[feaLib] Parse lookupflag statements

They do not have any impact yet, this change is just for the parsing.
This commit is contained in:
Sascha Brawer 2015-12-10 13:04:16 +01:00
parent ef03a3be47
commit 0dbb2fdcc0
3 changed files with 120 additions and 0 deletions

View File

@ -26,6 +26,13 @@ class Expression(object):
pass
class GlyphClassName(Expression):
def __init__(self, location, glyphclass):
Expression.__init__(self, location)
assert isinstance(glyphclass, GlyphClassDefinition)
self.glyphclass = glyphclass
class Block(Statement):
def __init__(self, location):
Statement.__init__(self, location)
@ -181,6 +188,17 @@ class LigatureSubstStatement(Statement):
builder.add_ligature_subst(self.location, glyphs, self.replacement)
class LookupFlagStatement(Statement):
def __init__(self, location, value, markAttachment, markFilteringSet):
Statement.__init__(self, location)
self.value = value
self.markAttachment = markAttachment
self.markFilteringSet = markFilteringSet
def build(self, builder):
pass # TODO: Implement.
class LookupReferenceStatement(Statement):
def __init__(self, location, lookup):
Statement.__init__(self, location)

View File

@ -175,6 +175,15 @@ class Parser(object):
self.expect_symbol_("]")
return result
def parse_glyphclass_name_(self):
name = self.expect_class_name_()
gc = self.glyphclasses_.resolve(name)
if gc is None:
raise FeatureLibError(
"Unknown glyph class @%s" % name,
self.cur_token_location_)
return ast.GlyphClassName(self.cur_token_location_, gc)
def parse_glyph_pattern_(self):
prefix, glyphs, lookups, suffix = ([], [], [], [])
while (self.next_token_ not in {"by", "from", ";", "<"} and
@ -260,6 +269,45 @@ class Parser(object):
self.lookups_.define(name, block)
return block
def parse_lookupflag_(self):
assert self.is_cur_keyword_("lookupflag")
location = self.cur_token_location_
# format B: "lookupflag 6;"
if self.next_token_type_ == Lexer.NUMBER:
value = self.expect_number_()
self.expect_symbol_(";")
return ast.LookupFlagStatement(location, value, None, None)
# format A: "lookupflag RightToLeft MarkAttachmentType @M;"
value, markAttachment, markFilteringSet = 0, None, None
flags = {
"RightToLeft": 1, "IgnoreBaseGlyphs": 2,
"IgnoreLigatures": 4, "IgnoreMarks": 8
}
seen = set()
while self.next_token_ != ";":
if self.next_token_ in seen:
raise FeatureLibError(
"%s can be specified only once" % self.next_token_,
self.next_token_location_)
seen.add(self.next_token_)
if self.next_token_ == "MarkAttachmentType":
self.expect_keyword_("MarkAttachmentType")
markAttachment = self.parse_glyphclass_name_()
if self.next_token_ == "UseMarkFilteringSet":
self.expect_keyword_("UseMarkFilteringSet")
markFilteringSet = self.parse_glyphclass_name_()
elif self.next_token_ in flags:
value = value | flags[self.expect_name_()]
else:
raise FeatureLibError(
'"%s" is not a recognized lookupflag' % self.next_token_,
self.next_token_location_)
self.expect_symbol_(";")
return ast.LookupFlagStatement(location, value,
markAttachment, markFilteringSet)
def parse_markClass_(self):
assert self.is_cur_keyword_("markClass")
location = self.cur_token_location_
@ -628,6 +676,8 @@ class Parser(object):
statements.append(self.parse_language_())
elif self.is_cur_keyword_("lookup"):
statements.append(self.parse_lookup_(vertical))
elif self.is_cur_keyword_("lookupflag"):
statements.append(self.parse_lookupflag_())
elif self.is_cur_keyword_("markClass"):
self.parse_markClass_()
elif self.is_cur_keyword_({"pos", "position"}):

View File

@ -335,6 +335,58 @@ class ParserTest(unittest.TestCase):
FeatureLibError, 'Unknown lookup "Huh"',
self.parse, "feature liga {lookup Huh;} liga;")
def parse_lookupflag_(self, s):
return self.parse("lookup L {%s} L;" % s).statements[0].statements[-1]
def test_lookupflag_format_A(self):
flag = self.parse_lookupflag_("lookupflag RightToLeft IgnoreMarks;")
self.assertIsInstance(flag, ast.LookupFlagStatement)
self.assertEqual(flag.value, 9)
self.assertIsNone(flag.markAttachment)
self.assertIsNone(flag.markFilteringSet)
def test_lookupflag_format_A_MarkAttachmentType(self):
flag = self.parse_lookupflag_(
"@TOP_MARKS = [acute grave macron];"
"lookupflag MarkAttachmentType @TOP_MARKS RightToLeft;")
self.assertIsInstance(flag, ast.LookupFlagStatement)
self.assertEqual(flag.value, 1)
self.assertIsInstance(flag.markAttachment, ast.GlyphClassName)
self.assertEqual(flag.markAttachment.glyphclass.glyphs,
{"acute", "grave", "macron"})
self.assertIsNone(flag.markFilteringSet)
def test_lookupflag_format_A_UseMarkFilteringSet(self):
flag = self.parse_lookupflag_(
"@BOTTOM_MARKS = [cedilla ogonek];"
"lookupflag UseMarkFilteringSet @BOTTOM_MARKS IgnoreLigatures;")
self.assertIsInstance(flag, ast.LookupFlagStatement)
self.assertEqual(flag.value, 4)
self.assertIsNone(flag.markAttachment)
self.assertIsInstance(flag.markFilteringSet, ast.GlyphClassName)
self.assertEqual(flag.markFilteringSet.glyphclass.glyphs,
{"cedilla", "ogonek"})
def test_lookupflag_format_B(self):
flag = self.parse_lookupflag_("lookupflag 7;")
self.assertIsInstance(flag, ast.LookupFlagStatement)
self.assertEqual(flag.value, 7)
self.assertIsNone(flag.markAttachment)
self.assertIsNone(flag.markFilteringSet)
def test_lookupflag_repeated(self):
self.assertRaisesRegex(
FeatureLibError,
'RightToLeft can be specified only once',
self.parse,
"feature test {lookupflag RightToLeft RightToLeft;} test;")
def test_lookupflag_unrecognized(self):
self.assertRaisesRegex(
FeatureLibError,
'"IgnoreCookies" is not a recognized lookupflag',
self.parse, "feature test {lookupflag IgnoreCookies;} test;")
def test_gpos_type_1_glyph(self):
doc = self.parse("feature kern {pos one <1 2 3 4>;} kern;")
pos = doc.statements[0].statements[0]