def select_expr(self) -> nodes.Node: """ select_expr -> expr | ID (COMMA ID)* FROM ID (WHERE assignee_sub_stmt (AND assignee_sub_stmt)* (order_sub_stmt)? (limit_sub_stmt)?)? Example: select 1+1 Example: select foo from foobar Example: select foo, bar from foobar where foo='foo', bar=100500 """ if self.token.type == TokenType.ID: result = [] column = nodes.Column(self.token) self.move_forward(TokenType.ID) result.append(column) while self.token.type == TokenType.COMMA: self.move_forward(TokenType.COMMA) column = nodes.Column(self.token) self.move_forward(TokenType.ID) result.append(column) self.move_forward(TokenType.FROM) table = nodes.Table(self.token) self.move_forward(TokenType.ID) where = [] if self.token.type == TokenType.WHERE: self.move_forward(TokenType.WHERE) where.append(self.assignee_sub_stmt()) while self.token.type == TokenType.AND: self.move_forward(TokenType.AND) where.append(self.assignee_sub_stmt()) if self.token.type == TokenType.ORDER: order = self.order_sub_stmt() else: order = nodes.Empty() if self.token.type == TokenType.LIMIT: limit = self.limit_sub_stmt() else: limit = nodes.Empty() node = nodes.SelectStatement( table=table, result=result, where=where, order=order, limit=limit ) else: node = nodes.SelectStatement( table=nodes.Empty(), result=self.expr(), where=None, order=nodes.Empty(), limit=nodes.Empty() ) return node
def test_or(self): self._compare('a|b', N.Or(N.Char('a'), N.Char('b'))) self._compare('ab|c', N.Or(N.Concat(N.Char('a'), N.Char('b')), N.Char('c'))) self._compare('a|bc', N.Or(N.Char('a'), N.Concat(N.Char('b'), N.Char('c')))) self._compare('a|b|c', N.Or(N.Or(N.Char('a'), N.Char('b')), N.Char('c'))) self._compare('|', N.Or(N.Empty(), N.Empty()))
def from_string(regex): # regex: str -> node: RegexNode if not regex: return nodes.Empty() state = ParseState() for i, c in enumerate(regex): if state.level == 0: if any(type_.char == c for type_ in nodes.UNARY_NODE_TYPES): if state.state != ParseState.BINARY: state.state = ParseState.UNARY state.opr_type = next(t for t in nodes.UNARY_NODE_TYPES if t.char == c) state.opr_index = i elif c == nodes.Or.char: state.opr_type = nodes.Or state.opr_index = i state.state = ParseState.BINARY elif state.state != ParseState.BINARY or state.opr_type != nodes.Or: if i > 0: state.state = ParseState.BINARY state.opr_type = nodes.Concat state.opr_index = i if c == '(': state.level += 1 elif c == ')': state.level -= 1 if state.state == ParseState.NO_OPR: if regex[0] == '(' and regex[-1] == ')': return from_string(regex[1:-1]) assert len(regex) == 1, 'Unexpected regex: {}'.format(regex) return nodes.Char(regex) elif state.state == ParseState.UNARY: assert state.opr_index == len( regex) - 1, 'Invalid sub-regex: {}'.format(regex) return state.opr_type(from_string(regex[:state.opr_index])) elif state.state == ParseState.BINARY: return state.opr_type( from_string(regex[:state.opr_index]), from_string(regex[state.opr_index + len(state.opr_type.char):])) else: raise Exception('Unknown state: {}'.format(state.state))
def test_parentheses(self): self._compare('()', N.Empty()) self._compare('(ab)+', N.OneOrMore(N.Concat(N.Char('a'), N.Char('b')))) self._compare('a(b)+', N.Concat(N.Char('a'), N.OneOrMore(N.Char('b')))) parse.from_string('a(bcd*|efgh?(jk)+)*')
def test_empty(self): self._compare('', N.Empty()) self._compare('a|', N.Or(N.Char('a'), N.Empty())) self._compare('|a', N.Or(N.Empty(), N.Char('a')))