destruction of libpt-ccc

This commit is contained in:
Christoph J. Scherr 2024-01-15 23:28:01 +01:00
parent 6dcc45d722
commit 9f393f0369
Signed by: PlexSheep
GPG Key ID: 7CDD0B14851A08EF
5 changed files with 15 additions and 508 deletions

View File

@ -1,9 +1,9 @@
name: Cargo Format, Check and Test
name: Cargo Check, Format, Fix and Test
on: [push, pull_request]
jobs:
format:
name: cargo fmt
name: cargo CI
permissions:
# Give the default GITHUB_TOKEN write permission to commit and push the
# added or changed files to the repository.
@ -11,22 +11,17 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --all-features --all-targets
- run: rustup component add rustfmt
- run: cargo fmt
- run: cargo fix --all-features --all-targets
- run: cargo fmt --all
- run: cargo test --all-features --all-targets
- uses: stefanzweifel/git-auto-commit-action@v5
with:
# Optional. Commit message for the created commit.
# Defaults to "Apply automatic changes"
commit_message: Automatical formatting
check:
name: cargo check
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo check --all-features --verbose
test:
name: cargo test
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- run: cargo test --all-features --verbose
commit_message: automatic cargo CI changes
commit_user_name: Gitea CI
commit_user_email: noreply@cscherr.de
commit_author: Gitea CI <noreply@cscherr.de>

View File

