more changes

This commit is contained in:
Freya Murphy 2024-02-23 11:32:47 -05:00
parent e649cef425
commit a888c09bd5
Signed by: freya
GPG key ID: 744AB800E383AE52
20 changed files with 377 additions and 160 deletions

27
Cargo.lock generated
View file

@ -114,7 +114,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn", "syn 2.0.48",
] ]
[[package]] [[package]]
@ -220,12 +220,26 @@ version = "0.1.0"
dependencies = [ dependencies = [
"clap", "clap",
"matrix", "matrix",
"matrix-stdlib",
"rustyline", "rustyline",
] ]
[[package]]
name = "matrix-macros"
version = "0.1.0"
dependencies = [
"matrix",
"quote",
"syn 1.0.109",
]
[[package]] [[package]]
name = "matrix-stdlib" name = "matrix-stdlib"
version = "0.1.0" version = "0.1.0"
dependencies = [
"matrix",
"matrix-macros",
]
[[package]] [[package]]
name = "memchr" name = "memchr"
@ -407,6 +421,17 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" 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]] [[package]]
name = "syn" name = "syn"
version = "2.0.48" version = "2.0.48"

View file

@ -1,3 +1,3 @@
[workspace] [workspace]
resolver = "2" resolver = "2"
members = [ "matrix", "matrix-bin" , "matrix-stdlib"] members = [ "matrix", "matrix-bin" , "matrix-macros", "matrix-stdlib"]

5
a.mat
View file

@ -1,5 +0,0 @@
a = 0;
while a < 1000000000 { a = a + 1 };

View file

@ -9,5 +9,7 @@ path = "src/main.rs"
[dependencies] [dependencies]
clap = { version = "4", features = [ "derive" ] } clap = { version = "4", features = [ "derive" ] }
ctrlc = "3"
matrix = { path = "../matrix" } matrix = { path = "../matrix" }
matrix-stdlib = { path = "../matrix-stdlib" }
rustyline = "13" rustyline = "13"

View file

@ -1,6 +1,6 @@
use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs}; use std::{path::PathBuf, io::{self, Read, IsTerminal}, fs};
use clap::Parser as ArgParser; 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; use repl::Repl;
mod repl; mod repl;
@ -50,24 +50,24 @@ impl<'a> State<'a> {
let parser = ParserBuilder::new() let parser = ParserBuilder::new()
.optimize(!args.disable_optimizations) .optimize(!args.disable_optimizations)
.build(); .build();
let vm = Vm::new(); let mut vm = Vm::new();
let compiler = CompilerBuilder::new() let compiler = CompilerBuilder::new()
.repl(repl) .repl(repl)
.debug(args.debug) .debug(args.debug)
.names(vm.names()) .names(vm.names())
.globals(vm.global_names())
.build(); .build();
matrix_stdlib::load(&mut vm);
(Self { parser, vm, compiler, repl }, file) (Self { parser, vm, compiler, repl }, file)
} }
pub fn execute(&mut self, code: String) -> matrix::Result<()> { pub fn execute(&mut self, code: String) -> matrix::Result<Value> {
let ast = self.parser.parse(code)?; let ast = self.parser.parse(code)?;
let fun = self.compiler.compile(&ast)?; let fun = self.compiler.compile(&ast)?;
let val = self.vm.run(fun)?; let val = self.vm.run(fun)?;
if self.repl { Ok(val)
println!("{val}");
}
Ok(())
} }
} }

View file

@ -1,3 +1,8 @@
use std::{io::Write, sync::atomic::Ordering};
use matrix::{value::Value, vm::Interupt};
use rustyline::Config;
use crate::State; use crate::State;
pub struct Repl<'a> { pub struct Repl<'a> {
@ -11,14 +16,29 @@ impl<'a> Repl<'a> {
} }
pub fn run(&mut self) { 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 { loop {
let Ok(line) = rl.readline(">> ") else { let Ok(line) = rl.readline(">> ") else {
break; break;
}; };
if let Err(err) = self.state.execute(line) { match self.state.execute(line) {
crate::error(err); Err(err) => crate::error(err),
Ok(val) => {
if val != Value::Nil {
println!("{val}");
}
}
} }
let _ = std::io::stdout().flush();
} }
} }

