Я пишу лексер / парсер / интерпретатор для моего собственного языка и до сих пор все работало. Я следовал за примерами в блоге Руслана Спивака ( Github ссылка на каждую статью).
Я хотел расширить мою языковую грамматику до того, что написано в статьях, чтобы включить больше операторов, таких как сравнения (<
, >=
и т. Д.), А также экспоненты (**
или ^
в моем языке) , У меня есть эта грамматика:
expression : exponent ((ADD | SUB) exponent)*
exponent : term ((POWER) term)*
# this one is right-associative (powers **)
term : comparison ((MUL | DIV) comparison)*
comparison : factor ((EQUAl | L_EQUAL | LESS
N_EQUAL | G_EQUAL | GREATER) factor)*
# these are all binary operations
factor : NUM | STR | variable
| ADD factor | SUB factor
| LPAREN expr RPAREN
# different types of 'base' types like integers
# also contains parenthesised expressions which are evalutaed first
Что касается разбора токенов, я использую тот же метод, что и в блоге Руслана. Вот тот, который будет анализировать строку exponent
, которая обрабатывает сложение и вычитание, несмотря на ее имя, поскольку грамматика говорит, что выражения анализируются как
exponent_expr (+ / -) exponent_expr
def exponent(self):
node = self.term()
while self.current_token.type in (ADD, SUB):
token = self.current_token
if token.type == ADD:
self.consume_token(ADD)
elif token.type == SUB:
self.consume_token(SUB)
node = BinaryOperation(left_node=node,
operator=token,
right_node=self.term())
return node
Теперь это очень хорошо разбирает левоассоциативные токены (поскольку поток токенов естественным образом идет слева направо), но я застрял в том, как анализировать экспоненциально правые показатели. Посмотрите на этот ожидаемый вход / выход для справки:
>>> 2 ** 3 ** 2
# should be parsed as...
>>> 2 ** (3 ** 2)
# which is...
>>> 2 ** 9
# which returns...
512
# Mine, at the moment, parses it as...
>>> (2 ** 3) ** 2
# which is...
>>> 8 ** 2
# which returns...
64
Чтобы решить эту проблему, я попытался переключить левый и правый узлы BinaryOperation()
конструктора, чтобы сделать текущий узел правым, а новый - левым, но это просто делает 2**5
синтаксическим анализом как 5**2
, что дает мне 25
вместо ожидаемого 32
.
Какие-нибудь подходы, которые я мог бы попробовать?