Haskell Megaparsec - 解析为标识符的保留字
Haskell Megaparsec - Reserved word parsed as identifier
我正在尝试使用 lamdba 表达式解析一种简单的语言。但是 runParser expr "lamdbda(x) (return x)
returns 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
我拼错了 "lambda",结束这个问题。
我正在尝试使用 lamdba 表达式解析一种简单的语言。但是 runParser expr "lamdbda(x) (return x)
returns 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
我拼错了 "lambda",结束这个问题。