11

それで、LuvvieScript の進歩を開始し、Twitter ですべてが少し始まりました... https://twitter.com/gordonguthrie/status/389659700741943296

Anthony Ramine https://twitter.com/nokusuは、私のやり方が間違っていて、Erlang AST ではなく Core Erlang を介して Erlang から JavaScript にコンパイルするべきだと指摘しました。これは私にとって説得力のある選択肢ですが、魅力的ではありません... Twitterはその議論の適切な媒体ではないので、ここに書いてアドバイスを得ようと思いました.

戦略概要

LuvvieScript には 3 つのコア要件があります。

  • 同じパフォーマンスの Javascript にコンパイルされる Erlang の有効なサブセット
  • Javascript ではなく LuvvieScript でブラウザーでデバッグできるように、完全なソース マップ
  • LuvvieScript モジュールを実行するための「ランタイム」クライアント側 JavaScript 環境 (サーバー側通信を使用) (一種のページ内スーパーバイザ...)

これらのオプションの 3 番目は、この議論の範囲外ですが、最初の 2 つはコアです。

lazy-gits の当然の帰結があります - 私はできるだけ多くの Erlang および Javascript 構文ツール (レクサー、パーサー、トークナイザー、AST 変換など) を使用し、最小限のコードを記述したいと考えています。

現在の考え方

コードが現在次の構造として書かれている方法:

基本的に、次のような Erlang AST を取得します。

 [{function,
      {19,{1,9}},
      atom1_fn,0,
      [{clause,
           {19,none},
           [],
           [[]],
           [{match,
                {20,none},
                [{var,{20,{5,6}},'D'}],
                [{atom,{20,{11,15}},blue}]},
            {var,{21,{5,6}},'D'}]}]}]},

次に、次のような Javascript JSON AST に変換します。

{
    "type": "Program",
    "body": [
        {
            "type": "VariableDeclaration",
            "declarations": [
                {
                    "type": "VariableDeclarator",
                    "id": {
                        "type": "Identifier",
                        "name": "answer",
                        "loc": {
                            "start": {
                                "line": 2,
                                "column": 4
                            },
                            "end": {
                                "line": 2,
                                "column": 10
                            }
                        }
                    },
                    "init": {
                        "type": "BinaryExpression",
                        "operator": "*",
                        "left": {
                            "type": "Literal",
                            "value": 6,
                            "raw": "6",
                            "loc": {
                                "start": {
                                    "line": 2,
                                    "column": 13
                                },
                                "end": {
                                    "line": 2,
                                    "column": 14
                                }
                            }
                        },
                        "right": {
                            "type": "Literal",
                            "value": 7,
                            "raw": "7",
                            "loc": {
                                "start": {
                                    "line": 2,
                                    "column": 17
                                },
                                "end": {
                                    "line": 2,
                                    "column": 18
                                }
                            }
                        },
                        "loc": {
                            "start": {
                                "line": 2,
                                "column": 13
                            },
                            "end": {
                                "line": 2,
                                "column": 18
                            }
                        }
                    },
                    "loc": {
                        "start": {
                            "line": 2,
                            "column": 4
                        },
                        "end": {
                            "line": 2,
                            "column": 18
                        }
                    }
                }
            ],
            "kind": "var",
            "loc": {
                "start": {
                    "line": 2,
                    "column": 0
                },
                "end": {
                    "line": 2,
                    "column": 19
                }
            }
        }
    ],
    "loc": {
        "start": {
            "line": 2,
            "column": 0
          },
        "end": {
            "line": 2,
            "column": 19
           }
    }
}

エル プロブレモ

Anthony の主張はよくできています - Core Erlang は Erlang よりも単純化されたより正規の言語であり、単純な Erlang よりも Javascript へのトランスパイルがより容易になるはずですが、十分に文書化されていません。

Core Erlang の AST のような表現を簡単に取得できます。

