通过 Core Erlang 将 Erlang 编译为 Javascript
Posted
技术标签:
【中文标题】通过 Core Erlang 将 Erlang 编译为 Javascript【英文标题】:Compiling Erlang To Javascript Via Core Erlang 【发布时间】:2013-10-27 13:37:48 【问题描述】:所以开始在 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(具有行号) 标记代码(保留 cmets 和空白)并使用这些标记构建一个字典,将行/列信息映射到标记 将字典和 AST 合并以给出行/列 AST(有些人想将不同 arities 的 fns 分组) 将这个新的 Erlang AST 转换为在 SpiderMonkey Parser 中实现的 Javascript AST APIhttps://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API 使用像brushtail这样的Javascript工具来改变Javascript AST中的尾调用https://github.com/puffnfresh/brushtail 使用像 ESCodeGen 这样的 Javascript 工具来发出 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
El Problemo
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 代码的工具 - 但问题真的是它做了 更容易编译out的Erlang?
选项
我可以 fork 核心 erlang 代码并添加源映射选项,但我在这里玩 Lazy Man 卡...
更新
为了回应 Eric 的回复,我应该澄清一下我是如何生成 Core Erlang cerl 记录的。我首先使用以下命令将我的普通 Erlang 编译为核心 Erlang:
c(some_module, to_core)
然后我在这个函数中使用core_scan
和core_parse
,这个函数是从compiler.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。我怀疑我需要自己添加这些选项:(
【问题讨论】:
你应该通过 Core Erlang 具体表示,使用 compile:file(File, [binary,to_core]) 代替。对于列号,您应该只参与 Erlang 开发并将它们变为现实,就像现在应该的那样。 Gordon,你为编译器本身准备了什么样的环境?这也应该在浏览器中运行,还是在完整的 OTP 安装中离线(可能在服务器端)? 【参考方案1】:行号作为注释提供。如果您查看我真正推荐您使用的 cerl 模块,您会发现几乎所有内容都需要一个注释列表。其中一个注释是代表行号的朴素数字。如果我没有记错 Core AST,并且 atom1_fn var 在第 10 行。AST 如下所示:
c_var,[10],atom1_fn,0
不,你必须自己做所有的簿记。没有什么可以为你做的。
我不确定我是否理解这个问题。
Anthony 所说的关于 Core Erlang 的一切都是真的。这正是我选择 Core Erlang 作为 Joxa 目标语言的原因。我从中吸取的教训是,虽然 Core Erlang 是一种非常容易定位的目标语言,但它有两个主要缺点,建议不要使用它。
Dialyzer 仅适用于 beam 文件的抽象代码块中的 Erlang AST。编译到 Core Erlang 时,无法将这样的 AST 放入抽象代码块中。因此,如果您以 Core Erlang 为目标,Dialyzer 将无法为您工作。无论您是否生成正确的规范属性,这都是正确的。
您失去了使用适用于 Erlang AST 的工具。例如,编译为 Erlang Source 的能力。 Core Erlang 到/从源代码编译器是非常错误的,根本不工作。这是许多实用领域的重大胜利。
由于上述原因,我实际上正在将 Joxa 重新定位到 Erlang AST。
顺便说一句,您可能对这个项目感兴趣。 https://github.com/5HT/shen。它是一个已经存在并且正在运行的 Erlang AST 的 JavaScript 编译器。虽然我没有很多经验。
** 编辑:您实际上可以看到从 Erlang 源生成的核心 erlang AST。这在学习如何编译为核心时有很大帮助。 ec_compile
在erlware_commons
repo 中有很多实用功能可以帮助解决这个问题。
【讨论】:
Eric,问题是我正在从 Erlang 编译 - 我不使用 cerl.erl 因为我没有在我的编译器中构建 Core Erlang AST - 这是将 Erlang 转换为JS(见更新) Eric,没有 Dialyzer 仅在 Erlang AST 上工作。事实上,它检索它只是为了将其编译为 Core Erlang,在此基础上完成分析。请参阅模块 dilyzer_utils,IIRC。编译器可以很好地修补以支持新选项 +core_info,该选项将用于将核心 AST 存储到 core_code(“核心”)BEAM 块中。如果您通过 +clint0,Core Erlang 编译器不会因大多数 Core 代码而崩溃。 Eric,当您说“Core Erlang 与源代码编译器之间存在很多错误”时,我假设您的意思是存在编译器无法处理的生成 Core Erlang 代码的情况。除此之外,每次编译到 BEAM 时都会使用 Core Erlang 编译器阶段,但它在涉及到当前从 Erlang 源代码获得的代码时存在一些盲点。除此之外,to_erl 只是当时内部编译器格式的转储,而 from_erl 只是将其读回并继续。 我已经弄清楚如何使用行号获取 Core Erlang - 只需要找到时间重新编写它。【参考方案2】:如何获得 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 处理的推荐方法是什么。
我先尝试了其他方法,但没有设置行号。
【讨论】:
以上是关于通过 Core Erlang 将 Erlang 编译为 Javascript的主要内容,如果未能解决你的问题,请参考以下文章
在 centos 中使用 kerl 安装 erlang 时出现加密错误