10

编译器(上)

语法分析

阅读量:5 · 预计 7 分钟读完

词法分析递归下降
阅读进度3%

第十章 编译器(上)

导读

在前面的章节中,我们构建了完整的计算机硬件系统,并学习了从机器语言到高级语言的软件栈。本章将开始构建Jack编译器的前端——将Jack源代码转换为VM代码。

编译器是计算机科学中最重要的软件之一。它将人类可读的高级语言转换为机器可执行的代码。通过本章的学习,你将理解编译器的工作原理,掌握词法分析、语法分析和代码生成的基本技术。

编译器的构建展示了软件工程的许多重要概念:形式语言理论、递归下降解析、符号表管理等。这些知识不仅对构建编译器有用,对理解编程语言、设计DSL(领域特定语言)等也很有帮助。

核心概念详解

10.1 编译器概述

编译器是将源代码转换为目标代码的程序。

编译器的组成

典型的编译器包含以下阶段:

词法分析(Lexical Analysis):将字符流转换为词法单元(token)流

语法分析(Syntax Analysis):将token流转换为抽象语法树(AST)

语义分析(Semantic Analysis):检查语义正确性,构建符号表

中间代码生成:生成中间表示(IR)

代码优化:优化中间代码

代码生成:生成目标代码

Jack编译器的简化

Jack编译器进行了简化:

  • 直接生成VM代码,跳过中间表示
  • 不做复杂的优化
  • 单遍编译,边解析边生成代码

10.2 词法分析

词法分析是编译的第一步,将源代码字符流转换为token流。

Token类型

Jack语言的token包括:

关键字

class, constructor, function, method, field, static,
var, int, char, boolean, void, true, false, null,
this, let, do, if, else, while, return

符号

{ } ( ) [ ] . , ; + - * / & | ~ < > =

整数常量

  • 0-32767之间的十进制数

字符串常量

  • 双引号包围的字符序列
  • 例如:"Hello, World!"

标识符

  • 字母或下划线开头
  • 后跟字母、数字或下划线
  • 例如:myVar, _count, Class1

词法分析器实现

词法分析器通常使用有限状态机实现:

python
class JackTokenizer:
    def __init__(self, input_file):
        self.input = input_file
        self.current_token = None
        self.token_type = None
    
    def has_more_tokens(self):
        # 检查是否还有更多token
        pass
    
    def advance(self):
        # 读取下一个token
        pass
    
    def get_token_type(self):
        # 返回当前token类型
        # KEYWORD, SYMBOL, IDENTIFIER, INT_CONST, STRING_CONST
        pass
    
    def get_keyword(self):
        # 返回关键字值
        pass
    
    def get_symbol(self):
        # 返回符号
        pass
    
    def get_identifier(self):
        # 返回标识符
        pass
    
    def get_int_constant(self):
        # 返回整数常量
        pass
    
    def get_string_constant(self):
        # 返回字符串常量
        pass

10.3 语法分析

语法分析是编译的核心,将token流转换为结构化的表示。

上下文无关文法

Jack语言的语法可以用上下文无关文法(CFG)描述:

class       → 'class' className '{' classVarDec* subroutineDec* '}'
classVarDec → ('static' | 'field') type varName (',' varName)* ';'
type        → 'int' | 'char' | 'boolean' | className
subroutineDec → ('constructor' | 'function' | 'method')
                ('void' | type) subroutineName '(' parameterList ')' subroutineBody
parameterList → ((type varName) (',' type varName)*)?
subroutineBody → '{' varDec* statements '}'
varDec      → 'var' type varName (',' varName)* ';'
statements  → statement*
statement   → letStatement | ifStatement | whileStatement | 
              doStatement | returnStatement
letStatement → 'let' varName ('[' expression ']')? '=' expression ';'
ifStatement → 'if' '(' expression ')' '{' statements '}'
              ('else' '{' statements '}')?
whileStatement → 'while' '(' expression ')' '{' statements '}'
doStatement → 'do' subroutineCall ';'
returnStatement → 'return' expression? ';'
expression  → term (op term)*
term        → integerConstant | stringConstant | keywordConstant |
              varName | varName '[' expression ']' | subroutineCall |
              '(' expression ')' | unaryOp term
subroutineCall → subroutineName '(' expressionList ')' |
                 (className | varName) '.' subroutineName '(' expressionList ')'
