Skip to main content

nomiscript/runtime/
symbol.rs

1use std::collections::HashMap;
2
3use crate::ast::Expr;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub 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)]
22pub 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
31impl Symbol {
32    pub fn new(name: impl Into<String>, kind: SymbolKind) -> Self {
33        Self {
34            name: name.into(),
35            kind,
36            value: None,
37            function: None,
38            doc: None,
39            properties: HashMap::new(),
40        }
41    }
42
43    #[must_use]
44    pub fn with_value(mut self, value: Expr) -> Self {
45        self.value = Some(value);
46        self
47    }
48
49    #[must_use]
50    pub fn name(&self) -> &str {
51        &self.name
52    }
53
54    #[must_use]
55    pub fn kind(&self) -> SymbolKind {
56        self.kind
57    }
58
59    #[must_use]
60    pub fn value(&self) -> Option<&Expr> {
61        self.value.as_ref()
62    }
63
64    pub fn set_value(&mut self, value: Expr) {
65        self.value = Some(value);
66    }
67
68    #[must_use]
69    pub fn with_function(mut self, func: Expr) -> Self {
70        self.function = Some(func);
71        self
72    }
73
74    #[must_use]
75    pub fn function(&self) -> Option<&Expr> {
76        self.function.as_ref()
77    }
78
79    pub fn set_function(&mut self, func: Expr) {
80        self.function = Some(func);
81    }
82
83    #[must_use]
84    pub fn with_doc(mut self, doc: impl Into<String>) -> Self {
85        self.doc = Some(doc.into());
86        self
87    }
88
89    #[must_use]
90    pub fn doc(&self) -> Option<&str> {
91        self.doc.as_deref()
92    }
93
94    pub fn set_doc(&mut self, doc: impl Into<String>) {
95        self.doc = Some(doc.into());
96    }
97
98    #[must_use]
99    pub fn get_property(&self, key: &str) -> Option<&Expr> {
100        self.properties.get(key)
101    }
102
103    pub fn set_property(&mut self, key: impl Into<String>, value: Expr) {
104        self.properties.insert(key.into(), value);
105    }
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)]
118pub struct SymbolTable {
119    symbols: HashMap<String, Symbol>,
120    struct_fields: HashMap<String, Vec<String>>,
121}
122
123impl SymbolTable {
124    #[must_use]
125    pub fn new() -> Self {
126        Self::default()
127    }
128
129    #[must_use]
130    pub fn with_builtins() -> Self {
131        let mut table = Self::new();
132        table.register_builtins();
133        table.load_standard_library();
134        // Re-register entity accessors after stdlib to override DEFSTRUCT-generated ones
135        table.register_entity_accessors();
136        table
137    }
138
139    #[must_use]
140    pub fn with_builtins_for_wasm() -> Self {
141        let mut table = Self::new();
142        table.register_builtins();
143        table.load_standard_library();
144        table.register_entity_accessors();
145        table
146    }
147
148    pub fn register_builtins(&mut self) {
149        let operators = ["+", "-", "*", "/", "=", "/=", "<", ">", "<=", ">=", "MOD"];
150        for op in operators {
151            self.define(Symbol::new(op, SymbolKind::Operator));
152        }
153
154        let special_forms = [
155            "IF",
156            "COND",
157            "LET",
158            "LET*",
159            "LETREC",
160            "DEFINE",
161            "DEFUN",
162            "DEFVAR",
163            "DEFPARAMETER",
164            "LAMBDA",
165            "QUOTE",
166            "FUNCTION",
167            "SET!",
168            "SETF",
169            "BEGIN",
170            "AND",
171            "OR",
172            "APPLY",
173            "FUNCALL",
174            "COMPILE",
175            "EVAL",
176            "DESCRIBE",
177            "DO",
178            "DO*",
179            "DEFMACRO",
180            "MACROEXPAND-1",
181            "MACROEXPAND",
182            "LABELS",
183            "DOLIST",
184            "DEFSTRUCT",
185        ];
186        for form in special_forms {
187            self.define(Symbol::new(form, SymbolKind::SpecialForm));
188        }
189
190        let natives = [
191            "CAR",
192            "CDR",
193            "CONS",
194            "LIST",
195            "NULL?",
196            "PAIR?",
197            "EQ?",
198            "EQUAL?",
199            "NOT",
200            "PRINT",
201            "DISPLAY",
202            "NEWLINE",
203            "LENGTH",
204            "APPEND",
205            "REVERSE",
206            "MAP",
207            "FILTER",
208            "FOLD",
209            "DEBUG",
210            "EQL",
211            "EQUAL",
212            "MAKE-STRUCT-INSTANCE",
213            "STRUCT-FIELD",
214            "STRUCT-P",
215            "STRUCT-SET-FIELD",
216            "UPCASE-STRING",
217            "GET-INPUT-ENTITIES",
218            "MAKE-STRUCT-RUNTIME",
219        ];
220        for native in natives {
221            self.define(Symbol::new(native, SymbolKind::Native));
222        }
223
224        self.register_entity_constants();
225        self.register_entity_accessors();
226
227        // Add MAP* family macros
228        self.add_map_family_macros();
229    }
230
231    fn register_entity_constants(&mut self) {
232        use crate::ast::{Expr, Fraction};
233        use scripting_format::{ContextType, EntityType};
234
235        let entity_types: &[(&str, EntityType)] = &[
236            ("+ENTITY-TRANSACTION+", EntityType::Transaction),
237            ("+ENTITY-SPLIT+", EntityType::Split),
238            ("+ENTITY-TAG+", EntityType::Tag),
239            ("+ENTITY-ACCOUNT+", EntityType::Account),
240            ("+ENTITY-COMMODITY+", EntityType::Commodity),
241        ];
242        for (name, ty) in entity_types {
243            self.define(
244                Symbol::new(*name, SymbolKind::Variable)
245                    .with_value(Expr::Number(Fraction::from_integer(i64::from(*ty as u8)))),
246            );
247        }
248
249        let context_types: &[(&str, ContextType)] = &[
250            ("+CONTEXT-CREATE+", ContextType::EntityCreate),
251            ("+CONTEXT-UPDATE+", ContextType::EntityUpdate),
252            ("+CONTEXT-DELETE+", ContextType::EntityDelete),
253            ("+CONTEXT-BATCH+", ContextType::BatchProcess),
254        ];
255        for (name, ct) in context_types {
256            self.define(
257                Symbol::new(*name, SymbolKind::Variable)
258                    .with_value(Expr::Number(Fraction::from_integer(i64::from(*ct as u8)))),
259            );
260        }
261    }
262
263    fn register_entity_accessors(&mut self) {
264        let accessors = [
265            "ENTITY-COUNT",
266            "CONTEXT-TYPE",
267            "PRIMARY-ENTITY-TYPE",
268            "PRIMARY-ENTITY-IDX",
269            "ENTITY-TYPE",
270            "ENTITY-PARENT-IDX",
271            "TRANSACTION-SPLIT-COUNT",
272            "TRANSACTION-TAG-COUNT",
273            "TRANSACTION-IS-MULTI-CURRENCY",
274            "TRANSACTION-POST-DATE",
275            "TRANSACTION-ENTER-DATE",
276            "SPLIT-VALUE-NUM",
277            "SPLIT-VALUE-DENOM",
278            "SPLIT-VALUE",
279            "SPLIT-RECONCILE-STATE",
280            "SPLIT-RECONCILE-DATE",
281            "CREATE-TAG",
282            "TAG-NAME",
283            "TAG-VALUE",
284            "STRING=",
285            "DELETE-ENTITY",
286        ];
287        for name in accessors {
288            self.define(Symbol::new(name, SymbolKind::Native));
289        }
290    }
291
292    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        let mapcar_params = LambdaParams {
297            required: vec!["func".to_string(), "list".to_string()],
298            optional: Vec::new(),
299            rest: Some("lists".to_string()),
300            key: Vec::new(),
301            aux: Vec::new(),
302        };
303        let mapcar_body = Expr::List(vec![
304            Expr::Symbol("APPLY".to_string()),
305            Expr::Quote(Box::new(Expr::Symbol("LIST".to_string()))),
306            Expr::Quote(Box::new(Expr::Symbol("MAP".to_string()))),
307            Expr::Symbol("func".to_string()),
308            Expr::Symbol("list".to_string()),
309            Expr::Symbol("lists".to_string()),
310        ]);
311        let mapcar_lambda = Expr::Lambda(mapcar_params, Box::new(mapcar_body));
312        self.define(Symbol::new("MAPCAR", SymbolKind::Macro).with_function(mapcar_lambda));
313
314        // MAPC - apply function for side effects, return the original list
315        let mapc_params = LambdaParams::simple(vec!["func".to_string(), "list".to_string()]);
316        let mapc_body = Expr::List(vec![
317            Expr::Symbol("LIST".to_string()),
318            Expr::Quote(Box::new(Expr::Symbol("MAP".to_string()))),
319            Expr::Symbol("func".to_string()),
320            Expr::Symbol("list".to_string()),
321        ]);
322        let mapc_lambda = Expr::Lambda(mapc_params, Box::new(mapc_body));
323        self.define(Symbol::new("MAPC", SymbolKind::Macro).with_function(mapc_lambda));
324    }
325
326    fn load_standard_library(&mut self) {
327        // Load financial domain structs automatically
328        self.load_financial_structs();
329
330        // Load essential Common Lisp macros
331        self.load_essential_macros();
332    }
333
334    fn load_financial_structs(&mut self) {
335        // Define financial domain structs that match the binary format
336        let financial_structs = [
337            (
338                "transaction",
339                vec![
340                    "id",
341                    "parent-idx",
342                    "post-date",
343                    "enter-date",
344                    "split-count",
345                    "tag-count",
346                    "is-multi-currency",
347                ],
348            ),
349            (
350                "split",
351                vec![
352                    "id",
353                    "parent-idx",
354                    "account-id",
355                    "commodity-id",
356                    "value-num",
357                    "value-denom",
358                    "reconcile-state",
359                    "reconcile-date",
360                ],
361            ),
362            ("tag", vec!["id", "parent-idx", "name", "value"]),
363            (
364                "account",
365                vec![
366                    "id",
367                    "parent-idx",
368                    "parent-account-id",
369                    "name",
370                    "path",
371                    "tag-count",
372                ],
373            ),
374            (
375                "commodity",
376                vec!["id", "parent-idx", "symbol", "name", "tag-count"],
377            ),
378        ];
379
380        // Use the special forms compiler to define each struct
381        for (struct_name, field_names) in financial_structs {
382            let mut defstruct_args = vec![Expr::Symbol(struct_name.to_uppercase())];
383            for field_name in field_names {
384                defstruct_args.push(Expr::Symbol(field_name.to_uppercase()));
385            }
386
387            // Call defstruct to define the struct and its accessors
388            if let Err(e) = crate::compiler::special::call(self, "DEFSTRUCT", &defstruct_args) {
389                tracing::warn!("Failed to load financial struct {}: {:?}", struct_name, e);
390            }
391        }
392    }
393
394    fn load_essential_macros(&mut self) {
395        use crate::ast::{Expr, LambdaParams};
396
397        // WHEN macro: (when test body...)
398        let when_params = LambdaParams {
399            required: vec!["test".to_string()],
400            optional: Vec::new(),
401            rest: Some("body".to_string()),
402            key: Vec::new(),
403            aux: Vec::new(),
404        };
405        let when_body = Expr::Quasiquote(Box::new(Expr::List(vec![
406            Expr::Symbol("IF".to_string()),
407            Expr::Unquote(Box::new(Expr::Symbol("test".to_string()))),
408            Expr::List(vec![
409                Expr::Symbol("BEGIN".to_string()),
410                Expr::UnquoteSplicing(Box::new(Expr::Symbol("body".to_string()))),
411            ]),
412            Expr::Nil,
413        ])));
414        let when_lambda = Expr::Lambda(when_params, Box::new(when_body));
415        self.define(Symbol::new("WHEN", SymbolKind::Macro).with_function(when_lambda));
416
417        // UNLESS macro: (unless test body...)
418        let unless_params = LambdaParams {
419            required: vec!["test".to_string()],
420            optional: Vec::new(),
421            rest: Some("body".to_string()),
422            key: Vec::new(),
423            aux: Vec::new(),
424        };
425        let unless_body = Expr::Quasiquote(Box::new(Expr::List(vec![
426            Expr::Symbol("IF".to_string()),
427            Expr::Unquote(Box::new(Expr::Symbol("test".to_string()))),
428            Expr::Nil,
429            Expr::List(vec![
430                Expr::Symbol("BEGIN".to_string()),
431                Expr::UnquoteSplicing(Box::new(Expr::Symbol("body".to_string()))),
432            ]),
433        ])));
434        let unless_lambda = Expr::Lambda(unless_params, Box::new(unless_body));
435        self.define(Symbol::new("UNLESS", SymbolKind::Macro).with_function(unless_lambda));
436
437        // Add more essential utility functions
438        self.add_utility_functions();
439    }
440
441    fn add_utility_functions(&mut self) {
442        use crate::ast::{Expr, LambdaParams};
443
444        // UPCASE function (for string manipulation)
445        let upcase_params = LambdaParams::simple(vec!["string".to_string()]);
446        let upcase_body = Expr::Symbol("UPCASE-STRING".to_string()); // Will be implemented as native
447        let upcase_lambda = Expr::Lambda(upcase_params, Box::new(upcase_body));
448        self.define(Symbol::new("UPCASE", SymbolKind::Function).with_function(upcase_lambda));
449    }
450
451    pub fn define(&mut self, symbol: Symbol) {
452        self.symbols.insert(symbol.name.clone(), symbol);
453    }
454
455    #[must_use]
456    pub fn lookup(&self, name: &str) -> Option<&Symbol> {
457        self.symbols.get(name)
458    }
459
460    pub fn lookup_mut(&mut self, name: &str) -> Option<&mut Symbol> {
461        self.symbols.get_mut(name)
462    }
463
464    pub fn remove(&mut self, name: &str) -> Option<Symbol> {
465        self.symbols.remove(name)
466    }
467
468    #[must_use]
469    pub fn contains(&self, name: &str) -> bool {
470        self.symbols.contains_key(name)
471    }
472
473    pub fn iter(&self) -> impl Iterator<Item = (&String, &Symbol)> {
474        self.symbols.iter()
475    }
476
477    pub fn define_struct_fields(&mut self, name: impl Into<String>, fields: Vec<String>) {
478        self.struct_fields.insert(name.into(), fields);
479    }
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)]
488mod tests {
489    use super::*;
490
491    #[test]
492    fn test_symbol_creation() {
493        let sym = Symbol::new("foo", SymbolKind::Variable);
494        assert_eq!(sym.name(), "foo");
495        assert_eq!(sym.kind(), SymbolKind::Variable);
496        assert!(sym.value().is_none());
497    }
498
499    #[test]
500    fn test_symbol_with_value() {
501        let sym = Symbol::new("x", SymbolKind::Variable).with_value(Expr::Bool(true));
502        assert!(sym.value().is_some());
503        assert_eq!(sym.value(), Some(&Expr::Bool(true)));
504    }
505
506    #[test]
507    fn test_symbol_properties() {
508        let mut sym = Symbol::new("test", SymbolKind::Function);
509        sym.set_property("doc", Expr::String("A test function".into()));
510        sym.set_property("pure", Expr::Bool(true));
511
512        assert_eq!(
513            sym.get_property("doc"),
514            Some(&Expr::String("A test function".into()))
515        );
516        assert_eq!(sym.get_property("pure"), Some(&Expr::Bool(true)));
517        assert!(sym.get_property("nonexistent").is_none());
518    }
519
520    #[test]
521    fn test_symbol_table_builtins() {
522        let table = SymbolTable::with_builtins();
523
524        assert!(table.contains("+"));
525        assert_eq!(table.lookup("+").unwrap().kind(), SymbolKind::Operator);
526
527        assert!(table.contains("IF"));
528        assert_eq!(table.lookup("IF").unwrap().kind(), SymbolKind::SpecialForm);
529
530        assert!(table.contains("CAR"));
531        assert_eq!(table.lookup("CAR").unwrap().kind(), SymbolKind::Native);
532
533        assert!(table.contains("EVAL"));
534        assert_eq!(
535            table.lookup("EVAL").unwrap().kind(),
536            SymbolKind::SpecialForm
537        );
538
539        assert!(table.contains("DEBUG"));
540        assert_eq!(table.lookup("DEBUG").unwrap().kind(), SymbolKind::Native);
541    }
542
543    #[test]
544    fn test_symbol_table_define_lookup() {
545        let mut table = SymbolTable::new();
546        table.define(Symbol::new("my-func", SymbolKind::Function));
547
548        assert!(table.contains("my-func"));
549        assert!(!table.contains("undefined"));
550
551        let sym = table.lookup("my-func").unwrap();
552        assert_eq!(sym.kind(), SymbolKind::Function);
553    }
554}