From 1e3fcde57a772aeb2ad3b6db5afd367c3282b35e Mon Sep 17 00:00:00 2001 From: Scott Richmond Date: Fri, 20 Jun 2025 14:29:31 -0400 Subject: [PATCH] work on prelude; update tailcall to deal properly with base fns --- src/compiler.rs | 41 ++++++++++++++++++++++++++++++++++++++--- src/main.rs | 47 +++++++++++++++++++++++++++++++++++++++++------ src/vm.rs | 29 +++++++++++++++++++++++++++-- 3 files changed, 106 insertions(+), 11 deletions(-) diff --git a/src/compiler.rs b/src/compiler.rs index b097383..3e36317 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -24,6 +24,7 @@ pub enum Op { Pop, PopN, PushBinding, + PushGlobal, Store, StoreAt, Stash, @@ -151,6 +152,7 @@ impl std::fmt::Display for Op { Pop => "pop", PopN => "pop_n", PushBinding => "push_binding", + PushGlobal => "push_global", Store => "store", StoreAt => "store_at", Stash => "stash", @@ -253,6 +255,7 @@ pub struct Chunk { pub bytecode: Vec, pub keywords: Vec<&'static str>, pub string_patterns: Vec, + pub env: imbl::HashMap<&'static str, Value>, } impl Chunk { @@ -265,7 +268,7 @@ impl Chunk { | Duplicate | Decrement | Truncate | Noop | LoadTuple | LoadList | Eq | Add | Sub | Mult | Div | Unbox | BoxStore | Assert | Get | At | Not | Panic | EmptyString | ConcatStrings | Stringify | MatchType | Return | Match | Print | AppendList - | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing => { + | ConcatList | PushList | PushDict | AppendDict | ConcatDict | Nothing | PushGlobal => { println!("{i:04}: {op}") } Constant | MatchConstant => { @@ -383,12 +386,14 @@ impl<'a> Compiler<'a> { name: &'static str, src: &'static str, enclosing: Option<&'a Compiler>, + env: imbl::HashMap<&'static str, Value>, ) -> Compiler<'a> { let chunk = Chunk { constants: vec![], bytecode: vec![], keywords: vec![], string_patterns: vec![], + env, }; Compiler { chunk, @@ -530,6 +535,31 @@ impl<'a> Compiler<'a> { } fn resolve_binding(&mut self, name: &'static str) { + if let Some(pos) = self.resolve_local(name) { + self.emit_op(Op::PushBinding); + self.emit_byte(pos); + self.stack_depth += 1; + return; + } + if let Some(pos) = self.resolve_upvalue(name) { + self.emit_op(Op::GetUpvalue); + self.emit_byte(pos); + self.stack_depth += 1; + return; + } + if self.chunk.env.contains_key(name) { + self.emit_constant(Value::Keyword(name)); + self.emit_op(Op::PushGlobal); + return; + } + let upvalue = self.get_upvalue(name); + self.emit_op(Op::GetUpvalue); + self.emit_byte(self.upvalues.len()); + self.upvalues.push(upvalue); + self.stack_depth += 1; + } + + fn resolve_binding_old(&mut self, name: &'static str) { match self.resolve_local(name) { Some(position) => { self.emit_op(Op::PushBinding); @@ -1250,8 +1280,13 @@ impl<'a> Compiler<'a> { let compiler = match compilers.get_mut(&arity) { Some(compiler) => compiler, None => { - let mut compiler = - Compiler::new(clause, self.name, self.src, Some(self)); + let mut compiler = Compiler::new( + clause, + self.name, + self.src, + Some(self), + self.chunk.env.clone(), + ); compiler.emit_op(Op::ResetMatch); compilers.insert(arity, compiler); compilers.get_mut(&arity).unwrap() diff --git a/src/main.rs b/src/main.rs index ceceae0..9417d7e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use chumsky::{input::Stream, prelude::*}; +use imbl::HashMap; use std::env; const DEBUG_COMPILE: bool = true; @@ -21,10 +22,43 @@ mod compiler; use crate::compiler::Compiler; mod value; +use value::Value; mod vm; use vm::Vm; +const PRELUDE: &str = " +fn print! (...args) -> base :print! (args) +fn inc (x) -> base :inc (x) +fn dec (x) -> base :dec (x) +fn eq? (x, y) -> base :eq? (x, y) + +#{print!, inc, dec, eq?} +"; + +pub fn prelude() -> HashMap<&'static str, Value> { + let tokens = lexer().parse(PRELUDE).into_output_errors().0.unwrap(); + let parsed = parser() + .parse(Stream::from_iter(tokens).map((0..PRELUDE.len()).into(), |(t, s)| (t, s))) + .into_output_errors() + .0 + .unwrap(); + let parsed: &'static Spanned = Box::leak(Box::new(parsed)); + let mut compiler = Compiler::new(parsed, "prelude", PRELUDE, None, HashMap::new()); + let base = base::make_base(); + compiler.emit_constant(base); + compiler.bind("base"); + compiler.compile(); + + let chunk = compiler.chunk; + let mut vm = Vm::new(chunk); + let prelude = vm.run().clone().unwrap(); + match prelude { + Value::Dict(hashmap) => *hashmap, + _ => unreachable!(), + } +} + pub fn run(src: &'static str) { let (tokens, lex_errs) = lexer().parse(src).into_output_errors(); if !lex_errs.is_empty() { @@ -47,11 +81,9 @@ pub fn run(src: &'static str) { // in any event, the AST should live forever let parsed: &'static Spanned = Box::leak(Box::new(parse_result.unwrap())); - let base = base::make_base(); + let prelude = prelude(); - let mut compiler = Compiler::new(parsed, "test", src, None); - compiler.emit_constant(base); - compiler.bind("base"); + let mut compiler = Compiler::new(parsed, "test", src, None, prelude); compiler.compile(); if DEBUG_COMPILE { @@ -80,8 +112,11 @@ pub fn run(src: &'static str) { pub fn main() { env::set_var("RUST_BACKTRACE", "1"); let src = " -fn print! (...args) -> base :print! (args) -print! (:foo, :bar, :baz) +[ + eq? (1, 2) + eq? (:foo, :foo) + inc (3) +] "; run(src); } diff --git a/src/vm.rs b/src/vm.rs index b61f78e..4a43650 100644 --- a/src/vm.rs +++ b/src/vm.rs @@ -301,6 +301,15 @@ impl Vm { self.push(binding_value); self.ip += 2; } + PushGlobal => { + let key = self.pop(); + let Value::Keyword(name) = key else { + unreachable!("internal Ludus error: expected key for global resolution") + }; + let value = self.chunk().env.get(name).unwrap(); + self.push(value.clone()); + self.ip += 1; + } Store => { self.return_register[0] = self.pop(); self.push(Value::Nothing); @@ -919,10 +928,26 @@ impl Vm { let value = match (arity, base_fn) { (0, BaseFn::Nullary(f)) => f(), (1, BaseFn::Unary(f)) => f(&self.pop()), - (2, BaseFn::Binary(f)) => f(&self.pop(), &self.pop()), - (3, BaseFn::Ternary(f)) => f(&self.pop(), &self.pop(), &self.pop()), + (2, BaseFn::Binary(f)) => { + let y = &self.pop(); + let x = &self.pop(); + f(x, y) + } + (3, BaseFn::Ternary(f)) => { + let z = &self.pop(); + let y = &self.pop(); + let x = &self.pop(); + f(x, y, z) + } _ => return self.panic("internal ludus error"), }; + // algo: + // clear the stack + self.stack.truncate(self.frame.stack_base); + // then pop back out to the enclosing stack frame + self.frame = self.call_stack.pop().unwrap(); + self.ip = self.frame.ip; + // finally, throw the value on the stack self.push(value); } Value::Partial(partial) => {