[feaLib] Implement parsing of feature blocks
This commit is contained in:
parent
3936e334f1
commit
944fab8a5f
@ -15,6 +15,20 @@ class FeatureFile(object):
|
||||
s.write(out, linesep)
|
||||
|
||||
|
||||
class FeatureBlock(object):
|
||||
def __init__(self, location, tag):
|
||||
self.location = location
|
||||
self.tag = tag
|
||||
self.statements = []
|
||||
|
||||
def write(self, out, linesep):
|
||||
tag = self.tag.strip()
|
||||
write(out, "feature %s {%s" % (tag, linesep))
|
||||
for s in self.statements:
|
||||
s.write(out, linesep)
|
||||
write(out, "} %s;%s" % (tag, linesep))
|
||||
|
||||
|
||||
class GlyphClassDefinition(object):
|
||||
def __init__(self, location, name, glyphs):
|
||||
self.location = location
|
||||
|
@ -34,11 +34,12 @@ class Parser(object):
|
||||
while self.next_token_type_ is not None:
|
||||
self.advance_lexer_()
|
||||
if self.cur_token_type_ is Lexer.GLYPHCLASS:
|
||||
self.parse_glyphclass_definition_()
|
||||
glyphclass = self.parse_glyphclass_definition_()
|
||||
self.doc_.statements.append(glyphclass)
|
||||
elif self.is_cur_keyword_("languagesystem"):
|
||||
self.parse_languagesystem_()
|
||||
elif self.is_cur_keyword_("feature"):
|
||||
break # TODO: Implement
|
||||
self.parse_feature_block_()
|
||||
else:
|
||||
raise ParserError("Expected languagesystem, feature, or "
|
||||
"glyph class definition",
|
||||
@ -62,7 +63,7 @@ class Parser(object):
|
||||
location)
|
||||
glyphclass = ast.GlyphClassDefinition(location, name, glyphs)
|
||||
self.glyphclasses_[-1][name] = glyphclass
|
||||
self.doc_.statements.append(glyphclass)
|
||||
return glyphclass
|
||||
|
||||
def parse_glyphclass_reference_(self):
|
||||
result = set()
|
||||
@ -103,12 +104,38 @@ class Parser(object):
|
||||
return result
|
||||
|
||||
def parse_languagesystem_(self):
|
||||
assert self.cur_token_ == "languagesystem"
|
||||
location = self.cur_token_location_
|
||||
script, language = self.expect_tag_(), self.expect_tag_()
|
||||
self.expect_symbol_(";")
|
||||
langsys = ast.LanguageSystemStatement(location, script, language)
|
||||
self.doc_.statements.append(langsys)
|
||||
|
||||
def parse_feature_block_(self):
|
||||
assert self.cur_token_ == "feature"
|
||||
location = self.cur_token_location_
|
||||
tag = self.expect_tag_()
|
||||
self.expect_symbol_("{")
|
||||
self.glyphclasses_.append({})
|
||||
block = ast.FeatureBlock(location, tag)
|
||||
self.doc_.statements.append(block)
|
||||
|
||||
while self.next_token_ != "}":
|
||||
self.advance_lexer_()
|
||||
if self.cur_token_type_ is Lexer.GLYPHCLASS:
|
||||
block.statements.append(self.parse_glyphclass_definition_())
|
||||
else:
|
||||
raise ParserError("Expected glyph class definition",
|
||||
self.cur_token_location_)
|
||||
|
||||
self.expect_symbol_("}")
|
||||
self.glyphclasses_.pop()
|
||||
endtag = self.expect_tag_()
|
||||
if tag != endtag:
|
||||
raise ParserError("Expected \"%s\"" % tag.strip(),
|
||||
self.cur_token_location_)
|
||||
self.expect_symbol_(";")
|
||||
|
||||
def is_cur_keyword_(self, k):
|
||||
return (self.cur_token_type_ is Lexer.NAME) and (self.cur_token_ == k)
|
||||
|
||||
|
@ -89,7 +89,7 @@ class ParserTest(unittest.TestCase):
|
||||
|
||||
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.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")))
|
||||
@ -98,6 +98,16 @@ class ParserTest(unittest.TestCase):
|
||||
ParserError, "Unknown glyph class @unknown",
|
||||
self.parse, "@bad = [@unknown];")
|
||||
|
||||
def test_glyphclass_scoping(self):
|
||||
[foo, liga, smcp] = self.parse(
|
||||
"@foo = [a b];"
|
||||
"feature liga { @bar = [@foo l]; } liga;"
|
||||
"feature smcp { @bar = [@foo s]; } smcp;"
|
||||
).statements
|
||||
self.assertEqual(foo.glyphs, {"a", "b"})
|
||||
self.assertEqual(liga.statements[0].glyphs, {"a", "b", "l"})
|
||||
self.assertEqual(smcp.statements[0].glyphs, {"a", "b", "s"})
|
||||
|
||||
def test_languagesystem(self):
|
||||
[langsys] = self.parse("languagesystem latn DEU;").statements
|
||||
self.assertEqual(langsys.script, "latn")
|
||||
@ -112,6 +122,12 @@ class ParserTest(unittest.TestCase):
|
||||
ParserError, "longer than 4 characters",
|
||||
self.parse, "languagesystem latn FOOBAR")
|
||||
|
||||
def test_feature_block(self):
|
||||
[liga] = self.parse("feature liga {} liga;").statements
|
||||
self.assertEqual(liga.tag, "liga")
|
||||
|
||||
# TODO: Implement the needed bits in the parser.
|
||||
@unittest.expectedFailure
|
||||
def test_roundtrip(self):
|
||||
self.roundtrip("mini.fea")
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user