summaryrefslogtreecommitdiffstats
path: root/src/silfont/feax_lexer.py
blob: 77764eff0792f67043d298676bb7402ce5d216ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
from fontTools.feaLib.lexer import IncludingLexer, Lexer
from fontTools.feaLib.error import FeatureLibError
import re, io

VARIABLE = "VARIABLE"

class feax_Lexer(Lexer):

    def __init__(self, *a):
        Lexer.__init__(self, *a)
        self.tokens_ = None
        self.stack_ = []
        self.empty_ = False

    def next_(self, recurse=False):
        while (not self.empty_):
            if self.tokens_ is not None:
                res = self.tokens_.pop(0)
                if not len(self.tokens_):
                    self.popstack()
                if res[0] != VARIABLE:
                    return (res[0], res[1], self.location_())
                return self.parse_variable(res[1])

            try:
                res = Lexer.next_(self)
            except IndexError as e:
                self.popstack()
                continue
            except StopIteration as e:
                self.popstack()
                continue
            except FeatureLibError as e:
                if u"Unexpected character" not in str(e):
                    raise e

                # only executes if exception occurred
                location = self.location_()
                text = self.text_
                start = self.pos_
                cur_char = text[start]
                if cur_char == '$':
                    self.pos_ += 1
                    self.scan_over_(Lexer.CHAR_NAME_CONTINUATION_)
                    varname = text[start+1:self.pos_]
                    if len(varname) < 1 or len(varname) > 63:
                        raise FeatureLibError("Bad variable name length for: %s" % varname, location)
                    res = (VARIABLE, varname, location)
                else:
                    raise FeatureLibError("Unexpected character: %r" % cur_char, location)
            return res
        raise StopIteration

    def __repr__(self):
        if self.tokens_ is not None:
            return str(self.tokens_)
        else:
            return str((self.text_[self.pos_:self.pos_+20], self.pos_, self.text_length_))

    def popstack(self):
        if len(self.stack_) == 0:
            self.empty_ = True
            return
        t = self.stack_.pop()
        if t[0] == 'tokens':
            self.tokens_ = t[1]
        else:
            self.text_, self.pos_, self.text_length_ = t[1]
            self.tokens_ = None

    def pushstack(self, v):
        if self.tokens_ is None:
            self.stack_.append(('text', (self.text_, self.pos_, self.text_length_)))
        else:
            self.stack_.append(('tokens', self.tokens_))
        self.stack_.append(v)
        self.popstack()

    def pushback(self, token_type, token):
        if self.tokens_ is not None:
            self.tokens_.append((token_type, token))
        else:
            self.pushstack(('tokens', [(token_type, token)]))
        
    def parse_variable(self, vname):
        t = str(self.scope.get(vname, ''))
        if t != '':
            self.pushstack(['text', (t + " ", 0, len(t)+1)])
        return self.next_()

class feax_IncludingLexer(IncludingLexer):

    @staticmethod
    def make_lexer_(file_or_path):
        if hasattr(file_or_path, "read"):
            fileobj, closing = file_or_path, False
        else:
            filename, closing = file_or_path, True
            fileobj = io.open(filename, mode="r", encoding="utf-8")
        data = fileobj.read()
        filename = getattr(fileobj, "name", None)
        if closing:
            fileobj.close()
        return feax_Lexer(data, filename)