Cum să scrie un parser pentru a construi JavaScript AST MemberExpression pentru o expresie.b[c.d][e].f[g[h[i.j]]]?

0

Problema

În urma de-a lungul liniilor de Cum se reprezintă în mod clar o.b[c.d][e].f[g[h[i.j]]] ca un obiect copac?, cum ai scrie un algoritm pentru a genera JS AST de exprimare a.b[c.d][e].f[g[h[i.j]]]? Încerc să scriu un parser pentru a genera un fel de obiect structura din această expresie (în mod ideal, mai intuitiv decât JS AST MemberExpression unul, prin urmare, că altă întrebare). Aș vrea să văd cum functioneaza algoritmul pentru a construi JavaScript MemberExpression copac.

În prezent, am avea acest tip de algoritm pentru a genera un fel de copac (dar se pare a fi incorectă în prezent):

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

Rezultatul dorit este aceasta (sau o mai bună, mai intuitiv structura de date, dacă sunteți atât de înclinat pentru a crea unul):

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

Motivul pentru care eu sunt luptă (parțial) e nu-mi place asta MemberExpression structură arborescentă, e înapoi sentiment și nu foarte intuitivă. Deci, dacă ai putea construi un simplu mult mai simplă structură de date care ar fi ideal (care a fost de altă cauză), dar dacă nu, atunci un algoritm pentru a construi acest lucru ar fi să mă duc.

Personal, aș încerca mai degrabă să genereze această structură, ca mi se pare mai intuitiv:

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

Dar nici unul lucrează pentru mine (sau ambele).

Dacă vom merge cu cel de-al doilea, urmatoarea mea problema va fi cum de a converti care structura de date în MemberExpression copac/structura de date :) Dar voi încerca să fac și eu asta în primul rând. Deci, este, probabil, mai bine pentru a construi MemberExpression în această întrebare, atunci nu pot lucra.

1

Cel mai bun răspuns

1
  1. Separat șir în grupuri de obiecte și proprietăți de primul nivel, ca

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

    1. Ia ultimul articol ca proprietate.
    2. Verificați dacă proprietatea începe cu suport apoi setați computed pentru a true și benzi proprietatea din jur paranteze.
    3. Returna un obiect cu
      • type: "MemberExpression" ,
      • object cu obiectul (2.),
      • property cu rezultatul de asteptare funcția principală 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

În alte limbi

Această pagină este în alte limbi

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