12
matrix-macros/Cargo.toml Normal file
View file

@ -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" }

52
matrix-macros/src/lib.rs Normal file
View file

@ -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<Self> {
let arity = input.parse()?;
input.parse::<Token![,]>()?;
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)
}

View file

@ -6,3 +6,5 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
matrix = { path = "../matrix" }
matrix-macros = { path = "../matrix-macros" }

36
matrix-stdlib/src/io.rs Normal file
View file

@ -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<Value>) -> Result<Value> {
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<Value>) -> Result<Value> {
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());
}

View file

@ -1,14 +1,7 @@
pub fn add(left: usize, right: usize) -> usize { use matrix::vm::Vm;
left + right
}
#[cfg(test)] mod io;
mod tests {
use super::*;
#[test] pub fn load(compiler: &mut Vm) {
fn it_works() { io::load(compiler);
let result = add(2, 2);
assert_eq!(result, 4);
}
} }

View file

@ -57,7 +57,8 @@ pub enum Expr {
Assign(Box<Expr>, Box<Expr>), Assign(Box<Expr>, Box<Expr>),
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>), If(Box<Expr>, Box<Expr>, Option<Box<Expr>>),
Function(Rc<str>, Vec<Rc<str>>, Box<Expr>), Function(Rc<str>, Vec<Rc<str>>, Box<Expr>, bool),
Lambda(Vec<Rc<str>>, Box<Expr>, bool),
Loop(Box<Expr>), Loop(Box<Expr>),
While(Box<Expr>, Box<Expr>), While(Box<Expr>, Box<Expr>),
@ -279,10 +280,14 @@ pub fn optimize(expr: Expr) -> Result<Expr> {
let block = Box::new(optimize(*block)?); let block = Box::new(optimize(*block)?);
E::Loop(block) E::Loop(block)
}, },
E::Function(ident, params, stmt) => { E::Function(ident, params, stmt, varadic) => {
let stmt = Box::new(optimize(*stmt)?); 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, expr) => {
E::Let(ident, Box::new(optimize(*expr)?)) E::Let(ident, Box::new(optimize(*expr)?))
}, },

View file

@ -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}; use std::{fmt::{Debug, Display}, rc::Rc};
#[derive(Clone)] #[derive(Clone)]
@ -36,11 +36,30 @@ impl Display for Chunk {
} }
} }
#[derive(Debug, Clone)]
pub struct Function { pub struct Function {
pub name: Rc<str>, pub name: Rc<str>,
pub arity: usize, pub arity: usize,
pub body: Chunk pub variadic: bool,
pub fun: InnerFunction
}
pub enum InnerFunction {
Compiled(Rc<Chunk>),
Native(Box<dyn Fn(&mut Vm, Vec<Value>) -> Result<Value>>),
}
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)] #[derive(Clone, Debug)]

View file

