[feaLib] Parse ignore sub
and ignore substitute
statements
This commit is contained in:
parent
482c498943
commit
bc8279bab1
@ -50,10 +50,15 @@ class LanguageSystemStatement(object):
|
||||
(self.script.strip(), self.language.strip(), linesep))
|
||||
|
||||
|
||||
class IgnoreSubstitutionRule(object):
|
||||
def __init__(self, location, prefix, glyphs, suffix):
|
||||
self.location = location
|
||||
self.prefix, self.glyphs, self.suffix = (prefix, glyphs, suffix)
|
||||
|
||||
|
||||
class SubstitutionRule(object):
|
||||
def __init__(self, location, old, new):
|
||||
self.location, self.old, self.new = (location, old, new)
|
||||
self.ignored = False
|
||||
self.old_prefix = []
|
||||
self.old_suffix = []
|
||||
|
||||
|
@ -105,25 +105,40 @@ class Parser(object):
|
||||
self.expect_symbol_("]")
|
||||
return result
|
||||
|
||||
def parse_substitute_(self):
|
||||
assert self.is_cur_keyword_("substitute") or self.is_cur_keyword("sub")
|
||||
location = self.cur_token_location_
|
||||
old_prefix, old, old_suffix = ([], [], [])
|
||||
while self.next_token_ != "by":
|
||||
def parse_glyph_pattern_(self):
|
||||
prefix, glyphs, suffix = ([], [], [])
|
||||
while self.next_token_ not in {"by", ";"}:
|
||||
gc = self.parse_glyphclass_(accept_glyphname=True)
|
||||
marked = False
|
||||
if self.next_token_ == "'":
|
||||
self.expect_symbol_("'")
|
||||
marked = True
|
||||
if marked:
|
||||
old.append(gc)
|
||||
elif old:
|
||||
old_suffix.append(gc)
|
||||
glyphs.append(gc)
|
||||
elif glyphs:
|
||||
suffix.append(gc)
|
||||
else:
|
||||
old_prefix.append(gc)
|
||||
if not old and not old_suffix: # eg., "sub f f i by"
|
||||
old = old_prefix
|
||||
old_prefix = []
|
||||
assert self.expect_name_() == "by"
|
||||
prefix.append(gc)
|
||||
if not glyphs and not suffix: # eg., "sub f f i by"
|
||||
return ([], prefix, [])
|
||||
else:
|
||||
return (prefix, glyphs, suffix)
|
||||
|
||||
def parse_ignore_(self):
|
||||
assert self.is_cur_keyword_("ignore")
|
||||
location = self.cur_token_location_
|
||||
self.advance_lexer_()
|
||||
if self.cur_token_ in ["substitute", "sub"]:
|
||||
prefix, glyphs, suffix = self.parse_glyph_pattern_()
|
||||
self.expect_symbol_(";")
|
||||
return ast.IgnoreSubstitutionRule(location, prefix, glyphs, suffix)
|
||||
raise ParserError("Expected \"substitute\"", self.next_token_location_)
|
||||
|
||||
def parse_substitute_(self):
|
||||
assert self.cur_token_ in {"substitute", "sub"}
|
||||
location = self.cur_token_location_
|
||||
old_prefix, old, old_suffix = self.parse_glyph_pattern_()
|
||||
self.expect_keyword_("by")
|
||||
new = self.parse_glyphclass_(accept_glyphname=True)
|
||||
self.expect_symbol_(";")
|
||||
rule = ast.SubstitutionRule(location, old, [new])
|
||||
@ -193,6 +208,8 @@ class Parser(object):
|
||||
self.advance_lexer_()
|
||||
if self.cur_token_type_ is Lexer.GLYPHCLASS:
|
||||
statements.append(self.parse_glyphclass_definition_())
|
||||
elif self.is_cur_keyword_("ignore"):
|
||||
statements.append(self.parse_ignore_())
|
||||
elif (self.is_cur_keyword_("substitute") or
|
||||
self.is_cur_keyword_("sub")):
|
||||
statements.append(self.parse_substitute_())
|
||||
@ -200,7 +217,8 @@ class Parser(object):
|
||||
statements.append(self.parse_valuerecord_definition_(vertical))
|
||||
else:
|
||||
raise ParserError(
|
||||
"Expected glyph class definition or valueRecordDef",
|
||||
"Expected glyph class definition, substitute, "
|
||||
"or valueRecordDef",
|
||||
self.cur_token_location_)
|
||||
|
||||
self.expect_symbol_("}")
|
||||
@ -231,6 +249,13 @@ class Parser(object):
|
||||
return symbol
|
||||
raise ParserError("Expected '%s'" % symbol, self.cur_token_location_)
|
||||
|
||||
def expect_keyword_(self, keyword):
|
||||
self.advance_lexer_()
|
||||
if self.cur_token_type_ is Lexer.NAME and self.cur_token_ == keyword:
|
||||
return self.cur_token_
|
||||
raise ParserError("Expected \"%s\"" % keyword,
|
||||
self.cur_token_location_)
|
||||
|
||||
def expect_name_(self):
|
||||
self.advance_lexer_()
|
||||
if self.cur_token_type_ is Lexer.NAME:
|
||||
|
@ -3,6 +3,7 @@ from __future__ import unicode_literals
|
||||
from fontTools.feaLib.lexer import LexerError
|
||||
from fontTools.feaLib.parser import Parser, ParserError, SymbolTable
|
||||
from fontTools.misc.py23 import *
|
||||
import fontTools.feaLib.ast as ast
|
||||
import codecs
|
||||
import os
|
||||
import shutil
|
||||
@ -109,6 +110,25 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertEqual(liga.statements[0].glyphs, {"a", "b", "l"})
|
||||
self.assertEqual(smcp.statements[0].glyphs, {"a", "b", "s"})
|
||||
|
||||
def test_ignore_sub(self):
|
||||
doc = self.parse("feature test {ignore sub e t' c;} test;")
|
||||
s = doc.statements[0].statements[0]
|
||||
self.assertEqual(type(s), ast.IgnoreSubstitutionRule)
|
||||
self.assertEqual(s.prefix, [{"e"}])
|
||||
self.assertEqual(s.glyphs, [{"t"}])
|
||||
self.assertEqual(s.suffix, [{"c"}])
|
||||
|
||||
def test_ignore_substitute(self):
|
||||
doc = self.parse(
|
||||
"feature test {"
|
||||
" ignore substitute f [a e] d' [a u]' [e y];"
|
||||
"} test;")
|
||||
s = doc.statements[0].statements[0]
|
||||
self.assertEqual(type(s), ast.IgnoreSubstitutionRule)
|
||||
self.assertEqual(s.prefix, [{"f"}, {"a", "e"}])
|
||||
self.assertEqual(s.glyphs, [{"d"}, {"a", "u"}])
|
||||
self.assertEqual(s.suffix, [{"e", "y"}])
|
||||
|
||||
def test_substitute_single_format_a(self): # GSUB LookupType 1
|
||||
doc = self.parse("feature smcp {substitute a by a.sc;} smcp;")
|
||||
sub = doc.statements[0].statements[0]
|
||||
@ -147,6 +167,11 @@ class ParserTest(unittest.TestCase):
|
||||
self.assertEqual(sub.old_suffix, [])
|
||||
self.assertEqual(sub.new, [{"f_f_i"}])
|
||||
|
||||
def test_substitute_missing_by(self):
|
||||
self.assertRaisesRegex(
|
||||
ParserError, "Expected \"by\"",
|
||||
self.parse, "feature liga {substitute f f i;} liga;")
|
||||
|
||||
def test_valuerecord_format_a_horizontal(self):
|
||||
doc = self.parse("feature liga {valueRecordDef 123 foo;} liga;")
|
||||
value = doc.statements[0].statements[0].value
|
||||
|
Loading…
x
Reference in New Issue
Block a user