diff --git a/Cargo.lock b/Cargo.lock index f9d798a..293058c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -445,9 +445,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" [[package]] name = "syn" -version = "2.0.90" +version = "2.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" +checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035" dependencies = [ "proc-macro2", "quote", diff --git a/talc-bin/Cargo.toml b/talc-bin/Cargo.toml index bfcbcae..6a70332 100644 --- a/talc-bin/Cargo.toml +++ b/talc-bin/Cargo.toml @@ -2,6 +2,7 @@ name = "talc-bin" version = "0.2.2" edition = "2021" +rust-version = "1.81.0" [[bin]] name = "talc" diff --git a/talc-lang/Cargo.toml b/talc-lang/Cargo.toml index cbeb161..692d47c 100644 --- a/talc-lang/Cargo.toml +++ b/talc-lang/Cargo.toml @@ -2,6 +2,7 @@ name = "talc-lang" version = "0.2.2" edition = "2021" +rust-version = "1.81.0" [dependencies] num = { version = "0.4", features = [] } diff --git a/talc-lang/src/compiler.rs b/talc-lang/src/compiler.rs index 10f81ce..7399882 100644 --- a/talc-lang/src/compiler.rs +++ b/talc-lang/src/compiler.rs @@ -69,6 +69,7 @@ struct Compiler<'a> { attrs: FuncAttrs, // variables scope: HashMap, + scope_count: usize, shadowed: Vec<(Symbol, Option)>, closes: Vec<(Symbol, usize)>, local_count: usize, @@ -85,7 +86,16 @@ pub fn compile(expr: &Expr, name: Option) -> Result { pub fn compile_repl(expr: &Expr, globals: &mut Vec) -> Result { let mut comp = Compiler::new_repl(globals); - comp.expr(expr)?; + match &expr.kind { + ExprKind::Block(xs) => { + for x in &xs[0..xs.len() - 1] { + comp.expr(x)?; + comp.emit_discard(); + } + comp.expr(&xs[xs.len() - 1])?; + } + _ => comp.expr(expr)?, + } Ok(comp.finish_repl(globals)) } @@ -99,6 +109,7 @@ impl<'a> Default for Compiler<'a> { chunk: Chunk::new(), attrs: FuncAttrs::default(), scope, + scope_count: 0, shadowed: Vec::new(), local_count: 1, closes: Vec::new(), @@ -286,10 +297,12 @@ impl<'a> Compiler<'a> { #[must_use] fn begin_scope(&mut self) -> usize { + self.scope_count += 1; self.shadowed.len() } fn end_scope(&mut self, scope: usize) { + self.scope_count -= 1; let mut locals = 0; while self.shadowed.len() > scope { let (name, var) = self.shadowed.pop().expect("scope bad"); @@ -377,6 +390,9 @@ impl<'a> Compiler<'a> { } fn declare_global(&mut self, name: Symbol) { + if self.scope.get(&name) == Some(&VarKind::Global) { + return + } let shadowed = self.scope.insert(name, VarKind::Global); self.shadowed.push((name, shadowed)); } @@ -403,7 +419,10 @@ impl<'a> Compiler<'a> { ResolveOutcome::Var(VarKind::Global) => { self.emit(I::StoreGlobal(Arg24::from_symbol(name))); } - ResolveOutcome::None if self.mode == CompilerMode::Repl => { + ResolveOutcome::None + if self.mode == CompilerMode::Repl && self.scope_count == 0 => + { + self.declare_global(name); self.emit(I::StoreGlobal(Arg24::from_symbol(name))); } ResolveOutcome::None => { diff --git a/talc-lang/src/exception.rs b/talc-lang/src/exception.rs index febebb5..2ef63a9 100644 --- a/talc-lang/src/exception.rs +++ b/talc-lang/src/exception.rs @@ -10,31 +10,23 @@ pub type Result = std::result::Result; #[derive(Clone, Debug)] pub struct Exception { pub ty: Symbol, - pub msg: Option>, + pub msg: Rc, } impl Exception { - pub fn new(ty: Symbol) -> Self { - Self { ty, msg: None } - } - - pub fn new_with_msg(ty: Symbol, msg: Rc) -> Self { - Self { ty, msg: Some(msg) } + pub fn new(ty: Symbol, msg: Rc) -> Self { + Self { ty, msg } } pub fn from_table(table: &Rc>>) -> Option { let table = table.borrow(); let ty = table.get(&(*SYM_TYPE).into())?; - if let Value::Symbol(ty) = ty { - let msg = table.get(&(*SYM_MSG).into()); - match msg { - None => Some(Self { ty: *ty, msg: None }), - Some(Value::String(msg)) => Some(Self { - ty: *ty, - msg: Some(msg.clone()), - }), - Some(_) => None, - } + let msg = table.get(&(*SYM_MSG).into())?; + if let (Value::Symbol(ty), Value::String(msg)) = (ty, msg) { + Some(Self { + ty: *ty, + msg: msg.clone(), + }) } else { None } @@ -43,27 +35,21 @@ impl Exception { pub fn to_table(self) -> Rc>> { let mut table = HashMap::new(); table.insert((*SYM_TYPE).into(), self.ty.into()); - if let Some(msg) = self.msg { - table.insert((*SYM_MSG).into(), Value::String(msg)); - } + table.insert((*SYM_MSG).into(), Value::String(self.msg)); Rc::new(RefCell::new(table)) } } impl Display for Exception { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if let Some(msg) = &self.msg { - write!(f, "{}: {}", self.ty.name(), msg) - } else { - write!(f, "{}", self.ty.name()) - } + write!(f, "{}: {}", self.ty.name(), self.msg) } } #[macro_export] macro_rules! exception { ($exc_ty:expr, $($t:tt)*) => { - $crate::exception::Exception::new_with_msg( + $crate::exception::Exception::new( $exc_ty, $crate::lstring::LString::from( format!($($t)*) @@ -73,9 +59,6 @@ macro_rules! exception { ($exc_ty:expr, $fstr:literal) => { $crate::exception::exception!($exc_ty, $fstr,) }; - ($exc_ty:expr) => { - $crate::exception::Exception::new($exc_ty) - }; } pub use exception; diff --git a/talc-lang/src/parser/parser.rs b/talc-lang/src/parser/parser.rs index 9068795..d6a8164 100644 --- a/talc-lang/src/parser/parser.rs +++ b/talc-lang/src/parser/parser.rs @@ -205,6 +205,18 @@ impl<'s> Parser<'s> { self.lexer.peek().unwrap().clone() } + fn parse_symbol(tok: Token) -> Result { + let inner = &tok.content[1..]; + let s = match inner.chars().next() { + Some('\'') => Symbol::get(&inner[1..inner.len() - 1]), + Some('\"') => Symbol::get( + &parse_str_escapes(&inner[1..inner.len() - 1]).span_err(tok.span)?, + ), + _ => Symbol::get(inner), + }; + Ok(s) + } + fn parse_table_items(&mut self) -> Result> { let mut items = Vec::new(); loop { @@ -284,7 +296,7 @@ impl<'s> Parser<'s> { fn parse_symbol_list(&mut self) -> Result> { let mut syms = Vec::new(); while let Some(tok) = try_next!(self, T::Symbol) { - syms.push(Symbol::get(tok.content)); + syms.push(Self::parse_symbol(tok)?); if try_next!(self, T::Comma).is_none() { break } @@ -420,16 +432,8 @@ impl<'s> Parser<'s> { Ok(E::Literal(s.into()).span(tok.span)) } T::Symbol => { - let inner = &tok.content[1..]; - let s = match inner.chars().next() { - Some('\'') => Symbol::get(&inner[1..inner.len() - 1]), - Some('\"') => Symbol::get( - &parse_str_escapes(&inner[1..inner.len() - 1]) - .span_err(tok.span)?, - ), - _ => Symbol::get(inner), - }; - Ok(E::Literal(s.into()).span(tok.span)) + let s = Self::parse_symbol(tok)?; + Ok(E::Literal(Value::from(s)).span(tok.span)) } T::True => Ok(E::Literal(Value::Bool(true)).span(tok.span)), T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)), @@ -531,6 +535,28 @@ impl<'s> Parser<'s> { Ok(lhs) } + fn parse_and(&mut self) -> Result { + let mut lhs = self.parse_precedence(0)?; + let mut span = lhs.span; + while try_next!(self, T::And).is_some() { + let rhs = self.parse_precedence(0)?; + span += rhs.span; + lhs = E::And(b(lhs), b(rhs)).span(span); + } + Ok(lhs) + } + + fn parse_or(&mut self) -> Result { + let mut lhs = self.parse_and()?; + let mut span = lhs.span; + while try_next!(self, T::Or).is_some() { + let rhs = self.parse_and()?; + span += rhs.span; + lhs = E::Or(b(lhs), b(rhs)).span(span); + } + Ok(lhs) + } + fn parse_lambda(&mut self) -> Result { let tok = try_next!(self, T::Backslash | T::Amper); match tok { @@ -555,7 +581,7 @@ impl<'s> Parser<'s> { let body_span = body.span; Ok(E::Lambda(args, b(body)).span(span + body_span)) } - None => self.parse_precedence(0), + None => self.parse_or(), _ => unreachable!("parse_lambda: guaranteed by try_next!"), } } @@ -571,40 +597,8 @@ impl<'s> Parser<'s> { Ok(lhs) } - fn parse_not(&mut self) -> Result { - if let Some(tok) = try_next!(self, T::Not) { - let expr = self.parse_not()?; - let span = tok.span + expr.span; - Ok(E::UnaryOp(UnaryOp::Not, b(expr)).span(span)) - } else { - self.parse_pipeline() - } - } - - fn parse_and(&mut self) -> Result { - let mut lhs = self.parse_not()?; - let mut span = lhs.span; - while try_next!(self, T::And).is_some() { - let rhs = self.parse_not()?; - span += rhs.span; - lhs = E::And(b(lhs), b(rhs)).span(span); - } - Ok(lhs) - } - - fn parse_or(&mut self) -> Result { - let mut lhs = self.parse_and()?; - let mut span = lhs.span; - while try_next!(self, T::Or).is_some() { - let rhs = self.parse_and()?; - span += rhs.span; - lhs = E::Or(b(lhs), b(rhs)).span(span); - } - Ok(lhs) - } - fn parse_assign(&mut self) -> Result { - let lhs = self.parse_or()?; + let lhs = self.parse_pipeline()?; let lhs_span = lhs.span; if let Some(op) = self.peek()?.kind.assign_op() { let Some(lval) = LValue::from_expr(lhs) else { diff --git a/talc-lang/src/value/index.rs b/talc-lang/src/value/index.rs index 9d970e2..3e605d1 100644 --- a/talc-lang/src/value/index.rs +++ b/talc-lang/src/value/index.rs @@ -17,15 +17,19 @@ impl Value { match (self, idx) { (V::List(l), V::Int(i)) => { let l = l.borrow(); - let Some(i) = i.to_usize() else { + let Some(i) = i.to_isize() else { throw!( *SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len() ) }; - if i < l.len() { - Ok(l[i].clone()) + let mut j = i; + if j < 0 { + j += l.len() as isize; + } + if (0..l.len() as isize).contains(&j) { + Ok(l[j as usize].clone()) } else { throw!( *SYM_INDEX_ERROR, @@ -84,15 +88,19 @@ impl Value { match (self, idx) { (V::List(l), V::Int(i)) => { let mut l = l.borrow_mut(); - let Some(i) = i.to_usize() else { + let Some(i) = i.to_isize() else { throw!( *SYM_INDEX_ERROR, "index {i} out of bounds for list of length {}", l.len() ) }; - if i < l.len() { - l[i] = val; + let mut j = i; + if j < 0 { + j += l.len() as isize; + } + if (0..l.len() as isize).contains(&j) { + l[j as usize] = val; Ok(()) } else { throw!( diff --git a/talc-lang/src/value/ops.rs b/talc-lang/src/value/ops.rs index 631ba48..606945c 100644 --- a/talc-lang/src/value/ops.rs +++ b/talc-lang/src/value/ops.rs @@ -28,15 +28,15 @@ impl Value { Value::Bool(b) => *b, Value::Range(r) => !r.is_empty(), Value::Int(n) => !n.is_zero(), - Value::Float(x) => *x != 0.0 && !x.is_nan(), + Value::Float(x) => *x != 0.0, Value::Ratio(r) => !r.is_zero(), - Value::Complex(c) => !(c.re == 0.0 && c.im == 0.0 || c.is_nan()), + Value::Complex(c) => !c.is_zero(), Value::String(s) => !s.is_empty(), - Value::List(l) => l.borrow().len() > 0, + Value::List(l) => !l.borrow().is_empty(), + Value::Table(t) => !t.borrow().is_empty(), Value::Cell(v) => v.borrow().truthy(), Value::Symbol(_) - | Value::Table(_) | Value::Function(_) | Value::NativeFunc(_) | Value::Native(_) => true, diff --git a/talc-lang/src/vm.rs b/talc-lang/src/vm.rs index dfc01c5..2a463af 100644 --- a/talc-lang/src/vm.rs +++ b/talc-lang/src/vm.rs @@ -258,7 +258,7 @@ impl Vm { .interrupt .fetch_and(false, std::sync::atomic::Ordering::Relaxed) { - throw!(*SYM_INTERRUPTED) + throw!(*SYM_INTERRUPTED, "interrupted") } Ok(()) } diff --git a/talc-macros/Cargo.toml b/talc-macros/Cargo.toml index 7f65a69..6739e7c 100644 --- a/talc-macros/Cargo.toml +++ b/talc-macros/Cargo.toml @@ -2,6 +2,7 @@ name = "talc-macros" version = "0.2.1" edition = "2021" +rust-version = "1.68.2" [lib] proc-macro = true diff --git a/talc-std/Cargo.toml b/talc-std/Cargo.toml index 1bf1fa9..1ead113 100644 --- a/talc-std/Cargo.toml +++ b/talc-std/Cargo.toml @@ -2,6 +2,7 @@ name = "talc-std" version = "0.2.2" edition = "2021" +rust-version = "1.81.0" [dependencies] talc-lang = { path = "../talc-lang" } diff --git a/talc-std/src/exception.rs b/talc-std/src/exception.rs index a6e9bfd..644ccf8 100644 --- a/talc-std/src/exception.rs +++ b/talc-std/src/exception.rs @@ -8,27 +8,13 @@ use talc_macros::native_func; use crate::unpack_args; -#[native_func(1)] +#[native_func(2)] pub fn throw(_: &mut Vm, args: Vec) -> Result { - let [_, arg] = unpack_args!(args); - let exc = match arg { - Value::Symbol(ty) => Exception::new(ty), - Value::List(l) => match l.borrow().as_slice() { - [Value::Symbol(ty)] | [Value::Symbol(ty), Value::Nil] => Exception::new(*ty), - [Value::Symbol(ty), Value::String(s)] => { - Exception::new_with_msg(*ty, s.clone()) - } - [] | [_] | [_, _] => { - throw!(*SYM_TYPE_ERROR, "wrong argument for throw") - } - [_, _, _, ..] => throw!( - *SYM_VALUE_ERROR, - "too many elements in list argument for throw" - ), - }, - _ => throw!(*SYM_TYPE_ERROR, "throw expected symbol or list"), + let [_, ty, msg] = unpack_args!(args); + let (Value::Symbol(ty), Value::String(msg)) = (ty, msg) else { + throw!(*SYM_TYPE_ERROR, "throw expected symbol and string") }; - Err(exc) + Err(Exception::new(ty, msg)) } #[native_func(1)] @@ -38,9 +24,9 @@ pub fn rethrow(_: &mut Vm, args: Vec) -> Result { if let Some(e) = Exception::from_table(&table) { return Err(e) } - throw!(*SYM_VALUE_ERROR, "argument not a valid exception") + throw!(*SYM_VALUE_ERROR, "rethrow: argument not a valid exception") } - throw!(*SYM_TYPE_ERROR, "argument not a valid exception") + throw!(*SYM_TYPE_ERROR, "rethrow expected table") } pub fn load(vm: &mut Vm) { diff --git a/talc-std/src/math.rs b/talc-std/src/math.rs index ec7039b..2c508d2 100644 --- a/talc-std/src/math.rs +++ b/talc-std/src/math.rs @@ -91,6 +91,7 @@ pub fn load(vm: &mut Vm) { vm.set_global_name("arg", arg().into()); vm.set_global_name("abs", abs().into()); vm.set_global_name("abs_sq", abs_sq().into()); + vm.set_global_name("conj", conj().into()); vm.set_global_name("sqrt", sqrt().into()); vm.set_global_name("cbrt", cbrt().into()); @@ -516,6 +517,18 @@ pub fn abs_sq(_: &mut Vm, args: Vec) -> Result { } } +#[native_func(1)] +pub fn conj(_: &mut Vm, args: Vec) -> Result { + let [_, x] = unpack_args!(args); + match x { + Value::Int(_) => Ok(x), + Value::Ratio(_) => Ok(x), + Value::Float(_) => Ok(x), + Value::Complex(x) => Ok(Value::Complex(x.conj())), + x => throw!(*SYM_TYPE_ERROR, "conj expected numeric argument, got {x:#}"), + } +} + // // continuous operations //