私は Alex で字句解析器を書き、それを Happy で書かれたパーサーに接続しようとしています。膨大な量のコードを貼り付けずに、問題を要約するために最善を尽くします。
lexer の単体テストから、文字列"\x7"
が lexed されることを知っています。
[TokenNonPrint '\x7', TokenEOF]
私のトークンタイプ(レクサーによって吐き出される)はToken
. ここで説明したようにlexWrap
andを定義しました。これにより、次のヘッダーとトークンの宣言が得られます。alexEOF
%name parseTokens
%tokentype { Token }
%lexer { lexWrap } { alexEOF }
%monad { Alex }
%error { parseError }
%token
NONPRINT {TokenNonPrint $$}
PLAIN { TokenPlain $$ }
パーサーとレクサーの組み合わせを次のように呼び出します。
parseExpr :: String -> Either String [Expr]
parseExpr s = runAlex s parseTokens
そして、ここに私の最初のいくつかの作品があります:
exprs :: { [Expr] }
exprs
: {- empty -} { trace "exprs 30" [] }
| exprs expr { trace "exprs 31" $ $2 : $1 }
nonprint :: { Cmd }
: NONPRINT { NonPrint $ parseNonPrint $1}
expr :: { Expr }
expr
: nonprint {trace "expr 44" $ Cmd $ $1}
| PLAIN { trace "expr 37" $ Plain $1 }
Expr
andのデータ型宣言NonPrint
は長く、ここではコンストラクターCmd
とNonPrint
マターだけなので省略します。この関数parseNonPrint
は、Parse.y の下部で次のように定義されています。
parseNonPrint :: Char -> NonPrint
parseNonPrint '\x7' = Bell
また、私のエラー処理関数は次のようになります。
parseError :: Token -> Alex a
parseError tokens = error ("Error processing token: " ++ show tokens)
このように書くと、次の hspec テストに合格することが期待されます。
parseExpr "\x7" `shouldBe` Right [Cmd (NonPrint Bell)]
しかし、代わりに、"exprs 30"
print once (5 つの異なる単体テストを実行しているにもかかわらず) とparseExpr
returnのすべてのテストが表示されRight []
ます。なぜそうなるのかはわかりませんが、exprs
それを防ぐためにプロダクションを変更しました:
exprs :: { [Expr] }
exprs
: expr { trace "exprs 30" [$1] }
| exprs expr { trace "exprs 31" $ $2 : $1 }
これで、ヒットした最初のトークンですべてのテストが失敗します --- 次のようにparseExpr "\x7"
失敗します。
uncaught exception: ErrorCall (Error processing token: TokenNonPrint '\a')
exprs -> expr -> nonprint -> NONPRINT
そして、パーサーがパスをたどって成功することを期待していたので、私は完全に混乱しています。この入力によってパーサーがエラー状態になる理由がわかりません。どのtrace
ステートメントもヒットしません (最適化されていますか?)。
私は何を間違っていますか?