Skip to main content

nomiscript/runtime/symbol/
builtins.rs

1//! Built-in registrations: operators, special forms, native fns,
2//! entity-type/context constants, typed-entity accessors, and the
3//! MAP-family macros (MAPCAR / MAPC).
4
5use crate::ast::{Expr, Fraction, LambdaParams};
6
7use super::entry::{Symbol, SymbolKind};
8use super::table::SymbolTable;
9
10impl SymbolTable {
11    pub fn register_builtins(&mut self) {
12        // Name lists tangle from doc/scripting/builtin_reference.org
13        // into builtins_generated.rs — the org registry tables are
14        // the single source of truth. Adding a builtin = one org row.
15        for op in super::builtins_generated::OPERATORS {
16            self.define(Symbol::new(*op, SymbolKind::Operator));
17        }
18        for form in super::builtins_generated::SPECIAL_FORMS {
19            self.define(Symbol::new(*form, SymbolKind::SpecialForm));
20        }
21        for native in super::builtins_generated::NATIVES {
22            self.define(Symbol::new(*native, SymbolKind::Native));
23        }
24
25        self.register_entity_constants();
26        self.register_entity_accessors();
27
28        self.add_map_family_macros();
29    }
30
31    fn register_entity_constants(&mut self) {
32        use scripting_format::{ContextType, EntityType};
33
34        let entity_types: &[(&str, EntityType)] = &[
35            ("+ENTITY-TRANSACTION+", EntityType::Transaction),
36            ("+ENTITY-SPLIT+", EntityType::Split),
37            ("+ENTITY-TAG+", EntityType::Tag),
38            ("+ENTITY-ACCOUNT+", EntityType::Account),
39            ("+ENTITY-COMMODITY+", EntityType::Commodity),
40        ];
41        for (name, ty) in entity_types {
42            self.define(
43                Symbol::new(*name, SymbolKind::Variable)
44                    .with_value(Expr::Number(Fraction::from_integer(i64::from(*ty as u8)))),
45            );
46        }
47
48        let context_types: &[(&str, ContextType)] = &[
49            ("+CONTEXT-CREATE+", ContextType::EntityCreate),
50            ("+CONTEXT-UPDATE+", ContextType::EntityUpdate),
51            ("+CONTEXT-DELETE+", ContextType::EntityDelete),
52            ("+CONTEXT-BATCH+", ContextType::BatchProcess),
53        ];
54        for (name, ct) in context_types {
55            self.define(
56                Symbol::new(*name, SymbolKind::Variable)
57                    .with_value(Expr::Number(Fraction::from_integer(i64::from(*ct as u8)))),
58            );
59        }
60    }
61
62    pub(super) fn register_entity_accessors(&mut self) {
63        let accessors = [
64            "ENTITY-COUNT",
65            "CONTEXT-TYPE",
66            "PRIMARY-ENTITY-TYPE",
67            "PRIMARY-ENTITY-IDX",
68            "ENTITY-TYPE",
69            "ENTITY-PARENT-IDX",
70            "TRANSACTION-SPLIT-COUNT",
71            "TRANSACTION-TAG-COUNT",
72            "TRANSACTION-IS-MULTI-CURRENCY",
73            "TRANSACTION-POST-DATE",
74            "TRANSACTION-ENTER-DATE",
75            "SPLIT-VALUE-NUM",
76            "SPLIT-VALUE-DENOM",
77            "SPLIT-VALUE",
78            "SPLIT-RECONCILE-STATE",
79            "SPLIT-RECONCILE-DATE",
80            "SPLIT-ACCOUNT-NAME",
81            "CREATE-TAG",
82            "TAG-NAME",
83            "TAG-VALUE",
84            "STRING=",
85            "DELETE-ENTITY",
86            // Typed-entity accessors (mirror `compiler::native::typed_entity`).
87            "ACCOUNT-ID",
88            "ACCOUNT-NAME",
89            "ACCOUNT-PARENT",
90            "COMMODITY-ID",
91            "COMMODITY-SYMBOL",
92            "COMMODITY-NAME",
93            "TRANSACTION-ID",
94            "TRANSACTION-NOTE",
95            "SPLIT-ID",
96            "SPLIT-ACCOUNT-ID",
97            "SPLIT-COMMODITY-ID",
98            "SPLIT-AMOUNT",
99            "TAG-ENTITY-ID",
100            "TAG-ENTITY-NAME",
101            "TAG-ENTITY-VALUE",
102            "PRICE-ID",
103            "PRICE-COMMODITY-ID",
104            "PRICE-CURRENCY-ID",
105            "PRICE-VALUE",
106            "PRICE-DATE",
107            "SSH-KEY-ID",
108            "SSH-KEY-FINGERPRINT",
109            "SSH-KEY-NAME",
110            "NODE-ID",
111            "NODE-LABEL",
112            "NODE-DEPTH",
113            "NODE-AMOUNT",
114            "NODE-CHILDREN",
115        ];
116        for name in accessors {
117            self.define(Symbol::new(name, SymbolKind::Native));
118        }
119    }
120
121    fn add_map_family_macros(&mut self) {
122        let mapcar_params = LambdaParams {
123            required: vec!["func".to_string(), "list".to_string()],
124            optional: Vec::new(),
125            rest: Some("lists".to_string()),
126            key: Vec::new(),
127            aux: Vec::new(),
128        };
129        let mapcar_body = Expr::List(vec![
130            Expr::Symbol("APPLY".to_string()),
131            Expr::Quote(Box::new(Expr::Symbol("LIST".to_string()))),
132            Expr::Quote(Box::new(Expr::Symbol("MAP".to_string()))),
133            Expr::Symbol("func".to_string()),
134            Expr::Symbol("list".to_string()),
135            Expr::Symbol("lists".to_string()),
136        ]);
137        let mapcar_lambda = Expr::Lambda(mapcar_params, Box::new(mapcar_body));
138        self.define(Symbol::new("MAPCAR", SymbolKind::Macro).with_function(mapcar_lambda));
139
140        let mapc_params = LambdaParams::simple(vec!["func".to_string(), "list".to_string()]);
141        let mapc_body = Expr::List(vec![
142            Expr::Symbol("LIST".to_string()),
143            Expr::Quote(Box::new(Expr::Symbol("MAP".to_string()))),
144            Expr::Symbol("func".to_string()),
145            Expr::Symbol("list".to_string()),
146        ]);
147        let mapc_lambda = Expr::Lambda(mapc_params, Box::new(mapc_body));
148        self.define(Symbol::new("MAPC", SymbolKind::Macro).with_function(mapc_lambda));
149    }
150}