O42aプログラミング言語

私はプログラミングが好きではありません。 結果が必要です。



プログラミングの「結果」が中間であることは明らかです。 その後、メンテナンス、エラーの修正、開発が行われます。したがって、すでに記述されたコードを処理します。 したがって、結果には、動作中のプログラムだけでなく、そのソースコードも含まれます。そのメンテナンスは、より高価で、適切ではないか、単純に、このコードのせいにされます。



しかし、主なことはそれが機能するということです。 そして、早ければ早いほど良い。



アイデアと結果を分離するのはプログラミングだけです。その本質は、思考の提示、問題を解決するロジック、機械処理でアクセス可能な表現です。



だからここに。 現代のプログラミング言語は思考の表現に干渉し、解決される問題に関連せず、言語翻訳者が「理解」するためにのみ必要とされる多くの詳細を負担します。 また、特にコンパイルされた言語の多くは非常に冗長ですが、構文についても問題ではありません。 まず第一に、それはそれが説明されるべき「用語」である言語的実体についてです。 これらのエンティティ(たとえば、関数、変数、クラス、メソッド、パッケージ、名前空間、一般化、テンプレート)は狭すぎて特殊であり、人間の理解よりも機械表現のために設計されています。 彼らは思考を彼らの言語に強制的に翻訳します。 もちろん難しいことではありません。 しかし、これは目前の問題には当てはまりません。 適切な言語エンティティの選択とそれらへの翻訳は、タスクから注意をそらし、集中力を低下させ、その結果、開発効率を低下させます。 そして、私はかなりの方法で疑っています。 このようなコードを読み取るプロセスの本質を理解することはさらに難しく、特にチーム開発において生産性に最も良い影響を与えることはありません。



現代のプログラミング言語の問題は、プログラマーに自分自身を適応させるのではなく、プログラマーが機械またはそれらが基づく理論に適応させることを強制することです。 そして、数学的理論は厳密であり、鉄は鉄であり、プログラマーの利便性は主観的であるという事実は、試してはいけないという意味ではありません。



o42aの主なアイデアは、プログラマーの作業を自動化することです。 そしてこれは、言語エンティティのタイプを、それらすべてを直接置き換えることができる単一のエンティティに根本的に削減することによって実現されます。 そのようなエンティティの効率的なマシン表現のタスクは、コンパイラに完全にあります。



アイデア



私はすぐに言わなければなりません:そのような実体は、その普遍性で役に立たないスイスのナイフのようなものであってはなりません。 しかし、そのようなレンガ(Lispのリストなど)から好きなものを構築することは、原始的なレンガではありません。



さまざまなプログラミングパラダイムでは、互いに互換性がないように思われるエンティティが多数あります。 それでは、どうやって彼らの共生を達成できますか?



このアイデアは何年も前にプロローグから提案されました。 述語計算の魔法に加えて、その中には別の顕著な特徴があります。述語の記録を見ると、それが同時に通常の関数の記録であることがわかります。 このように考えると、本質は変わらない。



この考えをできるだけ広く適用したかった。 異なるプログラミングパラダイムの異なる概念には、見かけ以上に多くの共通点があると思いませんか? この問題の主なものは、独断主義が少ないことです。



o42aの2番目のアイデアは、言語のセマンティクスと構文を分離する機能です。 構文は、言語エンティティに対応するために1対1である必要はありません。 構文は、プログラミング言語ではなく、プログラムのセマンティクスを表現する必要があります。 読みやすく、十分に厳格でなければなりません。 プログラムテキストを言語エンティティに取り込むのは、プログラマではなくコンパイラのタスクです。



MVCパラダイムから構文をビューに、プログラムエンティティをモデルにマップできます。 そして、あなたはそれをDSLと呼ぶことができます。



ここで構文を使用して説明を開始します。 その最も重要でない機能が最大の拒否を引き起こすと思います。



構文の基本



私は、人類が発明した最高の構文は書き言葉だと決めました。 そのため、言語の構文が書き言葉(もちろん、プログラミングで確立された伝統によれば英語)に近いほど良いです。 結局、「コードの読みやすさ」という表現はそのような選択の素因となります。なぜなら、書かれたスピーチは読みやすいように作成され、子供の頃から読むように教えられているからです。



