pub mod cast;
pub mod cvt;
pub mod error;
pub mod numeric;
pub mod opcodes;
pub mod postfix;
pub mod random;
pub mod scanf;
pub mod strtod;
pub mod token;
pub mod checksum;
pub mod string;
pub mod value;
pub mod array;
pub mod array_value;
use error::CalcError;
use opcodes::Opcode;
use value::ScalcString;
pub type CalcResult<T> = Result<T, CalcError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ExprKind {
Numeric,
String,
Array,
}
#[derive(Debug, Clone)]
pub struct CompiledExpr {
pub code: Vec<Opcode>,
pub kind: ExprKind,
pub uses_string: bool,
}
impl CompiledExpr {
pub fn empty(kind: ExprKind) -> Self {
Self {
code: Vec::new(),
kind,
uses_string: false,
}
}
pub fn is_empty(&self) -> bool {
self.code
.iter()
.all(|op| matches!(op, Opcode::Core(opcodes::CoreOp::End)))
}
pub fn check_until_ceiling(&self) -> Result<(), CalcError> {
let untils = self
.code
.iter()
.filter(|op| matches!(op, Opcode::Control(opcodes::ControlOp::Until(_))))
.count();
if untils > MAX_UNTILS {
return Err(CalcError::Overflow);
}
Ok(())
}
pub fn arg_usage(&self) -> (u32, u32) {
use opcodes::CoreOp;
let mut inputs: u32 = 0;
let mut stores: u32 = 0;
for op in &self.code {
match op {
Opcode::Core(CoreOp::End) => break,
Opcode::Core(CoreOp::PushVar(idx)) | Opcode::Core(CoreOp::PushDoubleVar(idx)) => {
if (*idx as usize) < CALC_NARGS {
inputs |= (1u32 << *idx) & !stores;
}
}
Opcode::Core(CoreOp::StoreVar(idx)) | Opcode::Core(CoreOp::StoreDoubleVar(idx)) => {
if (*idx as usize) < CALC_NARGS {
stores |= 1u32 << *idx;
}
}
_ => {}
}
}
(inputs, stores)
}
pub fn disassemble(&self) -> String {
let mut out = String::new();
for (i, op) in self.code.iter().enumerate() {
out.push_str(&format!("{:4}: {:?}\n", i, op));
}
out
}
}
pub(crate) fn isinf(v: f64) -> f64 {
if v.is_infinite() { 1.0 } else { 0.0 }
}
pub(crate) fn subrange_bounds(i: i64, j: i64, k: i64) -> (i64, i64) {
let wrap = |v: i64| if v < 0 { v + k } else { v };
(wrap(i).clamp(0, k), wrap(j).min(k))
}
pub(crate) fn abs_val(x: f64) -> f64 {
if x < 0.0 { -x } else { x }
}
pub const CALC_NARGS: usize = 21;
const MAX_UNTILS: usize = 9;
#[derive(Debug, Clone)]
pub struct NumericInputs {
pub vars: [f64; CALC_NARGS],
pub prev_val: f64,
num_args: usize,
}
impl NumericInputs {
pub fn new() -> Self {
Self::with_counts(CALC_NARGS)
}
pub fn with_counts(num_args: usize) -> Self {
NumericInputs {
vars: [0.0; CALC_NARGS],
prev_val: 0.0,
num_args: num_args.min(CALC_NARGS),
}
}
pub fn with_vars(vars: [f64; CALC_NARGS]) -> Self {
NumericInputs {
vars,
prev_val: 0.0,
num_args: CALC_NARGS,
}
}
pub(crate) fn num_arg(&self, i: usize) -> Option<f64> {
(i < self.num_args).then(|| self.vars[i])
}
pub(crate) fn num_arg_mut(&mut self, i: usize) -> Option<&mut f64> {
(i < self.num_args).then(|| &mut self.vars[i])
}
}
impl Default for NumericInputs {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct StringInputs {
pub num_vars: [f64; CALC_NARGS], pub str_vars: [ScalcString; CALC_NARGS],
pub prev_val: f64,
pub prev_sval: ScalcString,
num_args: usize,
num_sargs: usize,
}
impl StringInputs {
pub fn new() -> Self {
Self::with_counts(CALC_NARGS, CALC_NARGS)
}
pub fn with_counts(num_args: usize, num_sargs: usize) -> Self {
StringInputs {
num_vars: [0.0; CALC_NARGS],
str_vars: std::array::from_fn(|_| ScalcString::new()),
prev_val: 0.0,
prev_sval: ScalcString::new(),
num_args: num_args.min(CALC_NARGS),
num_sargs: num_sargs.min(CALC_NARGS),
}
}
pub(crate) fn num_arg(&self, i: usize) -> Option<f64> {
(i < self.num_args).then(|| self.num_vars[i])
}
pub(crate) fn num_arg_mut(&mut self, i: usize) -> Option<&mut f64> {
(i < self.num_args).then(|| &mut self.num_vars[i])
}
pub(crate) fn str_arg(&self, i: usize) -> Option<&ScalcString> {
(i < self.num_sargs).then(|| &self.str_vars[i])
}
pub(crate) fn str_arg_mut(&mut self, i: usize) -> Option<&mut ScalcString> {
(i < self.num_sargs).then(|| &mut self.str_vars[i])
}
}
impl Default for StringInputs {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct ArrayInputs {
pub num_vars: [f64; CALC_NARGS],
pub arrays: Vec<Vec<f64>>, pub array_size: usize,
pub prev_val: f64,
pub prev_aval: Vec<f64>,
pub amask: u32,
num_dargs: usize,
num_aargs: usize,
}
impl ArrayInputs {
pub fn new(array_size: usize) -> Self {
Self::with_counts(array_size, CALC_NARGS, CALC_NARGS)
}
pub fn with_counts(array_size: usize, num_dargs: usize, num_aargs: usize) -> Self {
ArrayInputs {
num_vars: [0.0; CALC_NARGS],
arrays: vec![Vec::new(); CALC_NARGS],
array_size,
prev_val: 0.0,
prev_aval: vec![0.0; array_size],
amask: 0,
num_dargs: num_dargs.min(CALC_NARGS),
num_aargs: num_aargs.min(CALC_NARGS),
}
}
pub(crate) fn num_arg(&self, i: usize) -> Option<f64> {
(i < self.num_dargs).then(|| self.num_vars[i])
}
pub(crate) fn num_arg_mut(&mut self, i: usize) -> Option<&mut f64> {
(i < self.num_dargs).then(|| &mut self.num_vars[i])
}
pub(crate) fn array_arg(&self, i: usize) -> Option<&Vec<f64>> {
(i < self.num_aargs).then(|| &self.arrays[i])
}
pub(crate) fn store_array(&mut self, i: usize, buf: Vec<f64>) {
if i < self.num_aargs {
self.arrays[i] = buf;
self.amask |= 1 << i;
}
}
}
impl Default for ArrayInputs {
fn default() -> Self {
Self::new(1)
}
}