如何将此树结构转换为 JS MemberExpression 树结构?
Posted
技术标签:
【中文标题】如何将此树结构转换为 JS MemberExpression 树结构?【英文标题】:How to convert this tree structure into a JS MemberExpression tree structure? 【发布时间】:2022-01-01 23:29:17 【问题描述】:我已经找到了一种使用我自己的树格式来表示表达式a.b[c.d][e].f[g[h[i.j]]]
的方法。该表达式表示为一棵树,如下所示:
"form": "nest",
"link": [
"form": "site",
"name": "a"
,
"form": "site",
"name": "b"
,
"form": "nest",
"link": [
"form": "site",
"name": "c"
,
"form": "site",
"name": "d"
]
,
"form": "nest",
"link": [
"form": "site",
"name": "e"
]
,
"form": "site",
"name": "f"
,
"form": "nest",
"link": [
"form": "site",
"name": "g"
,
"form": "nest",
"link": [
"form": "site",
"name": "h"
,
"form": "nest",
"link": [
"form": "site",
"name": "i"
,
"form": "site",
"name": "j"
]
]
]
]
现在,MemberExpression
的这个 JS AST 树结构也表示相同的字符串表达式:
"type": "MemberExpression",
"object":
"type": "MemberExpression",
"object":
"type": "MemberExpression",
"object":
"type": "MemberExpression",
"object":
"type": "MemberExpression",
"object":
"type": "Identifier",
"name": "a"
,
"property":
"type": "Identifier",
"name": "b"
,
"computed": false
,
"property":
"type": "MemberExpression",
"object":
"type": "Identifier",
"name": "c"
,
"property":
"type": "Identifier",
"name": "d"
,
"computed": false
,
"computed": true
,
"property":
"type": "Identifier",
"name": "e"
,
"computed": true
,
"property":
"type": "Identifier",
"name": "f"
,
"computed": false
,
"property":
"type": "MemberExpression",
"object":
"type": "Identifier",
"name": "g"
,
"property":
"type": "MemberExpression",
"object":
"type": "Identifier",
"name": "h"
,
"property":
"type": "MemberExpression",
"object":
"type": "Identifier",
"name": "i"
,
"property":
"type": "Identifier",
"name": "j"
,
"computed": false
,
"computed": true
,
"computed": true
,
"computed": true
所以这两个树结构代表相同的字符串表达式a.b[c.d][e].f[g[h[i.j]]]
。你会注意到第一个“巢”结构,有两种类型的对象,站点和巢。站点只是一个名称,而嵌套在 JS AST 术语中表示“计算”属性。所以一个巢就像parent[this_is_a_nest[and_another_nest]]
,而parent.site1.site2
。
如何将第一个树结构转换为第二个?
到目前为止我所拥有的还没有真正到达那里,这很令人困惑。
console.log(JSON.stringify(transform(getNest()), null, 2))
function transform(nest)
let i = 0
let stack = []
while (i < nest.link.length)
let object = nest.link[i++]
let property = nest.link[i]
let member =
type: 'MemberExpression'
stack.push(member)
if (object.form === 'nest')
member.object = transform(object)
else
member.object =
type: 'Identifier',
name: object.name
if (property)
if (property.form === 'nest')
member.property = transform(property)
member.computed = true
else
member.property =
type: 'Identifier',
name: property.name
let object = stack.pop()
while (stack.length)
let nextObject = stack.pop()
nextObject.object = object
object = nextObject
return object
function getNest()
return
"form": "nest",
"link": [
"form": "site",
"name": "a"
,
"form": "site",
"name": "b"
,
"form": "nest",
"link": [
"form": "site",
"name": "c"
,
"form": "site",
"name": "d"
]
,
"form": "nest",
"link": [
"form": "site",
"name": "e"
]
,
"form": "site",
"name": "f"
,
"form": "nest",
"link": [
"form": "site",
"name": "g"
,
"form": "nest",
"link": [
"form": "site",
"name": "h"
,
"form": "nest",
"link": [
"form": "site",
"name": "i"
,
"form": "site",
"name": "j"
]
]
]
]
目前还不知道如何简化问题以解决问题。
我不知道this 是否有任何帮助(用于 MemberExpression 的 acornjs 解析器)。
【问题讨论】:
【参考方案1】:应该这样做:
function transform(treeNode)
if (treeNode.form == "site")
return
"type": "Identifier",
"name": treeNode.name,
;
else if (treeNode.form == "nest")
const [base, ...props] = treeNode.link;
console.assert(base.form == "site");
return props.reduce((lhs, rhs) =>
if (rhs.form == "nest")
return
"type": "MemberExpression",
"object": lhs,
"property": transform(rhs), // returns MemberExpression or (if singleton) Identifier
"computed": true,
;
else if (rhs.form == "site")
return
"type": "MemberExpression",
"object": lhs,
"property": transform(rhs), // returns Identifier
"computed": false,
;
, transform(base));
你当然可以把reducer简化成只是
props.reduce((lhs, rhs) => (
"type": "MemberExpression",
"object": lhs,
"property": transform(rhs),
"computed": rhs.form == "nest",
), transform(base));
【讨论】:
不可能!您将其简化为本质!我实际上认为我也刚刚得到它,我也将发布我的答案:D 那么你的技术是什么,你是如何如此优雅地计算出来的?我不得不说这很棘手。基本上我如何学会更像你所做的:) @LancePollard 思考函数式递归和探索式递归会有所帮助。我很清楚它必须是一个纯函数,而不是使用堆栈和循环(尽管实际上我什至没有完全阅读您尝试的代码)。并且有两个遍历方向:一个在属性链中,另一个在嵌套表达式中。鉴于链是线性的,这将是对数组的简单折叠以生成嵌套的MemberExpression
链表,因为嵌套递归是必要的。然后就是reduce left还是right以及如何对begin进行特殊处理的问题【参考方案2】:
我刚刚在@Bergi 的回答之后稍微解决了这个问题,然后才看到它,太兴奋了!
function transform(nest)
let i = 0
let stack = [
type: 'Identifier',
name: nest.link[i++].name
]
while (i < nest.link.length)
const object = stack.shift()
const node = nest.link[i++]
if (node.form === 'nest')
const property = transform(node)
stack.push(
object: object,
property,
computed: true
)
else
let property =
type: 'Identifier',
name: node.name
stack.push(
object: object,
property: property,
computed: false
)
return stack.shift()
输出是:
"object":
"object":
"object":
"object":
"object":
"type": "Identifier",
"name": "a"
,
"property":
"type": "Identifier",
"name": "b"
,
"computed": false
,
"property":
"object":
"type": "Identifier",
"name": "c"
,
"property":
"type": "Identifier",
"name": "d"
,
"computed": false
,
"computed": true
,
"property":
"type": "Identifier",
"name": "e"
,
"computed": true
,
"property":
"type": "Identifier",
"name": "f"
,
"computed": false
,
"property":
"object":
"type": "Identifier",
"name": "g"
,
"property":
"object":
"type": "Identifier",
"name": "h"
,
"property":
"object":
"type": "Identifier",
"name": "i"
,
"property":
"type": "Identifier",
"name": "j"
,
"computed": false
,
"computed": true
,
"computed": true
,
"computed": true
【讨论】:
【参考方案3】:更短的递归解决方案:
function mem_tree(objs)
var o = null;
for (var obj of objs)
if (obj.form === 'site')
o = (o === null) ? type:"Identifier", name:obj.name : type: "MemberExpression", object:o, property:type:"Identifier", name:obj.name, computed:false
else
var r = mem_tree(obj.link);
o = (o === null) ? object:r : type: "MemberExpression", object:o, property:r, computed:true
return o;
var d = 'form': 'nest', 'link': ['form': 'site', 'name': 'a', 'form': 'site', 'name': 'b', 'form': 'nest', 'link': ['form': 'site', 'name': 'c', 'form': 'site', 'name': 'd'], 'form': 'nest', 'link': ['form': 'site', 'name': 'e'], 'form': 'site', 'name': 'f', 'form': 'nest', 'link': ['form': 'site', 'name': 'g', 'form': 'nest', 'link': ['form': 'site', 'name': 'h', 'form': 'nest', 'link': ['form': 'site', 'name': 'i', 'form': 'site', 'name': 'j']]]]
var result = mem_tree(d.link)
【讨论】:
以上是关于如何将此树结构转换为 JS MemberExpression 树结构?的主要内容,如果未能解决你的问题,请参考以下文章