Комплексные парсек парсеры - PullRequest
1 голос
/ 04 мая 2011

Я не совсем знаю, как еще спросить.Я думаю, что мне нужно общее руководство здесь.У меня есть что-то вроде этого:

expr = buildExpressionParser table term
    <?> "expression"

term = choice [
    (float >>= return . EDouble)
    , try (natural >>= return . EInteger)
    , try (stringLiteral >>= return . EString)
    , try (reserved "true" >> return (EBool True))
    , try (reserved "false" >> return (EBool False))
    , try assign
    , try ifelse
    , try lambda
    , try array
    , try eseq
    , parens expr
    ]
    <?> "simple expression"

Однако, когда я тестирую этот парсер, у меня чаще всего возникают проблемы ... например, когда я пытаюсь проанализировать

 (a,b) -> "b"

, это принимаетсяпарсером lambda, но парсер expr ненавидит его.И иногда он даже зависает полностью в вечных правилах.

Я прочитал Напишите себе схему , но он только анализирует однородный источник Схемы.

ВозможноЯ обычно думаю в неправильном направлении.

РЕДАКТИРОВАТЬ : Здесь внутренние парсеры:

assign = do
    i <- identifier
    reservedOp "="
    e <- expr
    return $ EAssign i e

ifelse = do
    reserved "if"
    e <- expr
    reserved "then"
    a <- expr
    reserved "else"
    b <- expr
    return $ EIfElse e a b

lambda = do
    ls <- parens $ commaSep identifier
    reservedOp "->"
    e <- expr
    return $ ELambda ls e

array = (squares $ commaSep expr) >>= return . EArray

eseq = do
    a <- expr
    semi <|> (newline >>= (\x -> return [x]))
    b <- expr
    return $ ESequence a b

table = [
        [binary "*" EMult AssocLeft, binary "/" EDiv AssocLeft, binary "%" EMod AssocLeft ],
        [binary "+" EPlus AssocLeft, binary "-" EMinus   AssocLeft ],
        [binary "~" EConcat AssocLeft],
        [prefixF "not" ENot],
        [binaryF "and" EAnd AssocLeft, binaryF "or" EAnd AssocLeft]
    ]

И под "ненавидит это" я имел в виду, что это говорит мне этоожидает целое число или число с плавающей запятой.

Ответы [ 2 ]

6 голосов
/ 04 мая 2011

Что Эдвард в комментариях и я оба пытаемся сделать, так это мысленно запустить ваш синтаксический анализатор, и это немного сложно без продолжения парсера. Я собираюсь сделать некоторые предположения здесь, и, возможно, они помогут вам уточнить ваш вопрос.

Угадай 1) : Вы попытались GHCI> parse expr "(input)" "(a,b) -> \"b\", и он вернул Left …. Было бы полезно узнать, в чем была ошибка.

Угадай 2) : Вы также попытались GHCI> parse lambda "(input)" "(a,b) -> \"b\", и он вернул Right …. основываясь на этом Эдварде, я и сделал вывод, что где-то в вашем синтаксическом анализаторе term или, возможно, в сгенерированном синтаксическом анализаторе expr существует конфликт. То, что какой-то фрагмент синтаксического анализатора преуспел в сопоставлении начала строки и возврате значение, но то, что остается, больше не действует. Было бы полезно, если бы вы попробовали GHCI> parse term "(input)" "(a,b) -> \"b\", поскольку это позволило бы нам узнать, была ли проблема в term или expr.

Guess 3) : Строка "(a,b)" сама по себе является допустимым выражением в грамматике, как вы ее запрограммировали. (Хотя, возможно, не так, как вы намеревались запрограммировать ;-). Попробуйте отправить это через анализатор expr и посмотрите, что произойдет.

Угадай 4) : Ваша грамматика оставлена ​​рекурсивной. Это то, что заставляет его застрять и зациклить навсегда. Parsec - это LL (k) парсер. Если вы привыкли к Yacc и семейству, которые являются парсерами LR (1) или LR (k), правила рекурсии в точности обратные. Если вы не поняли это последнее предложение, это нормально, но дайте нам знать.

Угадай 5) : код в построителе выражений выглядит так, как будто он взят из документации функции. Я думаю, что вы, возможно, также нашли выражение term. Если это так, вы указываете, откуда это произошло. если нет, то не могли бы вы объяснить в нескольких предложениях, как, по вашему мнению, term должно работать.


Общие советы : Большое количество try утверждений в конечном итоге (сейчас) может вызвать у вас горе. Они полезны в некоторых случаях, но и немного непослушны. Если следующий персонаж может определить, какой выбор должен быть успешным, он не нужен. Если вы просто пытаетесь получить что-то запущенное, большое количество возвратов сократит количество промежуточных форм, но также скрывает патологические случаи и делает ошибки более неясными.

4 голосов
/ 04 мая 2011

Похоже, что осталась рекурсия, которая приведет к зависанию анализатора, если choice in term когда-либо достигнет eseq:

expr -> term -> eseq -> expr

term (a,b) не будет анализироваться как lambda или array, поэтому он попадет в цикл eseq.

Я не понимаю, почему (a,b) -> "b" не анализируется как expr, так как термин choice должен попасть в lambda, который, как вы говорите, работает, до достижения eseq. Какая позиция сообщается в ошибке разбора?

...