簡體   English   中英

解析算術表達式內的表達式

[英]Parsing expressions inside arithmetic expressions

我想解析算術表達式。

這是我當前的解析器:

data AExpr
    = ExprAsAExpr Expr
    | IntConst Integer
    | Neg AExpr
    | ABinary ABinOp AExpr AExpr
    deriving (Show, Eq)

aExpr :: Parser AExpr
aExpr = makeExprParser aTerm aOperators

aTerm :: Parser AExpr
aTerm
    =   parens aExpr
    <|> IntConst <$> integerParser

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

使用這個我可以正確地解析這個:

1 + 2

生成這樣的AST。

(ABinary Add (IntConst 1) (IntConst 2))

我可以解析的另一件事是通用表達式。 這些可以是變量,方法調用,三元等。

例如

身份標識:

varName

這將產生:

(Identifier (Name "varName"))

方法調用:

methodCall()

這將產生:

(MethodCall (Name "methodCall") (BlockExpr []))

這是解析通用表達式的示例。

expressionParser :: Parser Expr
expressionParser
    =   methodCallParser
    <|> identifierParser

這工作正常,但我也想在此解析算術表達式。

expressionParser :: Parser Expr
expressionParser
    =   newClassInstanceParser
    <|> methodCallParser
    <|> AExprAsExpr <$> aExpr
    <|> identifierParser

這意味着使用expressionParser我現在可以解析所有不同的表達式,包括算術表達式。 如果碰巧是算術表達式,則將其包裝在AExprAsExpr

問題

我想解析包含其他表達式的算術表達式。

例如

x + y

為此,我最初的想法是更改算術解析器,以便它也解析表達式。

data AExpr
    = ExprAsAExpr Expr
    | IntConst Integer
    | Neg AExpr
    | ABinary ABinOp AExpr AExpr
    deriving (Show, Eq)

aExpr :: Parser AExpr
aExpr = makeExprParser aTerm aOperators

aTerm :: Parser AExpr
aTerm
    =   parens aExpr
    <|> IntConst <$> integerParser
    <|> ExprAsAExpr <$> expressionParser

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

這個問題是當aTerm調用表達式解析器,表達式解析器調用aExpr存在遞歸循環。 這導致無限循環。 還有一個問題是,所有identifiers現在都將被包裝在AExprAsExpr

在算術表達式內部解析表達式的正確方法是什么?

編輯我現在才意識到您正在使用makeExpressionParser而我的回答並不真正適用makeExpressionParser 無論如何,這個答案還是有幫助的?

Parsec是遞歸下降解析器的一種,這意味着您無法處理左遞歸。 您需要將其排除在外,如果語法與上下文無關,則可以始終這樣做。 您看到此分解完成的一種方法是為每個優先級生成一個乘積。 這是簡單算術的示例語法:

expr ::= addExpr
addExpr ::= mulExpr '+' addExpr
          | mulExpr '-' addExpr
          | mulExpr
mulExpr ::= term '*' mulExpr
          | term '/' mulExpr
          | term
term ::= '(' expr ')'
       | number

注意模式:每個生產中的第一個符號將調出下一個更具體的符號。 然后,顯式括號使我們可以重新輸入頂級產品。 通常,這是在遞歸下降中表示運算符優先級的方式。

上面的語法只能產生右對齊的表達式。 盡管它將完全正確地接受字符串,但是當解釋為左關聯時,它不會正確解析。 尤其是,

1 - 2 - 3 - 4

將被解析為

1 - (2 - (3 - 4))

根據我們的慣例,這是不正確的。 在一般的遞歸下降解析器中,您必須做一些技巧才能在此處正確關聯。 但是,在Parsec中,我們有many組合器,可以利用many組合器來發揮自己的優勢。 例如,要解析左相關減法,我們可以說

subExpr = foldl1 (-) <$> many1 mulExpr

這里的下一個級別顯然是chainl組合器,它似乎是專門為此目的而設計的(盡管我現在才了解它-猜想我應該更仔細地閱讀文檔)。 使用此示例

addExpr = chainl1 mulExpr oper
    where
    oper = choice [ (+) <$ symbol '+'
                  , (-) <$ symbol '-'
                  ]

我喜歡在Haskell中編寫解析器。 祝好運!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM