1*e1fe3e4aSElliott Hughesfrom fontTools.voltLib.error import VoltLibError 2*e1fe3e4aSElliott Hughes 3*e1fe3e4aSElliott Hughes 4*e1fe3e4aSElliott Hughesclass Lexer(object): 5*e1fe3e4aSElliott Hughes NUMBER = "NUMBER" 6*e1fe3e4aSElliott Hughes STRING = "STRING" 7*e1fe3e4aSElliott Hughes NAME = "NAME" 8*e1fe3e4aSElliott Hughes NEWLINE = "NEWLINE" 9*e1fe3e4aSElliott Hughes 10*e1fe3e4aSElliott Hughes CHAR_WHITESPACE_ = " \t" 11*e1fe3e4aSElliott Hughes CHAR_NEWLINE_ = "\r\n" 12*e1fe3e4aSElliott Hughes CHAR_DIGIT_ = "0123456789" 13*e1fe3e4aSElliott Hughes CHAR_UC_LETTER_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" 14*e1fe3e4aSElliott Hughes CHAR_LC_LETTER_ = "abcdefghijklmnopqrstuvwxyz" 15*e1fe3e4aSElliott Hughes CHAR_UNDERSCORE_ = "_" 16*e1fe3e4aSElliott Hughes CHAR_PERIOD_ = "." 17*e1fe3e4aSElliott Hughes CHAR_NAME_START_ = ( 18*e1fe3e4aSElliott Hughes CHAR_UC_LETTER_ + CHAR_LC_LETTER_ + CHAR_PERIOD_ + CHAR_UNDERSCORE_ 19*e1fe3e4aSElliott Hughes ) 20*e1fe3e4aSElliott Hughes CHAR_NAME_CONTINUATION_ = CHAR_NAME_START_ + CHAR_DIGIT_ 21*e1fe3e4aSElliott Hughes 22*e1fe3e4aSElliott Hughes def __init__(self, text, filename): 23*e1fe3e4aSElliott Hughes self.filename_ = filename 24*e1fe3e4aSElliott Hughes self.line_ = 1 25*e1fe3e4aSElliott Hughes self.pos_ = 0 26*e1fe3e4aSElliott Hughes self.line_start_ = 0 27*e1fe3e4aSElliott Hughes self.text_ = text 28*e1fe3e4aSElliott Hughes self.text_length_ = len(text) 29*e1fe3e4aSElliott Hughes 30*e1fe3e4aSElliott Hughes def __iter__(self): 31*e1fe3e4aSElliott Hughes return self 32*e1fe3e4aSElliott Hughes 33*e1fe3e4aSElliott Hughes def next(self): # Python 2 34*e1fe3e4aSElliott Hughes return self.__next__() 35*e1fe3e4aSElliott Hughes 36*e1fe3e4aSElliott Hughes def __next__(self): # Python 3 37*e1fe3e4aSElliott Hughes while True: 38*e1fe3e4aSElliott Hughes token_type, token, location = self.next_() 39*e1fe3e4aSElliott Hughes if token_type not in {Lexer.NEWLINE}: 40*e1fe3e4aSElliott Hughes return (token_type, token, location) 41*e1fe3e4aSElliott Hughes 42*e1fe3e4aSElliott Hughes def location_(self): 43*e1fe3e4aSElliott Hughes column = self.pos_ - self.line_start_ + 1 44*e1fe3e4aSElliott Hughes return (self.filename_ or "<volt>", self.line_, column) 45*e1fe3e4aSElliott Hughes 46*e1fe3e4aSElliott Hughes def next_(self): 47*e1fe3e4aSElliott Hughes self.scan_over_(Lexer.CHAR_WHITESPACE_) 48*e1fe3e4aSElliott Hughes location = self.location_() 49*e1fe3e4aSElliott Hughes start = self.pos_ 50*e1fe3e4aSElliott Hughes text = self.text_ 51*e1fe3e4aSElliott Hughes limit = len(text) 52*e1fe3e4aSElliott Hughes if start >= limit: 53*e1fe3e4aSElliott Hughes raise StopIteration() 54*e1fe3e4aSElliott Hughes cur_char = text[start] 55*e1fe3e4aSElliott Hughes next_char = text[start + 1] if start + 1 < limit else None 56*e1fe3e4aSElliott Hughes 57*e1fe3e4aSElliott Hughes if cur_char == "\n": 58*e1fe3e4aSElliott Hughes self.pos_ += 1 59*e1fe3e4aSElliott Hughes self.line_ += 1 60*e1fe3e4aSElliott Hughes self.line_start_ = self.pos_ 61*e1fe3e4aSElliott Hughes return (Lexer.NEWLINE, None, location) 62*e1fe3e4aSElliott Hughes if cur_char == "\r": 63*e1fe3e4aSElliott Hughes self.pos_ += 2 if next_char == "\n" else 1 64*e1fe3e4aSElliott Hughes self.line_ += 1 65*e1fe3e4aSElliott Hughes self.line_start_ = self.pos_ 66*e1fe3e4aSElliott Hughes return (Lexer.NEWLINE, None, location) 67*e1fe3e4aSElliott Hughes if cur_char == '"': 68*e1fe3e4aSElliott Hughes self.pos_ += 1 69*e1fe3e4aSElliott Hughes self.scan_until_('"\r\n') 70*e1fe3e4aSElliott Hughes if self.pos_ < self.text_length_ and self.text_[self.pos_] == '"': 71*e1fe3e4aSElliott Hughes self.pos_ += 1 72*e1fe3e4aSElliott Hughes return (Lexer.STRING, text[start + 1 : self.pos_ - 1], location) 73*e1fe3e4aSElliott Hughes else: 74*e1fe3e4aSElliott Hughes raise VoltLibError("Expected '\"' to terminate string", location) 75*e1fe3e4aSElliott Hughes if cur_char in Lexer.CHAR_NAME_START_: 76*e1fe3e4aSElliott Hughes self.pos_ += 1 77*e1fe3e4aSElliott Hughes self.scan_over_(Lexer.CHAR_NAME_CONTINUATION_) 78*e1fe3e4aSElliott Hughes token = text[start : self.pos_] 79*e1fe3e4aSElliott Hughes return (Lexer.NAME, token, location) 80*e1fe3e4aSElliott Hughes if cur_char in Lexer.CHAR_DIGIT_: 81*e1fe3e4aSElliott Hughes self.scan_over_(Lexer.CHAR_DIGIT_) 82*e1fe3e4aSElliott Hughes return (Lexer.NUMBER, int(text[start : self.pos_], 10), location) 83*e1fe3e4aSElliott Hughes if cur_char == "-" and next_char in Lexer.CHAR_DIGIT_: 84*e1fe3e4aSElliott Hughes self.pos_ += 1 85*e1fe3e4aSElliott Hughes self.scan_over_(Lexer.CHAR_DIGIT_) 86*e1fe3e4aSElliott Hughes return (Lexer.NUMBER, int(text[start : self.pos_], 10), location) 87*e1fe3e4aSElliott Hughes raise VoltLibError("Unexpected character: '%s'" % cur_char, location) 88*e1fe3e4aSElliott Hughes 89*e1fe3e4aSElliott Hughes def scan_over_(self, valid): 90*e1fe3e4aSElliott Hughes p = self.pos_ 91*e1fe3e4aSElliott Hughes while p < self.text_length_ and self.text_[p] in valid: 92*e1fe3e4aSElliott Hughes p += 1 93*e1fe3e4aSElliott Hughes self.pos_ = p 94*e1fe3e4aSElliott Hughes 95*e1fe3e4aSElliott Hughes def scan_until_(self, stop_at): 96*e1fe3e4aSElliott Hughes p = self.pos_ 97*e1fe3e4aSElliott Hughes while p < self.text_length_ and self.text_[p] not in stop_at: 98*e1fe3e4aSElliott Hughes p += 1 99*e1fe3e4aSElliott Hughes self.pos_ = p 100