通过Core Erlang编译Erlang到Javascript

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

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

战略概述

LuvvieScript有三个核心要求:

  • Erlang的一个有效子集,它编译为相同和高效的Javascript
  • 一个完整的源代码地图,以便它可以在LuvvieScript而不是Javascript的浏览器中调试
  • 一个'运行时'客户端JavaScript环境(带有服务器端通信)来执行LuvvieScript模块(一种页内管理器...)
  • 这些选项中的第三项不属于本次辩论的范围,但前两项是核心。

    有一个懒惰的推论 - 我想尽可能多地使用Erlang和Javascript语法工具(词法分析器,分析器,标记器,AST转换等等),并编写最少量的代码。

    当前的思考

    代码当前写入的方式如下所示:

  • 将代码编译到Erlang AST(具有行号)
  • 标记代码(保留注释和空格)并使用这些标记构建将行/列信息映射到标记的字典
  • 将词典和AST合并为一个行/列AST(有些人想把不同的组合的fns分组)
  • 将这个新的Erlang AST转换为Javascript AST,如SpiderMonkey解析器API所示https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API
  • 使用Javascript使用像brushtail突变尾巴呼叫在JavaScript AST https://github.com/puffnfresh/brushtail
  • 使用像ESCodeGen这样的Javascript utils来发射javascript https://github.com/Constellation/escodegen
  • 基本上我得到一个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
               }
        }
    }
    

    厄尔尼诺问题

    安东尼的观点非常好 - 核心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'}]}}}]}
    

    但没有线col / nos。 所以我可以得到一个可以生成JS的AST - 但不是SourceMaps。

    问题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代码并添加一个源映射选项,但我在这里播放Lazy Man卡......

    更新

    为了回应Eric的回应,我应该澄清我如何生成Core Erlang cerl记录。 我首先使用下面的代码将我的纯Erlang编译为核心erlang:

    c(some_module, to_core)
    

    然后,我在这个从compiler.erl core_parse的函数中使用了core_scancore_parse

    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。 我怀疑我需要自己添加这些选项:(


    1)行号作为注释提供。 如果你看看我真的推荐你使用的cerl模块,你会看到很多都需要注释列表。 其中一个注释是表示行号的未经修改的数字。 如果我没有直接记住核心AST,并且atom1_fn var在第10行。AST看起来如下:

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

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

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

    安东尼说的一切都是关于Core Erlang的。 这些都是我选择Core Erlang作为Joxa目标语言的原因。 我从中得到的教训是,尽管Core Erlang是一种很容易定位的目标语言,但它有两个主要缺点,推荐它们。

    1)透析器只能在beam文件的抽象代码块中使用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。 学习如何编译为核心时,这会有很大的帮助。 ec_compile中的erlware_commons有许多实用功能来帮助解决这个问题。


    你如何获得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处理内部结构的推荐方法是什么?

    我先尝试了其他的东西,但是行号没有设置。

    链接地址: http://www.djcxy.com/p/66285.html

    上一篇: Compiling Erlang To Javascript Via Core Erlang

    下一篇: Evaluation of Erlang recursive function (why does this work)