expressionList → (expression (',' expression)*)?

递归下降解析

递归下降是一种自顶向下的解析方法:

  • 每个非终结符对应一个解析函数
  • 函数之间相互调用
  • 自然映射到文法规则
python
class CompilationEngine:
    def __init__(self, tokenizer, output_file):
        self.tokenizer = tokenizer
        self.output = output_file
    
    def compile_class(self):
        # class → 'class' className '{' classVarDec* subroutineDec* '}'
        self.tokenizer.advance()  # 'class'
        self.tokenizer.advance()  # className
        self.tokenizer.advance()  # '{'
        
        while self.tokenizer.get_token() in ['static', 'field']:
            self.compile_class_var_dec()
        
        while self.tokenizer.get_token() in ['constructor', 'function', 'method']:
            self.compile_subroutine()
        
        self.tokenizer.advance()  # '}'
    
    def compile_class_var_dec(self):
        # classVarDec → ('static' | 'field') type varName (',' varName)* ';'
        kind = self.tokenizer.get_token()  # 'static' or 'field'
        self.tokenizer.advance()
        
        type = self.tokenizer.get_token()
        self.tokenizer.advance()
        
        var_name = self.tokenizer.get_token()
        self.tokenizer.advance()
        # 添加到符号表
        
        while self.tokenizer.get_token() == ',':
            self.tokenizer.advance()
            var_name = self.tokenizer.get_token()
            self.tokenizer.advance()
            # 添加到符号表
        
        self.tokenizer.advance()  # ';'
    
    def compile_subroutine(self):
        # subroutineDec → ...
        pass
    
    def compile_expression(self):
        # expression → term (op term)*
        self.compile_term()
        
        while self.tokenizer.get_token() in ['+', '-', '*', '/', '&', '|', '<', '>', '=']:
            op = self.tokenizer.get_token()
            self.tokenizer.advance()
            self.compile_term()
            # 生成VM代码

10.4 符号表

符号表存储程序中定义的标识符信息。

符号表结构

python
class SymbolTable:
    def __init__(self):
        self.table = {}  # name -> (type, kind, index)
        self.counts = {'static': 0, 'field': 0, 'var': 0, 'arg': 0}
    
    def define(self, name, type, kind):
        index = self.counts[kind]
        self.table[name] = (type, kind, index)
        self.counts[kind] += 1
    
    def type_of(self, name):
        return self.table[name][0]
    
    def kind_of(self, name):
        return self.table[name][1]
    
    def index_of(self, name):
        return self.table[name][2]
    
    def var_count(self, kind):
        return self.counts[kind]
    
    def start_subroutine(self):
        # 清除局部符号表
        self.table = {k: v for k, v in self.table.items() 
                     if v[1] in ['static', 'field']}
        self.counts['var'] = 0
        self.counts['arg'] = 0

作用域

Jack有两个作用域级别:

  • 类作用域:static和field变量
  • 子程序作用域:arg和var变量

每个子程序开始时,清除局部符号表,但保留类级别的符号。

10.5 表达式编译

表达式编译是编译器中最复杂的部分之一。

中缀到后缀转换

Jack表达式是中缀表示,需要转换为后缀表示以便VM执行:

中缀:a + b * c
后缀:a b c * +

使用调度场算法(shunting-yard algorithm)进行转换。

表达式编译实现

python
def compile_expression(self):
    # expression → term (op term)*
    self.compile_term()
    
    while self.tokenizer.get_token() in ['+', '-', '*', '/', '&', '|', '<', '>', '=']:
        op = self.tokenizer.get_token()
        self.tokenizer.advance()
        self.compile_term()
        
        # 生成VM代码
        if op == '+':
            self.emit('add')
        elif op == '-':
            self.emit('sub')
        elif op == '*':
            self.emit('call Math.multiply 2')
        elif op == '/':
            self.emit('call Math.divide 2')
        elif op == '&':
            self.emit('and')
        elif op == '|':
            self.emit('or')
        elif op == '<':
            self.emit('lt')
        elif op == '>':
            self.emit('gt')
        elif op == '=':
            self.emit('eq')

项的编译

