[feaLib] Clean up syntax tree for FeatureNames

The syntax tree representation now reflects the syntax of feature files.
Before this change, FeatureNames did not have their own `ast.Block`,
which had made the code quite messy.
This commit is contained in:
Sascha Brawer 2017-03-09 16:35:28 +01:00
parent 4210c9d2d6
commit 189c722626
4 changed files with 52 additions and 32 deletions

View File

@ -225,25 +225,20 @@ class FeatureBlock(Block):
builder.end_feature()
def asFea(self, indent=""):
res = indent + "feature {} {{\n".format(self.name.strip())
indent += SHIFT
if len(self.statements) and isinstance(self.statements[0], FeatureNameStatement):
res += indent + "featureNames {\n"
res += indent + SHIFT
res += ("\n" + indent + SHIFT).join(
[s.asFea(indent=indent + SHIFT * 2)
for s in self.statements if isinstance(s, FeatureNameStatement)])
res += "\n"
res += indent + "};\n" + indent
res += ("\n" + indent).join(
[s.asFea(indent=indent)
for s in self.statements if not isinstance(s, FeatureNameStatement)])
res += "\n"
else:
res += indent
res += ("\n" + indent).join([s.asFea(indent=indent) for s in self.statements])
res += "\n"
res += "{}}} {};\n".format(indent[:-len(SHIFT)], self.name.strip())
res = indent + "feature %s {\n" % self.name.strip()
res += Block.asFea(self, indent=indent)
res += indent + "} %s;\n" % self.name.strip()
return res
class FeatureNamesBlock(Block):
def __init__(self, location):
Block.__init__(self, location)
def asFea(self, indent=""):
res = indent + "featureNames {\n"
res += Block.asFea(self, indent=indent)
res += indent + "};\n"
return res

View File

@ -1152,27 +1152,30 @@ class Parser(object):
def parse_featureNames_(self, tag):
assert self.cur_token_ == "featureNames", self.cur_token_
block = self.ast.FeatureNamesBlock(self.cur_token_location_)
self.expect_symbol_("{")
for symtab in self.symbol_tables_:
symtab.enter_scope()
statements = []
while self.next_token_ != "}":
self.expect_keyword_("name")
while self.next_token_ != "}" or self.cur_comments_:
self.advance_lexer_(comments=True)
if self.cur_token_type_ is Lexer.COMMENT:
block.statements.append(self.ast.Comment(self.cur_token_location_, self.cur_token_))
elif self.is_cur_keyword_("name"):
location = self.cur_token_location_
platformID, platEncID, langID, string = self.parse_name_()
statements.append(
block.statements.append(
self.ast.FeatureNameStatement(location, tag, platformID,
platEncID, langID, string))
elif self.cur_token_ == ";":
continue
else:
raise FeatureLibError('Expected "name"',
self.cur_token_location_)
self.expect_symbol_("}")
for symtab in self.symbol_tables_:
symtab.exit_scope()
self.expect_symbol_(";")
return statements
return block
def parse_FontRevision_(self):
assert self.cur_token_ == "FontRevision", self.cur_token_
@ -1225,7 +1228,7 @@ class Parser(object):
elif self.is_cur_keyword_("valueRecordDef"):
statements.append(self.parse_valuerecord_definition_(vertical))
elif stylisticset and self.is_cur_keyword_("featureNames"):
statements.extend(self.parse_featureNames_(stylisticset))
statements.append(self.parse_featureNames_(stylisticset))
elif size_feature and self.is_cur_keyword_("parameters"):
statements.append(self.parse_size_parameters_())
elif size_feature and self.is_cur_keyword_("sizemenuname"):

View File

@ -1,5 +1,6 @@
- [feaLib] Added (partial) support for parsing feature file comments ``# ...``
appearing in between statements (#879).
- [feaLib] Cleaned up syntax tree for FeatureNames.
- [ttLib] Added support for reading/writing ``CFF2`` table (thanks to
@readroberts at Adobe), and ``TTFA`` (ttfautohint) table.

View File

@ -202,6 +202,27 @@ class ParserTest(unittest.TestCase):
self.assertIsInstance(ref, ast.FeatureReferenceStatement)
self.assertEqual(ref.featureName, "salt")
def test_FeatureNames_bad(self):
self.assertRaisesRegex(
FeatureLibError, 'Expected "name"',
self.parse, "feature ss01 { featureNames { feature test; } ss01;")
def test_FeatureNames_comment(self):
[feature] = self.parse(
"feature ss01 { featureNames { # Comment\n }; } ss01;").statements
[featureNames] = feature.statements
self.assertIsInstance(featureNames, ast.FeatureNamesBlock)
[comment] = featureNames.statements
self.assertIsInstance(comment, ast.Comment)
self.assertEqual(comment.text, "# Comment")
def test_FeatureNames_emptyStatements(self):
[feature] = self.parse(
"feature ss01 { featureNames { ;;; }; } ss01;").statements
[featureNames] = feature.statements
self.assertIsInstance(featureNames, ast.FeatureNamesBlock)
self.assertEqual(featureNames.statements, [])
def test_FontRevision(self):
doc = self.parse("table head {FontRevision 2.5;} head;")
s = doc.statements[0].statements[0]