fixes
All checks were successful
docs / test (push) Successful in 13s
webrepl / test (push) Successful in 3m58s

This commit is contained in:
trimill 2025-01-18 19:06:58 -05:00
parent a3001106ab
commit e7716d038e
25 changed files with 107 additions and 364 deletions

70
Cargo.lock generated
View file

@ -25,9 +25,9 @@ checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.6.0" version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36"
[[package]] [[package]]
name = "bumpalo" name = "bumpalo"
@ -55,9 +55,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
[[package]] [[package]]
name = "clap" name = "clap"
version = "4.5.23" version = "4.5.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3135e7ec2ef7b10c6ed8950f0f792ed96ee093fa088608f1c76e569722700c84" checksum = "a8eb5e908ef3a6efbe1ed62520fb7287959888c88485abe072543190ecc66783"
dependencies = [ dependencies = [
"clap_builder", "clap_builder",
"clap_derive", "clap_derive",
@ -65,9 +65,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_builder" name = "clap_builder"
version = "4.5.23" version = "4.5.26"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30582fc632330df2bd26877bde0c1f4470d57c582bbc070376afcd04d8cb4838" checksum = "96b01801b5fc6a0a232407abc821660c9c6d25a1cafc0d4f85f29fb8d9afc121"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"clap_lex", "clap_lex",
@ -75,9 +75,9 @@ dependencies = [
[[package]] [[package]]
name = "clap_derive" name = "clap_derive"
version = "4.5.18" version = "4.5.24"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ac6a0c7b1a9e9a5186361f67dfa1b88213572f427fb9ab038efb2bd8c582dab" checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
dependencies = [ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
@ -173,9 +173,9 @@ dependencies = [
[[package]] [[package]]
name = "js-sys" name = "js-sys"
version = "0.3.76" version = "0.3.77"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
dependencies = [ dependencies = [
"once_cell", "once_cell",
"wasm-bindgen", "wasm-bindgen",
@ -195,15 +195,15 @@ checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.4.14" version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.22" version = "0.4.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
[[package]] [[package]]
name = "memchr" name = "memchr"
@ -323,9 +323,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro2" name = "proc-macro2"
version = "1.0.92" version = "1.0.93"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99"
dependencies = [ dependencies = [
"unicode-ident", "unicode-ident",
] ]
@ -410,9 +410,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "0.38.42" version = "0.38.43"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" checksum = "a78891ee6bf2340288408954ac787aa063d8e8817e9f53abb37c695c6d834ef6"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
@ -421,6 +421,12 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "rustversion"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4"
[[package]] [[package]]
name = "rustyline" name = "rustyline"
version = "15.0.0" version = "15.0.0"
@ -451,9 +457,9 @@ checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
[[package]] [[package]]
name = "syn" name = "syn"
version = "2.0.95" version = "2.0.96"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46f71c0377baf4ef1cc3e3402ded576dccc315800fbc62dfc7fe04b009773b4a" checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -543,20 +549,21 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.99" version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"once_cell", "once_cell",
"rustversion",
"wasm-bindgen-macro", "wasm-bindgen-macro",
] ]
[[package]] [[package]]
name = "wasm-bindgen-backend" name = "wasm-bindgen-backend"
version = "0.2.99" version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
dependencies = [ dependencies = [
"bumpalo", "bumpalo",
"log", "log",
@ -568,9 +575,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro" name = "wasm-bindgen-macro"
version = "0.2.99" version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
dependencies = [ dependencies = [
"quote", "quote",
"wasm-bindgen-macro-support", "wasm-bindgen-macro-support",
@ -578,9 +585,9 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-macro-support" name = "wasm-bindgen-macro-support"
version = "0.2.99" version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@ -591,9 +598,12 @@ dependencies = [
[[package]] [[package]]
name = "wasm-bindgen-shared" name = "wasm-bindgen-shared"
version = "0.2.99" version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
dependencies = [
"unicode-ident",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"

View file

@ -2,7 +2,6 @@
members = ["talc-lang", "talc-bin", "talc-std", "talc-macros", "talc-web"] members = ["talc-lang", "talc-bin", "talc-std", "talc-macros", "talc-web"]
resolver = "2" resolver = "2"
[workspace.lints.clippy] [workspace.lints.clippy]
semicolon_if_nothing_returned = "warn" semicolon_if_nothing_returned = "warn"
allow_attributes = "warn" allow_attributes = "warn"
@ -18,6 +17,8 @@ manual_assert = "warn"
needless_pass_by_value = "warn" needless_pass_by_value = "warn"
unnested_or_patterns = "warn" unnested_or_patterns = "warn"
redundant_else = "warn" redundant_else = "warn"
cast_possible_wrap = "warn"
cast_possible_truncation = "warn"
[profile.release-opt] [profile.release-opt]

View file

@ -14,7 +14,7 @@ path = "src/main.rs"
[dependencies] [dependencies]
talc-lang = { path = "../talc-lang" } talc-lang = { path = "../talc-lang" }
talc-std = { path = "../talc-std" } talc-std = { path = "../talc-std" }
rustyline = "15.0" rustyline = "15"
clap = { version = "4.5", features = ["std", "help", "usage", "derive", "error-context"], default-features = false } clap = { version = "4", features = ["std", "help", "usage", "derive", "error-context"], default-features = false }
ctrlc = "3.4" ctrlc = "3"
lazy_static = "1.5" lazy_static = "1"

View file

@ -53,7 +53,7 @@ impl Completer for TalcHelper {
.map(LStr::to_string) .map(LStr::to_string)
.collect::<Vec<String>>(); .collect::<Vec<String>>();
keys.sort(); keys.sort();
Ok((pos - res.as_bytes().len(), keys)) Ok((pos - res.len(), keys))
} }
} }

View file

@ -6,7 +6,6 @@ use talc_lang::{
optimize::optimize, optimize::optimize,
parser, parser,
prelude::*, prelude::*,
serial,
symbol::Symbol, symbol::Symbol,
value::{function::disasm_recursive, Value}, value::{function::disasm_recursive, Value},
vm::Vm, vm::Vm,
@ -30,10 +29,6 @@ struct Args {
#[arg(short = 'a', long)] #[arg(short = 'a', long)]
show_ast: bool, show_ast: bool,
/// Compile to a bytecode file
#[arg(short, long)]
compile: Option<PathBuf>,
/// Don't apply optimizations /// Don't apply optimizations
#[arg(long)] #[arg(long)]
no_opt: bool, no_opt: bool,
@ -86,26 +81,6 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
} }
} }
if let Some(path) = &args.compile {
let f = std::fs::File::options()
.write(true)
.truncate(true)
.create(true)
.open(path);
let mut w = match f {
Ok(f) => f,
Err(e) => {
eprintln!("Error opening output file: {e}");
return ExitCode::FAILURE
}
};
if let Err(e) = serial::write_program(&mut w, &func) {
eprintln!("Error writing bytecode: {e}");
return ExitCode::FAILURE
}
return ExitCode::SUCCESS
}
let res = vm.run_function(func.clone(), vec![func.into()]); let res = vm.run_function(func.clone(), vec![func.into()]);
match res { match res {
@ -114,7 +89,7 @@ fn exec(name: Symbol, src: &str, args: &Args) -> ExitCode {
ExitCode::FAILURE ExitCode::FAILURE
} }
Ok(Value::Bool(false)) => ExitCode::FAILURE, Ok(Value::Bool(false)) => ExitCode::FAILURE,
Ok(Value::Int(n)) => ExitCode::from(n.to_u64().unwrap_or(1) as u8), Ok(Value::Int(n)) => ExitCode::from(n.to_u64().unwrap_or(1).to_le_bytes()[0]),
_ => ExitCode::SUCCESS, _ => ExitCode::SUCCESS,
} }
} }
@ -123,10 +98,6 @@ fn main() -> ExitCode {
let args = Args::parse(); let args = Args::parse();
if args.repl || args.args.is_empty() { if args.repl || args.args.is_empty() {
if args.compile.is_some() {
eprintln!("Error: cannot compile in REPL mode");
return ExitCode::FAILURE
}
return repl::repl(&args) return repl::repl(&args)
} }

View file

@ -9,5 +9,5 @@ workspace = true
[dependencies] [dependencies]
num = "0.4" num = "0.4"
lazy_static = "1.5" lazy_static = "1"
unicode-ident = "1.0" unicode-ident = "1"

View file

@ -71,6 +71,7 @@ impl From<Arg24> for usize {
impl From<Arg24> for i32 { impl From<Arg24> for i32 {
#[inline] #[inline]
#[expect(clippy::cast_possible_wrap)]
fn from(v: Arg24) -> Self { fn from(v: Arg24) -> Self {
let mut n = u32::from(v); let mut n = u32::from(v);
// sign-extend // sign-extend
@ -252,7 +253,7 @@ impl Chunk {
} }
pub fn set_line(&mut self, line: u32) { pub fn set_line(&mut self, line: u32) {
let instr = self.instrs.len() as u32; let instr = u32::try_from(self.instrs.len()).expect("chunk too big");
if let Some((i, l)) = self.lines.last_mut() { if let Some((i, l)) = self.lines.last_mut() {
if *i == instr { if *i == instr {
*l = line; *l = line;

View file

@ -483,20 +483,29 @@ impl<'a> Compiler<'a> {
self.emit(I::Index); self.emit(I::Index);
} }
ExprKind::FnCall(f, args) => { ExprKind::FnCall(f, args) => {
let Ok(argc) = u8::try_from(args.len()) else {
throw!(span.start, "too many arguments to function (max 255)")
};
self.expr(f)?; self.expr(f)?;
for a in args { for a in args {
self.expr(a)?; self.expr(a)?;
} }
self.emit(I::Call(args.len() as u8)); self.emit(I::Call(argc));
} }
ExprKind::TailCall(f, args) => { ExprKind::TailCall(f, args) => {
let Ok(argc) = u8::try_from(args.len()) else {
throw!(span.start, "too many arguments to function (max 255)")
};
self.expr(f)?; self.expr(f)?;
for a in args { for a in args {
self.expr(a)?; self.expr(a)?;
} }
self.emit(I::Tail(args.len() as u8)); self.emit(I::Tail(argc));
} }
ExprKind::AssocFnCall(o, f, args) => { ExprKind::AssocFnCall(o, f, args) => {
let Ok(argc) = u8::try_from(args.len() + 1) else {
throw!(span.start, "too many arguments to function (max 254)")
};
self.expr(o)?; self.expr(o)?;
self.emit(I::Dup); self.emit(I::Dup);
self.emit(I::Symbol(Arg24::from_symbol(*f))); self.emit(I::Symbol(Arg24::from_symbol(*f)));
@ -505,9 +514,12 @@ impl<'a> Compiler<'a> {
for a in args { for a in args {
self.expr(a)?; self.expr(a)?;
} }
self.emit(I::Call((args.len() + 1) as u8)); self.emit(I::Call(argc));
} }
ExprKind::AssocTailCall(o, f, args) => { ExprKind::AssocTailCall(o, f, args) => {
let Ok(argc) = u8::try_from(args.len() + 1) else {
throw!(span.start, "too many arguments to function (max 254)")
};
self.expr(o)?; self.expr(o)?;
self.emit(I::Dup); self.emit(I::Dup);
self.emit(I::Symbol(Arg24::from_symbol(*f))); self.emit(I::Symbol(Arg24::from_symbol(*f)));
@ -516,7 +528,7 @@ impl<'a> Compiler<'a> {
for a in args { for a in args {
self.expr(a)?; self.expr(a)?;
} }
self.emit(I::Tail((args.len() + 1) as u8)); self.emit(I::Tail(argc));
} }
ExprKind::Return(e) => { ExprKind::Return(e) => {
if let Some(e) = e { if let Some(e) = e {
@ -587,15 +599,15 @@ impl<'a> Compiler<'a> {
self.emit(I::NewList(0)); self.emit(I::NewList(0));
return Ok(()) return Ok(())
} }
fn finish_chunk(c: &mut Compiler, len: &mut usize, first: &mut bool) { fn finish_chunk(c: &mut Compiler, len: &mut u8, first: &mut bool) {
if *first { if *first {
c.emit(I::NewList(*len as u8)); c.emit(I::NewList(*len));
*first = false; *first = false;
} else { } else {
if *len == 0 { if *len == 0 {
return return
} }
c.emit(I::GrowList(*len as u8)); c.emit(I::GrowList(*len));
} }
*len = 0; *len = 0;
} }
@ -625,15 +637,15 @@ impl<'a> Compiler<'a> {
self.emit(I::NewTable(0)); self.emit(I::NewTable(0));
return Ok(()) return Ok(())
} }
fn finish_chunk(c: &mut Compiler, len: &mut usize, first: &mut bool) { fn finish_chunk(c: &mut Compiler, len: &mut u8, first: &mut bool) {
if *first { if *first {
c.emit(I::NewTable(*len as u8)); c.emit(I::NewTable(*len));
*first = false; *first = false;
} else { } else {
if *len == 0 { if *len == 0 {
return return
} }
c.emit(I::GrowTable(*len as u8)); c.emit(I::GrowTable(*len));
} }
*len = 0; *len = 0;
} }

View file

@ -6,7 +6,6 @@ pub mod number;
pub mod ops; pub mod ops;
pub mod optimize; pub mod optimize;
pub mod parser; pub mod parser;
pub mod serial;
pub mod symbol; pub mod symbol;
pub mod value; pub mod value;
pub mod vm; pub mod vm;

View file

@ -32,7 +32,7 @@ impl Pos {
pub fn advance(self, c: char) -> Pos { pub fn advance(self, c: char) -> Pos {
let idx = self let idx = self
.idx .idx
.checked_add(c.len_utf8() as u32) .checked_add(u32::try_from(c.len_utf8()).expect("character too long??"))
.expect("source file contains more than u32::MAX chars"); .expect("source file contains more than u32::MAX chars");
if c == '\n' { if c == '\n' {
Pos { Pos {

View file

@ -101,7 +101,7 @@ pub fn parse_str_escapes(src: &str) -> Result<LString, StrEscapeError> {
let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?; let n1 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
let c = chars.next().ok_or(StrEscapeError::HexEof)?; let c = chars.next().ok_or(StrEscapeError::HexEof)?;
let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?; let n2 = c.to_digit(16).ok_or(StrEscapeError::InvalidHex(c))?;
s.push_byte((n1 * 16 + n2) as u8); s.push_byte(u8::try_from(n1 * 16 + n2).expect("hex too large?"));
} }
'u' => { 'u' => {
let Some('{') = chars.next() else { let Some('{') = chars.next() else {

View file

@ -1,251 +0,0 @@
use std::fmt;
use std::io::{self, Write};
use num::{bigint::Sign, BigInt};
use crate::{
chunk::{Chunk, Instruction, TryTable},
lstring::LStr,
number::Int,
prelude::*,
symbol::Symbol,
value::{function::Function, Value},
};
const MAGIC: [u8; 8] = *b"\x7fTALC\0\0\0";
const VERSION: u32 = 1;
#[derive(Debug)]
pub enum SerialError {
Io(io::Error),
Serial(String),
}
impl std::error::Error for SerialError {}
impl fmt::Display for SerialError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SerialError::Io(e) => e.fmt(f),
SerialError::Serial(e) => e.fmt(f),
}
}
}
type Result<T> = std::result::Result<T, SerialError>;
impl From<io::Error> for SerialError {
fn from(value: io::Error) -> Self {
Self::Io(value)
}
}
impl From<String> for SerialError {
fn from(value: String) -> Self {
Self::Serial(value)
}
}
impl From<&str> for SerialError {
fn from(value: &str) -> Self {
Self::Serial(value.to_owned())
}
}
pub fn write_program(w: &mut impl Write, prog: &Function) -> Result<()> {
ProgramWriter::new(w).write_program(prog)
}
struct ProgramWriter<'w, W: Write> {
w: &'w mut W,
}
impl<'w, W: Write> ProgramWriter<'w, W> {
fn new(w: &'w mut W) -> Self {
Self { w }
}
fn write_program(&mut self, prog: &Function) -> Result<()> {
self.w.write_all(&MAGIC)?;
self.w.write_all(&VERSION.to_le_bytes())?;
self.write_function(prog)?;
Ok(())
}
fn write_function(&mut self, func: &Function) -> Result<()> {
self.write_bool(func.attrs.name.is_some())?;
if let Some(name) = func.attrs.name {
self.write_sym(name)?;
}
self.write_u32(func.attrs.arity as u32)?;
self.write_u32(func.state.len() as u32)?;
self.write_chunk(&func.chunk)?;
Ok(())
}
fn write_chunk(&mut self, chunk: &Chunk) -> Result<()> {
self.write_u32(chunk.consts.len() as u32)?;
self.write_u32(chunk.instrs.len() as u32)?;
self.write_u32(chunk.try_tables.len() as u32)?;
for c in &chunk.consts {
self.write_value(c)?;
}
for i in &chunk.instrs {
self.write_instr(*i)?;
}
for t in &chunk.try_tables {
self.write_try_table(t)?;
}
Ok(())
}
fn write_value(&mut self, val: &Value) -> Result<()> {
match val {
Value::Nil => self.write_u8(0),
Value::Bool(b) => {
self.write_u8(1)?;
self.write_bool(*b)
}
Value::Symbol(sym) => {
self.write_u8(2)?;
self.write_sym(*sym)
}
Value::Int(n) => {
self.write_u8(3)?;
self.write_int(n.clone())
}
Value::Ratio(r) => {
self.write_u8(4)?;
self.write_int(Int::from(r.numer().clone()))?;
self.write_int(Int::from(r.denom().clone()))
}
Value::Float(f) => {
self.write_u8(5)?;
self.write_f64(*f)
}
Value::Complex(z) => {
self.write_u8(6)?;
self.write_f64(z.re)?;
self.write_f64(z.im)
}
Value::Range(r) => {
self.write_u8(7)?;
if let Some(s) = &r.start {
self.write_u8(1)?;
self.write_int(s.into())?;
} else {
self.write_u8(0)?;
}
if let Some(e) = &r.end {
self.write_u8(1)?;
self.write_int(e.into())?;
} else {
self.write_u8(0)?;
}
self.write_bool(r.inclusive)
}
Value::String(s) => {
self.write_u8(8)?;
self.write_str(s)
}
Value::Function(func) => {
self.write_u8(9)?;
self.write_function(func)
}
Value::Cell(_)
| Value::List(_)
| Value::Table(_)
| Value::NativeFunc(_)
| Value::Native(_) => Err(format!(
"cannot serialize value of type {}",
val.get_type().name()
)
.into()),
}
}
fn write_instr(&mut self, i: Instruction) -> Result<()> {
let n: u32 = unsafe { std::mem::transmute(i) };
self.write_u32(n)
}
fn write_try_table(&mut self, t: &TryTable) -> Result<()> {
self.write_u32(t.local_count as u32)?;
self.write_u32(t.catches.len() as u32)?;
for catch in &t.catches {
self.write_u32(catch.addr as u32)?;
self.write_bool(catch.types.is_some())?;
if let Some(tys) = &catch.types {
self.write_u32(tys.len() as u32)?;
for ty in tys {
self.write_sym(*ty)?;
}
}
}
Ok(())
}
fn write_sym(&mut self, sym: Symbol) -> Result<()> {
self.write_str(sym.name())
}
fn write_str(&mut self, s: &LStr) -> Result<()> {
let Ok(n) = s.len().try_into() else {
return Err("string to long to serialize".into())
};
self.write_u32(n)?;
self.w.write_all(s.as_bytes())?;
Ok(())
}
fn write_f64(&mut self, x: f64) -> Result<()> {
self.w.write_all(&x.to_le_bytes())?;
Ok(())
}
fn write_int(&mut self, n: Int) -> Result<()> {
if let Some(i) = n.to_i64() {
self.write_u8(0x80)?;
self.write_i64(i)
} else {
let big = BigInt::from(n);
let (sign, digits) = big.to_u64_digits();
match sign {
Sign::Minus => self.write_u8(0xff)?,
Sign::NoSign => self.write_u8(0x00)?,
Sign::Plus => self.write_u8(0x01)?,
}
self.write_u32(digits.len() as u32)?;
for digit in digits {
self.write_u64(digit)?;
}
Ok(())
}
}
fn write_i64(&mut self, n: i64) -> Result<()> {
self.w.write_all(&n.to_le_bytes())?;
Ok(())
}
fn write_u64(&mut self, n: u64) -> Result<()> {
self.w.write_all(&n.to_le_bytes())?;
Ok(())
}
fn write_u32(&mut self, n: u32) -> Result<()> {
self.w.write_all(&n.to_le_bytes())?;
Ok(())
}
fn write_u8(&mut self, n: u8) -> Result<()> {
self.w.write_all(&[n])?;
Ok(())
}
fn write_bool(&mut self, n: bool) -> Result<()> {
self.w.write_all(&[n as u8])?;
Ok(())
}
}

View file

@ -76,7 +76,7 @@ impl Symbol {
let symno = table.names.len(); let symno = table.names.len();
assert!(symno <= MAX_SYMBOL, "too many symbols"); assert!(symno <= MAX_SYMBOL, "too many symbols");
let sym = Symbol(symno as u32); let sym = Symbol(u32::try_from(symno).expect("too many symbols"));
let name = LString::leak(name.to_owned()); let name = LString::leak(name.to_owned());
table.names.push(name); table.names.push(name);

View file

@ -25,11 +25,12 @@ impl Value {
) )
}; };
let mut j = i; let mut j = i;
let len = isize::try_from(l.len()).expect("list too big");
if j < 0 { if j < 0 {
j += l.len() as isize; j += len;
} }
if (0..l.len() as isize).contains(&j) { if (0..len).contains(&j) {
Ok(l[j as usize].clone()) Ok(l[usize::try_from(j).expect("checked not negative")].clone())
} else { } else {
throw!( throw!(
*SYM_INDEX_ERROR, *SYM_INDEX_ERROR,
@ -96,11 +97,12 @@ impl Value {
) )
}; };
let mut j = i; let mut j = i;
let len = isize::try_from(l.len()).expect("list too big");
if j < 0 { if j < 0 {
j += l.len() as isize; j += len;
} }
if (0..l.len() as isize).contains(&j) { if (0..len).contains(&j) {
l[j as usize] = val; l[usize::try_from(j).expect("checked not negative")] = val;
Ok(()) Ok(())
} else { } else {
throw!( throw!(

View file

@ -218,6 +218,7 @@ impl Vm {
mut exc: Exception, mut exc: Exception,
) -> Result<()> { ) -> Result<()> {
loop { loop {
#[expect(clippy::cast_possible_truncation)]
let line = frame.func.chunk.get_line(frame.ip as u32); let line = frame.func.chunk.get_line(frame.ip as u32);
exc.add_trace(line, frame.func.attrs.name); exc.add_trace(line, frame.func.attrs.name);
while let Some(try_frame) = frame.try_frames.pop() { while let Some(try_frame) = frame.try_frames.pop() {

View file

@ -11,5 +11,5 @@ workspace = true
proc-macro = true proc-macro = true
[dependencies] [dependencies]
syn = { version = "2.0", features = ["full"] } syn = { version = "2", features = ["full"] }
quote = "1.0" quote = "1"

View file

@ -10,9 +10,9 @@ workspace = true
[dependencies] [dependencies]
talc-lang = { path = "../talc-lang" } talc-lang = { path = "../talc-lang" }
talc-macros = { path = "../talc-macros" } talc-macros = { path = "../talc-macros" }
lazy_static = "1.5" lazy_static = "1"
num-bigint = { version = "0.4", features = ["rand"], optional = true } num-bigint = { version = "0.4", features = ["rand"], optional = true }
regex = { version = "1.11", optional = true } regex = { version = "1", optional = true }
rand = { version = "0.8", optional = true } rand = { version = "0.8", optional = true }
[features] [features]

View file

@ -502,10 +502,10 @@ pub fn popcnt(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if n.is_negative() { if n.is_negative() {
// count_zeros always succeeds on negative numbers // count_zeros always succeeds on negative numbers
let z = n.count_zeros().unwrap(); let z = n.count_zeros().unwrap();
Ok(Value::from(-(z as i64))) Ok(Value::from(-Int::from(z)))
} else { } else {
// count_ones always succeeds on nonnegative numbers // count_ones always succeeds on nonnegative numbers
let z = n.count_ones().unwrap(); let z = n.count_ones().unwrap();
Ok(Value::from(z as i64)) Ok(Value::from(z))
} }
} }

View file

@ -95,11 +95,7 @@ pub fn time_ns(_: &mut Vm, _: Vec<Value>) -> Result<Value> {
let time = SystemTime::now() let time = SystemTime::now()
.duration_since(UNIX_EPOCH) .duration_since(UNIX_EPOCH)
.expect("time went backwards"); .expect("time went backwards");
Ok(vec![ Ok(vec![time.as_secs().into(), time.subsec_nanos().into()].into())
(time.as_secs() as i64).into(),
(time.subsec_nanos() as i64).into(),
]
.into())
} }
#[native_func(1)] #[native_func(1)]
@ -210,7 +206,7 @@ pub fn arg(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
#[native_func(0)] #[native_func(0)]
pub fn argc(vm: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn argc(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_] = unpack_args!(args); let [_] = unpack_args!(args);
Ok(Value::from(vm.args().len() as i64)) Ok(Value::from(vm.args().len()))
} }
#[native_func(0)] #[native_func(0)]

View file

@ -647,9 +647,9 @@ pub fn table(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> { pub fn len(vm: &mut Vm, args: Vec<Value>) -> Result<Value> {
let [_, value] = unpack_args!(args); let [_, value] = unpack_args!(args);
match value { match value {
Value::String(s) => return Ok((s.chars().count() as i64).into()), Value::String(s) => return Ok((s.chars().count()).into()),
Value::List(l) => return Ok((l.borrow().len() as i64).into()), Value::List(l) => return Ok((l.borrow().len()).into()),
Value::Table(t) => return Ok((t.borrow().len() as i64).into()), Value::Table(t) => return Ok((t.borrow().len()).into()),
Value::Range(ref r) => { Value::Range(ref r) => {
if let Some(len) = r.len() { if let Some(len) = r.len() {
return Ok(len.into()) return Ok(len.into())

View file

@ -76,8 +76,8 @@ pub fn load(vm: &mut Vm) {
fn match_to_value(m: Match) -> Value { fn match_to_value(m: Match) -> Value {
Value::new_table(|t| { Value::new_table(|t| {
t.insert((*SYM_START).into(), (m.start() as i64).into()); t.insert((*SYM_START).into(), m.start().into());
t.insert((*SYM_END).into(), (m.end() as i64).into()); t.insert((*SYM_END).into(), m.end().into());
t.insert( t.insert(
(*SYM_STR).into(), (*SYM_STR).into(),
LString::from(m.as_str().to_string()).into(), LString::from(m.as_str().to_string()).into(),

View file

@ -41,7 +41,7 @@ pub fn ord(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
if chars.next().is_some() { if chars.next().is_some() {
throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1") throw!(*SYM_VALUE_ERROR, "argument to ord must have length 1")
}; };
Ok(Value::from(c as u32 as i64)) Ok(Value::from(c as u32))
} }
#[native_func(1)] #[native_func(1)]
@ -68,7 +68,7 @@ pub fn len_bytes(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
"len_bytes expected string argument, got {s:#}" "len_bytes expected string argument, got {s:#}"
) )
}; };
Ok(Value::from(s.len() as i64)) Ok(Value::from(s.len()))
} }
#[native_func(1)] #[native_func(1)]

View file

@ -275,7 +275,7 @@ pub fn func_arity(_: &mut Vm, args: Vec<Value>) -> Result<Value> {
let Some(attrs) = func.func_attrs() else { let Some(attrs) = func.func_attrs() else {
throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function") throw!(*SYM_TYPE_ERROR, "closure_state: {func:#} is not a function")
}; };
Ok((attrs.arity as i64).into()) Ok(attrs.arity.into())
} }
#[native_func(1)] #[native_func(1)]

View file

@ -2,6 +2,7 @@
name = "talc-web" name = "talc-web"
version = "0.1.0" version = "0.1.0"
edition = "2021" edition = "2021"
rust-version = "1.81.0"
[lib] [lib]
crate-type = ["cdylib", "rlib"] crate-type = ["cdylib", "rlib"]

View file

@ -119,15 +119,15 @@ function handleCtrlKey(event) {
} }
function keyPressed(event) { function keyPressed(event) {
if (event.key === "Enter" && !event.shiftKey) { if (event.key === "Enter" && !event.shiftKey && !event.altKey) {
event.preventDefault(); event.preventDefault();
execLine() execLine()
} else if (event.ctrlKey) { } else if (event.ctrlKey && !event.altKey) {
handleCtrlKey(event); handleCtrlKey(event);
} else if (event.key === "ArrowUp") { } else if (event.key === "ArrowUp" && !event.altKey) {
event.preventDefault(); event.preventDefault();
prevHistory(); prevHistory();
} else if (event.key === "ArrowDown") { } else if (event.key === "ArrowDown" && !event.altKey) {
event.preventDefault(); event.preventDefault();
nextHistory(); nextHistory();
} }