diff --git a/Cargo.lock b/Cargo.lock index 367187f..e1d6817 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "addr2line" @@ -198,6 +198,7 @@ dependencies = [ "clap", "colored", "interpreter", + "interpreter2", "lexer", "parser", ] @@ -418,6 +419,14 @@ dependencies = [ "parser", ] +[[package]] +name = "interpreter2" +version = "0.1.0" +dependencies = [ + "lexer", + "parser", +] + [[package]] name = "is-terminal" version = "0.4.9" @@ -483,6 +492,7 @@ dependencies = [ "cli", "compiler", "interpreter", + "interpreter2", "lexer", "parser", "vm", diff --git a/Cargo.toml b/Cargo.toml index c133656..30320e8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ members = [ "crates/lexer", "crates/parser", "crates/interpreter", + "crates/interpreter2", "crates/compiler", "bins/lsp", "bins/cli", @@ -20,6 +21,7 @@ members = [ lexer = { path = "./crates/lexer" } parser = { path = "./crates/parser" } interpreter = { path = "./crates/interpreter" } +interpreter2 = { path = "./crates/interpreter2" } compiler = { path = "./crates/compiler" } vm = { path = "./crates/vm" } cli = { path = "./bins/cli" } diff --git a/bins/cli/Cargo.toml b/bins/cli/Cargo.toml index c0b7a97..549afaf 100644 --- a/bins/cli/Cargo.toml +++ b/bins/cli/Cargo.toml @@ -13,5 +13,6 @@ path = "src/main.rs" lexer = { path = "../../crates/lexer" } parser = { path = "../../crates/parser" } interpreter = { path = "../../crates/interpreter" } +interpreter2 = { path = "../../crates/interpreter2" } colored = "2.0.4" clap = { version = "4.4.8", features = ["derive"] } diff --git a/bins/cli/src/main.rs b/bins/cli/src/main.rs index 68acc27..a9e0d67 100644 --- a/bins/cli/src/main.rs +++ b/bins/cli/src/main.rs @@ -1,15 +1,29 @@ +use interpreter::VType; pub use lexer::*; -use clap::Parser; +use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(author, version, about, long_about = None)] struct Cli { - #[arg(help = "Path to file to run")] - path: std::path::PathBuf, #[arg(short='c', long, default_value_t = false, help = "Disable colored output")] no_colors: bool, #[arg(short='v', long, default_value_t = false, help = "Enable verbose output")] verbose: bool, + #[command(subcommand)] + command: Option, +} + +#[derive(Subcommand)] +enum Commands { + /// run a file + Run { + #[arg(help = "Path to file to run")] + path: std::path::PathBuf, + }, + Run2 { + #[arg(help = "Path to file to run")] + path: std::path::PathBuf, + }, } fn tokenize(text: &str) -> Result, PError> { @@ -18,45 +32,111 @@ fn tokenize(text: &str) -> Result, PError> { fn main() { let args = Cli::parse(); - let text = { - std::fs::read_to_string(args.path.clone()).unwrap_or_else(|e| { - eprintln!("{}", e); - std::process::exit(1); - }) - }; - let path = args.path.to_str().unwrap(); - let colors = !args.no_colors; - - if args.verbose { - println!("INPUT:\t{}\n", text); - } - let tokens = tokenize(&text).unwrap_or_else(|e| { - eprintln!("{}", e.format_error(&text, path, colors)); + if args.command.is_none() { + eprintln!("No command provided. Use --help for more information."); std::process::exit(1); - }); - if args.verbose { - println!("TOKENS:\t{}\n", TokenVec(&tokens)); } + let cmd = args.command.unwrap(); + match cmd { + Commands::Run { path } => { + let path_str = path.to_str().unwrap(); + let text = { + std::fs::read_to_string(path.clone()).unwrap_or_else(|e| { + eprintln!("{}", e); + std::process::exit(1); + }) + }; + if !path.exists() { + eprintln!("File not found: {}", path_str); + std::process::exit(1); + } + let colors = !args.no_colors; - let mut iter = tokens.iter(); - let node = parser::parse(&mut iter).unwrap_or_else(|e| { - eprintln!("{}", e.format_error(&text, path, colors)); - std::process::exit(1); - }); + if args.verbose { + println!("INPUT:\t{}\n", text); + } + let tokens = tokenize(&text).unwrap_or_else(|e| { + eprintln!("{}", e.format_error(&text, path_str, colors)); + std::process::exit(1); + }); + if args.verbose { + println!("TOKENS:\t{}\n", TokenVec(&tokens)); + } - if args.verbose { - println!("TREEs:\t{}\n", node); - } + let mut iter = tokens.iter(); + let node = parser::parse(&mut iter).unwrap_or_else(|e| { + eprintln!("{}", e.format_error(&text, path_str, colors)); + std::process::exit(1); + }); + if args.verbose { + println!("TREEs:\t{}\n", node); + } - if args.verbose { - println!("EXECUTING:"); - } - let value = interpreter::interpret(node).unwrap_or_else(|e| { - eprintln!("{}", e.format_error(&text, path, colors)); - std::process::exit(1); - }); + if args.verbose { + println!("EXECUTING:"); + } + let value = interpreter::interpret(node).unwrap_or_else(|e| { + eprintln!("{}", e.format_error(&text, path_str, colors)); + std::process::exit(1); + }); - println!("\nExited with code: {}", value); + if let VType::Undefined = value { + println!("\nExited with code: 0"); + std::process::exit(0); + } + if let VType::Number(n)= value { + println!("\nExited with code: {}", n); + std::process::exit(n); + } + println!("\nExited with code: {}", value); + std::process::exit(0); + }, + Commands::Run2 { path } => { + let path_str = path.to_str().unwrap(); + let text = { + std::fs::read_to_string(path.clone()).unwrap_or_else(|e| { + eprintln!("{}", e); + std::process::exit(1); + }) + }; + if !path.exists() { + eprintln!("File not found: {}", path_str); + std::process::exit(1); + } + let colors = !args.no_colors; + + if args.verbose { + println!("INPUT:\t{}\n", text); + } + let tokens = tokenize(&text).unwrap_or_else(|e| { + eprintln!("{}", e.format_error(&text, path_str, colors)); + std::process::exit(1); + }); + if args.verbose { + println!("TOKENS:\t{}\n", TokenVec(&tokens)); + } + + let mut iter = tokens.iter(); + let node = parser::parse(&mut iter).unwrap_or_else(|e| { + eprintln!("{}", e.format_error(&text, path_str, colors)); + std::process::exit(1); + }); + + if args.verbose { + println!("TREEs:\t{}\n", node); + } + + if args.verbose { + println!("EXECUTING:"); + } + let value = interpreter2::interpret(node).unwrap_or_else(|e| { + eprintln!("{}", e); + std::process::exit(1); + }); + + std::process::exit(0); + }, + } } diff --git a/crates/compiler/src/lib.rs b/crates/compiler/src/lib.rs index 03deb77..5b34353 100644 --- a/crates/compiler/src/lib.rs +++ b/crates/compiler/src/lib.rs @@ -3,7 +3,7 @@ use std::{fmt::{Display, Formatter}}; use lexer::PError; use parser::{Node, Op, Value}; -use vm::{Instr, Instrs, OpCode}; +use vm::{Instr, Instrs}; struct Code { diff --git a/crates/interpreter/src/builtins.rs b/crates/interpreter/src/builtins.rs index 05b1e68..cb0dd23 100644 --- a/crates/interpreter/src/builtins.rs +++ b/crates/interpreter/src/builtins.rs @@ -1,8 +1,8 @@ use std::fmt::Display; -use lexer::PError; +use lexer::{Location, PError}; -use crate::vtype::VType; +use crate::{stack::StackFramePtr, vtype::VType}; pub struct Args<'a>(&'a Vec); impl Display for Args<'_> { @@ -22,6 +22,18 @@ pub enum Builtin { Print, Pow, Assert, + Trace, +} + +impl Display for Builtin { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Builtin::Print => write!(f, "print"), + Builtin::Pow => write!(f, "pow"), + Builtin::Assert => write!(f, "assert"), + Builtin::Trace => write!(f, "trace"), + } + } } impl Builtin { @@ -30,32 +42,28 @@ impl Builtin { "print" => Some(VType::Builtin(Builtin::Print)), "pow" => Some(VType::Builtin(Builtin::Pow)), "assert" => Some(VType::Builtin(Builtin::Assert)), + "trace" => Some(VType::Builtin(Builtin::Trace)), _ => None, } } - pub fn includes(name: &str) -> bool { - match name { - "print" => true, - "pow" => true, - "assert" => true, - _ => false, - } - } - pub fn call(&self, args: &Vec) -> Result { + pub fn call(&self, location: Location, args: &Vec, trace: &StackFramePtr) -> Result { match self{ Self::Print => { println!("{}", Args(args)); Ok(VType::Undefined) }, + Self::Trace => { + Ok(VType::String(trace.borrow().stacktrace())) + }, Self::Pow => { if args.len() != 2 { - return Err(PError::new(lexer::Location::zero(), "Expected 2 arguments")); + return Err(PError::new(location, "Expected 2 arguments")); } let a = args[0].clone(); let b = args[1].clone(); match (a, b) { (VType::Number(a), VType::Number(b)) => Ok(VType::Number(a.pow(b as u32))), - _ => Err(PError::new(lexer::Location::zero(), "Expected numbers")), + _ => Err(PError::new(location, "Expected numbers")), } }, Self::Assert => { @@ -63,11 +71,11 @@ impl Builtin { match a { VType::Bool(b) => { if !b { - return Err(PError::new(lexer::Location::zero(), "Assertion failed")); + return Err(PError::new(location, "Assertion failed")); } Ok(VType::Undefined) }, - _ => Err(PError::new(lexer::Location::zero(), "Expected boolean")), + _ => Err(PError::new(location, "Expected boolean")), } } diff --git a/crates/interpreter/src/context.rs b/crates/interpreter/src/context.rs index 5c746e3..7a824fe 100644 --- a/crates/interpreter/src/context.rs +++ b/crates/interpreter/src/context.rs @@ -1,17 +1,24 @@ -use std::{collections::HashMap, rc::Rc, fmt::Display}; +use std::{collections::HashMap, fmt::Display, rc::Rc, cell::RefCell}; -use lexer::Location; +use lexer::{Location}; use crate::vtype::VType; +pub enum StackError { + ValueError, + RefError, +} + #[derive(Debug, Clone)] pub struct Context { name: String, location: Location, dict: HashMap, - parent: Option>, + parent: Option>>, } + + impl Display for Context { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}:{}", self.name, self.location.get_line()) @@ -19,45 +26,77 @@ impl Display for Context { } impl Context { - pub fn new(name: &str, location: Location) -> Rc { - Rc::new(Context { + pub fn new(name: &str, location: Location) -> Rc> { + Rc::new(RefCell::new(Context { name: name.to_string(), location, dict: HashMap::new(), parent: None, - }) + })) } - pub fn new_child(name: &str, location: Location, parent: &Rc) -> Rc { - Rc::new(Context { - name: name.to_string(), + pub fn new_child(name: &str, location: Location, parent: &Rc>) -> Rc> { + Rc::new(RefCell::new(Context { + name: format!("{}:{}", parent.borrow().name, name), location, dict: HashMap::new(), parent: Some(parent.clone()), - }) + })) } - pub fn get_var(self: &Rc, name: &str) -> Option { + pub fn print_stack(&self) { + println!("Context: {}", self); + for (k, v) in &self.dict { + println!(" {}: {}", k, v); + } + if let Some(parent) = &self.parent { + println!("parent:"); + parent.borrow().print_stack(); + } + println!("---"); + } + + pub fn get_var(&self, name: &str) -> Option { if let Some(v) = self.dict.get(name) { return Some(v.clone()); } if let Some(parent) = &self.parent { - return parent.get_var(name); + return parent.borrow().get_var(name); } None } - pub fn define_var(&mut self, name: &str) { - self.dict.insert(name.to_string(), VType::Undefined); + + pub fn exists(&self, name: &str) -> bool { + let exists = self.dict.contains_key(name); + if !exists { + if let Some(parent) = &self.parent { + parent.borrow().exists(name) + } else { false } + } else { true } } - pub fn set_var(&mut self, name: &str, value: VType) { + + pub fn define_var(&mut self, name: &str, value: VType) { self.dict.insert(name.to_string(), value); } + + pub fn set_var(&mut self, name: &str, value: VType) -> Result<(), StackError> { + if self.dict.contains_key(name){ + self.dict.insert(name.to_string(), value); + return Ok(()); + }else{ + if let Some(parent) = &self.parent { + return parent.borrow_mut().set_var(name, value); + } else { + return Err(StackError::RefError); + } + } + } pub fn create_child(&mut self, name: &str, value: VType) { self.dict.insert(name.to_string(), value); } pub fn populate_stack(&self, stack: &mut Vec) { if let Some(ref parent) = self.parent { - parent.populate_stack(stack); + parent.borrow().populate_stack(stack); } stack.push(self.to_string()); } diff --git a/crates/interpreter/src/globals.rs b/crates/interpreter/src/globals.rs deleted file mode 100644 index 1727f1e..0000000 --- a/crates/interpreter/src/globals.rs +++ /dev/null @@ -1,20 +0,0 @@ - -struct Globals; -impl Globals { - pub fn is_builtin(name: &str) -> bool { - match name { - "print" => true, - "pow" => true, - _ => false, - } - } - pub fn builtin(name: &str) -> Option { - match name { - "print" => Some(VType::Builtin(Builtin::Print)), - "pow" => Some(VType::Builtin(Builtin::Pow)), - _ => None, - } - - } -} - diff --git a/crates/interpreter/src/lib.rs b/crates/interpreter/src/lib.rs index c970307..b1102f9 100644 --- a/crates/interpreter/src/lib.rs +++ b/crates/interpreter/src/lib.rs @@ -1,7 +1,9 @@ mod vtype; mod context; mod builtins; -use std::rc::Rc; +mod stack; +use std::{collections::VecDeque, rc::Rc}; +use std::cell::RefCell; use parser::{Node, Op}; use lexer::PError; @@ -9,6 +11,8 @@ use builtins::Builtin; pub use vtype::VType; use context::Context; +use crate::{context::StackError, stack::{StackFrame, StackFramePtr}}; + pub fn extract_names(node: &Node) -> Result,PError> { let mut names = Vec::new(); @@ -20,163 +24,189 @@ pub fn extract_names(node: &Node) -> Result,PError> { } Ok(names) } + macro_rules! compute_unary{ - ($node:ident, $ctx: ident, $op:ident) => { + ($node:ident, $ctx: ident, $stk: ident, $op:ident) => { { - let left = compute($node.left().unwrap(), $ctx)?; + let left = compute($node.left().unwrap(), $ctx, $stk)?; Ok(left.$op()) } }; } + macro_rules! compute_binary { - ($node:ident, $ctx: ident, $op:ident) => { + ($node:ident, $ctx: ident, $stk: ident, $op:ident) => { { - let left = compute($node.left().unwrap(), $ctx)?; - let right = compute($node.right().unwrap(), $ctx)?; + let left = compute($node.left().unwrap(), $ctx, $stk)?; + let right = compute($node.right().unwrap(), $ctx, $stk)?; Ok(left.$op(&right)) } }; } macro_rules! compute_binop { - ($node:ident, $ctx: ident, $op:tt) => { + ($node:ident, $ctx: ident, $stk: ident, $op:tt) => { { - let left = compute($node.left().unwrap(), $ctx)?; - let right = compute($node.right().unwrap(), $ctx)?; + let left = compute($node.left().unwrap(), $ctx, $stk)?; + let right = compute($node.right().unwrap(), $ctx, $stk)?; Ok(left $op right) } }; } -pub fn compute(node: &Node, context: &mut Rc) -> Result { - if let Some(v) = context.get_var("!return") { - //println!("Ret: {} {}", v, node); - return Ok(v.clone()) +pub fn compute(node: &Node, context: &Rc>, stack: &StackFramePtr) -> Result { + if stack.borrow().has_returned() { + let ret = stack.borrow().get_return_value().unwrap_or(VType::Undefined).clone(); + return Ok(ret); } match node.op { Op::Return => { - let value = compute(node.children.get(0).unwrap(), context)?; - Rc::get_mut(context).unwrap().set_var("!return", value.clone()); + let value = compute(node.get_child(0), context, stack)?; + stack.borrow_mut().set_return_value(value.clone()); Ok(value) }, Op::DefineFunc => { - let name = node.children.get(0).unwrap(); - let args = node.children.get(1).unwrap(); - let body = node.children.get(2).unwrap(); + let name = node.get_child(0); + let args = node.get_child(1); + let body = node.get_child(2); let arg_names = extract_names(args)?; - let func = VType::Func(arg_names, body.clone()); - Rc::get_mut(context).unwrap().set_var(name.id.as_ref().unwrap(), func); - //dict.insert(name.id.as_ref().unwrap().clone(), func); - Ok(VType::Ref(name.id.as_ref().unwrap().clone())) + let func = VType::Func(arg_names, body.clone(), format!("{}", name)); + let mut id = name.id.as_ref().unwrap_or(&"".to_string()).clone(); + if id.is_empty() { + id = format!("_anon_{}", node.location); + } + context.borrow_mut().define_var(id.as_str(), func); + Ok(VType::Ref(id)) }, Op::DefineVar => { - Rc::get_mut(context).unwrap().define_var(node.id.as_ref().unwrap()); + context.borrow_mut().define_var(node.id.as_ref().unwrap(), VType::Undefined); if !node.children.is_empty() { - compute(node.children.get(0).unwrap(), context)?; + compute(node.get_child(0), context, stack)?; } Ok(VType::Undefined) }, Op::While => { let mut last = VType::Undefined; - while context.get_var("!return").is_none() && compute(node.children.get(0).unwrap(), context)? != VType::Bool(false) { - last = compute(node.children.get(1).unwrap(), context)?; + while !stack.borrow().has_returned() && compute(node.get_child(0), context, stack)? != VType::Bool(false) { + last = compute(node.get_child(1), context, stack)?; } Ok(last) }, Op::Args => { let mut args = Vec::new(); for child in &node.children { - args.push(compute(child, context)?); + args.push(compute(child, context, stack)?); } Ok(VType::Args(args)) }, Op::Call => { - let func = compute(node.children.get(0).unwrap(), context)?; + let func = compute(node.get_child(0), context, stack)?; match func { VType::Builtin(func) => { - let args = compute(node.children.get(1).unwrap(), context)?; + let next_stack = StackFrame::new(format!("{}", func).as_str(), node.location, Some(stack.clone())); + let args = compute(node.get_child(1), context, stack)?; let VType::Args(args) = args else { return Err(PError::new(node.location, "Invalid arguments")); }; - func.call(&args) + func.call(node.location, &args, &next_stack) }, VType::Ref(name) => { - let args = compute(node.children.get(1).unwrap(), context)?; + let next_stack = StackFrame::new(name.as_str(), node.location, Some(stack.clone())); + let args = compute(node.get_child(1), context, stack)?; let VType::Args(args) = args else { return Err(PError::new(node.location, "Invalid arguments")); }; - let func = context.get_var(&name).unwrap(); + let func = context.borrow().get_var(&name).unwrap(); match func { - VType::Func(arg_names, body) => { - let mut child_context = Context::new_child(&name, node.location, context); + VType::Func(arg_names, body, name) => { + let child_context = Context::new_child(&name, node.location, context); for (idx, arg) in arg_names.iter().enumerate() { - Rc::get_mut(&mut child_context).unwrap().set_var(arg, args.get(idx).unwrap().clone()); - //newDict.insert(arg.clone(), args.get(idx).unwrap().clone()); + child_context.borrow_mut().define_var(arg, args.get(idx).unwrap().clone()) + } + let val = compute(&body, &child_context, &next_stack)?; + let ret = next_stack.borrow().get_return_value().unwrap_or(VType::Undefined).clone(); + + if let VType::Undefined = ret { + Ok(val) + } else { + Ok(ret) } - compute(&body, &mut child_context) }, _ => { return Err(PError::new(node.location, "Not a function")); } } }, - VType::Func(arg_names, body) => { - let args = compute(node.children.get(1).unwrap(), context)?; + VType::Func(arg_names, body, name) => { + let next_stack = StackFrame::new(name.as_str(), node.location, Some(stack.clone())); + let args = compute(node.get_child(1), context, stack)?; let VType::Args(args) = args else { return Err(PError::new(node.location, "Invalid arguments")); }; - let mut child_context = Context::new_child(node.id.as_ref().unwrap_or(&"".to_string()).as_str(), node.location, context); + let child_context = Context::new_child(node.id.as_ref().unwrap_or(&"".to_string()).as_str(), node.location, context); for (idx, arg) in arg_names.iter().enumerate() { - Rc::get_mut(&mut child_context).unwrap().set_var(arg, args.get(idx).unwrap().clone()); - //newDict.insert(arg.clone(), args.get(idx).unwrap().clone()); + child_context.borrow_mut().define_var(arg, args.get(idx).unwrap().clone()) + } + let val = compute(&body, &child_context, &next_stack)?; + let ret = next_stack.borrow().get_return_value().unwrap_or(VType::Undefined).clone(); + if let VType::Undefined = ret { + Ok(val) + } else { + Ok(ret) } - compute(&body, &mut child_context) }, _ => { return Err(PError::new(node.location, "Not a function")); } } }, - Op:: Branch=> { - let cond = compute(node.children.get(0).unwrap(), context)?; + Op::Branch => { + let cond = compute(node.get_child(0), context, stack)?; if let VType::Number(c) = cond { if c != 0 { - return compute(node.children.get(1).unwrap(), context); + return compute(node.get_child(1), context, stack); } else { - return compute(node.children.get(2).unwrap(), context); + return compute(node.get_child(2), context, stack); } } if let VType::Bool(b) = cond { if b { - return compute(node.children.get(1).unwrap(), context); + return compute(node.get_child(1), context, stack); } else { - return compute(node.children.get(2).unwrap(), context); + return compute(node.get_child(2), context, stack); } } else { return Err(PError::new(node.location, format!("Invalid condition {}", cond).as_str())); } }, - Op::Scope | Op::Paren | Op::Loop => { + Op::Paren => { let mut last = VType::Undefined; for child in &node.children { - last = compute(child, context)?; + last = compute(child, context, stack)?; } Ok(last) }, - Op::Add => compute_binop!(node, context, +), - Op::Sub => compute_binop!(node, context, -), - Op::Mul => compute_binop!(node, context, *), - Op::Div => compute_binop!(node, context, /), - Op::Mod => compute_binary!(node, context, modulo), - Op::Eq => compute_binary!(node, context, equal), - Op::Neq => compute_binary!(node, context, not_equal), - Op::Not => compute_unary!(node, context, not), - Op::Gt => compute_binary!(node, context, gt), - Op::Lt => compute_binary!(node, context, lt), - Op::Geq => compute_binary!(node, context, geq), - Op::Leq => compute_binary!(node, context, leq), - Op::And => compute_binary!(node, context, and), - Op::Or => compute_binary!(node, context, or), - + Op::Scope | Op::Loop => { + let mut last = VType::Undefined; + let child_context = Context::new_child("child", node.location, context); + for (i, child) in node.children.iter().enumerate() { + last = compute(child, &child_context, stack)?; + } + Ok(last) + }, + Op::Add => compute_binop!(node, context, stack, +), + Op::Sub => compute_binop!(node, context, stack, -), + Op::Mul => compute_binop!(node, context, stack, *), + Op::Div => compute_binop!(node, context, stack, /), + Op::Mod => compute_binary!(node, context, stack, modulo), + Op::Eq => compute_binary!(node, context, stack, equal), + Op::Neq => compute_binary!(node, context, stack, not_equal), + Op::Not => compute_unary!(node, context, stack, not), + Op::Gt => compute_binary!(node, context, stack, gt), + Op::Lt => compute_binary!(node, context, stack, lt), + Op::Geq => compute_binary!(node, context, stack, geq), + Op::Leq => compute_binary!(node, context, stack, leq), + Op::And => compute_binary!(node, context, stack, and), + Op::Or => compute_binary!(node, context, stack, or), Op::Value => { let Some(val) = node.value.as_ref() else { @@ -193,31 +223,46 @@ pub fn compute(node: &Node, context: &mut Rc) -> Result if let Some(v) = Builtin::try_new(id) { return Ok(v); } + if !context.borrow().exists(id) { + return Err(PError::new(node.location, format!("Variable {} not defined", id).as_str())); + } - let Some(val) = context.get_var(id) else { - return Err(PError::new(node.location, "Variable not defined")); - }; - /* - let Some(val) = dict.get(id) else { - return Err(PError::new(node.location, "Variable not defined")); + let Some(val) = context.borrow().get_var(id) else { + return Err(PError::new(node.location, format!("Variable {} not defined", id).as_str())); }; - */ + Ok(val.clone()) }, Op::Assign => { let Some(ref left) = node.left().unwrap().id else { return Err(PError::new(node.location, "Invalid LHS for assignment")); }; - let right = compute(node.right().unwrap(), context)?; - Rc::get_mut(context).unwrap().set_var(left, right.clone()); - //dict.insert(left.clone(), right.clone()); + if !context.borrow().exists(left) { + return Err(PError::new(node.location, format!("Variable {} not defined", left).as_str())); + } + let right = compute(node.right().unwrap(), context, stack)?; + if let Err(err) = context.borrow_mut().set_var(left, right.clone()) { + context.borrow().print_stack(); + match err { + StackError::ValueError => { + return Err(PError::new(node.location, format!("Value error setting var {}", left).as_str())); + }, + StackError::RefError => { + return Err(PError::new(node.location, format!("Ref error setting var {}", left).as_str())); + } + } + } Ok(right) - } + }, + Op::Access => todo!("Access not implemented"), } } + pub fn interpret(node: Node) -> Result { - let mut context = Context::new("global", node.location); - compute(&node, &mut context) + let context = Context::new("global", node.location); + let stack = StackFrame::new("global", node.location, None); + + compute(&node, &context, &stack) } diff --git a/crates/interpreter/src/stack.rs b/crates/interpreter/src/stack.rs new file mode 100644 index 0000000..40d7906 --- /dev/null +++ b/crates/interpreter/src/stack.rs @@ -0,0 +1,53 @@ +use std::cell::RefCell; +use std::rc::Rc; +use lexer::Location; +use crate::vtype::VType; + + +pub struct StackFrame { + function_name: String, + location: Location, + return_value: Option, + return_address: Option, +} + +pub type StackFramePtr = Rc>; + +impl StackFrame { + pub fn new(function_name: &str, location: Location, return_address: Option) -> Rc> { + Rc::new(RefCell::new(StackFrame { + function_name: function_name.to_string(), + location, + return_value: None, + return_address, + })) + } + + pub fn set_return_value(&mut self, value: VType) { + self.return_value = Some(value); + } + + pub fn has_returned(&self) -> bool { + self.return_value.is_some() + } + + pub fn get_return_value(&self) -> Option { + self.return_value.clone() + } + + pub fn stacktrace(&self) -> String { + let mut trace = format!("at {}:{}", self.function_name, self.location.get_line()); + if let Some(ret_addr) = &self.return_address { + trace.push_str(&format!("\n{}", ret_addr.borrow().stacktrace())); + } + trace + } + + pub fn get_depth(&self) -> usize { + if let Some(ret_addr) = &self.return_address { + 1 + ret_addr.borrow().get_depth() + } else { + 1 + } + } +} diff --git a/crates/interpreter/src/vtype.rs b/crates/interpreter/src/vtype.rs index 396c747..4e3f821 100644 --- a/crates/interpreter/src/vtype.rs +++ b/crates/interpreter/src/vtype.rs @@ -6,7 +6,7 @@ use crate::builtins::Builtin; #[derive(Debug, Clone, PartialEq, Eq)] pub enum VType { - Func(Vec, Node), + Func(Vec, Node, String), Ref(String), Builtin(Builtin), Bool(bool), @@ -24,6 +24,33 @@ impl From for VType { } } +impl From for String { + fn from(v: VType) -> Self { + match v { + VType::Number(n) => n.to_string(), + VType::String(s) => s, + VType::Undefined => "undefined".to_string(), + VType::Bool(b) => b.to_string(), + VType::Builtin(b) => format!("{:?}", b), + VType::Func(_, _, name) => { if !name.is_empty() { format!("fn {}", name) } else { "fn".to_string() } }, + VType::Ref(s) => format!("ref {}", s), + VType::Args(args) => { + let mut s = String::from("("); + let mut first = true; + for arg in args { + if !first { + s.push_str(", "); + } + first = false; + s.push_str(&arg.to_string()); + } + s.push(')'); + s + }, + } + } +} + impl From for VType { fn from(n: u32) -> Self { VType::Number(n as i32) @@ -49,7 +76,7 @@ impl Display for VType { VType::Undefined => write!(f, "undefined"), VType::Bool(b) => write!(f, "{}", b), VType::Builtin(b) => write!(f, "{:?}", b), - VType::Func(_, _) => write!(f, "function"), + VType::Func(_, _, name) => write!(f, "fn {}", name), VType::Ref(s) => write!(f, "ref {}", s), VType::Args(args) => { write!(f, "(")?; @@ -77,7 +104,8 @@ impl Add for VType { (VType::String(s1), VType::Number(s2)) => VType::String(s1 + &s2.to_string()), (VType::Number(s1), VType::String(s2)) => VType::String(s1.to_string() + &s2), (VType::String(s1), VType::String(s2)) => VType::String(s1 + &s2), - _ => panic!("Operation not yet implemented"), + (VType::String(s1), VType::Undefined) => VType::String(s1 + "undefined"), + (a, b) => panic!("Operation not yet implemented {:?} + {:?}", a, b), } } } diff --git a/crates/interpreter2/Cargo.toml b/crates/interpreter2/Cargo.toml new file mode 100644 index 0000000..94ce907 --- /dev/null +++ b/crates/interpreter2/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "interpreter2" +version = "0.1.0" +edition = "2021" + +[lib] +tests = true +path = "src/lib.rs" +crate-type = ["lib"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +lexer = { path = "../lexer" } +parser = { path = "../parser" } diff --git a/crates/interpreter2/src/lib.rs b/crates/interpreter2/src/lib.rs new file mode 100644 index 0000000..a8b2f15 --- /dev/null +++ b/crates/interpreter2/src/lib.rs @@ -0,0 +1,251 @@ +mod types; +use std::fmt::Display; +use std::{collections::VecDeque, rc::Rc}; +use std::cell::RefCell; + +use parser::{Node, Op, Value}; +use lexer::PError; + + +#[derive(Debug, Clone, PartialEq, Eq)] +enum VType { + Func(Vec, Node, String), + Ref(String), + Bool(bool), + Args(Vec), + Number(i32), + String(String), + Undefined, +} + + +impl VType { + fn unwrap_number(&self) -> Result { + if let VType::Number(n) = self { + Ok(*n) + } else { + Err(RuntimeError::new("Expected a number", None)) + } + } +} + +impl From for VType { + fn from(n: Value) -> Self { + match n { + Value::Number(n) => VType::Number(n), + Value::String(s) => VType::String(s), + } + } +} + +#[derive(Debug)] +pub struct RuntimeError { + message: String, + location: Option, +} + +impl RuntimeError { + fn new(message: &str, location: Option) -> Self { + RuntimeError { + message: message.to_string(), + location, + } + } +} + +impl Display for RuntimeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if let Some(loc) = &self.location { + write!(f, "{} at {:?}", self.message, loc) + } else { + write!(f, "{}", self.message) + } + } +} + +struct StackFrame<'a> { + node: &'a Node, + values: Vec, + parent: Option>>>, + + // You can add more fields as needed, such as local variables, return address, etc. +} + +impl StackFrame<'_> { + fn get_depth(&self) -> usize { + if let Some(parent) = &self.parent { + 1 + parent.borrow().get_depth() + } else { + 1 + } + } +} + +struct Stack<'a> { + frames: VecDeque>>>, + current_frame: Option>>>, +} + +pub fn exec(node: &Node) -> Result { + //println!("Executing node: {:?}", node); + let root = Rc::new(RefCell::new(StackFrame { + node, + values: Vec::new(), + parent: None, + })); + let mut stack = Stack { + frames: VecDeque::from([root.clone()]), + current_frame: Some(root.clone()), + }; + while stack.current_frame.is_some() { + let mut binding = &mut stack.current_frame.clone().unwrap(); + let mut frame = binding.borrow_mut(); + let count = frame.values.len(); + let node_count = frame.node.children_count(); + + println!("At node: {:?}, count: {}, node_count: {} depth: {}", frame.node.op, count, node_count, frame.get_depth()); + + let ret = compute(&frame.node, &frame.values)?; + if let Some(value) = ret { + stack.frames.pop_back(); + if frame.parent.is_none() { + println!("Final result: {:?}", value); + return Ok(value); + } + stack.current_frame = frame.parent.clone(); + frame.parent.clone().unwrap().borrow_mut().values.push(value); + } else { + let next_frame = Rc::new(RefCell::new(StackFrame { + node: frame.node.get_child(count), + values: Vec::new(), + parent: stack.current_frame.clone(), + })); + stack.frames.push_back(next_frame.clone()); + stack.current_frame = Some(next_frame); + } + } + + Ok(VType::Undefined) +} + +fn compute(node: &Node, values: &Vec) -> Result, RuntimeError> { + let node_count = node.children_count(); + + match node.op { + Op::Add => { + if values.len() != 2 { return Ok(None) } + match (&values[0], &values[1]) { + (VType::Number(a), VType::Number(b)) => Ok(Some(VType::Number(a + b))), + (VType::String(a), VType::String(b)) => Ok(Some(VType::String(format!("{}{}", a, b)))), + _ => Err(RuntimeError::new("Add operation requires both operands to be numbers or strings", None)), + } + }, + Op::Mod => { + if values.len() < 2 { return Ok(None) } + match (&values[0], &values[1]) { + (VType::Number(a), VType::Number(b)) => { + if *b == 0 { + Err(RuntimeError::new("Division by zero in modulus operation", None)) + } else { + Ok(Some(VType::Number(a % b))) + } + }, + _ => Err(RuntimeError::new("Modulus operation requires both operands to be numbers", None)), + } + }, + Op::Sub => { + if values.len() < 2 { return Ok(None) } + match (&values[0], &values[1]) { + (VType::Number(a), VType::Number(b)) => Ok(Some(VType::Number(a - b))), + _ => Err(RuntimeError::new("Sub operation requires both operands to be numbers", None)), + } + }, + Op::Mul => { + if values.len() < 2 { return Ok(None) } + match (&values[0], &values[1]) { + (VType::Number(a), VType::Number(b)) => Ok(Some(VType::Number(a * b))), + _ => Err(RuntimeError::new("Mul operation requires both operands to be numbers", None)), + } + }, + Op::Div => { + if values.len() < 2 { return Ok(None) } + match (&values[0], &values[1]) { + (VType::Number(a), VType::Number(b)) => { + if *b == 0 { + Err(RuntimeError::new("Division by zero", None)) + } else { + Ok(Some(VType::Number(a / b))) + } + }, + _ => Err(RuntimeError::new("Div operation requires both operands to be numbers", None)), + } + }, + Op::Scope => { + if values.len() < node_count { + return Ok(None); + } + Ok(Some(values.last().unwrap_or(&VType::Undefined).clone())) + } + Op::Paren => { + if values.len() < node_count { + return Ok(None); + } + Ok(Some(values.last().unwrap_or(&VType::Undefined).clone())) + } + Op::Args => { + if values.len() < node_count { + return Ok(None); + } + Ok(Some(VType::Args(values.clone()))) + }, + Op::Branch => todo!("Branch not implemented"), + Op::Loop => todo!("Loop not implemented"), + Op::Value => { + if let Some(val) = &node.value { + return Ok(Some(val.clone().into())); + } + Ok(Some(VType::Undefined)) + }, + Op::Variable => todo!("Variable not implemented"), + Op::Assign => todo!("Assign not implemented"), + Op::Call => todo!("Call not implemented"), + Op::While => todo!("While not implemented"), + Op::DefineFunc => todo!("DefineFunc not implemented"), + Op::DefineVar => todo!("DefineVar not implemented"), + Op::Return => todo!("Return not implemented"), + Op::Eq => { + if values.len() != 2 { return Ok(None) } + Ok(Some(VType::Bool(values.len() == 2 && values[0] == values[1]))) + } + Op::Neq => { + if values.len() != 2 { return Ok(None) } + Ok(Some(VType::Bool(values.len() == 2 && values[0] != values[1]))) + } + Op::Leq => todo!("Leq not implemented"), + Op::Lt => todo!("Lt not implemented"), + Op::Geq => todo!("Geq not implemented"), + Op::Gt => todo!("Gt not implemented"), + Op::Not => todo!("Not not implemented"), + Op::And => { + if values.len() < 1 { return Ok(None) } + else if values.len() == 1 { + if values[0] == VType::Bool(false) { + return Ok(Some(VType::Bool(false))); + }else { + return Ok(None); + } + } else { + return Ok(Some(VType::Bool(values[0] == VType::Bool(true) && values[1] == VType::Bool(true)))); + } + } + Op::Or => todo!("Or not implemented"), + Op::Access => todo!("Access not implemented"), + } +} + + +pub fn interpret(node: Node) -> Result { + + exec(&node).unwrap().unwrap_number() +} + diff --git a/crates/interpreter2/src/types.rs b/crates/interpreter2/src/types.rs new file mode 100644 index 0000000..c4a38f2 --- /dev/null +++ b/crates/interpreter2/src/types.rs @@ -0,0 +1,43 @@ +use std::fmt::Debug; + +trait Value: Debug + Send + Sync { + fn type_name(&self) -> &'static str; + fn as_any(&self) -> &dyn std::any::Any; +} + +trait Operator: Send + Sync { + fn name(&self) -> &'static str; + fn left_type(&self) -> &'static str; + fn right_type(&self) -> &'static str; + fn apply(&self, left: &dyn Value, right: &dyn Value) -> Result, String>; +} + + +impl Value for i32 { + fn type_name(&self) -> &'static str { + "i32" + } + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +struct OpAdd; +impl Operator for OpAdd { + fn name(&self) -> &'static str { + "+" + } + fn left_type(&self) -> &'static str { + "i32" + } + fn right_type(&self) -> &'static str { + "i32" + } + fn apply(&self, left: &dyn Value, right: &dyn Value) -> Result, String> { + if let (Some(l), Some(r)) = (left.as_any().downcast_ref::(), right.as_any().downcast_ref::()) { + Ok(Box::new(l + r)) + } else { + Err("Invalid types for Add".to_string()) + } + } +} diff --git a/crates/lexer/src/token.rs b/crates/lexer/src/token.rs index 254e467..39a6cf0 100644 --- a/crates/lexer/src/token.rs +++ b/crates/lexer/src/token.rs @@ -38,6 +38,8 @@ pub enum Token { Let(Location), And(Location), Or(Location), + None(Location), + Dot(Location), } impl Display for Token { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -77,6 +79,8 @@ impl Display for Token { Token::Let(pos) => write!(f, "Let {}", pos), Token::And(pos) => write!(f, "And {}", pos), Token::Or(pos) => write!(f, "And {}", pos), + Token::None(pos) => write!(f, "None {}", pos), + Token::Dot(pos) => write!(f, "Access {}", pos), } } } @@ -119,6 +123,8 @@ impl Token { Token::Let(pos) => *pos, Token::And(pos) => *pos, Token::Or(pos) => *pos, + Token::None(pos) => *pos, + Token::Dot(pos) => *pos, } } @@ -136,7 +142,8 @@ impl Token { | Token::Gt(..) | Token::Geq(..) | Token::And(..) - | Token::Or(..)) + | Token::Or(..) + | Token::Dot(..)) } pub fn is_modifier(&self) -> bool { matches!(self, Token::Not(..)) @@ -155,6 +162,7 @@ impl Token { pub fn is_noun(&self) -> bool { matches!(self, Token::Number(..) | Token::Id(..) + | Token::None(..) | Token::String(..)) } } diff --git a/crates/lexer/src/tokenizer.rs b/crates/lexer/src/tokenizer.rs index 64548e7..6e0fe84 100644 --- a/crates/lexer/src/tokenizer.rs +++ b/crates/lexer/src/tokenizer.rs @@ -70,6 +70,7 @@ impl<'a> Tokenizer<'a> { }, '+' => Ok(Token::Plus(self.get_location(1))), '-' => Ok(Token::Minus(self.get_location(1))), + '.' => Ok(Token::Dot(self.get_location(1))), '=' => { if self.accept("==") { return Ok(Token::Eq(p.set_range(2))); @@ -106,10 +107,10 @@ impl<'a> Tokenizer<'a> { let (size, num) = string(&mut self.it)?; Ok(Token::String(p.set_range(size), num)) }, - '\n' | ' ' => { + c if self.is_whitespace(c) => { self.ignore_white_spaces(); self.get_next() - } + }, _ => Err(PError::new(p.to_point(), "Unexpected character")) } } @@ -125,7 +126,7 @@ impl<'a> Tokenizer<'a> { fn ignore_white_spaces(&mut self) { while let Some((_, c)) = self.it.clone().next() { - if c == ' ' || c == '\n' { + if self.is_whitespace(c) { self.it.next(); } else { break; @@ -151,6 +152,10 @@ impl<'a> Tokenizer<'a> { true } + fn is_whitespace(&self, c: char) -> bool { + c == ' ' || c == '\n' || c == '\t' + } + } impl<'a> Iterator for Tokenizer<'a> { diff --git a/crates/parser/src/detail/node.rs b/crates/parser/src/detail/node.rs index a10ffa2..a3d3d67 100644 --- a/crates/parser/src/detail/node.rs +++ b/crates/parser/src/detail/node.rs @@ -1,5 +1,5 @@ use super::value::Value; -use std::{fmt::Display, collections::HashMap}; +use std::fmt::Display; use lexer::Location; #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -31,38 +31,40 @@ pub enum Op { Gt, And, Or, + Access } impl Op { pub fn priority(&self) -> u32 { match self { - Op::Scope => 0, - Op::DefineVar => 7, - Op::Return => 7, - Op::Branch => 7, - Op::Loop => 7, - Op::While => 7, - Op::Assign=> 6, - Op::And => 6, - Op::Or => 6, - Op::Lt=> 5, - Op::Leq=> 5, - Op::Gt=> 5, - Op::Geq=> 5, - Op::Neq=> 5, - Op::Eq=> 5, - Op::Add => 4, - Op::Sub => 4, - Op::Mul => 3, - Op::Div => 3, - Op::Mod => 2, - Op::Call => 1, - Op::Args => 1, - Op::Not=> 1, + Op::DefineVar => 8, + Op::Return => 8, + Op::Branch => 8, + Op::Loop => 8, + Op::While => 8, + Op::Assign=> 7, + Op::And => 7, + Op::Or => 7, + Op::Lt=> 6, + Op::Leq=> 6, + Op::Gt=> 6, + Op::Geq=> 6, + Op::Neq=> 6, + Op::Eq=> 6, + Op::Add => 5, + Op::Sub => 5, + Op::Mul => 4, + Op::Div => 4, + Op::Mod => 3, + Op::Call => 2, + Op::Args => 2, + Op::Not=> 2, + Op::Access => 1, Op::DefineFunc=> 0, Op::Value => 0, Op::Variable=> 0, Op::Paren => 0, + Op::Scope => 0, } } } @@ -97,6 +99,7 @@ impl Display for Op { Op::DefineVar => write!(f, "let"), Op::And => write!(f, "&&"), Op::Or => write!(f, "||"), + Op::Access => write!(f, "."), } } } @@ -114,10 +117,10 @@ impl Display for OptionalNode<'_> { } } +pub struct NodeChildNotExist; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Node { - pub defs: HashMap, pub op: Op, pub location: Location, pub value: Option, @@ -131,12 +134,18 @@ impl Node { op, location, value: None, - defs: HashMap::new(), id: None, children: Vec::new(), } } + pub fn children_count(&self) -> usize { + self.children.len() + } + pub fn get_child(&self, idx: usize) -> &Node { + self.children.get(idx).unwrap() + } + pub fn set_value(mut self, value: Value) -> Self { if Op::Value != self.op { panic!("Cannot set value on non-value node"); @@ -213,7 +222,7 @@ impl Node { } }, Op::Add | Op::Sub | Op::Mul | Op::Div | Op::Eq | Op::Neq - | Op::Mod | Op::Geq | Op::Gt | Op::Leq | Op::Lt | Op::And | Op::Or => { + | Op::Mod | Op::Geq | Op::Gt | Op::Leq | Op::Lt | Op::And | Op::Or | Op::Access => { if let Some(ref mut right) = self.right_mut() { if node.priority() < right.priority() { //println!("right: {} -> {}", right, node); @@ -302,8 +311,11 @@ impl Display for Node { Op::Assign=> { write!(f,"({} = {})", OptionalNode(self.children.get(0)), OptionalNode(self.children.get(1)))?; }, + Op::Access => { + write!(f,"({}.{})", OptionalNode(self.children.get(0)), OptionalNode(self.children.get(1)))?; + }, Op::Value => {write!(f, "{}", self.value.as_ref().unwrap())?;}, - Op::Variable => {write!(f, "{}", self.id.as_ref().unwrap())?;}, + Op::Variable => {write!(f, "{}", self.id.as_ref().unwrap_or(&"_anonymous_".to_string()))?;}, Op::Scope => { write!(f,"{{")?; for (idx, node) in self.children.iter().enumerate() { diff --git a/crates/parser/src/expr.rs b/crates/parser/src/expr.rs index 804211d..1630261 100644 --- a/crates/parser/src/expr.rs +++ b/crates/parser/src/expr.rs @@ -36,6 +36,7 @@ impl Expression { Token::Geq(loc) => Node::new(Op::Geq, *loc), Token::And(loc) => Node::new(Op::And, *loc), Token::Or(loc) => Node::new(Op::Or, *loc), + Token::Dot(loc) => Node::new(Op::Access, *loc), _ => { panic!("Unexpected token to build expression: {}", token); } @@ -108,7 +109,7 @@ impl Parser for Expression { tree.add(block); return Ok((tree, tok)); } else { - return Err(PError::new(Location::Eof, format!("Unexpected end of file, semicolon not found {}", token.get_location()).as_str())); + return Err(PError::new(token.get_location(), format!("Unexpected end of file, semicolon not found {}", token.get_location()).as_str())); } }, _ => { @@ -130,7 +131,11 @@ impl Parser for Expression { } }, ExpressionState::Op => { - if token.is_operator() { + if let Token::Dot(..) = token { + let node = Expression::node_from(token); + tree.add(node); + state = ExpressionState::Noun; + } else if token.is_operator() { let node = Expression::node_from(token); tree.add(node); state = ExpressionState::Noun; diff --git a/crates/parser/src/func.rs b/crates/parser/src/func.rs index de6c0fc..d2726ce 100644 --- a/crates/parser/src/func.rs +++ b/crates/parser/src/func.rs @@ -11,6 +11,7 @@ impl Func{ fn node_from(token: &Token) -> Node{ match token { Token::Id(loc, id) => Node::new(Op::Variable, *loc).set_id(id.clone()), + Token::None(loc) => Node::new(Op::Variable, *loc), _ => { panic!("Unexpected token in function builder: {}", token); } @@ -20,8 +21,13 @@ impl Func{ impl Parser for Func { fn consume(token: &Token, iter: &mut Iter) -> Result<(Node, Option), PError> { let mut tree = Node::new(Op::DefineFunc, token.get_location()); - let name = accept!(iter, Id); - tree.add(Func::node_from(name)); + + if let Some(Token::LParen(loc)) = iter.clone().next() { + tree.add(Func::node_from(&Token::None(loc.clone()))); + } else { + let name = accept!(iter, Id); + tree.add(Func::node_from(name)); + } accept!(iter, LParen); let (params, tok) = Params::consume(token, iter)?; tree.add(params); diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 2d672d6..cef0c75 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -67,6 +67,8 @@ mod tests { test_ast_expr!(ast_number, "{1}", [Number(1)]); test_ast_expr!(ast_op_add, "{(1 + 2)}", [Number(1), Plus(), Number(2)]); + test_ast_expr!(ast_access, "{(a.b)}", [Id("a".to_string()), Dot(), Id("b".to_string())]); + test_ast_expr!(ast_access_complex, "{((a.b) + (x.d))}", [Id("a".to_string()), Dot(), Id("b".to_string()), Plus(), Id("x".to_string()), Dot(), Id("d".to_string())]); test_ast_expr!(ast_op_sub, "{(1 - 2)}", [Number(1), Minus(), Number(2)]); test_ast_expr!(ast_op_mul, "{(1 * 2)}", [Number(1), Star(), Number(2)]); test_ast_expr!(ast_op_div, "{(1 / 2)}", [Number(1), Slash(), Number(2)]); diff --git a/crates/parser/src/object.rs b/crates/parser/src/object.rs new file mode 100644 index 0000000..b9014cd --- /dev/null +++ b/crates/parser/src/object.rs @@ -0,0 +1,57 @@ +use std::slice::Iter; +use lexer::{Location, PError, Token}; +use super::{Op, Expression, Node}; +use super::Parser; + +pub struct Object; +use crate::{accept}; + +impl Parser for Object { + fn consume(token: &Token, iter: &mut Iter) -> Result<(Node, Option), PError> { + let mut tree = Node::new(Op::Object, token.get_location()); + loop { + let Token::Id(loc, id) = &token else { + return Err(PError::new(token.get_location(), "Expected identifier for object name")); + }; + let mut def = Node::new(Op::DefineVar, *loc).set_id(id.clone()); + accept!(iter, Colon); + let mut assign = Node::new(Op::Assign, token.get_location()); + assign.add(Node::new(Op::Variable, token.get_location()).set_id(id.clone())); + let (exp, tok) = Expression::consume(token, iter)?; + + assign.add(exp); + def.add(assign); + tree.add(def); + + let Some(t) = tok else { + return Err(PError::new(Location::Eof, format!("Unexpected EOF").as_str())) + }; + match t { + Token::RBrace(..) => { + return Ok((tree, Some(t))); + }, + Token::Comma(..) => { + continue; + }, + _ => Err(PError::new(t.get_location(), format!("Unexpected token, missed comma?").as_str()))?, + } + } + } +} + +impl Object { + pub fn detect(token: &Token, iter: &mut Iter) -> bool { + let Token::Id(_, _) = &token else { + return false; + }; + let token = iter.next(); + let Some(token) = token else { + return false; + }; + if !matches!(token, Token::Colon(..)) { + return false; + } + return true; + } +} + diff --git a/crates/vm/src/vm.rs b/crates/vm/src/vm.rs index 9260bd9..c075c71 100644 --- a/crates/vm/src/vm.rs +++ b/crates/vm/src/vm.rs @@ -1,7 +1,7 @@ use std::fmt::Display; use lexer::PError; -use crate::{OpCode, Instr, Instrs}; +use crate::Instr; macro_rules! parse_instr { diff --git a/examples/shor.lum b/examples/shor.lum index 77800a7..3b204be 100644 --- a/examples/shor.lum +++ b/examples/shor.lum @@ -30,19 +30,6 @@ fn shor(x) { }; '' }; - -fn pow_mod(a, e, m) { - res = 1; - base = a % m; - while (e > 0) { - if (e % 2 == 1) { res = (res * base) % m; }; - base = (base * base) % m; - e = e / 2; - }; - res -}; - - x = 21; y = 49; print("gdc("+x+","+y+") = " + gdc(x,y)); diff --git a/test.lum b/test.lum index 5389a5a..91572a8 100644 --- a/test.lum +++ b/test.lum @@ -1,7 +1,7 @@ // Euclid GCD fn gcd(a, b) { while (b != 0) { - t = b; + let t = b; b = a % b; a = t; }; @@ -10,8 +10,8 @@ fn gcd(a, b) { // fast (a^e) mod m fn pow_mod(a, e, m) { - res = 1; - base = a % m; + let res = 1; + let base = a % m; while (e > 0) { if (e % 2 == 1) { res = (res * base) % m; }; base = (base * base) % m; @@ -22,8 +22,8 @@ fn pow_mod(a, e, m) { // multiplicative order of a mod n (assumes gcd(a,n)==1) fn findOrder(a, n) { - y = 1; - t = pow_mod(a, y, n); + let y = 1; + let t = pow_mod(a, y, n); while (t != 1) { y = y + 1; t = pow_mod(a, y, n); @@ -35,31 +35,32 @@ fn findOrder(a, n) { fn shor(x) { if (x % 2 == 0) { return "[ " + 2 + ", " + (x/2) + " ]"; }; - a = 2; + let a = 2; while (a < x) { - d = gcd(a, x); + let d = gcd(a, x); if (d > 1 && d < x) { return "[ " + d + ", " + (x/d) + " ]"; }; if (d == 1) { - r = findOrder(a, x); + let r = findOrder(a, x); if (r % 2 == 0) { - ar2 = pow_mod(a, r/2, x); // a^(r/2) mod x + let ar2 = pow_mod(a, r/2, x); // a^(r/2) mod x if (ar2 != x - 1) { // not -1 mod x - p = gcd(ar2 + 1, x); - q = gcd(ar2 - 1, x); - if (p*q == x && p > 1 && q > 1) { return "[ " + p + ", " + q + " ]"; }; + let p = gcd(ar2 + 1, x); + let q = gcd(ar2 - 1, x); + if (p*q == x && p > 1 && q > 1) { + return "[ " + p + ", " + q + " ]"; + }; }; }; }; a = a + 1; }; - "" }; // smoke tests -x = 21; y = 49; +let x = 21; +let y = 49; print("gcd("+x+","+y+") = " + gcd(x,y)); print("shor(15) = " + shor(15)); assert(gcd(15, 21) == 3); assert(shor(15) == "[ 5, 3 ]"); -0 diff --git a/test_fact.lum b/test_fact.lum new file mode 100644 index 0000000..e0c97fb --- /dev/null +++ b/test_fact.lum @@ -0,0 +1,2 @@ +fn fact(x) if (x < 1) { 1 } else { x * fact(x - 1) }; +fact(5) \ No newline at end of file diff --git a/tests/compute2_test.rs b/tests/compute2_test.rs new file mode 100644 index 0000000..1ffe637 --- /dev/null +++ b/tests/compute2_test.rs @@ -0,0 +1,43 @@ +use lexer::{Token,PError,Tokenizer}; + +fn parse(text: &str) -> Result { + let tokens:Vec = Tokenizer::new(text).collect::, PError>>()?; + let mut iter = tokens.iter(); + parser::parse(&mut iter) +} + +#[macro_export] +macro_rules! test_compute{ + ( $name:ident, $i:expr, $o:expr ) => { + #[test] + fn $name() { + let text = $i; + let node = parse(text).unwrap_or_else(|e| { + panic!("\nError:\n{}\n", e.format_error($i, "file.lum", false)); + }); + //println!("{}", node); + let value = interpreter2::interpret(node).unwrap_or_else(|e| { + panic!("\nError:\n{}\n", e) + }); + assert_eq!(value, $o); + } + }; +} + + +test_compute!(simple, "1 + 2", 3); +test_compute!(simple2, "1 + 2 * 5 - 2 / 1", 14); +test_compute!(with_braces, "2 * (3 + 4) ", 14); +test_compute!(with_variable, "let asd = 4; 2 * (3 + asd) ", 14); +test_compute!(with_two_variables, "let qwe=3; let asd = 4; 2 * (qwe + asd) ", 14); +test_compute!(conditional_positive, "if(1){5}else{7}", 5); +test_compute!(conditional_negative, "if(0){5}else{7}", 7); +//test_compute!(strings_multiplication, "let qwe='oko'; qwe*3", "okookooko"); +test_compute!(while_loop, "let a=0; while(a!=2) a = a + 1; a", 2); +test_compute!(while_loop_return, "let a=0; while(a<5) if (a == 3) { return a } else { a = a + 1; a}", 3); +test_compute!(functions_and_variables, "fn qwe(x) x*2; let asd = 4; 2 * (qwe(5) + asd)", 28); +test_compute!(recursive_functions, "fn fact(x) if (x <= 1) { 1 } else { x * fact(x - 1) }; fact(5)", 120); +test_compute!(lambda_functions, "let qwe = fn(x) x*2; qwe(14)", 28); +//test_compute!(goal_recursive_fib, "fn fib(x) if (x == 1 || x == 2) { 1 } else { fib(x - 1) + fib(x - 2) }; fib(10)", 55); +//test_compute_error!(err_goal, "123 + 'text'", "123 + 'text'\n ^ \nIncompatible types"); + diff --git a/tests/compute_test.rs b/tests/compute_test.rs index 2a1e082..33d4299 100644 --- a/tests/compute_test.rs +++ b/tests/compute_test.rs @@ -41,11 +41,16 @@ macro_rules! test_compute_error{ test_compute!(simple, "1 + 2", 3); test_compute!(with_braces, "2 * (3 + 4) ", 14); -test_compute!(with_variable, "asd = 4; 2 * (3 + asd) ", 14); -test_compute!(with_two_variables, "qwe=3; asd = 4; 2 * (qwe + asd) ", 14); +test_compute!(with_variable, "let asd = 4; 2 * (3 + asd) ", 14); +test_compute!(with_two_variables, "let qwe=3; let asd = 4; 2 * (qwe + asd) ", 14); test_compute!(conditional_positive, "if(1){5}else{7}", 5); test_compute!(conditional_negative, "if(0){5}else{7}", 7); -test_compute!(strings_multiplication, "qwe='oko'; qwe*3", "okookooko"); -test_compute!(while_loop, "a=0; while(a!=2) a = a + 1; a", 2); -//test_compute!(goal, "fn qwe(x) x*2; asd = 4; 2 * (qwe(5) + asd) ", 28); +test_compute!(strings_multiplication, "let qwe='oko'; qwe*3", "okookooko"); +test_compute!(while_loop, "let a=0; while(a!=2) a = a + 1; a", 2); +test_compute!(while_loop_return, "let a=0; while(a<5) if (a == 3) { return a } else { a = a + 1; a}", 3); +test_compute!(functions_and_variables, "fn qwe(x) x*2; let asd = 4; 2 * (qwe(5) + asd)", 28); +test_compute!(recursive_functions, "fn fact(x) if (x <= 1) { 1 } else { x * fact(x - 1) }; fact(5)", 120); +test_compute!(lambda_functions, "let qwe = fn(x) x*2; qwe(14)", 28); +//test_compute!(goal_recursive_fib, "fn fib(x) if (x == 1 || x == 2) { 1 } else { fib(x - 1) + fib(x - 2) }; fib(10)", 55); //test_compute_error!(err_goal, "123 + 'text'", "123 + 'text'\n ^ \nIncompatible types"); + diff --git a/tests/parsing_test.rs b/tests/parsing_test.rs index 6e85304..ce95839 100644 --- a/tests/parsing_test.rs +++ b/tests/parsing_test.rs @@ -141,8 +141,20 @@ mod expressions { test_parser!(expr_while_loop_complex, "a=0; while(a == 2) a = a + 1; a", "{(a = 0);while ((a == 2)) (a = (a + 1));a}"); - + test_parser!(expr_lambda_function, + "let a=fn(x) x;", + "{let a; (a = fn _anonymous_x x)}"); + test_parser!(expr_property_access, + "a.b", "{(a.b)}"); + test_parser!(expr_property_access_plus, + "a.b+x.d", "{((a.b) + (x.d))}"); + test_parser!(expr_property_access_deep, + "a.b.x.d", "{(((a.b).x).d)}"); /* + test_parser!(expr_lambda_function_eof, + "let a=fn(x) x", + "{let a; (a = fn _anonymous_x x)}"); + test_parser_error!(err_expr_just_braces_instead_of_operand, "123 + ()", "123 + ()\n ^\nInvalid expression, expected ID or Number"); diff --git a/tree-sitter-loom/package-lock.json b/tree-sitter-loom/package-lock.json new file mode 100644 index 0000000..ee00647 --- /dev/null +++ b/tree-sitter-loom/package-lock.json @@ -0,0 +1,36 @@ +{ + "name": "tree-sitter-loom", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "tree-sitter-loom", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "nan": "2.18.0" + }, + "devDependencies": { + "tree-sitter-cli": "0.20.8" + } + }, + "node_modules/nan": { + "version": "2.18.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.18.0.tgz", + "integrity": "sha512-W7tfG7vMOGtD30sHoZSSc/JVYiyDPEyQVso/Zz+/uQd0B0L46gtC+pHha5FFMRpil6fm/AoEcRWyOVi4+E/f8w==", + "license": "MIT" + }, + "node_modules/tree-sitter-cli": { + "version": "0.20.8", + "resolved": "https://registry.npmjs.org/tree-sitter-cli/-/tree-sitter-cli-0.20.8.tgz", + "integrity": "sha512-XjTcS3wdTy/2cc/ptMLc/WRyOLECRYcMTrSWyhZnj1oGSOWbHLTklgsgRICU3cPfb0vy+oZCC33M43u6R1HSCA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "tree-sitter": "cli.js" + } + } + } +} diff --git a/vars.lum b/vars.lum index 4cf8551..16eab11 100644 --- a/vars.lum +++ b/vars.lum @@ -1,10 +1,4 @@ - - - -{ -let a = 5; -}; - -print(a); - -a +1 == 1 && 2 == 4; +let a = "asd"; +a.test(); +5