docs(core): add documentation for all public API members
This commit is contained in:
parent
ee75539d73
commit
299e572186
9 changed files with 325 additions and 48 deletions
|
|
@ -1,3 +1,5 @@
|
|||
//! Expression tree for Argus specifications
|
||||
|
||||
use std::any::Any;
|
||||
use std::collections::HashSet;
|
||||
|
||||
|
|
@ -17,20 +19,61 @@ use crate::{ArgusResult, Error};
|
|||
/// All expressions that are numeric
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum NumExpr {
|
||||
/// A signed integer literal
|
||||
IntLit(i64),
|
||||
/// An unsigned integer literal
|
||||
UIntLit(u64),
|
||||
/// A floating point literal
|
||||
FloatLit(f64),
|
||||
IntVar { name: String },
|
||||
UIntVar { name: String },
|
||||
FloatVar { name: String },
|
||||
|
||||
Neg { arg: Box<NumExpr> },
|
||||
Add { args: Vec<NumExpr> },
|
||||
Sub { lhs: Box<NumExpr>, rhs: Box<NumExpr> },
|
||||
Mul { args: Vec<NumExpr> },
|
||||
Div { dividend: Box<NumExpr>, divisor: Box<NumExpr> },
|
||||
|
||||
Abs { arg: Box<NumExpr> },
|
||||
/// A signed integer variable
|
||||
IntVar {
|
||||
/// Name of the variable
|
||||
name: String,
|
||||
},
|
||||
/// A unsigned integer variable
|
||||
UIntVar {
|
||||
/// Name of the variable
|
||||
name: String,
|
||||
},
|
||||
/// A floating point number variable
|
||||
FloatVar {
|
||||
/// Name of the variable
|
||||
name: String,
|
||||
},
|
||||
/// Numeric negation of a numeric expression
|
||||
Neg {
|
||||
/// Numeric expression being negated
|
||||
arg: Box<NumExpr>,
|
||||
},
|
||||
/// Arithmetic addition of a list of numeric expressions
|
||||
Add {
|
||||
/// List of expressions being added
|
||||
args: Vec<NumExpr>,
|
||||
},
|
||||
/// Subtraction of two numbers
|
||||
Sub {
|
||||
/// LHS to the expression `lhs - rhs`
|
||||
lhs: Box<NumExpr>,
|
||||
/// RHS to the expression `lhs - rhs`
|
||||
rhs: Box<NumExpr>,
|
||||
},
|
||||
/// Arithmetic multiplication of a list of numeric expressions
|
||||
Mul {
|
||||
/// List of expressions being multiplied
|
||||
args: Vec<NumExpr>,
|
||||
},
|
||||
/// Divide two expressions `dividend / divisor`
|
||||
Div {
|
||||
/// The dividend
|
||||
dividend: Box<NumExpr>,
|
||||
/// The divisor
|
||||
divisor: Box<NumExpr>,
|
||||
},
|
||||
/// The absolute value of an expression
|
||||
Abs {
|
||||
/// Argument to `abs`
|
||||
arg: Box<NumExpr>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Expr for NumExpr {
|
||||
|
|
@ -63,33 +106,49 @@ impl Expr for NumExpr {
|
|||
/// Types of comparison operations
|
||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||
pub enum Ordering {
|
||||
/// Equality check for two expressions
|
||||
Eq,
|
||||
/// Non-equality check for two expressions
|
||||
NotEq,
|
||||
Less { strict: bool },
|
||||
Greater { strict: bool },
|
||||
/// Less than check
|
||||
Less {
|
||||
/// Denotes `lhs < rhs` if `strict`, and `lhs <= rhs` otherwise.
|
||||
strict: bool,
|
||||
},
|
||||
/// Greater than check
|
||||
Greater {
|
||||
/// Denotes `lhs > rhs` if `strict`, and `lhs >= rhs` otherwise.
|
||||
strict: bool,
|
||||
},
|
||||
}
|
||||
|
||||
impl Ordering {
|
||||
/// Check if `Ordering::Eq`
|
||||
pub fn equal() -> Self {
|
||||
Self::Eq
|
||||
}
|
||||
|
||||
/// Check if `Ordering::NotEq`
|
||||
pub fn not_equal() -> Self {
|
||||
Self::NotEq
|
||||
}
|
||||
|
||||
/// Check if `Ordering::Less { strict: true }`
|
||||
pub fn less_than() -> Self {
|
||||
Self::Less { strict: true }
|
||||
}
|
||||
|
||||
/// Check if `Ordering::Less { strict: false }`
|
||||
pub fn less_than_eq() -> Self {
|
||||
Self::Less { strict: false }
|
||||
}
|
||||
|
||||
/// Check if `Ordering::Greater { strict: true }`
|
||||
pub fn greater_than() -> Self {
|
||||
Self::Greater { strict: true }
|
||||
}
|
||||
|
||||
/// Check if `Ordering::Less { strict: false }`
|
||||
pub fn greater_than_eq() -> Self {
|
||||
Self::Greater { strict: false }
|
||||
}
|
||||
|
|
@ -98,17 +157,68 @@ impl Ordering {
|
|||
/// All expressions that are evaluated to be of type `bool`
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum BoolExpr {
|
||||
/// A `bool` literal
|
||||
BoolLit(bool),
|
||||
BoolVar { name: String },
|
||||
Cmp { op: Ordering, lhs: Box<NumExpr>, rhs: Box<NumExpr> },
|
||||
Not { arg: Box<BoolExpr> },
|
||||
And { args: Vec<BoolExpr> },
|
||||
Or { args: Vec<BoolExpr> },
|
||||
/// A `bool` variable
|
||||
BoolVar {
|
||||
/// Variable name
|
||||
name: String,
|
||||
},
|
||||
/// A comparison expression
|
||||
Cmp {
|
||||
/// The type of comparison
|
||||
op: Ordering,
|
||||
/// The LHS for the comparison
|
||||
lhs: Box<NumExpr>,
|
||||
/// The RHS for the comparison
|
||||
rhs: Box<NumExpr>,
|
||||
},
|
||||
/// Logical negation of an expression
|
||||
Not {
|
||||
/// Expression to be negated
|
||||
arg: Box<BoolExpr>,
|
||||
},
|
||||
/// Logical conjunction of a list of expressions
|
||||
And {
|
||||
/// Expressions to be "and"-ed
|
||||
args: Vec<BoolExpr>,
|
||||
},
|
||||
/// Logical disjunction of a list of expressions
|
||||
Or {
|
||||
/// Expressions to be "or"-ed
|
||||
args: Vec<BoolExpr>,
|
||||
},
|
||||
|
||||
Next { arg: Box<BoolExpr> },
|
||||
Always { arg: Box<BoolExpr> },
|
||||
Eventually { arg: Box<BoolExpr> },
|
||||
Until { lhs: Box<BoolExpr>, rhs: Box<BoolExpr> },
|
||||
/// A temporal next expression
|
||||
///
|
||||
/// Checks if the next time point in a signal is `true` or not.
|
||||
Next {
|
||||
/// Argument for `Next`
|
||||
arg: Box<BoolExpr>,
|
||||
},
|
||||
/// A temporal always expression
|
||||
///
|
||||
/// Checks if the signal is `true` for all points in a signal.
|
||||
Always {
|
||||
/// Argument for `Always`
|
||||
arg: Box<BoolExpr>,
|
||||
},
|
||||
/// A temporal eventually expression
|
||||
///
|
||||
/// Checks if the signal is `true` for some point in a signal.
|
||||
Eventually {
|
||||
/// Argument for `Eventually`
|
||||
arg: Box<BoolExpr>,
|
||||
},
|
||||
/// A temporal until expression
|
||||
///
|
||||
/// Checks if the `lhs` is always `true` for a signal until `rhs` becomes `true`.
|
||||
Until {
|
||||
/// LHS to `lhs Until rhs`
|
||||
lhs: Box<BoolExpr>,
|
||||
/// RHS to `lhs Until rhs`
|
||||
rhs: Box<BoolExpr>,
|
||||
},
|
||||
}
|
||||
|
||||
impl Expr for BoolExpr {
|
||||
|
|
@ -138,9 +248,12 @@ impl Expr for BoolExpr {
|
|||
}
|
||||
}
|
||||
|
||||
/// A reference to an expression (either [`BoolExpr`] or [`NumExpr`]).
|
||||
#[derive(Clone, Copy, Debug, derive_more::From)]
|
||||
pub enum ExprRef<'a> {
|
||||
/// A reference to a [`BoolExpr`]
|
||||
Bool(&'a BoolExpr),
|
||||
/// A reference to a [`NumExpr`]
|
||||
Num(&'a NumExpr),
|
||||
}
|
||||
|
||||
|
|
@ -155,28 +268,34 @@ pub struct ExprBuilder {
|
|||
}
|
||||
|
||||
impl ExprBuilder {
|
||||
/// Create a new `ExprBuilder` context.
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
declarations: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Declare a constant boolean expression
|
||||
pub fn bool_const(&self, value: bool) -> Box<BoolExpr> {
|
||||
Box::new(BoolExpr::BoolLit(value))
|
||||
}
|
||||
|
||||
/// Declare a constant integer expression
|
||||
pub fn int_const(&self, value: i64) -> Box<NumExpr> {
|
||||
Box::new(NumExpr::IntLit(value))
|
||||
}
|
||||
|
||||
/// Declare a constant unsigned integer expression
|
||||
pub fn uint_const(&self, value: u64) -> Box<NumExpr> {
|
||||
Box::new(NumExpr::UIntLit(value))
|
||||
}
|
||||
|
||||
/// Declare a constant floating point expression
|
||||
pub fn float_const(&self, value: f64) -> Box<NumExpr> {
|
||||
Box::new(NumExpr::FloatLit(value))
|
||||
}
|
||||
|
||||
/// Declare a boolean variable
|
||||
pub fn bool_var(&mut self, name: String) -> ArgusResult<Box<BoolExpr>> {
|
||||
if self.declarations.insert(name.clone()) {
|
||||
Ok(Box::new(BoolExpr::BoolVar { name }))
|
||||
|
|
@ -185,6 +304,7 @@ impl ExprBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Declare a integer variable
|
||||
pub fn int_var(&mut self, name: String) -> ArgusResult<Box<NumExpr>> {
|
||||
if self.declarations.insert(name.clone()) {
|
||||
Ok(Box::new(NumExpr::IntVar { name }))
|
||||
|
|
@ -193,6 +313,7 @@ impl ExprBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Declare a unsigned integer variable
|
||||
pub fn uint_var(&mut self, name: String) -> ArgusResult<Box<NumExpr>> {
|
||||
if self.declarations.insert(name.clone()) {
|
||||
Ok(Box::new(NumExpr::UIntVar { name }))
|
||||
|
|
@ -201,6 +322,7 @@ impl ExprBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Declare a floating point variable
|
||||
pub fn float_var(&mut self, name: String) -> ArgusResult<Box<NumExpr>> {
|
||||
if self.declarations.insert(name.clone()) {
|
||||
Ok(Box::new(NumExpr::FloatVar { name }))
|
||||
|
|
@ -209,10 +331,12 @@ impl ExprBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a [`NumExpr::Neg`] expression
|
||||
pub fn make_neg(&self, arg: Box<NumExpr>) -> Box<NumExpr> {
|
||||
Box::new(NumExpr::Neg { arg })
|
||||
}
|
||||
|
||||
/// Create a [`NumExpr::Add`] expression
|
||||
pub fn make_add<I>(&self, args: I) -> ArgusResult<Box<NumExpr>>
|
||||
where
|
||||
I: IntoIterator<Item = NumExpr>,
|
||||
|
|
@ -225,6 +349,7 @@ impl ExprBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a [`NumExpr::Mul`] expression
|
||||
pub fn make_mul<I>(&self, args: I) -> ArgusResult<Box<NumExpr>>
|
||||
where
|
||||
I: IntoIterator<Item = NumExpr>,
|
||||
|
|
@ -237,42 +362,52 @@ impl ExprBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a [`NumExpr::Div`] expression
|
||||
pub fn make_div(&self, dividend: Box<NumExpr>, divisor: Box<NumExpr>) -> Box<NumExpr> {
|
||||
Box::new(NumExpr::Div { dividend, divisor })
|
||||
}
|
||||
|
||||
/// Create a [`BoolExpr::Cmp`] expression
|
||||
pub fn make_cmp(&self, op: Ordering, lhs: Box<NumExpr>, rhs: Box<NumExpr>) -> Box<BoolExpr> {
|
||||
Box::new(BoolExpr::Cmp { op, lhs, rhs })
|
||||
}
|
||||
|
||||
/// Create a "less than" ([`BoolExpr::Cmp`]) expression
|
||||
pub fn make_lt(&self, lhs: Box<NumExpr>, rhs: Box<NumExpr>) -> Box<BoolExpr> {
|
||||
self.make_cmp(Ordering::Less { strict: true }, lhs, rhs)
|
||||
}
|
||||
|
||||
/// Create a "less than or equal" ([`BoolExpr::Cmp`]) expression
|
||||
pub fn make_le(&self, lhs: Box<NumExpr>, rhs: Box<NumExpr>) -> Box<BoolExpr> {
|
||||
self.make_cmp(Ordering::Less { strict: false }, lhs, rhs)
|
||||
}
|
||||
|
||||
/// Create a "greater than" ([`BoolExpr::Cmp`]) expression
|
||||
pub fn make_gt(&self, lhs: Box<NumExpr>, rhs: Box<NumExpr>) -> Box<BoolExpr> {
|
||||
self.make_cmp(Ordering::Greater { strict: true }, lhs, rhs)
|
||||
}
|
||||
|
||||
/// Create a "greater than or equal" ([`BoolExpr::Cmp`]) expression
|
||||
pub fn make_ge(&self, lhs: Box<NumExpr>, rhs: Box<NumExpr>) -> Box<BoolExpr> {
|
||||
self.make_cmp(Ordering::Greater { strict: false }, lhs, rhs)
|
||||
}
|
||||
|
||||
/// Create a "equals" ([`BoolExpr::Cmp`]) expression
|
||||
pub fn make_eq(&self, lhs: Box<NumExpr>, rhs: Box<NumExpr>) -> Box<BoolExpr> {
|
||||
self.make_cmp(Ordering::Eq, lhs, rhs)
|
||||
}
|
||||
|
||||
/// Create a "not equals" ([`BoolExpr::Cmp`]) expression
|
||||
pub fn make_neq(&self, lhs: Box<NumExpr>, rhs: Box<NumExpr>) -> Box<BoolExpr> {
|
||||
self.make_cmp(Ordering::NotEq, lhs, rhs)
|
||||
}
|
||||
|
||||
/// Create a [`BoolExpr::Not`] expression.
|
||||
pub fn make_not(&self, arg: Box<BoolExpr>) -> Box<BoolExpr> {
|
||||
Box::new(BoolExpr::Not { arg })
|
||||
}
|
||||
|
||||
/// Create a [`BoolExpr::Or`] expression.
|
||||
pub fn make_or<I>(&self, args: I) -> ArgusResult<Box<BoolExpr>>
|
||||
where
|
||||
I: IntoIterator<Item = BoolExpr>,
|
||||
|
|
@ -285,6 +420,7 @@ impl ExprBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a [`BoolExpr::And`] expression.
|
||||
pub fn make_and<I>(&self, args: I) -> ArgusResult<Box<BoolExpr>>
|
||||
where
|
||||
I: IntoIterator<Item = BoolExpr>,
|
||||
|
|
@ -297,26 +433,35 @@ impl ExprBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
/// Create a [`BoolExpr::Next`] expression.
|
||||
pub fn make_next(&self, arg: Box<BoolExpr>) -> Box<BoolExpr> {
|
||||
Box::new(BoolExpr::Next { arg })
|
||||
}
|
||||
|
||||
/// Create a [`BoolExpr::Always`] expression.
|
||||
pub fn make_always(&self, arg: Box<BoolExpr>) -> Box<BoolExpr> {
|
||||
Box::new(BoolExpr::Always { arg })
|
||||
}
|
||||
|
||||
/// Create a [`BoolExpr::Eventually`] expression.
|
||||
pub fn make_eventually(&self, arg: Box<BoolExpr>) -> Box<BoolExpr> {
|
||||
Box::new(BoolExpr::Eventually { arg })
|
||||
}
|
||||
|
||||
/// Create a [`BoolExpr::Until`] expression.
|
||||
pub fn make_until(&self, lhs: Box<BoolExpr>, rhs: Box<BoolExpr>) -> Box<BoolExpr> {
|
||||
Box::new(BoolExpr::Until { lhs, rhs })
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
pub mod arbitrary {
|
||||
//! Helper functions to generate arbitrary expressions using [`proptest`].
|
||||
//! Helper functions to generate arbitrary expressions using [`mod@proptest`].
|
||||
use proptest::prelude::*;
|
||||
|
||||
use super::*;
|
||||
|
||||
/// Generate arbitrary numeric expressions
|
||||
pub fn num_expr() -> impl Strategy<Value = Box<NumExpr>> {
|
||||
let leaf = prop_oneof![
|
||||
any::<i64>().prop_map(|val| Box::new(NumExpr::IntLit(val))),
|
||||
|
|
@ -351,6 +496,7 @@ pub mod arbitrary {
|
|||
)
|
||||
}
|
||||
|
||||
/// Generate arbitrary comparison expressions
|
||||
pub fn cmp_expr() -> impl Strategy<Value = Box<BoolExpr>> {
|
||||
use Ordering::*;
|
||||
let op = prop_oneof![Just(Eq), Just(NotEq),];
|
||||
|
|
@ -360,6 +506,7 @@ pub mod arbitrary {
|
|||
(op, lhs, rhs).prop_map(|(op, lhs, rhs)| Box::new(BoolExpr::Cmp { op, lhs, rhs }))
|
||||
}
|
||||
|
||||
/// Generate arbitrary boolean expressions
|
||||
pub fn bool_expr() -> impl Strategy<Value = Box<BoolExpr>> {
|
||||
let leaf = prop_oneof![
|
||||
any::<bool>().prop_map(|val| Box::new(BoolExpr::BoolLit(val))),
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
//! Iterators for expression trees
|
||||
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use super::{Expr, ExprRef};
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ internal_macros::forward_box_binop! {impl Div, div for NumExpr, NumExpr }
|
|||
use super::Ordering;
|
||||
|
||||
impl NumExpr {
|
||||
/// Convenience method to create an `lhs < rhs` expression.
|
||||
pub fn less_than(self, rhs: Self) -> BoolExpr {
|
||||
BoolExpr::Cmp {
|
||||
op: Ordering::Less { strict: true },
|
||||
|
|
@ -98,6 +99,7 @@ impl NumExpr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convenience method to create an `lhs <= rhs` expression.
|
||||
pub fn less_than_eq(self, rhs: Self) -> BoolExpr {
|
||||
BoolExpr::Cmp {
|
||||
op: Ordering::Less { strict: false },
|
||||
|
|
@ -106,6 +108,7 @@ impl NumExpr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convenience method to create an `lhs > rhs` expression.
|
||||
pub fn greater_than(self, rhs: Self) -> BoolExpr {
|
||||
BoolExpr::Cmp {
|
||||
op: Ordering::Greater { strict: true },
|
||||
|
|
@ -114,6 +117,7 @@ impl NumExpr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convenience method to create an `lhs >= rhs` expression.
|
||||
pub fn greater_than_eq(self, rhs: Self) -> BoolExpr {
|
||||
BoolExpr::Cmp {
|
||||
op: Ordering::Greater { strict: false },
|
||||
|
|
@ -122,6 +126,7 @@ impl NumExpr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convenience method to create an `lhs == rhs` expression.
|
||||
pub fn equal(self, rhs: Self) -> BoolExpr {
|
||||
BoolExpr::Cmp {
|
||||
op: Ordering::Eq,
|
||||
|
|
@ -130,6 +135,7 @@ impl NumExpr {
|
|||
}
|
||||
}
|
||||
|
||||
/// Convenience method to create an `lhs != rhs` expression.
|
||||
pub fn not_equal(self, rhs: Self) -> BoolExpr {
|
||||
BoolExpr::Cmp {
|
||||
op: Ordering::NotEq,
|
||||
|
|
|
|||
|
|
@ -5,17 +5,25 @@ use super::ExprRef;
|
|||
|
||||
/// A trait representing expressions
|
||||
pub trait Expr {
|
||||
/// Check if the given expression is a numeric expression
|
||||
fn is_numeric(&self) -> bool;
|
||||
/// Check if the given expression is a boolean expression
|
||||
fn is_boolean(&self) -> bool;
|
||||
|
||||
/// Get the arguments to the current expression.
|
||||
///
|
||||
/// If the expression doesn't contain arguments (i.e., it is a leaf expression) then
|
||||
/// the vector is empty.
|
||||
fn args(&self) -> Vec<ExprRef<'_>>;
|
||||
|
||||
/// Helper function for upcasting to [`std::any::Any`] and then downcasting to a
|
||||
/// concrete [`BoolExpr`](crate::expr::BoolExpr) or
|
||||
/// [`NumExpr`](crate::expr::NumExpr).
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
|
||||
/// An iterator over the AST starting from the current expression.
|
||||
fn iter(&self) -> AstIter<'_>;
|
||||
}
|
||||
|
||||
impl dyn Expr {
|
||||
/// Convenience method to downcast an expression to a concrete expression node.
|
||||
pub fn downcast_expr_ref<T>(&self) -> Option<&T>
|
||||
where
|
||||
T: Any,
|
||||
|
|
|
|||
|
|
@ -1,3 +1,15 @@
|
|||
//! # `argus-core`
|
||||
//!
|
||||
//! This crate provides some of the core functionality or interfaces for the other Argus
|
||||
//! components. Mainly, the crate provides:
|
||||
//!
|
||||
//! 1. Expression tree nodes for defining temporal logic specifications (see [`expr`]).
|
||||
//! 2. Different signal types for generating traces of data (see [`signals`]).
|
||||
//! 3. A list of possible errors any component in Argus can generate (see
|
||||
//! [`enum@Error`]).
|
||||
|
||||
#![warn(missing_docs)]
|
||||
|
||||
pub mod expr;
|
||||
pub mod prelude;
|
||||
pub mod signals;
|
||||
|
|
@ -6,33 +18,68 @@ use std::time::Duration;
|
|||
|
||||
use thiserror::Error;
|
||||
|
||||
/// Errors generated by all Argus components.
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
/// An identifier has been redeclared in a specification.
|
||||
///
|
||||
/// This is called mainly from [`expr::ExprBuilder`].
|
||||
#[error("redeclaration of identifier")]
|
||||
IdentifierRedeclaration,
|
||||
|
||||
/// An expression is provided with an insufficient number of arguments.
|
||||
///
|
||||
/// This is called for N-ary expressions:
|
||||
/// [`NumExpr::Add`](crate::expr::NumExpr::Add),
|
||||
/// [`NumExpr::Mul`](crate::expr::NumExpr::Mul),
|
||||
/// [`BoolExpr::And`](crate::expr::BoolExpr::And), and
|
||||
/// [`BoolExpr::Or`](crate::expr::BoolExpr::Or).
|
||||
#[error("insufficient number of arguments")]
|
||||
IncompleteArgs,
|
||||
|
||||
/// Attempting to `push` a new sample to a non-sampled signal
|
||||
/// ([`Signal::Empty`](crate::signals::Signal::Empty) or
|
||||
/// [`Signal::Constant`](crate::signals::Signal::Constant)).
|
||||
#[error("cannot push value to non-sampled signal")]
|
||||
InvalidPushToSignal,
|
||||
|
||||
/// Pushing the new value to the sampled signal makes it not strictly monotonically
|
||||
/// increasing.
|
||||
#[error(
|
||||
"trying to create a non-monotonically signal, signal end time ({end_time:?}) > sample time point \
|
||||
({current_sample:?})"
|
||||
)]
|
||||
NonMonotonicSignal { end_time: Duration, current_sample: Duration },
|
||||
NonMonotonicSignal {
|
||||
/// The time that the signal actually ends
|
||||
end_time: Duration,
|
||||
/// The time point of the new (erroneous) sample.
|
||||
current_sample: Duration,
|
||||
},
|
||||
|
||||
#[error("invalid operation due to bad type")]
|
||||
/// Attempting to perform an invalid operation on a signal
|
||||
#[error("invalid operation on signal")]
|
||||
InvalidOperation,
|
||||
|
||||
/// Attempting to index a signal not present in a trace (see
|
||||
/// [`mod@argus_semantics::Trace`].
|
||||
#[error("name not in signal trace")]
|
||||
SignalNotPresent,
|
||||
|
||||
/// Attempting to perform a signal operation not supported by the type
|
||||
#[error("incorrect signal type")]
|
||||
InvalidSignalType,
|
||||
|
||||
/// Incorrect cast of signal
|
||||
#[error("invalid cast from {from} to {to}")]
|
||||
InvalidCast { from: &'static str, to: &'static str },
|
||||
InvalidCast {
|
||||
/// Type of the signal being cast from
|
||||
from: &'static str,
|
||||
/// Type of the signal being cast to
|
||||
to: &'static str,
|
||||
},
|
||||
}
|
||||
|
||||
/// Alias for [`Error`](enum@Error)
|
||||
pub type ArgusError = Error;
|
||||
/// Alias for [`Result<T, ArgusError>`]
|
||||
pub type ArgusResult<T> = Result<T, Error>;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
#![doc(hidden)]
|
||||
|
||||
pub use crate::expr::{BoolExpr, Expr, ExprBuilder, ExprRef, NumExpr};
|
||||
pub use crate::signals::bool_ops::*;
|
||||
pub use crate::signals::cast::*;
|
||||
pub use crate::signals::cmp_ops::*;
|
||||
pub use crate::signals::num_ops::*;
|
||||
pub use crate::signals::Signal;
|
||||
pub use crate::{ArgusError, ArgusResult};
|
||||
|
|
|
|||
|
|
@ -1,18 +1,9 @@
|
|||
//! Concrete signal types
|
||||
//!
|
||||
//! In Argus, there are essentially 2 kinds of signals:
|
||||
//!
|
||||
//! 1. [`Signal<T>`] is a variable length signal with finitely many sampled points. This
|
||||
//! implies that the signal has a fixed start and end point (both inclusive) and can
|
||||
//! be iterated over.
|
||||
//! 2. [`ConstantSignal<T>`] is a signal that maintains a constant value throughtout
|
||||
//! its domain, and thus, do not require interpolation and extrapolation. Moreover,
|
||||
//! since they are defined over the entire time domain, they cannot be iterated over.
|
||||
pub mod bool_ops;
|
||||
pub mod cast;
|
||||
pub mod cmp_ops;
|
||||
//! Argus signals
|
||||
mod bool_ops;
|
||||
mod cast;
|
||||
mod cmp_ops;
|
||||
pub mod iter;
|
||||
pub mod num_ops;
|
||||
mod num_ops;
|
||||
pub mod traits;
|
||||
mod utils;
|
||||
|
||||
|
|
@ -31,9 +22,16 @@ use utils::intersect_bounds;
|
|||
use self::traits::LinearInterpolatable;
|
||||
use crate::{ArgusResult, Error};
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
/// Interpolation methods supported by Argus signals.
|
||||
///
|
||||
/// Defaults to `Linear` interpolation (which corresponds to constant interpolation for
|
||||
/// `bool` signals).
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub enum InterpolationMethod {
|
||||
/// Linear interpolation
|
||||
#[default]
|
||||
Linear,
|
||||
/// Interpolate from the nearest sample point.
|
||||
Nearest,
|
||||
}
|
||||
|
||||
|
|
@ -61,9 +59,12 @@ impl InterpolationMethod {
|
|||
}
|
||||
}
|
||||
|
||||
/// A single sample of a signal.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Sample<T> {
|
||||
/// The time point when this sample was taken.
|
||||
pub time: Duration,
|
||||
/// The value of the signal at th given sample.
|
||||
pub value: T,
|
||||
}
|
||||
|
||||
|
|
@ -73,13 +74,22 @@ pub struct Sample<T> {
|
|||
/// finite set of strictly monotonically increasing time points.
|
||||
#[derive(Default, Clone, Debug)]
|
||||
pub enum Signal<T> {
|
||||
/// An empty signal.
|
||||
///
|
||||
/// This is only used in special (usually error) scenarios.
|
||||
#[default]
|
||||
Empty,
|
||||
/// A signal that has a constant value for the entire time domain.
|
||||
Constant {
|
||||
/// The value of the signal for all time.
|
||||
value: T,
|
||||
},
|
||||
/// A finite set of signal values sampled at strictly monotonically increasing time
|
||||
/// points.
|
||||
Sampled {
|
||||
/// Values of the samples of the signal.
|
||||
values: Vec<T>,
|
||||
/// List of time points where the signal is sampled.
|
||||
time_points: Vec<Duration>,
|
||||
},
|
||||
}
|
||||
|
|
@ -302,6 +312,14 @@ impl<T> Signal<T> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Return the vector of points where the signal is sampled.
|
||||
///
|
||||
/// - If the signal is empty ([`Signal::Empty`]), the output is `None`.
|
||||
/// - If the signal is constant ([`Signal::Constant`]), the output is equivalent to
|
||||
/// `Some(vec![])` as there is no well defined list of sample points for a
|
||||
/// constant signal.
|
||||
/// - Finally, if the signal is sampled ([`Signal::Sampled`]), the output is the
|
||||
/// list of time points corresponding to the samples in the signal.
|
||||
pub fn time_points(&self) -> Option<Vec<&Duration>> {
|
||||
match self {
|
||||
Signal::Empty => None,
|
||||
|
|
@ -397,17 +415,33 @@ impl<T> Signal<T> {
|
|||
}
|
||||
|
||||
impl<T: Num> Signal<T> {
|
||||
/// Create a constant `0` signal
|
||||
pub fn zero() -> Self {
|
||||
Signal::constant(T::zero())
|
||||
}
|
||||
|
||||
/// Create a constant `1` signal
|
||||
pub fn one() -> Self {
|
||||
Signal::constant(T::one())
|
||||
}
|
||||
}
|
||||
|
||||
impl Signal<bool> {
|
||||
/// Create a constant `true` signal.
|
||||
pub fn const_true() -> Self {
|
||||
Signal::constant(true)
|
||||
}
|
||||
|
||||
/// Create a constant `false` signal.
|
||||
pub fn const_false() -> Self {
|
||||
Signal::constant(false)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "arbitrary"))]
|
||||
pub mod arbitrary {
|
||||
//! In this module, we use [`mod@proptest`] to define arbitrary generators for
|
||||
//! different signals.
|
||||
use proptest::prelude::*;
|
||||
use proptest::sample::SizeRange;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,22 @@
|
|||
//! Signal iterator.
|
||||
|
||||
use std::iter::{zip, Zip};
|
||||
use std::time::Duration;
|
||||
|
||||
use super::Signal;
|
||||
|
||||
/// An iterator over a [`Signal`].
|
||||
///
|
||||
/// This takes into account if the signal is iterable or not, i.e., it produces samples
|
||||
/// only for [`Signal::Sampled`] and empty iterators for [`Signal::Empty`] (as it
|
||||
/// contains no values) and [`Signal::Constant`] (as there is no well defined start and
|
||||
/// end to the signal).
|
||||
#[derive(Debug, Default)]
|
||||
pub enum Iter<'a, T> {
|
||||
#[doc(hidden)]
|
||||
#[default]
|
||||
Empty,
|
||||
#[doc(hidden)]
|
||||
Iter(Zip<core::slice::Iter<'a, Duration>, core::slice::Iter<'a, T>>),
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
//! Helper traits for Argus signals.
|
||||
|
||||
use std::any::Any;
|
||||
use std::cmp::Ordering;
|
||||
use std::time::Duration;
|
||||
|
|
@ -9,6 +11,10 @@ use crate::ArgusResult;
|
|||
|
||||
/// Trait for values that are linear interpolatable
|
||||
pub trait LinearInterpolatable {
|
||||
/// Compute the linear interpolation of two samples at `time`
|
||||
///
|
||||
/// This should assume that the `time` value is between the sample times of `a` and
|
||||
/// `b`. This should be enforced as an assertion.
|
||||
fn interpolate_at(a: &Sample<Self>, b: &Sample<Self>, time: Duration) -> Self
|
||||
where
|
||||
Self: Sized;
|
||||
|
|
@ -80,6 +86,8 @@ interpolate_for_num!(f64);
|
|||
/// This is mainly for external libraries to use for trait objects and downcasting to
|
||||
/// concrete [`Signal`] types.
|
||||
pub trait AnySignal {
|
||||
/// Convenience method to upcast a signal to [`std::any::Any`] for later downcasting
|
||||
/// to a concrete [`Signal`] type.
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
}
|
||||
|
||||
|
|
@ -92,6 +100,7 @@ impl<T: 'static> AnySignal for Signal<T> {
|
|||
macro_rules! impl_signal_cmp {
|
||||
($cmp:ident) => {
|
||||
paste! {
|
||||
/// Compute the time-wise comparison of two signals
|
||||
fn [<signal_ $cmp>](&self, other: &Rhs) -> Option<Signal<bool>> {
|
||||
self.signal_cmp(other, |ord| ord.[<is_ $cmp>]())
|
||||
}
|
||||
|
|
@ -120,6 +129,7 @@ pub trait SignalPartialOrd<Rhs = Self> {
|
|||
|
||||
/// Time-wise min-max of signal types
|
||||
pub trait SignalMinMax<Rhs = Self> {
|
||||
/// The output type of the signal after computing the min/max
|
||||
type Output;
|
||||
|
||||
/// Compute the time-wise min of two signals
|
||||
|
|
@ -131,24 +141,39 @@ pub trait SignalMinMax<Rhs = Self> {
|
|||
|
||||
/// Trait for converting between signal types
|
||||
pub trait SignalNumCast {
|
||||
/// Try to convert the signal values/samples to `i8`
|
||||
fn to_i8(&self) -> Option<Signal<i8>>;
|
||||
/// Try to convert the signal values/samples to `i16`
|
||||
fn to_i16(&self) -> Option<Signal<i16>>;
|
||||
/// Try to convert the signal values/samples to `i32`
|
||||
fn to_i32(&self) -> Option<Signal<i32>>;
|
||||
/// Try to convert the signal values/samples to `i64`
|
||||
fn to_i64(&self) -> Option<Signal<i64>>;
|
||||
/// Try to convert the signal values/samples to `u8`
|
||||
fn to_u8(&self) -> Option<Signal<u8>>;
|
||||
/// Try to convert the signal values/samples to `u16`
|
||||
fn to_u16(&self) -> Option<Signal<u16>>;
|
||||
/// Try to convert the signal values/samples to `u32`
|
||||
fn to_u32(&self) -> Option<Signal<u32>>;
|
||||
/// Try to convert the signal values/samples to `u64`
|
||||
fn to_u64(&self) -> Option<Signal<u64>>;
|
||||
/// Try to convert the signal values/samples to `f32`
|
||||
fn to_f32(&self) -> Option<Signal<f32>>;
|
||||
/// Try to convert the signal values/samples to `f64`
|
||||
fn to_f64(&self) -> Option<Signal<f64>>;
|
||||
}
|
||||
|
||||
/// Trait to cast signal onto some type
|
||||
pub trait TrySignalCast<T>: Sized + SignalNumCast {
|
||||
/// Try to cast the given signal to another numeric type.
|
||||
///
|
||||
/// This returns a [`ArgusError::InvalidCast`](crate::Error::InvalidCast) if
|
||||
/// some value in the signal isn't castable to the destination type.
|
||||
fn try_cast(&self) -> ArgusResult<T>;
|
||||
}
|
||||
|
||||
/// Trait for computing the absolute value of the samples in a signal
|
||||
pub trait SignalAbs {
|
||||
/// Compute the absolute value of the given signal
|
||||
fn abs(&self) -> Self;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue