def parse_expression_block(self): t0 = self._expect_token(OPEN_EXPRESSION_BLOCK) e = self.parse_expression() t2 = self._expect_token(CLOSE_EXPRESSION_BLOCK) return ExpressionStatement(e, span=TextSpan(clone(t0.start_pos), clone(t2.end_pos)))
def parse_all(self): body = list() start_pos = clone(self.peek_token().start_pos) while True: t0 = self.peek_token() if t0.type == END_OF_FILE: end_pos = clone(t0.end_pos) break else: body.append(self.parse()) return Template(body, span=TextSpan(start_pos, end_pos))
def parse(self): t0 = self.peek_token() if t0.type == TEXT: self.get_token() return TextStatement(self._get_text(t0), span=TextSpan(clone(t0.start_pos), clone(t0.end_pos))) elif t0.type == OPEN_CODE_BLOCK: return self.parse_code_block() elif t0.type == OPEN_EXPRESSION_BLOCK: return self.parse_expression_block() elif t0.type == OPEN_STATEMENT_BLOCK: return self.parse_statement() else: self._raise_parse_error( t0, [TEXT, OPEN_EXPRESSION_BLOCK, OPEN_STATEMENT_BLOCK])
def scan_string_lit(self): value = '' start_pos = clone(self._curr_pos) escaping = False c0 = self.get_char() if c0 != '\'': raise ScanError(self._filename, clone(self._curr_pos), c0) while True: ch = self.get_char() if ch == '\'': break elif ch == '\\': escaping = True continue if escaping: value += unescape(ch) else: value += ch return Token(STRING_LITERAL, start_pos, clone(self._curr_pos), value)
def scan_raw_identifier(self): c0 = self.get_char() if not is_id_start(c0): raise ScanError(self._filename, clone(self._curr_pos), c0) name = c0 while True: ch1 = self.peek_char() if is_id_part(ch1): self.get_char() name += ch1 else: break return name
def parse_statement(self): t0 = self._expect_token(OPEN_STATEMENT_BLOCK) t1 = self.get_token() if t1.type == IF_KEYWORD: self._statement_stack.append( [ENDIF_KEYWORD, ELIF_KEYWORD, ELSE_KEYWORD]) cond = self.parse_expression() self._expect_token(CLOSE_STATEMENT_BLOCK) then = list(self.parse_statement_block()) last_token = self.peek_token() cases = [ IfStatementCase(cond, then, span=TextSpan(clone(t0.start_pos), clone(last_token.end_pos))) ] while True: first_token = self._expect_token(OPEN_STATEMENT_BLOCK) t2 = self.get_token() if t2.type == ELIF_KEYWORD: cond = self.parse_expression() self._expect_token(CLOSE_STATEMENT_BLOCK) then = list(self.parse_statement_block()) last_token = self.peek_token() cases.append(IfStatementCase(cond, then), span=TextSpan(clone(first_token.start_pos), clone(last_token.end_pos))) elif t2.type == ELSE_KEYWORD: self._expect_token(CLOSE_STATEMENT_BLOCK) self._statement_stack[-1] = [ENDIF_KEYWORD] body = list(self.parse_statement_block()) self._expect_token(OPEN_STATEMENT_BLOCK) self._expect_token(ENDIF_KEYWORD) last_token = self._expect_token(CLOSE_STATEMENT_BLOCK) cases.append( IfStatementCase(None, body, span=TextSpan( clone(first_token.start_pos), clone(last_token.end_pos)))) break elif t2.type == ENDIF_KEYWORD: last_token = self._expect_token(CLOSE_STATEMENT_BLOCK) break return IfStatement(cases, span=TextSpan(clone(t0.start_pos), clone(last_token.end_pos))) elif t1.type == FOR_KEYWORD: self._statement_stack.append([ENDFOR_KEYWORD]) patt = self.parse_pattern() self._expect_token(IN_KEYWORD) e = self.parse_expression() self._expect_token(CLOSE_STATEMENT_BLOCK) body = list(self.parse_statement_block()) self._expect_token(OPEN_STATEMENT_BLOCK) self._expect_token(ENDFOR_KEYWORD) t7 = self._expect_token(CLOSE_STATEMENT_BLOCK) return ForInStatement(patt, e, body, span=TextSpan(clone(t0.start_pos), clone(t7.end_pos))) elif t1.type == JOIN_KEYWORD: self._statement_stack.append([ENDJOIN_KEYWORD]) patt = self.parse_pattern() self._expect_token(IN_KEYWORD) e = self.parse_expression() self._expect_token(WITH_KEYWORD) sep = self.parse_expression() self._expect_token(CLOSE_STATEMENT_BLOCK) body = list(self.parse_statement_block()) self._expect_token(OPEN_STATEMENT_BLOCK) self._expect_token(ENDJOIN_KEYWORD) t6 = self._expect_token(CLOSE_STATEMENT_BLOCK) return JoinStatement(patt, e, sep, body, span=TextSpan(clone(t0.start_pos), clone(t6.end_pos))) elif t1.type == SETINDENT_KEYWORD: self._statement_stack.append([ENDSETINDENT_KEYWORD]) e = self.parse_expression() self._expect_token(CLOSE_STATEMENT_BLOCK) body = list(self.parse_statement_block()) self._expect_token(OPEN_STATEMENT_BLOCK) self._expect_token(ENDSETINDENT_KEYWORD) t5 = self._expect_token(CLOSE_STATEMENT_BLOCK) return SetIndentStatement(e, body, span=TextSpan(clone(t0.start_pos), clone(t5.end_pos))) elif t1.type == NOINDENT_KEYWORD: self._statement_stack.append([ENDNOINDENT_KEYWORD]) self._expect_token(CLOSE_STATEMENT_BLOCK) body = list(self.parse_statement_block()) self._expect_token(OPEN_STATEMENT_BLOCK) self._expect_token(ENDNOINDENT_KEYWORD) t5 = self._expect_token(CLOSE_STATEMENT_BLOCK) return SetIndentStatement(ConstExpression(0), body, span=TextSpan(clone(t0.start_pos), clone(t5.end_pos))) else: expected = [ FOR_KEYWORD, JOIN_KEYWORD, IF_KEYWORD, NOINDENT_KEYWORD, SETINDENT_KEYWORD ] if len(self._statement_stack) > 0: expected.extend(self._statement_stack[-1]) self._raise_parse_error(t1, expected)
def scan(self): while True: if self._mode == TEXT_MODE: yield self.scan_raw_text() elif self._mode == CODE_BLOCK_MODE: in_string_literal = False start_pos = clone(self._curr_pos) text = '' while True: c0 = self.peek_char(1) c1 = self.peek_char(2) if c0 == '!' and c1 == '}': break if c0 == EOF: raise ScanError(self._filename, start_pos, c0) text += self.get_char() self._mode = TEXT_MODE end_pos = clone(self._curr_pos) yield Token(CODE_BLOCK_CONTENT, start_pos, end_pos, text) start_pos = clone(self._curr_pos) self.get_char() self.get_char() end_pos = clone(self._curr_pos) yield Token(CLOSE_CODE_BLOCK, start_pos, end_pos) elif self._mode == STATEMENT_MODE: self.skip_ws() start_pos = clone(self._curr_pos) c0 = self.peek_char() if c0 == EOF: yield Token(END_OF_FILE, clone(self._curr_pos), clone(self._curr_pos)) elif c0 == ':': self.get_char() yield Token(COLON, start_pos, clone(self._curr_pos)) elif c0 == '.': self.get_char() yield Token(DOT, start_pos, clone(self._curr_pos)) elif c0 == '!': self.get_char() c1 = self.get_char() if c1 == '=': yield Token(NEQ_OPERATOR, start_pos, clone(self._curr_pos), '!=') elif c1 == '}': yield Token(CLOSE_CODE_BLOCK, start_pos, clone(self._curr_pos)) else: raise ScanError(self._filename, clone(self._curr_pos), c0) elif c0 == '%': self.get_char() c1 = self.get_char() if c1 == '}': self._mode = TEXT_MODE yield Token(CLOSE_STATEMENT_BLOCK, start_pos, clone(self._curr_pos)) else: yield Token(MOD_OPERATOR, start_pos, clone(self._curr_pos), '%') elif c0 == '}': self.get_char() c1 = self.get_char() if c1 == '}': self._mode = TEXT_MODE yield Token(CLOSE_EXPRESSION_BLOCK, start_pos, clone(self._curr_pos)) else: raise ScanError(self._filename, clone(self._curr_pos), c0) elif c0 == ',': self.get_char() yield Token(COMMA, start_pos, clone(self._curr_pos)) elif c0 == '(': self.get_char() yield Token(OPEN_PAREN, start_pos, clone(self._curr_pos)) elif c0 == ')': self.get_char() yield Token(CLOSE_PAREN, start_pos, clone(self._curr_pos)) elif c0 == '[': self.get_char() yield Token(OPEN_BRACKET, start_pos, clone(self._curr_pos)) elif c0 == ']': self.get_char() yield Token(CLOSE_BRACKET, start_pos, clone(self._curr_pos)) elif c0 == '\'': yield self.scan_string_lit() elif is_digit(c0): self.get_char() digits = c0 while is_digit(self.peek_char()): digits += self.get_char() yield Token(INTEGER, start_pos, clone(self._curr_pos), int(digits)) elif is_operator_start(c0): op = c0 self.get_char() while is_operator_part(self.peek_char()): op += self.get_char() if not op in OPERATORS: raise ScanError(self._filename, start_pos, op) yield Token(OPERATORS[op], start_pos, clone(self._curr_pos), op) elif is_id_start(c0): name = self.scan_raw_identifier() if name in NAMED_OPERATORS: yield Token(NAMED_OPERATORS[name], start_pos, clone(self._curr_pos), name) elif name in KEYWORDS: yield Token(KEYWORDS[name], start_pos, clone(self._curr_pos), name) else: yield Token(IDENTIFIER, start_pos, clone(self._curr_pos), name) else: raise ScanError(self._filename, clone(self._curr_pos), c0)
def scan_raw_text(self): text = '' start_pos = clone(self._curr_pos) while True: ch0 = self.peek_char(1) if ch0 == EOF: if len(text) > 0: return Token(TEXT, start_pos, clone(self._curr_pos), text) return Token(END_OF_FILE, clone(self._curr_pos), clone(self._curr_pos)) elif ch0 == '{': if len(text) > 0: return Token(TEXT, start_pos, clone(self._curr_pos), text) ch1 = self.peek_char(2) if ch1 == '{': self._mode = STATEMENT_MODE start_pos = clone(self._curr_pos) self.get_char() self.get_char() return Token(OPEN_EXPRESSION_BLOCK, start_pos, clone(self._curr_pos)) elif ch1 == '%': self._mode = STATEMENT_MODE start_pos = clone(self._curr_pos) self.get_char() self.get_char() return Token(OPEN_STATEMENT_BLOCK, start_pos, clone(self._curr_pos)) elif ch1 == '!': self._mode = CODE_BLOCK_MODE start_pos = clone(self._curr_pos) self.get_char() self.get_char() return Token(OPEN_CODE_BLOCK, start_pos, clone(self._curr_pos)) elif ch1 == '#': start_pos = clone(self._curr_pos) self.get_char() self.get_char() while True: ch2 = self.get_char() if ch2 == '#': ch3 = self.get_char() if ch3 == '}': ch4 = self.peek_char() if ch4 == '\n': self.get_char() start_pos = clone(self._curr_pos) break else: self.get_char() text += ch0 else: self.get_char() text += ch0