それは大きなトピックですが、代わりに「本を読んで、子供に」という気前の良いことであなたを追い払うのではなく、頭を包むのを助けるためのポインタを喜んで与えます。
ほとんどのコンパイラーやインタープリターは次のように機能します。
トークン化:コードテキストをスキャンし、トークンのリストにそれを破ります。
文字列をスペースで分割することはできないため、この手順は難しい場合がありますif (bar) foo += "a string";
。8つのトークンのリストであるWORD、OPEN_PAREN、WORD、CLOSE_PAREN、WORD、ASIGNMENT_ADD、STRING_LITERAL、TERMINATOR を認識する必要があります。ご覧のとおり、スペースでソースコードを分割するだけでは機能しません。各文字をシーケンスとして読み取る必要があります。したがって、英数字に遭遇した場合は、英数字以外の文字とその文字列をヒットするまで文字を読み続けます読んだばかりの単語は、後でさらに分類される単語です。"a string"
トークナイザーの粒度は、STRING_LITERALと呼ばれる1つのトークンとして飲み込んで後で解析するか、または"a string"
OPEN_QUOTE、UNPARSED_TEXT、CLOSE_QUOTE、その他何でも、これはコーディング中に自分で決めなければならない多くの選択肢の1つにすぎません。
Lex:これでトークンのリストができました。最初のパスでは、各文字列のコンテキストを理解しようとしてあまり労力を費やさないため、おそらくWORDのようなあいまいな分類でいくつかのトークンにタグを付けました。したがって、ソーストークンのyoutリストを再度読み取り、言語のキーワードに基づいて、より具体的なトークンタイプで各曖昧なトークンを再分類します。「if」などのWORDがあり、「if」がシンボルIFという特別なキーワードのリストにあるため、そのトークンのシンボルタイプをWORDからIF、および特別なキーワードリストにないWORDに変更します。 、WORD fooなどはIDENTIFIERです。
解析:したがってif (bar) foo += "a string";
、次のような字句トークンのリストを作成しました:IF OPEN_PAREN IDENTIFER CLOSE_PAREN IDENTIFIER ASIGN_ADD STRING_LITERAL TERMINATOR。ステップは、トークンのシーケンスをステートメントとして認識することです。これは解析中です。これは、次のような文法を使用して行います。
STATEMENT:= ASIGN_EXPRESSION | IF_STATEMENT
IF_STATEMENT:= IF、PAREN_EXPRESSION、STATEMENT
ASIGN_EXPRESSION:= IDENTIFIER、ASIGN_OP、VALUE
PAREN_EXPRESSSION:= OPEN_PAREN、VALUE、CLOSE_PAREN
値:= IDENTIFIER | STRING_LITERAL | PAREN_EXPRESSION
ASIGN_OP:= EQUAL | ASIGN_ADD | ASIGN_SUBTRACT | ASIGN_MULT
「|」を使用するプロダクション 用語間は「これらのいずれかに一致する」ことを意味し、用語間にコンマがある場合は「この一連の用語に一致する」ことを意味します
これはどのように使用しますか?最初のトークンから始めて、トークンのシーケンスをこれらの制作物と一致させてください。最初にトークンリストをSTATEMENTと照合しようとすると、STATEMENTのルールを読んで「STATEMENTはASIGN_EXPRESSIONまたはIF_STATEMENTのいずれか」と表示されるため、ASIGN_EXPRESSIONと最初に一致しようとするため、ASIGN_EXPRESSIONの文法ルールを検索します「ASIGN_EXPRESSIONはIDENTIFIERで、その後にASIGN_OPが続き、VALUEが続くため、IDENTIFIERの文法規則を検索すると、IDENTIFIERの文法規則はないので、IDENTIFIERはそれ以上必要ないという意味の「端末」を意味します。トークンを直接照合するために解析しますが、最初のソーストークンはIFであり、IFはIDENTIFIERと同じではないため、一致は失敗しました。今何?STATEMENTルールに戻り、次の用語IF_STATEMENTに一致させようとします。IF_STATEMENTをルックアップし、IFで始まり、IFをルックアップし、IFがターミナルであり、最初のトークンとターミナルを比較し、IFトークンが一致し、次の用語がPAREN_EXPRESSIONであり、PAREN_EXPRESSIONをルックアップします。これはターミナルではなく、最初の用語ですPAREN_EXPRESSIONはOPEN_PARENで始まり、OPEN_PARENを検索します。これは端末であり、OPEN_PARENを次のトークンに一致させます。
このステップにアプローチする最も簡単な方法は、parse()という関数を使用して、一致させようとしているソースコードトークンと、一致させようとしている文法用語を渡すことです。文法用語が端末でない場合、再帰的です。同じソーストークンとこの文法規則の最初の用語を渡してparse()を再度呼び出します。これが「再帰降下パーサー」と呼ばれる理由ですそこからparse()。
parse()がASIGN_EXPRESSIONなどのプロダクションに一致するたびに、そのコードを表す構造体を作成します。この構造には、元のソーストークンへの参照が含まれます。これらの構造のリストを作成し始めます。この構造全体を抽象構文ツリー(AST)と呼びます
コンパイルおよび/または実行:文法の特定のプロダクションに対して、AST構造が与えられた場合、そのASTのチャンクをコンパイルまたは実行するハンドラー関数を作成しました。
それでは、タイプASIGN_ADDを持つASTの一部を見てみましょう。インタープリターとして、ASIGN_ADD_execute()関数があります。この関数はの解析ツリーに対応するASTの一部として渡されるためfoo += "a string"
、この関数はその構造を調べ、構造の最初の用語はIDENTIFIERでなければならず、2番目の用語はVALUEであることがわかっているため、ASIGN_ADD_execute() VALUE用語をVALUE_eval()関数に渡します。この関数は、メモリ内の評価値を表すオブジェクトを返します。その後、ASIGN_ADD_execute()は変数テーブルで「foo」のルックアップを行い、eval_value()によって返されたものへの参照を格納します関数。
それは通訳です。コンパイラーは、代わりにハンドラー関数を使用して、ASTを実行する代わりにバイトコードまたはマシンコードに変換します。
手順1〜3、および一部4は、FlexやBisonなどのツールを使用して簡単に作成できます。(aka。Lex and Yacc)しかし、通訳を自分でゼロから書くことは、おそらくプログラマーが達成できる最も強力な運動です。他のすべてのプログラミングの課題は、この課題をサミットした後は些細なことのようです。
私のアドバイスは小さなものから始まります。小さな言語で、小さな文法で、いくつかの簡単な文を解析して実行してから、そこから成長していきます。
これらを読んで、幸運を祈ります!
http://www.iro.umontreal.ca/~felipe/IFT2030-Automne2002/Complements/tinyc.c
http://en.wikipedia.org/wiki/Recursive_descent_parser
lex
、yacc
とbison
。