diff --git a/examples/again.myc b/examples/again.myc index 5e6c557..673e52b 100644 --- a/examples/again.myc +++ b/examples/again.myc @@ -1,10 +1,10 @@ -;; Benchmark: 2.5us -;; Benchmark-Repeat: 804 +;; Benchmark: 2.5us +;; Benchmark-Repeat: 804 ;; Output: 120 (do (def factorial (fn [n acc] (if (= n 0) acc - (again [(- n 1) (* acc n)])))) + (again (- n 1) (* acc n))))) (factorial 5 1)) diff --git a/examples/record_optimizations.myc b/examples/record_optimizations.myc index 802bc3e..7998e75 100644 --- a/examples/record_optimizations.myc +++ b/examples/record_optimizations.myc @@ -1,21 +1,12 @@ ;; Benchmark: 1.7ms ;; Benchmark-Repeat: 3 -;; Benchmark: TBD -;; Tests the effect of record inlining and field lookup optimization -;; Output: 10000 -(do - (macro while [cond body] - `(do - (def _while_loop (fn [] - (if ~cond - (do ~body (_while_loop)) - ...))) - (_while_loop))) - - (def config {:start 0 :limit 10000 :step 2}) - (def x (.start config)) - - (while (< x (.limit config)) - (assign x (+ x (.step config)))) - x -) +;; Tests the effect of record inlining and field lookup optimization +;; Output: 10000 +(do + (def config {:start 0 :limit 10000 :step 2}) + (def x (.start config)) + + (while (< x (.limit config)) + (assign x (+ x (.step config)))) + x +) diff --git a/src/ast/compiler/macros.rs b/src/ast/compiler/macros.rs index 77fe8d9..c460a27 100644 --- a/src/ast/compiler/macros.rs +++ b/src/ast/compiler/macros.rs @@ -22,6 +22,7 @@ struct ExpansionState<'a> { type MacroMap = HashMap, (Vec>, Node)>; /// A registry for macro declarations. +#[derive(Clone)] pub struct MacroRegistry { scopes: Vec, } @@ -78,6 +79,10 @@ impl MacroExpander { } } + pub fn into_registry(self) -> MacroRegistry { + self.registry + } + pub fn expand(&mut self, node: Node) -> Result, String> { self.expand_recursive(node) } diff --git a/src/ast/environment.rs b/src/ast/environment.rs index 2c2ce70..c253467 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -32,6 +32,7 @@ pub struct Environment { pub monomorph_cache: Rc>, pub debug_mode: bool, pub optimization: bool, + pub macro_registry: Rc>, } struct EnvFunctionRegistry { @@ -104,6 +105,7 @@ impl Environment { monomorph_cache: Rc::new(RefCell::new(HashMap::new())), debug_mode: false, optimization: true, + macro_registry: Rc::new(RefCell::new(MacroRegistry::new())), }; env.register_stdlib(); env @@ -119,7 +121,15 @@ impl Environment { global_types: self.global_types.clone(), global_values: self.global_values.clone(), }; - MacroExpander::new(MacroRegistry::new(), evaluator) + MacroExpander::new(self.macro_registry.borrow().clone(), evaluator) + } + + fn eval_prelude(&self, source: &str) { + let mut parser = crate::ast::parser::Parser::new(source).expect("Failed to parse prelude"); + let untyped_ast = parser.parse_expression().expect("Failed to parse prelude expression"); + let mut expander = self.get_expander(); + let _ = expander.expand(untyped_ast).expect("Failed to expand prelude"); + *self.macro_registry.borrow_mut() = expander.into_registry(); } pub fn register_native( @@ -174,6 +184,7 @@ impl Environment { fn register_stdlib(&self) { rtl::register(self); + self.eval_prelude(include_str!("rtl/prelude.myc")); } pub fn dump_ast(&self, source: &str) -> Result { diff --git a/src/ast/parser.rs b/src/ast/parser.rs index eb2c0e0..afa6f04 100644 --- a/src/ast/parser.rs +++ b/src/ast/parser.rs @@ -153,10 +153,23 @@ impl<'a> Parser<'a> { } fn parse_again(&mut self, identity: Identity) -> Result, String> { - let args = Box::new(self.parse_expression()?); + let mut elements = Vec::new(); + + while *self.peek() != TokenKind::RightParen && *self.peek() != TokenKind::EOF { + elements.push(self.parse_expression()?); + } + + let args_node = Node { + identity: identity.clone(), + kind: UntypedKind::Tuple { elements }, + ty: (), + }; + Ok(Node { identity, - kind: UntypedKind::Again { args }, + kind: UntypedKind::Again { + args: Box::new(args_node), + }, ty: (), }) } diff --git a/src/ast/rtl/prelude.myc b/src/ast/rtl/prelude.myc new file mode 100644 index 0000000..2854a43 --- /dev/null +++ b/src/ast/rtl/prelude.myc @@ -0,0 +1,9 @@ +;; Myc Prelude +;; This file is evaluated during Environment bootstrapping. +;; It contains standard macros and functions. + +(macro while [cond body] + `((fn [] (if ~cond + (do ~body (again)) + ))) +) diff --git a/src/integration_test.rs b/src/integration_test.rs index 4a6ce4c..2834fc2 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -267,7 +267,7 @@ mod tests { #[should_panic(expected = "'again' is only allowed in tail position to avoid dead code.")] fn test_again_non_tail_panic() { let env = Environment::new(); - let source = "(do (def f (fn [x] (do (again [(- x 1)]) x))) (f 5))"; + let source = "(do (def f (fn [x] (do (again (- x 1)) x))) (f 5))"; // This will trigger the TCO pass which contains the validation logic let _ = env.run_script(source); } diff --git a/test_while.myc b/test_while.myc new file mode 100644 index 0000000..a1c2bc6 --- /dev/null +++ b/test_while.myc @@ -0,0 +1 @@ +(while (< 1 2) (print 1)) \ No newline at end of file