o42aにはキーワードはありません。 必要なものすべてに、アイコンが使用されます。 一見、これは恐ろしい決断のように思えるかもしれません。 ただし、言語には本質が1つしかないことを考慮に入れる必要があり、それが参加する構文構造はそれほど多くありません。 そのため、多くのバッジは必要ありません。



名前


o42aの名前は大文字と小文字を区別せず、スペースで区切られた単語、ラテン数字、ハイフンで構成されます。

Hello World Links 2- 3- 4 -90
      
      





単語の文字は、Unicode文字です。 行の複数のスペースは、1つのスペースと同じ意味です。 スペースは、文字と非文字、数字と非数字の間にある必要はありません。 名前は数字またはハイフンで始まってはならず、ハイフンで終わってはなりません。 ハイフンは単一でなければなりません。 ハイフンの前のスペースは、減算記号と区別するために禁止されています。

したがって、上記の名前は異なる方法で記述することができます。

 Hello world links2-3-4 -90 
      
      





コメント


o42aへのコメントも珍しいです。 チルダは区切り文字として使用されます。 さらに、それらは小文字とブロックコメントの両方に使用されます。



行コメントは、2つ以上のチルダで始まります。

 a + b ~~ The sum
      
      





行コメントは、行末または2つ以上のチルドで終了します。

 a +~~~plus~~~ b
      
      





ブロックコメントは、3つ以上のチルダの水平線で始まり、終わります。 空白以外の文字は、行と同じ行にあるべきではありません。

 ~~~~~~~~~~~~~~~~~~~~~~~~~~ Copyright (C) 2012 This file is part of o42a. ~~~~~~~~~~~~~~~~~~~~~~~~~~
      
      





文書化のために、Markdownが使用されることになっています。



改行とアンダースコア


o42aのステートメントは文に結合さ 、カンマ、ピリオド、セミコロン、感嘆符、疑問符などのさまざまな句読点で区切ることができます。 これらの標識にはそれぞれ独自の目的があります。 ただし、これについては後で説明します。 行末の句読点がオプションであることが重要です-ピリオドが想定されます。 式または処方は、アンダースコアを使用して明示的に次の行に移動する必要があります。

 Sum = _left operand + _right operand
      
      





アンダースコアは、前の行の末尾または次の行の先頭に配置できます。



また、名前自体にスペースが含まれている可能性があるため、名前を区切るにはアンダースコアを使用する必要があります。

 Print error _nl ~~     "\n" ~~      (stderr  C).
      
      





その他


10進数:

 1 234 567
      
      





言語には、実数や16進表記のリテラルはありません。 ただし、フレーズがありますので、これは問題ではありません(詳細は後で説明します)。

 float '3,141 592 653 59'
      
      





文字列はCのように「接着」されます:

 "abc" "def" ~~  ,  : "abcdef"
      
      





バックスラッシュを使用したシールドは正常です。 Unicodeコードポイントは常に16進数で記述され、特別な方法でエスケープされます。

 "\t \42a\ \" \' \\ \r\n"
      
      





複数行テキストのサポートがあります。 複数行テキストのエスケープは機能しません。

 """"""    """"""
      
      





オブジェクト



オブジェクトはo42aの核心です。



オブジェクトは、別のオブジェクトから継承することにより作成されます。 これがオブジェクトを作成する唯一の方法です。



すべてのオブジェクトは、 Void



オブジェクトから直接または間接的に継承されます。誰からも継承されない唯一のオブジェクトです。



フィールドと継承


オブジェクトにはフィールドがあります。 フィールドは、ネストされた名前付きオブジェクトです。



次にフィールド宣言の例を示します。

 Object := void ( Field := "Value" ~~ `Field` -    `Object`   `String`. )
      
      





デフォルトでは、フィールドにはパブリックスコープがあります。 ただし、内部(プライベート)および保護(保護)として宣言できます。



o42aの式は、既存のオブジェクトにアクセスするか、新しいオブジェクトを作成します。 他の式はありません。 したがって、o42aのすべての式はオブジェクトへの参照です。 文字列リテラルは、標準のString