{c_module,[],
    {c_literal,[],basic_types},
    [{c_var,[],{atom1_fn,0}},
     {c_var,[],{atom2_fn,0}},
     {c_var,[],{bish_fn,1}},
     {c_var,[],{boolean_fn,0}},
     {c_var,[],{float_fn,0}},
     {c_var,[],{int_fn,0}},
     {c_var,[],{module_info,0}},
     {c_var,[],{module_info,1}},
     {c_var,[],{string_fn,0}}],
    [],
    [{{c_var,[],{int_fn,0}},{c_fun,[],[],{c_literal,[],1}}},
     {{c_var,[],{float_fn,0}},{c_fun,[],[],{c_literal,[],2.3}}},
     {{c_var,[],{boolean_fn,0}},{c_fun,[],[],{c_literal,[],true}}},
     {{c_var,[],{atom1_fn,0}},{c_fun,[],[],{c_literal,[],blue}}},
     {{c_var,[],{atom2_fn,0}},{c_fun,[],[],{c_literal,[],'Blue 4 U'}}},
     {{c_var,[],{string_fn,0}},{c_fun,[],[],{c_literal,[],"string theory"}}},
     {{c_var,[],{bish_fn,1}},
      {c_fun,[],
          [{c_var,[],'_cor0'}],
          {c_case,[],
              {c_var,[],'_cor0'},
              [{c_clause,[],
                   [{c_literal,[],bash}],
                   {c_literal,[],true},
                   {c_literal,[],berk}},
               {c_clause,[],
                   [{c_literal,[],bosh}],
                   {c_literal,[],true},
                   {c_literal,[],bork}},
               {c_clause,
                   [compiler_generated],
                       [{c_var,[],'_cor1'}],
                   {c_literal,[],true},
                   {c_primop,[],
                       {c_literal,[],match_fail},
                       [{c_tuple,[],
                            [{c_literal,[],case_clause},
                             {c_var,[],'_cor1'}]}]}}]}}},
     {{c_var,[],{module_info,0}},
      {c_fun,[],[],
          {c_call,[],
              {c_literal,[],erlang},
              {c_literal,[],get_module_info},
              [{c_literal,[],basic_types}]}}},
     {{c_var,[],{module_info,1}},
      {c_fun,[],
          [{c_var,[],'_cor0'}],
          {c_call,[],
              {c_literal,[],erlang},
              {c_literal,[],get_module_info},
              [{c_literal,[],basic_types},{c_var,[],'_cor0'}]}}}]}

ただし、行の列/番号はありません。したがって、JS を生成する AST を取得できますが、決定的に SourceMap は取得できません。

質問 1必要な行情報を取得するにはどうすればよいですか - (「通常の」Erlang トークンから列情報を取得できます...)

Erlang Core は、生成プロセスにおいて通常の Erlang とは少し異なります。これは、関数呼び出しで変数名を独自の内部変数に置き換え始めるためです。これにより、ソース マップの問題も発生します。例として、次の Erlang 節があります。

bish_fn(A) ->
    case A of
        bash -> berk;
        bosh -> bork
    end.

Erlang AST は名前を適切に保持します。

 [{function,
      {31,{1,8}},
      bish_fn,1,
      [{clause,
           {31,none},
           [{var,{31,{11,12}},'A'}],
           [[]],
           [{'case',
                {32,none},
                [{var,{32,{11,12}},'A'}],
                [{clause,
                     {33,none},
                     [{atom,{33,{9,13}},bash}],
                     [[]],
                     [{atom,{34,{13,17}},berk}]},
                 {clause,
                     {35,none},
                     [{atom,{35,{9,13}},bosh}],
                     [[]],
                     [{atom,{36,{13,17}},bork}]}]}]}]}]},

コア Erlang は、関数で呼び出されるパラメーターの名前を既に変更しています。

'bish_fn'/1 =
    %% Line 30
    fun (_cor0) ->
    %% Line 31
    case _cor0 of
      %% Line 32
      <'bash'> when 'true' ->
          'berk'
      %% Line 33
      <'bosh'> when 'true' ->
          'bork'
      ( <_cor1> when 'true' ->
        primop 'match_fail'
            ({'case_clause',_cor1})
        -| ['compiler_generated'] )
    end

質問 2 Core Erlang で変数名を保存またはマップするためにできることはありますか?

