Parsec:使用以“$”(无空格)开头的变量解析表达式
Parsec: parsing expressions with variables that start with '$' (no whitespace)
我想使用 Parsec 的 Token 和 Expr 模块解析包含以 $
开头的变量的表达式(如 $a=$b
)。这是我的代码的简化版本:
module Main where
import Control.Monad.Identity
import Control.Applicative
import Text.Parsec
import Text.Parsec.String
import qualified Text.Parsec.Token as Tok
import qualified Text.Parsec.Language as Tok
import qualified Text.Parsec.Expr as Ex
data Expr
= BinaryOp String Expr Expr
| Var String
deriving (Show)
lexer :: Tok.TokenParser ()
lexer = Tok.makeTokenParser style
where
style = Tok.emptyDef
{ Tok.reservedOpNames = ["="]
, Tok.reservedNames = []
, Tok.identStart = letter
, Tok.identLetter = alphaNum
}
reservedOp = Tok.reservedOp lexer
identifier = Tok.identifier lexer
whiteSpace = Tok.whiteSpace lexer
parseExpr :: String -> Either ParseError Expr
parseExpr = parse (whiteSpace *> expr <* eof) ""
expr :: Parser Expr
expr = Ex.buildExpressionParser opTable terms <?> "expression"
where
opTable =
[ [ Ex.Infix (reservedOp "=" >> return (BinaryOp "=")) Ex.AssocLeft ] ]
terms =
try var
var :: Parser Expr
var = Var <$> (char '$' >> identifier)
--
main :: IO ()
main = case parseExpr "$a=$b" of
Left err -> print err
Right expr -> print expr
这适用于运算符周围有空格的表达式(如 $a = $b
),但没有空格($a=$b
)我得到错误:
(line 1, column 5):
unexpected '$'
expecting operator
此外,如果我修改解析器以解析不以 $
开头的变量,则解析器可以使用和不使用空格。所以 $
和它们之间没有空格的运算符的组合似乎有问题。
问题出在默认标记解析器中 opStart
和 opLetter
的定义:
emptyDef :: LanguageDef st
emptyDef = LanguageDef
{ commentStart = ""
...
, opStart = opLetter emptyDef
, opLetter = oneOf ":!#$%&*+./<=>?@\^|-~"
...
}
令牌解析器使用 opStart
和 opLetter
贪婪地匹配运算符名称,因此 $a=$b
解析与 $a =$ b
相同。由于 =$
不是运算符,因此您会收到语法错误。
如果您从 opLetter
中删除 $
,事情应该会起作用,例如:
lexer :: Tok.TokenParser ()
lexer = Tok.makeTokenParser style
where
style = Tok.emptyDef
{ Tok.reservedOpNames = ["="]
, Tok.reservedNames = []
, Tok.identStart = letter
, Tok.identLetter = alphaNum
, Tok.opLetter = oneOf ":!#%" -- add this line
}
我想使用 Parsec 的 Token 和 Expr 模块解析包含以 $
开头的变量的表达式(如 $a=$b
)。这是我的代码的简化版本:
module Main where
import Control.Monad.Identity
import Control.Applicative
import Text.Parsec
import Text.Parsec.String
import qualified Text.Parsec.Token as Tok
import qualified Text.Parsec.Language as Tok
import qualified Text.Parsec.Expr as Ex
data Expr
= BinaryOp String Expr Expr
| Var String
deriving (Show)
lexer :: Tok.TokenParser ()
lexer = Tok.makeTokenParser style
where
style = Tok.emptyDef
{ Tok.reservedOpNames = ["="]
, Tok.reservedNames = []
, Tok.identStart = letter
, Tok.identLetter = alphaNum
}
reservedOp = Tok.reservedOp lexer
identifier = Tok.identifier lexer
whiteSpace = Tok.whiteSpace lexer
parseExpr :: String -> Either ParseError Expr
parseExpr = parse (whiteSpace *> expr <* eof) ""
expr :: Parser Expr
expr = Ex.buildExpressionParser opTable terms <?> "expression"
where
opTable =
[ [ Ex.Infix (reservedOp "=" >> return (BinaryOp "=")) Ex.AssocLeft ] ]
terms =
try var
var :: Parser Expr
var = Var <$> (char '$' >> identifier)
--
main :: IO ()
main = case parseExpr "$a=$b" of
Left err -> print err
Right expr -> print expr
这适用于运算符周围有空格的表达式(如 $a = $b
),但没有空格($a=$b
)我得到错误:
(line 1, column 5):
unexpected '$'
expecting operator
此外,如果我修改解析器以解析不以 $
开头的变量,则解析器可以使用和不使用空格。所以 $
和它们之间没有空格的运算符的组合似乎有问题。
问题出在默认标记解析器中 opStart
和 opLetter
的定义:
emptyDef :: LanguageDef st
emptyDef = LanguageDef
{ commentStart = ""
...
, opStart = opLetter emptyDef
, opLetter = oneOf ":!#$%&*+./<=>?@\^|-~"
...
}
令牌解析器使用 opStart
和 opLetter
贪婪地匹配运算符名称,因此 $a=$b
解析与 $a =$ b
相同。由于 =$
不是运算符,因此您会收到语法错误。
如果您从 opLetter
中删除 $
,事情应该会起作用,例如:
lexer :: Tok.TokenParser ()
lexer = Tok.makeTokenParser style
where
style = Tok.emptyDef
{ Tok.reservedOpNames = ["="]
, Tok.reservedNames = []
, Tok.identStart = letter
, Tok.identLetter = alphaNum
, Tok.opLetter = oneOf ":!#%" -- add this line
}