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

            
5
use crate::ast::{Expr, Fraction, LambdaParams};
6

            
7
use super::entry::{Symbol, SymbolKind};
8
use super::table::SymbolTable;
9

            
10
impl SymbolTable {
11
114769
    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
1262459
        for op in super::builtins_generated::OPERATORS {
16
1262459
            self.define(Symbol::new(*op, SymbolKind::Operator));
17
1262459
        }
18
5049836
        for form in super::builtins_generated::SPECIAL_FORMS {
19
5049836
            self.define(Symbol::new(*form, SymbolKind::SpecialForm));
20
5049836
        }
21
3672608
        for native in super::builtins_generated::NATIVES {
22
3672608
            self.define(Symbol::new(*native, SymbolKind::Native));
23
3672608
        }
24

            
25
114769
        self.register_entity_constants();
26
114769
        self.register_entity_accessors();
27

            
28
114769
        self.add_map_family_macros();
29
114769
    }
30

            
31
114769
    fn register_entity_constants(&mut self) {
32
        use scripting_format::{ContextType, EntityType};
33

            
34
114769
        let entity_types: &[(&str, EntityType)] = &[
35
114769
            ("+ENTITY-TRANSACTION+", EntityType::Transaction),
36
114769
            ("+ENTITY-SPLIT+", EntityType::Split),
37
114769
            ("+ENTITY-TAG+", EntityType::Tag),
38
114769
            ("+ENTITY-ACCOUNT+", EntityType::Account),
39
114769
            ("+ENTITY-COMMODITY+", EntityType::Commodity),
40
114769
        ];
41
573845
        for (name, ty) in entity_types {
42
573845
            self.define(
43
573845
                Symbol::new(*name, SymbolKind::Variable)
44
573845
                    .with_value(Expr::Number(Fraction::from_integer(i64::from(*ty as u8)))),
45
573845
            );
46
573845
        }
47

            
48
114769
        let context_types: &[(&str, ContextType)] = &[
49
114769
            ("+CONTEXT-CREATE+", ContextType::EntityCreate),
50
114769
            ("+CONTEXT-UPDATE+", ContextType::EntityUpdate),
51
114769
            ("+CONTEXT-DELETE+", ContextType::EntityDelete),
52
114769
            ("+CONTEXT-BATCH+", ContextType::BatchProcess),
53
114769
        ];
54
459076
        for (name, ct) in context_types {
55
459076
            self.define(
56
459076
                Symbol::new(*name, SymbolKind::Variable)
57
459076
                    .with_value(Expr::Number(Fraction::from_integer(i64::from(*ct as u8)))),
58
459076
            );
59
459076
        }
60
114769
    }