オブジェクトから継承されたオブジェクトへの参照です。 数値-それぞれInteger



から。



コロンを介してオブジェクトのフィールドにアクセスできます。

 Object: field
      
      





任意のオブジェクトを継承できます。 オブジェクトが継承されると、そのすべてのフィールドも継承されます。 同時に、フィールドはオーバーロードされる可能性があります。

 Derived object := object ( ~~ `Derived object`   `Object`. Field = "New value" ~~  `Field` . )
      
      





フィールドをオーバーロードするには、 :=



代わりに=



記号を使用します。



ただし、継承されたオブジェクトでは、継承されたオブジェクトとまったく同じ名前のフィールドを宣言できます。

 Another object := object ( Field := 123 ~~        `Field`. )
      
      





この場合、新しいオブジェクトには同じ名前の2つのフィールドがあります。 次のようにアクセスできます。

 Another object: field ~~ 123 Another object: field @object ~~ "Value" Another object: field @another object ~~ 123
      
      





プロトタイプと抽象フィールド


見にくいことはないので、オブジェクトはクラスを置き換えます。 実際、オブジェクトが自身の構造に関する完全な情報を持っている場合、なぜクラスが必要なのでしょうか? 残っているアプリケーションは1つだけです。(抽象的な)プログラムインターフェイスとそのいくつかの異なる実装を指定する必要がある場合。



これらの目的のために、プロトタイプを使用できます。

 Interface :=> void ( ~~~ .     `:=>`. ~~~ Name :=< string ~~~  .     `:=<`. ~~~ )
      
      





プロトタイプと通常のオブジェクトの違いは、それらのコンテンツ(フィールドなど)にアクセスできないことです。 次のコードはエラーになります。

 Interface: name ~~ : `Interface` -  .
      
      





ただし、プロトタイプは他のオブジェクトと同様に継承できます。 実際、これが必要なのはこれだけです。



さらに、プロトタイプには抽象フィールドが含まれる場合があります。 そのようなフィールドは、継承中にオーバーロードする必要があります。

 Implementation 1 := interface ( Name = "Implementation 1" Implementation 1-specific field := 1 ) Implementation 2 := interface ( Name = "Implementation 2" Implementation 2-specific field := 2 )
      
      





その目的から、プロトタイプは普通のクラスです。 対照的に、o42aの通常のオブジェクトはクラスとそのインスタンスの両方です。



後者は、Javaプログラマーに馴染みがあるはずです。 これらは匿名クラスです:

 Runnable task = new Runnable() { @Override public void run() { System.err.println("Done!"); } }
      
      





この式は、匿名クラスとそのインスタンスの両方を作成します。 o42aの違いは、この方法で作成された「クラス」は必ずしも匿名ではないということです。



オブジェクト値


各オブジェクトには意味があります。 この値の型はオブジェクト-祖先から継承され、 void



でない限り変更できません。



o42aにはいくつかのタイプの値があります。 それらはそれぞれ標準オブジェクトで表されます。 以下にいくつかの単純なタイプを示します。



より複雑な型が存在します。たとえば、行と配列、関係、変数などです。 型システムは、必要に応じて時間とともに拡大します。



オブジェクトの値は必ずしも定数ではありません。 定義で定義されたアルゴリズムを使用して計算されます 。 値定義は、オブジェクトの本体にある一連のステートメントです。 それは非常に複雑になる可能性があります:条件、サイクル、その他何でも。 ただし、オブジェクトの実際の値は、次形式の値の処方(戻り)によって指定されます。

 = value
      
      





次の宣言は同等です。

 Value := 5 ~~   ,  : Value := integer (= 5)
      
      





値の定義は継承され、オーバーロードできます。



次に、2つの数値の合計を決定する例を示します。

 Sum :=> integer ( Left operand :=< integer Right operand :=< integer = Left operand + right operand )
      
      





同じ定義が異なる値につながる可能性があることに注意してください。

 Sum (Left operand = 1. Right operand = 2) ~~ 3 Sum (Left operand = -1. Right opernad = 10) ~~ 9
      
      





アダプターとサンプル


