bugfixes
This commit is contained in:
parent
2a8a7b347e
commit
88c99fbac1
13 changed files with 117 additions and 110 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
name = "talc-bin"
|
||||
version = "0.2.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.81.0"
|
||||
|
||||
[[bin]]
|
||||
name = "talc"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
name = "talc-lang"
|
||||
version = "0.2.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.81.0"
|
||||
|
||||
[dependencies]
|
||||
num = { version = "0.4", features = [] }
|
||||
|
|
|
@ -69,6 +69,7 @@ struct Compiler<'a> {
|
|||
attrs: FuncAttrs,
|
||||
// variables
|
||||
scope: HashMap<Symbol, VarKind>,
|
||||
scope_count: usize,
|
||||
shadowed: Vec<(Symbol, Option<VarKind>)>,
|
||||
closes: Vec<(Symbol, 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> {
|
||||
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 => {
|
||||
|
|
|
@ -10,31 +10,23 @@ pub type Result<T> = std::result::Result<T, Exception>;
|
|||
#[derive(Clone, Debug)]
|
||||
pub struct Exception {
|
||||
pub ty: Symbol,
|
||||
pub msg: Option<Rc<LStr>>,
|
||||
pub msg: Rc<LStr>,
|
||||
}
|
||||
|
||||
impl Exception {
|
||||
pub fn new(ty: Symbol) -> Self {
|
||||
Self { ty, msg: None }
|
||||
}
|
||||
|
||||
pub fn new_with_msg(ty: Symbol, msg: Rc<LStr>) -> Self {
|
||||
Self { ty, msg: Some(msg) }
|
||||
pub fn new(ty: Symbol, msg: Rc<LStr>) -> Self {
|
||||
Self { ty, msg }
|
||||
}
|
||||
|
||||
pub fn from_table(table: &Rc<RefCell<HashMap<HashValue, Value>>>) -> Option<Self> {
|
||||
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<RefCell<HashMap<HashValue, Value>>> {
|
||||
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;
|
||||
|
|
|
@ -205,6 +205,18 @@ impl<'s> Parser<'s> {
|
|||
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>> {
|
||||
let mut items = Vec::new();
|
||||
loop {
|
||||
|
@ -284,7 +296,7 @@ impl<'s> Parser<'s> {
|
|||
fn parse_symbol_list(&mut self) -> Result<Vec<Symbol>> {
|
||||
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<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> {
|
||||
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<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> {
|
||||
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 {
|
||||
|
|
|
@ -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!(
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -258,7 +258,7 @@ impl Vm {
|
|||
.interrupt
|
||||
.fetch_and(false, std::sync::atomic::Ordering::Relaxed)
|
||||
{
|
||||
throw!(*SYM_INTERRUPTED)
|
||||
throw!(*SYM_INTERRUPTED, "interrupted")
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
name = "talc-macros"
|
||||
version = "0.2.1"
|
||||
edition = "2021"
|
||||
rust-version = "1.68.2"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
name = "talc-std"
|
||||
version = "0.2.2"
|
||||
edition = "2021"
|
||||
rust-version = "1.81.0"
|
||||
|
||||
[dependencies]
|
||||
talc-lang = { path = "../talc-lang" }
|
||||
|
|
|
@ -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<Value>) -> Result<Value> {
|
||||
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<Value>) -> Result<Value> {
|
|||
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) {
|
||||
|
|
|
@ -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<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
|
||||
//
|
||||
|
|
Loading…
Reference in a new issue