From 9dc0cc7841f28903050a9608f42921958446a28e Mon Sep 17 00:00:00 2001 From: John Date: Fri, 19 Apr 2024 10:49:25 -0500 Subject: [PATCH] cl-interpret: Give the interpreter a little love And stop copying strings around. --- compiler/cl-interpret/src/builtin.rs | 13 ++- compiler/cl-interpret/src/interpret.rs | 130 +++++++++++++++++-------- compiler/cl-interpret/src/lib.rs | 36 ++++--- sample-code/fib.cl | 21 +++- sample-code/fizzbuzz.cl | 4 +- 5 files changed, 139 insertions(+), 65 deletions(-) diff --git a/compiler/cl-interpret/src/builtin.rs b/compiler/cl-interpret/src/builtin.rs index ede42bc..24da9cc 100644 --- a/compiler/cl-interpret/src/builtin.rs +++ b/compiler/cl-interpret/src/builtin.rs @@ -6,7 +6,10 @@ use super::{ temp_type_impl::ConValue, BuiltIn, Callable, }; -use std::io::{stdout, Write}; +use std::{ + io::{stdout, Write}, + rc::Rc, +}; builtins! { const MISC; @@ -65,7 +68,7 @@ builtins! { Ok(match (lhs, rhs) { (ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b), - (ConValue::String(a), ConValue::String(b)) => ConValue::String(a.to_string() + b), + (ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(), _ => Err(Error::TypeError)? }) } @@ -184,6 +187,12 @@ builtins! { _ => Err(Error::TypeError)?, }) } + pub fn deref(tail) -> IResult { + Ok(match tail { + ConValue::Ref(v) => Rc::as_ref(v).clone(), + _ => tail.clone(), + }) + } } /// Turns an argument slice into an array with the (inferred) correct number of elements diff --git a/compiler/cl-interpret/src/interpret.rs b/compiler/cl-interpret/src/interpret.rs index 9bcc2c0..96da7c7 100644 --- a/compiler/cl-interpret/src/interpret.rs +++ b/compiler/cl-interpret/src/interpret.rs @@ -5,7 +5,7 @@ //! meaningless to get a pointer to one, and would be undefined behavior to dereference a pointer to //! one in any situation. -use std::borrow::Borrow; +use std::{borrow::Borrow, rc::Rc}; use super::*; use cl_ast::*; @@ -40,18 +40,21 @@ impl Interpret for Item { } } impl Interpret for Alias { - fn interpret(&self, env: &mut Environment) -> IResult { - todo!("Interpret type alias in {env}") + fn interpret(&self, _env: &mut Environment) -> IResult { + println!("TODO: {self}"); + Ok(ConValue::Empty) } } impl Interpret for Const { - fn interpret(&self, env: &mut Environment) -> IResult { - todo!("interpret const in {env}") + fn interpret(&self, _env: &mut Environment) -> IResult { + println!("TODO: {self}"); + Ok(ConValue::Empty) } } impl Interpret for Static { - fn interpret(&self, env: &mut Environment) -> IResult { - todo!("interpret static in {env}") + fn interpret(&self, _env: &mut Environment) -> IResult { + println!("TODO: {self}"); + Ok(ConValue::Empty) } } impl Interpret for Module { @@ -71,18 +74,22 @@ impl Interpret for Function { } } impl Interpret for Struct { - fn interpret(&self, env: &mut Environment) -> IResult { - todo!("Interpret structs in {env}") + fn interpret(&self, _env: &mut Environment) -> IResult { + println!("TODO: {self}"); + Ok(ConValue::Empty) } } impl Interpret for Enum { - fn interpret(&self, env: &mut Environment) -> IResult { - todo!("Interpret enums in {env}") + fn interpret(&self, _env: &mut Environment) -> IResult { + println!("TODO: {self}"); + Ok(ConValue::Empty) } } impl Interpret for Impl { fn interpret(&self, env: &mut Environment) -> IResult { - todo!("Enter a struct's namespace and insert function definitions into it in {env}"); + println!("TODO: {self}"); + let Self { target: _, body } = self; + body.interpret(env) } } impl Interpret for Stmt { @@ -229,24 +236,24 @@ impl Interpret for Binary { } let tail = tail.interpret(env)?; match kind { - BinaryKind::Mul => env.call("mul", &[head, tail]), - BinaryKind::Div => env.call("div", &[head, tail]), - BinaryKind::Rem => env.call("rem", &[head, tail]), - BinaryKind::Add => env.call("add", &[head, tail]), - BinaryKind::Sub => env.call("sub", &[head, tail]), - BinaryKind::Shl => env.call("shl", &[head, tail]), - BinaryKind::Shr => env.call("shr", &[head, tail]), - BinaryKind::BitAnd => env.call("and", &[head, tail]), - BinaryKind::BitOr => env.call("or", &[head, tail]), - BinaryKind::BitXor => env.call("xor", &[head, tail]), - BinaryKind::RangeExc => env.call("range_exc", &[head, tail]), - BinaryKind::RangeInc => env.call("range_inc", &[head, tail]), - BinaryKind::Lt => env.call("lt", &[head, tail]), - BinaryKind::LtEq => env.call("lt_eq", &[head, tail]), - BinaryKind::Equal => env.call("eq", &[head, tail]), - BinaryKind::NotEq => env.call("neq", &[head, tail]), - BinaryKind::GtEq => env.call("gt_eq", &[head, tail]), - BinaryKind::Gt => env.call("gt", &[head, tail]), + BinaryKind::Lt => head.lt(&tail), + BinaryKind::LtEq => head.lt_eq(&tail), + BinaryKind::Equal => head.eq(&tail), + BinaryKind::NotEq => head.neq(&tail), + BinaryKind::GtEq => head.gt_eq(&tail), + BinaryKind::Gt => head.gt(&tail), + BinaryKind::RangeExc => head.range_exc(tail), + BinaryKind::RangeInc => head.range_inc(tail), + BinaryKind::BitAnd => head & tail, + BinaryKind::BitOr => head | tail, + BinaryKind::BitXor => head ^ tail, + BinaryKind::Shl => head << tail, + BinaryKind::Shr => head >> tail, + BinaryKind::Add => head + tail, + BinaryKind::Sub => head - tail, + BinaryKind::Mul => head * tail, + BinaryKind::Div => head / tail, + BinaryKind::Rem => head % tail, BinaryKind::Dot => todo!("search within a type's namespace!"), BinaryKind::Call => match tail { ConValue::Empty => head.call(env, &[]), @@ -255,6 +262,36 @@ impl Interpret for Binary { }, _ => Ok(head), } + + // // Temporarily disabled, to avoid function dispatch overhead while I screw around + // // Not like it helped much in the first place! + // match kind { + // BinaryKind::Mul => env.call("mul", &[head, tail]), + // BinaryKind::Div => env.call("div", &[head, tail]), + // BinaryKind::Rem => env.call("rem", &[head, tail]), + // BinaryKind::Add => env.call("add", &[head, tail]), + // BinaryKind::Sub => env.call("sub", &[head, tail]), + // BinaryKind::Shl => env.call("shl", &[head, tail]), + // BinaryKind::Shr => env.call("shr", &[head, tail]), + // BinaryKind::BitAnd => env.call("and", &[head, tail]), + // BinaryKind::BitOr => env.call("or", &[head, tail]), + // BinaryKind::BitXor => env.call("xor", &[head, tail]), + // BinaryKind::RangeExc => env.call("range_exc", &[head, tail]), + // BinaryKind::RangeInc => env.call("range_inc", &[head, tail]), + // BinaryKind::Lt => env.call("lt", &[head, tail]), + // BinaryKind::LtEq => env.call("lt_eq", &[head, tail]), + // BinaryKind::Equal => env.call("eq", &[head, tail]), + // BinaryKind::NotEq => env.call("neq", &[head, tail]), + // BinaryKind::GtEq => env.call("gt_eq", &[head, tail]), + // BinaryKind::Gt => env.call("gt", &[head, tail]), + // BinaryKind::Dot => todo!("search within a type's namespace!"), + // BinaryKind::Call => match tail { + // ConValue::Empty => head.call(env, &[]), + // ConValue::Tuple(args) => head.call(env, &args), + // _ => Err(Error::TypeError), + // }, + // _ => Ok(head), + // } } } @@ -291,7 +328,7 @@ impl Interpret for Path { if parts.len() == 1 { match parts.last().expect("parts should not be empty") { PathPart::SuperKw | PathPart::SelfKw => todo!("Path navigation"), - PathPart::Ident(Identifier(s)) => env.get(s).cloned(), + PathPart::Ident(Identifier(s)) => env.get(s), } } else { todo!("Path navigation!") @@ -316,7 +353,7 @@ impl Interpret for Array { for expr in values { out.push(expr.interpret(env)?) } - Ok(ConValue::Array(out)) + Ok(ConValue::Array(out.into())) } } impl Interpret for ArrayRep { @@ -327,14 +364,21 @@ impl Interpret for ArrayRep { _ => Err(Error::TypeError)?, }; let value = value.interpret(env)?; - Ok(ConValue::Array(vec![value; repeat as usize])) + Ok(ConValue::Array(vec![value; repeat as usize].into())) } } impl Interpret for AddrOf { fn interpret(&self, env: &mut Environment) -> IResult { let Self { count: _, mutable: _, expr } = self; - // this is stupid - todo!("Create reference\nfrom expr: {expr}\nin env:\n{env}\n") + match expr.as_ref() { + ExprKind::Index(_) => todo!("AddrOf array index"), + // ExprKind::Path(Path { absolute: false, parts }) => match parts.as_slice() { + // [PathPart::Ident(Identifier(id))] => env.get_ref(id), + // _ => todo!("Path traversal in addrof"), + // }, + ExprKind::Path(_) => todo!("Path traversal in addrof"), + _ => Ok(ConValue::Ref(Rc::new(expr.interpret(env)?))), + } } } impl Interpret for Block { @@ -357,13 +401,15 @@ impl Interpret for Group { impl Interpret for Tuple { fn interpret(&self, env: &mut Environment) -> IResult { let Self { exprs } = self; - Ok(ConValue::Tuple(exprs.iter().try_fold( - vec![], - |mut out, element| { - out.push(element.interpret(env)?); - Ok(out) - }, - )?)) + Ok(ConValue::Tuple( + exprs + .iter() + .try_fold(vec![], |mut out, element| { + out.push(element.interpret(env)?); + Ok(out) + })? + .into(), + )) } } impl Interpret for Loop { diff --git a/compiler/cl-interpret/src/lib.rs b/compiler/cl-interpret/src/lib.rs index f9fbdcf..ae44e39 100644 --- a/compiler/cl-interpret/src/lib.rs +++ b/compiler/cl-interpret/src/lib.rs @@ -30,7 +30,7 @@ pub mod temp_type_impl { function::Function, BuiltIn, Callable, Environment, }; - use std::ops::*; + use std::{ops::*, rc::Rc}; type Integer = isize; @@ -50,11 +50,13 @@ pub mod temp_type_impl { /// A unicode character Char(char), /// A string - String(String), + String(Rc), + /// A reference + Ref(Rc), /// An Array - Array(Vec), + Array(Rc<[ConValue]>), /// A tuple - Tuple(Vec), + Tuple(Rc<[ConValue]>), /// An exclusive range RangeExc(Integer, Integer), /// An inclusive range @@ -166,6 +168,7 @@ pub mod temp_type_impl { char => ConValue::Char, &str => ConValue::String, String => ConValue::String, + Rc => ConValue::String, Function => ConValue::Function, Vec => ConValue::Tuple, &'static dyn BuiltIn => ConValue::BuiltIn, @@ -199,7 +202,7 @@ pub mod temp_type_impl { Add: add = [ (ConValue::Empty, ConValue::Empty) => ConValue::Empty, (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b), - (ConValue::String(a), ConValue::String(b)) => ConValue::String(a + &b), + (ConValue::String(a), ConValue::String(b)) => (a.to_string() + &b).into(), _ => Err(Error::TypeError)? ] BitAnd: bitand = [ @@ -259,6 +262,7 @@ pub mod temp_type_impl { ConValue::Bool(v) => v.fmt(f), ConValue::Char(v) => v.fmt(f), ConValue::String(v) => v.fmt(f), + ConValue::Ref(v) => write!(f, "&{v}"), ConValue::Array(array) => { '['.fmt(f)?; for (idx, element) in array.iter().enumerate() { @@ -296,13 +300,15 @@ pub mod interpret; pub mod function { //! Represents a block of code which lives inside the Interpreter + use super::{Callable, ConValue, Environment, Error, IResult, Interpret}; use cl_ast::{Function as FnDecl, Identifier, Param}; + use std::rc::Rc; /// Represents a block of code which persists inside the Interpreter #[derive(Clone, Debug)] pub struct Function { /// Stores the contents of the function declaration - decl: Box, + decl: Rc, // /// Stores the enclosing scope of the function // env: Box, } @@ -322,19 +328,17 @@ pub mod function { name } fn call(&self, env: &mut Environment, args: &[ConValue]) -> IResult { - let FnDecl { name: Identifier(name), bind: declargs, body, sign: _ } = &*self.decl; + let FnDecl { name: Identifier(name), bind, body, sign: _ } = &*self.decl; // Check arg mapping - if args.len() != declargs.len() { - return Err(Error::ArgNumber { want: declargs.len(), got: args.len() }); + if args.len() != bind.len() { + return Err(Error::ArgNumber { want: bind.len(), got: args.len() }); } let Some(body) = body else { return Err(Error::NotDefined(name.into())); }; // TODO: completely refactor data storage let mut frame = env.frame("fn args"); - for (Param { mutability: _, name: Identifier(name) }, value) in - declargs.iter().zip(args) - { + for (Param { mutability: _, name: Identifier(name) }, value) in bind.iter().zip(args) { frame.insert(name, Some(value.clone())); } match body.interpret(&mut frame) { @@ -364,10 +368,12 @@ pub mod env { ops::{Deref, DerefMut}, }; + type StackFrame = HashMap>; + /// Implements a nested lexical scope #[derive(Clone, Debug)] pub struct Environment { - frames: Vec<(HashMap>, &'static str)>, + frames: Vec<(StackFrame, &'static str)>, } impl Display for Environment { @@ -444,10 +450,10 @@ pub mod env { /// Resolves a variable immutably. /// /// Returns a reference to the variable's contents, if it is defined and initialized. - pub fn get(&self, id: &str) -> IResult<&ConValue> { + pub fn get(&self, id: &str) -> IResult { for (frame, _) in self.frames.iter().rev() { match frame.get(id) { - Some(Some(var)) => return Ok(var), + Some(Some(var)) => return Ok(var.clone()), Some(None) => return Err(Error::NotInitialized(id.into())), _ => (), } diff --git a/sample-code/fib.cl b/sample-code/fib.cl index 44a6eb0..81239e5 100644 --- a/sample-code/fib.cl +++ b/sample-code/fib.cl @@ -1,16 +1,29 @@ // Calculate Fibonacci numbers fn main() { + for num in 0..=30 { + print("fib(", num, ") = ", fib_iterative(num)) + } for num in 0..=30 { print("fib(", num, ") = ", fib(num)) } } -/// Implements the classic recursive definition of fib() +/// The classic recursive definition of fib() fn fib(a: i64) -> i64 { if a > 1 { fib(a - 1) + fib(a - 2) - } else { - 1 - } + } else a +} + +/// The classic iterative algorithm for fib() +fn fib_iterative(n: i64) -> i64 { + let mut a = 0; + let mut b = 1; + let mut c = 1; + for _ in 0..n { + a = b; + b = c; + c = a + b; + } else a } diff --git a/sample-code/fizzbuzz.cl b/sample-code/fizzbuzz.cl index 333b76a..ce5a94e 100644 --- a/sample-code/fizzbuzz.cl +++ b/sample-code/fizzbuzz.cl @@ -1,11 +1,11 @@ // FizzBuzz, using the unstable variadic-`print` builtin fn main() { - fizz_buzz(10, 20) + fizzbuzz(10, 20) } // Outputs FizzBuzz for numbers between `start` and `end`, inclusive -fn fizz_buzz(start: i128, end: i128) { +fn fizzbuzz(start: i128, end: i128) { for x in start..=end { print(if x % 15 == 0 { "FizzBuzz"