Подтвердить что ты не робот

Полные примеры парсера с парсеком?

Я пытаюсь сделать синтаксический анализатор для простого функционального языка, немного похожего на Caml, но я, похоже, придерживаюсь простейших вещей.

Итак, я хотел бы знать, есть ли более полные примеры парсеров parsec, что выходит за рамки "это то, как вы разобрали 2 + 3". Особенно вызовы функций в терминах и т.д.

И я прочитал "Напишите вам схему", но синтаксис схемы довольно прост и не очень помогает в обучении.

Большинство проблем, с которыми я столкнулся, - это правильно использовать try, <|> и choice, потому что я действительно не понимаю, почему parsec никогда не анализирует a(6) как вызов функции с использованием этого синтаксического анализатора:

expr = choice [number, call, ident]

number = liftM Number float <?> "Number"

ident = liftM Identifier identifier <?> "Identifier"

call = do
    name <- identifier
    args <- parens $ commaSep expr
    return $ FuncCall name args
    <?> "Function call"

EDIT Добавлен код для завершения, хотя на самом деле это не то, что я спросил:

AST.hs

module AST where

data AST
    = Number Double
    | Identifier String
    | Operation BinOp AST AST
    | FuncCall String [AST]
    deriving (Show, Eq)

data BinOp = Plus | Minus | Mul | Div
    deriving (Show, Eq, Enum)

Lexer.hs

module Lexer (
            identifier, reserved, operator, reservedOp, charLiteral, stringLiteral,
            natural, integer, float, naturalOrFloat, decimal, hexadecimal, octal,
            symbol, lexeme, whiteSpace, parens, braces, angles, brackets, semi,
            comma, colon, dot, semiSep, semiSep1, commaSep, commaSep1
    ) where

import Text.Parsec
import qualified Text.Parsec.Token as P
import Text.Parsec.Language (haskellStyle)

lexer = P.makeTokenParser haskellStyle

identifier = P.identifier lexer
reserved = P.reserved lexer
operator = P.operator lexer
reservedOp = P.reservedOp lexer
charLiteral = P.charLiteral lexer
stringLiteral = P.stringLiteral lexer
natural = P.natural lexer
integer = P.integer lexer
float = P.float lexer
naturalOrFloat = P.naturalOrFloat lexer
decimal = P.decimal lexer
hexadecimal = P.hexadecimal lexer
octal = P.octal lexer
symbol = P.symbol lexer
lexeme = P.lexeme lexer
whiteSpace = P.whiteSpace lexer
parens = P.parens lexer
braces = P.braces lexer
angles = P.angles lexer
brackets = P.brackets lexer
semi = P.semi lexer
comma = P.comma lexer
colon = P.colon lexer
dot = P.dot lexer
semiSep = P.semiSep lexer
semiSep1 = P.semiSep1 lexer
commaSep = P.commaSep lexer
commaSep1 = P.commaSep1 lexer

Parser.hs

module Parser where

import Control.Monad (liftM)
import Text.Parsec
import Text.Parsec.String (Parser)
import Lexer
import AST

expr = number <|> callOrIdent

number = liftM Number float <?> "Number"

callOrIdent = do
    name <- identifier
    liftM (FuncCall name) (parens $ commaSep expr) <|> return (Identifier name)
4b9b3361

Ответ 1

Хм,

*Expr> parse expr "" "a(6)"
Right (FuncCall "a" [Number 6.0])

эта часть работает для меня после заполнения отсутствующих частей.

Изменить: Я заполнил недостающие фрагменты, написав свой собственный синтаксический анализатор float, который может анализировать целые литералы. Парсер float из Text.Parsec.Token, с другой стороны, анализирует только литералы с долей или экспонентой, поэтому он не смог разобрать "6".

Однако

*Expr> parse expr "" "variable"
Left (line 1, column 9):
unexpected end of input
expecting "("

когда вызов завершается с ошибкой после анализа идентификатора, эта часть входа потребляется, поэтому идентификатор не проверяется, и общий анализ не выполняется. Вы можете a) сделать его try call в списке выбора expr, чтобы вызов терпел неудачу, не потребляя вход, или b) записывать парсер callOrIdent для использования в expr, например.

callOrIdent = do
    name <- identifier
    liftM (FuncCall name) (parens $ commaSep expr) <|> return (Identifier name)

который избегает try и, следовательно, может работать лучше.

Ответ 2

Я написал серию примеров о том, как разбирать римские цифры с парсеком. Это довольно просто, но вам или другим новичкам может показаться полезным:

https://github.com/russell91/roman

Ответ 3

Книга Напишите себе схему за 48 часов - это отличный, подробный обзор и руководство по функциям Parsec. Он проводит вас через все с подробными примерами, и к концу вы реализовали довольно значительную часть схемы в интерпретаторе parsec.