Итак, вы правы, что сложная часть - это приоритет.Я думаю, что есть приблизительно два способа справиться с этим для языка стиля ML
- Приоритет определяется фиксированными правилами
- Приоритет определяется пользователем
Ocaml делает вариант 1. Приоритет и ассоциативность оператора определяются его первым символом.
Haskell делает вариант 2. Приоритет и ассоциативность определяются с помощью операторов (и объявление может прийти после того, как используется оператор).
Довольно просто увидеть, как анализировать (1): вы просто анализируете его как обычно, за исключением того, что вместо разрешения оператора +
на этом уровне приоритета вы определяете любой оператор, начинающийся с +
.Это оставляет вопрос о том, как вам следует разбирать выражение типа a +* b +- c
.Я не знаю, как ocaml связал бы это, но мое предположение было бы основано либо на втором символе, либо на том же уровне приоритета (например, как разбор +
и -
на том же уровне приоритета и ассоциирование слева такa + b - c + d
анализируется как ((a + b) - c) + d
).
Я думаю, у вас также есть правильная идея для разбора (2), но это сложно.Я думаю, что ваш тип немного неправильный, и что вы на самом деле хотите, это что-то вроде:
type operator = Op of string
type expression =
| Var of string
| Operator of operator
| App of expression * expression
| Tuple of expression list
| Infix of expression * (operator * expression) list
В частности, вы не можете иметь Infix of expression * operator * expression
, потому что тогда как вы анализируете a OP b OP c
?В основном у вас есть два варианта:
Infix (Infix (Var a, Op OP, Var b), Op OP, Var c)
Infix (Var a, Op OP, Infix (Var b, Op OP, Var c))
Вариант 1 эквивалентен (a OP b) OP c
и работает для -
и |>
, но не в стиле Haskell $
и, конечно, не для a + b * c
.Аналогично, вариант 2 работает для +
, но не для -
или /
.Кроме того, недостаточно просто отменить это искажение перед сортировкой приоритета, потому что выражение (a OP b) OP c
должно быть проанализировано как вариант 1, даже если оно не исправлено.
Обратите внимание, (если мы хотим язык стиля ML) нужен способ выразить функцию оператора в виде значения, например, (+)
, но это можно, например, включить в Var
.
Как только вы получите этот уровень синтаксического анализа, вы можете ждать, пока не получитеопределили любые правила приоритета операторов для операторов, а затем вы можете их проанализировать.
Некоторые другие вещи, которые могут быть полезныс конкретными символами, например, !
.Haskell допускает использование постфиксных операторов в качестве расширения, но только с использованием срезов (т. Е. Расширение ослабляет определение (x*)
с (\y -> (*) x y)
до ((*) x)
, поэтому (*)
может принимать один аргумент. Если вы хотите иметь возможность предварительно иПостфиксные операторы определяются пользователем, вы можете изменить тип, чтобы отбрасывать приложение и правило, что у вас может быть ровно один оператор между выражениями, а затем сделать шаг, чтобы проанализировать список expression | operator
в нечто вменяемое, например, делает a * + b
анализировать как a (*(+b))
, (a) * (+b)
, (a*) (+b)
, (a*) + (b)
или ((a*)+) b
? Может быть, эта трудность тоже будет плохой для читателей-людей.
Как справиться с приоритетом? В Haskell вы выбираетецелое число от 0 до 9. В perl6 вы вместо этого просто говорите, что eg * является более жестким, чем +, и если два оператора с неопределенным отношением появляются вместе, язык требует, чтобы вы указали в скобках.
Это может бытьСтоит отметить путь perl6 в качестве другого варианта. В этом случае операторы должны иметь свой приоритет и ассоциативность / фиксированность, определенные доОн используется, и синтаксический анализатор динамически добавляет их между объявлением и использованием (можно также сделать это со всей грамматикой языка, поэтому анализ будущих выражений зависит от оценки более ранних выражений, что немного менее безумно).