Вот определение Rule
:
class Rule[-I <: HList, +O <: HList]
Документация вы связаны дает больше объяснений, но, по существу I
является входным Правилу и O
является выход правила. Обозначение двоеточия может быть немного запутанным. parboiled2
использует стек для управления состоянием. Просто помните, что типы в списках двоеточия (HList
) создаются/сдвигаются слева направо и потребляются/появляются справа налево. В HList
A:B:C
, C
- это вершина стека, и ее необходимо использовать в первую очередь.
~
запускает одно правило, а затем следующее. Таким образом, в первом примере a
типа Rule[, A]
ничего не потребляет и «производит» A
, а b
типа Rule[,B]
ничего не потребляет и «производит» B
. Тогда имеет смысл, что если вы запустили a
, а затем b
, вы должны были бы создать A
, а затем B
. Итоговый тип: Rule[, A:B]
.
Когда вы добавляете входы, ситуация становится намного сложнее. Вы должны убедиться, что типы, созданные a
(или независимо от первого правила), являются типами, которые будут потреблять b
. ~
как раз как функция состав. Ff мы хотим составить g
и f
, чтобы получить g . f
, мы должны быть уверены, что вывод f
- это тот же самый тип, что и вход g
.
Давайте рассмотрим третий пример в таблице.
a
имеет тип Rule[A, B:C]
b
имеет тип Rule[D:B:C, E:F]
Когда мы запускаем a
A
потребляется из стека, B
добавляется в стек, а C
добавляется к стек. Затем запускается b
, сначала он потребляет C
, затем B
, затем он потребляет D
со стека. Чтобы быть в нужном месте в нужное время, D
должен быть под A
, который потреблял a
. b
затем произведет E
, а затем F
.
В общей сложности мы потребляли D
и A
. B
и C
не учитываются, потому что они были произведены и использованы внутри правила. После потребления D
и A
, мы оставляем E
и F
на стеке. Таким образом, тип a ~ b
- Rule[D:A, E:F]
.
Четвертый пример в README дает пример, где a
производит неправильные типы для b
, чтобы потреблять. В этом случае a ~ b
является незаконным.