web-dev-qa-db-ja.com

人々は新しいプログラミング言語をどのように作成しますか?

人々は新しいプログラミング言語をどのように作成しますか?具体的には:

  1. 彼らは何語でそれを書きますか?
  2. 言語は、高水準言語とマシンコードの中間にあるものでなければなりませんか?
  3. プログラミング言語を作成する段階/要素は何ですか-たとえば、CまたはC++
19
Yogesh Tripathi
I will give you a good idea of where computers started and how they've progressed.

If you want to get into the history of computers I'd look at things such as the Jacquard loom and Babbage's difference engine. These two inventions probably had the greatest early influence on early modern computing. Usage of punch cards (Jacquard) and the mechanical 'calculation' (Babbage) provided a great foundation.

それでは、コンピューターの仕組みから始めましょう。すべてのコンピューターの中心にあるのはトランジスターです(真空管の前)。トランジスタの背後にある大きなことは、状態に応じて2つの異なる方法で電気を送ることができることです*。これにより、電気の論理的な流れを作り出すことができます。これにより、あらゆる種類の素晴らしいものを作成できます**:ナンドゲート、ゲート、半加算器、多重化など。

これらの電子ビルディングブロックが配置されたため、基本的に2種類の信号が入力されます。エレクトロニクスに何をすべきかを伝える信号、およびエレクトロニクスが計算する実際のデータ。したがって、追加するコマンドは、たとえば、レジ​​スタ1からデータを取得し、レジスタ2のデータに追加し、その情報をレジスタ3に格納します。このコマンドは、コンピュータをレジスタ1 2は加算器への入力として動作し、レジスタ3は結果を保存します。同じことが減算乗算などにも当てはまります。また、特定の行にジャンプしたり、メモリから情報を読み取ったりするコマンドもあります。

これらのバイナリコマンドは、CPUを「設定」するために必要なマシンコードです。

これまで、私はあなたの質問に実際には答えていませんでしたが、何が起こっているのかを理解するためにいくつかのフレームワークを作成しました。 (あなたはすべてを学びたいと言った= P)

これで、マシンコードで実行されるコンピューターができました。これ以上のことはありません。現在、これは非常に使いにくいマシンです。したがって、アセンブリはほとんどの場合、最初に作成される言語の1つです。 Assemblyを使用するには、アセンブラーを作成する必要があります。アセンブラは本質的にコンパイラであり、アセンブリ言語をマシンコードに変換します。その結果、アセンブリ言語はマシンコードコマンドで1対1になります。これをバイナリでコーディングしているので、アイデアはシンプルに保つことをお勧めします。これで、アセンブリ言語をマシンコードに変換できるものができました。

したがって、2つのレベルの「言語」があります。0-マシンコード:これはCPUが理解するコードであり、バイナリであり、あまりユーザーフレンドリーではありません。 1-アセンブリ言語:これはいくつかの「英語のような」用語を使用しますが、それでも比較的不格好であり、マシンコードではコマンドごとに1対1です。

3番目を追加しましょう。

2-高水準言語。

高レベル言語とは、Cのように英語により似たものです。ループやデータ構造、その他の便利なものがあります。 Cを使用するには、コンパイラを作成する必要があります。コンパイラは、C言語で記述されたコードを取得し、オブジェクトコードを作成します(アセンブリ言語と同様)。次に、別のプログラムがそのオブジェクトコードを機械語に変換します。現在、これらの2つのステップは通常、効率のために1つにまとめられています。これで最初の定義ができました。コンパイラーは、高水準言語をオブジェクト(またはマシン)コードに変換します***。

最初の高レベル言語(C)ができたので、必要がない限り、アセンブリ言語を再び使用するのはおそらく愚かで苦痛のようです。そのため、新しい言語とコンパイラをC、または現在作成している他の言語で作成できます****。

それでは、インタープリターを見てみましょう。インタープリターは、プログラムを独自に読み取り、実行するプログラムです。高レベルのコードをマシンコードに変換するプログラムを作成する代わりに、これは高レベルのコードを読み取り(通常は一度に1行ずつ)、実行します。

Javaを例にとります。Javaは、基本的に、誰かがプログラム(Cなど)を作成したことを意味します。このCプログラムは、 Javaコードを実行して実行します。コンピューターとコードの間には別のレイヤーがあります。

この応答はglめくようなもの、CPUの構築方法、言語を作成する際の考慮事項、同じ言語が異なるCPUでどのように実行できるかなど、多くのものがあります。コンパイラなどに対するインタープリターの利点。しかし、うまくいけば、十分なバックグラウンドと情報が得られ、それを自分で読んで研究することができます。

25
user239887

1)彼らは何語でそれを書きますか?

