1 术语界定:终结符与非终结符

1.1 终结符的定义与特征

终结符(terminal)是形式文法中作为最终输出的“最小可用符号”。在给定文法的产生式体系内,终结符通常不再被任何产生式右部替换生成,因此可把它理解为推导过程的终点元素。形式语言中的字符串由终结符构成:也就是说,语言的成员最终都能表示为一个由终结符按序排列得到的序列。

从可观察角度看,终结符往往对应“输入端或输出端真正出现的原子单位”,例如编译器中词法分析得到的记号、或在抽象层面上表示具体字符与固定字面量。

1.2 非终结符的定义与特征

非终结符(nonterminal)是用于描述语法结构的“可展开类别”。它们出现在产生式的左部(以及右部)以表达替换规则:某个非终结符可以被若干符号(终结符与非终结符的混合)所替换,从而继续构造更复杂的句型。非终结符本身并不直接构成语言字符串;只有在推导结束时,所有非终结符都应被替换掉,最终得到的串才由终结符组成。

常见非终结符例如“表达式”“语句”“因子”等,它们更像是语法层的类别标签,用来组织结构而非直接承载具体文本。

1.3 两者在“可展开性”上的差异

二者的核心差别在于“能否继续替换”。终结符不参与产生式展开(或至少在正常文法使用中不会再被替换),而非终结符会触发产生式的替换行为。由此,推导过程呈现出一种从结构标签到最终符号序列的演化:先用非终结符搭建骨架,再逐步展开,直至只剩下终结符。

这种区分也影响解析算法的直觉:预测与展开依赖非终结符,而匹配输入时关注终结符序列中的终结符。

1.4 例子:从符号到语法类别的直观对应

考虑一个简化算术语言,终结符可设为数字与运算符本身,例如 id(标识符)与 +。非终结符可以设为 Expr(表达式)与 Term(项)。当文法给出产生式规则时,Expr 可以展开成由 Term+ 组成的结构;而 Term 进一步展开,最终把所有抽象类别替换成 id+ 等终结符。 因此,终结符更接近“文本中真实出现的片段”,非终结符更接近“语法结构的分工与层次”。

2 形式文法中的位置与作用

2.1 文法组成要素概览

形式文法通常由符号集合与产生式集合等构成。其符号集合分为终结符集合与非终结符集合,二者在推导末端与中间阶段承担不同角色。产生式给出从某个非终结符到一串符号(包含终结符与非终结符)的替换方式。

在实践中,文法还会包含一个开始符号(start symbol),用于标定推导从哪个非终结符出发。理解这一点有助于把握终结符与非终结符并非“自由漂浮”,而是被文法的规则组织起来。

2.2 产生式(产生规则)如何连接符号

产生式是文法运作的引擎。每条产生式的左部一般是单一非终结符,右部是一个由符号序列构成的表达式。应用产生式时,意味着把左部的非终结符替换为右部的符号序列。

在这个机制下,非终结符决定了“能生成哪些结构”,而终结符决定了“结构最终会落到哪些具体符号上”。因此,终结符与非终结符并不是抽象概念的对立,而是同一套规则系统中的不同层次。

2.3 句型与推导中符号的流转

推导过程中,符号序列会从包含非终结符的句型逐步演化。早期句型往往较“抽象”,包含较多非终结符;随着不断应用产生式,非终结符数量逐渐减少,直至被完全替换,得到只由终结符组成的最终句子。

这种流转关系也使得“可推导性”变得可计算:如果存在一条从开始符号出发的产生式应用序列,使得最终得到目标终结符串,则该串属于文法生成的语言。

2.4 语言(Language)与符号集合的关系

语言(language)在形式语言理论中通常指所有可由文法推导得到的终结符串组成的集合。换言之,语言的元素不直接包含非终结符;非终结符只在推导过程的中间阶段出现。

因此可以形成一个层次对应:非终结符与产生式描述“结构生成能力”,而终结符串则对应“结构落地后的具体结果”。

3 生成过程:从非终结符到终结符

3.1 推导(Derivation)的基本概念

推导(derivation)是从开始符号出发,反复应用产生式,逐步生成句型序列直至得到最终句子的过程。推导的每一步都属于一次“替换操作”,替换对象通常是当前句型中选中的某个非终结符。

推导可以看作“文法规则的执行轨迹”,而终结符串则是轨迹执行到终点后的产物。

3.2 左推导与右推导

左推导与右推导的区别在于每一步选择替换的位置不同。左推导通常每次优先替换最左侧的非终结符;右推导则优先替换最右侧的非终结符。

两者生成的中间过程可能不同,但只要最终得到的终结符序列相同,都表示该序列在文法中可由规则系统生成。选择哪种推导方式更多影响分析方式与实现细节,而不改变语言成员的集合。

3.3 最终句子如何由终结符构成

当推导完成时,句型中不再存在非终结符。此时句型就是一个纯终结符的序列,它对应语言中的一个句子。由于终结符不再被产生式替换,推导终止于“只剩终结符”的状态。

换句话说,终结符提供了“最终可读的产物形式”,而非终结符提供了“可生成该产物所需的结构蓝图”。

3.4 推导树/语法树的对应关系

为了更直观地展示推导结构,人们常使用推导树或语法树(也常被更广义地称为语法结构树)。在树中,根节点通常是开始符号,内部节点对应非终结符,叶子节点对应终结符。每次产生式应用可以理解为在树中对某个非终结符节点进行展开,替换为一组子节点。

因此,推导树把“逐步替换”转化为“层级结构”的可视化表示:从根向下的展开路径对应推导步骤,叶子从左到右的读取序列对应最终终结符串。

