1
use std::collections::HashMap;
2

            
3
use crate::ast::Expr;
4

            
5
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6
pub enum SymbolKind {
7
    /// Built-in operators like +, -, *, /
8
    Operator,
9
    /// Native functions implemented in the runtime
10
    Native,
11
    /// User-defined functions
12
    Function,
13
    /// Variables and constants
14
    Variable,
15
    /// Special forms like if, let, define, lambda
16
    SpecialForm,
17
    /// User-defined macros
18
    Macro,
19
}
20

            
21
#[derive(Debug, Clone, PartialEq)]
22
pub struct Symbol {
23
    name: String,
24
    kind: SymbolKind,
25
    value: Option<Expr>,
26
    function: Option<Expr>,
27
    doc: Option<String>,
28
    properties: HashMap<String, Expr>,
29
}
30

            
31
impl Symbol {
32
2857452
    pub fn new(name: impl Into<String>, kind: SymbolKind) -> Self {
33
2857452
        Self {
34
2857452
            name: name.into(),
35
2857452
            kind,
36
2857452
            value: None,
37
2857452
            function: None,
38
2857452
            doc: None,
39
2857452
            properties: HashMap::new(),
40
2857452
        }
41
2857452
    }
42

            
43
    #[must_use]
44
170789
    pub fn with_value(mut self, value: Expr) -> Self {
45
170789
        self.value = Some(value);
46
170789
        self
47
170789
    }
48

            
49
    #[must_use]
50
45
    pub fn name(&self) -> &str {
51
45
        &self.name
52
45
    }
53

            
54
    #[must_use]
55
277936
    pub fn kind(&self) -> SymbolKind {
56
277936
        self.kind
57
277936
    }
58

            
59
    #[must_use]
60
120892
    pub fn value(&self) -> Option<&Expr> {
61
120892
        self.value.as_ref()
62
120892
    }
63

            
64
24948
    pub fn set_value(&mut self, value: Expr) {
65
24948
        self.value = Some(value);
66
24948
    }
67

            
68
    #[must_use]
69
1113325
    pub fn with_function(mut self, func: Expr) -> Self {
70
1113325
        self.function = Some(func);
71
1113325
        self
72
1113325
    }
73

            
74
    #[must_use]
75
272222
    pub fn function(&self) -> Option<&Expr> {
76
272222
        self.function.as_ref()
77
272222
    }
78

            
79
    pub fn set_function(&mut self, func: Expr) {
80
        self.function = Some(func);
81
    }
82

            
83
    #[must_use]
84
133
    pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
85
133
        self.doc = Some(doc.into());
86
133
        self
87
133
    }
88

            
89
    #[must_use]
90
134
    pub fn doc(&self) -> Option<&str> {
91
134
        self.doc.as_deref()
92
134
    }
93

            
94
    pub fn set_doc(&mut self, doc: impl Into<String>) {
95
        self.doc = Some(doc.into());
96
    }
97

            
98
    #[must_use]
99
3
    pub fn get_property(&self, key: &str) -> Option<&Expr> {
100
3
        self.properties.get(key)
101
3
    }
102

            
103
2
    pub fn set_property(&mut self, key: impl Into<String>, value: Expr) {
104
2
        self.properties.insert(key.into(), value);
105
2
    }
106

            
107
    pub fn remove_property(&mut self, key: &str) -> Option<Expr> {
108
        self.properties.remove(key)
109
    }
110

            
111
    #[must_use]
112
    pub fn properties(&self) -> &HashMap<String, Expr> {
113
        &self.properties
114
    }