それは完全にプログラマーに依存します。 C、C++にはいくつかの言語が実装されています。他のいくつかはブートストラップされます。 ( CはCで記述されている )。リンクからの抜粋:

Bootstrapping:
Basically you start with a very minimal process/set of functions that can be used to 
compile the code that defines a slightly more functional compiler. 
This creates your next compiler which then can then be used to build code that can do even 
more. 
You repeat this process until you have a full blown compiler that can 
compile all the language features. 

実際、ある言語で書かれているのはcompilerまたはinterpreterです。たとえば、Goは言語であり、2つのコンパイラ、gcgccgoがあります。 gcはCで書かれ、gccgoは主にC++で書かれたgccフロントエンドです。

2)言語は、高水準言語とマシンコードの間にあるものでなければなりませんか?

必ずしも。既に説明したように、コンパイラーまたはインタープリターは高水準言語でも作成できます。言語の記述とは、基本的に、言語の一連のルールを定義すること、または言語の仕様を記述することを意味します。

3)プログラミング言語を作成する段階/要素は何ですか-例えば、CまたはC++(私の基礎研究から、すべてのプログラミング言語にはコンパイラまたはインタープリターが必要であることがわかりましたが、二)

簡単に言えば:

  1. 言語のセマンティクスを準備します。あなたの言語の文法を引き出します。
  2. 使用する言語がコンパイラーまたはインタープリターのどちらを使用するかを決定します。
    2つの違い(違いの部分の詳細 here。 ):

インタープリターの世界では、ユーザーは通常エディターでプログラムを編集し、インタープリターで直接実行します。コンパイルの世界では、ユーザーはプログラムを編集してコンパイルし、結果の実行可能ファイルをどこかに保存して実行します。

コンパイラは、プログラム全体を入力として受け取ります。通訳者は、単一の命令を入力として受け取ります。

エラーはコンパイラーでプログラム全体がチェックされた後に表示されますが、エラーは解釈されるすべての命令(存在する場合)に対して表示されます。

  1. フロントエンドを書く:ユーザーがエラーや警告などをどのように見るか.

  2. パーサー情報を使用して、オブジェクトコードまたは中間表現を記述します。

  3. すべてをバインドするエグゼキュータまたはコードジェネレータを記述します。

  4. テストとドキュメント。

5
Ayushi Jha

1)彼らは何語でそれを書きますか?

彼らが望むものは何でも。それは言語の焦点と彼らが使いやすいものに依存します。 AssemblyからHaskellまで何でもコンパイラーを書くことができます。それは、実装がどれほど複雑であるかによって異なり、作成する言語のルールによって異なります。たとえば、言語の規則はかなり単純なので、Cコンパイラを書くのは比較的簡単です。 C++。そんなにない。

2)言語は、高水準言語とマシンコードの間にあるものでなければなりませんか?

いや。コンパイラはちょうど別のプログラム;大量のテキストを読み取り、別のテキストファイルまたはマシンコードのバイナリファイルを出力します。低レベルの言語で書く必要はありません。多くのコンパイラがCで書かれているのは、多くの人々が最もよく知っていることですが、それはすべてのコンパイラmustがCまたはアセンブラーで書かれていることを意味しません。高レベルの言語を使用すると、おそらくタスクがはるかに簡単になります。

3)プログラミング言語を作成する段階/要素は何ですか-例えば、CまたはC++(私の基礎研究から、すべてのプログラミング言語にはコンパイラまたはインタープリターが必要であることがわかりましたが、二)

通常、インタープリターrunsは翻訳中のプログラムですが、コンパイラーは実行せずにコードを翻訳するだけです。この行は最近かなりぼやけています(たとえば、Javaコードはコンパイル済みプラットフォームに依存しないバイトコードになり、それは解釈済みによってJVMからネイティブマシンコードへ)。

コンパイラの大きな部分は次のとおりです。

  • lexer。ソーステキストをtokens(リテラル、識別子、句読点など)に分割します。
  • parser。これらのトークンを受け取り、言語に一致させますgrammar;
  • コードジェネレーター。パーサーからの結果に基づいてターゲットコードを出力します。

コードジェネレーターには、ソースコードのリテラル変換よりも効率的なマシンコードを生成するためのある種の最適化ロジックも含まれている場合があります。たとえば、i = a * 10などの整数リテラルで何かを乗算する場合、gccはちょっとしたトリックを行います。のようなコードを生成する代わりに

movl     a, %edx  ;; write value of a to register edx
movl   $10, %eax  ;; write value of 10 to register eax
imull %edx, %eax  ;; multiply %edx by %eax, store in %eax

生成する

