2015-09-30 09:52:49 +01:00
|
|
|
from __future__ import print_function, division, absolute_import
|
|
|
|
import fontTools.voltLib.ast as ast
|
2015-09-30 14:06:51 +01:00
|
|
|
import fontTools.feaLib.parser as parser
|
2015-09-30 09:52:49 +01:00
|
|
|
from fontTools.voltLib.lexer import Lexer
|
|
|
|
from fontTools.voltLib.error import VoltLibError
|
|
|
|
import codecs
|
|
|
|
|
|
|
|
class Parser(object):
|
|
|
|
def __init__(self, path):
|
|
|
|
self.doc_ = ast.VoltFile()
|
2015-09-30 14:06:51 +01:00
|
|
|
self.groups_ = SymbolTable()
|
2015-09-30 09:52:49 +01:00
|
|
|
self.next_token_type_, self.next_token_ = (None, None)
|
|
|
|
self.next_token_location_ = None
|
|
|
|
try:
|
|
|
|
with codecs.open(path, "rb", "utf-8") as f:
|
|
|
|
self.lexer_ = Lexer(f.read(), path)
|
|
|
|
except IOError as err:
|
|
|
|
raise VoltLibError(str(err), location)
|
|
|
|
self.advance_lexer_()
|
|
|
|
|
|
|
|
def parse(self):
|
|
|
|
statements = self.doc_.statements
|
|
|
|
while self.next_token_type_ is not None:
|
|
|
|
self.advance_lexer_()
|
|
|
|
if self.is_cur_keyword_("DEF_GLYPH"):
|
|
|
|
statements.append(self.parse_def_glyph_())
|
2015-09-30 14:06:51 +01:00
|
|
|
elif self.is_cur_keyword_("DEF_GROUP"):
|
|
|
|
statements.append(self.parse_def_group_())
|
2015-09-30 09:52:49 +01:00
|
|
|
elif self.is_cur_keyword_("END"):
|
|
|
|
if self.next_token_type_ is not None:
|
|
|
|
raise VoltLibError("Expected the end of the file",
|
|
|
|
self.cur_token_location_)
|
|
|
|
return self.doc_
|
|
|
|
else:
|
2015-09-30 14:06:51 +01:00
|
|
|
raise VoltLibError("Expected DEF_GLYPH, DEF_GROUP",
|
2015-09-30 09:52:49 +01:00
|
|
|
self.cur_token_location_)
|
|
|
|
return self.doc_
|
|
|
|
|
|
|
|
def parse_def_glyph_(self):
|
|
|
|
assert self.is_cur_keyword_("DEF_GLYPH")
|
|
|
|
location = self.cur_token_location_
|
|
|
|
name = self.expect_string_()
|
|
|
|
self.expect_keyword_("ID")
|
|
|
|
gid = self.expect_number_()
|
|
|
|
if gid < 0:
|
|
|
|
raise VoltLibError("Invalid glyph ID", self.cur_token_location_)
|
|
|
|
gunicode = None
|
|
|
|
if self.next_token_ == "UNICODE":
|
|
|
|
self.expect_keyword_("UNICODE")
|
|
|
|
gunicode = [self.expect_number_()]
|
|
|
|
if gunicode[0] < 0:
|
|
|
|
raise VoltLibError("Invalid glyph UNICODE",
|
|
|
|
self.cur_token_location_)
|
|
|
|
elif self.next_token_ == "UNICODEVALUES":
|
|
|
|
self.expect_keyword_("UNICODEVALUES")
|
2015-09-30 14:06:51 +01:00
|
|
|
gunicode = self.parse_unicode_values_()
|
2015-09-30 09:52:49 +01:00
|
|
|
# Apparently TYPE is optional
|
|
|
|
gtype = None
|
|
|
|
if self.next_token_ == "TYPE":
|
|
|
|
self.expect_keyword_("TYPE")
|
|
|
|
gtype = self.expect_name_()
|
|
|
|
assert gtype in ("BASE", "LIGATURE", "MARK")
|
|
|
|
components = None
|
|
|
|
if gtype == "LIGATURE":
|
|
|
|
self.expect_keyword_("COMPONENTS")
|
|
|
|
components = self.expect_number_()
|
|
|
|
self.expect_keyword_("END_GLYPH")
|
|
|
|
def_glyph = ast.GlyphDefinition(location, name, gid,
|
|
|
|
gunicode, gtype, components)
|
|
|
|
return def_glyph
|
|
|
|
|
2015-09-30 14:06:51 +01:00
|
|
|
def parse_def_group_(self):
|
|
|
|
assert self.is_cur_keyword_("DEF_GROUP")
|
|
|
|
location = self.cur_token_location_
|
|
|
|
name = self.expect_string_()
|
|
|
|
enum = None
|
|
|
|
if self.next_token_ == "ENUM":
|
|
|
|
self.expect_keyword_("ENUM")
|
|
|
|
enum = self.parse_enum_()
|
|
|
|
self.expect_keyword_("END_GROUP")
|
|
|
|
if self.groups_.resolve(name) is not None:
|
|
|
|
raise VoltLibError('Glyph group "%s" already defined' % name,
|
|
|
|
location)
|
|
|
|
def_group = ast.GroupDefinition(location, name, enum)
|
|
|
|
self.groups_.define(name, def_group)
|
|
|
|
return def_group
|
|
|
|
|
|
|
|
def parse_unicode_values_(self):
|
2015-09-30 09:52:49 +01:00
|
|
|
location = self.cur_token_location_
|
|
|
|
unicode_values = self.expect_string_().split(',')
|
|
|
|
return [int(uni[2:], 16) for uni in unicode_values]
|
|
|
|
|
2015-09-30 14:06:51 +01:00
|
|
|
def parse_enum_(self):
|
|
|
|
assert self.is_cur_keyword_("ENUM")
|
|
|
|
location = self.cur_token_location_
|
|
|
|
enum = {'glyphs': [], 'groups': []}
|
|
|
|
while self.next_token_ != "END_ENUM":
|
|
|
|
if self.next_token_ == "GLYPH":
|
|
|
|
self.expect_keyword_("GLYPH")
|
|
|
|
name = self.expect_string_()
|
|
|
|
enum['glyphs'].append(name)
|
|
|
|
elif self.next_token_ == "GROUP":
|
|
|
|
self.expect_keyword_("GROUP")
|
|
|
|
name = self.expect_string_()
|
|
|
|
enum['groups'].append(name)
|
|
|
|
self.expect_keyword_("END_ENUM")
|
|
|
|
return enum
|
|
|
|
|
2015-09-30 09:52:49 +01:00
|
|
|
def is_cur_keyword_(self, k):
|
|
|
|
return (self.cur_token_type_ is Lexer.NAME) and (self.cur_token_ == k)
|
|
|
|
|
|
|
|
def expect_string_(self):
|
|
|
|
self.advance_lexer_()
|
|
|
|
if self.cur_token_type_ is not Lexer.STRING:
|
|
|
|
raise VoltLibError("Expected a string", self.cur_token_location_)
|
|
|
|
return self.cur_token_
|
|
|
|
|
|
|
|
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 VoltLibError("Expected \"%s\"" % keyword,
|
|
|
|
self.cur_token_location_)
|
|
|
|
|
|
|
|
def expect_name_(self):
|
|
|
|
self.advance_lexer_()
|
|
|
|
if self.cur_token_type_ is Lexer.NAME:
|
|
|
|
return self.cur_token_
|
|
|
|
raise VoltLibError("Expected a name", self.cur_token_location_)
|
|
|
|
|
|
|
|
def expect_number_(self):
|
|
|
|
self.advance_lexer_()
|
|
|
|
if self.cur_token_type_ is not Lexer.NUMBER:
|
|
|
|
raise VoltLibError("Expected a number", self.cur_token_location_)
|
|
|
|
return self.cur_token_
|
|
|
|
|
|
|
|
def advance_lexer_(self):
|
|
|
|
self.cur_token_type_, self.cur_token_, self.cur_token_location_ = (
|
|
|
|
self.next_token_type_, self.next_token_, self.next_token_location_)
|
|
|
|
try:
|
|
|
|
(self.next_token_type_, self.next_token_,
|
|
|
|
self.next_token_location_) = self.lexer_.next()
|
|
|
|
except StopIteration:
|
|
|
|
self.next_token_type_, self.next_token_ = (None, None)
|
2015-09-30 14:06:51 +01:00
|
|
|
|
|
|
|
class SymbolTable(parser.SymbolTable):
|
|
|
|
def __init__(self):
|
|
|
|
parser.SymbolTable.__init__(self)
|