十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Baserow 公式语言语法工程解析:基于 ANTLR 的文法设计、词法规则与双端解析器生成流程

Baserow 公式语言语法工程解析:基于 ANTLR 的文法设计、词法规则与双端解析器生成流程 Baserow 公式语言语法工程解析基于 ANTLR 的文法设计、词法规则与双端解析器生成流程【免费下载链接】baserowBuild databases, automations, apps agents with AI — no code. Open source platform available on cloud and self-hosted. GDPR, HIPAA, SOC 2 compliant. Best Airtable alternative.项目地址: https://gitcode.com/GitHub_Trending/ba/baserow导读Baserow 的公式字段Formula Field允许用户在数据库字段中编写表达式实现跨字段计算、字符串拼接与条件判断。支撑这一能力的是仓库formula/目录下的自定义公式语言及其 ANTLR 文法。本文以 formula/README.md 为主干深入解析 Baserow Formula Language 的文法定义.g4文件、词法规则、运算符优先级设计并结合 后端解析器 与 前端解析器 的源码实现说明如何通过./build.sh一键重新生成 JavaScript 与 Python 双端解析器。读完本文你将掌握 Baserow 公式语言的完整语法骨架、修改语法的标准流程以及语法变更如何贯穿前后端代码生成链路。Baserow Formula Language 是什么Baserow 是一个开源的无代码数据库平台其公式字段支持类电子表格的表达式例如concat(field(姓名), - , field(部门))、field(单价) * field(数量)等。为了让这套公式语言同时服务于后端Python负责计算、校验与持久化和前端JavaScript负责输入时的实时解析、自动补全与高亮Baserow 选择使用 ANTLR 定义一份独立于语言的文法再据此生成 Python3 与 JavaScript 两套解析器代码。formula/README.md 明确说明该目录存放的是 Baserow 自定义公式语言的 ANTLR 文法。目录结构如下BaserowFormula.g4解析器文法parser grammar定义表达式如何组合成语法树BaserowFormulaLexer.g4词法文法lexer grammar定义字符串如何被切分为 tokenBaserowFormula.tokens 与 BaserowFormulaLexer.tokensANTLR 生成的 token 编号表供词法/解析器文法及代码生成共享Dockerfile构建解析器所用的 Java/ANTLR 运行环境README.md修改语言的官方流程说明。此外公式语言的运行时实现分布在 backend/src/baserow/core/formula/Python 后端与 web-frontend/modules/core/formula/前端两处文法是这两处实现共享的语言规范源头。文法结构BaserowFormula.g4解析入口规则与递归下降root是 ANTLR 解析的起点它要求整个输入必须恰好匹配一个expr并紧随文件结束符root : expr EOF ;expr是核心的递归规则。从 BaserowFormula.g4 可以看到它通过|分隔多个备选分支ANTLR 从上到下依次尝试匹配命中第一个能完整匹配的分支。由于expr可以引用自身用户能够构造任意嵌套的复杂表达式例如括号内的二元运算、函数调用中再嵌套函数调用。标签Label与 Visitor 生成文法中大量使用# Label后缀为规则分支打标签。例如| SINGLEQ_STRING_LITERAL # StringLiteral | DOUBLEQ_STRING_LITERAL # StringLiteral | INTEGER_LITERAL # IntegerLiteral | NUMERIC_LITERAL # DecimalLiteral | (TRUE | FALSE) # BooleanLiteral注释中解释了这种设计的动机多个不同的分支被打上同一个标签如单引号字符串与双引号字符串都标记为StringLiteral后ANTLR 代码生成阶段会为每个标签生成对应的visitLabel方法使开发者可以在 Visitor 中按逻辑分组统一处理一类节点。以 Python 端为例formula_execution_visitor.py 中visitStringLiteral、visitDecimalLiteral、visitBooleanLiteral正是这些标签生成的钩子方法它们分别在求值阶段把 token 文本转换为 Python 的字符串、浮点数和布尔值。运算符优先级规则顺序即优先级文法注释特别强调规则出现的顺序直接控制表达式的执行优先级。由于SLASH/STAR所在的二元运算规则排在最前PLUS/MINUS规则排在其后因此11/2中1/2会先于加法被求值乘除优先于加减。完整优先级从高到低依次为优先级规则分支说明最高ws_or_comment expr/expr ws_or_comment空白与注释包裹不影响值OPEN_PAREN expr CLOSE_PAREN括号强制分组expr op(SLASH \| STAR) expr乘除expr op(PLUS \| MINUS) expr加减expr op(GT \| LT \| GTE \| LTE) expr大小比较expr op(EQUAL \| BANG_EQUAL) expr相等比较expr opAMP_AMP expr逻辑与最低expr opPIPE_PIPE expr逻辑或\|\|这一顺序与求值 Visitor 中的实现一一对应在 formula_execution_visitor.py 中visitBinaryOp根据实际命中的运算符 tokenPLUS/MINUS/SLASH/EQUAL/BANG_EQUAL/STAR等将节点映射到add、minus、divide、equal、not_equal、multiply等操作。字段引用与函数调用文法中对字段引用和函数调用有专门的语法形态| FIELD OPEN_PAREN field_reference CLOSE_PAREN # FieldReference // FIELDBYID has been deprecated and should not be used, it is only included here // for backwards compatability. | FIELDBYID OPEN_PAREN INTEGER_LITERAL CLOSE_PAREN # FieldByIdReference | LOOKUP OPEN_PAREN field_reference COMMA WHITESPACE? field_reference CLOSE_PAREN # LookupFieldReference | func_name OPEN_PAREN (expr (COMMA expr)*)? CLOSE_PAREN # FunctionCall要点如下field(字段名)通过FIELD关键字加字符串字面量引用字段是当前推荐的字段引用方式field_by_id(123)通过FIELDBYID加整数引用字段文法注释明确标注该写法已废弃仅出于向后兼容保留不应在新公式中使用后端 formula_execution_visitor.py 中还专门导出了FieldByIdReferencesAreDeprecated异常用于提示lookup(关联字段, 目标字段)LOOKUP支持通过两个字段引用实现关联表的查找取值且允许在逗号后出现可选空白函数名(参数1, 参数2, ...)func_name由identifier构成参数是零个或多个由逗号分隔的expr因此参数本身也可以是任意嵌套表达式。func_name与identifier的定义支持 ASCII 标识符与 Unicode 标识符IDENTIFIER_UNICODE这意味着函数名可以使用非 ASCII 字符为多语言场景留出了空间。词法规则BaserowFormulaLexer.g4解析BaserowFormulaLexer.g4 负责把原始公式字符串切分为 token。其结构可划分为几个层次。Fragment 与大小写不敏感的关键字文法开头通过fragment A : (A|a) ;等 26 个字母片段定义了大小写不敏感的基础单元再组合出TRUE、FALSE、FIELD、FIELDBYID、LOOKUP等关键字。例如TRUE : T R U E; FIELD : F I E L D; FIELDBYID : F I E L D UNDERSCORE B Y UNDERSCORE I D;这意味着TRUE、true、True等大小写变体都会被识别为布尔字面量field(...)与FIELD(...)等价。求值 Visitor 中visitBooleanLiteral正是通过ctx.TRUE() is not None判断取值的验证了这一点。字面量字符串SINGLEQ_STRING_LITERAL单引号...与DOUBLEQ_STRING_LITERAL双引号...内部支持反斜杠转义数字NUMERIC_LITERAL匹配形如-3.14、2.5E-3的十进制小数INTEGER_LITERAL匹配-42、1E6等整数及科学计数法特殊字面量BIT_STRING如B1010、REGEX_STRINGe...形式的正则字符串、HEX_INTEGER_LITERALxFF十六进制。空白与注释BLOCK_COMMENT/* ... */、LINE_COMMENT// ...与WHITESPACE空格、制表符、回车、换行作为独立的 token 保留在流中供解析器文法中的ws_or_comment规则显式引用。ws_or_comment既可在expr左侧也可在右侧出现从而允许公式中任意位置书写空白和注释而不影响求值。运算符 token 家族词法文法定义了极其丰富的运算符 token除了解析器文法实际使用的 - * /、 、 !、 ||之外还包含大量面向 PostgreSQL 风格操作符的保留 token例如-、-、#、#、、、、~*、~~等。这些 token 虽未全部出现在当前解析器文法的expr规则中但它们的预定义保证了公式语言未来的可扩展性。ErrorCharacter 兜底文法最后定义了ErrorCharacter : . ;并注释说明任何未匹配上述规则的字符都会以ErrorCharactertoken 出现在 token 流中从而保证词法分析器本身永不报语法错误所有错误处理统一交给解析器层完成。双端解析器从同一份文法到 Python 与 JavaScriptBaserow 的公式语言必须同时被前后端解析这一需求通过 ANTLR 的跨语言代码生成实现。后端Python3后端在 backend/src/baserow/core/formula/parser/ 下保存了生成的解析器generated/BaserowFormulaLexer.py生成的词法分析器generated/BaserowFormula.py生成的解析器generated/BaserowFormulaVisitor.py与generated/BaserowFormulaListener.pyVisitor/Listener 基类。parser.py 展示了后端如何驱动 ANTLR 解析公式def get_parse_tree_for_formula(formula: str): lexer BaserowFormulaLexer(InputStream(formula)) stream CommonTokenStream(lexer) parser BaserowFormula(stream) parser.removeErrorListeners() parser.addErrorListener(BaserowFormulaErrorListener()) return parser.root()代码中通过parser.removeErrorListeners()移除 ANTLR 默认的错误监听器再注入自定义的BaserowFormulaErrorListener其syntaxError方法会把EOF替换为更友好的 the end of the formula并抛出BaserowFormulaSyntaxError如Invalid syntax at line 1, col 5: ...从而让语法错误以业务异常的形式向上传播。该函数上方注释特别警告get_parse_tree_for_formula被迁移migration代码直接使用改动时必须保持向后兼容——这是修改解析链路时的一条重要工程约束。前端JavaScript/ANTLR4前端把生成代码放在 web-frontend/modules/core/formula/parser/generated/BaserowFormula.js、BaserowFormulaLexer.js等驱动方式与后端几乎一一对应见 parser.jsexport default function parseBaserowFormula(formula, throwOnError true) { const chars new antlr4.InputStream(formula) const lexer new BaserowFormulaLexer(chars) const tokens new antlr4.CommonTokenStream(lexer) const parser new BaserowFormula(tokens) parser.removeErrorListeners() parser.addErrorListener({ syntaxError: (recognizer, offendingSymbol, line, column, msg, err) { if (throwOnError) { throw new BaserowFormulaParserError(offendingSymbol, line, column, msg) } }, }) parser.buildParseTrees true return parser.root() }前端同样移除默认错误监听器并注册自定义监听器在throwOnError为真时抛出BaserowFormulaParserError返回的 parse tree 会被输入组件如FormulaInputField.vue用于实时校验与渲染。getTokenStreamForFormula则提供 token 流给自动补全模块 formulaAutocompleter.js 使用。求值与校验的 Visitor 分工生成的BaserowFormulaVisitor基类被两个具体 Visitor 继承实现校验与执行的分离formula_validation_visitor.py遍历语法树做静态校验类型检查、函数存在性、参数合法性前端对应 formulaValidationVisitor.jsformula_execution_visitor.py对公式求值。从代码可以看到visitFunctionCall会把函数名小写化后从FunctionCollection注册表中取出函数类型再依次parse_args、execute(context, args)其中context是运行时上下文runtime_formula_context.py而具体的公式函数实现注册在 registries.py 中。修改公式语言的完整流程formula/README.md 给出了修改语言语法的标准操作流程全文核心步骤可归纳为修改文法文件所有对公式语言**语法syntax**的改动都必须先修改本目录下的.g4文件——需要调整 token 时改BaserowFormulaLexer.g4需要调整表达式结构时改BaserowFormula.g4重新生成双端解析器运行构建脚本由 Docker 容器内的 ANTLR 在正确位置生成 JavaScript 与 Python3 两套解析器替换旧的生成代码同步实现与测试新语法生效后需要在前端web-frontend/modules/core/formula/与后端backend/src/baserow/core/formula/的 Visitor 实现中补充对应的访问逻辑并添加测试。仓库中已有大量用例可供参考例如前端 formulaParser.spec.js、formulaExecutionVisitor.spec.js 与后端tests/baserow/core/formula下的测试。构建环境formula/Dockerfileformula/Dockerfile 提供了生成解析器所需的 Java 运行环境基于openjdk:17.0.2镜像支持通过UID/GID构建参数指定运行用户避免生成文件属主与宿主机不一致设置ANTLR_VERSION4.9并从 ANTLR 官网下载antlr-4.9-complete.jar重命名为antlr.jar加入CLASSPATH以WORKDIR /workspace作为工作目录挂载文法与生成目标目录。关于 README 中提到的./build.sh当前仓库的formula/目录下并未提交该脚本文件目录中仅有文法、tokens、Dockerfile 与 README其实现可能存在于构建流水线或本地开发约定中。因此实际操作时应依据 docs/development/development-environment.md 等开发文档确认仓库当前推荐的构建入口但无论如何构建的实质都是在formula/Dockerfile定义的 Java/ANTLR 4.9 环境中用org.antlr.v4.Tool以-DlanguagePython3和-DlanguageJavaScript分别针对BaserowFormulaLexer.g4、BaserowFormula.g4生成解析器并输出到后端 backend/src/baserow/core/formula/parser/generated/ 与前端 web-frontend/modules/core/formula/parser/generated/ 两个目录——这两处生成产物的存在本身即是该流程的产物证据。实践建议与注意事项优先级靠规则顺序而非显式声明Baserow 公式语言的运算符优先级完全由expr规则中分支的书写顺序决定。新增二元运算符时务必把更高优先级的规则放在更靠前的位置并同步在visitBinaryOp中补充映射与测试。不要使用field_by_id文法中该写法已被明确标记为 deprecated 且仅用于向后兼容新公式一律使用field(字段名)。生成代码是产物而非源码parser/generated/下的文件由 ANTLR 生成手动修改会在下次构建时被覆盖所有语言层面的改动都应回到.g4文件。同时注意 parser.py 中该函数被迁移代码直接使用的注释改动解析行为时必须考虑数据库迁移的兼容性。双端一致性前后端使用同一份文法生成解析器是保证前端实时校验结果与后端持久化计算结果一致的关键架构决策。修改文法后必须同时重新生成两端代码并分别跑通 formulaParser.spec.js 等测试否则会出现前端可输入、后端拒绝或反之的割裂。总结Baserow Formula Language 是典型的一份文法、双端解析的工程实践BaserowFormulaLexer.g4定义词法关键字、字面量、运算符、错误字符兜底BaserowFormula.g4通过带标签的递归规则定义语法与运算符优先级ANTLR 据此生成 Python3 与 JavaScript 两套解析器分别被后端parser.py和前端parser.js的 Visitor 体系消费。理解这套文法与构建流程是扩展 Baserow 公式能力新增函数、运算符或语法糖的起点。【免费下载链接】baserowBuild databases, automations, apps agents with AI — no code. Open source platform available on cloud and self-hosted. GDPR, HIPAA, SOC 2 compliant. Best Airtable alternative.项目地址: https://gitcode.com/GitHub_Trending/ba/baserow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表