babel 源码分析 一 parse

5 篇文章 1 订阅
4 篇文章 0 订阅

上一篇:babel 源码分析 一 入口

接着上篇,我们继续分析 babel 如何将原始代码转化为 AST 。

上篇中执行 normalizeFile 函数时最终执行的是下面的 parse 方法。

// babel-parser/src/index.js

export function parse(input: string, options?: Options): File {
  if (options?.sourceType === "unambiguous") {
    // some code
  } else {
    // 🌵🌵🌵 执行这里 🌵🌵🌵
    return getParser(options, input).parse();
  }
}

沿用上篇中的命令,这里的 options 对应就是

{
  sourceType: 'module',
  sourceFileName: '/Users/9527/Desktop/babel/src/index.js',
  plugins: [
    'classProperties',
    'classPrivateProperties',
    'classPrivateMethods',
    'numericSeparator',
    'logicalAssignment',
    'nullishCoalescingOperator',
    'optionalChaining',
    'jsonStrings',
    'optionalCatchBinding',
    'asyncGenerators',
    'objectRestSpread',
    'exportNamespaceFrom',
    'dynamicImport'
  ]
}

接着执行 getParser 方法

function getParser(options: ?Options, input: string): Parser {
  let cls = Parser;
  if (options?.plugins) {
    validatePlugins(options.plugins); // 校验插件
    cls = getParserClass(options.plugins);
  }
	
	// 🌵🌵🌵 执行这里 🌵🌵🌵
  return new cls(options, input);
}

在 getParserClass 方法中,会将我们的插件和 parse 中内置的插件进行对比,如果没有匹配到就返回空数组,下面是 parse 内置的一些插件:

const mixinPlugins = {
  estree,
  jsx,
  flow,
  typescript,
  v8intrinsic,
  placeholders
};

这里没有匹配到,所以返回的还是 Parser,这个类拥有一条很长的继承链,如下

Parser
-> StatementParser 
-> ExpressionParser 
-> LValParser 
-> NodeUtils 
-> UtilParser 
-> Tokenizer 
-> ParserError
-> CommentsParser
-> BaseParser

// 对应的目录结构
├── parser
│   ├── base.js
│   ├── comments.js
│   ├── error-codes.js
│   ├── error-message.js
│   ├── error.js
│   ├── expression.js
│   ├── index.js
│   ├── lval.js
│   ├── node.js
│   ├── statement.js
│   └── util.js

每一个类都单独做了一些事情,然后传递给下个类,就像我们平常请假需要层层审批一样,这种模式也被称为责任链模式。

为了方便理解接下来的讲解,我们写一段最简单的 ES6 代码,看下 parse 是如何解析的,假如我们待解析的代码为:

let age = 18

那么运行入口文件中执行 getParser(options, input).parse() 时会运行下面代码:

parse(): File {
  this.enterInitialScopes();
  const file = this.startNode();
  const program = this.startNode();
  this.nextToken();
  file.errors = null;
  // 🌵🌵🌵 执行这里 🌵🌵🌵
  this.parseTopLevel(file, program);
  file.errors = this.state.errors;
  return file;
}

生成了 file 和 program 两个节点,然后将这两个节点传入到 parseTopLevel 中执行。

parseTopLevel(file: N.File, program: N.Program): N.File {
  // 🌵🌵🌵 执行这里 🌵🌵🌵
  file.program = this.parseProgram(program);
  file.comments = this.state.comments;

  if (this.options.tokens) file.tokens = this.tokens;

  return this.finishNode(file, "File");
}

首先是处理 program 节点,这个节点的 body 会存放我们书写代码生成的节点,不过此时还未挂载 body 暂时不存在,节点目前结构如下:

// program 节点
Node {
  type: '',
  start: 0,
  end: 0,
  loc: SourceLocation {
    start: Position { line: 1, column: 0 },
    end: undefined,
    filename: undefined,
    identifierName: undefined
  },
  range: undefined,
  leadingComments: undefined,
  trailingComments: undefined,
  innerComments: undefined,
  extra: undefined,
  sourceType: 'module',
  interpreter: null
}

接着执行 parseProgram 方法:

parseProgram(program, end = types.eof, sourceType = this.options.sourceType) {
  program.sourceType = sourceType;
  program.interpreter = this.parseInterpreterDirective();
  // 🌵🌵🌵 执行这里 🌵🌵🌵
  this.parseBlockBody(program, true, true, end);

  if (this.inModule && !this.options.allowUndeclaredExports && this.scope.undefinedExports.size > 0) {
    for (const [name] of Array.from(this.scope.undefinedExports)) {
      const pos = this.scope.undefinedExports.get(name);
      this.raise(pos, ErrorMessages.ModuleExportUndefined, name);
    }
  }

  return this.finishNode(program, "Program");
}

将 program 节点传入到 parseBlockBody 中向下执行。

parseBlockBody(node, allowDirectives, topLevel, end, afterBlockParse) {
  const body = node.body = [];
  const directives = node.directives = [];
  // 🌵🌵🌵 执行这里 🌵🌵🌵
  this.parseBlockOrModuleBlockBody(body, allowDirectives ? directives : undefined, topLevel, end, afterBlockParse);
}

