def test_single_number(self): token = Token() token.type = "number" token.value = "123.456" tokens = [token] self.parser.set_tokens(tokens) expression_tree = self.parser.get_expression_tree() assert expression_tree is not None assert expression_tree["node_type"] == "number" assert expression_tree["value"] == 123.456
def make_rpn(self, expression): """ Parse an excel formula expression into reverse polish notation Core algorithm taken from wikipedia with varargs extensions from http://www.kallisti.net.nz/blog/2008/02/extension-to-the-shunting-yard- algorithm-to-allow-variable-numbers-of-arguments-to-functions/ """ """ Parse an excel formula expression into reverse polish notation Core algorithm taken from wikipedia with varargs extensions from http://www.kallisti.net.nz/blog/2008/02/extension-to-the-shunting-yard- algorithm-to-allow-variable-numbers-of-arguments-to-functions/ """ lexer = Tokenizer(expression) # amend token stream to ease code production tokens = [] for token, next_token in zip(lexer.items, lexer.items[1:] + [None]): if token.matches(Token.FUNC, Token.OPEN): tokens.append(token) token = Token('(', Token.PAREN, Token.OPEN) elif token.matches(Token.FUNC, Token.CLOSE): token = Token(')', Token.PAREN, Token.CLOSE) elif token.matches(Token.ARRAY, Token.OPEN): tokens.append(token) tokens.append(Token('(', Token.PAREN, Token.OPEN)) tokens.append(Token('', Token.ARRAYROW, Token.OPEN)) token = Token('(', Token.PAREN, Token.OPEN) elif token.matches(Token.ARRAY, Token.CLOSE): tokens.append(token) token = Token(')', Token.PAREN, Token.CLOSE) elif token.matches(Token.SEP, Token.ROW): tokens.append(Token(')', Token.PAREN, Token.CLOSE)) tokens.append(Token(',', Token.SEP, Token.ARG)) tokens.append(Token('', Token.ARRAYROW, Token.OPEN)) token = Token('(', Token.PAREN, Token.OPEN) elif token.matches(Token.PAREN, Token.OPEN): token.value = '(' elif token.matches(Token.PAREN, Token.CLOSE): token.value = ')' tokens.append(token) output = [] stack = [] were_values = [] arg_count = [] # shunting yard start for token in tokens: if token.type == token.OPERAND: output.append(self.make_node(token)) if were_values: were_values[-1] = True elif token.type != token.PAREN and token.subtype == token.OPEN: if token.type in (token.ARRAY, Token.ARRAYROW): token = Token(token.type, token.type, token.subtype) stack.append(token) arg_count.append(0) if were_values: were_values[-1] = True were_values.append(False) elif token.type == token.SEP: while stack and (stack[-1].subtype != token.OPEN): output.append(self.make_node(stack.pop())) if not len(were_values): raise FormulaParserError( "Mismatched or misplaced parentheses") were_values.pop() arg_count[-1] += 1 were_values.append(False) elif token.is_operator: while stack and stack[-1].is_operator: if token.precedence < stack[-1].precedence: output.append(self.make_node(stack.pop())) else: break stack.append(token) elif token.subtype == token.OPEN: assert token.type in (token.FUNC, token.PAREN, token.ARRAY) stack.append(token) elif token.subtype == token.CLOSE: while stack and stack[-1].subtype != Token.OPEN: output.append(self.make_node(stack.pop())) if not stack: raise FormulaParserError( "Mismatched or misplaced parentheses") stack.pop() if stack and stack[-1].is_funcopen: f = self.make_node((stack.pop())) f.num_args = arg_count.pop() + int(were_values.pop()) output.append(f) else: assert token.type == token.WSPACE, \ 'Unexpected token: {}'.format(token) while stack: if stack[-1].subtype in (Token.OPEN, Token.CLOSE): raise FormulaParserError("Mismatched or misplaced parentheses") output.append(self.make_node(stack.pop())) return output