movl    a, %edx ;; same as above
movl %edx, %eax ;; copy %edx to %eax
sall   $2, %eax ;; shift %eax left by 2 places, effectively multiplying by 4
addl %edx, %eax ;; add %edx to %eax
addl %eax, %eax ;; double %eax

a3の場合、これにより

 3 << 2 == 12
12 +  3 == 15
15 + 15 == 30

一部のハードウェアでは、シフトと加算は乗算と比較して比較的高速であるため、looksより多くの命令があるため効率が低くても、実際には単純なimull呼び出しよりも少し速く実行されます。

夏のセッションでコンパイラークラスを受講しました1 ほぼ30年前なので、私のスキルは正確には最新ではありません。しかし、大きなものは実際には時間とともに変化しません。新しい言語を作成するための大変な作業は、a)やりたいことを決定すること、b)人間が読める形式で、構文解析が容易なこと、c)セマンティクスを正しくすることです。


1.これをしないでください。コンパイラの構築は、CSカリキュラムのより厳しいクラスの1つであり、短期間のセッションで行うべきではありません。
1
John Bode

これは始めるのに役立つ素晴らしい本です http://www.Amazon.com/Language-Implementation-Patterns-Domain-Specific-Programming/dp/193435645X/

言語を構築する段階は

  1. レキシン字句解析とは、特定のカテゴリのトークンを読み取ることができることを意味します。トークンには、一連の数字12376または「Hello」などのテキスト文字列を使用できます。字句解析は、最初の文字を見て(また、2番目の文字を先読みして)、それが何であるかを判別します。数字の場合は数字を見てから一連の数字の読み取りに進み(サブルーチンを呼び出す)、文字列の場合は引用符を見てから文字列の読み取りに進みます。字句解析の結果は、タイプ(この例では数値または文字列)であるトークンと、トークンのテキストです。これは通常、種類を表すために宣言された定数を持つKind intおよびText文字列として構造体に格納されます。

  2. 次の構成要素はパーサーです。パーサーは一連のトークンを確認するため、識別子が表示される場合がありますが、先を見ると=が表示されます。次に、割り当てに分岐します。パーサーはツリーを構築します。割り当ての場合、「assign」タイプの「ノード」を構築し、最初の子に識別子を、2番目の子に式を格納します。すべてのツリーノードは「操作」です。つまり、何らかの操作を行います。ノードとして文字列または整数だけでなく、ノードとして「追加」または「追加」などがあります(式である場合を除き、式は操作に含まれます)。

  3. 最後の部分は実行です。これは、ツリーを歩いてノードを実行することにより行われます。

メモリ、スコープ、先読みの機械など、他の多くの機械が関係しています。これは上記のリンクで説明されています。

1
William Egge

最初の質問について。すべての言語にはコンパイラが必要です。それは事実ですが、各言語には独自の異なるコンパイル方法があります。たとえば、JavaはJDKをコンパイルし、JVMを実行します。プログラミングした言語に関係なく(常に高レベル言語として)、コードを変換する中間層があります。マシンコード(別名バイナリコード)へ。これらの中間層は中間レベルの言語で作成されています(それらの束はそこにあります。ここで読むことができます http://en.wikipedia.org/wiki/ Timeline_of_programming_languages )および低レベルのプログラミング(まだマシンコードではない)で今日最も広く使用されています。

最初の答えと同じようにプログラミング言語のレベルは異なりますが、覚えておく必要がある最初で最も重要なことは、コンピューターが1または0であるバイナリコードを理解し、1と0の一連の文字列が一緒にプログラムの動作を定義することです。コンピューターが行うすべてのことは、それに還元することができます。

これまでに、2つの質問に答えたことを願っています。

3番目の方法については、コンパイラに常にトリックがあります。新しいプログラミング言語が必要な場合は、コードをマシンコードに変換できるようにコンパイラが必要です。あなたが今見ることができるものは、色を付けた特定のキーワードやコードをコンパイルする前のエラーの検証のように、それらはIDESです(それらについてはここで読むことができます http://en.wikipedia.org/wiki/Integrated_development_environment )。

たとえば、コード内のキーワードとして「ウサギ」を使用し、カンマの後にウサギを使用する場合、コンパイラは、コードを読み取ることによって正しく使用されたかどうかを判断する中間層プログラムである必要があります。 。

コンパイラにはさまざまな段階があります。コードを読み取り、すべての依存関係(コードで記述された他のファイル)と現在のコード行が正常かどうかを判断できる必要があります。その後、「ウサギ」を、使用するたびにプログラムに実行させたいものに変換する必要があります。最終的には、JavaはJVMを使用するため、マシンまたは独自の別のアプリケーションが直接読み取ることができるファイルを生成します。

これがあなたにいくらかの光を与えることを願っています。