编译原理笔记-源码学习-语法分析(3)

109 阅读3分钟

parseMaybeConditional

  1. 调用parseExprOps方法获取单个表达式
  2. 调用this.eat(tt.question), 如果后面接了问号, 则此时创建新node, 并将node.test设置为前面解析出来的node
  3. 然后依次调用parseMaybeAssign得到接下来的2个表达式内容. 返回为NodeTypes.ConditionalExpression
  4. 如果没有读到问号, 则此时直接返回为parseExprOps解析到的表达式
/**
 * 只有在parseMaybeAssign中调用
 * 解析 ? : 三元表达式
 * @param {string} forInit 
 * @param {*} refDestructuringErrors 
 * @returns 
 */
pp.parseMaybeConditional = function(forInit, refDestructuringErrors) {
  let startPos = this.start, startLoc = this.startLoc
  let expr = this.parseExprOps(forInit, refDestructuringErrors)
  if (this.checkExpressionErrors(refDestructuringErrors)) return expr
  if (this.eat(tt.question)) {
    let node = this.startNodeAt(startPos, startLoc)
    node.test = expr // ? 前面的内容
    node.consequent = this.parseMaybeAssign() // 第一个表达式
    this.expect(tt.colon) // :
    node.alternate = this.parseMaybeAssign(forInit) // 第二个表达式
    return this.finishNode(node, NodeTypes.ConditionalExpression)
  }
  return expr
}

parseExprOps

  1. 首先调用parseMaybeUnary调出单个变量
  2. 判断expr.type === "ArrowFunctionExpression", 如果是, 则调用parseExprOp继续读出表达式运算
/**
 * 只有parseMaybeConditional调用, 解析表达式
 * @param {string} forInit
 */
pp.parseExprOps = function(forInit, refDestructuringErrors) {
  let startPos = this.start, startLoc = this.startLoc
  let expr = this.parseMaybeUnary(refDestructuringErrors, false, false, forInit)
  if (this.checkExpressionErrors(refDestructuringErrors)) return expr
  return expr.start === startPos && expr.type === "ArrowFunctionExpression" ? expr : this.parseExprOp(expr, startPos, startLoc, -1, forInit)
}

parseMaybeUnary

  1. 首先判断是否有await上下文并且在async内, 如果是, 则调用parseAwait解析表达式
  2. 判断是否存在prefix, 这里是指++/--或者typeof, delete, 如果是, 则新建node, 并且将node.operator设置为操作符, 设置prefix为true, 并且递归调用parseMaybeUnary解析出后面的内容.
    1. 如果是++/--, 则检查一下合法性.
    2. 如果读出来是单个变量名, 并且运算符为delete, 则报错.
    3. 如果读出来是私有变量, 并且运算符为delete, 则报错. 否则返回node
  3. 如果sawUnary为false, 并且为私有变量, 则此时调用parsePrivateIdent解析为私有变量.
  4. 如果以上都不是, 则此时调用parseExprSubscripts解析子表达式, 然后判断是否有后缀运算符, 如果有, 则创建新的节点并设置为UnaryExpression
  5. 最后, 检查是否存在幂运算符**, 如果有则创建一个新的 AST 节点,类型为 BinaryExpression,并返回该节点
/**
 * 读出单个变量或者表达式, 如a, b.c, ccc[0], ddd(), ++a, a++, delete a[1], typeof a
 * @param {*} refDestructuringErrors 
 * @param {boolean} sawUnary 
 * @param {boolean} incDec 
 * @param {*} forInit 
 * @returns 
 */
