【发布时间】:2021-12-27 06:10:39
【问题描述】:
我想将 JavaScript 转换成 LinkScript。我是这样开始的:
const acorn = require('acorn')
const fs = require('fs')
const input = fs.readFileSync('./tmp/parse.in.js', 'utf-8')
const jst = acorn.parse(input, {
ecmaVersion: 2021,
sourceType: 'module'
})
fs.writeFileSync('tmp/parse.out.js.json', JSON.stringify(jst, null, 2))
const linkScriptText = generateLinkScriptText(convertToLinkScriptAst(jst))
fs.writeFileSync('tmp/parse.out.link', linkScriptText)
function convertToLinkScriptAst(jst) {
const lst = {}
switch (jst.type) {
case 'Program':
convertProgram(jst, lst)
break
}
return lst
}
function convertProgram(jst, lst) {
lst.zones = []
jst.body.forEach(node => {
switch (node.type) {
case 'VariableDeclaration':
convertVariableDeclaration(node).forEach(vnode => {
lst.zones.push(vnode)
})
break
case 'ExpressionStatement':
break
default: throw JSON.stringify(node)
}
})
}
function convertVariableDeclaration(jst) {
return jst.declarations.map(dec => {
switch (dec.type) {
case 'VariableDeclarator':
return convertVariableDeclarator(jst.kind, dec)
break
default: throw JSON.stringify(dec)
}
})
}
function convertVariableDeclarator(kind, jst) {
return {
type: 'host',
immutable: kind === 'const',
name: jst.id.name,
value: convertVariableValue(jst.init)
}
}
function convertVariableValue(jst) {
if (!jst) return
switch (jst.type) {
case 'Literal':
return convertLiteral(jst)
break
}
}
function convertLiteral(jst) {
switch (typeof jst.value) {
case 'string':
return {
type: 'string',
value: jst.value
}
case 'number':
return {
type: 'number',
value: jst.value
}
default: throw JSON.stringify(jst)
}
}
function generateLinkScriptText(lst) {
const text = []
lst.zones.forEach(zone => {
switch (zone.type) {
case 'host':
generateHost(zone).forEach(line => {
text.push(line)
})
break
}
})
return text.join('\n')
}
function generateHost(lst) {
const text = []
if (lst.value) {
switch (lst.value.type) {
case 'string':
text.push(`host ${lst.name}, text <${lst.value.value}>`)
break
case 'number':
text.push(`host ${lst.name}, size ${lst.value.value}`)
break
}
} else {
text.push(`host ${lst.name}`)
}
return text
}
基本上,您将 JS 解析为 AST,然后以某种方式将此 AST 转换为目标语言的 AST(在本例中为 LinkScript)。然后将输出的 AST 转换为文本。问题是,这样做的一般策略是什么?好像挺难的。
更详细地说,我需要知道您可以在 JavaScript 中创建的所有结构类型,以及您可以在 LinkScript 中创建的所有结构类型,以及一个如何映射到另一个。在我的脑海中,看着 JS,我可以手动找出相应的 LinkScript 应该是什么样子。但尝试以编程方式执行此操作是另一回事,我对我应该采取的一般方法有点迷茫。
首先,尽管我已经做了 10 多年的 JavaScript,但我对 JS AST 并不是很了解。我正计划编写一些示例 sn-ps 代码并使用 acorn 查看 AST 的外观。其次,似乎有太多东西的组合,让人不知所措。
我会继续沿着我在上面开始的这条路走下去吗?还是有更结构化或更规范的方法?如何更好地将问题分解为更易于管理的块?
此外,它并不总是像进行简单的一对一映射那样容易。有时事情的顺序会改变。例如,在 JS 中你可能有:
a = x + y
但在 LinkScript 中,这将是:
call add
bind a, link x
bind b, link y
save a
所以赋值表达式有点颠倒。在其他情况下会变得更加复杂。
因此,就好像我需要研究每种单独的映射类型,并就如何进行这种映射提出详细的计划或算法。然后似乎我需要研究数千种可能的转换/映射类型。所以从这个意义上说,从心理上来说,这似乎是一个非常耗时的问题。
有没有更简单的方法?
很长一段时间(几年?)我一直想这样做,但就像我暗示的那样,这似乎总是一项极其艰巨的任务。我认为这是因为我没有在脑海中清楚地看到我可以接收 AST 的所有不同方式/角度,而且我不知道如何将其归结为我可以看到的东西。
除了弄清楚如何进行每种类型的映射/转换之外,我还应该有一些可以扩展的不错的代码。这通常是我的强项(用简单的 API 编写干净的代码),但在这里我很挣扎,因为是的,我还没有看到完整的画面。
【问题讨论】:
-
我觉得有点内疚,将此标记为“需要更多关注”的问题,因为我理解你的那种不堪重负的感觉。但请注意,Stack Overflow 上还有其他“我如何编写转译器”问题,这些问题在结果方面也表现不佳。所以我认为你不会得到你想要的答案。 Babel 是开源的,从那里开始。我只是认为这个问题太大了,其中不大的部分无异于图书馆推荐,正如马特的回答开始显示的那样。
标签: javascript algorithm parsing abstract-syntax-tree transpiler