For the complete documentation index, see llms.txt. This page is also available as Markdown.

Zongsoft.Expressions

Zongsoft.Expressions 的表达式求值抽象、词法分析器、token 模型和 tokenizer 扩展方式。

Zongsoft.Expressions 提供表达式求值抽象和词法分析基础设施。它把一段表达式文本先切分成常量、标识符、关键字和符号 token,再交给具体表达式求值器或上层解析器解释语义。

这个命名空间更像“表达式基础层”,而不是某一种固定语法的完整脚本引擎。框架中的条件表达式、配置表达式、命令参数表达式和部分数据查询表达式,都可以在这套基础层之上定义自己的关键字、操作符和求值规则。

Lexer 只负责词法切分,不负责判断 1 + 2 应该如何计算,也不负责处理操作符优先级。算术、条件、成员访问或数据库表达式语义,需要由调用方或具体 IExpressionEvaluator 实现完成。

主要职责

  • 定义表达式求值器接口、基础类和运行选项。

  • 提供可复用的词法分析器 Lexer 和扫描器 TokenScanner。

  • 把表达式文本切分为 TokenType.Constant、TokenType.Identifier、TokenType.Symbol 和 TokenType.Keyword。

  • 内置空值、布尔值、数字、字符串、标识符和符号 tokenizer。

  • 支持调用方追加或调整 tokenizer,以识别领域关键字和自定义符号。

  • 为插件配置、命令解释、条件解析、数据表达式和轻量脚本求值提供底层能力。

核心类型

类型
作用
常见用法

表达式求值器契约。

按表达式文本、选项和变量集合返回求值结果。

求值器基类。

统一名称匹配、默认选项、全局变量和释放逻辑。

求值运行选项。

指定输入、输出、错误输出和扩展属性。

词法分析入口。

从字符串或流创建 TokenScanner。

token 扫描器。

逐个扫描 token,或作为集合枚举所有 token。

分词器接口。

为新的字面量、关键字或领域符号提供识别逻辑。

token 对象。

携带 token 类型和值。

符号 token。

表示 +、==、&&、??、括号等内置符号。

语法异常。

分词过程中遇到非法字符或不完整字面量时抛出。

词法分析流程

Lexer 维护一个 tokenizer 列表。TokenScanner.Scan() 每次扫描时会先跳过空白字符,然后按列表顺序尝试每个 tokenizer;第一个返回有效 token 的 tokenizer 决定当前位置的识别结果。如果所有 tokenizer 都无法识别当前字符,而输入尚未结束,则抛出 SyntaxException。

默认 Lexer 的 tokenizer 顺序如下:

顺序
tokenizer
识别内容

1

NullTokenizer

null,忽略大小写。

2

NumberTokenizer

整数、小数,以及 L、F、D、M 数字后缀。

3

StringTokenizer

单引号或双引号字符串。

4

BooleanTokenizer

true、false,忽略大小写。

5

IdentifierTokenizer

以字母或 _ 开头,后续为字母、数字或 _ 的标识符。

6

SymbolTokenizer

内置符号 token。

来源:framework/Zongsoft.Core/test/Expressions/LexerTest.cs(节选;上下文见源文件)。

Discussions 没有自行构造词法器;上面的 核心类库 测试依次断言整数常量 1、加号和单精度常量 2。完整测试继续覆盖标识符、字符串、负号和其他运算符。后续它是普通条件、数据查询条件还是命令参数条件,取决于调用方如何解释这些 token。

Token 规则

内置 token 大致分为四类:

token 类型
示例
说明

Constant

null、true、30L、4.5、5.5m、'text'

字面量常量,Token.Value 会保存解析后的 .NET 值。

Identifier

Name、_abc123、Field1

变量名、字段名、参数名、方法名或调用方定义的标识。

Symbol

+、-、*、/、==、&&、??、(、)、[、]

内置操作符或分隔符。

Keyword

in、between

由调用方显式加入 KeywordTokenizer 后才会出现。

数字字面量从数字开始:不带小数点时默认解析为 int,包含小数点时默认解析为 double;后缀 L 表示 long,F 表示 float,D 表示 double,M 表示 decimal。数字不能以小数点结尾,也不能包含多个小数点。

