菜单

解析器实现

相关源文件

此页面描述了 Swift 解析器的实现,重点介绍了它如何将 Swift 源代码转换为抽象语法树(AST)。解析器是 Swift 编译器前端的关键组成部分,它在词法分析和语义分析之间架起了桥梁。有关词法分析的信息,请参阅词法分析器文档;有关 AST 结构本身的信息,请参阅 抽象语法树 (AST)

架构概述

Swift 解析器实现为一种递归下降解析器,它从词法分析器中消耗令牌并生成 AST。该解析器设计得非常健壮,具有良好的错误恢复能力,即使在存在语法错误的情况下也能提供有用的诊断信息。

来源: lib/Parse/Parser.cpp17-26 include/swift/Parse/Parser.h120-188

解析器组织结构

解析器在逻辑上分为多个组件,每个组件负责解析不同的语言结构。主 Parser 类充当入口点并协调这些组件。

来源: include/swift/Parse/Parser.h120-188 lib/Parse/ParseDecl.cpp1-15 lib/Parse/ParseExpr.cpp1-15 lib/Parse/ParseStmt.cpp1-15 lib/Parse/ParseType.cpp1-15 lib/Parse/ParsePattern.cpp1-15

核心解析器组件

Parser 类包含几个管理其状态和功能的关键组件

组件类型目的
SourceMgrSourceManager &管理源文件缓冲区和位置
DiagsDiagnosticEngine &处理诊断信息的发出
SFSourceFile &表示正在解析的源文件
LLexer *提供令牌的词法分析器
上下文ASTContext &用于节点分配的 AST 上下文
Tok标记当前正在处理的令牌
PreviousLocSourceLoc上一个令牌的位置
CurDeclContextDeclContext *当前声明上下文
状态管理PersistentParserState *持久化解析器状态
TokReceiverConsumeTokenReceiver *已消耗令牌的可选接收器

来源: include/swift/Parse/Parser.h130-216

解析过程

解析源文件的主要入口点是 parseTopLevelItems 方法,该方法处理令牌直到到达文件末尾。根据令牌的类型,解析器会分派到专门的方法来解析声明、语句和其他 Swift 结构。

来源: lib/Parse/ParseDecl.cpp175-249

parseTopLevelItems 方法

parseTopLevelItems 方法是 Swift 源文件的主要解析循环。

void Parser::parseTopLevelItems(SmallVectorImpl<ASTNode> &items) {
  // Prime the lexer.
  if (Tok.is(tok::NUM_TOKENS))
    consumeTokenWithoutFeedingReceiver();

  // Parse the body of the file.
  while (!Tok.is(tok::eof)) {
    // Skip SIL declarations for now
    if (isStartOfSILDecl()) {
      skipSILUntilSwiftDecl();
      continue;
    }

    // Determine parsing mode based on source file kind
    BraceItemListKind braceItemListKind;
    switch (SF.Kind) {
    case SourceFileKind::Main:
      braceItemListKind = BraceItemListKind::TopLevelCode;
      break;
    case SourceFileKind::Library:
    case SourceFileKind::Interface:
    case SourceFileKind::SIL:
      braceItemListKind = BraceItemListKind::TopLevelLibrary;
      break;
    case SourceFileKind::MacroExpansion:
    case SourceFileKind::DefaultArgument:
      braceItemListKind = BraceItemListKind::MacroExpansion;
      break;
    }

    parseBraceItems(items, braceItemListKind);

    // Handle misplaced conditional compilation directives
    if (Tok.is(tok::pound_else) || Tok.is(tok::pound_elseif) ||
        Tok.is(tok::pound_endif)) {
      diagnose(Tok.getLoc(),
               diag::unexpected_conditional_compilation_block_terminator);
      consumeToken();
    }
  }

  // Optionally validate parser output for new parser diagnostics
  #if SWIFT_BUILD_SWIFT_SYNTAX
  // Code for validating the new parser diagnostics output
  #endif
}

来源: lib/Parse/ParseDecl.cpp175-249

解析特定的语言结构

解析器将各种 Swift 语言结构的解析委托给专门的方法。每个方法负责解析特定类型的结构并构建相应的 AST 节点。

表达式

表达式由 parseExpr 及其相关方法解析,这些方法处理各种表达式,包括字面量、运算符、函数调用、闭包等。

来源: lib/Parse/ParseExpr.cpp44-375

语句

语句由 parseStmt 及其相关方法解析,这些方法处理各种语句类型,如 if、for、while、switch、return 等。

来源: lib/Parse/ParseStmt.cpp

声明

声明由 parseDecl 及其相关方法解析,这些方法处理各种声明类型,如函数、变量、类型、协议等。

来源: lib/Parse/ParseDecl.cpp

类型

类型由 parseType 及其相关方法解析,这些方法处理各种类型形式,包括简单类型、函数类型、元组类型等。

来源: lib/Parse/ParseType.cpp

模式

模式由 parsePattern 及其相关方法解析,这些方法处理在绑定上下文和模式匹配中使用的各种模式形式。

来源: lib/Parse/ParsePattern.cpp

解析器状态管理

Parser 类在解析期间管理几个状态方面

当前令牌和位置

解析器维护当前令牌 (Tok) 和上一个令牌的位置 (PreviousLoc),这些在消耗令牌时会更新。

令牌消耗

解析器使用 consumeToken()consumeIf()skipUntil() 等方法来消耗令牌。这些方法会推进解析器的位置并更新当前令牌。

bool Parser::consumeIf(tok Kind) {
  if (Tok.is(Kind)) {
    consumeToken(Kind);
    return true;
  }
  return false;
}

