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

90 阅读2分钟

parseParenExpression

可以看到, 除了前后的要求()外, 整体调用了parseExpression进行解析

/**
 * 解析表达式语句, 类似while (aaa > bbb)的括号部分
 * @returns {Node}
 */
pp.parseParenExpression = function() {
  this.expect(tt.parenL)
  let val = this.parseExpression()
  this.expect(tt.parenR)
  return val
}

parseExpression

  1. 调用parseMaybeAssign解析出expr
  2. 如果往下读到了,, 则此时循环使用parseMaybeAssign读出并且完成为SequenceExpression
  3. 如果没有读到,, 则此时直接返回为expr
/**
 * 解析单条表达式, 主要是赋值表达式.
 * @param {string | boolean} forInit let/const/var
 * @param {*} refDestructuringErrors 
 * @returns 
 */
pp.parseExpression = function(forInit, refDestructuringErrors) {
  let startPos = this.start, startLoc = this.startLoc
  // let a = b;
  let expr = this.parseMaybeAssign(forInit, refDestructuringErrors)
  if (this.type === tt.comma) {
    // let a,b,c = xxx;
    let node = this.startNodeAt(startPos, startLoc)
    node.expressions = [expr]
    while (this.eat(tt.comma)) node.expressions.push(this.parseMaybeAssign(forInit, refDestructuringErrors))
    return this.finishNode(node, "SequenceExpression")
  }
  return expr
}

parseMaybeAssign

  1. 判断是否在yield上下文中, 如果是则调用parseYield返回
  2. 调用parseMaybeConditional解读出左式.
  3. 然后判断this.type是否assign(如a = b), 如果是
  4. 将node.operator设置为this.value, 然后调用toAssignable设置转化left的类型, 将node.left设置为left, 然后继续调用parseMaybeAssign解析并设置为right.
  5. 如果不是, 则直接检查错误, 并返回
/**
 * 解析 a = b; a += b; xxx -= xxxx;
 * 同时兼容三元表达式a?b:c
 * 解析时, 表达式支持逻辑运算符等
 * @param {string | boolean} forInit 
 * @param {*} refDestructuringErrors 
 * @param {Function} afterLeftParse 
 * @returns 
 */
pp.parseMaybeAssign = function(forInit, refDestructuringErrors, afterLeftParse) {
  if (this.isContextual("yield")) {
    if (this.inGenerator) return this.parseYield(forInit)
    // The tokenizer will assume an expression is allowed after
    // `yield`, but this isn't that kind of yield
    else this.exprAllowed = false
  }

  let ownDestructuringErrors = false, oldParenAssign = -1, oldTrailingComma = -1, oldDoubleProto = -1
  if (refDestructuringErrors) {
    oldParenAssign = refDestructuringErrors.parenthesizedAssign
    oldTrailingComma = refDestructuringErrors.trailingComma
    oldDoubleProto = refDestructuringErrors.doubleProto
    refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = -1
  } else {
    refDestructuringErrors = new DestructuringErrors
    ownDestructuringErrors = true
  }

  let startPos = this.start, startLoc = this.startLoc
  if (this.type === tt.parenL || this.type === tt.name) {
    this.potentialArrowAt = this.start
    this.potentialArrowInForAwait = forInit === "await"
  }
  let left = this.parseMaybeConditional(forInit, refDestructuringErrors)
  if (afterLeftParse) left = afterLeftParse.call(this, left, startPos, startLoc)
  if (this.type.isAssign) {
    let node = this.startNodeAt(startPos, startLoc)
    node.operator = this.value
    if (this.type === tt.eq)
      left = this.toAssignable(left, false, refDestructuringErrors)
    if (!ownDestructuringErrors) {
      refDestructuringErrors.parenthesizedAssign = refDestructuringErrors.trailingComma = refDestructuringErrors.doubleProto = -1
    }
    if (refDestructuringErrors.shorthandAssign >= left.start)
      refDestructuringErrors.shorthandAssign = -1 // reset because shorthand default was used correctly
    if (this.type === tt.eq)
      this.checkLValPattern(left)
    else
      this.checkLValSimple(left)
    node.left = left
    this.next()
    node.right = this.parseMaybeAssign(forInit)
    if (oldDoubleProto > -1) refDestructuringErrors.doubleProto = oldDoubleProto
    return this.finishNode(node, NodeTypes.AssignmentExpression)
  } else {
    if (ownDestructuringErrors) this.checkExpressionErrors(refDestructuringErrors, true)
  }
  if (oldParenAssign > -1) refDestructuringErrors.parenthesizedAssign = oldParenAssign
  if (oldTrailingComma > -1) refDestructuringErrors.trailingComma = oldTrailingComma
  return left
}

parseYield

简单的调用parseMaybeAssign进行解析, 它需要单独处理的原因是语法上可以有yield *这种可迭代对象 参考yield*

/**
 * parseMaybeAssign中调用
 * @param {*} forInit 
 * @returns 
 */
pp.parseYield = function(forInit) {
  if (!this.yieldPos) this.yieldPos = this.start

  let node = this.startNode()
  this.next()
  if (this.type === tt.semi || this.canInsertSemicolon() || (this.type !== tt.star && !this.type.startsExpr)) {
    node.delegate = false
    node.argument = null
  } else {
    node.delegate = this.eat(tt.star)
    node.argument = this.parseMaybeAssign(forInit)
  }
  return this.finishNode(node, "YieldExpression")
}