Jak napisać parser do budowy wyrażenia JavaScript AST MemberExpression dla wyrażenia a.b[c.d][e].f[g[h[i.j]]]?

0

Pytanie

Postępując zgodnie z instrukcjami wyświetlanymi o tym, jak jasno przedstawić a.b[c.d][e].f[g[h[i.j]]] w postaci drzewa obiektów?, jak byś napisał algorytm do generowania tego JS AST z wyrażenia a.b[c.d][e].f[g[h[i.j]]]? Próbuję napisać parser do tworzenia jakiejkolwiek struktury obiektu z tej wypowiedzi (najlepiej bardziej intuicyjnego niż JS AST MemberExpression jeden, stąd inna kwestia). Chciałbym zobaczyć, jak działa algorytm wyznaczania JavaScript MemberExpression drzewo.

Obecnie mam taki algorytm do tworzenia jakiegoś drzewa (ale obecnie wydaje się błędna).:

const patterns = [
  [/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*/, 'name'],
  [/^\[/, 'open'],
  [/^\]/, 'close'],
  [/^\./, 'stem']
]

console.log(parsePath('a.b[c.d][e].f[g[h[i.j]]]'))

function parsePath(str) {
  let node
  let nest = []
  let result = nest
  let stack = [nest]
  while (str.length) {
    nest = stack[stack.length - 1]
    p:
    for (let pattern of patterns) {
      let match = str.match(pattern[0])
      if (match) {
        if (pattern[1] === 'name') {
          node = {
            form: `term`,
            name: match[0],
            link: []
          }
          nest.push(node)
        } else if (pattern[1] === 'stem') {
          stack.push(node.link)
        } else if (pattern[1] === 'open') {
          node = {
            form: 'read',
            link: []
          }
          nest.push(node)
          stack.push(node.link)
        } else if (pattern[1] === 'close') {
          stack.pop()
        }

        str = str.substr(match[0].length)
        break p
      }
    }
  }
  return result[0]
}

Pożądany wynik taki (lub lepsza, bardziej intuicyjna struktura danych, jeśli jesteś tak skłonny ją tworzyć).:

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

Powodem, dla którego walczę (częściowo), w tym, że nie podoba mi się to MemberExpression struktura drzewa, to отсталое uczucie i nie bardzo intuicyjne. Tak więc, jeśli można zbudować bardziej prostą i czytelną strukturę danych, która byłaby idealnym (był to inna kwestia), ale jeśli nie, to po prostu łódź aby ją zbudować kazał mi iść dalej.

Osobiście wolałbym spróbować stworzyć tę strukturę, ponieważ uważam że to jest bardziej intuicyjny:

{
  type: 'site',
  site: [
    {
      type: 'term',
      term: 'a'
    },
    {
      type: 'term',
      term: 'b'
    },
    {
      type: 'sink',
      sink: [
        {
          type: 'term',
          term: 'c'
        },
        {
          type: 'term',
          term: 'd'
        }
      ]
    },
    {
      type: 'sink',
      sink: [
        {
          type: 'term',
          term: 'e'
        }
      ]
    },
    {
      type: 'term',
      term: 'f'
    },
    {
      type: 'sink',
      sink: [
        {
          type: 'term',
          term: 'g'
        },
        {
          type: 'sink',
          sink: [
            {
              type: 'term',
              term: 'h'
            },
            {
              type: 'sink',
              sink: [
                {
                  type: 'term',
                  term: 'i'
                },
                {
                  type: 'term',
                  term: 'j'
                }
              ]
            }
          ]
        }
      ]
    }
  ]
}

Ale albo jeden z nich działa na mnie (lub oba).

Jeśli przejdziemy do drugiego, mój następny problem będzie w tym, jak przekształcić tę strukturę danych w MemberExpression drewna/struktura danych :) Ale najpierw spróbuję zrobić to sam. Więc chyba lepiej zbudować wyrażenie MemberExpression w tej kwestii, to będę to odpracować.

1

Najlepsza odpowiedź

1
  1. Podziel wiersz na grupy obiektów i właściwości pierwszego poziomu, np.

    [
        "a",
        "b",
        "[c.d]",
        "[e]",
        "f",
        "[g[h[i.j]]]"
    ]
    
  2. Zobacz obiekt

    1. Weź ostatni przedmiot jako własności.
    2. Sprawdź, rozpoczyna się czy właściwość z nawiasów, a następnie ustaw computed Dla true i usuń właściwość z innych nawiasów.
    3. Odzyskaj obiekt z
      • type: "MemberExpression" ,
      • object z obiektem (2.),
      • property z wynikiem połączenia podstawowe funkcje getAST (1.),
      • computed.

function getAST(string) {

    function getObject(parts) {
        if (parts.length === 1) return { type: "Identifier", name: parts[0] };

        let property = parts.pop(),
            computed = false;

        if (property.startsWith('[')) {
            computed = true;
            property = property.slice(1, -1);
        }

        return {
            type: "MemberExpression",
            object: getObject(parts),
            property: getAST(property),
            computed
        };
    }

    let i = 0,
        dot,
        bracket,
        parts = [];

    while (i < string.length) {
        dot = string.indexOf('.', i);
        bracket = string.indexOf('[', i);

        if (dot !== -1 && (bracket === -1 || dot < bracket)) {
            const temp = string.slice(i, dot);
            if (temp) parts.push(temp);
            i = dot + 1;
            continue;
        }

        if (bracket !== -1 && (dot === -1 || bracket < dot)) {
            const temp = string.slice(i, bracket);
            if (temp) parts.push(temp);
            i = bracket;

            let open = 1,
                j = i;

            while (++j < string.length) {
                if (string[j] === '[') open++;
                if (string[j] === ']') open--;
                if (!open) break;
            }

            j++;
            parts.push(string.slice(i, j));

            i = j;
            continue;
        }
        parts.push(string.slice(i));
        break;
    }

    return getObject(parts);
}

console.log(getAST('a.b[c.d][e].f[g[h[i.j]]]'));
.as-console-wrapper { max-height: 100% !important; top: 0; }

2021-11-24 07:30:42

W innych językach

Ta strona jest w innych językach

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