#![allow(
unknown_lints,
clippy::single_match,
clippy::needless_range_loop,
clippy::mutable_key_type,
clippy::match_like_matches_macro,
mismatched_lifetime_syntaxes
)]
#![warn(missing_docs)]
mod algorithm;
mod array;
mod assembly;
mod boxed;
mod check;
mod compile;
mod constant;
mod cowslice;
mod error;
mod ffi;
mod fill;
pub mod format;
mod function;
mod grid_fmt;
mod impl_prim;
pub mod lsp;
#[doc(hidden)]
pub mod profile;
mod run;
mod run_prim;
mod shape;
#[cfg(feature = "stand")]
#[doc(hidden)]
pub mod stand;
mod sys;
mod tree;
mod types;
mod value;
#[cfg(feature = "window")]
#[doc(hidden)]
pub mod window;
#[allow(unused_imports)]
pub use self::{
algorithm::{IgnoreError, media},
array::*,
assembly::*,
boxed::*,
compile::*,
constant::*,
error::*,
ffi::*,
function::*,
impl_prim::*,
lsp::{SpanKind, Spans},
run::*,
run_prim::*,
shape::*,
sys::*,
tree::*,
value::*,
};
#[doc(inline)]
pub use uiua_parser::*;
use self::algorithm::get_ops;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
pub type Ident = ecow::EcoString;
fn is_default<T: Default + PartialEq>(v: &T) -> bool {
v == &T::default()
}
const _: () = {
assert!(
size_of::<usize>() >= size_of::<u32>(),
"Code requires that word size be at least four bytes"
);
};
#[cfg(test)]
mod tests {
use std::{path::*, process::exit};
use uiua_parser::PrimClass;
use crate::{Compiler, Primitive, Uiua};
fn test_files(filter: impl Fn(&Path) -> bool) -> impl Iterator<Item = PathBuf> {
std::fs::read_dir("tests")
.unwrap()
.map(|entry| entry.unwrap().path())
.filter(move |path| {
path.is_file() && path.extension().is_some_and(|s| s == "ua") && filter(path)
})
}
#[test]
#[cfg(feature = "native_sys")]
fn suite() {
use super::*;
use std::thread;
let threads: Vec<_> = test_files(|path| {
!(path.file_stem().unwrap())
.to_string_lossy()
.contains("error")
})
.map(|path| {
thread::spawn(move || {
let code = std::fs::read_to_string(&path).unwrap();
let mut env = Uiua::with_native_sys();
let mut comp = Compiler::with_backend(NativeSys);
if let Err(e) = comp
.load_str_src(&code, &path)
.and_then(|comp| env.run_asm(comp.asm.clone()))
{
panic!("Test failed in {}:\n{}", path.display(), e.report());
}
if let Some(diag) = comp
.take_diagnostics()
.into_iter()
.find(|d| d.kind > DiagnosticKind::Advice)
{
panic!("Test failed in {}:\n{}", path.display(), diag.report());
}
let (stack, under_stack) = env.take_stacks();
if !stack.is_empty() {
panic!("{} had a non-empty stack", path.display());
}
if !under_stack.is_empty() {
panic!("{} had a non-empty under stack", path.display());
}
_ = Spans::from_input(&code);
})
})
.collect();
for thread in threads {
if let Err(e) = thread.join() {
if let Some(s) = e.downcast_ref::<String>() {
panic!("{s}");
} else {
panic!("{e:?}");
}
}
}
_ = std::fs::remove_file("example.ua");
}
#[test]
#[cfg(feature = "native_sys")]
fn error_dont_crash() {
use super::*;
let path = Path::new("tests_special/error.ua");
let mut code = std::fs::read_to_string(path).unwrap();
if code.contains('\r') {
code = code.replace('\r', "");
}
for section in code.split("\n\n") {
let mut env = Uiua::with_native_sys();
let mut comp = Compiler::new();
let res = comp
.load_str_src(section, path)
.and_then(|comp| env.run_asm(comp.finish()));
match res {
Ok(_) => {
if (comp.take_diagnostics().into_iter())
.filter(|diag| diag.kind > DiagnosticKind::Advice)
.count()
== 0
{
panic!(
"Test succeeded when it should have failed in {}:\n{}",
path.display(),
section
);
}
}
Err(e) => {
let message = e.to_string();
if message.contains("interpreter") {
panic!(
"Test resulted in an interpreter bug in {}:\n{}\n{}",
path.display(),
e.report(),
section
);
}
}
}
}
}
#[test]
#[cfg(feature = "native_sys")]
fn assembly_round_trip() {
use super::*;
let path = Path::new("tests_special/uasm.ua");
let mut comp = Compiler::with_backend(NativeSys);
comp.load_file(path).unwrap();
let asm = comp.finish();
let root = asm.root.clone();
let uasm = asm.to_uasm();
let asm = Assembly::from_uasm(&uasm).unwrap();
assert_eq!(asm.root, root);
let mut env = Uiua::with_native_sys();
env.run_asm(asm.clone()).unwrap();
env = Uiua::with_native_sys();
env.run_asm(asm).unwrap();
}
#[test]
fn lsp_spans() {
use super::*;
for path in test_files(|_| true) {
let code = std::fs::read_to_string(&path).unwrap();
Spans::from_input(&code);
}
}
#[test]
fn no_dbgs() {
if crate::compile::invert::DEBUG {
panic!("compile::invert::DEBUG is true");
}
if crate::compile::optimize::DEBUG {
panic!("compile::optimize::DEBUG is true");
}
if crate::compile::algebra::DEBUG {
panic!("compile::algebra::DEBUG is true");
}
}
#[test]
fn external_bind_before() {
let mut comp = Compiler::new();
comp.create_bind_function("F", (2, 1), |env| {
let a = env.pop_num().unwrap();
let b = env.pop_num().unwrap();
env.push(a + b);
Ok(())
})
.unwrap();
comp.load_str(
"F = |2 # External!\n\
F 1 2",
)
.unwrap();
let mut env = Uiua::with_native_sys();
env.run_compiler(&mut comp)
.unwrap_or_else(|e| panic!("{e}"));
let res = env.pop_int().unwrap();
assert_eq!(res, 3);
}
#[test]
fn external_bind_after() {
let mut comp = Compiler::new();
comp.load_str(
"F = |2 # External!\n\
F 1 2",
)
.unwrap();
comp.create_bind_function("F", (2, 1), |env| {
let a = env.pop_num().unwrap();
let b = env.pop_num().unwrap();
env.push(a + b);
Ok(())
})
.unwrap();
let mut env = Uiua::with_native_sys();
env.run_compiler(&mut comp)
.unwrap_or_else(|e| panic!("{e}"));
let res = env.pop_int().unwrap();
assert_eq!(res, 3);
}
#[test]
#[ignore] fn fuzz() {
let iter = Primitive::non_deprecated().filter(|p| !matches!(p, Primitive::Sys(_)));
let arg_strs: Vec<_> = ((0..3).map(|n| {
((0..=6).map(|i| {
let mut s = String::new();
for j in 0..i {
if j > 0 {
s.push(' ');
}
s.push('[');
for k in 0..n {
if k > 0 {
s.push(' ');
}
s.push_str(&(j * n + k).to_string());
}
s.push(']');
}
s
}))
.collect::<Vec<_>>()
}))
.collect();
for needs_name in [false, true] {
for a in
(iter.clone()).filter(|p| p.class() != PrimClass::Arguments || p.sig().is_none())
{
for b in iter.clone() {
for c in iter.clone() {
if a.glyph().is_none()
|| b.glyph().is_none()
|| c.glyph().is_none() != needs_name
{
continue;
}
if [a, c] == [Primitive::Repeat, Primitive::Infinity]
|| [a, b] == [Primitive::Un, Primitive::Repeat]
|| a == Primitive::Do
{
continue;
}
let arg_count = (a.sig().zip(b.sig()).zip(c.sig()))
.map(|((a, b), c)| c.compose(b.compose(a)).args())
.unwrap_or(4);
for args in &arg_strs {
let args = &args[arg_count];
let code = format!("{a}{b}{c} {args}");
eprintln!("{code}");
if let Err(e) = Uiua::with_safe_sys().run_str(&code)
&& e.to_string().contains("The interpreter has crashed!")
{
exit(1);
}
}
}
}
}
}
}
}