この質問へのフォローアップとして、変数と式を持つ式言語を解析しようとしていますcase ... of ...
。構文はインデントベースにする必要があります。
すべての行が最初の行に対してインデントされている限り、式は複数の行にまたがることができます。つまり、これは単一のアプリケーションとして解析する必要があります。
f x y z q
式の各選択肢は、キーワード
case
に対してインデントされた独自の行にある必要があります。case
右側は複数の行にまたがることができます。case E of C -> x D -> f x y
右辺としてと を
case
使用して、2 つの選択肢を持つ単一に解析する必要があります。x
f x y
コードを次のように簡略化しました。
import qualified Text.Megaparsec.Lexer as L
import Text.Megaparsec hiding (space)
import Text.Megaparsec.Char hiding (space)
import Text.Megaparsec.String
import Control.Monad (void)
import Control.Applicative
data Term = Var String
| App [Term]
| Case Term [(String, Term)]
deriving Show
space :: Parser ()
space = L.space (void spaceChar) empty empty
name :: Parser String
name = try $ do
s <- some letterChar
if s `elem` ["case", "of"]
then fail $ unwords ["Unexpected: reserved word", show s]
else return s
term :: Parser () -> Parser Term
term sp = App <$> atom `sepBy1` try sp
where
atom = choice [ caseBlock
, Var <$> L.lexeme sp name
]
caseBlock = L.lineFold sp $ \sp' ->
Case <$>
(L.symbol sp "case" *> L.lexeme sp (term sp) <* L.symbol sp' "of") <*>
alt sp' `sepBy` try sp' <* sp
alt sp' = L.lineFold sp' $ \sp'' ->
(,) <$> L.lexeme sp' name <* L.symbol sp' "->" <*> term sp''
ご覧のとおり、この回答の手法を使用して、キーワードよりもインデントされたエースでalt
エルナティブを分離しようとしています。sp'
case
問題
これは、アプリケーションのみで構成される単一の式で機能するようです。
λ» parseTest (L.lineFold space term) "x y\n z"
App [Var "x",Var "y",Var "z"]
リンクされた回答の手法を使用したそのような式のリストでは機能しません:
λ» parseTest (L.lineFold space $ \sp -> (term sp `sepBy` try sp)) "x\n y\nz"
3:1:
incorrect indentation (got 1, should be greater than 1)
case
行の折りたたみを使用しようとすると、式がゲートから失敗します。
λ» parseTest (L.lineFold space term) "case x of\n C -> y\n D -> z"
1:5:
Unexpected: reserved word "case"
case
最も外側の式では行を折りたたむことなく、1 つの選択肢のみで機能します。
λ» parseTest (term space) "case x of\n C -> y\n z"
App [Case (App [Var "x"]) [("C",App [Var "y",Var "z"])]]
しかし、複数のエルナティブcase
があるとすぐに失敗します:alt
λ» parseTest (term space) "case x of\n C -> y\n D -> z"
3:2:
incorrect indentation (got 2, should be greater than 2)
私は何を間違っていますか?