Articles

Reading Code Right, With Some Help From The Lexer

Vaidehi Joshi
Vaidehi Joshi

Follow

Nov 27, 2017 · 16 min read

Reading code right, with some help from the lexer.

Software is all about logic. プログラミングは、数学と狂気の方程式に重いフィールドであることの評判を集めています。 そして、コンピュータサイエンスは、この誤解の核心にあるようです。確かに、いくつかの数学があり、いくつかの式があります—しかし、私たちのマシンがどのように機能するかを理解するために、実際に微積分学の博士号を 実際、私たちがコードを書く過程で学ぶ多くのルールとパラダイムは、複雑なコンピュータサイエンスの概念に適用されるのと同じルールとパラダイムです。 そして時には、これらのアイデアは実際にコンピュータサイエンスに由来し、私たちはそれを知らなかった。

私たちが使用するプログラミング言語にかかわらず、私たちのほとんどがコードを書くとき、私たちは意図的に私たちのコードの異なる部分が関 言い換えれば、1つのクラス、オブジェクト、またはメソッドが1つのものだけに関係し、責任を負うように、コードを分割することは一般的に良いこと 私たちがこれをしなければ、物事は非常に乱雑になり、ウェブの混乱に絡み合う可能性があります。 時には、これは依然として懸念の分離でさえも起こります。

結局のところ、私たちのコンピュータの内部の仕組みでさえ、非常によく似た設計パラダイムに従います。 たとえば、コンパイラにはさまざまな部分があり、各部分はコンパイルプロセスの特定の部分を処理する責任があります。 先週、解析木の作成を担当するパーサーについて学んだときに、このことに少し遭遇しました。 しかし、パーサーはおそらくすべてを担当することはできません。

パーサーは仲間からの助けを必要とし、最終的に彼らが誰であるかを学ぶ時です!

最近解析について学んだとき、私たちは文法、構文、そしてコンパイラがプログラミング言語内でそれらのことにどのように反応し、応答するかに しかし、コンパイラが何であるかを実際に強調したことはありません! コンパイルプロセスの内部の仕組みに入ると、コンパイラの設計について多くのことを学ぶことになるので、ここで何を話しているのかを正確に理

コンパイラは怖いように聞こえるかもしれませんが、彼らの仕事は実際には理解するにはあまりにも複雑ではありません。

しかし、最初に、可能な限り最も単純な定義から始めましょう。 コンパイラは、私たちのコード(または任意のコード、任意のプログラミング言語)を読み取り、それを別の言語に変換するプログラムです。

コンパイラ:定義。

一般的に言えば、コンパイラは実際には高レベルの言語から低レベルの言語にコードを翻訳するだけです。 コンパイラがコードを変換する下位レベルの言語は、アセンブリコード、マシンコード、またはオブジェクトコードと呼ばれることがよくあります。 それはほとんどのプログラマーが実際に扱うか、または機械コードを書いていないことを述べる価値がある;むしろ、私達は私達のプログラムを取り、私達のコンピュータが実行可能プログラムとして動くものである機械コードに翻訳するためにコンパイラーに依存する。

私たちは、コンパイラを、私たち、プログラマ、そして私たちのコンピュータの間の仲介者と考えることができます。

コンパイラは、私たちのマシンが理解し、実行可能な方法で起こりたいことを翻訳する作業を行います。

コンパイラがなければ、マシンコードを書くことによってコンピュータと通信することを余儀なくされます。 マシンコードは、多くの場合、人間の目には0と1の束のように見えることがあります—それはすべてバイナリです、覚えていますか? -これは、読み取り、書き込み、およびデバッグすることが非常に困難になります。 コンパイラは、マシンコードについて考えずに、はるかにエレガントで明確で読みやすい言語を使用してプログラムを書くことを非常に簡単にした

私たちは、今後数週間にわたって神秘的なコンパイラについてますます解凍していきます。 しかし、今のところ、手元の質問に戻りましょう:コンパイラの最も単純な部分は何ですか?

各コンパイラは、どのように設計されていても、異なるフェーズを持っています。 これらのフェーズは、コンパイラの固有の部分をどのように区別できるかです。Div>

: phase one of a compiler