此时会给节点添加 body 属性,因为我们代码不可能只有一行,所有使用数组存放,然后继续执行 parseBlockOrModuleBlockBody 方法。

parseBlockOrModuleBlockBody(body, directives, topLevel, end, afterBlockParse) {
  const oldStrict = this.state.strict;
  let hasStrictModeDirective = false;
  let parsedNonDirective = false;

  while (!this.match(end)) {
    // 🌵🌵🌵 执行这里 🌵🌵🌵
    const stmt = this.parseStatement(null, topLevel);

    if (directives && !parsedNonDirective) {
      if (this.isValidDirective(stmt)) {
        const directive = this.stmtToDirective(stmt);
        directives.push(directive);

        if (!hasStrictModeDirective && directive.value.value === "use strict") {
          hasStrictModeDirective = true;
          this.setStrict(true);
        }

        continue;
      }

      parsedNonDirective = true;
      this.state.strictErrors.clear();
    }

    body.push(stmt);
  }

  if (afterBlockParse) {
    afterBlockParse.call(this, hasStrictModeDirective);
  }

  if (!oldStrict) {
    this.setStrict(false);
  }

  this.next();
}

这里使用 while 来循环判断,如果没有结束就继续执行 parseStatement 方法。

parseStatement(context, topLevel) {
  if (this.match(types.at)) {
    this.parseDecorators(true);
  }
  // 🌵🌵🌵 执行这里 🌵🌵🌵
  return this.parseStatementContent(context, topLevel);
}

传入参数给 parseStatementContent 方法继续执行:

parseStatementContent(context, topLevel) {
  let starttype = this.state.type;
  const node = this.startNode();
  let kind;

  if (this.isLet(context)) {
    starttype = types._var;
    kind = "let";
  }

  switch (starttype) {
    case types._break:
    case types._continue:
      return this.parseBreakContinueStatement(node, starttype.keyword);
    case types._function:
      if (this.lookaheadCharCode() === 46) break;

      if (context) {
        if (this.state.strict) {
          this.raise(this.state.start, ErrorMessages.StrictFunction);
        } else if (context !== "if" && context !== "label") {
          this.raise(this.state.start, ErrorMessages.SloppyFunction);
        }
      }

      return this.parseFunctionStatement(node, false, !context);

    case types._const:
    case types._var:
      kind = kind || this.state.value;

      if (context && kind !== "var") {
        this.raise(this.state.start, ErrorMessages.UnexpectedLexicalDeclaration);
      }
      // 🌵🌵🌵 执行这里 🌵🌵🌵
      return this.parseVarStatement(node, kind);

      // ... code ...
    default:
      {
        if (this.isAsyncFunction()) {
          if (context) {
            this.raise(this.state.start, ErrorMessages.AsyncFunctionInSingleStatementContext);
          }

          this.next();
          return this.parseFunctionStatement(node, true, !context);
        }
      }
  }

  const maybeName = this.state.value;
  const expr = this.parseExpression();

  if (starttype === types.name && expr.type === "Identifier" && this.eat(types.colon)) {
    return this.parseLabeledStatement(node, maybeName, expr, context);
  } else {
    return this.parseExpressionStatement(node, expr);
  }
}

这里通过首先通过 this.startNode() 生成了新的节点,然后通过 this.isLet(context) 判断是否 let 声明,我们之前假设待代码是 let age = 18 , 所以会匹配到 types._var,然后执行 parseVarStatement 方法。

  parseVarStatement(node, kind) {
    this.next();
    this.parseVar(node, false, kind);
    this.semicolon();
    return this.finishNode(node, "VariableDeclaration");
  }

这个方法会解析变量,添加末尾分号等操作,最后调用 finishNode 执行后会返回一个变量声明节点,结构如下:

{
    "type":"VariableDeclaration",
    "start":0,
    "end":18,
    "loc":{
        "start":{
            "line":1,
            "column":0
        },
        "end":{
            "line":1,
            "column":18
        }
    },
    "declarations":[
        {
            "type":"VariableDeclarator",
            "start":4,
            "end":17,
            "loc":{
                "start":{
                    "line":1,
                    "column":4
                },
                "end":{
                    "line":1,
                    "column":17
                }
            },
            "id":{
                "type":"Identifier",
                "start":4,
                "end":8,
                "loc":{
                    "start":{
                        "line":1,
                        "column":4
                    },
                    "end":{
                        "line":1,
                        "column":8
                    },
                    "identifierName":"name"
                },
                "name":"name"
            },
            "init":{
                "type":"NumericLiteral",
                "start":11,
                "end":17,
                "loc":{
                    "start":{
                        "line":1,
                        "column":11
                    },
                    "end":{
                        "line":1,
                        "column":17
                    }
                },
                "extra":{
                    "rawValue":123123,
                    "raw":"123123"
                },
                "value":123123
            }
        }
    ],
    "kind":"let"
}

之后就是一路 return,然后在 parseBlockOrModuleBlockBody 中将返回的节点添加到 body 中,因为我们只有一行代码,所以继续执行 this.next() 时也会 return 出栈。

至此 parse 解析完成。

下一篇:babel源码分析 - traverse

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值