質問 3コア Erlang が、Erlang にコンパイルやすく、Erlang コードを変更するツールを作成しやすいように明示的に設計されていることを高く評価しいます。

オプション

コアの erlang コードをフォークしてソース マッピング オプションを追加することもできますが、ここでは怠け者のカードをプレイします...

アップデート

Eric の回答に応えて、Core Erlang のセルレコードを生成する方法を明確にする必要があります。最初に、以下を使用して、プレーンな Erlang をコア erlang にコンパイルします。

c(some_module, to_core)

次に、この関数でcore_scanとからニックネームを使用します。core_parsecompiler.erl

compile(File) ->
    case file:read_file(File) of
        {ok,Bin} ->
            case core_scan:string(binary_to_list(Bin)) of
                {ok,Toks,_} ->
                    case core_parse:parse(Toks) of
                        {ok, Mod} ->
                            {ok, Mod};
                        {error,E} ->
                            {error, {parse, E}}
                    end;
                {error,E,_} ->
                    {error, {scan, E}}
            end;
        {error,E} ->
            {error,{read, E}}
    end.

問題は、そのツールチェーンに注釈付きの AST を発行させる方法です。これらのオプションを自分で追加する必要があると思います:(

4

2 に答える 2

6
  1. 行番号は注釈として提供されます。使用することを強くお勧めするcerlモジュールを見ると、ほとんどすべてが注釈のリストを取っていることがわかります。これらの注釈の 1 つは、行番号を表す飾り気のない数字です。Core AST を直接正しく覚えていて、atom1_fn var が 10 行目にあった場合、AST は次のようになります。

    {c_var,[10],{atom1_fn,0}}

  2. いいえ、すべての簿記を自分で行う必要があります。あなたのためにそれをするものは何もありません。

  3. この質問を理解しているかどうかわかりません。

Anthony が言ったことはすべて Core Erlang について真実でした。これらは、私が Joxa のターゲット言語として Core Erlang を選んだ理由とまったく同じです。そこから私が学んだ教訓は、Core Erlang は非常にターゲットにしやすいターゲット言語である一方で、それを推奨しない 2 つの大きな欠点があるということです。

  1. Dialyzer は、beam ファイルの抽象コード ブロック内の Erlang AST でのみ機能します。Core Erlang にコンパイルするときに、そのような AST をその抽象コード ブロックに入れる方法はありません。したがって、Core Erlang を対象とする場合、Dialyzer は機能しません。これは、正しい仕様属性を生成するかどうかに関係なく当てはまります。

  2. Erlang AST で動作するツールを使用できなくなります。たとえば、Erlang ソースにコンパイルする機能。Core Erlang to/from source コンパイラは非常にバグが多く、単に動作しません。これは、実用的な使用の多くの分野で大きな成果を上げています。

上記の理由から、私は実際に Joxa を Erlang AST にリターゲットする過程にいます。

ところで、このプロジェクトに興味があるかもしれません。https://github.com/5HT/shen . すでに存在し、機能している Erlang AST 用の JavaScript コンパイラです。私はそれについて多くの経験がありませんが。

** 編集: Erlang ソースから生成されたコア erlang AST を実際に見ることができます。これは、コアにコンパイルする方法を学ぶときに非常に役立ちます。ec_compileレポには、erlware_commonsそれを支援するための多くのユーティリティ関数があります。

于 2013-10-18T16:31:23.230 に答える
1

Core Erlang はどのように入手しますか? 私は使用しています

dialyzer_utils:get_core_from_src(File)

ここで、c_let c_variable などと素敵な行番号で素敵な構造を取得します。しかし、c("",[to_core]) を実行したときに得られる Core Erlang とは異なることに気付きました。たとえば、レコード アクセスごとに c_case を取得し、これは c("",[to_core]) によって生成された .core ファイルで最適化されます。

Erlang によって処理される内部構造としてコア Erlang を取得するための推奨されるアプローチは何ですか。

最初に別のことを試しましたが、行番号が設定されていませんでした。

于 2015-01-02T21:09:08.210 に答える