MuMath ANTLR4 语法解析指南:grammars-v4 中 mumath 文法的结构、规则与实战用法
【免费下载链接】grammars-v4Grammars written for ANTLR v4; expectation that the grammars are free of actions.项目地址: https://gitcode.com/gh_mirrors/gr/grammars-v4
MuMath(muMATH)是 20 世纪 70 年代诞生的一款早期计算机代数系统,其源码文件具有独特的函数式与命令式混合语法。本指南以 grammars-v4 仓库中的 mumath 目录 为对象,完整拆解由 Tom Everett 移植、Dan Stanger 原始编写的 ANTLR4 文法 mumath.g4,覆盖程序结构、语句构造、表达式优先级、词法记号定义以及基于 Maven 与 antlr4-tools 的构建、测试与跨语言生成方法。读完本文,你将能够读懂任何一份 MuMath 语法的解析树结构,掌握 ANTLR4 文法组织表达式优先级与语句块的通用套路,并能在本地直接编译、测试这套文法。
MuMath 文法文件简介
mumath/README.md 对该文法的定位非常明确:这是一份面向 MuMath 源码文件的 ANTLR4 文法,由 Tom Everett 从 Dan Stanger 编写的 ANTLR3 文法移植而来。这份移植历史也原样保留在 mumath.g4 的文件头注释中:
/* * MuMath originally written for Antlr3 by Dan Stanger * * Ported to Antlr4 by Tom Everett * */与 grammars-v4 仓库中绝大多数文法一样,该文法不含任何嵌入的语义动作(actions),只负责生成语法树,适合作为解析器生成的纯净输入。目录结构非常精简:
- mumath.g4:唯一的文法文件(词法 + 语法规则合并在同一文件中)
- examples/example1.txt:用于验证解析器的示例输入
- pom.xml:Maven 构建与自动化测试配置
- desc.xml:声明该文法支持的 ANTLR 目标语言
- README.md:简短的项目说明
语法全景:从 program 顶层规则看 MuMath 程序结构
语法入口规则program定义在 mumath.g4,它决定了 MuMath 文件的整体形态:
program : ((functionDefinition | assignment | functionDesignator) (SEMI | DOLLAR))* EOF ;从这条规则可以读出三层信息:
- 一个 MuMath 程序由零个或多个顶层元素组成,每个元素要么是函数定义(
functionDefinition)、赋值语句(assignment),要么是函数调用/设计符(functionDesignator)。 - 每个顶层元素必须以分号
;(SEMI)或美元符$(DOLLAR)结尾——$在 MuMath 中同样承担语句终结符的职责。 - 整个输入必须以
EOF结束,未完整解析的输入会被判定为语法错误。
也就是说,MuMath 是一种语句驱动的语言:顶层没有声明区、没有BEGIN...END包裹,程序体就是一系列被终结符隔开的赋值、函数定义和函数调用。这与仓库中其他过程式语言(如 tinyc、pascal)以program ... begin ... end为主干的风格明显不同。
语句级构造:赋值、函数定义、块、循环与条件
program之下,MuMath 的语句层由五类规则组成,均位于 mumath.g4。
赋值(assignment)——支持多重目标
assignment : (ID COLON)+ expression ;(ID COLON)+表示一个或多个标识符:前缀,随后接一个表达式。这意味着 MuMath 支持类似A:B:expr的多重赋值形式:多个变量名依次跟冒号,共享同一个表达式结果。这在早期代数语言中用于一次性给多个符号绑定值,与后来语言中的并行解构赋值思路一脉相承。
函数定义(functionDefinition)
functionDefinition : FUNCTION ID list_ COMMA statments (COMMA)? ENDFUN ; list_ : LPAREN (RPAREN | ID (COMMA ID)* RPAREN) ;函数定义以关键字FUNCTION开头、ENDFUN结尾。list_规则解析形参列表,且允许空参数表(),也允许逗号分隔的多个标识符。函数体是statments(注意:文法中这个规则名的拼写是statments而非statements,属于移植时保留的原始命名,使用时需留意),末尾的(COMMA)?表示函数体之后可以带一个可选的逗号再结束。
语句序列(statments)
statments : (loop | when | block | assignment | expression | functionDesignator) (COMMA statments)* ;语句序列是 MuMath 的核心组合子:一条语句可以是循环、条件、块、赋值、表达式或函数调用,且多条语句用逗号,连接——这与顶层用分号/美元符分隔不同,是 MuMath 在语句块内使用逗号作为分隔符的特色语法。
块、循环与条件
block : BLOCK statments COMMA ENDBLOCK ; loop : LOOP statments (COMMA)? ENDLOOP ; when : WHEN expression ((COMMA)? EXIT COMMA statments (COMMA)? EXIT) ;block:BLOCK ... ENDBLOCK结构,块内语句序列以逗号结束。loop:LOOP ... ENDLOOP结构,构成循环体,循环出口机制由when提供。when:MuMath 的条件控制结构,WHEN 条件 EXIT, 语句, EXIT——条件成立时执行第一个EXIT分支,否则落入第二个EXIT分支。这是 MuMath 特有的"条件即出口"写法:用EXIT同时表达条件分支与循环退出。
表达式体系:运算符分级与优先级
MuMath 的表达式规则完整覆盖了代数系统所需的运算层次,分布在 mumath.g4。ANTLR4 文法通过规则嵌套的深度来表达运算符优先级——越内层的规则,优先级越高。
四层优先级结构
| 优先级(由低到高) | 规则 | 运算符 |
|---|---|---|
| 1. 关系运算 | expression/relationalOperator | =,EQ,<>,<,<=,>=,>,== |
| 2. 加法级 | simpleExpression/addingOperator | +,-,OR |
| 3. 乘法级 | term/multiplyingOperator | *,/,mod,AND,^(幂) |
| 4. 因子级 | factor | 标识符、常量、括号、函数调用、NOT |
expression : simpleExpression (relationalOperator simpleExpression)* ; simpleExpression : (MINUS)? term (addingOperator term)* ; term : factor (multiplyingOperator factor)* ; factor : ID | constant | LPAREN expression RPAREN | functionDesignator | NOT factor ;值得注意的几个文法设计点(以下均为从文法结构可直接观察到的行为):
^(POWER)被放在乘法级multiplyingOperator中,与*、/、mod、AND同级,而不是像多数语言那样独占更高的优先级层。因此X^2*Y会被解析为(X^2)*Y还是X^(2*Y),取决于同层左递归的实际推导顺序;从文法结构看,^并不具有独立于乘除的优先级层。- 一元负号
-挂在simpleExpression层,只作用于第一个term,优先级低于因子级,因此-X^2实际对应-(X^2)还是(-X)^2需按该层的推导顺序判断。 NOT是因子级规则(NOT factor),优先级最高,可以递归作用于任意因子。OR与+、-同级,AND与*、/同级,这与现代语言中逻辑运算通常低于算术运算的习惯不同,是 MuMath 文法自身的取舍。
等号的双重身份:EQF 与 EQC
equal规则同时接受EQF(关键字EQ)与EQC(符号=)两种写法:
equal : (EQF | EQC) ; EQF : 'EQ' ; EQC : '=' ;由于=在assignment中并不作为赋值符出现(赋值用的是:),文法中不存在=的歧义冲突,=与EQ均可安全地充当关系比较运算符。
函数调用与常量
functionDesignator : ID LPAREN ((actualParameter (COMMA actualParameter)*) |) RPAREN ; actualParameter : expression | assignment ; constant : (NUMBER | STRING | QUOTE ID | QUOTE STRING) ;functionDesignator允许空参数表,也允许一个或多个actualParameter;每个实参可以是表达式,也可以是赋值语句。constant除数字与字符串外,还允许'标识符与'字符串(QUOTE即单引号')这种带引号的符号形式,用于表示 MuMath 中的"引号化"表达式(阻止求值的符号)。
词法层:关键字、运算符、标识符与字面量
词法规则 与语法规则合并在同一.g4文件中,全部采用字面量或字符区间定义。
关键字(大小写敏感的保留字)
| 记号 | 字面量 | 用途 |
|---|---|---|
BLOCK/ENDBLOCK | BLOCK/ENDBLOCK | 语句块边界 |
FUNCTION/ENDFUN | FUNCTION/ENDFUN | 函数定义边界 |
LOOP/ENDLOOP | LOOP/ENDLOOP | 循环边界 |
WHEN/EXIT | WHEN/EXIT | 条件出口 |
OR/AND/NOT | OR/AND/NOT | 逻辑运算符 |
MOD | mod(小写) | 取模 |
EQF | EQ | 等于(关键字形式) |
注意一个细节:MOD的字面量是小写mod,而OR、AND、NOT、WHEN、EXIT、BLOCK等均为大写——这意味着 MuMath 的保留字是大小写敏感的,且mod必须小写书写。
运算符与分隔符
| 记号 | 字面量 | 记号 | 字面量 |
|---|---|---|---|
EQUATION | == | PLUS | + |
EQC | = | MINUS | - |
NOT_EQUAL | <> | STAR | * |
LT/LE | </<= | SLASH | / |
GT/GE | >/>= | POWER | ^ |
QUOTE | ' | COMMA | , |
LPAREN/RPAREN | (/) | SEMI | ; |
COLON | : | DOLLAR | $ |
标识符、数组下标、字符串与数字
ID : ('A' .. 'Z' | '@' | '{' | '#') ('A' .. 'Z' | '0' .. '9' | '#' | '}')* (ARR)? ; ARR : '[' NUMBER ']' ; STRING : '"' (~ '"')* '"' ; NUMBER : ('0' .. '9')+ ;- 标识符只能以大写字母
A-Z、@、{或#开头,后续字符限大写字母、数字、#、}。因此 MuMath 变量名是大写体系(如X、LOG),小写字母不参与标识符构成。 - 标识符尾部可以携带一个数组下标
ARR,形如[数字](如A[3]),下标必须是纯数字。 - 字符串使用双引号包裹,且内部不允许出现未转义的双引号(
~ '"'排除双引号)。 - 数字仅由十进制数字组成,文法中未定义小数、负数或科学计数法记号(负号由语法层的
MINUS处理)。
空白与注释
WS : (' ' | '\t' | '\n' | '\r') -> skip ; COMMENT : '%' ('\n' | ~ ('%' | '\n'))* '%' -> skip ;WS跳过空格、制表符、换行与回车,即空白不参与语法。- 注释采用
% ... %定界:以%开头,内容可以是换行符或任意非%非换行字符,再以%结束。从规则结构看,注释不能跨行(内容中虽允许出现换行,但最终必须在本行内以%闭合),且注释在词法阶段直接被skip丢弃。
用示例验证:INT (LOG (X)^10,X);的解析过程
目录中唯一的示例文件 examples/example1.txt 内容为:
INT (LOG (X)^10,X);这是一条典型的 MuMath 定积分表达式(对LOG(X)^10关于X积分)。对照文法逐层推导:
program匹配一个顶层元素:INT (LOG (X)^10,X)是functionDesignator,结尾的;是SEMI,随后EOF结束。functionDesignator:ID为INT,括号内是两个actualParameter,由COMMA分隔:- 第一个实参
LOG (X)^10:外层是term中的factor——functionDesignator(LOG(X)),随后multiplyingOperator的^(POWER),再乘方因子10。其中LOG(X)内部是嵌套的functionDesignator,X是factor中的ID。 - 第二个实参
X:直接是factor→ID。
- 第一个实参
这行示例同时验证了顶层语句、函数调用、嵌套调用、幂运算与实参列表的解析路径,可以作为运行文法测试的标准输入。
构建、测试与跨语言目标
Maven 构建与自动化测试
mumath/pom.xml 使用两个插件完成"生成解析器 + 自动跑示例"的闭环:
- antlr4-maven-plugin:
sourceDirectory指向文法所在目录${basedir},只包含mumath.g4一个文法文件,并开启visitor=true与listener=true,即生成代码同时包含 Visitor 与 Listener 两种遍历接口。 - antlr4test-maven-plugin(khubla):以
program为入口规则、mumath为文法名,将examples/目录作为测试样例来源。也就是说,只要mvn test能成功解析 examples/example1.txt,就视为文法测试通过。
仓库根目录的 test.sh 提供了统一入口:它会先检查mvn是否可用,然后执行mvn test。在仓库根目录运行:
./test.sh # 或仅测试本文法 mvn -f mumath/pom.xml test支持的目标语言
desc.xml(其格式受 _scripts/desc.xsd 约束)声明了该文法可生成的目标语言:
CSharp;Cpp;Dart;Go;Java;JavaScript;PHP;Python3;TypeScript;Antlr4ng这意味着同一份mumath.g4可以面向 Java、C#、C++、Dart、Go、JavaScript、PHP、Python3、TypeScript 以及 ANTLR4ng 运行时生成对应的 Lexer/Parser 代码,语法规则本身无需改动——这正是 grammars-v4 中"纯文法、无动作"设计带来的跨语言可移植性。
无需安装 JDK 的快速试玩
仓库的 _scripts/antlr4-tools/README.md 提供了免安装方案:pip install antlr4-tools后即可获得antlr4与antlr4-parse命令,首次运行会自动下载 Java 运行时与 ANTLR jar。例如用解释器直接解析示例输入并打印语法树:
pip install antlr4-tools antlr4-parse mumath/mumath.g4 program -tree < mumath/examples/example1.txt输出即为program规则下的完整解析树,可用于快速核对上文对INT (LOG (X)^10,X);的逐层推导;也可以加-tokens参数查看词法记号流,观察INT、LPAREN、POWER、SEMI等记号的实际切分结果。
小结与使用提示
- 本文法来自 Dan Stanger 的 ANTLR3 版本、由 Tom Everett 移植为 ANTLR4,位于 mumath/mumath.g4,顶层入口规则为
program,示例与测试配置齐全,可直接通过 Maven 或 antlr4-tools 运行。 - 语法上的三个辨识点:块内语句以逗号分隔、赋值采用
ID:前缀且支持多重赋值、条件由WHEN ... EXIT, ..., EXIT表达。 - 表达式的四层优先级(关系 < 加减/OR < 乘除/mod/AND/幂 < 因子)完全由文法规则嵌套决定,其中
^与AND被归入乘法层、OR被归入加法层,是这套文法区别于现代语言的特色。 - 词法上保留字大小写敏感(
mod为小写)、标识符仅限大写体系字符、注释使用% ... %定界,使用时应严格遵循这些约束,避免在输入文件中混入小写变量名或未闭合的注释。
对于需要在 Java、Python、TypeScript 等任意受支持目标中解析 MuMath 文件的开发者,直接复用 mumath.g4 并按 desc.xml 声明的目标生成代码即可;若需修改文法行为(例如调整^的优先级层级),所有语法规则与词法记号都集中在这一个文件中,便于定位与维护。
【免费下载链接】grammars-v4Grammars written for ANTLR v4; expectation that the grammars are free of actions.项目地址: https://gitcode.com/gh_mirrors/gr/grammars-v4
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考