[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))
|
(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):
|
class SubstitutionRule(object):
|
||||||
def __init__(self, location, old, new):
|
def __init__(self, location, old, new):
|
||||||
self.location, self.old, self.new = (location, old, new)
|
self.location, self.old, self.new = (location, old, new)
|
||||||
self.ignored = False
|
|
||||||
self.old_prefix = []
|
self.old_prefix = []
|
||||||
self.old_suffix = []
|
self.old_suffix = []
|
||||||
|
|
||||||
|
@ -105,25 +105,40 @@ class Parser(object):
|
|||||||
self.expect_symbol_("]")
|
self.expect_symbol_("]")
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def parse_substitute_(self):
|
def parse_glyph_pattern_(self):
|
||||||
assert self.is_cur_keyword_("substitute") or self.is_cur_keyword("sub")
|
prefix, glyphs, suffix = ([], [], [])
|
||||||
location = self.cur_token_location_
|
while self.next_token_ not in {"by", ";"}:
|
||||||
old_prefix, old, old_suffix = ([], [], [])
|
|
||||||
while self.next_token_ != "by":
|
|
||||||
gc = self.parse_glyphclass_(accept_glyphname=True)
|
gc = self.parse_glyphclass_(accept_glyphname=True)
|
||||||
marked = False
|
marked = False
|
||||||
if self.next_token_ == "'":
|
if self.next_token_ == "'":
|
||||||
self.expect_symbol_("'")
|
self.expect_symbol_("'")
|
||||||
|
marked = True
|
||||||
if marked:
|
if marked:
|
||||||
old.append(gc)
|
glyphs.append(gc)
|
||||||
elif old:
|
elif glyphs:
|
||||||
old_suffix.append(gc)
|
suffix.append(gc)
|
||||||
else:
|
else:
|
||||||
old_prefix.append(gc)
|
prefix.append(gc)
|
||||||
if not old and not old_suffix: # eg., "sub f f i by"
|
if not glyphs and not suffix: # eg., "sub f f i by"
|
||||||
old = old_prefix
|
return ([], prefix, [])
|
||||||
old_prefix = []
|
else:
|
||||||
assert self.expect_name_() == "by"
|
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)
|
new = self.parse_glyphclass_(accept_glyphname=True)
|
||||||
self.expect_symbol_(";")
|
self.expect_symbol_(";")
|
||||||
rule = ast.SubstitutionRule(location, old, [new])
|
rule = ast.SubstitutionRule(location, old, [new])
|
||||||
@ -193,6 +208,8 @@ class Parser(object):
|
|||||||
self.advance_lexer_()
|
self.advance_lexer_()
|
||||||
if self.cur_token_type_ is Lexer.GLYPHCLASS:
|
if self.cur_token_type_ is Lexer.GLYPHCLASS:
|
||||||
statements.append(self.parse_glyphclass_definition_())
|
statements.append(self.parse_glyphclass_definition_())
|
||||||
|
elif self.is_cur_keyword_("ignore"):
|
||||||
|
statements.append(self.parse_ignore_())
|
||||||
elif (self.is_cur_keyword_("substitute") or
|
elif (self.is_cur_keyword_("substitute") or
|
||||||
self.is_cur_keyword_("sub")):
|
self.is_cur_keyword_("sub")):
|
||||||
statements.append(self.parse_substitute_())
|
statements.append(self.parse_substitute_())
|
||||||
@ -200,7 +217,8 @@ class Parser(object):
|
|||||||
statements.append(self.parse_valuerecord_definition_(vertical))
|
statements.append(self.parse_valuerecord_definition_(vertical))
|
||||||
else:
|
else:
|
||||||
raise ParserError(
|
raise ParserError(
|
||||||
"Expected glyph class definition or valueRecordDef",
|
"Expected glyph class definition, substitute, "
|
||||||
|
"or valueRecordDef",
|
||||||
self.cur_token_location_)
|
self.cur_token_location_)
|
||||||
|
|
||||||
self.expect_symbol_("}")
|
self.expect_symbol_("}")
|
||||||
@ -231,6 +249,13 @@ class Parser(object):
|
|||||||
return symbol
|
return symbol
|
||||||
raise ParserError("Expected '%s'" % symbol, self.cur_token_location_)
|
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):
|
def expect_name_(self):
|
||||||
self.advance_lexer_()
|
self.advance_lexer_()
|
||||||
if self.cur_token_type_ is Lexer.NAME:
|
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.lexer import LexerError
|
||||||
from fontTools.feaLib.parser import Parser, ParserError, SymbolTable
|
from fontTools.feaLib.parser import Parser, ParserError, SymbolTable
|
||||||
from fontTools.misc.py23 import *
|
from fontTools.misc.py23 import *
|
||||||
|
import fontTools.feaLib.ast as ast
|
||||||
import codecs
|
import codecs
|
||||||
import os
|
import os
|
||||||
import shutil
|
import shutil
|
||||||
@ -109,6 +110,25 @@ class ParserTest(unittest.TestCase):
|
|||||||
self.assertEqual(liga.statements[0].glyphs, {"a", "b", "l"})
|
self.assertEqual(liga.statements[0].glyphs, {"a", "b", "l"})
|
||||||
self.assertEqual(smcp.statements[0].glyphs, {"a", "b", "s"})
|
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
|
def test_substitute_single_format_a(self): # GSUB LookupType 1
|
||||||
doc = self.parse("feature smcp {substitute a by a.sc;} smcp;")
|
doc = self.parse("feature smcp {substitute a by a.sc;} smcp;")
|
||||||
sub = doc.statements[0].statements[0]
|
sub = doc.statements[0].statements[0]
|
||||||
@ -147,6 +167,11 @@ class ParserTest(unittest.TestCase):
|
|||||||
self.assertEqual(sub.old_suffix, [])
|
self.assertEqual(sub.old_suffix, [])
|
||||||
self.assertEqual(sub.new, [{"f_f_i"}])
|
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):
|
def test_valuerecord_format_a_horizontal(self):
|
||||||
doc = self.parse("feature liga {valueRecordDef 123 foo;} liga;")
|
doc = self.parse("feature liga {valueRecordDef 123 foo;} liga;")
|
||||||
value = doc.statements[0].statements[0].value
|
value = doc.statements[0].statements[0].value
|
||||||
|
Loading…
x
Reference in New Issue
Block a user