Home > OS >  How do you flatten a sequence of assignments in a JavaScript AST?
How do you flatten a sequence of assignments in a JavaScript AST?

Time:11-23

Given this expression in JavaScript:

ipad[15] = opad[15] = some[12] = some[13] = undefined

I get this AST (from acornjs):

{
  "type": "Program",
  "body": [
    {
      "type": "AssignmentExpression",
      "left": {
        "type": "MemberExpression",
        "object": {
          "type": "Identifier",
          "start": 3943,
          "end": 3947,
          "name": "ipad"
        },
        "property": {
          "type": "Literal",
          "start": 3948,
          "end": 3950,
          "value": 15,
          "raw": "15"
        },
        "computed": true
      },
      "right": {
        "type": "AssignmentExpression",
        "left": {
          "type": "MemberExpression",
          "object": {
            "type": "Identifier",
            "start": 3954,
            "end": 3958,
            "name": "opad"
          },
          "property": {
            "type": "Literal",
            "start": 3959,
            "end": 3961,
            "value": 15,
            "raw": "15"
          },
          "computed": true
        },
        "right": {
          "type": "AssignmentExpression",
          "left": {
            "type": "MemberExpression",
            "object": {
              "type": "Identifier",
              "start": 3965,
              "end": 3969,
              "name": "some"
            },
            "property": {
              "type": "Literal",
              "start": 3970,
              "end": 3972,
              "value": 12,
              "raw": "12"
            },
            "computed": true
          },
          "right": {
            "type": "AssignmentExpression",
            "left": {
              "type": "MemberExpression",
              "object": {
                "type": "Identifier",
                "start": 3976,
                "end": 3980,
                "name": "some"
              },
              "property": {
                "type": "Literal",
                "start": 3981,
                "end": 3983,
                "value": 13,
                "raw": "13"
              },
              "computed": true
            },
            "right": {
              "type": "Identifier",
              "start": 3987,
              "end": 3996,
              "name": "undefined"
            },
            "operator": "="
          },
          "operator": "="
        },
        "operator": "="
      },
      "operator": "="
    }
  ]
}

How can I convert this AST to something that when reserialized would produce:

ipad[15] = undefined
opad[15] = undefined
some[12] = undefined
some[13] = undefined

Basically, flattening the AST. How can it be done? I have been thinking about this for hours and trying to modify this source code to get it working, but it's a bit mind bending.

Each AssignmentExpression has a right property. So I feel like just setting them at the top level to the last right item would work, but it's for some reason eluding me.

I am logging in that normalize_AssignmentExpression function, and it is showing:

RIGHT Node { type: 'Identifier', start: 3987, end: 3996, name: 'undefined' }
RIGHT {
  type: 'AssignmentExpression',
  left: {
    type: 'MemberExpression',
    object: Node { type: 'Identifier', start: 3976, end: 3980, name: 'some' },
    property: Node {
      type: 'Literal',
      start: 3981,
      end: 3983,
      value: 13,
      raw: '13'
    },
    computed: true
  },
  right: Node {
    type: 'Identifier',
    start: 3987,
    end: 3996,
    name: 'undefined'
  },
  operator: '='
}
RIGHT {
  type: 'AssignmentExpression',
  left: {
    type: 'MemberExpression',
    object: Node { type: 'Identifier', start: 3965, end: 3969, name: 'some' },
    property: Node {
      type: 'Literal',
      start: 3970,
      end: 3972,
      value: 12,
      raw: '12'
    },
    computed: true
  },
  right: {
    type: 'AssignmentExpression',
    left: {
      type: 'MemberExpression',
      object: [Node],
      property: [Node],
      computed: true
    },
    right: Node {
      type: 'Identifier',
      start: 3987,
      end: 3996,
      name: 'undefined'
    },
    operator: '='
  },
  operator: '='
}
RIGHT {
  type: 'AssignmentExpression',
  left: {
    type: 'MemberExpression',
    object: Node { type: 'Identifier', start: 3954, end: 3958, name: 'opad' },
    property: Node {
      type: 'Literal',
      start: 3959,
      end: 3961,
      value: 15,
      raw: '15'
    },
    computed: true
  },
  right: {
    type: 'AssignmentExpression',
    left: {
      type: 'MemberExpression',
      object: [Node],
      property: [Node],
      computed: true
    },
    right: {
      type: 'AssignmentExpression',
      left: [Object],
      right: [Node],
      operator: '='
    },
    operator: '='
  },
  operator: '='
}

Not sure if that helps.

I try to make it like this, but it just outputs one:

function normalize_AssignmentExpression(node, scope) {
  const [left, leftExps] = normalizeProperty(node.type, 'left', node.left.type, node.left, scope)
  let [right, rightExps] = normalizeProperty(node.type, 'right', node.right.type, node.right, scope)
  const exps = [
    ...leftExps,
    ...rightExps
  ]

  let furthestRight = right
  while (furthestRight.type === 'AssignmentExpression') {
    furthestRight = furthestRight.right
  }

  if (left.type === 'ArrayPattern') {
    const assignments = []
    left.elements.forEach(el => {
      assignments.push(
        createAssignmentExpression(
          el,
          createMemberExpression(right, el),
          node.operator
        )
      )
    })

    return [
      assignments,
      exps
    ]
  } else {
    console.log('RIGHT', right)
    const assignment = createAssignmentExpression(left, furthestRight, node.operator)
    return [
      assignment,
      exps
    ]
  }
}

CodePudding user response:

This does it:

function normalize_AssignmentExpression(node, scope) {
  const [left, leftExps] = normalizeProperty(node.type, 'left', node.left.type, node.left, scope)
  let [right, rightExps] = normalizeProperty(node.type, 'right', node.right.type, node.right, scope)
  const exps = [
    ...leftExps,
    ...rightExps
  ]

  let furthestRight = Array.isArray(right) ? right[0] : right
  let rights = Array.isArray(right) ? right.slice(1) : []
  let lefts = [left]
  while (furthestRight.type === 'AssignmentExpression') {
    lefts.push(furthestRight.left)
    furthestRight = furthestRight.right
  }

  if (left.type === 'ArrayPattern') {
    const assignments = []
    left.elements.forEach(el => {
      assignments.push(
        createAssignmentExpression(
          el,
          createMemberExpression(right, el),
          node.operator
        )
      )
    })

    return [
      assignments,
      exps
    ]
  } else {
    const assignments = []
    lefts.forEach(l => {
      const assignment = createAssignmentExpression(l, furthestRight, node.operator)
      assignments.push(assignment)
    })
    assignments.push(...rights)
    return [
      assignments,
      exps
    ]
  }
}
  • Related