我想將 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 不是很了解。我計劃撰寫一些示例代碼片段并查看 AST 使用acorn. 其次,似乎有太多的組合讓人難以抗拒。
我是否繼續沿著我在上面開始的這條路走下去?或者是否有更結構化或紀律性更強的方法?我如何更好地將問題分解為更易于管理的塊?
此外,它并不總是像進行簡單的一對一映射那樣容易。有時事情的順序會改變。例如,在 JS 中你可能有:
a = x y
但在 LinkScript 中,這將是:
call add
bind a, link x
bind b, link y
save a
所以賦值運算式有點顛倒。在其他情況下它會變得更加復雜。
所以就好像我需要研究每種型別的映射,并就如何進行這種映射提出詳細的計劃或演算法。那么似乎我需要研究成千上萬種可能的轉換/映射型別。所以從這個意義上說,在精神上解決這個問題似乎是一個非常耗時的問題。
有更容易的方法嗎?
很長一段時間(幾年?)我一直想這樣做,但這似乎總是像我暗示的那樣極其艱巨的任務。我認為這是因為我沒有在腦海中清楚地看到我可以接收 AST 的所有不同方式/角度,而且我不知道如何將其歸結為我可以看到的東西。
In addition to just figuring out how to do each type of mapping/transformation, I also should have somewhat decent code that I am able to extend. That is usually my strong suit (coming up with clean code with a simple API), but here I am struggling because yeah I don't see the full picture yet.
uj5u.com熱心網友回復:
撰寫轉譯器是一項非常艱巨的作業……但出于各種原因,JavaScript 作業流已經充滿了轉譯器,因此有很多工具可以提供幫助。
如果您的目標語言看起來像 JavaScript,那么您可以將您的轉譯器撰寫為 Babel 的插件:https ://babeljs.io/
否則,也許從jscodeshift開始,它將為您提供一個易于訪問的 AST。
許多開源 javascript 工具,如eslint,也有 javscript 決議器,您可以稍微費力地提取。
另請參閱AST Explorer
一旦你有了一個 AST,你通常會遞回地處理它,可能遵循訪問者模式,將每個 AST 節點轉換為等效的目標結構。然后也許可以通過窺視孔優化來簡化生成的 AST。然后最后序列化它。jscodeshift 帶有一個 javascript 序列化程式,您可以用自己的序列化程式替換它。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/362064.html
標籤:javascript algorithm parsing abstract-syntax-tree transpiler