最近、パーサーと解析ツリーについて学んだとき、コンパイルの冒険ですでにフェーズの一つに遭遇しました。 私たちは、解析は、いくつかの入力を取り、それから解析ツリーを構築するプロセスであることを知っています,これは時々解析の行為と呼ばれています. 結局のところ、構文解析の作業は、構文解析と呼ばれるコンパイルプロセスのフェーズに固有のものです。

しかし、パーサーは薄い空気から解析ツリーを構築するだけではありません。 それはいくつかの助けを持っています! パーサーにはいくつかのトークン(端末とも呼ばれます)が与えられ、それらのトークンから解析ツリーが構築されていることを思い出します。 しかし、それはどこからそれらのトークンを取得しますか? パーサーにとって幸運なことに、それは真空中で動作する必要はありません。

これは、構文分析フェーズの前に来るコンパイルプロセスの別のフェーズ、すなわち字句分析フェーズに私たちをもたらします。div>

“語彙”という用語は、それを含む文から分離して、その文法的な文脈にかかわらず、単語の意味を指します。 この定義だけに基づいて自分の意味を推測しようとすると、字句解析フェーズはプログラム自体の個々の単語/用語と関係があり、単語を含む文の文法や意

字句解析フェーズは、コンパイルプロセスの最初のステップです。 それは文の文法やテキストやプログラムの意味を知っていたり気にしたりしません。

ソースプログラムのコードを解析するには、字句解析を行う必要があります。 パーサーが読み取る前に、プログラムを最初にスキャンし、分割し、特定の方法でグループ化する必要があります。

先週、構文解析フェーズを見始めたとき、構文解析ツリーは文の個々の部分を見て、式をより単純な部分に分解することによって構築されることを学びま しかし、字句分析段階では、コンパイラはこれらの「個々の部分」を認識していないか、アクセスできません。 むしろ、最初にそれらを識別して見つけてから、テキストを個々の部分に分割する作業を行う必要があります。たとえば、To sleep, perchance to dream.のようなシェイクスピアの文を読むと、スペースと句読点が文の「単語」を分割していることがわかります。 これは、もちろん、文を読んで”lex”し、文法のために解析するように訓練されているためです。しかし、コンパイラにとっては、同じ文が最初に読み込まれたときにTosleepperhachancetodreamように見えるかもしれません。 この文を読むと、実際の「言葉」が何であるかを判断するのは少し難しいです! 私たちのコンパイラは同じように感じていると確信しています。だから、私たちのマシンはこの問題をどのように処理しますか? さて、コンパイルプロセスの字句解析フェーズでは、常に二つの重要なことを行います:コードをスキャンし、それを評価します。

字句解析プロセスの二つのステップ!

スキャンと評価の仕事は、時には一つのプログラムにまとめたり、互いに依存する二つの別々のプログラムにすることができます。 スキャンと評価の作業を担当するコンパイラ内のプログラムは、しばしば字句解析またはトークナイザと呼ばれ、字句解析フェーズ全体は、字句解析またはトークナイザのプロセスと呼ばれることがあります。

スキャンするには、読むためのperchance

字句解析の二つのコアステップの最初はスキャンです。 スキャンは、入力されたテキストを実際に”読む”作業と考えることができます。 この入力テキストは、文字列、文、式、またはプログラム全体である可能性があることに注意してください! プロセスのこの段階では、それはまだ何も意味しない文字の巨大な塊であり、連続した塊であるため、実際には問題ではありません。これがどのように正確に起こるかを確認するための例を見てみましょう。

ソーステキストまたはソースコードである元の文To sleep, perchance to dream.Tosleep,perchancetodream.のような入力テキストとして読み取られます。p>

スキャンプロセス、ステップ1。

私たちのコンパイラがしなければならない最初のことは、実際にテキストのblobを可能な限り小さな部分に分割することで、テキp>

巨大なテキストの塊をダイビングする最も簡単な方法は、それをゆっくりと体系的に、一度に一つの文字を読むことです。 そして、これはまさにコンパイラが何をするかです。

多くの場合、スキャン処理はスキャナと呼ばれる別のプログラムによって処理され、その唯一の仕事はソースファイル/テキストを一度に一 私たちのスキャナにとって、それは本当に私たちのテキストがどのように大きな問題ではありません;それは私たちのファイルを”読み取る”ときに表示されますすべては、一度に一つの文字です。

ここで私たちのシェイクスピアの文は、私たちのスキャナによって読み取られるものです:

スキャンプロセス、ステップ2。figcaption>

