「48時間でSchemeを書こう」の版間の差分

削除された内容 追加された内容
→‎構文解析: 翻訳準備
161 行
== 構文解析 ==
 
=== Writing a Simple Parser簡単なパーサ ===
 
では、非常に簡単なパーサを書いてみましょう。それには[http://www.haskell.org/ghc GHC]に付いてくる[http://www.cs.uu.nl/~daan/download/parsec/parsec.html Parsec]ライブラリ(Ubuntuではlibghc6-parsec-devパッケージをインストール)を使いますが、他のコンパイラを使っている場合は別にダウンロードする必要があるかもしれません。
Now, let's try writing a very simple parser. We'll be using the [http://www.cs.uu.nl/~daan/download/parsec/parsec.html Parsec] library, which comes with [http://www.haskell.org/ghc GHC] (needs to install libghc6-parsec-dev on Ubuntu) but may need to be downloaded separately if you're using another compiler.
 
まずは次の行をimportセクションに加えてください。
Start by adding this line to the import section:
 
<syntaxhighlight lang="Haskell">
import Text.ParserCombinators.Parsec hiding (spaces)
</syntaxhighlight>
 
これによって、私たちが後に定義することになる関数と名前が衝突する<code>spaces</code>関数以外のParsecのライブラリ関数を使えるようになります。
This makes the Parsec library functions available to us, except the <code>spaces</code> function, whose name conflicts with a function that we'll be defining later.
 
では、Schemeの識別子で許される記号の一つを認識するパーサを定義します。
Now, we'll define a parser that recognizes one of the symbols allowed in Scheme identifiers:
 
<syntaxhighlight lang="Haskell">
symbol :: Parser Char
symbol = oneOf "!#$%&amp;|*+-/:&lt;<=&gt;>?@^_~"
</syntaxhighlight>
 
This is another example of a monad: in this case, the "extra information" that is being hidden is all the info about position in the input stream, backtracking record, first and follow sets, etc. これもモナドの一例です。この場合、隠蔽された「追加情報」は入力ストリーム、バックトラックの記録、ファーストとフォロー集合などの全ての情報です。それらはParsec takes care of all of that for us. We need only use the がやってくれます。私たちはParsec library function のライブラリ関数[http://www.cs.uu.nl/~daan/download/parsec/parsec.html#oneOf oneOf], and it'll recognize a single one of any of the characters in the string passed to it. を使うだけでよく、それは引数で与えられた文字列中のどれか一文字を認識します。Parsec provides a number of pre-built parsers: for example, はいくつもの既成のパーサを提供します。例えば、[http://www.cs.uu.nl/~daan/download/parsec/parsec.html#letter letter] and [http://www.cs.uu.nl/~daan/download/parsec/parsec.html#digit digit] are library functions. And as you're about to see, you can compose primitive parsers into more sophisticated productions.はライブラリ関数です。そしてこれから見ていくように、これらの基本的なパーサを使ってより洗練された複雑なパーサを組み立てていくことができます。
 
私たちのパーサを呼び出し、起こり得るエラーを処理する関数を定義しましょう。
Let's define a function to call our parser and handle any possible errors:
 
<syntaxhighlight lang="Haskell">
readExpr :: String -&gt; String
readExpr :: String -> String
readExpr input = case parse symbol "lisp" input of
Left err -&gt;> "No match: " ++ show err
Right val -&gt;> "Found value"
</syntaxhighlight>
 
As you can see from the type signature, 型宣言からわかるように、<code>readExpr</code> is a function は文字列から文字列への関数(<code>-&gt;></code>) from a String to a String. We name the parameter です。<code>input</code>, and pass it, along with the という引数を取り、それと共に上で定義した<code>symbol</code> action we defined above and the name of the parser アクションと、作ったパーサの名前(<code>"lisp"</code>), to the Parsec function [http://www.cs.uu.nl/~daan/download/parsec/parsec.html#parse parse].関数に渡します。
 
<code>parse</code> can return either the parsed value or an error, so we need to handle the error case. Following typical はパースした結果の値かエラーを返すので、エラーの場合も扱わなければなりません。Haskell convention, の典型的な慣習に倣って、Parsec returns an [http://www.haskell.org/onlinereport/standard-prelude.html#$tEither Either] data type, using the データ型を返します。<code>Left</code> constructor to indicate an error and the 構築子でエラーを、<code>Right</code> one for a normal value.で通常の値を表します。
 
<code>parse</code>の結果をこれらの選択に対してマッチさせるには<code>case...of</code>構文を使います。もしLeftの値(エラー)を得たら、そのエラーを<code>err</code>に束縛し、エラーの文字列表現と共に<code>"No match"</code>を返します。もしRightの値を得たら、それを<code>val</code>に束縛し無視して、文字列<code>"Found value"</code>を返します。
We use a <code>case...of</code> construction to match the result of <code>parse</code> against these alternatives. If we get a Left value (error), then we bind the error itself to <code>err</code> and return "No match" with the string representation of the error. If we get a Right value, we bind it to <code>val</code>, ignore it, and return the string "Found value".
 
<code>case...of</code>は[[#Evaluation, Part 1|後に]]もっと全然詳しく見ることになるパターン・マッチの一例です。
The <code>case...of</code> construction is an example of pattern matching, which we will see in much greater detail [[../Evaluation, Part 1#Beginnings of an evaluator: Primitives|later on]].
 
最後に、<code>readExpr</code>を呼び、結果を出力するように<code>main</code>関数を変更しなければいけません(ファイルの頭に<code>import System</code>も追加しましょう)。
Finally, we need to change our main function to call <code>readExpr</code> and print out the result (need to add <code>import System</code> in the beginning of the file now):
 
<syntaxhighlight lang="haskell">
main :: IO ()
main =:: doIO args &lt;- getArgs()
main = do args <- getArgs
putStrLn <span class="changed_code">(readExpr (args !! 0))</span>
putStrLn (readExpr (args !! 0))
</syntaxhighlight>
<!-- putStrLn <span class="changed_code">(readExpr (args !! 0))</span> -->
 
これをコンパイル・実行するには、正常にリンクさせるため、コマンドラインで<code>-package parsec</code>を指定します。
To compile and run this, you need to specify <code>-package parsec</code> on the command line, or else there will be link errors. For example:
 
<syntaxhighlight lang="bash">
debian:/home/jdtang/haskell_tutorial/code#% ghc -package parsec -o simple_parser [../code/listing3.1.hs listing3.1.hs]
debian:/home/jdtang/haskell_tutorial/code# ./simple_parser $
% ./simple_parser $
Found value
debian:/home/jdtang/haskell_tutorial/code# ./simple_parser a
% ./simple_parser a
No match: "lisp" (line 1, column 1):
unexpected "a"
</syntaxhighlight>
 
=== Whitespace ===