///|
/// JSONata 函数签名参数描述。
///
/// 对应 JSONata-js `parseSignature` 中每个参数节点的字段:
/// - `base_type`:基础类型码(`s`/`n`/`b`/`o`/`a`/`f`/`j`/`x`/`l`/`(`)。
/// - `regex`:用于匹配参数符号的正则字符类(如 `[sm]`、`[asnblfom]`)。
/// - `array`:基础类型是否为 `a`(数组),允许标量提升为单元素数组。
/// - `subtype`:`a<...>` / `f<...>` 的内层类型字符串(如 `s`、`n`)。
/// - `optional`:`?` 后缀,参数可缺省。
/// - `variadic`:`+` 后缀,参数可重复 1 次以上。
/// - `context`:`-` 后缀,参数缺省时使用上下文值。
priv struct SigParam {
base_type : String
mut regex : String
mut array : Bool
mut subtype : String?
mut optional : Bool
mut variadic : Bool
mut context : Bool
}
///|
fn SigParam::new(base_type : String, regex : String) -> SigParam {
{
base_type,
regex,
array: false,
subtype: None,
optional: false,
variadic: false,
context: false,
}
}
///|
/// 已解析的 JSONata 函数签名。
///
/// 仅含参数列表;返回类型在解析阶段被丢弃(当前实现不校验返回值)。
priv struct FunctionSignature {
params : Array[SigParam]
}
///|
/// 判断签名字符串是否包含复杂构造(需要严格校验)。
///
/// 仅含基础类型字符(如 `sn`、`sss`)的签名视为简单签名,沿用旧有宽松校验;
/// 含 `?`/`+`/`-`/`:`/`<`/`>`/`(`/`)` 任意一个的签名视为复杂签名,
/// 走完整 JSONata 语义校验(包含参数数量与类型检查)。
fn is_complex_signature(sig : String) -> Bool {
for c in sig {
match c {
'?' | '+' | '-' | ':' | '<' | '>' | '(' | ')' => return true
_ => ()
}
}
false
}
///|
/// 剥离外层 `<>`,返回签名内部内容。
fn strip_outer_angles(sig : String) -> String {
let chars = sig.to_array()
let n = chars.length()
if n >= 2 && chars[0] == '<' && chars[n - 1] == '>' {
let out = StringBuilder::new()
let end = n - 1
for i in 1..`)。
///
/// 错误:
/// - `S0401`:`<...>` 类型参数出现在非 `a`/`f` 类型之后;或签名结构无法解析。
/// - `S0402`:`(...)` 选择类型内嵌 `<...>` 参数化类型(当前不支持)。
fn parse_function_signature(
sig : String,
) -> FunctionSignature raise @error.JsonataError {
let inner = strip_outer_angles(sig)
let chars = inner.to_array()
let n = chars.length()
let params : Array[SigParam] = []
let mut prev_param : SigParam? = None
let mut i = 0
while i < n {
let c = chars[i]
if c == ':' {
// 返回类型起始;当前实现不校验返回值,直接丢弃剩余字符
break
}
match c {
's' | 'n' | 'b' | 'o' | 'l' => {
let p = SigParam::new(c.to_string(), "[\{c}m]")
params.push(p)
prev_param = Some(p)
}
'a' => {
let p = SigParam::new("a", "[asnblfom]")
p.array = true
params.push(p)
prev_param = Some(p)
}
'f' => {
let p = SigParam::new("f", "f")
params.push(p)
prev_param = Some(p)
}
'j' => {
let p = SigParam::new("j", "[asnblom]")
params.push(p)
prev_param = Some(p)
}
'x' => {
let p = SigParam::new("x", "[asnblfom]")
params.push(p)
prev_param = Some(p)
}
'-' =>
// 上下文填充:修改前一个参数
match prev_param {
Some(p) =>
if p.context {
raise @error.signature_error(
"S0401: 重复的上下文修饰符 '-' in signature: \{sig}",
code=Some("S0401"),
)
} else {
p.context = true
p.regex = p.regex + "?"
}
None =>
raise @error.signature_error(
"S0401: 上下文修饰符 '-' 缺少前驱参数 in signature: \{sig}",
code=Some("S0401"),
)
}
'?' =>
match prev_param {
Some(p) => p.optional = true
None =>
raise @error.signature_error(
"S0401: 可选修饰符 '?' 缺少前驱参数 in signature: \{sig}",
code=Some("S0401"),
)
}
'+' =>
match prev_param {
Some(p) => p.variadic = true
None =>
raise @error.signature_error(
"S0401: 可变修饰符 '+' 缺少前驱参数 in signature: \{sig}",
code=Some("S0401"),
)
}
'(' => {
// 选择类型:搜索匹配的 ')'
let mut depth = 1
let mut j = i + 1
let mut found = false
while j < n {
if chars[j] == '(' {
depth += 1
} else if chars[j] == ')' {
depth -= 1
if depth == 0 {
found = true
break
}
}
j += 1
}
if !found {
raise @error.signature_error(
"S0401: 未闭合的 '(' in signature: \{sig}",
code=Some("S0401"),
)
}
// 提取 (...) 内的字符
let choice = StringBuilder::new()
let start = i + 1
for k in start..' in signature: \{sig}",
code=Some("S0402"),
)
}
let p = SigParam::new("(\{choice_str})", "[\{choice_str}m]")
params.push(p)
prev_param = Some(p)
i = j
}
'<' =>
// 类型参数:仅允许出现在 'a' 或 'f' 之后
match prev_param {
Some(p) if p.base_type == "a" || p.base_type == "f" => {
// 搜索匹配的 '>'
let mut depth = 1
let mut j = i + 1
let mut found = false
while j < n {
if chars[j] == '<' {
depth += 1
} else if chars[j] == '>' {
depth -= 1
if depth == 0 {
found = true
break
}
}
j += 1
}
if !found {
raise @error.signature_error(
"S0401: 未闭合的 '<' in signature: \{sig}",
code=Some("S0401"),
)
}
let sub = StringBuilder::new()
let start = i + 1
for k in start..
raise @error.signature_error(
"S0401: 类型参数 '<...>' 只能跟在 'a' 或 'f' 之后 in signature: \{sig}",
code=Some("S0401"),
)
}
' ' | '\t' | '\n' | '\r' => () // 忽略空白
_ =>
raise @error.signature_error(
"S0401: 无法识别的签名符号 '\{c}' in signature: \{sig}",
code=Some("S0401"),
)
}
i += 1
}
{ params, }
}
///|
/// 将运行时值转换为 JSONata 类型符号字符。
///
/// - `s` string、`n` number、`b` boolean、`o` object、`a` array、`f` function、`l` null
/// - `m` missing(undefined)
fn get_value_symbol(value : JsonataValue) -> Char {
match value {
Undefined => 'm'
Func(_) => 'f'
Regex(..) => 'f'
Json(Json::String(_)) => 's'
Json(Json::Number(_)) => 'n'
Json(Json::True) | Json(Json::False) => 'b'
Json(Json::Null) => 'l'
Json(Json::Object(_)) => 'o'
Json(Json::Array(_)) => 'a'
Object(_) => 'o'
Sequence(_) => 'a'
}
}
///|
/// 判断符号字符是否匹配参数 regex。
///
/// regex 形如 `[abc]` 或 `[abc]?`(context 修饰后),取方括号内字符;
/// 也支持非括号形式如 `f`(单一类型码)。
fn symbol_in_class(sym : Char, regex : String) -> Bool {
let chars = regex.to_array()
let n = chars.length()
// 若 regex 以 '[' 开头,取方括号内字符
if n > 0 && chars[0] == '[' {
let mut i = 1
while i < n && chars[i] != ']' {
if chars[i] == sym {
return true
}
i += 1
}
return false
}
// 否则 regex 是单一类型码(如 "f"),直接比较首字符
if n > 0 {
return chars[0] == sym
}
false
}
///|
/// 校验数组参数的元素类型,并在必要时将标量包装为单元素数组。
///
/// - 对于 `a` 等带子类型的数组参数:
/// - `m`(undefined):跳过元素校验,原样返回(与 JSONata 行为一致)。
/// - 数组实参:检查所有元素是否匹配子类型首字符;不匹配抛出 T0412。
/// - 标量实参:检查标量本身是否匹配子类型首字符;匹配则包装为单元素数组。
/// - 无子类型:原样返回。
fn validate_array_arg(
arg : JsonataValue,
sym : Char,
subtype : String,
) -> JsonataValue raise @error.JsonataError {
// undefined 直通——JSONata 中 undefined 通过所有签名检查
if sym == 'm' {
return arg
}
let sub_chars = subtype.to_array()
let expected_char = sub_chars[0]
match sym {
'a' => {
// 数组实参:逐个元素检查类型
let items = arg.to_array()
for item in items {
let item_sym = get_value_symbol(item)
if item_sym != expected_char {
raise @error.signature_error(
"T0412: Argument has wrong type. Expected array of \{subtype}, got \{item_sym}",
code=Some("T0412"),
)
}
}
arg
}
_ => {
// 标量实参:检查标量本身是否匹配子类型
if sym != expected_char {
raise @error.signature_error(
"T0412: Argument has wrong type. Expected array of \{subtype}, got \{sym}",
code=Some("T0412"),
)
}
// 包装为单元素数组
let arr : Array[Json] = [arg.to_json()]
Json(Json::array(arr))
}
}
}
///|
/// 严格校验参数列表,返回可能被包装(标量→单元素数组)后的参数。
///
/// 错误:
/// - `T0410`:参数数量或类型不匹配。
/// - `T0411`:上下文填充值类型不兼容。
/// - `T0412`:数组元素类型不匹配。
///
/// 实现策略:先用回溯匹配为每个参数分配实参数量(对齐 JSONata-js
/// `parseSignature` 生成的正则 `^(p1)(p2)...(pN)$` 的贪婪与回溯语义),
/// 然后按分配结果逐参数校验类型与子类型。
fn FunctionSignature::validate(
self : FunctionSignature,
args : Array[JsonataValue],
context : JsonataValue,
) -> Array[JsonataValue] raise @error.JsonataError {
let n_params = self.params.length()
// counts[i] = 第 i 个参数消费的实参数量(用于 variadic 回溯)
let counts : Array[Int] = []
for _ in 0.. validate_array_arg(arg, sym, subtype)
None => arg
}
} else {
arg
}
validated.push(final_arg)
arg_idx += 1
}
}
validated
}
///|
/// 回溯匹配:为 params 从 param_idx 开始,args 从 arg_idx 开始,
/// 尝试为每个参数分配匹配的实参,使总和等于剩余 args 数量。
///
/// - 对于必需参数:分配恰好 1 个匹配实参。
/// - 对于可选参数:分配 0 或 1 个匹配实参。
/// - 对于可变参数:分配 1+ 个匹配实参。
/// - 对于上下文参数:分配 0 或 1 个匹配实参(缺省走上下文)。
fn backtrack_match(
params : Array[SigParam],
args : Array[JsonataValue],
param_idx : Int,
arg_idx : Int,
counts : Array[Int],
) -> Bool {
let n_params = params.length()
let n_args = args.length()
if param_idx == n_params {
return arg_idx == n_args
}
let param = params[param_idx]
// 确定可分配的实参范围
let max_count = if param.variadic {
// 可变:可消费 0..(剩余-后续必需) 个
n_args - arg_idx
} else if param.optional || param.context {
1 // 可选/上下文:0 或 1
} else {
1 // 必需:1
}
// 起始 count:可变/可选/上下文从 0 开始;必需从 1 开始
let start_count = if param.variadic || param.optional || param.context {
0
} else {
1
}
// 但可变参数至少要 1 个(除非后续参数能消费剩下的)
// 先尝试从大到小(贪婪),更接近 JSONata 正则语义
let mut k = max_count
while k >= start_count {
// 检查 args[arg_idx..arg_idx+k] 是否都匹配 param 类型
let mut ok = true
for j in 0..= n_args {
ok = false
break
}
let sym = get_value_symbol(args[arg_idx + j])
if !symbol_in_class(sym, param.regex) {
ok = false
break
}
}
if ok {
// 可变参数若 k==0,要求后续能匹配所有剩余
// 必需参数 k 必须 >= 1
if !param.variadic && !param.optional && !param.context && k == 0 {
// 必需参数不能为 0
k -= 1
continue
}
counts[param_idx] = k
if backtrack_match(params, args, param_idx + 1, arg_idx + k, counts) {
return true
}
}
k -= 1
}
// 必需参数若未匹配到,则失败
false
}
///|
/// 对外入口:解析签名并校验参数。
///
/// 简单签名(仅基础类型字符)走宽松校验:仅检查 min(args, params) 个参数类型,
/// 不强制参数数量;保持与历史内建函数签名兼容。
/// 复杂签名走严格校验。
pub fn validate_function_args(
signature : String,
args : Array[JsonataValue],
context : JsonataValue,
) -> Array[JsonataValue] raise @error.JsonataError {
if !is_complex_signature(signature) {
// 简单签名:仅做逐参数类型检查,不强制数量
let chars = signature.to_array()
let limit = args.length().min(chars.length())
for i in 0.. Bool {
if sym == 'm' {
return true
}
match expected {
'j' =>
sym == 's' ||
sym == 'n' ||
sym == 'b' ||
sym == 'o' ||
sym == 'a' ||
sym == 'l'
'x' =>
sym == 's' ||
sym == 'n' ||
sym == 'b' ||
sym == 'o' ||
sym == 'a' ||
sym == 'l' ||
sym == 'f'
's' => sym == 's'
'n' => sym == 'n'
'b' => sym == 'b'
'o' => sym == 'o'
'a' => sym == 'a'
'f' => sym == 'f'
'l' => sym == 'l'
_ => true
}
}