オブジェクトを作成するとき、祖先に加えて、オブジェクトを作成する1つ以上のサンプルを指定できます。

 Object := ancestor & sample 1 & sample 2 (~~  ~~)
      
      





この場合、サンプルのフィールドと定義は新しいオブジェクトに継承され、オブジェクト自体はサンプルと互換性があります( Barbara Liskov置換原理の意味 )。 継承の競合の可能性は、特定の規則に従って解決されます。



はい、これは多重継承です。 しかし、それを適用することは常に適切ですか? 実際には、継承(複数を含む)は次の3つの場合のいずれかで使用されます。

  1. オブジェクトが、パーティクル「it」の代わりに、オブジェクトによって継承されたオブジェクトのバリアントであることを示すため。 たとえば、「スイカはベリーです。」
  2. オブジェクトにいくつかのプロパティを与えるために、いくつかの言語ではこの目的のために「不純物」(特性、ミックスイン)が使用されます。
  3. オブジェクトに別のプログラムインターフェイスを追加したり、別のタイプにしたりするには、この場合はコンポジションを使用することをお勧めします。


最初の2つのケースでサンプルを使用すると便利です。o42aの3つ目のケースでは、別個のアダプターメカニズムがあります。



アダプタは、識別子が名前ではなく別のオブジェクトであるオブジェクトフィールドです。

 Foo := void ( Value := 123 @String := "Foo=" + value ~~ `String`   `@`  ~~   ,    . )
      
      





アダプタオブジェクトは、常にその識別子オブジェクトを継承します。 キャストするとき、o42aは最初にオブジェクトが目的のオブジェクトから継承されているかどうかを確認し、それからアダプターを使用しようとします。 このコードは次のとおりです。

 Print [foo] nl
      
      





Print



パラメーターは文字列でなければならず、 String



Foo



オブジェクトは継承されないという事実にもかかわらず、 Foo=123



Print



ます。 対応するアダプターがパラメーターとして渡されます。



標準値型は、アダプタを使用して文字列およびその他の型にキャストされます。



アダプターに直接アクセスすることもできます。

 Foo @@string
      
      





アダプター自体のフィールドにアクセスすることもできます。

 Foo: length @string ~~ 7
      
      





アダプタフィールドにアクセスするための構文は、オブジェクト自体のフィールドにアクセスするための構文と同じであることに注意してください。 フィールドのソース(この場合は@string



)を示す場合を@string



、必須です。



アダプターは他の場合にも使用されます。 たとえば、メインアプリケーションオブジェクトを示すには:

 Use namespace 'Console' ~~ `Print`  `Main`    `Console`. @Main := * { ~~  ,    `Main`  . Print "Hello, World!" nl }
      
      





アプリケーションは、 @Main



アダプターへのアクセスで構成され、有名なテキストを印刷します。



多くのアプリケーションを発明することができます。 たとえば、アダプタを使用して、任意のオブジェクトのハッシュ関数を計算できます。 @Hash code



アダプターを定義する@Hash code







アダプタを使用すると、任意のオブジェクトに目的の機能を追加できます。 これにより、 Void



ベースオブジェクトにフィールドをVoid



必要がなくなります。 さらに、注釈付きまたは特別な名前の__str__



形式のメソッドとは異なり、タイプセーフ__str__







一般的なプログラミング



o42aには、特徴的なタイプのパラメーターを備えた使い慣れたテンプレート(テンプレート)や一般化(ジェネリック)はありません。 o42aの各オブジェクトはすでに一般化されており、それを含むオブジェクトと隣接するフィールドによってパラメーター化されています。



実際、オブジェクトへの参照は通常静的ではありません。 これは、そのようなリンクを含むオブジェクトを継承する場合、継承オブジェクトは同じリンクを使用して別のオブジェクトを受け取ることができることを意味します。



