Haskell Megaparsec - зарезервированное слово, проанализированное как идентификатор - PullRequest
0 голосов
/ 10 ноября 2018

Я пытаюсь разобрать простой язык с выражениями lamdba. Но runParser expr "lamdbda(x) (return x) возвращает Right (Var "lamdba") вместо Right (Lambda ["x"] (Return (Var "x")))

Полагаю, мне нужно куда-то добавить try, но я не могу понять, где. lambdaExpr правильно разбирает lamdbas.

Ast.hs

data Expr = Const Integer
          | BinOp Op Expr Expr
          | Neg Expr
          | If Expr Expr Expr
          | Var String
          | Lambda [String] Stmt
  deriving (Show, Eq)

data Op = Multiply
        | Divide
        | Add
        | Subtract
  deriving (Show, Eq)

data Stmt = Decl String Expr
          | Seq [Stmt]
          | Print Expr
          | Return Expr
  deriving (Show, Eq)

Parser.hs

module Parser where

import Ast

import Control.Monad
import Data.Void
import Control.Monad.Combinators.Expr
import Text.Megaparsec
import Text.Megaparsec.Char
import qualified Text.Megaparsec.Char.Lexer as L

type Parser = Parsec Void String

sc :: Parser ()
sc = L.space space1 lineCmnt blockCmnt
  where lineCmnt = L.skipLineComment "--"
        blockCmnt = L.skipBlockComment "{-" "-}"

lexeme :: Parser a -> Parser a
lexeme = L.lexeme sc

symbol :: String -> Parser String
symbol = L.symbol sc

parens :: Parser a -> Parser a
parens = between (symbol "(") (symbol ")")


integer :: Parser Integer
integer = lexeme L.decimal

rword :: String -> Parser ()
rword w = (lexeme . try) (string w *> notFollowedBy alphaNumChar)

rws :: [String] -- list of reserved words
rws = ["if", "then", "else", "let", "print", "lambda", "return"]

identifier :: Parser String
identifier = (lexeme . try) (p >>= check)
  where
    p       = (:) <$> letterChar <*> many alphaNumChar
    check x = if x `elem` rws
              then fail $ "keyword " ++ show x ++ " cannot be an identifier"
              else return x

ifExpr :: Parser Expr
ifExpr = do rword "if"
            cond <- expr
            rword "then"
            thn <- expr
            rword "else"
            els <- expr
            return $ If cond thn els

lambdaExpr :: Parser Expr
lambdaExpr = do rword "lambda"
                args <- parens $ sepBy identifier (char ',')
                s <- stmt
                return $ Lambda args s

expr :: Parser Expr
expr = makeExprParser term operators

term :: Parser Expr
term = parens expr
  <|> lambdaExpr
  <|> Const <$> integer
  <|> Var <$> identifier
  <|> ifExpr

operators :: [[Operator Parser Expr]]
operators =
  [ [Prefix (Neg <$ symbol "-") ]
  , [ InfixL (BinOp Multiply <$ symbol "*")
    , InfixL (BinOp Divide   <$ symbol "/") ]
  , [ InfixL (BinOp Add      <$ symbol "+")
    , InfixL (BinOp Subtract <$ symbol "-") ]
  ]

declStmt :: Parser Stmt
declStmt = do rword "let"
              name <- identifier
              void $ symbol "="
              e <- expr
              return $ Decl name e

printStmt :: Parser Stmt
printStmt = do rword "print"
               e <- expr
               return $ Print e

returnStmt :: Parser Stmt
returnStmt = do rword "return"
                e <- expr
                return $ Return e

stmt :: Parser Stmt
stmt = f <$> sepBy1 stmt' (symbol ";")
  where
    -- if there's only one stmt return it without using ‘Seq’
    f l = if length l == 1 then head l else Seq l

stmt' :: Parser Stmt
stmt' = declStmt
        <|> printStmt
        <|> returnStmt

runParser :: Parser a -> String -> Either (ParseError (Token String) Void) a
runParser p input = Text.Megaparsec.runParser p "" input

1 Ответ

0 голосов
/ 10 ноября 2018

Я написал "лямбду" с ошибкой, закрывая этот вопрос.

...