pp.parseMaybeUnary = function(refDestructuringErrors, sawUnary, incDec, forInit) {
  let startPos = this.start, startLoc = this.startLoc, expr
  if (this.isContextual("await") && this.canAwait) {
    // 如果允许await 则调用parseAwait
    expr = this.parseAwait(forInit)
    sawUnary = true
  } else if (this.type.prefix) {
    // 如果是++/--或者typeof, delete等
    let node = this.startNode(), update = this.type === tt.incDec
    node.operator = this.value
    node.prefix = true
    this.next()
    // 读出需要操作的变量
    node.argument = this.parseMaybeUnary(null, true, update, forInit)
    this.checkExpressionErrors(refDestructuringErrors, true)
    // 如果是++/--, 检查合法性
    if (update) this.checkLValSimple(node.argument)
    else if (this.strict && node.operator === "delete" &&
             node.argument.type === "Identifier")
      // 如果是delete aaa这种类型, 报错       
      this.raiseRecoverable(node.start, "Deleting local variable in strict mode")
    else if (node.operator === "delete" && isPrivateFieldAccess(node.argument))
      // 如果是私有变量, 则报错
      this.raiseRecoverable(node.start, "Private fields can not be deleted")
    else sawUnary = true
    expr = this.finishNode(node, update ? nt.UpdateExpression : nt.UnaryExpression)
  } else if (!sawUnary && this.type === tt.privateId) {
    if (forInit || this.privateNameStack.length === 0) this.unexpected()
    // 解析私有变量
    expr = this.parsePrivateIdent()
    // only could be private fields in 'in', such as #x in obj
    // 在这里私有变量下一个词必须接in, 否则错误
    if (this.type !== tt._in) this.unexpected()
  } else {
    expr = this.parseExprSubscripts(refDestructuringErrors, forInit)
    // 这一段检查没懂
    if (this.checkExpressionErrors(refDestructuringErrors)) return expr
    while (this.type.postfix && !this.canInsertSemicolon()) {
      // ++/--
      let node = this.startNodeAt(startPos, startLoc)
      node.operator = this.value
      node.prefix = false
      node.argument = expr
      this.checkLValSimple(expr)
      this.next()
      expr = this.finishNode(node, nt.UnaryExpression)
    }
  }

  if (!incDec && this.eat(tt.starstar)) {
    if (sawUnary)
      this.unexpected(this.lastTokStart)
    else
      return this.buildBinary(startPos, startLoc, expr, this.parseMaybeUnary(null, false, false, forInit), "**", false)
  } else {
    return expr
  }
}

parseAwait

  1. 调用parseMaybeUnary解析出对应的表达式
/**
 * parseMaybeUnary中调用
 * 如await aaa()或者await promise, 返回AwaitExpression, 并将调用的内容使用parseMaybeUnary解析出来设置为argument
 * @param {*} forInit 
 * @returns 
 */
pp.parseAwait = function(forInit) {
  if (!this.awaitPos) this.awaitPos = this.start

  let node = this.startNode()
  this.next()
  node.argument = this.parseMaybeUnary(null, true, false, forInit)
  return this.finishNode(node, NodeTypes.AwaitExpression)
}

parseExprSubscripts

  1. 首先调用parseExprAtom解析表达式的初始部分
  2. 其次判断是否箭头函数, 如果是, 则直接返回
  3. 如果不是箭头函数, 则调用parseSubscripts进行解析
// Parse call, dot, and `[]`-subscript expressions.
/**
 * 解析调用链或者箭头函数, 主要在parseMaybeUnary中调用.
 * 正常情况下, 默认会进入这里解析.
 * @param {*} refDestructuringErrors 
 * @param {*} forInit 
 * @returns 
 */
pp.parseExprSubscripts = function (refDestructuringErrors, forInit) {
  let startPos = this.start, startLoc = this.startLoc
  let expr = this.parseExprAtom(refDestructuringErrors, forInit)
  // 箭头函数
  if (expr.type === NodeTypes.ArrowFunctionExpression && this.input.slice(this.lastTokStart, this.lastTokEnd) !== ")")
    return expr
  let result = this.parseSubscripts(expr, startPos, startLoc, false, forInit)
  if (refDestructuringErrors && result.type === NodeTypes.MemberExpression) {
    if (refDestructuringErrors.parenthesizedAssign >= result.start) refDestructuringErrors.parenthesizedAssign = -1
    if (refDestructuringErrors.parenthesizedBind >= result.start) refDestructuringErrors.parenthesizedBind = -1
    if (refDestructuringErrors.trailingComma >= result.start) refDestructuringErrors.trailingComma = -1
  }
  return result
}