以下に例を示します。

 Base := void ( A := void ( F := 123 ~~ `Base: a`   `F'. ) B := a ( ~~ `Base: b`   `Base: a`.  = 456 ) ) Object := base ( ~~  `Base`. A = * ( ~~  `A`. G := f * 10 ~~   `A: g`. ) ) Object: a: g ~~ 1230 Object: b: g ~~ 4560
      
      





Object: a: g



フィールドはBase: b



後に定義されていることに注意してください。 ただし、式Object: b: g



完全に正しいです。



実際、オブジェクトは他のオブジェクトだけでなく、それらを参照する式を継承します。 別のコンテキストで実行されると、そのような式は別のオブジェクトに解決される可能性があり、したがって、わずかに異なる継承階層につながる可能性があります。 式が許可されるどのようなコンテキストでも、結果のオブジェクトは常に元のオブジェクトと互換性がある、つまり、それがオブジェクトであるか、その相続人であることが重要です。



このような機能は、従来の一般化を完全に置き換えるものではありませんが、多くの場合、それらの必要性を排除します。 より複雑な場合、o42aはマクロメカニズムを提供します(これも独特ですが、それ以外はどうですか?)。 ただし、この記事ではマクロとメタプログラミングの可能性については説明しません。



上記の要約



o42aには、まだ話していないことがたくさんあります。



そしてもちろん、私は多くの詳細を省略しました。 これは将来の記事の資料です。



ただし、この記事の目的は、概念とその実装を紹介することです。 すべてを明確に説明したいと思います。



したがって、言語の主要で唯一のセマンティックユニットは、多くのものを置き換えることができるオブジェクトです。



冗長性と正規化



すべてを支払う必要があります。 そして、「すべてがオブジェクトである」アプローチの価格は高いです。



最も単純な表現

 a + b
      
      



ここa



b



b



は追加するだけの整数で、実際にはオブジェクトのヒープであることがわかります。

 Integers: add ( Left operand = a Right operand = b )
      
      



それらの値を要求するためだけに構築する必要があります(フィールドのオーバーロードによって継承されます)。



ただし、これには予期しないことは何もありません。 マシン表現と人間の理解との不一致が、このマシン表現の深刻な冗長性につながることはすぐに明らかになりました。 もちろん、「額」に気付かない限り。 しかし、これは必要ありません。



冗長性は排除できます。 このプロセスは正規化と呼ばれます。 彼のおかげで、上記の例は2つの数字を追加してコンパイルされます。 結局、これが単なる追加であると人が理解すれば、コンパイラはこれを教えることができます。



正規化手法は非常に重要です。 そして、これらの技術の実装はまだ始まったばかりです。 ただし、このような単純なケースでは、すべてが機能します。 これは、たとえば、正規化をオンおよびオフにしてo42aテストスイートをコンパイルすることで確認できます( o42ac -normalize=0



)。 実行可能ファイルのサイズは4倍異なります。



LLVM IRの「Hello、World!」プログラムは次のとおりです。

hello_world.ll
 ; ModuleID = 'hello_world' target datalayout = "Ep:64:64:64-S0-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f16:16:16-f32:32:32-f64:64:64-f128:128:128-v64:64:64-v128:128:128-a0:0:64" target triple = "x86_64-pc-linux-gnu" %o42a_val_t = type { i32, i32, i64 } @CONST.STRING.1 = private constant %o42a_val_t { i32 6145, i32 13, i64 ptrtoint ([13 x i8]* @DATA.STRING.0 to i64) } @CONST.STRING.2 = private constant %o42a_val_t { i32 1, i32 1, i64 10 } @DATA.STRING.0 = private constant [13 x i8] c"Hello, World!" define i32 @main(i32, i8*) nounwind { main: call void @o42a_init() nounwind call void @o42a_io_print_str(%o42a_val_t* @CONST.STRING.1) nounwind call void @o42a_io_print_str(%o42a_val_t* @CONST.STRING.2) nounwind ret i32 0 } declare void @o42a_init() declare void @o42a_io_print_str(%o42a_val_t*)
      
      





同意します、ここには余計なものはほとんどありません。 そして、オブジェクトのような単一の複雑な構造ではありません。 しかし、いくつかはプログラムに関与しています。



「正規化」という名前は意図的に選択されました。 常に適切なヒューリスティックではなく、多くの場合誤った仮定に基づく「最適化」とは対照的に、正規化は、最小の冗長性の原則に従って達成される予測可能な結果を​​意味します。 また、言語エンティティの数を制限する言語の厳密なセマンティクスは、正規化の手法とルールの実装に非常に役立ちます。 この問題に数学的理論をもたらすことができれば...



正規化の本質は、ソースの構成に従って実行可能コードを生成する代わりに、コンパイラがこのオブジェクトまたはそのオブジェクトがプログラムでどのように使用されているかを把握しようとし、この知識に従って、まずユニバーサル「オブジェクト」を最小限の冗長性に単純化することです実行エンティティ(定数、実行可能コードのブロック、関数、またはいくつかの関数の1つへのポインター、構造...)および実行可能コードを既に生成します。



考えてみると、正規化はプログラムの開発プロセスを根本的に変えます。 プログラムインターフェイスを事前に検討し、何かをクラス、関数、そのパラメーターまたは他の何かとして実装するかどうかを決定する必要がある場合、正規化コンパイラーは、すでに記述されたプログラムに基づいて、その方法を正確に把握して、独自にそのような決定を行います別の実体が実際に使用されて使用される べきではありません。 正規化コンパイラの観点から見ると、前提に基づいて事前に作成されたプログラムインターフェイスに関するアーキテクチャ上の決定は、時期尚早な最適化にすぎません。



しかし、正規化に加えて、他の何かが実装されています:



現状と展望



o42aプロジェクトは、過去3年半の間、フルタイムで1人で実施されています。 ですから、私はこの仕事を真剣に受け止めていることを理解しなければなりません。



このプロジェクトは学術的なものとして考えられたことはなく、実用的な汎用プログラミング言語とそのプラットフォームの作成を目指しています。 私の最初のアイデアは、私がまだ学生だったずっと前に思いつきましたが、新しいプロジェクトを開発し、古いプロジェクトをサポートする豊富な経験を持つ言語をすでに作成し始めました。 したがって、言語の概念そのもの、言語に関するすべての決定、および実際の実装は、実際の経験の成果であり、抽象的な理論ではありません。



プロジェクト開発は、プロトタイプの実装段階です。 コンパイラはJavaで記述されており、LLVMを使用して実行可能コードを生成します。 ソースコード 、GPLv3 +ライセンス(LGPLv3 +のランタイムライブラリ)で利用できますこのプロジェクトには、かなり厄介な英語のドキュメントを備えたWebサイトと、まだ空のフォーラムがあります(明白な理由から直接リンクは提供していません)。



o42a-0.2.4の現在のバージョンには、13万行を超えるソースコードが含まれています。コンパイラはまだ非常に壊れやすく、ライブラリはほとんど存在しません。ターゲットプラットフォームはGNU / Linux x86_64であり、他のプラットフォームはテストされていません。



今後数ヶ月で、コレクションライブラリとI / Oライブラリを実装する予定です。同時に、コンパイラーをデバッグし、言語自体に関する残りの質問を解決します。また、Rosetta Codeを使用して例を作成します。バージョン0.3.0は既に何らかの形で使用できると想定されています。



将来の見通しでは、すべてがかなり霧に覆われています。最初は、開発にそれほど時間がかかるとは思っていませんでしたが、今では作業量について十分に理解しています。ある人にとっては、これは非常に多く、私が近年生きてきた節約は使い果たされています。そのため、開発が深刻なオフィスの「翼の下」に置かれていなければ、ネイティブのoDeskでフリーランスに戻らなければならないため、開発は遅くなります。まだ寄付をお願いするつもりはありません。それはどういうわけか軽薄です:あなたはまともなお金を集めることはありませんが、義務があります。



誰もがそのような野心的なプロジェクトを行うことを真剣に望んでいる場合-お問い合わせください。私は単純なプログラマーですが、このモンスターを収益化するためのアイデアを思いつくことができます。これはウェブの2つのゼロ価格の10k-want-milen-startupではないことに注意してください。これらは、深刻なマーケティングを含む、深刻な長期投資です。



一般にオープンソース、特にo42aプロジェクトの利益のために無料で働きたい場合、o42aに直接関連せず、スタンドアロンプ​​ロジェクト(純粋なCの必要なライブラリ)として有用なタスクを含むいくつかのタスクを見つけるでしょう。



開発に資金を提供する方法について考えている場合は、共有してください。



All Articles