115
}
116

            
117
#[derive(Debug, Clone, Default)]
118
pub struct SymbolTable {
119
    symbols: HashMap<String, Symbol>,
120
    struct_fields: HashMap<String, Vec<String>>,
121
}
122

            
123
impl SymbolTable {
124
    #[must_use]
125
15991
    pub fn new() -> Self {
126
15991
        Self::default()
127
15991
    }
128

            
129
    #[must_use]
130
12589
    pub fn with_builtins() -> Self {
131
12589
        let mut table = Self::new();
132
12589
        table.register_builtins();
133
12589
        table.load_standard_library();
134
        // Re-register entity accessors after stdlib to override DEFSTRUCT-generated ones
135
12589
        table.register_entity_accessors();
136
12589
        table
137
12589
    }
138

            
139
    #[must_use]
140
1584
    pub fn with_builtins_for_wasm() -> Self {
141
1584
        let mut table = Self::new();
142
1584
        table.register_builtins();
143
1584
        table.load_standard_library();
144
1584
        table.register_entity_accessors();
145
1584
        table
146
1584
    }
147

            
148
14173
    pub fn register_builtins(&mut self) {
149
14173
        let operators = ["+", "-", "*", "/", "=", "/=", "<", ">", "<=", ">=", "MOD"];
150
155903
        for op in operators {
151
155903
            self.define(Symbol::new(op, SymbolKind::Operator));
152
155903
        }
153

            
154
14173
        let special_forms = [
155
14173
            "IF",
156
14173
            "COND",
157
14173
            "LET",
158
14173
            "LET*",
159
14173
            "LETREC",
160
14173
            "DEFINE",
161
14173
            "DEFUN",
162
14173
            "DEFVAR",
163
14173
            "DEFPARAMETER",
164
14173
            "LAMBDA",
165
14173
            "QUOTE",
166
14173
            "FUNCTION",
167
14173
            "SET!",
168
14173
            "SETF",
169
14173
            "BEGIN",
170
14173
            "AND",
171
14173
            "OR",
172
14173
            "APPLY",
173
14173
            "FUNCALL",
174
14173
            "COMPILE",
175
14173
            "EVAL",
176
14173
            "DESCRIBE",
177
14173
            "DO",
178
14173
            "DO*",
179
14173
            "DEFMACRO",
180
14173
            "MACROEXPAND-1",
181
14173
            "MACROEXPAND",
182
14173
            "LABELS",
183
14173
            "DOLIST",
184
14173
            "DEFSTRUCT",
185
14173
        ];
186
425190
        for form in special_forms {
187
425190
            self.define(Symbol::new(form, SymbolKind::SpecialForm));
188
425190
        }
189

            
190
14173
        let natives = [
191
14173
            "CAR",
192
14173
            "CDR",
193
14173
            "CONS",
194
14173
            "LIST",
195
14173
            "NULL?",
196
14173
            "PAIR?",
197
14173
            "EQ?",
198
14173
            "EQUAL?",
199
14173
            "NOT",
200
14173
            "PRINT",
201
14173
            "DISPLAY",
202
14173
            "NEWLINE",
203
14173
            "LENGTH",
204
14173
            "APPEND",
205
14173
            "REVERSE",
206
14173
            "MAP",
207
14173
            "FILTER",
208
14173
            "FOLD",
209
14173
            "DEBUG",
210
14173
            "EQL",
211
14173
            "EQUAL",
212
14173
            "MAKE-STRUCT-INSTANCE",
213
14173
            "STRUCT-FIELD",
214
14173
            "STRUCT-P",
215
14173
            "STRUCT-SET-FIELD",
216
14173
            "UPCASE-STRING",
217
14173
            "GET-INPUT-ENTITIES",
218
14173
            "MAKE-STRUCT-RUNTIME",
219
14173
        ];
220
396844
        for native in natives {
221
396844
            self.define(Symbol::new(native, SymbolKind::Native));
222
396844
        }
223

            
224
14173
        self.register_entity_constants();
225
14173
        self.register_entity_accessors();
226

            
227
        // Add MAP* family macros
228
14173
        self.add_map_family_macros();
229
14173
    }
230

            
231
14173
    fn register_entity_constants(&mut self) {
232
        use crate::ast::{Expr, Fraction};
233
        use scripting_format::{ContextType, EntityType};
234

            
235
14173
        let entity_types: &[(&str, EntityType)] = &[
236
14173
            ("+ENTITY-TRANSACTION+", EntityType::Transaction),
237
14173
            ("+ENTITY-SPLIT+", EntityType::Split),
238
14173
            ("+ENTITY-TAG+", EntityType::Tag),
239
14173
            ("+ENTITY-ACCOUNT+", EntityType::Account),
240
14173
            ("+ENTITY-COMMODITY+", EntityType::Commodity),
241
14173
        ];
242
70865
        for (name, ty) in entity_types {
243
70865
            self.define(
244
70865
                Symbol::new(*name, SymbolKind::Variable)
245
70865
                    .with_value(Expr::Number(Fraction::from_integer(i64::from(*ty as u8)))),
246
70865
            );
247
70865
        }
248

            
249
14173
        let context_types: &[(&str, ContextType)] = &[
250
14173
            ("+CONTEXT-CREATE+", ContextType::EntityCreate),
251
14173
            ("+CONTEXT-UPDATE+", ContextType::EntityUpdate),
252
14173
            ("+CONTEXT-DELETE+", ContextType::EntityDelete),
253
14173
            ("+CONTEXT-BATCH+", ContextType::BatchProcess),
254
14173
        ];
255
56692
        for (name, ct) in context_types {
256
56692
            self.define(
257
56692
                Symbol::new(*name, SymbolKind::Variable)
258
56692
                    .with_value(Expr::Number(Fraction::from_integer(i64::from(*ct as u8)))),
259
56692
            );
260
56692
        }
261
14173
    }
262

            
263
28346
    fn register_entity_accessors(&mut self) {
264
28346
        let accessors = [
265
28346
            "ENTITY-COUNT",
266
28346
            "CONTEXT-TYPE",
267
28346
            "PRIMARY-ENTITY-TYPE",
268
28346
            "PRIMARY-ENTITY-IDX",
269
28346
            "ENTITY-TYPE",
270
28346
            "ENTITY-PARENT-IDX",
271
28346
            "TRANSACTION-SPLIT-COUNT",
272
28346
            "TRANSACTION-TAG-COUNT",
273
28346
            "TRANSACTION-IS-MULTI-CURRENCY",
274
28346
            "TRANSACTION-POST-DATE",
275
28346
            "TRANSACTION-ENTER-DATE",
276
28346
            "SPLIT-VALUE-NUM",
277
28346
            "SPLIT-VALUE-DENOM",
278
28346
            "SPLIT-VALUE",
279
28346
            "SPLIT-RECONCILE-STATE",
280
28346
            "SPLIT-RECONCILE-DATE",
281
28346
            "CREATE-TAG",
282
28346
            "TAG-NAME",
283
28346
            "TAG-VALUE",
284
28346
            "STRING=",
285
28346
            "DELETE-ENTITY",
286
28346
        ];
287
595266
        for name in accessors {
288
595266
            self.define(Symbol::new(name, SymbolKind::Native));
289
595266
        }
290
28346
    }
291

            
292
14173
    fn add_map_family_macros(&mut self) {
293
        use crate::ast::{Expr, LambdaParams};
294

            
295
        // MAPCAR - alias to MAP, supports one or more list arguments
296
14173
        let mapcar_params = LambdaParams {
297
14173
            required: vec!["func".to_string(), "list".to_string()],
298
14173
            optional: Vec::new(),
299
14173
            rest: Some("lists".to_string()),
300
14173
            key: Vec::new(),
301
14173
            aux: Vec::new(),
302
14173
        };
303
14173
        let mapcar_body = Expr::List(vec![
304
14173
            Expr::Symbol("APPLY".to_string()),
305
14173
            Expr::Quote(Box::new(Expr::Symbol("LIST".to_string()))),
306
14173
            Expr::Quote(Box::new(Expr::Symbol("MAP".to_string()))),
307
14173
            Expr::Symbol("func".to_string()),
308
14173
            Expr::Symbol("list".to_string()),
309
14173
            Expr::Symbol("lists".to_string()),
310
14173
        ]);
311
14173
        let mapcar_lambda = Expr::Lambda(mapcar_params, Box::new(mapcar_body));
312
14173
        self.define(Symbol::new("MAPCAR", SymbolKind::Macro).with_function(mapcar_lambda));
313

            
314
        // MAPC - apply function for side effects, return the original list
315
14173
        let mapc_params = LambdaParams::simple(vec!["func".to_string(), "list".to_string()]);
316
14173
        let mapc_body = Expr::List(vec![
317
14173
            Expr::Symbol("LIST".to_string()),
318
14173
            Expr::Quote(Box::new(Expr::Symbol("MAP".to_string()))),
319
14173
            Expr::Symbol("func".to_string()),
320
14173
            Expr::Symbol("list".to_string()),
321
14173
        ]);
322
14173
        let mapc_lambda = Expr::Lambda(mapc_params, Box::new(mapc_body));
323
14173
        self.define(Symbol::new("MAPC", SymbolKind::Macro).with_function(mapc_lambda));
324
14173
    }
325

            
326
14173
    fn load_standard_library(&mut self) {
327
        // Load financial domain structs automatically
328
14173
        self.load_financial_structs();
329

            
330
        // Load essential Common Lisp macros
331
14173
        self.load_essential_macros();
332
14173
    }
333

            
334
14173
    fn load_financial_structs(&mut self) {
335
        // Define financial domain structs that match the binary format
336
14173
        let financial_structs = [
337
14173
            (
338
14173
                "transaction",
339
14173
                vec![
340
14173
                    "id",
341
14173
                    "parent-idx",
342
14173
                    "post-date",
343
14173
                    "enter-date",
344
14173
                    "split-count",
345
14173
                    "tag-count",
346
14173
                    "is-multi-currency",
347
14173
                ],
348
14173
            ),
349
14173
            (
350
14173
                "split",
351
14173
                vec![
352
14173
                    "id",
353
14173
                    "parent-idx",
354
14173
                    "account-id",
355
14173
                    "commodity-id",
356
14173
                    "value-num",
357
14173
                    "value-denom",
358
14173
                    "reconcile-state",
359
14173
                    "reconcile-date",
360
14173
                ],
361
14173
            ),
362
14173
            ("tag", vec!["id", "parent-idx", "name", "value"]),
363
14173
            (
364
14173
                "account",
365
14173
                vec![
366
14173
                    "id",
367
14173
                    "parent-idx",
368
14173
                    "parent-account-id",
369
14173
                    "name",
370
14173
                    "path",
371
14173
                    "tag-count",
372
14173
                ],
373
14173
            ),
374
14173
            (
375
14173
                "commodity",
376
14173
                vec!["id", "parent-idx", "symbol", "name", "tag-count"],
377
14173
            ),
378
14173
        ];
379

            
380
        // Use the special forms compiler to define each struct
381
70865
        for (struct_name, field_names) in financial_structs {
382
70865
            let mut defstruct_args = vec![Expr::Symbol(struct_name.to_uppercase())];
383
425190
            for field_name in field_names {
384
425190
                defstruct_args.push(Expr::Symbol(field_name.to_uppercase()));
385
425190
            }
386

            
387
            // Call defstruct to define the struct and its accessors
388
70865
            if let Err(e) = crate::compiler::special::call(self, "DEFSTRUCT", &defstruct_args) {
389
                tracing::warn!("Failed to load financial struct {}: {:?}", struct_name, e);
390
70865
            }
391
        }
392
14173
    }
393

            
394
14173
    fn load_essential_macros(&mut self) {
395
        use crate::ast::{Expr, LambdaParams};
396

            
397
        // WHEN macro: (when test body...)
398
14173
        let when_params = LambdaParams {
399
14173
            required: vec!["test".to_string()],
400
14173
            optional: Vec::new(),
401
14173
            rest: Some("body".to_string()),
402
14173
            key: Vec::new(),
403
14173
            aux: Vec::new(),
404
14173
        };
405
14173
        let when_body = Expr::Quasiquote(Box::new(Expr::List(vec![
406
14173
            Expr::Symbol("IF".to_string()),
407
14173
            Expr::Unquote(Box::new(Expr::Symbol("test".to_string()))),
408
14173
            Expr::List(vec![
409
14173
                Expr::Symbol("BEGIN".to_string()),
410
14173
                Expr::UnquoteSplicing(Box::new(Expr::Symbol("body".to_string()))),
411
14173
            ]),
412
14173
            Expr::Nil,
413
14173
        ])));
414
14173
        let when_lambda = Expr::Lambda(when_params, Box::new(when_body));
415
14173
        self.define(Symbol::new("WHEN", SymbolKind::Macro).with_function(when_lambda));
416

            
417
        // UNLESS macro: (unless test body...)
418
14173
        let unless_params = LambdaParams {
419
14173
            required: vec!["test".to_string()],
420
14173
            optional: Vec::new(),
421
14173
            rest: Some("body".to_string()),
422
14173
            key: Vec::new(),
423
14173
            aux: Vec::new(),
424
14173
        };
425
14173
        let unless_body = Expr::Quasiquote(Box::new(Expr::List(vec![
426
14173
            Expr::Symbol("IF".to_string()),
427
14173
            Expr::Unquote(Box::new(Expr::Symbol("test".to_string()))),
428
14173
            Expr::Nil,
429
14173
            Expr::List(vec![
430
14173
                Expr::Symbol("BEGIN".to_string()),
431
14173
                Expr::UnquoteSplicing(Box::new(Expr::Symbol("body".to_string()))),
432
14173
            ]),
433
14173
        ])));
434
14173
        let unless_lambda = Expr::Lambda(unless_params, Box::new(unless_body));
435
14173
        self.define(Symbol::new("UNLESS", SymbolKind::Macro).with_function(unless_lambda));
436

            
437
        // Add more essential utility functions
438
14173
        self.add_utility_functions();
439
14173
    }
440

            
441
14173
    fn add_utility_functions(&mut self) {
442
        use crate::ast::{Expr, LambdaParams};
443

            
444
        // UPCASE function (for string manipulation)
445
14173
        let upcase_params = LambdaParams::simple(vec!["string".to_string()]);
446
14173
        let upcase_body = Expr::Symbol("UPCASE-STRING".to_string()); // Will be implemented as native
447
14173
        let upcase_lambda = Expr::Lambda(upcase_params, Box::new(upcase_body));
448
14173
        self.define(Symbol::new("UPCASE", SymbolKind::Function).with_function(upcase_lambda));
449
14173
    }
450

            
451
2857405
    pub fn define(&mut self, symbol: Symbol) {
452
2857405
        self.symbols.insert(symbol.name.clone(), symbol);
453
2857405
    }
454

            
455
    #[must_use]
456
312854
    pub fn lookup(&self, name: &str) -> Option<&Symbol> {
457
312854
        self.symbols.get(name)
458
312854
    }
459

            
460
28394
    pub fn lookup_mut(&mut self, name: &str) -> Option<&mut Symbol> {
461
28394
        self.symbols.get_mut(name)
462
28394
    }
463

            
464
934
    pub fn remove(&mut self, name: &str) -> Option<Symbol> {
465
934
        self.symbols.remove(name)
466
934
    }
467

            
468
    #[must_use]
469
315
    pub fn contains(&self, name: &str) -> bool {
470
315
        self.symbols.contains_key(name)
471
315
    }
472

            
473
    pub fn iter(&self) -> impl Iterator<Item = (&String, &Symbol)> {
474
        self.symbols.iter()
475
    }
476

            
477
73549
    pub fn define_struct_fields(&mut self, name: impl Into<String>, fields: Vec<String>) {
478
73549
        self.struct_fields.insert(name.into(), fields);
479
73549
    }
480

            
481
    #[must_use]
482
    pub fn struct_fields(&self, name: &str) -> Option<&[String]> {
483
        self.struct_fields.get(name).map(Vec::as_slice)
484
    }
485
}
486

            
487
#[cfg(test)]
488
mod tests {
489
    use super::*;
490

            
491
    #[test]
492
1
    fn test_symbol_creation() {
493
1
        let sym = Symbol::new("foo", SymbolKind::Variable);
494
1
        assert_eq!(sym.name(), "foo");
495
1
        assert_eq!(sym.kind(), SymbolKind::Variable);
496
1
        assert!(sym.value().is_none());
497
1
    }
498

            
499
    #[test]
500
1
    fn test_symbol_with_value() {
501
1
        let sym = Symbol::new("x", SymbolKind::Variable).with_value(Expr::Bool(true));
502
1
        assert!(sym.value().is_some());
503
1
        assert_eq!(sym.value(), Some(&Expr::Bool(true)));
504
1
    }
505

            
506
    #[test]
507
1
    fn test_symbol_properties() {
508
1
        let mut sym = Symbol::new("test", SymbolKind::Function);
509
1
        sym.set_property("doc", Expr::String("A test function".into()));
510
1
        sym.set_property("pure", Expr::Bool(true));
511

            
512
1
        assert_eq!(
513
1
            sym.get_property("doc"),
514
1
            Some(&Expr::String("A test function".into()))
515
        );
516
1
        assert_eq!(sym.get_property("pure"), Some(&Expr::Bool(true)));
517
1
        assert!(sym.get_property("nonexistent").is_none());
518
1
    }
519

            
520
    #[test]
521
1
    fn test_symbol_table_builtins() {
522
1
        let table = SymbolTable::with_builtins();
523

            
524
1
        assert!(table.contains("+"));
525
1
        assert_eq!(table.lookup("+").unwrap().kind(), SymbolKind::Operator);
526

            
527
1
        assert!(table.contains("IF"));
528
1
        assert_eq!(table.lookup("IF").unwrap().kind(), SymbolKind::SpecialForm);
529

            
530
1
        assert!(table.contains("CAR"));
531
1
        assert_eq!(table.lookup("CAR").unwrap().kind(), SymbolKind::Native);
532

            
533
1
        assert!(table.contains("EVAL"));
534
1
        assert_eq!(
535
1
            table.lookup("EVAL").unwrap().kind(),
536
            SymbolKind::SpecialForm
537
        );
538

            
539
1
        assert!(table.contains("DEBUG"));
540
1
        assert_eq!(table.lookup("DEBUG").unwrap().kind(), SymbolKind::Native);
541
1
    }
542

            
543
    #[test]
544
1
    fn test_symbol_table_define_lookup() {
545
1
        let mut table = SymbolTable::new();
546
1
        table.define(Symbol::new("my-func", SymbolKind::Function));
547

            
548
1
        assert!(table.contains("my-func"));
549
1
        assert!(!table.contains("undefined"));
550

            
551
1
        let sym = table.lookup("my-func").unwrap();
552
1
        assert_eq!(sym.kind(), SymbolKind::Function);
553
1
    }
554
}