python
def compile_term(self):
    token = self.tokenizer.get_token()
    
    if token.isdigit():
        # 整数常量
        self.emit(f'push constant {token}')
        self.tokenizer.advance()
    
    elif token.startswith('"'):
        # 字符串常量
        string = token[1:-1]
        self.emit(f'push constant {len(string)}')
        self.emit('call String.new 1')
        for char in string:
            self.emit(f'push constant {ord(char)}')
            self.emit('call String.appendChar 2')
        self.tokenizer.advance()
    
    elif token in ['true', 'false', 'null', 'this']:
        # 关键字常量
        if token == 'true':
            self.emit('push constant 0')
            self.emit('neg')
        elif token == 'false' or token == 'null':
            self.emit('push constant 0')
        elif token == 'this':
            self.emit('push pointer 0')
        self.tokenizer.advance()
    
    elif token == '(' :
        # 括号表达式
        self.tokenizer.advance()
        self.compile_expression()
        self.tokenizer.advance()  # ')'
    
    elif token in ['-', '~']:
        # 一元运算
        op = token
        self.tokenizer.advance()
        self.compile_term()
        if op == '-':
            self.emit('neg')
        elif op == '~':
            self.emit('not')
    
    else:
        # 变量或函数调用
        name = token
        self.tokenizer.advance()
        
        if self.tokenizer.get_token() == '[':
            # 数组访问
            self.emit_push_var(name)  # 数组基地址
            self.tokenizer.advance()
            self.compile_expression()
            self.emit('add')
            self.emit('pop pointer 1')
            self.emit('push that 0')
            self.tokenizer.advance()  # ']'
        
        elif self.tokenizer.get_token() == '(':
            # 函数调用
            self.tokenizer.advance()
            self.compile_expression_list()
            self.emit(f'call {name} {n_args}')
            self.tokenizer.advance()  # ')'
        
        elif self.tokenizer.get_token() == '.':
            # 方法或函数调用
            self.tokenizer.advance()
            method_name = self.tokenizer.get_token()
            self.tokenizer.advance()
            self.tokenizer.advance()  # '('
            if name in self.class_vars:
                # 方法调用
                self.emit_push_var(name)
                self.compile_expression_list()
                self.emit(f'call {class_name}.{method_name} {n_args + 1}')
            else:
                # 函数调用
                self.compile_expression_list()
                self.emit(f'call {name}.{method_name} {n_args}')
            self.tokenizer.advance()  # ')'
        
        else:
            # 变量
            self.emit_push_var(name)

10.6 语句编译

Jack支持五种语句类型。

let语句

python
def compile_let(self):
    # let varName ('[' expression ']')? '=' expression ';'
    self.tokenizer.advance()  # 'let'
    var_name = self.tokenizer.get_token()
    self.tokenizer.advance()
    
    if self.tokenizer.get_token() == '[':
        # 数组赋值
        self.emit_push_var(var_name)  # 数组基地址
        self.tokenizer.advance()
        self.compile_expression()
        self.emit('add')  # 计算目标地址
        self.tokenizer.advance()  # ']'
        self.tokenizer.advance()  # '='
        self.compile_expression()
        self.emit('pop pointer 1')
        self.emit('pop that 0')  # 这里需要修正
    else:
        # 普通赋值
        self.tokenizer.advance()  # '='
        self.compile_expression()
        self.emit_pop_var(var_name)
    
    self.tokenizer.advance()  # ';'

if语句

python
def compile_if(self):
    # if '(' expression ')' '{' statements '}' ('else' '{' statements '}')?
    label_else = f"IF_ELSE_{self.label_count}"
    label_end = f"IF_END_{self.label_count}"
    self.label_count += 1
    
    self.tokenizer.advance()  # 'if'
    self.tokenizer.advance()  # '('
    self.compile_expression()
    self.tokenizer.advance()  # ')'
    
    self.emit('not')
    self.emit(f'if-goto {label_else}')
    
    self.tokenizer.advance()  # '{'
    self.compile_statements()
    self.tokenizer.advance()  # '}'
    
    self.emit(f'goto {label_end}')
    self.emit(f'label {label_else}')
    
    if self.tokenizer.get_token() == 'else':
        self.tokenizer.advance()  # 'else'
        self.tokenizer.advance()  # '{'
        self.compile_statements()
        self.tokenizer.advance()  # '}'
    
    self.emit(f'label {label_end}')

while语句