@ -1,225 +0,0 @@
//! # Results and Errors for the calculate module
//!
//! This module defines the errors and results that can be processed from any given term.
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
use std::fmt::Display;
pub use num_traits::PrimInt;
#[allow(unused_imports)] // we possibly want to use all log levels
use libpt_log::*;
#[allow(unused_imports)] // import more complex math stuff from there
use libpt_math;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
/// Quick Result with a ccc error
pub type Result<T> = std::result::Result<T, Error>;
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
/// ## Supported Operations
///
/// This `enum` contains all operations supported in this module.
#[non_exhaustive]
#[derive(Debug)]
pub enum Operator {
/// Mathmatical addition
Addition,
/// Mathmatical subtraction
Subtraction,
/// Mathmatical multiplication
Multiplication,
/// Mathmatical division
Division,
/// Mathmatical modulo, finite field arithmetic
Modulo,
/// Any function, seel [`Function`]
Function(Function)
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// ## Supported Functions
///
/// This `enum` contains all functions supported in this module.
///
/// A function has a name followed by braces directly afterwards.
/// A function may have 0 to 31 Arguments.
///
/// Example: `sqrt(19)`, `floor(19.9)`
#[non_exhaustive]
#[derive(Debug)]
pub enum Function {
/// Draw the mathmatical root, attribute n is the nth root
Root(u16),
/// round up
Floor,
/// round down
Ceil,
/// round to nearest integer
/// (commercial rounding)
Round,
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Top Level Error Type
///
/// Contains many variants of other errors, that can occur when using the crate.
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
/// The term has bad syntax
SyntaxError(String)
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Represents some kind of computed value
#[derive(Debug)]
pub enum Value {
/// Variable value
Variable(VarVal),
/// Numerical value
Numerical(NumVal),
/// Complex number value
Complex(ComplVal),
}
/// Represents some kind of numeric value
#[non_exhaustive]
#[derive(Debug)]
pub enum NumVal {
/// Value > 0
Signed(i128),
/// Value can be negative
Unsigned(u128),
/// Value is not an integer
Float(f64)
}
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// Represents a Value with at least one variable,
///
/// currently not implemented
#[derive(Debug)]
pub struct VarVal {
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Represents a Value with a complex number,
///
/// currently not implemented
#[derive(Debug)]
pub struct ComplVal {
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl<T: num_traits::PrimInt> From<T> for NumVal where
u128: TryFrom<T>,
u128: TryFrom<T> {
fn from(value: T) -> Self {
if T::min_value().is_zero() {
// unsigned data types
// `u128` is the largest unsigned datatype, any other type will fit.
NumVal::Unsigned(value.to_u128().unwrap())
}
else {
// signed data types
// `i128` is the largest unsigned datatype, any other type will fit.
NumVal::Signed(value.to_i128().unwrap())
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Display Errors with a nice little reason
impl Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::SyntaxError(reason) => {
write!(f, "Syntax Error: {}", reason)
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl<T: PrimInt> From<T> for Value where
u128: TryFrom<T>,
u128: TryFrom<T> {
fn from(value: T) -> Self {
NumVal::from(value).into()
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl From<NumVal> for Value {
fn from(value: NumVal) -> Self {
Value::Numerical(value)
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Display for Value {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Value::Numerical(val) => {
write!(f, "{}", val)
}
Value::Complex(val) => {
write!(f, "{}", val)
}
Value::Variable(val) => {
write!(f, "{}", val)
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Display for NumVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
NumVal::Float(val) => {
write!(f, "{val}")
}
NumVal::Signed(val) => {
write!(f, "{val}")
}
NumVal::Unsigned(val) => {
write!(f, "{val}")
}
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Display for ComplVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "")
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
impl Display for VarVal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "")
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -19,14 +19,7 @@
#![warn(clippy::pedantic)]
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
pub mod base;
pub use base::{Error, Result, Value};
pub mod term;
pub use term::*;
#[allow(unused_imports)] // we possibly want to use all log levels
use libpt_log::*;
#[allow(unused_imports)] // import more complex math stuff from there
use libpt_log;
use libpt_math;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
@ -40,44 +33,8 @@ use libpt_math;
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// ## A Calculator struct
///
/// This struct does not do anything at the moment, but aims to be the target for high level
/// control. Instead of using the [`Calculator`], you could just use the [`Term`] struct for
/// lower level control.
#[derive(Debug)]
pub struct Calculator;
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl Calculator {
/// Do a single calculation without doing anything else
pub fn oneshot(t: String) -> Result<Value> {
trace!(orig=t, "parsing original string to Term");
let t = Term::new(t)?;
trace!("term has been parsed, starting Calculation");
debug!("parsed term: {t:#?}");
Self::calc(t)
}
/// ## Calculate a [`Term`]
///
/// This method makes use of the
/// [shunting yard algorithm](https://en.wikipedia.org/wiki/Shunting_yard_algorithm) to
/// Calculate the final value of any term.
///
/// This method only processes a single term at a time, without caching.
pub fn calc(mut t: Term) -> Result<Value> {
trace!("Calculating term {t:?}");
t.prepare()?;
t.process()?;
if t.result.is_none() {
let reason = format!("Term was processed but no result was assigned.");
// FIXME: unfitting error type
return Err(Error::SyntaxError(reason))
}
return t.result.unwrap()
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////

View File

@ -1,223 +0,0 @@
//! # A term that can be the input for calculation
//!
//! Short description
//!
//! Details
//!
//! ## Section 1
//!
//! ## Section 2
//// ATTRIBUTES ////////////////////////////////////////////////////////////////////////////////////
// we want docs
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
// we want Debug everywhere.
#![warn(missing_debug_implementations)]
// enable clippy's extra lints, the pedantic version
#![warn(clippy::pedantic)]
use std::collections::VecDeque;
//// IMPORTS ///////////////////////////////////////////////////////////////////////////////////////
pub use super::{Error, Result, Value, base::{self, *}};
#[allow(unused_imports)] // we possibly want to use all log levels
use libpt_log::*;
#[allow(unused_imports)] // import more complex math stuff from there
use libpt_math;
//// TYPES /////////////////////////////////////////////////////////////////////////////////////////
//// CONSTANTS /////////////////////////////////////////////////////////////////////////////////////
//// STATICS ///////////////////////////////////////////////////////////////////////////////////////
//// MACROS ////////////////////////////////////////////////////////////////////////////////////////
//// ENUMS /////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
/// ## Parsed value to be calculated
///
/// This enum represents anything that goes to the output queue of [`Term::prepare()`] and will
/// then be used to actually calculate something in [`Term::process()`].
#[derive(Debug)]
enum Token {
/// Some kind of operator
#[allow(unused)] // tmp
Operator(Operator),
/// A concrete value that we can calculate something with. May be a constant, integer, float,
/// etc.
/// The Token has a value that can be used in calculation
Value(super::base::Value),
/// A variable of some kind that will be present in the result
#[allow(unused)] // tmp
Variable(char),
}
//// STRUCTS ///////////////////////////////////////////////////////////////////////////////////////
/// ## Term that can be calculated
///
/// Represents a signular term, that can be calculated. Terms will be evaluated by the [`Term::prepare`]
/// function, afterwards calculated (as much as possible) in the [`Term::process`] function.
///
#[derive(Debug)]
pub struct Term {
/// the original expression to calculate
pub original: String,
/// the filtered text of the expression, only with relevant information
pub text: String,
/// the calculated result, may be of diffrent types, see [`crate::math::calculator::result`].
pub result: Option<Result<Value>>,
/////////////////////////////////////
///// internal values following /////
/////////////////////////////////////
#[allow(unused)] // tmp
operator_stack: Vec<Operator>,
output_queue: VecDeque<Token>
}
//// IMPLEMENTATION ////////////////////////////////////////////////////////////////////////////////
impl Term {
/// Build a new term from an expression
///
/// Invalid terms will result in an [`Err`].
pub fn new(orig: String) -> Result<Term> {
return Ok(
Term {
original: orig,
text: String::new(), // will be initialized in `prepare()`
result: None,
operator_stack: Vec::new(),
output_queue: VecDeque::new()
}
)
}
/// Prepare the term for the processing.
pub fn prepare(&mut self) -> Result<()> {
trace!("preparing term: {:#?}", self);
self.text = Self::filter(&self.original)?;
// Storage for unfinished tokens
let _unfinished_chars: Vec<char> = Vec::new();
for (_index, c) in self.original.chars().enumerate() {
// this will be a mess, but it has to be before i can sort the mess.
match c {
// TODO: make function to check if character is an operator, use it
_ => {
let reason = format!("The meaning of '{c}' could not be identified.");
warn!(reason);
return Err(Error::SyntaxError(reason));
}
}
}
Ok(())
}
/// Process a prepared term, calculating it's result
pub fn process(&mut self) -> Result<()> {
debug!("processing term: {:#?}", self);
debug!("queue: {:#?}", self.output_queue);
// TODO: process RPN and set result
self.result = Some(Ok(19.into()));
Ok(())
}
/// Convert a character into a token
///
/// Returns: A tuple with a [`Token`] and a [`bool`]. If the bool is false, the [`Token`] is
/// marked as "incomplete", meaning that the character cannot be used yet.
#[allow(unused)] // tmp
fn to_tok(_s: Vec<char>) -> Result<Token> {
Ok(19.into())
}
/// only leave relevant chars for calculation
// TODO: make function to check if character is an operator, use it
fn filter(s: &str) -> Result<String> {
// pre checks
// NOTE: Apperently, "alphanumeric" in Rust is a pretty broad term.
// Even CJK characters or Japanese Kana are allowed:
// - 'さ' alphanumeric
// - '数' alphanumeric
// - '学' alphanumeric
// - '+' not alphanumeric
for c in s.chars() {
#[cfg(debug_assertions)] {
debug!("filter checks for '{c}':
alphanumeric: {}
allowed special: {}
EXCEPT IF
ascii control: {}
",
!c.is_alphanumeric(),
!Self::is_allowed_special_c(&c),
c.is_ascii_control(),
)
}
if
(
!c.is_alphanumeric() ||
!Self::is_allowed_special_c(&c)
)
&&
(
c.is_ascii_control()
)
{
// TODO: allow any unicode char to be a variable
let reason = format!("'{c}' is not a valid character, only alphanumeric, punctuation, operators are allowed.");
warn!(reason);
return Err(Error::SyntaxError(reason));
}
}
// filter out single chars
let mut filtered = String::new();
for c in s.chars() {
if !Self::is_ignore(&c) {
filtered.push(c);
}
}
return Ok(filtered)
}
/// check if we should ignore this character
fn is_ignore(c: &char) -> bool {
match *c {
' ' => true,
_ => false
}
}
/// allowed special chars
fn is_allowed_special_c(c: &char) -> bool {
match *c {
'+' | '-' | '*' | '/' | '%' => true,
_ => false
}
}
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// Helper methods for Tokens
impl Token { }
////////////////////////////////////////////////////////////////////////////////////////////////////
impl<T> From<T> for Token where
T: Into<Value>,
T: PrimInt,
u128: TryFrom<T>
{
fn from(value: T) -> Self {
Token::Value(base::Value::from(value))
}
}
//// PUBLIC FUNCTIONS //////////////////////////////////////////////////////////////////////////////
//// PRIVATE FUNCTIONS /////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -13,3 +13,6 @@ keywords.workspace = true
categories.workspace = true
[dependencies]
anyhow = "1.0.79"
cucumber = "0.20.2"
libpt-log = {path = "../libpt-log" }