Я пишу код для разбора команд простого императивного языка, определенного в Theory of Programming Languages (Reynolds, 1998).
У меня есть модуль лексера, который по заданной строке извлекает из нее токены, если это допустимое языковое выражение, а затем я передаю этот список токенов синтаксическому анализатору, который должен построить внутреннее представление команды (определяемое как алгебраический тип данных).
Это мои токены:
--Tokens for the parser
data Token = Kw Keyword
| Num Int
| Op Operator
| Str String
| Sym Symbol
deriving Show
У меня проблемы с бинарными операторами. Я приведу в качестве примера сумму, но это происходит одинаково со всеми, будь то логические или целые числа.
Например, если бы я запустил программу parse "x:=2+3", я должен получить следующий список токенов из лексера
[Str "x", Op Colon , Op Equal, Num 2, OP, Plus, Num 3]
что я и получаю.
Но тогда синтаксический анализатор должен вернуть команду
Назначить "x" (Ibin Plus (Const 2) (Const 3)
, что является правильным представлением команды. Но вместо этого я получаю следующее представление:
Назначить "x" (Const 2)
Я предполагаю, что я облажался в какой-то момент в функции pIntExpr, потому что идентификатор переменной и := присваивания анализируются нормально, и он не анализирует последние элементы. Вот соответствующие парсеры для этого примера, чтобы посмотреть, может ли кто-нибудь сориентировать меня в том, что я делаю неправильно.
-- Integer expressions
data IntExpr = Const Int
| Var Iden --Iden=String
| Neg IntExpr
| IBin OpInt IntExpr IntExpr
deriving Show
type TParser = Parsec [Token] ()
--Internal representation of the commands
data Comm = Skip
| Assign Iden IntExpr
| If Assert Comm Comm
| Seq Comm Comm
| While Assert Comm
| Newvar Iden IntExpr Comm
deriving Show
--Parser for non sequential commands
pComm' :: TParser Comm
pComm' = choice [pif,pskip,pAssign,pwhile,pNewVar]
--Parser for the assignment command
pAssign :: TParser Comm
pAssign = do v <- pvar
_ <- silentOp Colon
_ <- silentOp Equal
e <- pIntExp
return $ Assign v e
-- Integer expressions parser
pIntExp :: TParser IntExpr
pIntExp = choice [ var' --An intexp is either a variable
, num --Or a numeric constant
, pMul --Or <intexp>x<intexp>
, pSum --Or <intexp>+<intexp>
, pRes --Or <intexp>-<intexp>
, pDiv --Division
, pMod --Modulus
, pNeg --Unary "-"
]
-- Parser for <intexp>+<intexp>
pSum :: TParser IntExpr
pSum = do
e <- pIntExp
_ <- silentOp Lexer.Plus
e' <- pIntExp
return $ IBin Lang.Plus e e'
ОБНОВЛЕНИЕ С УЧЕТОМ ОТВЕТА AndrewC
К сожалению, перемещение синтаксического анализатора var' вниз в списке выбора не сработало, он дает тот же результат. Но я принял во внимание ответ AndrewC и попытался «вручную» отследить выполнение (я не знаком с отладчиком ghci, в итоге сделал много отдельных шагов и в конце концов потерялся). Вот как я это рассуждаю:
Я получил этот список токенов от лексера: [Str "x", Op Colon, Op Equal, Num 2, OP Plus, Num 3]
Таким образом, синтаксический анализатор pComm' терпит неудачу с pif и pskip, но успешно с pAssign потребляет Str "x", Op Colon и Op Equal и попытка проанализировать [Num 2, OP Plus, Num 3] с помощью pIntExp (!!)
Затем синтаксический анализатор pIntExp пытается использовать синтаксический анализатор var' и терпит неудачу, но синтаксический анализатор num использует Num 2. токен и поэтому возвращает ошибочный результат Назначить "x" (Const 2).
Помня о совете AndrewC относительно выбора, я также переместил анализатор num вниз в списке. Для простоты я буду рассматривать pIntExp как выбор [pSum, num, var´], который подходит для данного конкретного примера.
Первая часть рассуждения остается прежней. Итак, я начну с (!!), где у нас было
[Num 2, Op Plus, Num 3] для анализа с помощью pIntExp
pIntExp теперь сначала пытается с pSum, который, в свою очередь, снова "вызывает" pIntExp, который снова пытается использовать pSum, и так далее. программа зависает. Я попробовал это, и это действительно зависает и никогда не заканчивается.
Итак, мне интересно, есть ли форма, позволяющая сделать синтаксический анализатор pSum "упреждающим" для токена Op Plus, а затем проанализировать соответствующие выражения?
ОБНОВЛЕНИЕ 2: После того, как я немного погуглил, теперь, когда я определил проблему, я обнаружил, что комбинационные парсеры chainl1 и/или chainl могут быть именно тем, что мне нужно. Я буду играть с этим, и если я сработаю, выложу решение