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]]
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",

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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