[feaLib] Parse lookup blocks and references

This commit is contained in:
Sascha Brawer 2015-08-11 10:59:26 +02:00
parent 6d7540ecac
commit c165a1e019
3 changed files with 95 additions and 3 deletions

View File

@ -14,6 +14,13 @@ class FeatureBlock(object):
self.statements = []
class LookupBlock(object):
def __init__(self, location, name, use_extension):
self.location = location
self.name, self.use_extension = name, use_extension
self.statements = []
class GlyphClassDefinition(object):
def __init__(self, location, name, glyphs):
self.location = location
@ -41,6 +48,11 @@ class IgnoreSubstitutionRule(object):
self.prefix, self.glyphs, self.suffix = (prefix, glyphs, suffix)
class LookupReferenceStatement(object):
def __init__(self, location, lookup):
self.location, self.lookup = (location, lookup)
class ScriptStatement(object):
def __init__(self, location, script):
self.location = location

View File

@ -24,8 +24,11 @@ class Parser(object):
def __init__(self, path):
self.doc_ = ast.FeatureFile()
self.glyphclasses_ = SymbolTable()
self.lookups_ = SymbolTable()
self.valuerecords_ = SymbolTable()
self.symbol_tables_ = [self.glyphclasses_, self.valuerecords_]
self.symbol_tables_ = {
self.glyphclasses_, self.lookups_, self.valuerecords_
}
self.next_token_type_, self.next_token_ = (None, None)
self.next_token_location_ = None
self.lexer_ = IncludingLexer(path)
@ -42,11 +45,13 @@ class Parser(object):
self.parse_valuerecord_definition_(vertical=False))
elif self.is_cur_keyword_("languagesystem"):
self.parse_languagesystem_()
elif self.is_cur_keyword_("lookup"):
statements.append(self.parse_lookup_(vertical=False))
elif self.is_cur_keyword_("feature"):
statements.append(self.parse_feature_block_())
else:
raise ParserError("Expected languagesystem, feature, or "
"glyph class definition",
raise ParserError("Expected feature, languagesystem, "
"lookup, or glyph class definition",
self.cur_token_location_)
return self.doc_
@ -147,6 +152,28 @@ class Parser(object):
return ast.LanguageStatement(location, language.strip(),
include_default, required)
def parse_lookup_(self, vertical):
assert self.is_cur_keyword_("lookup")
location, name = self.cur_token_location_, self.expect_name_()
if self.next_token_ == ";":
lookup = self.lookups_.resolve(name)
if lookup is None:
raise ParserError("Unknown lookup \"%s\"" % name,
self.cur_token_location_)
self.expect_symbol_(";")
return ast.LookupReferenceStatement(location, lookup)
use_extension = False
if self.next_token_ == "useExtension":
self.expect_keyword_("useExtension")
use_extension = True
block = ast.LookupBlock(location, name, use_extension)
self.parse_block_(block, vertical)
self.lookups_.define(name, block)
return block
def parse_script_(self):
assert self.is_cur_keyword_("script")
location, script = self.cur_token_location_, self.expect_tag_()
@ -232,6 +259,8 @@ class Parser(object):
statements.append(self.parse_ignore_())
elif self.is_cur_keyword_("language"):
statements.append(self.parse_language_())
elif self.is_cur_keyword_("lookup"):
statements.append(self.parse_lookup_(vertical))
elif self.is_cur_keyword_("script"):
statements.append(self.parse_script_())
elif (self.is_cur_keyword_("substitute") or

View File

@ -173,6 +173,57 @@ class ParserTest(unittest.TestCase):
self.assertTrue(s.include_default)
self.assertTrue(s.required)
def test_lookup_block(self):
[lookup] = self.parse("lookup Ligatures {} Ligatures;").statements
self.assertEqual(lookup.name, "Ligatures")
self.assertFalse(lookup.use_extension)
def test_lookup_block_useExtension(self):
[lookup] = self.parse("lookup Foo useExtension {} Foo;").statements
self.assertEqual(lookup.name, "Foo")
self.assertTrue(lookup.use_extension)
def test_lookup_block_name_mismatch(self):
self.assertRaisesRegex(
ParserError, 'Expected "Foo"',
self.parse, "lookup Foo {} Bar;")
def test_lookup_block_with_horizontal_valueRecordDef(self):
doc = self.parse("feature liga {"
" lookup look {"
" valueRecordDef 123 foo;"
" } look;"
"} liga;")
[liga] = doc.statements
[look] = liga.statements
[foo] = look.statements
self.assertEqual(foo.value.xAdvance, 123)
self.assertEqual(foo.value.yAdvance, 0)
def test_lookup_block_with_vertical_valueRecordDef(self):
doc = self.parse("feature vkrn {"
" lookup look {"
" valueRecordDef 123 foo;"
" } look;"
"} vkrn;")
[vkrn] = doc.statements
[look] = vkrn.statements
[foo] = look.statements
self.assertEqual(foo.value.xAdvance, 0)
self.assertEqual(foo.value.yAdvance, 123)
def test_lookup_reference(self):
[foo, bar] = self.parse("lookup Foo {} Foo;"
"feature Bar {lookup Foo;} Bar;").statements
[ref] = bar.statements
self.assertEqual(type(ref), ast.LookupReferenceStatement)
self.assertEqual(ref.lookup, foo)
def test_lookup_reference_unknown(self):
self.assertRaisesRegex(
ParserError, 'Unknown lookup "Huh"',
self.parse, "feature liga {lookup Huh;} liga;")
def test_script(self):
doc = self.parse("feature test {script cyrl;} test;")
s = doc.statements[0].statements[0]