字符串可以使用单引号或双引号包裹,并支持常见转义:\\、\'、\"、\s、\t、\n、\r。字符串字面量不能跨行,缺少结束引号时会抛出 SyntaxException。

内置符号覆盖常见运算和分隔场景:

类别
符号

算术

+、-、*、/、%

逻辑与比较

!、&&、`

空值与条件

??、?、:

访问与分隔

.、,、;、`

括号

(、)、[、]、{、}

关键字与自定义 tokenizer

默认词法器没有启用业务关键字,因此 in、between 这类文本会先被识别为标识符。需要领域关键字时,可以创建新的 Lexer 实例,并把 KeywordTokenizer 放在 IdentifierTokenizer 之前。

来源:framework/Zongsoft.Core/test/Expressions/LexerTest.cs(节选;上下文见源文件)。

KeywordTokenizer(true, ...) 表示忽略大小写,因此 IN、in 和 In 都可以被识别为同一个关键字 token。关键字识别后仍只产生 token,不会自动实现集合包含、范围匹配或 SQL 翻译;这些语义应由后续解析器或求值器处理。

自定义 tokenizer 适合这些场景:

  • 需要识别领域关键字,例如 between、like、contains。

  • 需要把某类字面量直接转换为特定值,例如日期、时间段、枚举名或变量占位符。

  • 需要限制可用符号,或为某个 DSL 增加专用符号。

实现自定义 tokenizer 时应遵守一个原则:只在当前位置能完整识别时返回 token;不能识别时返回失败结果,并把读取器偏移量恢复到尝试前的位置。这样后续 tokenizer 才能继续判断同一段文本。

表达式求值器

IExpressionEvaluator 定义了表达式求值的公共形状。调用方传入表达式文本、可选变量集合和可选运行选项,求值器返回一个对象结果。

来源:framework/externals/python/test/PythonExpressionEvaluatorTest.cs(节选;上下文见源文件)。

上面是 Python 插件的实际测试,Subtract 为同一测试类中的静态方法,变量字典还接收脚本写回的 result。求值器通常由具体模块实现自己的语法和语义。ExpressionEvaluatorBase 提供这些通用能力:

  • Name 表示求值器名称。

  • 实现服务匹配接口,可以按名称忽略大小写查找求值器。

  • Global 保存求值器级全局变量。

  • Options 保存默认输入、输出、错误输出和扩展属性。

  • Dispose() 释放时会清理全局变量。

ExpressionEvaluatorOptions 默认使用 Console.In、Console.Out 和 Console.Error,也可以通过静态方法或扩展方法替换输入输出。

来源:framework/externals/python/test/PythonExpressionEvaluatorTest.cs(节选;上下文见源文件)。

输出隔离测试先释放首轮输出对象,再执行下一次调用和另一个求值器调用,用于验证输出流恢复。它不证明多个调用并发时共享运行时完全隔离;更多边界见脚本专题。

如果应用中注册了多个 IExpressionEvaluator 实现,可以结合服务模型按名称选择。例如 ExpressionEvaluatorBase 已经支持忽略大小写匹配,因此调用方可以通过服务发现机制查找某个命名求值器。

现有语言实现的注册名是 Lua、Python 和 Scriban。选择方式、部署和语法差异见脚本与表达式。Discussions 未注册 JavaScript 求值器,不能把名称查找成功视为任意语言都已经可用。

使用建议

把 Lexer 用在“需要先理解文本结构”的边界层:例如解析配置值、命令参数、查询条件、筛选规则或轻量表达式。对于固定格式并且语义很简单的文本,普通字符串处理可能更直接;对于需要变量、括号、操作符和字面量组合的场景,词法分析能让后续解析更清晰。

为领域语法添加关键字时,尽量用新的 Lexer 实例,不要直接修改全局 Lexer.Instance。全局实例适合作为默认通用词法器;领域语法通常应该拥有自己的 tokenizer 顺序,避免影响其他模块。

来自用户输入的表达式应限制语法范围,并在求值前做白名单校验。词法器可以告诉你文本被切成了哪些 token,但不会自动判断某个标识符、方法名、字段名或操作符是否允许暴露给外部用户。

子命名空间

命名空间
说明

Zongsoft.Expressions.Tokenization

内置 tokenizer、字面量 tokenizer 基类和关键字、字符串、数字、布尔、空值、标识符、符号分词器。

相关资源

最后更新于