# MoonRules V1 DSL

## 规则文档

顶层对象必须包含字符串 `id`、字符串 `description` 和 `condition`：

```json
{
  "id": "adult-check",
  "description": "成年人准入",
  "condition": { ">=": [{ "var": "user.age" }, 18] }
}
```

每个操作节点只能有一个操作符键。原始 JSON 值和数组可以直接作为字面量；对象字面量必须写成 `{"literal": {...}}`。

## 数据路径

`{"var":"user.age"}` 读取输入对象中的 `user.age`。V1 只接受非空的点号字段段，不接受 `items[0]`、通配符、转义点或数组索引。

规则路径描述规则文档位置，例如 `condition.and[1].==[0].var`；数据路径描述输入取值位置，例如 `user.role`。两者不能互换。

## 操作符表

| 操作符 | 参数数量 | 运行时要求 | 示例 |
|---|---:|---|---|
| `var` | 1 | 字符串数据路径 | `{"var":"user.age"}` |
| `literal` | 1 | JSON 对象 | `{"literal":{"tier":"pro"}}` |
| `and` | 至少 1 | 布尔结果 | `{"and":[true,false]}` |
| `or` | 至少 1 | 布尔结果 | `{"or":[false,true]}` |
| `not` | 1 | 布尔结果 | `{"not":[true]}` |
| `==` | 2 | 任意 JSON | `{"==":[1,1.0]}` |
| `!=` | 2 | 任意 JSON | `{"!=":["a","b"]}` |
| `>` | 2 | 数字 | `{">":[3,2]}` |
| `>=` | 2 | 数字 | `{">=":[3,3]}` |
| `<` | 2 | 数字 | `{"<":[2,3]}` |
| `<=` | 2 | 数字 | `{"<=":[3,3]}` |
| `in` | 2 | 第二项为数组 | `{"in":["admin",["viewer","admin"]]}` |
| `contains` | 2 | 两个字符串 | `{"contains":["moonrules","rules"]}` |
| `starts_with` | 2 | 两个字符串 | `{"starts_with":["moonrules","moon"]}` |
| `ends_with` | 2 | 两个字符串 | `{"ends_with":["moonrules","rules"]}` |

操作符参数统一使用数组；`var` 和 `literal` 是两个特殊单参数节点。

## 深度相等

`==` 与 `in` 使用相同语义：

- 对象键顺序不影响结果，递归比较键值集合。
- 数组按顺序逐元素比较。
- 数字按数值比较，因此 `1` 等于 `1.0`。
- `null` 只等于 `null`。
- 不同 JSON 类型不相等，不做隐式转换。
- 缺失变量产生错误，不等同于 `null`。

## 解析与检查边界

解析器负责 JSON 结构和操作符名称。检查器负责参数数量、点号路径、明显错误的字面量类型和静态预算。变量的实际类型只能在执行时验证。