61

            
62
229538
    pub(super) fn register_entity_accessors(&mut self) {
63
229538
        let accessors = [
64
229538
            "ENTITY-COUNT",
65
229538
            "CONTEXT-TYPE",
66
229538
            "PRIMARY-ENTITY-TYPE",
67
229538
            "PRIMARY-ENTITY-IDX",
68
229538
            "ENTITY-TYPE",
69
229538
            "ENTITY-PARENT-IDX",
70
229538
            "TRANSACTION-SPLIT-COUNT",
71
229538
            "TRANSACTION-TAG-COUNT",
72
229538
            "TRANSACTION-IS-MULTI-CURRENCY",
73
229538
            "TRANSACTION-POST-DATE",
74
229538
            "TRANSACTION-ENTER-DATE",
75
229538
            "SPLIT-VALUE-NUM",
76
229538
            "SPLIT-VALUE-DENOM",
77
229538
            "SPLIT-VALUE",
78
229538
            "SPLIT-RECONCILE-STATE",
79
229538
            "SPLIT-RECONCILE-DATE",
80
229538
            "SPLIT-ACCOUNT-NAME",
81
229538
            "CREATE-TAG",
82
229538
            "TAG-NAME",
83
229538
            "TAG-VALUE",
84
229538
            "STRING=",
85
229538
            "DELETE-ENTITY",
86
229538
            // Typed-entity accessors (mirror `compiler::native::typed_entity`).
87
229538
            "ACCOUNT-ID",
88
229538
            "ACCOUNT-NAME",
89
229538
            "ACCOUNT-PARENT",
90
229538
            "ACCOUNT-TYPE",
91
229538
            "COMMODITY-ID",
92
229538
            "COMMODITY-SYMBOL",
93
229538
            "COMMODITY-NAME",
94
229538
            "TRANSACTION-ID",
95
229538
            "TRANSACTION-NOTE",
96
229538
            "TRANSACTION-AMOUNT",
97
229538
            "SPLIT-ID",
98
229538
            "SPLIT-ACCOUNT-ID",
99
229538
            "SPLIT-COMMODITY-ID",
100
229538
            "SPLIT-AMOUNT",
101
229538
            "TAG-ENTITY-ID",
102
229538
            "TAG-ENTITY-NAME",
103
229538
            "TAG-ENTITY-VALUE",
104
229538
            "PRICE-ID",
105
229538
            "PRICE-COMMODITY-ID",
106
229538
            "PRICE-CURRENCY-ID",
107
229538
            "PRICE-VALUE",
108
229538
            "PRICE-DATE",
109
229538
            "SSH-KEY-ID",
110
229538
            "SSH-KEY-FINGERPRINT",
111
229538
            "SSH-KEY-NAME",
112
229538
            "SSH-KEY-TYPE",
113
229538
            "SSH-KEY-CREATED-AT",
114
229538
            "SSH-KEY-LAST-USED-AT",
115
229538
        ];
116
11476900
        for name in accessors {
117
11476900
            self.define(Symbol::new(name, SymbolKind::Native));
118
11476900
        }
119
229538
    }
120

            
121
114769
    fn add_map_family_macros(&mut self) {
122
114769
        let mapcar_params = LambdaParams {
123
114769
            required: vec!["func".to_string(), "list".to_string()],
124
114769
            optional: Vec::new(),
125
114769
            rest: Some("lists".to_string()),
126
114769
            key: Vec::new(),
127
114769
            aux: Vec::new(),
128
114769
        };
129
        // Backquoted so the argument FORMS splice into the expansion unevaluated;
130
        // evaluating them here would bake a runtime list's placeholder into it.
131
114769
        let mapcar_body = map_call_template(vec![
132
114769
            unquote("func"),
133
114769
            unquote("list"),
134
114769
            Expr::UnquoteSplicing(Box::new(Expr::Symbol("lists".to_string()))),
135
        ]);
136
114769
        let mapcar_lambda = Expr::Lambda(mapcar_params, Box::new(mapcar_body));
137
114769
        self.define(Symbol::new("MAPCAR", SymbolKind::Macro).with_function(mapcar_lambda));
138

            
139
114769
        let mapc_params = LambdaParams::simple(vec!["func".to_string(), "list".to_string()]);
140
114769
        let mapc_body = map_call_template(vec![unquote("func"), unquote("list")]);
141
114769
        let mapc_lambda = Expr::Lambda(mapc_params, Box::new(mapc_body));
142
114769
        self.define(Symbol::new("MAPC", SymbolKind::Macro).with_function(mapc_lambda));
143
114769
    }
144
}
145

            
146
/// `` `(map ,@args) `` — a MAP call template for the map-family macros.
147
229538
fn map_call_template(args: Vec<Expr>) -> Expr {
148
229538
    let mut call = vec![Expr::Symbol("MAP".to_string())];
149
229538
    call.extend(args);
150
229538
    Expr::Quasiquote(Box::new(Expr::List(call)))
151
229538
}
152

            
153
459076
fn unquote(param: &str) -> Expr {
154
459076
    Expr::Unquote(Box::new(Expr::Symbol(param.to_string())))
155
459076
}