作为实际语言的解析器的一个简化子问题,我试图实现一个解析器,用于虚拟语言的表达式,这种语言看起来类似于标准命令式语言(如Python、JavaScript等)。它的语法具有以下构造:
[a-zA-Z]+)+、*和括号的算术表达式.进行结构访问(如foo.bar.buz)(1, foo, bar.buz)) (为了消除歧义,元组为(x,))foo(1, bar, buz()))foo()()是合法的,因为foo()可能返回一个函数)因此,在这种语言中,一个相当复杂的程序是
(1+2*3, f(4,5,6)(bar) + qux.quux()().quuux)联想性应该是
( (1+(2*3)), ( ((f(4,5,6))(bar)) + ((((qux.quux)())()).quuux) ) )我目前正在使用非常好的uu-parsinglib,一个应用程序解析器组合器库。
第一个问题显然是直观的表达式语法(expr -> identifier | number | expr * expr | expr + expr | (expr)是左递归的)。但是我可以使用pChainl组合器解决这个问题(参见下面示例中的parseExpr )。
剩下的问题(因此这个问题)是函数应用程序,函数从其他函数(f()())返回。同样,语法是递归的expr -> fun-call | ...; fun-call -> expr ( parameter-list )。有什么好办法用uu-parsinglib来解决这个问题吗?(这个问题应该直接适用于parsec、attoparsec和其他解析器组合器,我猜也是这样)。
见下面我当前版本的程序。它工作得很好,但是函数应用程序只处理用于删除左递归的标识符:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
module TestExprGrammar
(
) where
import Data.Foldable (asum)
import Data.List (intercalate)
import Text.ParserCombinators.UU
import Text.ParserCombinators.UU.Utils
import Text.ParserCombinators.UU.BasicInstances
data Node =
NumberLiteral Integer
| Identifier String
| Tuple [Node]
| MemberAccess Node Node
| FunctionCall Node [Node]
| BinaryOperation String Node Node
parseFunctionCall :: Parser Node
parseFunctionCall =
FunctionCall <$>
parseIdentifier {- `parseExpr' would be correct but left-recursive -}
<*> parseParenthesisedNodeList 0
operators :: [[(Char, Node -> Node -> Node)]]
operators = [ [('+', BinaryOperation "+")]
, [('*' , BinaryOperation "*")]
, [('.', MemberAccess)]
]
samePrio :: [(Char, Node -> Node -> Node)] -> Parser (Node -> Node -> Node)
samePrio ops = asum [op <$ pSym c <* pSpaces | (c, op) <- ops]
parseExpr :: Parser Node
parseExpr =
foldr pChainl
(parseIdentifier
<|> parseNumber
<|> parseTuple
<|> parseFunctionCall
<|> pParens parseExpr
)
(map samePrio operators)
parseNodeList :: Int -> Parser [Node]
parseNodeList n =
case n of
_ | n < 0 -> parseNodeList 0
0 -> pListSep (pSymbol ",") parseExpr
n -> (:) <$>
parseExpr
<* pSymbol ","
<*> parseNodeList (n-1)
parseParenthesisedNodeList :: Int -> Parser [Node]
parseParenthesisedNodeList n = pParens (parseNodeList n)
parseIdentifier :: Parser Node
parseIdentifier = Identifier <$> pSome pLetter <* pSpaces
parseNumber :: Parser Node
parseNumber = NumberLiteral <$> pNatural
parseTuple :: Parser Node
parseTuple =
Tuple <$> parseParenthesisedNodeList 1
<|> Tuple [] <$ pSymbol "()"
instance Show Node where
show n =
let showNodeList ns = intercalate ", " (map show ns)
showParenthesisedNodeList ns = "(" ++ showNodeList ns ++ ")"
in case n of
Identifier i -> i
Tuple ns -> showParenthesisedNodeList ns
NumberLiteral n -> show n
FunctionCall f args -> show f ++ showParenthesisedNodeList args
MemberAccess f g -> show f ++ "." ++ show g
BinaryOperation op l r -> "(" ++ show l ++ op ++ show r ++ ")"发布于 2014-10-05 19:47:54
简单地回顾一下类列表组合子 for uu-parsinglib (我更熟悉parsec),我认为您可以通过折叠pSome组合器的结果来解决这个问题:
parseFunctionCall :: Parser Node
parseFunctionCall =
foldl' FunctionCall <$>
parseIdentifier {- `parseExpr' would be correct but left-recursive -}
<*> pSome (parseParenthesisedNodeList 0)这也相当于Alternative some组合器,它确实应该适用于您提到的其他解析库。
发布于 2014-10-05 00:05:44
我不知道这个库,但是可以教你如何删除左递归。标准的右递归表达式语法是
E -> T E'
E' -> + TE' | eps
T -> F T'
T' -> * FT' | eps
F -> NUMBER | ID | ( E ) 要添加函数应用程序,必须确定其优先级级别。在我见过的大多数语言中,它是最高的。因此,您可以为函数应用程序添加另一层产品。
E -> T E'
E' -> + TE' | eps
T -> AT'
T' -> * A T' | eps
A -> F A'
A' -> ( E ) A' | eps
F -> NUMBER | ID | ( E ) 是的,这是一种看上去毛茸茸的语法,比左边的递归语法要大。这就是自上而下的预测分析的代价。如果您想要更简单的语法,可以使用自下而上的解析器生成器la yacc。
https://stackoverflow.com/questions/26198078
复制相似问题