To sleep, perchance to dream.eoftabspacenewline

スキャンプロセス、ステップ3。

今、私たちのスキャナは、その可能な最小の部分に私たちのソーステキストを読んで、分割していることを、それは私たちの文の”言葉”を次に、スキャナは分割された文字を順番に見て、どの文字が単語の一部であり、どの文字がそうでないかを判断する必要があります。

次に、スキャナ スキャナが読み取る各文字について、行とその文字がソーステキスト内で見つかった場所の位置をマークします。

ここに示されている画像は、私たちのシェイクスピアの文のためのこのプロセスを示しています。 私たちは、スキャナが私たちの文の各文字の行と列をマークしていることを見ることができます。 行と列の表現は、行列または文字の配列と考えることができます。私たちのファイルには1行しかないので、すべてが行0spacesnewlineseof、およびすべての句読点を文字として読み取るため、これらも文字テーブルに表示

スキャンプロセス、ステップ4。

ソーステキストがスキャンされ、マークされたら、コンパイラはこれらの文字を単語に変換する準備ができています。 スキャナは、ファイル内のspacesnewlineseofTospaceTosleeps-l-e-e-p,pspaceで囲まれているため、カンマ自体は”単語”とみなされます。

単語sleep,のような一連の語彙素が返されます。

スキャンプロセス、ステップ5。

私たちのスキャナは、それが最初に読み取ることができなかったその入力としてテキストの塊を取った方法に注意してくださ その後、区切り文字として文字間のスペースと句読点を使用して、文字列を可能な限り最小の語彙に分割しました。しかし、この作業のすべてにもかかわらず、字句解析フェーズのこの時点では、スキャナはこれらの単語について何も知りません。 確かに、それは異なる形やサイズの単語にテキストを分割しますが、それらの単語が何であるかについては、スキャナは考えていません! 単語はリテラル文字列であるか、句読点であるか、または完全に何か他のものである可能性があります!p>

スキャナは、単語自体、またはそれらがどのような単語の”タイプ”について何も知らない。 それはちょうど単語が終わり、テキスト自体の内で始まるところを知っている。

これは、字句分析の第二段階:評価のために私たちを設定します。 テキストをスキャンし、ソースコードを個々の字句単位に分割したら、スキャナが返した単語を評価し、どのような種類の単語を扱っているかを把握する必

重要な部分を評価する

ソーステキストのスキャンが完了し、語彙素を特定したら、語彙素”words”で何かをする必要があります。 これは字句解析の評価ステップであり、コンパイラ設計では入力を字句変換またはトークン化するプロセスと呼ばれることがよくあります。div>

スキャンしたコードを評価することはどういう意味ですか?

スキャンしたコードを評価するとき、私たちが実際にやっているのは、スキャナが生成した各語彙を詳しく調べることだけです。 私たちのコンパイラは、各語彙素単語を見て、それがどのような種類の単語であるかを決定する必要があります。 私たちのテキスト内の各”単語”の語彙素の種類を決定するプロセスは、コンパイラが個々の語彙素をトークンに変換し、それによって入力文字列をトークン化する方法です。

解析木について学んでいたときに、最初にトークンに遭遇しました。 トークンは、各プログラミング言語の核心にある特別なシンボルです。 (, ), +, -, if, else, thenなどのトークンはすべて、式のさまざまな部分とさまざまな要素が互いにどのように関係しているかをコンパイラが理解するのに役 構文解析フェーズの中心であるパーサーは、どこかからトークンを受信することに依存し、それらのトークンを解析ツリーに変換します。P>

トークン:定義。

まあ、何を推測しますか?

まあ、何を推測しますか?

まあ、何を推測しますか?

まあ、何を推測しますか?

まあ、 私たちは最終的に”どこか”を考え出しました! 結局のところ、パーサーに送信されるトークンは、字句解析フェーズで、字句解析とも呼ばれるトークナイザによって生成されます。

私たちのシェークスピアの文をトークン化!

では、トークンはどのように見えますか? トークンは非常に単純で、通常はトークン名といくつかの値(オプション)で構成されるペアとして表されます。

たとえば、Shakespearean文字列をトークン化すると、主に文字列リテラルと区切り文字になるトークンになります。 字句"dream”<string literal, "dream">.<separator, .>