4 解析过程:从终结符反推结构

4.1 自顶向下解析与非终结符的展开

自顶向下解析(top-down parsing)从开始符号出发,试图通过选择产生式不断展开非终结符,来逐步逼近输入的终结符序列。其核心思想是“用文法猜测结构”,然后把猜测与输入相匹配,直到形成完整匹配。

在此过程中,非终结符扮演的是“被预测与展开”的角色;终结符则出现在匹配阶段,作为对输入进行验证的依据。

4.2 自底向上解析与句柄/归约思想

自底向上解析(bottom-up parsing)则从输入中的终结符出发,逐步识别局部结构,最终归约成更高层的非终结符。若说自顶向下是“从大到小展开”,自底向上更像是“从小到大组合”。

归约思想可以理解为:当输入片段与某种产生式右部结构相对应时,用对应的左部非终结符替换该片段。不断重复后,最终得到开始符号对应的整体结构。

4.3 终结符在输入流中的角色

在解析阶段,终结符对应输入中的“可见记号”。解析器的任务是确定:这些终结符是否能被文法生成,并找出对应的结构分解方式。由于终结符不会在文法规则中被替换,它们在解析中的作用更偏向匹配与约束:它们限制了可能的展开或归约路径。

因此,终结符序列既是输入数据,也是推断结构可能性的边界条件

4.4 常见错误理解:把非终结符当作输入词

一种常见误区是将非终结符直接当成输入文本中的“词”。在标准形式语言框架中,非终结符是文法用于描述结构的符号类别,输入流通常只包含终结符(或由词法分析映射得到的终结符记号)。解析器可能在内部推导中引入非终结符,但它们并非输入中原样出现的单位。

这种混淆会导致对解析过程的方向理解错误:把非终结符当输入,会让“展开/归约”的角色关系变得混乱。

5 与编译器实现相关的落地理解

5.1 词法分析输出:终结符的来源

在编译器前端,词法分析(lexical analysis)把原始字符流切分为记号,并为语法分析提供输入序列。若文法以这些记号为终结符,则终结符的来源是词法阶段产生的 token。具体到实现层面,终结符常以记号类型或字面值的组合形式出现,例如 idnumber+ 等。

这也说明终结符并不必然等同于“原始字符”,它可能是“经词法处理后的最小单位”。

5.2 语法分析输入:终结符序列与文法

语法分析(syntax analysis)接收的通常是一串终结符记号序列。解析器根据文法的产生式规则决定该序列是否属于某个语言集合,以及对应的语法结构如何组织。

在实现上,解析动作(展开或归约)会在非终结符层面进行,而最终对输入的消耗与核验由终结符序列驱动。换句话说,终结符决定了“你输入了什么”,非终结符与产生式决定了“你能把它组织成什么结构”。

5.3 抽象语法树(AST)与符号类别映射

在进一步的语义处理阶段,编译器常构建抽象语法树(AST,Abstract Syntax Tree)。AST通常更强调语义相关的结构,可能省略一些纯语法层面的细节。构造AST时,非终结符类别常映射为树节点类型,而终结符通常映射为叶子或携带具体值的字段

例如在表达式解析中,某些产生式对应的非终结符会成为 AST 中的“二元运算”“变量引用”等节点类型;终结符如操作符与标识符则提供运算种类或变量名。

5.4 课堂小梗:符号“上锁”和“可翻译”直觉

在课堂讨论里,终结符常被比作“上锁的词”:它不会再被规则打开替换;非终结符则像“可翻译的标签”:每次遇到它,就能通过产生式把它翻译成更具体的一组符号,直到最终全都变成“上锁”的终结符。 这种直觉并不替代正式定义,但能帮助理解解析器为何必须在某些符号上做展开或归约,而在另一些符号上只做匹配。

6 设计文法时的实践要点

6.1 何时该把某类符号设为终结符

通常当某类符号在输入中以原子记号形式出现,并且不希望在语法层继续拆解时,可以将其设计为终结符。比如词法分析直接输出的 token 类型,常作为终结符使用。这样做能减少语法分析阶段的不确定性,也让产生式更聚焦于结构层的组合。

此外,如果某符号只是“字面层面的固定片段”,也往往适合直接作为终结符纳入规则体系。

6.2 何时应引入非终结符以抽象结构

需要表达层次结构、复用某种组合模式,或希望将复杂句型拆分为可管理的片段时,应引入非终结符。例如“语句”“表达式”“块”等类别能把众多具体情况抽象到统一的语法骨架中。

合适的非终结符设计能让产生式更清晰:结构规律集中在非终结符展开上,而具体终结符负责最终的匹配结果。

6.3 终结符命名与可读性规范

终结符的命名常影响调试与教学可读性。命名时可以遵循一致的风格:运算符与关键字可直接使用其符号或保留字形式;类别性记号可使用抽象名称并保持与词法记号类型对齐。良好的命名能降低“这个终结符到底代表什么输入”的认知成本。

同时,避免终结符与非终结符命名混用,有助于减少解析过程中的误解。

6.4 非终结符数量与文法复杂度的权衡

非终结符越多,文法的抽象粒度可能越细,但产生式数量与解析难度也可能随之上升。过度细分会让规则变得冗长,维护成本提高;而非终结符过少又可能导致产生式过于粗糙,难以表达必要的结构约束。

因此在设计阶段需要平衡:保证结构表达的准确性同时,控制文法规模与解析实现复杂度,力求在可读性、可维护性与可解析性之间取得合理折中。