From a888c09bd54de77fb2004754a0e14ce14a906232 Mon Sep 17 00:00:00 2001 From: Freya Murphy Date: Fri, 23 Feb 2024 11:32:47 -0500 Subject: [PATCH] more changes --- Cargo.lock | 27 +++++++++++++- Cargo.toml | 2 +- a.mat | 5 --- matrix-bin/Cargo.toml | 2 + matrix-bin/src/main.rs | 14 +++---- matrix-bin/src/repl.rs | 26 +++++++++++-- matrix-macros/Cargo.toml | 12 ++++++ matrix-macros/src/lib.rs | 52 ++++++++++++++++++++++++++ matrix-stdlib/Cargo.toml | 2 + matrix-stdlib/src/io.rs | 36 ++++++++++++++++++ matrix-stdlib/src/lib.rs | 15 ++------ matrix/src/ast.rs | 11 ++++-- matrix/src/chunk.rs | 25 +++++++++++-- matrix/src/compiler.rs | 40 +++++++++++++++----- matrix/src/gc.rs | 4 +- matrix/src/lex.rs | 12 +++++- matrix/src/parse.rs | 57 +++++++++++++++++++++------- matrix/src/value.rs | 52 +++++++++++++++----------- matrix/src/vm.rs | 80 ++++++++++++++++++++++++++++++++-------- syntax_thing | 63 ------------------------------- 20 files changed, 377 insertions(+), 160 deletions(-) delete mode 100644 a.mat create mode 100644 matrix-macros/Cargo.toml create mode 100644 matrix-macros/src/lib.rs create mode 100644 matrix-stdlib/src/io.rs delete mode 100644 syntax_thing diff --git a/Cargo.lock b/Cargo.lock index 45a0c7e..8a03761 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,7 +114,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.48", ] [[package]] @@ -220,12 +220,26 @@ version = "0.1.0" dependencies = [ "clap", "matrix", + "matrix-stdlib", "rustyline", ] +[[package]] +name = "matrix-macros" +version = "0.1.0" +dependencies = [ + "matrix", + "quote", + "syn 1.0.109", +] + [[package]] name = "matrix-stdlib" version = "0.1.0" +dependencies = [ + "matrix", + "matrix-macros", +] [[package]] name = "memchr" @@ -407,6 +421,17 @@ version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.48" diff --git a/Cargo.toml b/Cargo.toml index d1bda86..7e1bcac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] resolver = "2" -members = [ "matrix", "matrix-bin" , "matrix-stdlib"] +members = [ "matrix", "matrix-bin" , "matrix-macros", "matrix-stdlib"] diff --git a/a.mat b/a.mat deleted file mode 100644 index e689317..0000000 --- a/a.mat +++ /dev/null @@ -1,5 +0,0 @@ -a = 0; - -while a < 1000000000 { a = a + 1 }; - - diff --git a/matrix-bin/Cargo.toml b/matrix-bin/Cargo.toml index 029923b..c4b4d5a 100644 --- a/matrix-bin/Cargo.toml +++ b/matrix-bin/Cargo.toml @@ -9,5 +9,7 @@ path = "src/main.rs" [dependencies] clap = { version = "4", features = [ "derive" ] } +ctrlc = "3" matrix = { path = "../matrix" } +matrix-stdlib = { path = "../matrix-stdlib" } rustyline = "13" diff --git a/matrix-bin/src/main.rs b/matrix-bin/src/main.rs index 72972c6..909ee77 100644 --- a/matrix-bin/src/main.rs +++ b/matrix-bin/src/main.rs @@ -1,6 +1,6 @@ use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs}; use clap::Parser as ArgParser; -use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}}; +use matrix::{compiler::{Compiler, CompilerBuilder}, vm::Vm, parse::{Parser, ParserBuilder}, value::Value}; use repl::Repl; mod repl; @@ -50,24 +50,24 @@ impl<'a> State<'a> { let parser = ParserBuilder::new() .optimize(!args.disable_optimizations) .build(); - let vm = Vm::new(); + let mut vm = Vm::new(); let compiler = CompilerBuilder::new() .repl(repl) .debug(args.debug) .names(vm.names()) + .globals(vm.global_names()) .build(); + matrix_stdlib::load(&mut vm); + (Self { parser, vm, compiler, repl }, file) } - pub fn execute(&mut self, code: String) -> matrix::Result<()> { + pub fn execute(&mut self, code: String) -> matrix::Result { let ast = self.parser.parse(code)?; let fun = self.compiler.compile(&ast)?; let val = self.vm.run(fun)?; - if self.repl { - println!("{val}"); - } - Ok(()) + Ok(val) } } diff --git a/matrix-bin/src/repl.rs b/matrix-bin/src/repl.rs index 81fd289..1b5addc 100644 --- a/matrix-bin/src/repl.rs +++ b/matrix-bin/src/repl.rs @@ -1,3 +1,8 @@ +use std::{io::Write, sync::atomic::Ordering}; + +use matrix::{value::Value, vm::Interupt}; +use rustyline::Config; + use crate::State; pub struct Repl<'a> { @@ -11,14 +16,29 @@ impl<'a> Repl<'a> { } pub fn run(&mut self) { - let mut rl = rustyline::DefaultEditor::new().unwrap(); + + let interupt = self.state.vm.interupt(); + ctrlc::set_handler(move || { + interupt.store(Interupt::KeyboardInterupt as usize, Ordering::SeqCst); + }).unwrap(); + + let config = Config::builder() + .check_cursor_position(true) + .build(); + let mut rl = rustyline::DefaultEditor::with_config(config).unwrap(); loop { let Ok(line) = rl.readline(">> ") else { break; }; - if let Err(err) = self.state.execute(line) { - crate::error(err); + match self.state.execute(line) { + Err(err) => crate::error(err), + Ok(val) => { + if val != Value::Nil { + println!("{val}"); + } + } } + let _ = std::io::stdout().flush(); } } diff --git a/matrix-macros/Cargo.toml b/matrix-macros/Cargo.toml new file mode 100644 index 0000000..df84237 --- /dev/null +++ b/matrix-macros/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "matrix-macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "1", features = ["full"] } +quote = "1" +matrix = { path = "../matrix" } diff --git a/matrix-macros/src/lib.rs b/matrix-macros/src/lib.rs new file mode 100644 index 0000000..8099fd6 --- /dev/null +++ b/matrix-macros/src/lib.rs @@ -0,0 +1,52 @@ +use proc_macro::TokenStream; +use syn::{ItemFn, parse::Parse, Token, LitBool, LitInt}; +use quote::quote; + +struct NativeFuncParams { + arity: LitInt, + variadic: LitBool, +} + +impl Parse for NativeFuncParams { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let arity = input.parse()?; + input.parse::()?; + let variadic = input.parse()?; + Ok(Self { arity , variadic }) + } +} + +#[proc_macro_attribute] +pub fn native_func(input: TokenStream, annotated_item: TokenStream) -> TokenStream { + let itemfn: ItemFn = syn::parse(annotated_item).unwrap(); + + let input: NativeFuncParams = syn::parse(input).unwrap(); + let arity = input.arity; + let variadic = input.variadic; + + let visibility = itemfn.vis; + let block = itemfn.block; + let name = itemfn.sig.ident; + let name_str = name.to_string(); + let inputs = itemfn.sig.inputs; + let output = itemfn.sig.output; + + assert!(itemfn.sig.constness.is_none(), "item must not be const"); + assert!(itemfn.sig.asyncness.is_none(), "item must not be async"); + assert!(itemfn.sig.unsafety.is_none(), "item must not be unsafe"); + assert!(itemfn.sig.abi.is_none(), "item must not contain an ABI specifier"); + assert!(itemfn.sig.variadic.is_none(), "item must not be variadic"); + + let expanded = quote! { + #visibility fn #name() -> ::std::rc::Rc< ::matrix::chunk::Function> { + ::std::rc::Rc::new( ::matrix::chunk::Function { + name: ::std::rc::Rc::from( #name_str ), + arity: #arity, + variadic: #variadic, + fun: ::matrix::chunk::InnerFunction::Native(Box::new(|#inputs| #output #block)) + }) + } + }; + + TokenStream::from(expanded) +} diff --git a/matrix-stdlib/Cargo.toml b/matrix-stdlib/Cargo.toml index 5140bfa..a892c04 100644 --- a/matrix-stdlib/Cargo.toml +++ b/matrix-stdlib/Cargo.toml @@ -6,3 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +matrix = { path = "../matrix" } +matrix-macros = { path = "../matrix-macros" } diff --git a/matrix-stdlib/src/io.rs b/matrix-stdlib/src/io.rs new file mode 100644 index 0000000..288e99e --- /dev/null +++ b/matrix-stdlib/src/io.rs @@ -0,0 +1,36 @@ +use matrix::{value::Value, self, vm::Vm, Result}; +use matrix_macros::native_func; + +#[native_func(1, true)] +fn print(_vm: &mut Vm, args: Vec) -> Result { + let [values] = args.try_into().unwrap(); + if let Value::List(list) = values { + for (i, value) in list.iter().enumerate() { + print!("{}", value.boring_print()); + if i != 0 { + print!(" "); + } + } + } + Ok(Value::Nil) +} + +#[native_func(1, true)] +fn println(_vm: &mut Vm, args: Vec) -> Result { + let [values] = args.try_into().unwrap(); + if let Value::List(list) = values { + for (i, value) in list.iter().enumerate() { + print!("{}", value.boring_print()); + if i != 0 { + print!(" "); + } + } + } + print!("\n"); + Ok(Value::Nil) +} + +pub fn load(vm: &mut Vm) { + vm.load_native_fn(print()); + vm.load_native_fn(println()); +} diff --git a/matrix-stdlib/src/lib.rs b/matrix-stdlib/src/lib.rs index 7d12d9a..312d397 100644 --- a/matrix-stdlib/src/lib.rs +++ b/matrix-stdlib/src/lib.rs @@ -1,14 +1,7 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +use matrix::vm::Vm; -#[cfg(test)] -mod tests { - use super::*; +mod io; - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); - } +pub fn load(compiler: &mut Vm) { + io::load(compiler); } diff --git a/matrix/src/ast.rs b/matrix/src/ast.rs index 19a4d04..68e325e 100644 --- a/matrix/src/ast.rs +++ b/matrix/src/ast.rs @@ -57,7 +57,8 @@ pub enum Expr { Assign(Box, Box), If(Box, Box, Option>), - Function(Rc, Vec>, Box), + Function(Rc, Vec>, Box, bool), + Lambda(Vec>, Box, bool), Loop(Box), While(Box, Box), @@ -279,10 +280,14 @@ pub fn optimize(expr: Expr) -> Result { let block = Box::new(optimize(*block)?); E::Loop(block) }, - E::Function(ident, params, stmt) => { + E::Function(ident, params, stmt, varadic) => { let stmt = Box::new(optimize(*stmt)?); - E::Function(ident, params, stmt) + E::Function(ident, params, stmt, varadic) } + E::Lambda(params, stmt, varadic) => { + let stmt = Box::new(optimize(*stmt)?); + E::Lambda(params, stmt, varadic) + }, E::Let(ident, expr) => { E::Let(ident, Box::new(optimize(*expr)?)) }, diff --git a/matrix/src/chunk.rs b/matrix/src/chunk.rs index 276ffe3..19ff8e6 100644 --- a/matrix/src/chunk.rs +++ b/matrix/src/chunk.rs @@ -1,4 +1,4 @@ -use crate::{value::Value, ast::{UnaryOp, BinaryOp}}; +use crate::{value::Value, ast::{UnaryOp, BinaryOp}, vm::Vm, Result}; use std::{fmt::{Debug, Display}, rc::Rc}; #[derive(Clone)] @@ -36,11 +36,30 @@ impl Display for Chunk { } } -#[derive(Debug, Clone)] pub struct Function { pub name: Rc, pub arity: usize, - pub body: Chunk + pub variadic: bool, + pub fun: InnerFunction +} + +pub enum InnerFunction { + Compiled(Rc), + Native(Box) -> Result>), +} + +impl Debug for Function { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + use InnerFunction as F; + match self.fun { + F::Compiled(_) => { + write!(f, "[Function {}]", self.name) + }, + F::Native(_) => { + write!(f, "[NativeFunction {}]", self.name) + } + } + } } #[derive(Clone, Debug)] diff --git a/matrix/src/compiler.rs b/matrix/src/compiler.rs index 5531d7e..f573437 100644 --- a/matrix/src/compiler.rs +++ b/matrix/src/compiler.rs @@ -1,13 +1,15 @@ use std::{fmt::Display, rc::Rc, cell::RefCell}; -use crate::{ast::Expr, chunk::{Function, Instruction}, chunk::{Chunk, self}, value::Value, Result}; +use crate::{ast::Expr, chunk::{Function, Instruction, InnerFunction}, chunk::{Chunk, self}, value::Value, Result}; use Instruction as I; use Value as V; use Expr as E; +pub type NamesTable = Rc>>>; + pub struct CompilerBuilder<'c> { - globals: Rc>>>, - names: Rc>>>, + globals: NamesTable, + names: NamesTable, repl: bool, debug: bool, name: Rc, @@ -37,12 +39,12 @@ impl<'c> CompilerBuilder<'c> { self } - pub fn globals(mut self, globals: Rc>>>) -> Self { + pub fn globals(mut self, globals: NamesTable) -> Self { self.globals = globals; self } - pub fn names(mut self, names: Rc>>>) -> Self { + pub fn names(mut self, names: NamesTable) -> Self { self.names = names; self } @@ -133,6 +135,8 @@ impl<'c> Compiler<'c> { CompilerBuilder::new() .name(name) .debug(self.debug) + .globals(self.globals.clone()) + .names(self.names.clone()) .repl(false) .parent(self) .build() @@ -269,13 +273,14 @@ impl<'c> Compiler<'c> { self.compile_expr(elseb)?; } }, - E::Function(name, params, body) => { + E::Function(name, params, body, varadic) => { let chunk = self.compile_function(name.clone(), params, body)?; let fun = Value::Function(Rc::new( chunk::Function { name: name.clone(), arity: params.len(), - body: chunk + fun: InnerFunction::Compiled(chunk.into()), + variadic: *varadic } )); self.emit_const(fun); @@ -288,6 +293,19 @@ impl<'c> Compiler<'c> { self.emit(I::CreateLocal); } }, + E::Lambda(params, body, varadic) => { + let name: Rc = Rc::from(""); + let chunk = self.compile_function(name.clone(), params, body)?; + let fun = Value::Function(Rc::new( + chunk::Function { + name: name.clone(), + arity: params.len(), + fun: InnerFunction::Compiled(chunk.into()), + variadic: *varadic + } + )); + self.emit_const(fun); + }, E::Loop(expr) => { let idx = self.cur(); self.loop_top.push((idx as usize, self.scopes.len())); @@ -365,7 +383,9 @@ impl<'c> Compiler<'c> { }, E::Literal(val) => self.compile_value(val), E::Ident(name) => { - if let Some(local) = self.find_local(name) { + if name.as_ref() == "_" { + self.emit(I::Nil); + } else if let Some(local) = self.find_local(name) { self.emit(I::LoadLocal(local.idx as u16)); } else if let Some(global) = self.find_global(name) { self.emit(I::LoadGlobal(global as u16)); @@ -377,7 +397,7 @@ impl<'c> Compiler<'c> { self.compile_expr(rhs)?; self.emit(I::Dup); match lhs.as_ref() { - E::Ident(name) => { + E::Ident(name) if name.as_ref() != "_" => { if let Some(local) = self.find_local(&name) { self.emit(I::StoreLocal(local.idx as u16)); } else if let Some(global) = self.find_global(&name) { @@ -540,7 +560,7 @@ impl<'c> Compiler<'c> { self.chunk = Chunk::new(); self.compile_expr(body)?; self.finish()?; - let fun = Function { name: self.name.clone(), body: self.chunk.clone(), arity: 0 }; + let fun = Function { name: self.name.clone(), fun: InnerFunction::Compiled(self.chunk.clone().into()), arity: 0, variadic: false }; Ok(Rc::new(fun)) } } diff --git a/matrix/src/gc.rs b/matrix/src/gc.rs index 98d3228..0f31aea 100644 --- a/matrix/src/gc.rs +++ b/matrix/src/gc.rs @@ -24,9 +24,9 @@ impl Gc { } impl Gc { - pub fn clone_inside(&mut self) -> Self { + pub fn clone_inside(&self) -> Self { unsafe { - let data = self.ptr.as_mut().data.clone(); + let data = self.ptr.as_ref().data.clone(); Self::new(data) } } diff --git a/matrix/src/lex.rs b/matrix/src/lex.rs index ab0359b..16032f9 100644 --- a/matrix/src/lex.rs +++ b/matrix/src/lex.rs @@ -47,6 +47,8 @@ pub enum Token { Comma, Range, Colon, + Backslash, + Varadic, // math Regex(RegexToken), @@ -443,6 +445,7 @@ impl Lexer { '{' => LeftBrace, '}' => RightBrace, ':' => Colon, + '\\' => Backslash, ';' => SemiColon, '+' => { match next { @@ -627,7 +630,14 @@ impl Lexer { }, '.' => { if next == '.' { - Range + self.next(); + match self.peek() { + '.' => { + self.next(); + Varadic + }, + _ => Range + } } else if next.is_digit(10) { self.lex_number(char)? } else { diff --git a/matrix/src/parse.rs b/matrix/src/parse.rs index 4e1de2c..b505415 100644 --- a/matrix/src/parse.rs +++ b/matrix/src/parse.rs @@ -40,7 +40,7 @@ pub enum Error { UnexpectedToken(Token), ExpectedToken(Token), ExpectedTokenName(&'static str), - MatrixCoDomainError(usize, usize, usize), + MatrixInvDomain(usize, usize, usize), NotAssignable(Expr), ValueError(value::Error), } @@ -53,7 +53,7 @@ impl Display for Error { UnexpectedToken(tok) => write!(f, "Unexpected token: '{tok:?}'"), ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"), ExpectedTokenName(name) => write!(f, "Expected {name} token"), - MatrixCoDomainError(row, should, was) => write!(f, "In col {row} of matrix, codomain was expected to be {should} but was given {was}"), + MatrixInvDomain(row, should, was) => write!(f, "In row {row} of matrix, domain was expected to be {should} but was given {was}"), NotAssignable(expr) => write!(f, "{expr:?} is not assignable"), ValueError(err) => write!(f, "{err}"), } @@ -199,11 +199,11 @@ impl Parser { if parts.len() == 1 { Ok(E::List(parts.pop().unwrap())) } else { - let codomain = parts[0].len(); - let domain = parts.len(); + let codomain = parts.len(); + let domain = parts[0].len(); for (i, part) in parts.iter().enumerate() { if part.len() != codomain { - return Err(Error::MatrixCoDomainError(i, codomain, part.len()).into()) + return Err(Error::MatrixInvDomain(i, domain, part.len()).into()) } } let mut data = Vec::new(); @@ -259,20 +259,29 @@ impl Parser { Ok(expr) } - fn parse_params(&mut self) -> Result>> { + fn parse_params(&mut self) -> Result<(Vec>, bool)> { use T::*; let tok = self.lexer.next_token()?; match tok { - Ident(ident) => return Ok(vec![ident]), + Ident(ident) => { + let params = vec![ident]; + if self.lexer.peek_token()? == T::Varadic { + self.lexer.next_token()?; + return Ok((params, true)) + } else { + return Ok((params, false)) + } + } LeftParen => (), _ => return Err(Error::UnexpectedToken(tok).into()), } let mut params = Vec::new(); + let mut varadic = false; if self.lexer.peek_token()? == T::RightParen { self.lexer.next_token()?; - return Ok(params); + return Ok((params, varadic)); } loop { @@ -280,13 +289,18 @@ impl Parser { params.push(ident); let next = self.lexer.next_token()?; match next { + Varadic => { + varadic = true; + self.force_token(T::RightParen)?; + break; + } Comma => continue, RightParen => break, _ => return Err(Error::UnexpectedToken(next).into()), } } - Ok(params) + Ok((params, varadic)) } fn parse_ident(&mut self) -> Result> { @@ -308,12 +322,23 @@ impl Parser { fn parse_function(&mut self) -> Result { self.force_token(T::Function)?; let ident = self.parse_ident()?; - let params = match self.lexer.peek_token()? { - T::LeftBrace => vec![], + let (params, varadic) = match self.lexer.peek_token()? { + T::LeftBrace => (vec![], false), _ => self.parse_params()?, }; let expr = self.parse_expr()?; - Ok(E::Function(ident, params, Box::new(expr))) + Ok(E::Function(ident, params, Box::new(expr), varadic)) + } + + fn parse_lambda(&mut self) -> Result { + self.force_token(T::Backslash)?; + let (params, varadic) = match self.lexer.peek_token()? { + T::Arrow => (vec![], false), + _ => self.parse_params()?, + }; + self.force_token(T::Arrow)?; + let expr = self.parse_expr()?; + Ok(E::Lambda(params, Box::new(expr), varadic)) } fn parse_do_while(&mut self) -> Result { @@ -387,7 +412,7 @@ impl Parser { match next { T::SemiColon => continue, T::RightBrace => break, - _ => return Err(Error::UnexpectedToken(next).into()) + _ => return Err(Error::ExpectedToken(T::SemiColon).into()) } } if self.lexer.peek_token()? == T::RightBrace { @@ -416,6 +441,7 @@ impl Parser { use T::*; match self.lexer.peek_token()? { Function => self.parse_function(), + Backslash => self.parse_lambda(), Do => self.parse_do_while(), While => self.parse_while(), Let => self.parse_let(), @@ -641,6 +667,11 @@ impl Parser { }; let expr = self.parse_expr()?; block.push(expr); + match self.lexer.next_token()? { + T::Eof => break, + T::SemiColon => continue, + _ => return Err(Error::ExpectedToken(T::SemiColon).into()) + }; } Ok(E::Block(block)) } diff --git a/matrix/src/value.rs b/matrix/src/value.rs index b7a8218..1e75902 100644 --- a/matrix/src/value.rs +++ b/matrix/src/value.rs @@ -171,18 +171,17 @@ impl Value { str }, Matrix(m) => { - let mut str = "[[".to_string(); - for (i, el) in m.2.iter().enumerate() { - if i != 0 { - if (i % m.1) == 0 { - str.push_str(" ; "); - } else { - str.push_str(" "); + let mut str = "\n".to_string(); + for row in m.2.chunks(m.0) { + str.push_str(" "); + for (i, v) in row.iter().enumerate() { + if i != 0 { + str.push(' '); } + str.push_str(&v.boring_print()); } - str.push_str(&el.boring_print()); + str.push('\n'); } - str.push_str("]]"); str }, Table(t) => { @@ -199,7 +198,7 @@ impl Value { str }, Function(fun) => { - format!("[Function: {}]", fun.name) + format!("{fun:?}") } } } @@ -227,18 +226,17 @@ impl Value { str }, Matrix(m) => { - let mut str = "[[".to_string(); - for (i, el) in m.2.iter().enumerate() { - if i != 0 { - if (i % m.1) == 0 { - str.push_str(" ; "); - } else { - str.push_str(" "); + let mut str = "\n".to_string(); + for row in m.2.chunks(m.0) { + str.push_str(" "); + for (i, v) in row.iter().enumerate() { + if i != 0 { + str.push(' '); } + str.push_str(&v.pretty_print()); } - str.push_str(&el.pretty_print()); + str.push('\n'); } - str.push_str("]]"); str }, Table(t) => { @@ -256,7 +254,7 @@ impl Value { }, Function(_) => { format!("\x1b[36m{}\x1b[0m", self.boring_print()) - } + }, } } } @@ -449,6 +447,16 @@ impl Value { (l, r) => Err(Error::Exponent(l, r).into()) } } + + pub fn clone_inside(&self) -> Self { + use Value as V; + match self { + V::List(l) => V::List(l.clone_inside()), + V::Table(t) => V::Table(t.clone_inside()), + V::Matrix(m) => V::Matrix(m.clone_inside()), + _ => self.clone() + } + } } impl Eq for Value {} @@ -523,7 +531,7 @@ impl Value { return Err(Error::IndexOutOfBounds(*i as usize, l.len()).into()) } Ok(l[*i as usize].clone()) - } + }, _ => return Err(self::Error::BadIndex(self.clone(), index.clone()).into()) } } @@ -553,7 +561,7 @@ impl Value { pub fn index(&self, index: &Vec) -> Result { if index.len() == 0 { - Ok(self.clone()) + Ok(self.clone_inside()) } else if index.len() == 1 { self.index_single(&index[0]) } else { diff --git a/matrix/src/vm.rs b/matrix/src/vm.rs index d0729eb..4c6618d 100644 --- a/matrix/src/vm.rs +++ b/matrix/src/vm.rs @@ -1,5 +1,5 @@ use std::{rc::Rc, fmt::{Debug, Display}, usize, ops::{Index, IndexMut}, collections::HashMap, cell::RefCell}; -use crate::{value::{Value, self, ValueMap}, gc::Gc, chunk::{Function, Instruction}, Result}; +use crate::{value::{Value, self, ValueMap}, gc::Gc, chunk::{Function, Instruction, Chunk, InnerFunction}, Result, compiler::NamesTable}; #[derive(Debug)] pub enum Error { @@ -7,6 +7,7 @@ pub enum Error { NotFunction(Value), InvArity(usize, usize, Rc), UndefinedGlobal(Rc), + ExecNative, } impl std::error::Error for Error {} @@ -19,6 +20,7 @@ impl Display for Error { NotFunction(v) => write!(f, "{v:?} is not a function"), InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"), UndefinedGlobal(name) => write!(f, "{name} is not defined"), + ExecNative => write!(f, "cannot execute a native function"), } } } @@ -88,17 +90,20 @@ impl Index for Stack { } struct StackFrame { - fun: Rc, + #[allow(dead_code)] + name: Rc, + body: Rc, ip: usize, bp: usize, } impl StackFrame { - fn new(vm: &Vm, fun: Rc) -> Self { + fn new(vm: &Vm, name: Rc, body: Rc, arity: usize) -> Self { Self { - bp: vm.stack.len() - fun.arity, + name, + body, + bp: vm.stack.len() - arity, ip: 0, - fun, } } } @@ -108,7 +113,8 @@ pub struct Vm { locals: Stack, frames: Vec, globals: HashMap, - names: Rc>>>, + names: NamesTable, + global_names: NamesTable, } impl Vm { @@ -121,10 +127,14 @@ impl Vm { self.stack.pop() } - pub fn names(&self) -> Rc>>> { + pub fn names(&self) -> NamesTable { self.names.clone() } + pub fn global_names(&self) -> NamesTable { + self.global_names.clone() + } + pub fn new() -> Self { Self { stack: Stack::new(), @@ -132,14 +142,33 @@ impl Vm { frames: Vec::new(), globals: HashMap::new(), names: Rc::new(RefCell::new(Vec::new())), + global_names: Rc::new(RefCell::new(Vec::new())) } } + pub fn load_native_fn(&mut self, fun: Rc) { + let idx = self.global_names.borrow().len(); + self.global_names.borrow_mut().push(fun.name.clone()); + self.globals.insert(idx as u16, Value::Function(fun)); + } + + fn init_frame(&mut self, fun: Rc) -> Result { + let InnerFunction::Compiled(ref compiled_chunk) = fun.fun else { + return Err(self::Error::ExecNative.into()) + }; + Ok(StackFrame::new( + self, + fun.name.clone(), + compiled_chunk.clone(), + fun.arity + )) + } + pub fn run(&mut self, fun: Rc) -> Result { - let mut frame = StackFrame::new(&self, fun); + let mut frame = self.init_frame(fun)?; loop { use Instruction::*; - let ins = frame.fun.body.code[frame.ip].clone(); + let ins = frame.body.code[frame.ip].clone(); frame.ip += 1; match ins { @@ -159,7 +188,7 @@ impl Vm { let val = self.pop(); self.globals.insert(idx, val); }, - Const(idx) => self.push(frame.fun.body.constants[idx as usize].clone()), + Const(idx) => self.push(frame.body.constants[idx as usize].clone()), Int(i) => self.push(Value::Int(i as i64)), True => self.push(Value::Bool(true)), False => self.push(Value::Bool(false)), @@ -210,12 +239,33 @@ impl Vm { let Value::Function(fun) = fun else { return Err(Error::NotFunction(fun).into()) }; - if fun.arity != arity as usize { - return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into()) + let arity = arity as usize; + let normal_args = if fun.variadic { fun.arity - 1 } else { fun.arity }; + if (fun.variadic && arity < normal_args) || (!fun.variadic && arity != normal_args) { + return Err(Error::InvArity(normal_args, arity as usize, fun.name.clone()).into()) + } + if fun.variadic { + if arity == normal_args { + self.push(Value::List(vec![].into())); + } else if arity > normal_args { + let count = arity - normal_args; + let varadic_params = self.stack.split_off(self.stack.len() - count).inner; + self.push(Value::List(varadic_params.into())); + } + } + let name = fun.name.clone(); + match &fun.fun { + InnerFunction::Compiled(body) => { + let new_frame = StackFrame::new(self, name, body.clone(), fun.arity); + self.frames.push(frame); + frame = new_frame; + }, + InnerFunction::Native(native_fun) => { + let params = self.stack.split_off(self.stack.len() - fun.arity).inner; + let res = native_fun(self, params)?; + self.stack.push(res); + } } - let new_frame = StackFrame::new(&self, fun); - self.frames.push(frame); - frame = new_frame; }, Return => { let Some(prev_frame) = self.frames.pop() else { diff --git a/syntax_thing b/syntax_thing deleted file mode 100644 index 8f48af7..0000000 --- a/syntax_thing +++ /dev/null @@ -1,63 +0,0 @@ -root => root_expr* -root_expr => function -root_expr => expr -function => FUNCTION IDENT params block -params => IDENT -params => OP CP -params => OP params_impl -params_impl => IDENT COMMA params_impl -params_impl => IDENT CP -block => expr* -expr => if_expr -if_expr => IF math_expr block ELSE if_expr -if_expr => IF math_expr block ELSE block -if_expr => IF math_expr block -expr => DO block WHILE math_expr -expr => WHILE math_expr block -expr => LET IDENT EQ math_expr END -expr => LET IDENT END -expr => IDENT EQ math_expr END -expr => block -expr => RETURN math_expr END -expr => BREAK -expr => CONTINUE -expr => math_expr END -math_expr => (math_expr_and OR)* math_expr_and -math_expr_and => math_expr_and AND math_expr_and -math_expr_and => math_expr_compare -math_expr_compare => math_expr_compare math_expr_compare - -power_term => power_term POWER power_term -power_term => power_term BIT_AND power_term -power_term => power_term BIT_OR power_term -power_term => power_term SHIFT_LEFT power_term -power_term => power_term SHIFT_RIGHT power_term -power_term => term -term => value -term => tuple -term => list -term => table -term => IDENT -value => INT DIVIDE INT -value => INT -value => FLOAT -value => COMPLEX -value => STRING -value => REGEX -value => TRUE -value => FALSE -tuple => OP CP -tuple => OP tuple_impl -tuple_impl => math_expr COMMA tuple_impl -tuple_impl => math_expr CP -list => LBRK RBRK -list => LBRK list_impl -list_impl => math_expr COMMA tuple_impl -list_impl => math_expr RBRK -table => LBRC RBRC -table => LBRC table_impl -table_impl => table_key EQ math_expr COMMA table_impl -table_impl => table_key EQ math_expr RBRC -table_key => LBRK math_expr RBRK -table_key => IDENT -table_key => STRING