これらのトークンのそれぞれが字句をまったく変更していないことに気付くでしょう。 具体的には、追加された詳細は、私たちが扱っているトークンのカテゴリ(どのタイプの”単語”)を教えてくれます。

シェイクスピアの文章をトークン化したので、ソースファイルのトークンの種類にはそれほど多様ではないことがわかります。 私たちの文には文字列と句読点しかありませんでしたが、それはトークンの氷山の一角にすぎません! 語彙素はに分類することができる「単語」の他のタイプの多くがあります。

ソースコード内で見つかったトークンの一般的な形式。

ここに示されている表は、ほとんどすべてのプログラミング言語でソースファイルを読み取るときにコンパイラが見る最も一般的なトークンのいくつかを示しています。 literalsseparators{}())を含む句読点の任意のタイプです。

However, there are also keywords, which are terms that are reserved in the language (such as ifvarwhilereturn), as well as operators, which operate on arguments and return some value ( +-x/identifiersと同様に、ユーザーが書いた行またはブロックコメントである可能性があります。

私たちの元の文は、私たちにトークンの二つの例を示しました。 代わりにvar toSleep = "to dream";を読むように文を書き直しましょう。 どのように私たちのコンパイラは、シェイクスピアのこのバージョンをlex可能性がありますか?div>

どのように私たちのレクサーは、この文をトークン化しますか?

ここでは、より多くの種類のトークンがあることがわかります。 変数を宣言しているvarkeywordidentifiertoSleep=operator"to dream";区切り文字で終わります。

トークン化プロセスについて注意すべき重要なことは、空白(スペース、改行、タブ、行末など)をトークン化していないことです。)、またそれをパーサーに渡すこともできません。 トークンのみがパーサーに与えられ、最終的に解析ツリーになることに注意してください。また、異なる言語には空白として構成される異なる文字があることにも言及する価値があります。

例えば、いくつかの状況では、Pythonプログラミング言語は、関数のスコープがどのように変化するかを示すために、タブやスペースを含むインデントを使用 そのため、Pythonコンパイラのトークナイザは、特定の状況では、tabspace実際にはパーサーに渡す必要があ

レクサーとスキャナの制約。

トークナイザのこの側面は、レクサー/トークナイザがスキャナとどのように異なるかを対比する良い方法です。 スキャナは無知であり、テキストを小さな可能な部分(その”単語”)に分割する方法しか知っていませんが、レクサー/トークナイザははるかに認識し、より正確

トークナイザは、コンパイルされている言語の複雑さと仕様を知る必要があります。 tabsnewlinesがコンパイルされている言語で特定の意味を持つことができる場合、トークナイザはそれらの詳細を認識する必要があります。 一方、スキャナは、それが分割する言葉が何であるかさえも知らず、それらが何を意味するのかははるかに少なくなります。

コンパイラのスキャナははるかに言語に依存しませんが、トークナイザは定義上言語固有でなければなりません。

字句解析プロセスのこれら二つの部分は手に手を取り、それらはコンパイルプロセスの最初の段階の中心です。 当然、異なったcompliersは自身の独特な方法で設計されている。 いくつかのコンパイラは、スキャンとトークン化のステップを単一のプロセスと単一のプログラムとして実行しますが、他のコンパイラはそれらを別のクラスに分割します。P>

字句分析:簡単な視覚的な要約!

いずれの場合も、構文解析フェーズはそれに直接依存するため、字句解析のステップはコンパイルにとって非常に重要です。 そして、コンパイラの各部分には独自の役割がありますが、お互いに依存し、お互いに依存しています—良い友人がいつもしているように。

リソース

コンパイラの記述と設計にはさまざまな方法があるため、それらを教えるにはさまざまな方法があります。 コンパイルの基本について十分な研究を行うと、いくつかの説明が他のものよりもはるかに詳細になっていることがかなり明確になります。 あなた自身がより多くを学びたいと思う場合は、以下のコンパイラに関するさまざまなリソースがあります—字句分析フェーズに焦点を当てています。

  1. 第4章-Crafting Interpreters,Robert Nystrom
  2. コンパイラ構築,教授Allan Gottlieb
  3. コンパイラの基礎,教授James Alan Farrell
  4. プログラミング言語を書く—字句解析器,Andy Balaam
  5. パーサーとコンパイラがどのように動作するかについてのノート,Stephen Raymond Ferg
  6. トークンと字句の違いは何ですか?,StackOverflow