From c81c728d5487197275ad0c4196c1f998bbbd81b4 Mon Sep 17 00:00:00 2001 From: minish Date: Tue, 1 Jul 2025 23:49:05 -0400 Subject: [PATCH] runtime prototype --- src/lexer.rs | 13 +- src/main.rs | 11 +- src/parser.rs | 60 ++++----- src/parser/util.rs | 50 ++++---- src/runtime.rs | 303 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 376 insertions(+), 61 deletions(-) create mode 100644 src/runtime.rs diff --git a/src/lexer.rs b/src/lexer.rs index 480ed3d..0cc98c7 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,16 +1,21 @@ -use std::{fmt, iter::Peekable}; +use std::{fmt, iter::Peekable, rc::Rc}; -#[derive(Debug)] +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Ident(String); impl fmt::Display for Ident { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.0.fmt(f) } } +impl AsRef for Ident { + fn as_ref(&self) -> &str { + &self.0 + } +} #[derive(Debug)] pub enum Literal { - String(String), + String(Rc), Integer(i64), Boolean(bool), Nil, @@ -234,7 +239,7 @@ where }, c if c == delim => { self.eat(); - break Ok(Token::Literal(Literal::String(str))); + break Ok(Token::Literal(Literal::String(Rc::new(str)))); } _ => str.push(self.next_unwrap()), } diff --git a/src/main.rs b/src/main.rs index 808d70e..bad4114 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,13 @@ -use std::time::Instant; +use std::{rc::Rc, time::Instant}; -use crate::{lexer::Lexer, parser::Parser}; +use crate::{ + lexer::Lexer, + parser::{Expr, Parser}, +}; mod lexer; mod parser; +mod runtime; fn main() { let script = std::fs::read_to_string("./start.leaf").unwrap(); @@ -12,5 +16,6 @@ fn main() { let start = Instant::now(); let block = parser.parse().unwrap(); println!("Parse took {:?}", start.elapsed()); - parser::util::display(parser::Expr::Block(block)); + // parser::util::display(&Rc::new(Expr::Block(Rc::new(block)))); + runtime::exec(&block).unwrap(); } diff --git a/src/parser.rs b/src/parser.rs index e39f9c5..161c237 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,4 +1,4 @@ -use std::{fmt, iter::Peekable}; +use std::{fmt, iter::Peekable, rc::Rc}; use crate::lexer::{Associativity, LexError, Literal, Precedence, Token}; @@ -7,37 +7,37 @@ pub mod util; #[derive(Debug)] pub enum Expr { // Data and variables - Assignment(Box, Box), + Assignment(Rc, Rc), Literal(Literal), // Runtime datatypes - Block(Block), + Block(Rc), // Control flow - Return(Box), - Call(Box, Vec), + Return(Rc), + Call(Rc, Vec>), // Unary operations - Negate(Box), - Not(Box), + Negate(Rc), + Not(Rc), // Binary operations: logical - EqualTo(Box, Box), - NotEqualTo(Box, Box), - And(Box, Box), - Or(Box, Box), + EqualTo(Rc, Rc), + NotEqualTo(Rc, Rc), + And(Rc, Rc), + Or(Rc, Rc), // Binary operations: comparison - LessThan(Box, Box), - LessThanOrEqualTo(Box, Box), - GreaterThan(Box, Box), - GreaterThanOrEqualTo(Box, Box), + LessThan(Rc, Rc), + LessThanOrEqualTo(Rc, Rc), + GreaterThan(Rc, Rc), + GreaterThanOrEqualTo(Rc, Rc), // Binary operations: arithmetic - Add(Box, Box), - Subtract(Box, Box), - Multiply(Box, Box), - Divide(Box, Box), - Exponent(Box, Box), + Add(Rc, Rc), + Subtract(Rc, Rc), + Multiply(Rc, Rc), + Divide(Rc, Rc), + Exponent(Rc, Rc), } #[derive(Debug, Default)] pub struct Block { - pub exprs: Vec, + pub exprs: Vec>, } #[derive(Debug)] @@ -93,7 +93,7 @@ where did_skip = true; } - return did_skip; + did_skip } fn try_peek(&mut self) -> Result<&Token> { // Peek doesn't advance the token stream, so @@ -108,10 +108,10 @@ where self.tokens.next().ok_or(ParseError::UnexpectedEnd) } - fn parse_expr(&mut self, min_prec: Precedence, in_group: bool) -> Result> { + fn parse_expr(&mut self, min_prec: Precedence, in_group: bool) -> Result> { let mut lhs = match self.try_next()? { // literal - Token::Literal(lit) => Box::new(Expr::Literal(lit)), + Token::Literal(lit) => Rc::new(Expr::Literal(lit)), // start of group Token::ParenOpen => { @@ -126,14 +126,14 @@ where let b = self.parse_block(true)?; // skip curly brace self.eat(); - Box::new(Expr::Block(b)) + Rc::new(Expr::Block(Rc::new(b))) } // unary ops!! (prefix) t if t.prefix_precedence().is_some() => { let prec = t.prefix_precedence().unwrap(); let rhs = self.parse_expr(prec, in_group)?; - Box::new(match t { + Rc::new(match t { Token::Minus => Expr::Negate(rhs), Token::Not => Expr::Not(rhs), Token::Return => Expr::Return(rhs), @@ -168,7 +168,7 @@ where let mut exprs = Vec::new(); while !matches!(self.try_peek()?, Token::ParenClose) { - exprs.push(*self.parse_expr(Precedence::Min, false)?); + exprs.push(self.parse_expr(Precedence::Min, false)?); // Continue if there is a comma, // ignore closing parens @@ -181,7 +181,7 @@ where // eat closing paren self.eat(); - lhs = Box::new(Expr::Call(lhs, exprs)); + lhs = Rc::new(Expr::Call(lhs, exprs)); continue; } @@ -203,7 +203,7 @@ where let rhs = self.parse_expr(prec, in_group)?; // join to lhs - lhs = Box::new(match op { + lhs = Rc::new(match op { // equality Token::EqualTo => Expr::EqualTo(lhs, rhs), Token::NotEqualTo => Expr::NotEqualTo(lhs, rhs), @@ -243,7 +243,7 @@ where Err(ParseError::UnexpectedEnd) if !in_block => break, // try to parse expr - Ok(_) => exprs.push(*self.parse_expr(Precedence::Min, false)?), + Ok(_) => exprs.push(self.parse_expr(Precedence::Min, false)?), // invalid Err(err) => return Err(err), diff --git a/src/parser/util.rs b/src/parser/util.rs index 25dd7a5..a1d720c 100644 --- a/src/parser/util.rs +++ b/src/parser/util.rs @@ -1,11 +1,13 @@ +use std::rc::Rc; + use crate::parser::Expr; -pub fn display(e: Expr) { +pub fn display(e: &Rc) { let result = fmt_expr(e, 0); println!("{result}"); } -fn fmt_binop(left: Expr, right: Expr, op: &str, depth: usize) -> String { +fn fmt_binop(left: &Rc, right: &Rc, op: &str, depth: usize) -> String { format!( "({} {} {})", fmt_expr(left, depth), @@ -14,15 +16,15 @@ fn fmt_binop(left: Expr, right: Expr, op: &str, depth: usize) -> String { ) } -fn fmt_expr(e: Expr, depth: usize) -> String { - match e { - Expr::Assignment(l, r) => fmt_binop(*l, *r, "=", depth), +fn fmt_expr(e: &Rc, depth: usize) -> String { + match &**e { + Expr::Assignment(l, r) => fmt_binop(l, r, "=", depth), Expr::Literal(l) => l.to_string(), Expr::Call(l, r) => { - let mut result = fmt_expr(*l, depth); + let mut result = fmt_expr(l, depth); result.push('('); let len = r.len(); - for (i, e) in r.into_iter().enumerate() { + for (i, e) in r.iter().enumerate() { result.push_str(&fmt_expr(e, depth)); if i + 1 != len { result.push_str(", "); @@ -31,11 +33,11 @@ fn fmt_expr(e: Expr, depth: usize) -> String { result.push(')'); result } - Expr::Return(l) => format!("return {}", fmt_expr(*l, depth)), + Expr::Return(r) => format!("return {}", fmt_expr(r, depth)), Expr::Block(b) => { let mut result = String::new(); let len = b.exprs.len(); - for (i, expr) in b.exprs.into_iter().enumerate() { + for (i, expr) in b.exprs.iter().enumerate() { result.push_str(&" ".repeat(depth)); result.push_str(&fmt_expr(expr, depth + 1)); if depth != 0 || i + 1 != len { @@ -47,20 +49,20 @@ fn fmt_expr(e: Expr, depth: usize) -> String { } result } - Expr::Negate(l) => format!("(-{})", fmt_expr(*l, depth)), - Expr::Not(l) => format!("(!{})", fmt_expr(*l, depth)), - Expr::EqualTo(l, r) => fmt_binop(*l, *r, "==", depth), - Expr::NotEqualTo(l, r) => fmt_binop(*l, *r, "!=", depth), - Expr::And(l, r) => fmt_binop(*l, *r, "&&", depth), - Expr::Or(l, r) => fmt_binop(*l, *r, "||", depth), - Expr::LessThan(l, r) => fmt_binop(*l, *r, "<", depth), - Expr::LessThanOrEqualTo(l, r) => fmt_binop(*l, *r, "<=", depth), - Expr::GreaterThan(l, r) => fmt_binop(*l, *r, ">", depth), - Expr::GreaterThanOrEqualTo(l, r) => fmt_binop(*l, *r, ">=", depth), - Expr::Add(l, r) => fmt_binop(*l, *r, "+", depth), - Expr::Subtract(l, r) => fmt_binop(*l, *r, "-", depth), - Expr::Multiply(l, r) => fmt_binop(*l, *r, "*", depth), - Expr::Divide(l, r) => fmt_binop(*l, *r, "/", depth), - Expr::Exponent(l, r) => fmt_binop(*l, *r, "^", depth), + Expr::Negate(r) => format!("(-{})", fmt_expr(r, depth)), + Expr::Not(r) => format!("(!{})", fmt_expr(r, depth)), + Expr::EqualTo(l, r) => fmt_binop(l, r, "==", depth), + Expr::NotEqualTo(l, r) => fmt_binop(l, r, "!=", depth), + Expr::And(l, r) => fmt_binop(l, r, "&&", depth), + Expr::Or(l, r) => fmt_binop(l, r, "||", depth), + Expr::LessThan(l, r) => fmt_binop(l, r, "<", depth), + Expr::LessThanOrEqualTo(l, r) => fmt_binop(l, r, "<=", depth), + Expr::GreaterThan(l, r) => fmt_binop(l, r, ">", depth), + Expr::GreaterThanOrEqualTo(l, r) => fmt_binop(l, r, ">=", depth), + Expr::Add(l, r) => fmt_binop(l, r, "+", depth), + Expr::Subtract(l, r) => fmt_binop(l, r, "-", depth), + Expr::Multiply(l, r) => fmt_binop(l, r, "*", depth), + Expr::Divide(l, r) => fmt_binop(l, r, "/", depth), + Expr::Exponent(l, r) => fmt_binop(l, r, "^", depth), } } diff --git a/src/runtime.rs b/src/runtime.rs new file mode 100644 index 0000000..3f5e477 --- /dev/null +++ b/src/runtime.rs @@ -0,0 +1,303 @@ +use std::{cell::LazyCell, collections::HashMap, fmt, fmt::Write as _, rc::Rc}; + +use crate::{ + lexer::{Ident, Literal}, + parser::{Block, Expr}, +}; + +macro_rules! type_error { + () => { + return Err(RuntimeError::Type.into()) + }; +} +macro_rules! uncallable { + () => { + return Err(RuntimeError::Uncallable.into()) + }; +} + +use Expr as E; +use Literal as L; +use Value as V; + +fn builtin_print(args: Args) -> RuntimeResult { + let mut result = String::new(); + for v in args { + let _ = write!(result, "{v}\t"); + } + println!("{result}"); + Ok(V::nil()) +} +fn builtin_if(args: Args) -> RuntimeResult { + let Some(Value::Boolean(cond)) = args.get(0).map(|v| &**v) else { + type_error!() + }; + let cond = *cond; + + let b_else = args.get(2).map(|v| &**v); + let b_if = args.get(1).map(|v| &**v); + + let empty = Vec::new(); + Ok(match (b_if, b_else) { + (Some(V::Block(b_if)), _) if cond => exec_block(&b_if, empty)?, + (_, Some(V::Block(b_else))) if !cond => exec_block(&b_else, empty)?, + (Some(V::Nil) | None, _) => V::nil(), + (_, Some(V::Nil) | None) => V::nil(), + (_, _) => uncallable!(), + }) +} + +thread_local! { + static NIL: LazyCell> = const { LazyCell::new(|| Rc::new(V::Nil)) }; + static TRUE: LazyCell> = const { LazyCell::new(|| Rc::new(V::Boolean(true))) }; + static FALSE: LazyCell> = const { LazyCell::new(|| Rc::new(V::Boolean(false))) }; +} + +pub enum Value { + String(Rc), + Integer(i64), + Boolean(bool), + Block(Rc), + BuiltinFn(fn(Args) -> RuntimeResult), + Nil, +} +impl Value { + fn bool(b: bool) -> Rc { + if b { + Self::bool_true() + } else { + Self::bool_false() + } + } + fn int(i: i64) -> Rc { + Rc::new(V::Integer(i)) + } + fn str(s: Rc) -> Rc { + Rc::new(V::String(s)) + } + fn block(b: Rc) -> Rc { + Rc::new(V::Block(b)) + } + fn builtin_fn(f: fn(Args) -> RuntimeResult) -> Rc { + Rc::new(V::BuiltinFn(f)) + } + fn bool_true() -> Rc { + TRUE.with(|v| (**v).clone()) + } + fn bool_false() -> Rc { + FALSE.with(|v| (**v).clone()) + } + fn nil() -> Rc { + NIL.with(|v| (**v).clone()) + } +} +impl PartialEq for Value { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (V::String(s1), V::String(s2)) => s1 == s2, + (V::Integer(i1), V::Integer(i2)) => i1 == i2, + (V::Boolean(b1), V::Boolean(b2)) => b1 == b2, + (V::Nil, V::Nil) => true, + _ => false, + } + } +} +impl fmt::Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + V::String(s) => write!(f, "{s}"), + V::Integer(i) => write!(f, "{i}"), + V::Boolean(b) => write!(f, "{b}"), + V::Block(_b) => write!(f, ""), + V::BuiltinFn(_f) => write!(f, ""), + V::Nil => write!(f, "nil"), + } + } +} + +#[derive(Debug)] +pub enum RuntimeError { + Type, + Uncallable, +} +impl fmt::Display for RuntimeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Type => write!(f, "type error"), + Self::Uncallable => write!(f, "uncallable"), + } + } +} +pub type RuntimeResult = Result, RuntimeError>; + +type Args = Vec>; + +enum EvalOutcome { + EarlyReturn(Rc), + Error(RuntimeError), +} +impl From for EvalOutcome { + fn from(err: RuntimeError) -> Self { + Self::Error(err) + } +} + +fn eval(scope: &mut HashMap>, e: &Rc) -> Result, EvalOutcome> { + Ok(match &**e { + E::Assignment(l, r) => { + let Expr::Literal(Literal::Ident(id)) = &**l else { + type_error!() + }; + let v = eval(scope, r)?; + scope.insert(id.clone(), v.clone()); + v + } + E::Literal(L::Ident(id)) => match id.as_ref() { + "print" => V::builtin_fn(builtin_print), + "if" => V::builtin_fn(builtin_if), + _ => scope.get(id).cloned().unwrap_or_else(V::nil), + }, + E::Literal(L::String(s)) => V::str(s.clone()), + E::Literal(L::Integer(i)) => V::int(*i), + E::Literal(L::Boolean(b)) => V::bool(*b), + E::Literal(L::Nil) => V::nil(), + E::Block(b) => V::block(b.clone()), + E::Return(r) => { + let r = eval(scope, r)?; + return Err(EvalOutcome::EarlyReturn(r)); + } + E::Call(l, r) => { + let l = eval(scope, l)?; + let args = r.iter().map(|e| eval(scope, e)).collect::>()?; + match &*l { + V::BuiltinFn(f) => f(args)?, + V::Block(b) => exec_block(b, args)?, + _ => uncallable!(), + } + } + E::Negate(r) => { + let l = eval(scope, r)?; + let V::Integer(i) = *l else { type_error!() }; + V::int(-i) + } + E::Not(r) => { + let l = eval(scope, r)?; + let V::Boolean(b) = *l else { type_error!() }; + V::bool(!b) + } + E::EqualTo(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::bool(*l == *r) + } + E::NotEqualTo(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::bool(*l != *r) + } + E::And(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::bool(if let (V::Boolean(b1), V::Boolean(b2)) = (&*l, &*r) { + *b1 && *b2 + } else { + type_error!() + }) + } + E::Or(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::bool(if let (V::Boolean(b1), V::Boolean(b2)) = (&*l, &*r) { + *b1 || *b2 + } else { + type_error!() + }) + } + E::LessThan(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::bool(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1 < i2 + } else { + type_error!() + }) + } + E::LessThanOrEqualTo(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::bool(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1 <= i2 + } else { + type_error!() + }) + } + E::GreaterThan(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::bool(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1 > i2 + } else { + type_error!() + }) + } + E::GreaterThanOrEqualTo(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::bool(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1 >= i2 + } else { + type_error!() + }) + } + E::Add(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1 + i2 + } else { + type_error!() + }) + } + E::Subtract(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1 + i2 + } else { + type_error!() + }) + } + E::Multiply(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1 * i2 + } else { + type_error!() + }) + } + E::Divide(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1 / i2 + } else { + type_error!() + }) + } + E::Exponent(l, r) => { + let (l, r) = (eval(scope, l)?, eval(scope, r)?); + V::int(if let (V::Integer(i1), V::Integer(i2)) = (&*l, &*r) { + i1.pow(*i2 as u32) + } else { + type_error!() + }) + } + }) +} + +fn exec_block(b: &Block, _args: Args) -> RuntimeResult { + let mut scope = HashMap::new(); + + for e in &b.exprs { + match eval(&mut scope, e) { + Ok(_) => {} + Err(EvalOutcome::EarlyReturn(v)) => return Ok(v), + Err(EvalOutcome::Error(err)) => return Err(err), + } + } + + Ok(V::nil()) +} + +pub fn exec(b: &Block) -> RuntimeResult { + exec_block(b, Vec::new()) +}