void Parser::consumeToken() {
  assert(Tok.isNot(tok::eof) && "Lexing past eof!");
  
  if (TokReceiver)
    TokReceiver->receive(Tok);
  
  PreviousLoc = Tok.getLoc();
  L->lex(Tok);
}

来源: include/swift/Parse/Parser.h770-900

声明上下文管理

解析器跟踪当前声明上下文 (CurDeclContext),用于名称查找和上下文敏感的解析。 ContextChange 类提供了 RAII 风格的上下文管理。

class ContextChange {
protected:
  Parser &P;
  DeclContext *OldContext;

public:
  ContextChange(Parser &P, DeclContext *DC)
    : P(P), OldContext(P.CurDeclContext) {
    assert(DC && "Can't change to a null context!");
    P.CurDeclContext = DC;
  }

  ~ContextChange() {
    P.CurDeclContext = OldContext;
  }
};

来源: include/swift/Parse/Parser.h236-260

错误处理和恢复

解析器包含强大的错误处理和恢复机制,以便在遇到语法错误后继续解析。

来源: include/swift/AST/DiagnosticsParse.def lib/Parse/Parser.cpp

特殊解析器功能

延迟解析

为了提高性能,解析器可以延迟解析函数体和其他大型结构,直到需要为止。

bool Parser::isDelayedParsingEnabled() const {
  // Delayed parsing is disabled for primary files to ensure that errors in
  // the primary files are discovered during the first parse.
  if (SF.isPrimary())
    return false;

  // Otherwise, use the global flag.
  return Context.LangOpts.DelayedParsing;
}

来源: include/swift/Parse/Parser.h170-180

解析器结果管理

解析器使用 ParserResult<T> 模板来跟踪解析状态,包括错误和代码补全。

来源: include/swift/Parse/ParserResult.h

超前查看和回溯

解析器有时可以超前查看多个令牌以确定适当的解析策略,或者在尝试解析不匹配输入的结构后进行回溯。

Parser::BacktrackingScope::~BacktrackingScope() {
  // If this was a successful parse, do not backtrack.
  if (Backtrack) {
    // Restore the token position and parser lexing/context.
    P.L->backtrackToPosition(BacktrackState);
    P.ConsumeMultiTok(StartingToks);
  }
}

来源: include/swift/Parse/Parser.h650-670

构建 AST

当解析器处理源代码时,它会构建一个 AST,该 AST 代表代码的结构和语义。不同的解析器方法构建不同种类的 AST 节点。

来源: lib/Parse/ParseDecl.cpp lib/Parse/ParseExpr.cpp lib/Parse/ParseStmt.cpp lib/Parse/ParseType.cpp lib/Parse/ParsePattern.cpp

诊断

解析器会针对解析过程中遇到的语法错误和其他问题发出诊断信息。诊断信息定义在 include/swift/AST/DiagnosticsParse.def 中,并使用 diagnose 方法发出。

void Parser::diagnose(SourceLoc Loc, Diagnostic<> ID) {
  Diags.diagnose(Loc, ID);
}

template<typename ...DiagArgTypes, typename ...ArgTypes>
void Parser::diagnose(SourceLoc Loc, Diagnostic<DiagArgTypes...> ID,
                     ArgTypes &&...Args) {
  Diags.diagnose(Loc, ID, std::forward<ArgTypes>(Args)...);
}

常见的诊断类别包括:

  • 缺失的标记(如括号、关键字)
  • 无效语法
  • 意外的标记
  • 未终止的构造(注释、字符串)
  • 使用已弃用的语法

来源: include/swift/AST/DiagnosticsParse.def lib/Parse/Parser.cpp

与 SIL 解析的集成

在 SIL 模式下,解析器也支持解析 Swift 中间语言 (SIL)。parseTopLevelSIL 方法负责解析 SIL 声明。

bool Parser::parseTopLevelSIL() {
  assert(SIL && isInSILMode());

  // Prime the lexer.
  if (Tok.is(tok::NUM_TOKENS))
    consumeTokenWithoutFeedingReceiver();

  auto skipToNextSILDecl = [&]() {
    while (!Tok.is(tok::eof) && !isStartOfSILDecl())
      skipSingle();
  };

  auto hadError = false;
  while (!Tok.is(tok::eof)) {
    // Skip Swift decls, only parse SIL decls
    if (isStartOfSwiftDecl()) {
      skipToNextSILDecl();
      continue;
    }

    // Handle different SIL declaration kinds
    switch (Tok.getKind()) {
      case tok::kw_sil:
      case tok::kw_sil_stage:
      case tok::kw_sil_vtable:
      // ... other SIL keywords ...
        // Parse the specific SIL declaration
        break;
      default:
        diagnose(Tok, diag::expected_sil_keyword);
        skipToNextSILDecl();
        hadError = true;
        break;
    }
  }
  return hadError;
}

来源: lib/Parse/ParseDecl.cpp252-307 lib/SIL/Parser/ParseSIL.cpp

结论

Swift 解析器是 Swift 编译器中一个复杂但结构良好的组件。它采用递归下降方法来解析 Swift 源代码,并构建一个捕获代码语法和语义结构的 AST。解析器包含强大的错误处理和恢复机制,能够产生有用的诊断信息,并在存在语法错误时继续解析。

该解析器设计得灵活且可扩展,支持各种源文件类型、条件编译和特殊的语言特性。它与词法分析器集成以消耗标记,并与 AST 上下文集成以构建抽象语法树节点。