ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

grammars-v4 中的 BCPL 语法:从 1981 年经典教材到 ANTLR v4 的移植实践

2026/9/24 6:23:14 拓冰建站 浏览量
grammars-v4 中的 BCPL 语法:从 1981 年经典教材到 ANTLR v4 的移植实践 编程语言编译器开发工具【免费下载链接】grammars-v4Grammars written for ANTLR v4; expectation that the grammars are free of actions.项目地址https://gitcode.com/gh_mirrors/gr/grammars-v4点击查看免费下载本文介绍 grammars-v4 仓库中 BCPL 语言的 ANTLR v4 语法实现。BCPLBasic Combined Programming Language是 C 语言的直接祖先之一本仓库以 Martin Richards 与 Colin Whitby-Strevens 合著的《BCPL: the language and its compiler》剑桥大学出版社1981第 8 章语法为蓝本人工录入并改造成 ANTLR v4 文法随后借助转换脚本消除相互左递归、划分词法与语法边界。读完本文你将掌握该语法模块的目录结构、词法/语法规则的对应关系、换行符如何通过自定义 TokenStream 参与语法判定以及如何在本仓库中构建与测试这套文法。语法来源以官方教材第 8 章为蓝本BCPL 语法模块的权威来源是 Martin Richards 与 Colin Whitby-Strevens 合著的《BCPL: the language and its compiler》Cambridge University Press, 1981第 8 章。根据 bcpl/readme.md 的说明这套语法由人工逐条录入并针对 ANTLR v4 进行了适配改造。Martin Richards 本人正是 BCPL 语言的发明者因此以该书语法为蓝本最大程度上保证了文法与官方语言定义的吻合度。在仓库的ignore目录中保留了完整的改造前形态与辅助脚本bcpl/ignore/origbcpl.g4从书中录入的原始语法未拆分词法/语法规则名沿用书中的小写命名如letter、octal_digit、hex_digit、digit、character_constant、number、identifier其中还保留了书中语法对字母、八进制数字、十六进制数字的逐字符展开写法bcpl/ignore/bcpl.g4转换过程中的中间产物bcpl/ignore/transform.sh核心转换脚本负责消除左递归与词法/语法边界划分详见下一节。从源码结构可以推断ignore目录专门用于存放未经转换的原始语法与转换工具避免与仓库中可实际使用的bcplLexer.g4、bcplParser.g4混淆。转换流程用 Trash 消除相互左递归并划分词法/语法边界readme 明确提到语法录入后脚本 bcpl/ignore/transform.sh 借助工具Trash完成两件事一是移除相互左递归mutual left recursion二是在词法器与解析器之间定义恰当的分工边界。第一步消除相互左递归原始语法中repeatable_command与repeated_command存在相互左递归引用。transform.sh 通过 Trash 的 XPath 式查询定位规则引用并逐步改写// 读取书中语法文件 read bcpl.g4 // 解析文法任何变换前必须先 parse parse // 将 repeatable_command 规则中具体出现的 repeated_command // 引用展开unfold为实际使用的形式 unfold //parserRuleSpec[RULE_REF/text()repeatable_command]//RULE_REF[text()repeated_command] // 对展开后的规则去括号ungroup补全相互左递归结构 ungroup //parserRuleSpec[RULE_REF/text()repeatable_command]//labeledAlt/alternative/element[ebnf//RULE_REF[text()repeatable_command]] write quit完成改写后脚本立即用dotnet-antlr对文法做语法检查、生成代码、编译并运行测试确保变换没有破坏文法正确性。这一变换—校验的循环正是 ANTLR v4 对左递归规则有限制仅支持直接左递归不支持间接/相互左递归背景下的典型处理路径——从源码结构看最终解析器中repeatable_command不再引用repeated_command规则而是直接内联了REPEAT、REPEATUNTIL expression、REPEATWHILE expression等后续形式见 bcpl/bcplParser.g4。第二步重命名规则、划分词法/语法边界脚本的第二阶段用rename命令把书中位于解析器层的字符级规则改名为词法规则大写首字母从而把字符识别下沉到词法器把结构识别留在解析器rename //parserRuleSpec//labeledAlt//RULE_REF[text() letter] Letter rename //parserRuleSpec//labeledAlt//RULE_REF[text() octal_digit] Octal_digit rename //parserRuleSpec//labeledAlt//RULE_REF[text() hex_digit] Hex_digit rename //parserRuleSpec//labeledAlt//RULE_REF[text() digit] Digit rename //parserRuleSpec//labeledAlt//RULE_REF[text() string_constant] String_constant // 注意顺序one_character 必须紧跟在 string_constant 之后重命名 rename //parserRuleSpec//labeledAlt//RULE_REF[text() one_character] One_character rename //parserRuleSpec//labeledAlt//RULE_REF[text() character_constant] Character_constant rename //parserRuleSpec//labeledAlt//RULE_REF[text() octal_number] Octal_number rename //parserRuleSpec//labeledAlt//RULE_REF[text() hex_number] Hex_number rename //parserRuleSpec//labeledAlt//RULE_REF[text() number] Number rename //parserRuleSpec//labeledAlt//RULE_REF[text() identifier] Identifier write quit这一步完成后原先字母、数字、字符串常量等字符级定义从解析器规则变为词法规则最终形成了今天仓库中的 bcpl/bcplLexer.g4 与 bcpl/bcplParser.g4 双文件结构。转换脚本末尾同样以dotnet-antlr校验、生成、构建、测试收尾并清理Generated临时目录。词法器BCPL 的 Token 全貌bcpl/bcplLexer.g4 是独立的lexer grammar bcplLexer其 Token 设计可以归纳为五类。运算符与分隔符BCPL 的特色赋值运算符族被逐一映射为独立 Token例如Token字面量说明CEQ:普通赋值ACEQ:按位与赋值LLCEQ/RRCEQ:/:移位赋值SCEQ/SLCEQ*://:乘除赋值PLCEQ/NCEQ:/~:加减/按位取反赋值MODCEQMOD:取模赋值EQVCEQ/NEQVCEQ/XORCEQEQV:/NEQV:/XOR:逻辑等价类赋值CB/OB$)/$(}或}/{块结束/开始兼容 BCPL 的$( $)风格与 C 风格花括号CC::名字空间/字段选择DD/DOT../.区间/点需要特别说明的是 BCPL 的向量下标运算符!TokenBANG与地址运算符TokenAT、**间接引用%TokenPERCENT**都在词法器层被保留为运算符 Token。关键字BCPL 关键字以K前缀命名如KIF、KWHILE、KUNTIL、KFOR、KREPEAT、KREPEATUNTIL、KREPEATWHILE、KTEST、KVALOF、KMANIFEST、KGLOBAL、KLET、KSTATIC、KRESULTS对应RESULTIS等涵盖声明、控制流与块结构三类语义。此外TRUE、FALSE、EQ、NE、MOD、REM、ABS、FLOAT、FIX、SLCT、TABLE、MATCH、EVERY等 BCPL 保留词也都拥有独立 Token。数字与字符/字符串常量Binary_number : (#B | #b) [01]; Character_constant : \ *? . \; Digits : Digit; Hex_number : (#X | #x) Hex_digit Hex_digit*; Octal_number : # Octal_digit Octal_digit*; String_constant : ~* /* 255 或更少字符 */ ;这里体现了 BCPL 的数字字面量传统以#前缀区分进制——#加八进制数字表示八进制数#X/#x表示十六进制数#B/#b表示二进制数无前缀数字串为十进制。字符常量支持可选的*前缀BCPL 的转义记法字符串常量则限定为 255 个字符以内词法规则中以注释标注了这一约束。注释与换行的通道设计关键实现点Comment : (/* .*? */ | // ~(\n | \r)*) - channel(HIDDEN); WS : [ \t] - channel(HIDDEN); NL : [\n\r] - channel(2);注释与空白被送往HIDDEN通道而换行符被送往自定义通道 2而不是简单地丢弃。这一设计是解析器能够实现换行即语句分隔符的关键——BCPL 使用换行以及分号作为语句分隔符解析器必须能够感知换行位置详见下文IsNl/IsNotNl谓词与ChannelCommonTokenStream的配合。词法器还通过四个 fragment 规则Letter、Octal_digit、Hex_digit、Digit组织字符类Identifier允许字母、数字、点号与下划线且允许点号出现在标识符内部——这是 BCPL 标识符的经典写法例如writef、带点段的系统名。解析器从声明到命令的层级结构bcpl/bcplParser.g4 是parser grammar bcplParser通过options { tokenVocab bcplLexer; superClass bcplParserBase; }关联词法器并指定自定义解析器基类。入口规则为program : directive* declaration_part EOF ;即一个程序由若干directiveGET/SECTION文件包含指令、一个声明序列构成。语法规则的分节注释直接沿用了原书章节编号8.8.x方便对照教材阅读8.8.2 运算符mult_op*/REM、add_op、shift_op、and_op、or_op8.8.3-8.8.5 表达式expression、constant_expression常量表达式按优先级层层嵌套c_mult_E→c_add_E→c_shift_E→c_and_E→constant_expression、expression_list、name_list8.8.6 声明manifest_declarationMANIFEST、static_declarationSTATIC、global_declarationGLOBAL、simultaneous_declarationLET ... AND ...支持简单定义、VEC向量定义、函数定义与BE例程定义同时声明8.8.7-8.8.9 命令assignment左侧left_hand_side_listassop 右侧expression_list、simple_commandBREAK/LOOP/ENDCASE/RETURN/FINISH、goto_command、routine_command、resultis_command、switchon_command、if_command/unless_command/test_commandTEST ... THEN/DO ... ELSE/OR ...、repetitive_commandREPEAT族、UNTIL、WHILE、FOR、以及带前缀label_prefix、case_prefix、default_prefix的command规则8.8.10 块与复合命令block$( ... $)或{ ... }、compound_command、command_list、declaration_part其中semi规则允许;或多个换行作为分隔符扩展部分directiveGET/SECTION、fnameFLT、bexp基本表达式含SLCT位段选择、MATCH/EVERY模式匹配、VALOF表达式、TABLE向量常量等 BCPL 特色构造、以及e0~e9一系列优先级递减的表达式层e9最高、e0最低每个层级通过nonl此处不是换行谓词连接后续算子实现运算符优先级与结合性的精确控制。换行敏感语法自定义 TokenStream 与解析器基类BCPL 的换行具有语法意义可充当语句分隔符但 ANTLR 默认的CommonTokenStream只会按DEFAULT_TOKEN_CHANNEL取 Token会丢弃通道 2 上的换行信息。仓库为此提供了两层配套实现。ChannelCommonTokenStream按通道回看/前瞻Java 版本 bcpl/Java/ChannelCommonTokenStream.java 与 C# 版本 bcpl/CSharp/ChannelCommonTokenStream.cs 扩展了CommonTokenStream新增带通道参数的LT(k, ch)方法其内部通过myPreviousTokenOnChannel/myNextTokenOnChannel在指定通道这里是通道 2即换行通道上向前/向后查找 Token同时保证DEFAULT_TOKEN_CHANNEL上的 Token 与 EOF 始终可见。这样解析器就能在取下一个普通 Token的同时单独查询上一个/下一个换行 Token的位置。bcplParserBaseIsNl / IsNotNl 谓词Java 版本 bcpl/Java/bcplParserBase.java 与 C# 版本 bcpl/CSharp/bcplParserBase.cs 实现了Parser的自定义子类并在构造函数中用ChannelCommonTokenStream包装输入流。两个语义谓词正是换行判定逻辑public boolean IsNl() { Token c ((ChannelCommonTokenStream)this.getInputStream()).LT(-1, 2); Token d ((ChannelCommonTokenStream)this.getInputStream()).LT(1, 2); return c.getType() bcplParser.NL; } public boolean IsNotNl() { Token c ((ChannelCommonTokenStream)this.getInputStream()).LT(-1, 2); Token d ((ChannelCommonTokenStream)this.getInputStream()).LT(1, 2); return d.getType() ! bcplParser.NL; }解析器中nl/nonl规则分别以{ IsNl() }?与{ IsNotNl() }?作为语义谓词见 bcpl/bcplParser.g4 与 bcpl/bcplParser.g4从而决定表达式链中的下一个算子是否可以跨行继续或语句是否在此处结束。这套自定义 TokenStream 按通道取 Token 基类谓词的组合是本语法模块最具复用价值的设计任何换行敏感的语言如 BCPL、旧式 BASIC都可以照搬这一模式。示例与测试仓库提供了 6 个 BCPL 示例文件bcpl/examples/fact.bcpl阶乘递归示例bcpl/examples/fib.bcpl斐波那契数列bcpl/examples/hw.bcplHello Worldbcpl/examples/cg8086.b、bcpl/examples/interp.b、bcpl/examples/unify.b来自互联网收集的较大程序。以 bcpl/examples/fact.bcpl 为例它涵盖了GET指令、LET ... VALOF ...函数定义、FOR ... TO ... DO循环、RESULTIS返回值与AND多函数同时声明、以及n0 - 1, n*factorial(n-1)的 BCPL 条件表达式写法可作为快速验证语法树的手工测试用例。readme 指出示例的最佳来源是 Martin Richards 维护的 BCPL 编译器源码发布包bcpl.gtz仓库示例即从互联网收集而来。构建与验证方式该模块的 Maven 配置 bcpl/pom.xml 定义了完整的构建链路antlr4-maven-plugin从${basedir}目录选取bcplParser.g4与bcplLexer.g4生成解析器代码同时开启visitor与listener两种遍历模式antlr4test-maven-plugincom.khubla.antlr以program为入口规则、examples/为示例目录将仓库中的 BCPL 示例作为测试输入自动运行验证文法能完整解析全部示例。因此在本仓库根目录下可以按常规方式执行 Maven 构建与测试mvn test -pl bcpl或先安装父 POM 后整体构建。此外仓库的 test.sh 脚本体系也覆盖了各语法模块的批量验证。desc.xmlbcpl/desc.xml声明该模块的目标语言为CSharp;Java与仓库中CSharp/、Java/两个目标目录一一对应——如果你需要为其他语言如 Python、Go、TypeScript生成解析器可参考仓库根目录 README.md 中的多语言说明自行生成。小结BCPL 语法模块的价值在于它完整呈现了从经典教材文法到可运行 ANTLR v4 语法的移植方法论忠实来源以《BCPL: the language and its compiler》第 8 章为蓝本规则分节注释与书中 8.8.x 编号一一对应可复现的转换流程ignore/transform.sh展示了用 Trash 消除相互左递归、重命名规则、划分词法/语法边界的完整操作序列换行敏感语法的工程方案ChannelCommonTokenStreambcplParserBaseIsNl/IsNotNl的组合为处理依赖换行的语言提供了现成的参考实现开箱即用的测试闭环6 个示例文件与 Maven 测试插件配合可一键验证文法正确性。该模块代码总量不大、结构清晰既是学习 BCPL 语言的语法入门材料也是研究 ANTLR v4 移植技巧左递归消除、通道机制、语义谓词的极佳范本。赞分享编程语言编译器开发工具【免费下载链接】grammars-v4Grammars written for ANTLR v4; expectation that the grammars are free of actions.项目地址https://gitcode.com/gh_mirrors/gr/grammars-v4点击查看免费下载相关推荐grammars-v4 中的 HyperTalk 语法用 ANTLR v4 解析 HyperCard 脚本语言grammars v4 中的 HyperTalk 语法用 ANTLR v4 解析 HyperCard 脚本语言 导读 本文讲解 grammars v4 htt编程语言编译器开发工具GNU Bison 语法文件的 ANTLR v4 解析实现grammars-v4 中的 bison 文法剖析GNU Bison 语法文件的 ANTLR v4 解析实现grammars v4 中的 bison 文法剖析 导读 本文围绕 grammars v4 仓库中的编程语言编译器开发工具移动端语法解析优化antlr/grammars-v4实战指南移动端语法解析优化antlr/grammars v4实战指南 在移动应用开发中语法解析Syntax Parsing是处理代码编辑器、动态配置解析等功能的编程语言编译器开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考