@ -1,13 +1,15 @@
use std::{fmt::Display, rc::Rc, cell::RefCell}; 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 Instruction as I;
use Value as V; use Value as V;
use Expr as E; use Expr as E;
pub type NamesTable = Rc<RefCell<Vec<Rc<str>>>>;
pub struct CompilerBuilder<'c> { pub struct CompilerBuilder<'c> {
globals: Rc<RefCell<Vec<Rc<str>>>>, globals: NamesTable,
names: Rc<RefCell<Vec<Rc<str>>>>, names: NamesTable,
repl: bool, repl: bool,
debug: bool, debug: bool,
name: Rc<str>, name: Rc<str>,
@ -37,12 +39,12 @@ impl<'c> CompilerBuilder<'c> {
self self
} }
pub fn globals(mut self, globals: Rc<RefCell<Vec<Rc<str>>>>) -> Self { pub fn globals(mut self, globals: NamesTable) -> Self {
self.globals = globals; self.globals = globals;
self self
} }
pub fn names(mut self, names: Rc<RefCell<Vec<Rc<str>>>>) -> Self { pub fn names(mut self, names: NamesTable) -> Self {
self.names = names; self.names = names;
self self
} }
@ -133,6 +135,8 @@ impl<'c> Compiler<'c> {
CompilerBuilder::new() CompilerBuilder::new()
.name(name) .name(name)
.debug(self.debug) .debug(self.debug)
.globals(self.globals.clone())
.names(self.names.clone())
.repl(false) .repl(false)
.parent(self) .parent(self)
.build() .build()
@ -269,13 +273,14 @@ impl<'c> Compiler<'c> {
self.compile_expr(elseb)?; 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 chunk = self.compile_function(name.clone(), params, body)?;
let fun = Value::Function(Rc::new( let fun = Value::Function(Rc::new(
chunk::Function { chunk::Function {
name: name.clone(), name: name.clone(),
arity: params.len(), arity: params.len(),
body: chunk fun: InnerFunction::Compiled(chunk.into()),
variadic: *varadic
} }
)); ));
self.emit_const(fun); self.emit_const(fun);
@ -288,6 +293,19 @@ impl<'c> Compiler<'c> {
self.emit(I::CreateLocal); self.emit(I::CreateLocal);
} }
}, },
E::Lambda(params, body, varadic) => {
let name: Rc<str> = Rc::from("<lambda>");
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) => { E::Loop(expr) => {
let idx = self.cur(); let idx = self.cur();
self.loop_top.push((idx as usize, self.scopes.len())); 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::Literal(val) => self.compile_value(val),
E::Ident(name) => { 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)); self.emit(I::LoadLocal(local.idx as u16));
} else if let Some(global) = self.find_global(name) { } else if let Some(global) = self.find_global(name) {
self.emit(I::LoadGlobal(global as u16)); self.emit(I::LoadGlobal(global as u16));
@ -377,7 +397,7 @@ impl<'c> Compiler<'c> {
self.compile_expr(rhs)?; self.compile_expr(rhs)?;
self.emit(I::Dup); self.emit(I::Dup);
match lhs.as_ref() { match lhs.as_ref() {
E::Ident(name) => { E::Ident(name) if name.as_ref() != "_" => {
if let Some(local) = self.find_local(&name) { if let Some(local) = self.find_local(&name) {
self.emit(I::StoreLocal(local.idx as u16)); self.emit(I::StoreLocal(local.idx as u16));
} else if let Some(global) = self.find_global(&name) { } else if let Some(global) = self.find_global(&name) {
@ -540,7 +560,7 @@ impl<'c> Compiler<'c> {
self.chunk = Chunk::new(); self.chunk = Chunk::new();
self.compile_expr(body)?; self.compile_expr(body)?;
self.finish()?; 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)) Ok(Rc::new(fun))
} }
} }

View file

@ -24,9 +24,9 @@ impl<T> Gc<T> {
} }
impl <T: Clone> Gc<T> { impl <T: Clone> Gc<T> {
pub fn clone_inside(&mut self) -> Self { pub fn clone_inside(&self) -> Self {
unsafe { unsafe {
let data = self.ptr.as_mut().data.clone(); let data = self.ptr.as_ref().data.clone();
Self::new(data) Self::new(data)
} }
} }

View file

@ -47,6 +47,8 @@ pub enum Token {
Comma, Comma,
Range, Range,
Colon, Colon,
Backslash,
Varadic,
// math // math
Regex(RegexToken), Regex(RegexToken),
@ -443,6 +445,7 @@ impl Lexer {
'{' => LeftBrace, '{' => LeftBrace,
'}' => RightBrace, '}' => RightBrace,
':' => Colon, ':' => Colon,
'\\' => Backslash,
';' => SemiColon, ';' => SemiColon,
'+' => { '+' => {
match next { match next {
@ -627,7 +630,14 @@ impl Lexer {
}, },
'.' => { '.' => {
if next == '.' { if next == '.' {
Range self.next();
match self.peek() {
'.' => {
self.next();
Varadic
},
_ => Range
}
} else if next.is_digit(10) { } else if next.is_digit(10) {
self.lex_number(char)? self.lex_number(char)?
} else { } else {

View file

@ -40,7 +40,7 @@ pub enum Error {
UnexpectedToken(Token), UnexpectedToken(Token),
ExpectedToken(Token), ExpectedToken(Token),
ExpectedTokenName(&'static str), ExpectedTokenName(&'static str),
MatrixCoDomainError(usize, usize, usize), MatrixInvDomain(usize, usize, usize),
NotAssignable(Expr), NotAssignable(Expr),
ValueError(value::Error), ValueError(value::Error),
} }
@ -53,7 +53,7 @@ impl Display for Error {
UnexpectedToken(tok) => write!(f, "Unexpected token: '{tok:?}'"), UnexpectedToken(tok) => write!(f, "Unexpected token: '{tok:?}'"),
ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"), ExpectedToken(tok) => write!(f, "Expected token: '{tok:?}'"),
ExpectedTokenName(name) => write!(f, "Expected {name} token"), 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"), NotAssignable(expr) => write!(f, "{expr:?} is not assignable"),
ValueError(err) => write!(f, "{err}"), ValueError(err) => write!(f, "{err}"),
} }
@ -199,11 +199,11 @@ impl Parser {
if parts.len() == 1 { if parts.len() == 1 {
Ok(E::List(parts.pop().unwrap())) Ok(E::List(parts.pop().unwrap()))
} else { } else {
let codomain = parts[0].len(); let codomain = parts.len();
let domain = parts.len(); let domain = parts[0].len();
for (i, part) in parts.iter().enumerate() { for (i, part) in parts.iter().enumerate() {
if part.len() != codomain { 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(); let mut data = Vec::new();
@ -259,20 +259,29 @@ impl Parser {
Ok(expr) Ok(expr)
} }
fn parse_params(&mut self) -> Result<Vec<Rc<str>>> { fn parse_params(&mut self) -> Result<(Vec<Rc<str>>, bool)> {
use T::*; use T::*;
let tok = self.lexer.next_token()?; let tok = self.lexer.next_token()?;
match tok { 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 => (), LeftParen => (),
_ => return Err(Error::UnexpectedToken(tok).into()), _ => return Err(Error::UnexpectedToken(tok).into()),
} }
let mut params = Vec::new(); let mut params = Vec::new();
let mut varadic = false;
if self.lexer.peek_token()? == T::RightParen { if self.lexer.peek_token()? == T::RightParen {
self.lexer.next_token()?; self.lexer.next_token()?;
return Ok(params); return Ok((params, varadic));
} }
loop { loop {
@ -280,13 +289,18 @@ impl Parser {
params.push(ident); params.push(ident);
let next = self.lexer.next_token()?; let next = self.lexer.next_token()?;
match next { match next {
Varadic => {
varadic = true;
self.force_token(T::RightParen)?;
break;
}
Comma => continue, Comma => continue,
RightParen => break, RightParen => break,
_ => return Err(Error::UnexpectedToken(next).into()), _ => return Err(Error::UnexpectedToken(next).into()),
} }
} }
Ok(params) Ok((params, varadic))
} }
fn parse_ident(&mut self) -> Result<Rc<str>> { fn parse_ident(&mut self) -> Result<Rc<str>> {
@ -308,12 +322,23 @@ impl Parser {
fn parse_function(&mut self) -> Result<Expr> { fn parse_function(&mut self) -> Result<Expr> {
self.force_token(T::Function)?; self.force_token(T::Function)?;
let ident = self.parse_ident()?; let ident = self.parse_ident()?;
let params = match self.lexer.peek_token()? { let (params, varadic) = match self.lexer.peek_token()? {
T::LeftBrace => vec![], T::LeftBrace => (vec![], false),
_ => self.parse_params()?, _ => self.parse_params()?,
}; };
let expr = self.parse_expr()?; 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<Expr> {
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<Expr> { fn parse_do_while(&mut self) -> Result<Expr> {
@ -387,7 +412,7 @@ impl Parser {
match next { match next {
T::SemiColon => continue, T::SemiColon => continue,
T::RightBrace => break, T::RightBrace => break,
_ => return Err(Error::UnexpectedToken(next).into()) _ => return Err(Error::ExpectedToken(T::SemiColon).into())
} }
} }
if self.lexer.peek_token()? == T::RightBrace { if self.lexer.peek_token()? == T::RightBrace {
@ -416,6 +441,7 @@ impl Parser {
use T::*; use T::*;
match self.lexer.peek_token()? { match self.lexer.peek_token()? {
Function => self.parse_function(), Function => self.parse_function(),
Backslash => self.parse_lambda(),
Do => self.parse_do_while(), Do => self.parse_do_while(),
While => self.parse_while(), While => self.parse_while(),
Let => self.parse_let(), Let => self.parse_let(),
@ -641,6 +667,11 @@ impl Parser {
}; };
let expr = self.parse_expr()?; let expr = self.parse_expr()?;
block.push(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)) Ok(E::Block(block))
} }

View file

@ -171,18 +171,17 @@ impl Value {
str str
}, },
Matrix(m) => { Matrix(m) => {
let mut str = "[[".to_string(); let mut str = "\n".to_string();
for (i, el) in m.2.iter().enumerate() { for row in m.2.chunks(m.0) {
if i != 0 { str.push_str(" ");
if (i % m.1) == 0 { for (i, v) in row.iter().enumerate() {
str.push_str(" ; "); if i != 0 {
} else { str.push(' ');
str.push_str(" ");
} }
str.push_str(&v.boring_print());
} }
str.push_str(&el.boring_print()); str.push('\n');
} }
str.push_str("]]");
str str
}, },
Table(t) => { Table(t) => {
@ -199,7 +198,7 @@ impl Value {
str str
}, },
Function(fun) => { Function(fun) => {
format!("[Function: {}]", fun.name) format!("{fun:?}")
} }
} }
} }
@ -227,18 +226,17 @@ impl Value {
str str
}, },
Matrix(m) => { Matrix(m) => {
let mut str = "[[".to_string(); let mut str = "\n".to_string();
for (i, el) in m.2.iter().enumerate() { for row in m.2.chunks(m.0) {
if i != 0 { str.push_str(" ");
if (i % m.1) == 0 { for (i, v) in row.iter().enumerate() {
str.push_str(" ; "); if i != 0 {
} else { str.push(' ');
str.push_str(" ");
} }
str.push_str(&v.pretty_print());
} }
str.push_str(&el.pretty_print()); str.push('\n');
} }
str.push_str("]]");
str str
}, },
Table(t) => { Table(t) => {
@ -256,7 +254,7 @@ impl Value {
}, },
Function(_) => { Function(_) => {
format!("\x1b[36m{}\x1b[0m", self.boring_print()) format!("\x1b[36m{}\x1b[0m", self.boring_print())
} },
} }
} }
} }
@ -449,6 +447,16 @@ impl Value {
(l, r) => Err(Error::Exponent(l, r).into()) (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 {} impl Eq for Value {}
@ -523,7 +531,7 @@ impl Value {
return Err(Error::IndexOutOfBounds(*i as usize, l.len()).into()) return Err(Error::IndexOutOfBounds(*i as usize, l.len()).into())
} }
Ok(l[*i as usize].clone()) Ok(l[*i as usize].clone())
} },
_ => return Err(self::Error::BadIndex(self.clone(), index.clone()).into()) _ => return Err(self::Error::BadIndex(self.clone(), index.clone()).into())
} }
} }
@ -553,7 +561,7 @@ impl Value {
pub fn index(&self, index: &Vec<Value>) -> Result<Self> { pub fn index(&self, index: &Vec<Value>) -> Result<Self> {
if index.len() == 0 { if index.len() == 0 {
Ok(self.clone()) Ok(self.clone_inside())
} else if index.len() == 1 { } else if index.len() == 1 {
self.index_single(&index[0]) self.index_single(&index[0])
} else { } else {

View file

@ -1,5 +1,5 @@
use std::{rc::Rc, fmt::{Debug, Display}, usize, ops::{Index, IndexMut}, collections::HashMap, cell::RefCell}; 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)] #[derive(Debug)]
pub enum Error { pub enum Error {
@ -7,6 +7,7 @@ pub enum Error {
NotFunction(Value), NotFunction(Value),
InvArity(usize, usize, Rc<str>), InvArity(usize, usize, Rc<str>),
UndefinedGlobal(Rc<str>), UndefinedGlobal(Rc<str>),
ExecNative,
} }
impl std::error::Error for Error {} impl std::error::Error for Error {}
@ -19,6 +20,7 @@ impl Display for Error {
NotFunction(v) => write!(f, "{v:?} is not a function"), NotFunction(v) => write!(f, "{v:?} is not a function"),
InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"), InvArity(wanted, had, name) => write!(f, "function {name} takes {wanted} params but was given {had}"),
UndefinedGlobal(name) => write!(f, "{name} is not defined"), UndefinedGlobal(name) => write!(f, "{name} is not defined"),
ExecNative => write!(f, "cannot execute a native function"),
} }
} }
} }
@ -88,17 +90,20 @@ impl<T> Index<usize> for Stack<T> {
} }
struct StackFrame { struct StackFrame {
fun: Rc<Function>, #[allow(dead_code)]
name: Rc<str>,
body: Rc<Chunk>,
ip: usize, ip: usize,
bp: usize, bp: usize,
} }
impl StackFrame { impl StackFrame {
fn new(vm: &Vm, fun: Rc<Function>) -> Self { fn new(vm: &Vm, name: Rc<str>, body: Rc<Chunk>, arity: usize) -> Self {
Self { Self {
bp: vm.stack.len() - fun.arity, name,
body,
bp: vm.stack.len() - arity,
ip: 0, ip: 0,
fun,
} }
} }
} }
@ -108,7 +113,8 @@ pub struct Vm {
locals: Stack<Value>, locals: Stack<Value>,
frames: Vec<StackFrame>, frames: Vec<StackFrame>,
globals: HashMap<u16, Value>, globals: HashMap<u16, Value>,
names: Rc<RefCell<Vec<Rc<str>>>>, names: NamesTable,
global_names: NamesTable,
} }
impl Vm { impl Vm {
@ -121,10 +127,14 @@ impl Vm {
self.stack.pop() self.stack.pop()
} }
pub fn names(&self) -> Rc<RefCell<Vec<Rc<str>>>> { pub fn names(&self) -> NamesTable {
self.names.clone() self.names.clone()
} }
pub fn global_names(&self) -> NamesTable {
self.global_names.clone()
}
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
stack: Stack::new(), stack: Stack::new(),
@ -132,14 +142,33 @@ impl Vm {
frames: Vec::new(), frames: Vec::new(),
globals: HashMap::new(), globals: HashMap::new(),
names: Rc::new(RefCell::new(Vec::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<Function>) {
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<Function>) -> Result<StackFrame> {
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<Function>) -> Result<Value> { pub fn run(&mut self, fun: Rc<Function>) -> Result<Value> {
let mut frame = StackFrame::new(&self, fun); let mut frame = self.init_frame(fun)?;
loop { loop {
use Instruction::*; use Instruction::*;
let ins = frame.fun.body.code[frame.ip].clone(); let ins = frame.body.code[frame.ip].clone();
frame.ip += 1; frame.ip += 1;
match ins { match ins {
@ -159,7 +188,7 @@ impl Vm {
let val = self.pop(); let val = self.pop();
self.globals.insert(idx, val); 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)), Int(i) => self.push(Value::Int(i as i64)),
True => self.push(Value::Bool(true)), True => self.push(Value::Bool(true)),
False => self.push(Value::Bool(false)), False => self.push(Value::Bool(false)),
@ -210,12 +239,33 @@ impl Vm {
let Value::Function(fun) = fun else { let Value::Function(fun) = fun else {
return Err(Error::NotFunction(fun).into()) return Err(Error::NotFunction(fun).into())
}; };
if fun.arity != arity as usize { let arity = arity as usize;
return Err(Error::InvArity(fun.arity, arity as usize, fun.name.clone()).into()) 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 => { Return => {
let Some(prev_frame) = self.frames.pop() else { let Some(prev_frame) = self.frames.pop() else {

View file

@ -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 <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