0

Oberon に似た言語のパーサーを実装しています。レクサーによって返されるトークンのリストが正しいことがわかるので、Alex を使用してレクサーを正常に作成できました。

パーサーにトークン リストを渡すと、最初のトークンで停止します。

これは私のパーサーです:

...

%name myParse
%error { parseError }

%token
    KW_PROCEDURE        { KW_TokenProcedure }
    KW_END              { KW_TokenEnd }
    ';'                 { KW_TokenSemiColon }
    identifier          { TokenVariableIdentifier $$ }

%%

ProcedureDeclaration    :   ProcedureHeading ';' ProcedureBody identifier     { putStrLn("C") }

ProcedureHeading        :   KW_PROCEDURE identifier { putStrLn("D") }

ProcedureBody           :   KW_END                      { putStrLn("E") }   
                        | DeclarationSequence KW_END    { putStrLn("F") }

DeclarationSequence     :   ProcedureDeclaration        { putStrLn("G") }

{
parseError :: [Token] -> a
parseError _ = error "Parse error"

main = do
  inStr <- getContents
  print (alexScanTokens inStr)
  myParse (alexScanTokens inStr)
  putStrLn("DONE")
}

これは、パーサーに与えるテスト コードです。

PROCEDURE proc;
END proc

これは、レクサーによって返されるトークン リストです。

[KW_TokenProcedure,TokenVariableIdentifier "proc",KW_TokenSemiColon,KW_TokenEnd,TokenVariableIdentifier "proc"]

パーサーはエラーを出しませんが、私の ProcedureDeclaration ルールに固執し、C のみを出力します。

出力は次のようになります。

C
DONE

理由はありますか?


アップデート:

私は最初の一歩を踏み出し、以前に与えられたテスト入力を解析することができました. 今度はパーサーを変更して、同じレベルの複数のプロシージャーの宣言を認識できるようにしました。これを行うために、これは私の新しいパースがどのように見えるかです:

...

%name myParse
%error { parseError }

%token
    KW_PROCEDURE        { KW_TokenProcedure }
    KW_END              { KW_TokenEnd }
    ';'                 { KW_TokenSemiColon }
    identifier          { TokenVariableIdentifier $$ }

%%

ProcedureDeclarationList    :   ProcedureDeclaration                                { $1 }
                            |   ProcedureDeclaration ';' ProcedureDeclarationList   { $3:[$1] }

ProcedureDeclaration        :   ProcedureHeading ';' ProcedureBody identifier       { addProcedureToProcedure $1 $3 }

ProcedureHeading            :   KW_PROCEDURE identifier                             { defaultProcedure { procedureName = $2 } }

ProcedureBody               :   KW_END                                              { Nothing }
                            |   DeclarationSequence KW_END                          { Just $1 }

DeclarationSequence         :    ProcedureDeclarationList                           { $1 }

{
parseError :: [Token] -> a
parseError _ = error "Parse error"

main = do
  inStr <- getContents
  let result = myParse (alexScanTokens inStr)
  putStrLn ("result: " ++ show(result))
}

問題は、コンパイルに失敗して次のエラーが発生することです。

Occurs check: cannot construct the infinite type: t5 ~ [t5]
    Expected type: HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
      Actual type: HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn t5 t5 t6 t7 t8 t9
                   -> HappyAbsSyn [t5] t5 t6 t7 t8 t9
    ...

ルールの 2 番目の要素が原因であることは確かですが、そのProcedureDeclarationsList理由はわかりません。

4

2 に答える 2

1

ここで注意すべき点が 2 つあります。

  1. happy は、最初のプロダクション ルールを のトップレベル プロダクションとして使用しmyParseます。

最初のプロダクション ルールはProcedureDeclarationであるため、解析しようとするのはこれだけです。DeclarationSequenceおそらく最初のルールを作りたいと思うでしょう。

  1. プロダクションの戻り値の型は IO アクションであり、Haskell の IO アクションは値です。の一部になるまで「実行」されませんmain。つまり、プロダクションを次のように記述する必要があります。

    DeclarationSequence : ProcedureDeclaration
        { do $1; putStrLn("G") }
    ProcedureDeclaration : ProcedureHeading ';' ProcedureBody identifier
        { do $1; $3; putStrLn("C") }
    

つまり、ルールの戻り値は、によってDeclarationSequence返される IO アクションです。ProcedureDeclarationputStrLn "G"

ProducedureDeclarationまた、ルールの戻り値は、によって返さProcudureHeadingれるアクションの後に によって返されるアクションがProcedureBody続きputStrLn "C"ます。

>>演算子を使用してルールの RHS を記述することもできます。

{ $1 >> putStrLn "G" }
{ $1 >> $3 >> putStrLn "C" }

アクションの順序を決定する必要があることに注意してください。つまり、前/後/順番です。

作業例: http://lpaste.net/162432

于 2016-05-04T16:00:30.233 に答える
0

あなたの式は問題なく解析されているようです。の戻り値の型を確認してください。myParseそうなると思いますがIO ()、実際のアクションはputStrLn("D")- に書き込んだものProcedureDeclarationです。次に、do ブロックで put 呼び出しを行うと、モナド アクションとして、または単にリンクmyParseしていると解釈されます。「D」を出力するアクションを返すため、出力はまさに期待どおりです。print .. >> myParse (..) >> putStrLn ..myParse

ProcedureBodyおよびで他のアクションが定義されていDeclarationSequenceます。しかし、これらのアクションを決して使用することはありません。次のように記述します。

    do
      let a = putStrLn "E"
      putStrLn("C")

「C」を出力するaものは、決して使用されません。パーサーと同じです。これらのアクションを呼び出したい場合は、関連するコードを記述$1 >> putStrLn("C") >> $2してみてください。ProcedureDeclaration

于 2016-05-04T15:22:01.650 に答える