python
def compile_while(self):
    # while '(' expression ')' '{' statements '}'
    label_start = f"WHILE_START_{self.label_count}"
    label_end = f"WHILE_END_{self.label_count}"
    self.label_count += 1
    
    self.emit(f'label {label_start}')
    
    self.tokenizer.advance()  # 'while'
    self.tokenizer.advance()  # '('
    self.compile_expression()
    self.tokenizer.advance()  # ')'
    
    self.emit('not')
    self.emit(f'if-goto {label_end}')
    
    self.tokenizer.advance()  # '{'
    self.compile_statements()
    self.tokenizer.advance()  # '}'
    
    self.emit(f'goto {label_start}')
    self.emit(f'label {label_end}')

do语句

python
def compile_do(self):
    # do subroutineCall ';'
    self.tokenizer.advance()  # 'do'
    self.compile_subroutine_call()
    self.emit('pop temp 0')  # 丢弃返回值
    self.tokenizer.advance()  # ';'

return语句

python
def compile_return(self):
    # return expression? ';'
    self.tokenizer.advance()  # 'return'
    
    if self.tokenizer.get_token() != ';':
        self.compile_expression()
    else:
        # void函数返回this或0
        if self.current_subroutine_type == 'constructor':
            self.emit('push pointer 0')
        else:
            self.emit('push constant 0')
    
    self.emit('return')
    self.tokenizer.advance()  # ';'

10.7 子程序编译

子程序(构造函数、函数、方法)的编译需要特殊处理。

构造函数

python
def compile_constructor(self):
    # constructor ClassName new(...)
    self.tokenizer.advance()  # 'constructor'
    class_name = self.class_name
    self.tokenizer.advance()  # 'new'
    self.tokenizer.advance()  # '('
    
    self.compile_parameter_list()
    self.tokenizer.advance()  # ')'
    
    self.emit(f'function {class_name}.new {n_locals}')
    
    # 分配内存
    n_fields = self.symbol_table.var_count('field')
    self.emit(f'push constant {n_fields}')
    self.emit('call Memory.alloc 1')
    self.emit('pop pointer 0')
    
    self.tokenizer.advance()  # '{'
    self.compile_subroutine_body()
    self.tokenizer.advance()  # '}'

方法

python
def compile_method(self):
    # method returnType methodName(...)
    self.tokenizer.advance()  # 'method'
    self.tokenizer.advance()  # methodName
    self.tokenizer.advance()  # '('
    
    self.compile_parameter_list()
    self.tokenizer.advance()  # ')'
    
    self.emit(f'function {class_name}.{method_name} {n_locals}')
    
    # 设置this指针
    self.emit('push argument 0')
    self.emit('pop pointer 0')
    
    self.tokenizer.advance()  # '{'
    self.compile_subroutine_body()
    self.tokenizer.advance()  # '}'

函数

python
def compile_function(self):
    # function returnType functionName(...)
    self.tokenizer.advance()  # 'function'
    self.tokenizer.advance()  # functionName
    self.tokenizer.advance()  # '('
    
    self.compile_parameter_list()
    self.tokenizer.advance()  # ')'
    
    self.emit(f'function {class_name}.{function_name} {n_locals}')
    
    self.tokenizer.advance()  # '{'
    self.compile_subroutine_body()
    self.tokenizer.advance()  # '}'

10.8 编译器架构

完整的Jack编译器架构:

JackCompiler
├── JackTokenizer (词法分析)
├── CompilationEngine (语法分析+代码生成)
│   ├── SymbolTable (符号表)
│   └── VMWriter (VM代码输出)
└── JackAnalyzer (可选的XML输出)

主程序流程

python
def compile(jack_file, vm_file):
    tokenizer = JackTokenizer(jack_file)
    vm_writer = VMWriter(vm_file)
    engine = CompilationEngine(tokenizer, vm_writer)
    
    engine.compile_class()
    
    vm_writer.close()

重要知识点

知识点1:文法与解析器

文法定义了语言的结构,解析器根据文法识别程序结构。常见的解析方法:

  • 递归下降:自顶向下,直观易懂
  • LL解析:自顶向下,使用预测表
  • LR解析:自底向上,功能强大

知识点2:错误恢复

编译器需要能够处理源代码中的错误:

  • 恐慌模式:跳过错误部分,寻找同步点
  • 短语级恢复:进行局部修正
  • 错误产生式:使用特殊的文法规则

知识点3:中间表示

中间表示(IR)是编译器的内部表示:

  • 抽象语法树(AST):树形结构
  • 三地址码:类似汇编的线性表示
  • 字节码:紧凑的二进制表示

