Как преобразовать эту древовидную структуру в древовидную структуру JS MemberExpression?

0

Вопрос

Я придумал способ представления выражения 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"
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Теперь то же самое строковое выражение также представлено этой древовидной структурой JS AST для MemberExpression:

{
  "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"
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  }
}

На самом деле я еще не знаю, как упростить проблему таким образом, чтобы решить ее.

Я не знаю, поможет ли это (анализатор acornjs для MemberExpression).

3

Лучший ответ

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));
  }
}

Вы, конечно, можете упростить редуктор, чтобы просто

props.reduce((lhs, rhs) => ({
  "type": "MemberExpression",
  "object": lhs,
  "property": transform(rhs),
  "computed": rhs.form == "nest",
}), transform(base));
2021-11-23 13:35:06

Ни за что! Вы упростили это до самой сути! Я на самом деле думаю, что я тоже только что получил это, я тоже опубликую свой ответ :D
Lance Pollard

Итак, какова была ваша техника, как вы так элегантно ее придумали? Я бы сказал, что это было довольно сложно. В принципе, как мне научиться быть более похожим на то, что вы сделали :)
Lance Pollard

@LancePollard Функциональное мышление и рекурсия гроккинга помогают. Мне было ясно, что это должна быть чистая функция, не использующая стек и цикл (хотя на самом деле я даже не полностью прочитал код вашей попытки). И есть два направления обхода: одно вниз по цепочке свойств, другое во вложенных выражениях. Учитывая, что цепочка линейна, это было бы простым сгибом массива для создания вложенных MemberExpression связанный список, для вложенности необходима рекурсия. Тогда это был просто вопрос о том, уменьшать ли влево или вправо и как в частном случае начать
Bergi
1

Более короткое рекурсивное решение:

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)
2021-11-24 05:25:09
0

Я просто немного решил эту проблему после ответа @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
}
2021-11-23 13:45:28

На других языках

Эта страница на других языках

Italiano
..................................................................................................................
Polski
..................................................................................................................
Română
..................................................................................................................
한국어
..................................................................................................................
हिन्दी
..................................................................................................................
Français
..................................................................................................................
Türk
..................................................................................................................
Česk
..................................................................................................................
Português
..................................................................................................................
ไทย
..................................................................................................................
中文
..................................................................................................................
Español
..................................................................................................................
Slovenský
..................................................................................................................