より簡潔なコード例で最初に投稿されたので、質問を書き直しました。
完全にオプションのセミコロンを持つ言語をほぼ完全にシュガーとして考えてみましょう。
;; foo; bar;;;;
有効ですfoo bar foobar
有効ですif (+1); foo
if (+1) foo
はセマンティクスが異なるため、;
空白と見なすことはできません
パーサーの例を次に示します。
{-# LANGUAGE OverloadedStrings #-}
import Text.Trifecta
import Text.Trifecta.Delta
import Text.PrettyPrint.ANSI.Leijen (putDoc, (<>), linebreak)
import Control.Monad.Trans.State.Strict
import Control.Applicative
type TestParser a = StateT Int Parser a
data AST a = Foo a | Bar a deriving (Show)
pFoo :: TestParser (AST (Delta, Int))
pFoo = curry Foo <$ string "foo" <*> position <* modify (+1) <*> get
pBar :: TestParser (AST (Delta, Int))
pBar = curry Bar <$ string "bar" <*> position <*> get
pStmt :: TestParser (AST (Delta, Int))
pStmt = semi *> pStmt <|> pFoo <|> pBar <?> "statement"
pTest :: TestParser [AST (Delta, Int)]
pTest = some pStmt
main :: IO ()
main
= do let res = parseByteString (evalStateT pTest 0)
(Directed "(test)" 0 0 0 0) ";;foo;bar;\nfoo;; foobarbar;;"
case res of
Success ast
-> print ast
Failure errdoc
-> putDoc (errdoc <> linebreak)
このようなパーサーで私が抱えている問題は、pStmt
. 現時点では、次のエラーが発生します。
(test):2:18: error: unexpected
EOF, expected: statement
foo;; foobarbar;;<EOF>
これは、ステートメント (in semi *> pStmt
) が必要なためですが、積み重ねられたセミコロンが式の最初と最後を砂糖漬けにする可能性があるため、すでに期待する前に本当に期待/解析したいのかどうか確信が持てません。
私が開発したハックの 1 つNop
は、AST でコンストラクターとして使用することでしたが、実際にはそうしたくありません。ハックのように感じられ、ドキュメントによってはセミコロンの数が多いため、メモリ使用量が大幅に増加します。
解決策/提案を探しています。
目的の文法の EBNF 形式を試みます。
expr = "foo" | "bar"
expr with sugar = expr | ";"
program = { [white space], expr with sugar, [white space] }