知识点4:属性文法

属性文法扩展了上下文无关文法:

  • 为每个文法符号关联属性
  • 通过语义规则计算属性值
  • 用于类型检查和代码生成

知识点5:编译器优化

编译器优化提高生成代码的质量:

  • 常量折叠:在编译时计算常量表达式
  • 死代码消除:删除不可达代码
  • 循环优化:优化循环结构
  • 内联展开:将函数调用替换为函数体

常见误区

误区1:认为编译器很神秘

实际上,编译器的基本原理并不复杂。词法分析和语法分析都有成熟的算法和工具。

误区2:忽视错误处理

编译器必须能够处理错误输入。良好的错误处理机制对于用户体验至关重要。

误区3:不理解符号表的作用

符号表是编译器的核心数据结构。正确管理符号表对于类型检查和代码生成至关重要。

误区4:认为递归下降效率低

虽然递归下降可能不是最快的解析方法,但它简单直观,对于大多数应用足够高效。

误区5:过度追求完美

编译器是一个复杂的系统。在实践中,需要在功能、性能和开发时间之间找到平衡。

实践应用

实践1:实现词法分析器

python
class JackTokenizer:
    KEYWORDS = {'class', 'constructor', 'function', 'method', 'field',
                'static', 'var', 'int', 'char', 'boolean', 'void',
                'true', 'false', 'null', 'this', 'let', 'do', 'if',
                'else', 'while', 'return'}
    
    SYMBOLS = {'{', '}', '(', ')', '[', ']', '.', ',', ';',
               '+', '-', '*', '/', '&', '|', '~', '<', '>', '='}
    
    def __init__(self, input_file):
        with open(input_file) as f:
            self.content = f.read()
        self.pos = 0
        self.current_token = None
        self.token_type = None
    
    def has_more_tokens(self):
        self.skip_whitespace_and_comments()
        return self.pos < len(self.content)
    
    def advance(self):
        self.skip_whitespace_and_comments()
        if self.pos >= len(self.content):
            return
        
        char = self.content[self.pos]
        
        if char.isdigit():
            self.read_int_constant()
        elif char == '"':
            self.read_string_constant()
        elif char.isalpha() or char == '_':
            self.read_identifier_or_keyword()
        elif char in self.SYMBOLS:
            self.current_token = char
            self.token_type = 'SYMBOL'
            self.pos += 1
        else:
            raise SyntaxError(f"Unexpected character: {char}")
    
    def skip_whitespace_and_comments(self):
        while self.pos < len(self.content):
            if self.content[self.pos].isspace():
                self.pos += 1
            elif self.content[self.pos:self.pos+2] == '//':
                while self.pos < len(self.content) and self.content[self.pos] != '\n':
                    self.pos += 1
            elif self.content[self.pos:self.pos+2] == '/*':
                self.pos += 2
                while self.pos < len(self.content) - 1:
                    if self.content[self.pos:self.pos+2] == '*/':
                        self.pos += 2
                        break
                    self.pos += 1
            else:
                break
    
    def read_int_constant(self):
        start = self.pos
        while self.pos < len(self.content) and self.content[self.pos].isdigit():
            self.pos += 1
        self.current_token = self.content[start:self.pos]
        self.token_type = 'INT_CONST'
    
    def read_string_constant(self):
        self.pos += 1  # skip opening quote
        start = self.pos
        while self.pos < len(self.content) and self.content[self.pos] != '"':
            self.pos += 1
        self.current_token = self.content[start:self.pos]
        self.token_type = 'STRING_CONST'
        self.pos += 1  # skip closing quote
    
    def read_identifier_or_keyword(self):
        start = self.pos
        while self.pos < len(self.content) and (self.content[self.pos].isalnum() or self.content[self.pos] == '_'):
            self.pos += 1
        token = self.content[start:self.pos]
        if token in self.KEYWORDS:
            self.current_token = token
            self.token_type = 'KEYWORD'
        else:
            self.current_token = token
            self.token_type = 'IDENTIFIER'

实践2:实现符号表

