Skip to main content

binary_operators

Function binary_operators 

Source
pub fn binary_operators() -> &'static [(&'static str, &'static str)]
Expand description

The binary operator ops: (symbol, op tag). Both operands are l/r exprs.

Examples found in repository?
examples/gen-schema.rs (line 94)
44fn build_catalog() -> Value {
45    let mut ops: Vec<Value> = Vec::new();
46
47    // Two leaves.
48    ops.push(json!({
49        "name": "Data",
50        "tag": "Data",
51        "form": "leaf",
52        "description": "A raw input series by name (e.g. close, pe, revenue_growth). In DSL, a bare identifier lowers to this.",
53        "args": [
54            { "name": "name", "kind": "string", "required": true }
55        ]
56    }));
57    ops.push(json!({
58        "name": "Const",
59        "tag": "Const",
60        "form": "leaf",
61        "description": "A constant scalar value, broadcast across the panel. In DSL, a bare number operand lowers to this.",
62        "args": [
63            { "name": "value", "kind": "number", "required": true }
64        ]
65    }));
66
67    // Function-style ops.
68    for op in meta::function_ops() {
69        let args: Vec<Value> = op
70            .fields
71            .iter()
72            .map(|f| {
73                let mut m = Map::new();
74                m.insert("name".into(), json!(f.name));
75                m.insert("kind".into(), json!(f.kind));
76                m.insert("required".into(), json!(f.required));
77                if let Some(d) = &f.default {
78                    m.insert("default".into(), d.clone());
79                }
80                Value::Object(m)
81            })
82            .collect();
83        ops.push(json!({
84            "name": op.name,
85            "aliases": op.aliases,
86            "tag": op.tag,
87            "form": "function",
88            "description": op.description,
89            "args": args,
90        }));
91    }
92
93    // Binary operators (l, r).
94    for (symbol, tag) in meta::binary_operators() {
95        ops.push(json!({
96            "name": symbol,
97            "tag": tag,
98            "form": "operator",
99            "description": binop_description(tag),
100            "args": [
101                { "name": "l", "kind": "expr", "required": true },
102                { "name": "r", "kind": "expr", "required": true }
103            ]
104        }));
105    }
106
107    // Unary operators: negation and logical NOT.
108    ops.push(json!({
109        "name": "-",
110        "tag": "Neg",
111        "form": "operator",
112        "unary": true,
113        "description": "Negation (-`of`).",
114        "args": [
115            { "name": "of", "kind": "expr", "required": true }
116        ]
117    }));
118    ops.push(json!({
119        "name": "not",
120        "tag": "Not",
121        "form": "operator",
122        "unary": true,
123        "description": "Logical NOT of a boolean panel (NaN is falsy, so `not` of NaN is 1).",
124        "args": [
125            { "name": "of", "kind": "expr", "required": true }
126        ]
127    }));
128
129    Value::Array(ops)
130}
131
132/// Terse descriptions for the operator ops (which live in `BINOPS`, not `ROWS`).
133fn binop_description(tag: &str) -> &'static str {
134    match tag {
135        "Gt" => "1 where `l` is greater than `r`, else 0.",
136        "Lt" => "1 where `l` is less than `r`, else 0.",
137        "Ge" => "1 where `l` is greater than or equal to `r`, else 0.",
138        "Le" => "1 where `l` is less than or equal to `r`, else 0.",
139        "And" => "Logical AND of two boolean panels.",
140        "Or" => "Logical OR of two boolean panels.",
141        "Add" => "Element-wise `l` + `r`.",
142        "Sub" => "Element-wise `l` - `r`.",
143        "Mul" => "Element-wise `l` * `r`.",
144        "Div" => "Element-wise `l` / `r`.",
145        other => panic!("no description for operator tag `{other}`"),
146    }
147}
148
149/// One `oneOf` branch of the Expr schema: an object tagged by `op` with its fields.
150fn op_object_schema(
151    tag: &str,
152    description: &str,
153    fields: &[(&str, &str, bool)], // (name, kind, required)
154) -> Value {
155    let mut properties = Map::new();
156    properties.insert("op".into(), json!({ "const": tag }));
157    let mut required: Vec<Value> = vec![json!("op")];
158    for (name, kind, req) in fields {
159        properties.insert((*name).into(), field_schema(kind));
160        if *req {
161            required.push(json!(*name));
162        }
163    }
164    json!({
165        "type": "object",
166        "title": tag,
167        "description": description,
168        "properties": Value::Object(properties),
169        "required": required,
170        "additionalProperties": false,
171    })
172}
173
174/// Build the `lemon-spec.schema.json` value: a JSON Schema over the Expr tree.
175fn build_spec_schema() -> Value {
176    let mut branches: Vec<Value> = Vec::new();
177
178    // Leaves.
179    branches.push(op_object_schema(
180        "Data",
181        "A raw input series by name.",
182        &[("name", "string", true)],
183    ));
184    branches.push(op_object_schema(
185        "Const",
186        "A constant scalar value, broadcast across the panel.",
187        &[("value", "number", true)],
188    ));
189
190    // Function-style ops.
191    for op in meta::function_ops() {
192        let fields: Vec<(&str, &str, bool)> = op
193            .fields
194            .iter()
195            .map(|f| (f.name, f.kind, f.required))
196            .collect();
197        branches.push(op_object_schema(op.tag, op.description, &fields));
198    }
199
200    // Binary operators.
201    for (_, tag) in meta::binary_operators() {
202        branches.push(op_object_schema(
203            tag,
204            binop_description(tag),
205            &[("l", "expr", true), ("r", "expr", true)],
206        ));
207    }
208
209    // Unary operators.
210    branches.push(op_object_schema(
211        "Neg",
212        "Negation (-`of`).",
213        &[("of", "expr", true)],
214    ));
215    branches.push(op_object_schema(
216        "Not",
217        "Logical NOT of a boolean panel (NaN is falsy, so `not` of NaN is 1).",
218        &[("of", "expr", true)],
219    ));
220
221    json!({
222        "$schema": "https://json-schema.org/draft/2020-12/schema",
223        "$id": "https://github.com/citrusquant/citrusquant/schema/lemon-spec.schema.json",
224        "title": "Lemon strategy spec (Expr tree)",
225        "description": "The serializable lemon strategy AST: a JSON tree tagged by \"op\". Generated from crates/lemon metadata; do not edit by hand.",
226        "$ref": "#/$defs/Expr",
227        "$defs": {
228            "Expr": {
229                "oneOf": branches
230            }
231        }
232    })
233}