mirror of
				https://github.com/fooflington/selfdefined.git
				synced 2025-11-04 07:39:03 +00:00 
			
		
		
		
	
		
			
	
	
		
			217 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			217 lines
		
	
	
		
			6.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								import {types as tt} from "./tokentype"
							 | 
						||
| 
								 | 
							
								import {Parser} from "./state"
							 | 
						||
| 
								 | 
							
								import {has} from "./util"
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const pp = Parser.prototype
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Convert existing expression atom to assignable pattern
							 | 
						||
| 
								 | 
							
								// if possible.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.toAssignable = function(node, isBinding) {
							 | 
						||
| 
								 | 
							
								  if (this.options.ecmaVersion >= 6 && node) {
							 | 
						||
| 
								 | 
							
								    switch (node.type) {
							 | 
						||
| 
								 | 
							
								    case "Identifier":
							 | 
						||
| 
								 | 
							
								    case "ObjectPattern":
							 | 
						||
| 
								 | 
							
								    case "ArrayPattern":
							 | 
						||
| 
								 | 
							
								      break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case "ObjectExpression":
							 | 
						||
| 
								 | 
							
								      node.type = "ObjectPattern"
							 | 
						||
| 
								 | 
							
								      for (let i = 0; i < node.properties.length; i++) {
							 | 
						||
| 
								 | 
							
								        let prop = node.properties[i]
							 | 
						||
| 
								 | 
							
								        if (prop.kind !== "init") this.raise(prop.key.start, "Object pattern can't contain getter or setter")
							 | 
						||
| 
								 | 
							
								        this.toAssignable(prop.value, isBinding)
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								      break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case "ArrayExpression":
							 | 
						||
| 
								 | 
							
								      node.type = "ArrayPattern"
							 | 
						||
| 
								 | 
							
								      this.toAssignableList(node.elements, isBinding)
							 | 
						||
| 
								 | 
							
								      break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case "AssignmentExpression":
							 | 
						||
| 
								 | 
							
								      if (node.operator === "=") {
							 | 
						||
| 
								 | 
							
								        node.type = "AssignmentPattern"
							 | 
						||
| 
								 | 
							
								        delete node.operator
							 | 
						||
| 
								 | 
							
								        // falls through to AssignmentPattern
							 | 
						||
| 
								 | 
							
								      } else {
							 | 
						||
| 
								 | 
							
								        this.raise(node.left.end, "Only '=' operator can be used for specifying default value.")
							 | 
						||
| 
								 | 
							
								        break
							 | 
						||
| 
								 | 
							
								      }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case "AssignmentPattern":
							 | 
						||
| 
								 | 
							
								      if (node.right.type === "YieldExpression")
							 | 
						||
| 
								 | 
							
								        this.raise(node.right.start, "Yield expression cannot be a default value")
							 | 
						||
| 
								 | 
							
								      break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case "ParenthesizedExpression":
							 | 
						||
| 
								 | 
							
								      node.expression = this.toAssignable(node.expression, isBinding)
							 | 
						||
| 
								 | 
							
								      break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    case "MemberExpression":
							 | 
						||
| 
								 | 
							
								      if (!isBinding) break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    default:
							 | 
						||
| 
								 | 
							
								      this.raise(node.start, "Assigning to rvalue")
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return node
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Convert list of expression atoms to binding list.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.toAssignableList = function(exprList, isBinding) {
							 | 
						||
| 
								 | 
							
								  let end = exprList.length
							 | 
						||
| 
								 | 
							
								  if (end) {
							 | 
						||
| 
								 | 
							
								    let last = exprList[end - 1]
							 | 
						||
| 
								 | 
							
								    if (last && last.type == "RestElement") {
							 | 
						||
| 
								 | 
							
								      --end
							 | 
						||
| 
								 | 
							
								    } else if (last && last.type == "SpreadElement") {
							 | 
						||
| 
								 | 
							
								      last.type = "RestElement"
							 | 
						||
| 
								 | 
							
								      let arg = last.argument
							 | 
						||
| 
								 | 
							
								      this.toAssignable(arg, isBinding)
							 | 
						||
| 
								 | 
							
								      if (arg.type !== "Identifier" && arg.type !== "MemberExpression" && arg.type !== "ArrayPattern")
							 | 
						||
| 
								 | 
							
								        this.unexpected(arg.start)
							 | 
						||
| 
								 | 
							
								      --end
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								    if (isBinding && last && last.type === "RestElement" && last.argument.type !== "Identifier")
							 | 
						||
| 
								 | 
							
								      this.unexpected(last.argument.start)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  for (let i = 0; i < end; i++) {
							 | 
						||
| 
								 | 
							
								    let elt = exprList[i]
							 | 
						||
| 
								 | 
							
								    if (elt) this.toAssignable(elt, isBinding)
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return exprList
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Parses spread element.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.parseSpread = function(refDestructuringErrors) {
							 | 
						||
| 
								 | 
							
								  let node = this.startNode()
							 | 
						||
| 
								 | 
							
								  this.next()
							 | 
						||
| 
								 | 
							
								  node.argument = this.parseMaybeAssign(false, refDestructuringErrors)
							 | 
						||
| 
								 | 
							
								  return this.finishNode(node, "SpreadElement")
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.parseRest = function(allowNonIdent) {
							 | 
						||
| 
								 | 
							
								  let node = this.startNode()
							 | 
						||
| 
								 | 
							
								  this.next()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  // RestElement inside of a function parameter must be an identifier
							 | 
						||
| 
								 | 
							
								  if (allowNonIdent) node.argument = this.type === tt.name ? this.parseIdent() : this.unexpected()
							 | 
						||
| 
								 | 
							
								  else node.argument = this.type === tt.name || this.type === tt.bracketL ? this.parseBindingAtom() : this.unexpected()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  return this.finishNode(node, "RestElement")
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Parses lvalue (assignable) atom.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.parseBindingAtom = function() {
							 | 
						||
| 
								 | 
							
								  if (this.options.ecmaVersion < 6) return this.parseIdent()
							 | 
						||
| 
								 | 
							
								  switch (this.type) {
							 | 
						||
| 
								 | 
							
								  case tt.name:
							 | 
						||
| 
								 | 
							
								    return this.parseIdent()
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  case tt.bracketL:
							 | 
						||
| 
								 | 
							
								    let node = this.startNode()
							 | 
						||
| 
								 | 
							
								    this.next()
							 | 
						||
| 
								 | 
							
								    node.elements = this.parseBindingList(tt.bracketR, true, true)
							 | 
						||
| 
								 | 
							
								    return this.finishNode(node, "ArrayPattern")
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  case tt.braceL:
							 | 
						||
| 
								 | 
							
								    return this.parseObj(true)
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  default:
							 | 
						||
| 
								 | 
							
								    this.unexpected()
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.parseBindingList = function(close, allowEmpty, allowTrailingComma, allowNonIdent) {
							 | 
						||
| 
								 | 
							
								  let elts = [], first = true
							 | 
						||
| 
								 | 
							
								  while (!this.eat(close)) {
							 | 
						||
| 
								 | 
							
								    if (first) first = false
							 | 
						||
| 
								 | 
							
								    else this.expect(tt.comma)
							 | 
						||
| 
								 | 
							
								    if (allowEmpty && this.type === tt.comma) {
							 | 
						||
| 
								 | 
							
								      elts.push(null)
							 | 
						||
| 
								 | 
							
								    } else if (allowTrailingComma && this.afterTrailingComma(close)) {
							 | 
						||
| 
								 | 
							
								      break
							 | 
						||
| 
								 | 
							
								    } else if (this.type === tt.ellipsis) {
							 | 
						||
| 
								 | 
							
								      let rest = this.parseRest(allowNonIdent)
							 | 
						||
| 
								 | 
							
								      this.parseBindingListItem(rest)
							 | 
						||
| 
								 | 
							
								      elts.push(rest)
							 | 
						||
| 
								 | 
							
								      if (this.type === tt.comma) this.raise(this.start, "Comma is not permitted after the rest element")
							 | 
						||
| 
								 | 
							
								      this.expect(close)
							 | 
						||
| 
								 | 
							
								      break
							 | 
						||
| 
								 | 
							
								    } else {
							 | 
						||
| 
								 | 
							
								      let elem = this.parseMaybeDefault(this.start, this.startLoc)
							 | 
						||
| 
								 | 
							
								      this.parseBindingListItem(elem)
							 | 
						||
| 
								 | 
							
								      elts.push(elem)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								  return elts
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.parseBindingListItem = function(param) {
							 | 
						||
| 
								 | 
							
								  return param
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Parses assignment pattern around given atom if possible.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.parseMaybeDefault = function(startPos, startLoc, left) {
							 | 
						||
| 
								 | 
							
								  left = left || this.parseBindingAtom()
							 | 
						||
| 
								 | 
							
								  if (this.options.ecmaVersion < 6 || !this.eat(tt.eq)) return left
							 | 
						||
| 
								 | 
							
								  let node = this.startNodeAt(startPos, startLoc)
							 | 
						||
| 
								 | 
							
								  node.left = left
							 | 
						||
| 
								 | 
							
								  node.right = this.parseMaybeAssign()
							 | 
						||
| 
								 | 
							
								  return this.finishNode(node, "AssignmentPattern")
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								// Verify that a node is an lval — something that can be assigned
							 | 
						||
| 
								 | 
							
								// to.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								pp.checkLVal = function(expr, isBinding, checkClashes) {
							 | 
						||
| 
								 | 
							
								  switch (expr.type) {
							 | 
						||
| 
								 | 
							
								  case "Identifier":
							 | 
						||
| 
								 | 
							
								    if (this.strict && this.reservedWordsStrictBind.test(expr.name))
							 | 
						||
| 
								 | 
							
								      this.raiseRecoverable(expr.start, (isBinding ? "Binding " : "Assigning to ") + expr.name + " in strict mode")
							 | 
						||
| 
								 | 
							
								    if (checkClashes) {
							 | 
						||
| 
								 | 
							
								      if (has(checkClashes, expr.name))
							 | 
						||
| 
								 | 
							
								        this.raiseRecoverable(expr.start, "Argument name clash")
							 | 
						||
| 
								 | 
							
								      checkClashes[expr.name] = true
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  case "MemberExpression":
							 | 
						||
| 
								 | 
							
								    if (isBinding) this.raiseRecoverable(expr.start, (isBinding ? "Binding" : "Assigning to") + " member expression")
							 | 
						||
| 
								 | 
							
								    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  case "ObjectPattern":
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < expr.properties.length; i++)
							 | 
						||
| 
								 | 
							
								      this.checkLVal(expr.properties[i].value, isBinding, checkClashes)
							 | 
						||
| 
								 | 
							
								    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  case "ArrayPattern":
							 | 
						||
| 
								 | 
							
								    for (let i = 0; i < expr.elements.length; i++) {
							 | 
						||
| 
								 | 
							
								      let elem = expr.elements[i]
							 | 
						||
| 
								 | 
							
								      if (elem) this.checkLVal(elem, isBinding, checkClashes)
							 | 
						||
| 
								 | 
							
								    }
							 | 
						||
| 
								 | 
							
								    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  case "AssignmentPattern":
							 | 
						||
| 
								 | 
							
								    this.checkLVal(expr.left, isBinding, checkClashes)
							 | 
						||
| 
								 | 
							
								    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  case "RestElement":
							 | 
						||
| 
								 | 
							
								    this.checkLVal(expr.argument, isBinding, checkClashes)
							 | 
						||
| 
								 | 
							
								    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  case "ParenthesizedExpression":
							 | 
						||
| 
								 | 
							
								    this.checkLVal(expr.expression, isBinding, checkClashes)
							 | 
						||
| 
								 | 
							
								    break
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								  default:
							 | 
						||
| 
								 | 
							
								    this.raise(expr.start, (isBinding ? "Binding" : "Assigning to") + " rvalue")
							 | 
						||
| 
								 | 
							
								  }
							 | 
						||
| 
								 | 
							
								}
							 |