python
class SymbolTable:
    def __init__(self):
        self.class_scope = {}
        self.subroutine_scope = {}
        self.counts = {'static': 0, 'field': 0, 'var': 0, 'arg': 0}
    
    def start_subroutine(self):
        self.subroutine_scope = {}
        self.counts['var'] = 0
        self.counts['arg'] = 0
    
    def define(self, name, type, kind):
        index = self.counts[kind]
        if kind in ['static', 'field']:
            self.class_scope[name] = (type, kind, index)
        else:
            self.subroutine_scope[name] = (type, kind, index)
        self.counts[kind] += 1
    
    def var_count(self, kind):
        return self.counts[kind]
    
    def type_of(self, name):
        if name in self.subroutine_scope:
            return self.subroutine_scope[name][0]
        elif name in self.class_scope:
            return self.class_scope[name][0]
        return None
    
    def kind_of(self, name):
        if name in self.subroutine_scope:
            return self.subroutine_scope[name][1]
        elif name in self.class_scope:
            return self.class_scope[name][1]
        return None
    
    def index_of(self, name):
        if name in self.subroutine_scope:
            return self.subroutine_scope[name][2]
        elif name in self.class_scope:
            return self.class_scope[name][2]
        return None

实践3:实现VM代码输出

python
class VMWriter:
    def __init__(self, output_file):
        self.output = open(output_file, 'w')
    
    def write_push(self, segment, index):
        self.output.write(f'push {segment} {index}\n')
    
    def write_pop(self, segment, index):
        self.output.write(f'pop {segment} {index}\n')
    
    def write_arithmetic(self, command):
        self.output.write(f'{command}\n')
    
    def write_label(self, label):
        self.output.write(f'label {label}\n')
    
    def write_goto(self, label):
        self.output.write(f'goto {label}\n')
    
    def write_if(self, label):
        self.output.write(f'if-goto {label}\n')
    
    def write_call(self, name, n_args):
        self.output.write(f'call {name} {n_args}\n')
    
    def write_function(self, name, n_locals):
        self.output.write(f'function {name} {n_locals}\n')
    
    def write_return(self):
        self.output.write('return\n')
    
    def close(self):
        self.output.close()

实践4:实现表达式编译

python
def compile_expression(self):
    self.compile_term()
    
    while self.tokenizer.token_type == 'SYMBOL' and \
          self.tokenizer.current_token in '+-*/&|<>=':
        op = self.tokenizer.current_token
        self.tokenizer.advance()
        self.compile_term()
        
        op_map = {
            '+': 'add', '-': 'sub', '&': 'and', '|': 'or',
            '<': 'lt', '>': 'gt', '=': 'eq'
        }
        
        if op in op_map:
            self.vm_writer.write_arithmetic(op_map[op])
        elif op == '*':
            self.vm_writer.write_call('Math.multiply', 2)
        elif op == '/':
            self.vm_writer.write_call('Math.divide', 2)

实践5:测试编译器

使用提供的测试程序测试编译器:

jack
// Test.jack
class Main {
    function void main() {
        var int x;
        let x = 5 + 3;
        do Output.printInt(x);
        return;
    }
}

预期VM输出:

function Main.main 1
push constant 5
push constant 3
add
pop local 0
push local 0
call Output.printInt 1
pop temp 0
push constant 0
return

本章小结

本章我们构建了Jack编译器的前端,将Jack源代码转换为VM代码。

核心要点回顾

词法分析:将字符流转换为token流,识别关键字、符号、标识符、常量。

语法分析:使用递归下降方法,根据文法规则解析程序结构。

符号表:存储标识符信息,支持作用域管理。

表达式编译:将中缀表达式转换为后缀表示,生成VM代码。

语句编译:处理let、if、while、do、return等语句。

子程序编译:处理构造函数、方法和函数的编译。

关键技能掌握

  • 理解编译器的工作原理
  • 掌握词法分析和语法分析的方法
  • 能够实现符号表
  • 能够生成VM代码
  • 理解表达式和语句的编译过程

与后续章节的联系

本章构建的编译器前端是后续章节的基础:

  • 第十一章将完善编译器,处理更复杂的语言特性
  • 第十二章将实现Jack标准库,为编译后的程序提供运行时支持

学习建议

理解原理:深入理解编译器的工作原理,而不仅仅是实现

动手实践:亲手实现编译器的各个组件

测试验证:使用各种测试程序验证编译器的正确性

循序渐进:从简单特性开始,逐步增加复杂度

通过本章的学习,你已经掌握了编译器前端的核心知识。编译器是连接人类思维和机器执行的桥梁,理解其工作原理对于理解编程语言和计算本质至关重要。