This commit is contained in:
trimill 2024-12-25 22:58:12 -05:00
parent 2a8a7b347e
commit 88c99fbac1
13 changed files with 117 additions and 110 deletions

4
Cargo.lock generated
View file

@ -445,9 +445,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.90" version = "2.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" checksum = "d53cbcb5a243bd33b7858b1d7f4aca2153490815872d86d955d6ea29f743c035"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",

View file

@ -2,6 +2,7 @@
name = "talc-bin" name = "talc-bin"
version = "0.2.2" version = "0.2.2"
edition = "2021" edition = "2021"
rust-version = "1.81.0"
[[bin]] [[bin]]
name = "talc" name = "talc"

View file

@ -2,6 +2,7 @@
name = "talc-lang" name = "talc-lang"
version = "0.2.2" version = "0.2.2"
edition = "2021" edition = "2021"
rust-version = "1.81.0"
[dependencies] [dependencies]
num = { version = "0.4", features = [] } num = { version = "0.4", features = [] }

View file

@ -69,6 +69,7 @@ struct Compiler<'a> {
attrs: FuncAttrs, attrs: FuncAttrs,
// variables // variables
scope: HashMap<Symbol, VarKind>, scope: HashMap<Symbol, VarKind>,
scope_count: usize,
shadowed: Vec<(Symbol, Option<VarKind>)>, shadowed: Vec<(Symbol, Option<VarKind>)>,
closes: Vec<(Symbol, usize)>, closes: Vec<(Symbol, usize)>,
local_count: usize, local_count: usize,
@ -85,7 +86,16 @@ pub fn compile(expr: &Expr, name: Option<Symbol>) -> Result<Function> {
pub fn compile_repl(expr: &Expr, globals: &mut Vec<Symbol>) -> Result<Function> { pub fn compile_repl(expr: &Expr, globals: &mut Vec<Symbol>) -> Result<Function> {
let mut comp = Compiler::new_repl(globals); 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)) Ok(comp.finish_repl(globals))
} }
@ -99,6 +109,7 @@ impl<'a> Default for Compiler<'a> {
chunk: Chunk::new(), chunk: Chunk::new(),
attrs: FuncAttrs::default(), attrs: FuncAttrs::default(),
scope, scope,
scope_count: 0,
shadowed: Vec::new(), shadowed: Vec::new(),
local_count: 1, local_count: 1,
closes: Vec::new(), closes: Vec::new(),
@ -286,10 +297,12 @@ impl<'a> Compiler<'a> {
#[must_use] #[must_use]
fn begin_scope(&mut self) -> usize { fn begin_scope(&mut self) -> usize {
self.scope_count += 1;
self.shadowed.len() self.shadowed.len()
} }
fn end_scope(&mut self, scope: usize) { fn end_scope(&mut self, scope: usize) {
self.scope_count -= 1;
let mut locals = 0; let mut locals = 0;
while self.shadowed.len() > scope { while self.shadowed.len() > scope {
let (name, var) = self.shadowed.pop().expect("scope bad"); let (name, var) = self.shadowed.pop().expect("scope bad");
@ -377,6 +390,9 @@ impl<'a> Compiler<'a> {
} }
fn declare_global(&mut self, name: Symbol) { fn declare_global(&mut self, name: Symbol) {
if self.scope.get(&name) == Some(&VarKind::Global) {
return
}
let shadowed = self.scope.insert(name, VarKind::Global); let shadowed = self.scope.insert(name, VarKind::Global);
self.shadowed.push((name, shadowed)); self.shadowed.push((name, shadowed));
} }
@ -403,7 +419,10 @@ impl<'a> Compiler<'a> {
ResolveOutcome::Var(VarKind::Global) => { ResolveOutcome::Var(VarKind::Global) => {
self.emit(I::StoreGlobal(Arg24::from_symbol(name))); 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))); self.emit(I::StoreGlobal(Arg24::from_symbol(name)));
} }
ResolveOutcome::None => { ResolveOutcome::None => {

View file

@ -10,31 +10,23 @@ pub type Result<T> = std::result::Result<T, Exception>;
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Exception { pub struct Exception {
pub ty: Symbol, pub ty: Symbol,
pub msg: Option<Rc<LStr>>, pub msg: Rc<LStr>,
} }
impl Exception { impl Exception {
pub fn new(ty: Symbol) -> Self { pub fn new(ty: Symbol, msg: Rc<LStr>) -> Self {
Self { ty, msg: None } Self { ty, msg }
}
pub fn new_with_msg(ty: Symbol, msg: Rc<LStr>) -> Self {
Self { ty, msg: Some(msg) }
} }
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> { pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
let table = table.borrow(); let table = table.borrow();
let ty = table.get(&(*SYM_TYPE).into())?; let ty = table.get(&(*SYM_TYPE).into())?;
if let Value::Symbol(ty) = ty { let msg = table.get(&(*SYM_MSG).into())?;
let msg = table.get(&(*SYM_MSG).into()); if let (Value::Symbol(ty), Value::String(msg)) = (ty, msg) {
match msg { Some(Self {
None => Some(Self { ty: *ty, msg: None }),
Some(Value::String(msg)) => Some(Self {
ty: *ty, ty: *ty,
msg: Some(msg.clone()), msg: msg.clone(),
}), })
Some(_) => None,
}
} else { } else {
None None
} }
@ -43,27 +35,21 @@ impl Exception {
pub fn to_table(self) -> Rc<RefCell<HashMap<HashValue, Value>>> { pub fn to_table(self) -> Rc<RefCell<HashMap<HashValue, Value>>> {
let mut table = HashMap::new(); let mut table = HashMap::new();
table.insert((*SYM_TYPE).into(), self.ty.into()); table.insert((*SYM_TYPE).into(), self.ty.into());
if let Some(msg) = self.msg { table.insert((*SYM_MSG).into(), Value::String(self.msg));
table.insert((*SYM_MSG).into(), Value::String(msg));
}
Rc::new(RefCell::new(table)) Rc::new(RefCell::new(table))
} }
} }
impl Display for Exception { impl Display for Exception {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(msg) = &self.msg { write!(f, "{}: {}", self.ty.name(), self.msg)
write!(f, "{}: {}", self.ty.name(), msg)
} else {
write!(f, "{}", self.ty.name())
}
} }
} }
#[macro_export] #[macro_export]
macro_rules! exception { macro_rules! exception {
($exc_ty:expr, $($t:tt)*) => { ($exc_ty:expr, $($t:tt)*) => {
$crate::exception::Exception::new_with_msg( $crate::exception::Exception::new(
$exc_ty, $exc_ty,
$crate::lstring::LString::from( $crate::lstring::LString::from(
format!($($t)*) format!($($t)*)
@ -73,9 +59,6 @@ macro_rules! exception {
($exc_ty:expr, $fstr:literal) => { ($exc_ty:expr, $fstr:literal) => {
$crate::exception::exception!($exc_ty, $fstr,) $crate::exception::exception!($exc_ty, $fstr,)
}; };
($exc_ty:expr) => {
$crate::exception::Exception::new($exc_ty)
};
} }
pub use exception; pub use exception;

View file

@ -205,6 +205,18 @@ impl<'s> Parser<'s> {
self.lexer.peek().unwrap().clone() self.lexer.peek().unwrap().clone()
} }
fn parse_symbol(tok: Token) -> Result<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(s)
}
fn parse_table_items(&mut self) -> Result<Vec<TableItem>> { fn parse_table_items(&mut self) -> Result<Vec<TableItem>> {
let mut items = Vec::new(); let mut items = Vec::new();
loop { loop {
@ -284,7 +296,7 @@ impl<'s> Parser<'s> {
fn parse_symbol_list(&mut self) -> Result<Vec<Symbol>> { fn parse_symbol_list(&mut self) -> Result<Vec<Symbol>> {
let mut syms = Vec::new(); let mut syms = Vec::new();
while let Some(tok) = try_next!(self, T::Symbol) { 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() { if try_next!(self, T::Comma).is_none() {
break break
} }
@ -420,16 +432,8 @@ impl<'s> Parser<'s> {
Ok(E::Literal(s.into()).span(tok.span)) Ok(E::Literal(s.into()).span(tok.span))
} }
T::Symbol => { T::Symbol => {
let inner = &tok.content[1..]; let s = Self::parse_symbol(tok)?;
let s = match inner.chars().next() { Ok(E::Literal(Value::from(s)).span(tok.span))
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))
} }
T::True => Ok(E::Literal(Value::Bool(true)).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)), T::False => Ok(E::Literal(Value::Bool(false)).span(tok.span)),
@ -531,6 +535,28 @@ impl<'s> Parser<'s> {
Ok(lhs) Ok(lhs)
} }
fn parse_and(&mut self) -> Result<Expr> {
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<Expr> {
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<Expr> { fn parse_lambda(&mut self) -> Result<Expr> {
let tok = try_next!(self, T::Backslash | T::Amper); let tok = try_next!(self, T::Backslash | T::Amper);
match tok { match tok {
@ -555,7 +581,7 @@ impl<'s> Parser<'s> {
let body_span = body.span; let body_span = body.span;
Ok(E::Lambda(args, b(body)).span(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!"), _ => unreachable!("parse_lambda: guaranteed by try_next!"),
} }
} }
@ -571,40 +597,8 @@ impl<'s> Parser<'s> {
Ok(lhs) Ok(lhs)
} }
fn parse_not(&mut self) -> Result<Expr> {
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<Expr> {
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<Expr> {
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<Expr> { fn parse_assign(&mut self) -> Result<Expr> {
let lhs = self.parse_or()?; let lhs = self.parse_pipeline()?;
let lhs_span = lhs.span; let lhs_span = lhs.span;
if let Some(op) = self.peek()?.kind.assign_op() { if let Some(op) = self.peek()?.kind.assign_op() {
let Some(lval) = LValue::from_expr(lhs) else { let Some(lval) = LValue::from_expr(lhs) else {

View file

@ -17,15 +17,19 @@ impl Value {
match (self, idx) { match (self, idx) {
(V::List(l), V::Int(i)) => { (V::List(l), V::Int(i)) => {
let l = l.borrow(); let l = l.borrow();
let Some(i) = i.to_usize() else { let Some(i) = i.to_isize() else {
throw!( throw!(
*SYM_INDEX_ERROR, *SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}", "index {i} out of bounds for list of length {}",
l.len() l.len()
) )
}; };
if i < l.len() { let mut j = i;
Ok(l[i].clone()) if j < 0 {
j += l.len() as isize;
}
if (0..l.len() as isize).contains(&j) {
Ok(l[j as usize].clone())
} else { } else {
throw!( throw!(
*SYM_INDEX_ERROR, *SYM_INDEX_ERROR,
@ -84,15 +88,19 @@ impl Value {
match (self, idx) { match (self, idx) {
(V::List(l), V::Int(i)) => { (V::List(l), V::Int(i)) => {
let mut l = l.borrow_mut(); let mut l = l.borrow_mut();
let Some(i) = i.to_usize() else { let Some(i) = i.to_isize() else {
throw!( throw!(
*SYM_INDEX_ERROR, *SYM_INDEX_ERROR,
"index {i} out of bounds for list of length {}", "index {i} out of bounds for list of length {}",
l.len() l.len()
) )
}; };
if i < l.len() { let mut j = i;
l[i] = val; if j < 0 {
j += l.len() as isize;
}
if (0..l.len() as isize).contains(&j) {
l[j as usize] = val;
Ok(()) Ok(())
} else { } else {
throw!( throw!(

View file

@ -28,15 +28,15 @@ impl Value {
Value::Bool(b) => *b, Value::Bool(b) => *b,
Value::Range(r) => !r.is_empty(), Value::Range(r) => !r.is_empty(),
Value::Int(n) => !n.is_zero(), 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::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::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::Cell(v) => v.borrow().truthy(),
Value::Symbol(_) Value::Symbol(_)
| Value::Table(_)
| Value::Function(_) | Value::Function(_)
| Value::NativeFunc(_) | Value::NativeFunc(_)
| Value::Native(_) => true, | Value::Native(_) => true,

View file

@ -258,7 +258,7 @@ impl Vm {
.interrupt .interrupt
.fetch_and(false, std::sync::atomic::Ordering::Relaxed) .fetch_and(false, std::sync::atomic::Ordering::Relaxed)
{ {
throw!(*SYM_INTERRUPTED) throw!(*SYM_INTERRUPTED, "interrupted")
} }
Ok(()) Ok(())
} }

View file

@ -2,6 +2,7 @@
name = "talc-macros" name = "talc-macros"
version = "0.2.1" version = "0.2.1"
edition = "2021" edition = "2021"
rust-version = "1.68.2"
[lib] [lib]
proc-macro = true proc-macro = true

View file

@ -2,6 +2,7 @@
name = "talc-std" name = "talc-std"
version = "0.2.2" version = "0.2.2"
edition = "2021" edition = "2021"
rust-version = "1.81.0"
[dependencies] [dependencies]
talc-lang = { path = "../talc-lang" } talc-lang = { path = "../talc-lang" }

View file

@ -8,27 +8,13 @@ use talc_macros::native_func;
use crate::unpack_args; use crate::unpack_args;
#[native_func(1)] #[native_func(2)]
pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn throw(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, arg] = unpack_args!(args); let [_, ty, msg] = unpack_args!(args);
let exc = match arg { let (Value::Symbol(ty), Value::String(msg)) = (ty, msg) else {
Value::Symbol(ty) => Exception::new(ty), throw!(*SYM_TYPE_ERROR, "throw expected symbol and string")
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"),
}; };
Err(exc) Err(Exception::new(ty, msg))
} }
#[native_func(1)] #[native_func(1)]
@ -38,9 +24,9 @@ pub fn rethrow(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if let Some(e) = Exception::from_table(&table) { if let Some(e) = Exception::from_table(&table) {
return Err(e) 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) { pub fn load(vm: &mut Vm) {

View file

@ -91,6 +91,7 @@ pub fn load(vm: &mut Vm) {
vm.set_global_name("arg", arg().into()); vm.set_global_name("arg", arg().into());
vm.set_global_name("abs", abs().into()); vm.set_global_name("abs", abs().into());
vm.set_global_name("abs_sq", abs_sq().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("sqrt", sqrt().into());
vm.set_global_name("cbrt", cbrt().into()); vm.set_global_name("cbrt", cbrt().into());
@ -516,6 +517,18 @@ pub fn abs_sq(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
} }
} }
#[native_func(1)]
pub fn conj(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
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 // continuous operations
// //