如何将此树结构转换为 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 树结构?的主要内容,如果未能解决你的问题,请参考以下文章

如何设置 D3.js svg 的背景颜色?

如何将此对象转换为 PHP 中的数组

如何将此 apache 重写转换为 nginx?

如何将此功能组件转换为类

如何将此数据编码为 JSON 中的父/子结构

如何通过按下按钮上的文本输入搜索此树视图中的特定节点?