11

所以开始在 LuvvieScript 上取得进展,然后一切都在 Twitter 上开始了...... https://twitter.com/gordonguthrie/status/389659700741943296

Anthony Ramine https://twitter.com/nokusu指出我做错了,我应该通过 Core Erlang 而不是 Erlang AST 从 Erlang 编译到 JavaScript。这对我来说既是一个引人注目但又不吸引人的选择...... Twitter 不是该讨论的正确媒介,我想我会在这里写下来并获得一些建议。

战略概述

LuvvieScript 具有三个核心要求:

  • Erlang 的有效子集,可编译为相同且高性能的 Javascript
  • 一个完整的 Source Map,以便可以在浏览器中使用 LuvvieScript 而不是 Javascript 进行调试
  • 一个“运行时”客户端 javascript 环境(带有服务器端通信),用于执行 LuvvieScript 模块(一种页内主管......)

这些选项中的第三个有点超出了本次辩论的范围,但前两个是核心。

有一个惰性 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,但它的文档记录不是很好。

我可以很容易地获得类似 AST 的 Core Erlang 表示:

{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——但关键不是 SourceMaps。

问题 1如何获取我需要的行信息 - (我已经可以从“普通”Erlang 令牌中获取列信息......)

Erlang Core 在生产过程中与普通 Erlang 略有不同,因为它开始将函数调用中的变量名替换为自己的内部变量名,这也会导致一些 Source Map 问题。一个例子是这个 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}]}]}]}]}]},

Core 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我很欣赏 Core Erlang 的明确设计是为了让编译Erlang 和编写改变 Erlang 代码的工具变得容易——但问题真的会让从Erlang编译出来更容易吗?

选项

我可以分叉核心 erlang 代码并添加源映射选项,但我在这里玩Lazy Man卡......

更新

作为对 Eric 的回应,我应该澄清我是如何生成 Core Erlang cerl 记录的。我首先使用以下方法将我的普通 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 模块,您会发现几乎所有内容都需要一个注释列表。其中一个注释是代表行号的朴素数字。如果我没记错Core AST,并且atom1_fn var在第10行。AST看起来如下:

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

  2. 不,你必须自己做所有的簿记。没有什么可以为你做的。

  3. 我不确定我是否理解这个问题。

Anthony 所说的关于 Core Erlang 的一切都是真实的。这正是我选择 Core Erlang 作为 Joxa 目标语言的原因。我从中学到的教训是,虽然 Core Erlang 是一种非常容易定位的目标语言,但它有两个主要缺点,建议不要使用它。

  1. Dialyzer 仅适用于光束文件的抽象代码块中的 Erlang AST。在编译为 Core Erlang 时,无法将这样的 AST 放入抽象代码块中。因此,如果您以 Core Erlang 为目标,Dialyzer 将无法为您工作。无论您是否生成正确的规范属性,这都是正确的。

  2. 你失去了使用在 Erlang AST 上工作的工具。例如,编译为 Erlang Source 的能力。Core Erlang 到/从源代码编译器是非常错误的,根本不工作。这是许多实用领域的重大胜利。

由于上述原因,我实际上正在将 Joxa 重新定位到 Erlang AST。

顺便说一句,你可能对这个项目感兴趣。https://github.com/5HT/shen。它是一个已经存在并且正在运行的 Erlang AST 的 JavaScript 编译器。虽然我没有太多的经验。

** 编辑:您实际上可以看到从 Erlang 源生成的核心 erlang AST。这在学习如何编译为核心时有很大帮助。ec_compileerlware_commonsrepo 中有很多实用功能可以帮助解决这个问题。

于 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 文件中进行了优化。

将 Core Erlang 作为 Erlang 处理的内部结构的推荐方法是什么。

我先尝试了其他方法,但未设置行号。

于 2015-01-02T21:09:08.210 回答