feat: expose parser in argus

This commit is contained in:
Anand Balakrishnan 2023-10-04 14:46:32 -07:00
parent 50d5a0a78a
commit f97d593926
7 changed files with 241 additions and 66 deletions

View file

@ -119,7 +119,8 @@ pub enum ExprRef<'a> {
} }
/// An expression (either [`BoolExpr`] or [`NumExpr`]) /// An expression (either [`BoolExpr`] or [`NumExpr`])
#[derive(Clone, Debug, derive_more::From, derive_more::TryInto)] #[derive(Clone, Debug)]
#[enum_dispatch(AnyExpr)]
pub enum Expr { pub enum Expr {
/// A reference to a [`BoolExpr`] /// A reference to a [`BoolExpr`]
Bool(BoolExpr), Bool(BoolExpr),

View file

@ -18,3 +18,4 @@ log = "0.4.20"
paste = "1.0.14" paste = "1.0.14"
pyo3 = "0.19.2" pyo3 = "0.19.2"
pyo3-log = "0.8.3" pyo3-log = "0.8.3"
ariadne = "0.3.0"

View file

@ -3,7 +3,7 @@ from __future__ import annotations
from typing import List, Optional, Tuple, Type, Union from typing import List, Optional, Tuple, Type, Union
from . import _argus from . import _argus
from ._argus import dtype from ._argus import dtype, parse_expr
from .exprs import ConstBool, ConstFloat, ConstInt, ConstUInt, VarBool, VarFloat, VarInt, VarUInt from .exprs import ConstBool, ConstFloat, ConstInt, ConstUInt, VarBool, VarFloat, VarInt, VarUInt
from .signals import BoolSignal, FloatSignal, IntSignal, Signal, UnsignedIntSignal from .signals import BoolSignal, FloatSignal, IntSignal, Signal, UnsignedIntSignal
@ -87,6 +87,7 @@ def signal(
__all__ = [ __all__ = [
"dtype", "dtype",
"parse_expr",
"declare_var", "declare_var",
"literal", "literal",
"signal", "signal",

View file

@ -1,8 +1,12 @@
from typing import ClassVar, Literal, Protocol, TypeAlias, final from typing import ClassVar, Literal, TypeAlias, final
from typing_extensions import Self from typing_extensions import Self
class NumExpr(Protocol): def parse_expr(expr_str: str) -> Expr: ...
class Expr: ...
class NumExpr(Expr):
def __ge__(self, other: Self) -> NumExpr: ... def __ge__(self, other: Self) -> NumExpr: ...
def __gt__(self, other: Self) -> NumExpr: ... def __gt__(self, other: Self) -> NumExpr: ...
def __le__(self, other: Self) -> NumExpr: ... def __le__(self, other: Self) -> NumExpr: ...
@ -64,7 +68,7 @@ class Div(NumExpr):
class Abs(NumExpr): class Abs(NumExpr):
def __init__(self, arg: NumExpr) -> None: ... def __init__(self, arg: NumExpr) -> None: ...
class BoolExpr(Protocol): class BoolExpr(Expr):
def __and__(self, other: Self) -> BoolExpr: ... def __and__(self, other: Self) -> BoolExpr: ...
def __invert__(self) -> BoolExpr: ... def __invert__(self) -> BoolExpr: ...
def __or__(self, other: Self) -> BoolExpr: ... def __or__(self, other: Self) -> BoolExpr: ...
@ -192,5 +196,9 @@ class FloatSignal(Signal):
@final @final
class Trace: ... class Trace: ...
def eval_bool_semantics(expr: BoolExpr, trace: Trace) -> BoolSignal: ... def eval_bool_semantics(
def eval_robust_semantics(expr: BoolExpr, trace: Trace) -> BoolSignal: ... expr: BoolExpr, trace: Trace, *, interpolation_method: _InterpolationMethod = "linear"
) -> BoolSignal: ...
def eval_robust_semantics(
expr: BoolExpr, trace: Trace, *, interpolation_method: _InterpolationMethod = "linear"
) -> BoolSignal: ...

View file

@ -10,6 +10,7 @@ from argus._argus import (
ConstUInt, ConstUInt,
Div, Div,
Eventually, Eventually,
Expr,
Mul, Mul,
Negate, Negate,
Next, Next,
@ -46,4 +47,7 @@ __all__ = [
"VarFloat", "VarFloat",
"VarInt", "VarInt",
"VarUInt", "VarUInt",
"Expr",
"BoolExpr",
"NumExpr",
] ]

View file

@ -4,14 +4,61 @@ use argus::expr::*;
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::pyclass::CompareOp; use pyo3::pyclass::CompareOp;
/// A base expression
///
/// This is an abstract base class that provides an interface for all specific
#[pyclass(name = "Expr", subclass, module = "argus")]
#[derive(Debug, Clone)]
pub struct PyExpr;
impl PyExpr {
pub fn from_expr(py: Python, expr: Expr) -> PyResult<PyObject> {
match expr {
Expr::Bool(e) => PyBoolExpr::from_expr(py, e),
Expr::Num(e) => PyNumExpr::from_expr(py, e),
}
}
}
/// A base numeric expression /// A base numeric expression
/// ///
/// This is an abstract base class that provides an interface for all numeric /// This is an abstract base class that provides an interface for all numeric
/// expressions supported in Argus (literals, arithmetic, and so on). /// expressions supported in Argus (literals, arithmetic, and so on).
#[pyclass(name = "NumExpr", subclass, module = "argus")] #[pyclass(name = "NumExpr", subclass, extends = PyExpr, module = "argus")]
#[derive(Debug, Clone, derive_more::From)] #[derive(Debug, Clone, derive_more::From)]
pub struct PyNumExpr(pub Box<NumExpr>); pub struct PyNumExpr(pub Box<NumExpr>);
macro_rules! make_expr {
($py:expr, $expr:expr, $subclass:expr) => {
PyCell::new(
$py,
PyClassInitializer::from(PyExpr)
.add_subclass(Self(Box::new($expr)))
.add_subclass($subclass),
)
.map(|obj| obj.to_object($py))
};
}
impl PyNumExpr {
pub fn from_expr(py: Python, expr: NumExpr) -> PyResult<PyObject> {
match expr {
NumExpr::IntLit(_) => make_expr!(py, expr, ConstInt),
NumExpr::UIntLit(_) => make_expr!(py, expr, ConstUInt),
NumExpr::FloatLit(_) => make_expr!(py, expr, ConstFloat),
NumExpr::IntVar(_) => make_expr!(py, expr, VarInt),
NumExpr::UIntVar(_) => make_expr!(py, expr, VarUInt),
NumExpr::FloatVar(_) => make_expr!(py, expr, VarFloat),
NumExpr::Neg(_) => make_expr!(py, expr, Negate),
NumExpr::Add(_) => make_expr!(py, expr, Add),
NumExpr::Sub(_) => make_expr!(py, expr, Sub),
NumExpr::Mul(_) => make_expr!(py, expr, Mul),
NumExpr::Div(_) => make_expr!(py, expr, Div),
NumExpr::Abs(_) => make_expr!(py, expr, Abs),
}
}
}
#[pymethods] #[pymethods]
impl PyNumExpr { impl PyNumExpr {
fn __repr__(&self) -> String { fn __repr__(&self) -> String {
@ -61,8 +108,10 @@ pub struct ConstInt;
#[pymethods] #[pymethods]
impl ConstInt { impl ConstInt {
#[new] #[new]
fn new(val: i64) -> (Self, PyNumExpr) { fn new(val: i64) -> PyClassInitializer<Self> {
(Self, Box::new(NumExpr::IntLit(argus::expr::IntLit(val))).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::IntLit(argus::expr::IntLit(val))).into())
.add_subclass(Self)
} }
} }
@ -78,8 +127,10 @@ pub struct ConstUInt;
#[pymethods] #[pymethods]
impl ConstUInt { impl ConstUInt {
#[new] #[new]
fn new(val: u64) -> (Self, PyNumExpr) { fn new(val: u64) -> PyClassInitializer<Self> {
(Self, Box::new(NumExpr::UIntLit(argus::expr::UIntLit(val))).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::UIntLit(argus::expr::UIntLit(val))).into())
.add_subclass(Self)
} }
} }
@ -90,8 +141,10 @@ pub struct ConstFloat;
#[pymethods] #[pymethods]
impl ConstFloat { impl ConstFloat {
#[new] #[new]
fn new(val: f64) -> (Self, PyNumExpr) { fn new(val: f64) -> PyClassInitializer<ConstFloat> {
(Self, Box::new(NumExpr::FloatLit(argus::expr::FloatLit(val))).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::FloatLit(argus::expr::FloatLit(val))).into())
.add_subclass(Self)
} }
} }
@ -102,8 +155,10 @@ pub struct VarInt;
#[pymethods] #[pymethods]
impl VarInt { impl VarInt {
#[new] #[new]
fn new(name: String) -> (Self, PyNumExpr) { fn new(name: String) -> PyClassInitializer<Self> {
(Self, Box::new(NumExpr::IntVar(argus::expr::IntVar { name })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::IntVar(argus::expr::IntVar { name })).into())
.add_subclass(Self)
} }
} }
@ -114,8 +169,10 @@ pub struct VarUInt;
#[pymethods] #[pymethods]
impl VarUInt { impl VarUInt {
#[new] #[new]
fn new(name: String) -> (Self, PyNumExpr) { fn new(name: String) -> PyClassInitializer<Self> {
(Self, Box::new(NumExpr::UIntVar(argus::expr::UIntVar { name })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::UIntVar(argus::expr::UIntVar { name })).into())
.add_subclass(Self)
} }
} }
@ -126,8 +183,10 @@ pub struct VarFloat;
#[pymethods] #[pymethods]
impl VarFloat { impl VarFloat {
#[new] #[new]
fn new(name: String) -> (Self, PyNumExpr) { fn new(name: String) -> PyClassInitializer<Self> {
(Self, Box::new(NumExpr::FloatVar(argus::expr::FloatVar { name })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::FloatVar(argus::expr::FloatVar { name })).into())
.add_subclass(Self)
} }
} }
@ -138,9 +197,11 @@ pub struct Negate;
#[pymethods] #[pymethods]
impl Negate { impl Negate {
#[new] #[new]
fn new(arg: PyNumExpr) -> (Self, PyNumExpr) { fn new(arg: PyNumExpr) -> PyClassInitializer<Self> {
let arg = arg.0; let arg = arg.0;
(Self, Box::new(NumExpr::Neg(argus::expr::Neg { arg })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::Neg(argus::expr::Neg { arg })).into())
.add_subclass(Self)
} }
} }
@ -153,9 +214,11 @@ pub struct Add;
#[pymethods] #[pymethods]
impl Add { impl Add {
#[new] #[new]
fn new(args: Vec<PyNumExpr>) -> (Self, PyNumExpr) { fn new(args: Vec<PyNumExpr>) -> PyClassInitializer<Self> {
let args: Vec<NumExpr> = args.into_iter().map(|arg| *arg.0).collect(); let args: Vec<NumExpr> = args.into_iter().map(|arg| *arg.0).collect();
(Self, Box::new(NumExpr::Add(argus::expr::Add { args })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::Add(argus::expr::Add { args })).into())
.add_subclass(Self)
} }
} }
@ -165,10 +228,12 @@ pub struct Sub;
#[pymethods] #[pymethods]
impl Sub { impl Sub {
#[new] #[new]
fn new(lhs: PyNumExpr, rhs: PyNumExpr) -> (Self, PyNumExpr) { fn new(lhs: PyNumExpr, rhs: PyNumExpr) -> PyClassInitializer<Self> {
let lhs = lhs.0; let lhs = lhs.0;
let rhs = rhs.0; let rhs = rhs.0;
(Self, Box::new(NumExpr::Sub(argus::expr::Sub { lhs, rhs })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::Sub(argus::expr::Sub { lhs, rhs })).into())
.add_subclass(Self)
} }
} }
@ -178,9 +243,11 @@ pub struct Mul;
#[pymethods] #[pymethods]
impl Mul { impl Mul {
#[new] #[new]
fn new(args: Vec<PyNumExpr>) -> (Self, PyNumExpr) { fn new(args: Vec<PyNumExpr>) -> PyClassInitializer<Self> {
let args: Vec<NumExpr> = args.into_iter().map(|arg| *arg.0).collect(); let args: Vec<NumExpr> = args.into_iter().map(|arg| *arg.0).collect();
(Self, Box::new(NumExpr::Mul(argus::expr::Mul { args })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::Mul(argus::expr::Mul { args })).into())
.add_subclass(Self)
} }
} }
@ -190,13 +257,12 @@ pub struct Div;
#[pymethods] #[pymethods]
impl Div { impl Div {
#[new] #[new]
fn new(dividend: PyNumExpr, divisor: PyNumExpr) -> (Self, PyNumExpr) { fn new(dividend: PyNumExpr, divisor: PyNumExpr) -> PyClassInitializer<Self> {
let dividend = dividend.0; let dividend = dividend.0;
let divisor = divisor.0; let divisor = divisor.0;
( PyClassInitializer::from(PyExpr)
Self, .add_subclass(Box::new(NumExpr::Div(argus::expr::Div { dividend, divisor })).into())
Box::new(NumExpr::Div(argus::expr::Div { dividend, divisor })).into(), .add_subclass(Self)
)
} }
} }
@ -206,16 +272,36 @@ pub struct Abs;
#[pymethods] #[pymethods]
impl Abs { impl Abs {
#[new] #[new]
fn new(arg: PyNumExpr) -> (Self, PyNumExpr) { fn new(arg: PyNumExpr) -> PyClassInitializer<Self> {
let arg = arg.0; let arg = arg.0;
(Self, Box::new(NumExpr::Abs(argus::expr::Abs { arg })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(NumExpr::Abs(argus::expr::Abs { arg })).into())
.add_subclass(Self)
} }
} }
#[pyclass(name = "BoolExpr", subclass, module = "argus")] #[pyclass(name = "BoolExpr", subclass, extends=PyExpr, module = "argus")]
#[derive(Debug, Clone, derive_more::From)] #[derive(Debug, Clone, derive_more::From)]
pub struct PyBoolExpr(pub Box<BoolExpr>); pub struct PyBoolExpr(pub Box<BoolExpr>);
impl PyBoolExpr {
pub fn from_expr(py: Python, expr: BoolExpr) -> PyResult<PyObject> {
match expr {
BoolExpr::BoolLit(_) => make_expr!(py, expr, ConstBool),
BoolExpr::BoolVar(_) => make_expr!(py, expr, VarBool),
BoolExpr::Cmp(_) => make_expr!(py, expr, Cmp),
BoolExpr::Not(_) => make_expr!(py, expr, Not),
BoolExpr::And(_) => make_expr!(py, expr, And),
BoolExpr::Or(_) => make_expr!(py, expr, Or),
BoolExpr::Next(_) => make_expr!(py, expr, Next),
BoolExpr::Oracle(_) => make_expr!(py, expr, Oracle),
BoolExpr::Always(_) => make_expr!(py, expr, Always),
BoolExpr::Eventually(_) => make_expr!(py, expr, Eventually),
BoolExpr::Until(_) => make_expr!(py, expr, Until),
}
}
}
#[pymethods] #[pymethods]
impl PyBoolExpr { impl PyBoolExpr {
fn __repr__(&self) -> String { fn __repr__(&self) -> String {
@ -241,8 +327,10 @@ pub struct ConstBool;
#[pymethods] #[pymethods]
impl ConstBool { impl ConstBool {
#[new] #[new]
fn new(val: bool) -> (Self, PyBoolExpr) { fn new(val: bool) -> PyClassInitializer<Self> {
(Self, Box::new(BoolExpr::BoolLit(argus::expr::BoolLit(val))).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(BoolExpr::BoolLit(argus::expr::BoolLit(val))).into())
.add_subclass(Self)
} }
} }
@ -252,8 +340,10 @@ pub struct VarBool;
#[pymethods] #[pymethods]
impl VarBool { impl VarBool {
#[new] #[new]
fn new(name: String) -> (Self, PyBoolExpr) { fn new(name: String) -> PyClassInitializer<Self> {
(Self, Box::new(BoolExpr::BoolVar(argus::expr::BoolVar { name })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(BoolExpr::BoolVar(argus::expr::BoolVar { name })).into())
.add_subclass(Self)
} }
} }
@ -265,11 +355,13 @@ pub struct Cmp;
pub struct PyOrdering(Ordering); pub struct PyOrdering(Ordering);
impl Cmp { impl Cmp {
fn new(op: PyOrdering, lhs: PyNumExpr, rhs: PyNumExpr) -> (Self, PyBoolExpr) { fn new(op: PyOrdering, lhs: PyNumExpr, rhs: PyNumExpr) -> PyClassInitializer<Self> {
let op = op.0; let op = op.0;
let lhs = lhs.0; let lhs = lhs.0;
let rhs = rhs.0; let rhs = rhs.0;
(Self, Box::new(BoolExpr::Cmp(argus::expr::Cmp { op, lhs, rhs })).into()) PyClassInitializer::from(PyExpr)
.add_subclass(Box::new(BoolExpr::Cmp(argus::expr::Cmp { op, lhs, rhs })).into())
.add_subclass(Self)
} }
} }
@ -312,9 +404,11 @@ pub struct Not;
#[pymethods] #[pymethods]
impl Not { impl Not {
#[new] #[new]
fn new(arg: PyBoolExpr) -> (Self, PyBoolExpr) { fn new(arg: PyBoolExpr) -> PyClassInitializer<Self> {
let arg = arg.0; let arg = arg.0;
(Self, PyBoolExpr(Box::new(BoolExpr::Not(argus::expr::Not { arg })))) PyClassInitializer::from(PyExpr)
.add_subclass(PyBoolExpr(Box::new(BoolExpr::Not(argus::expr::Not { arg }))))
.add_subclass(Self)
} }
} }
@ -324,9 +418,11 @@ pub struct And;
#[pymethods] #[pymethods]
impl And { impl And {
#[new] #[new]
fn new(args: Vec<PyBoolExpr>) -> (Self, PyBoolExpr) { fn new(args: Vec<PyBoolExpr>) -> PyClassInitializer<Self> {
let args: Vec<BoolExpr> = args.into_iter().map(|arg| *arg.0).collect(); let args: Vec<BoolExpr> = args.into_iter().map(|arg| *arg.0).collect();
(Self, PyBoolExpr(Box::new(BoolExpr::And(argus::expr::And { args })))) PyClassInitializer::from(PyExpr)
.add_subclass(PyBoolExpr(Box::new(BoolExpr::And(argus::expr::And { args }))))
.add_subclass(Self)
} }
} }
@ -336,9 +432,11 @@ pub struct Or;
#[pymethods] #[pymethods]
impl Or { impl Or {
#[new] #[new]
fn new(args: Vec<PyBoolExpr>) -> (Self, PyBoolExpr) { fn new(args: Vec<PyBoolExpr>) -> PyClassInitializer<Self> {
let args: Vec<BoolExpr> = args.into_iter().map(|arg| *arg.0).collect(); let args: Vec<BoolExpr> = args.into_iter().map(|arg| *arg.0).collect();
(Self, PyBoolExpr(Box::new(BoolExpr::Or(argus::expr::Or { args })))) PyClassInitializer::from(PyExpr)
.add_subclass(PyBoolExpr(Box::new(BoolExpr::Or(argus::expr::Or { args }))))
.add_subclass(Self)
} }
} }
@ -348,9 +446,28 @@ pub struct Next;
#[pymethods] #[pymethods]
impl Next { impl Next {
#[new] #[new]
fn new(arg: PyBoolExpr) -> (Self, PyBoolExpr) { fn new(arg: PyBoolExpr) -> PyClassInitializer<Self> {
let arg = arg.0; let arg = arg.0;
(Self, PyBoolExpr(Box::new(BoolExpr::Next(argus::expr::Next { arg })))) PyClassInitializer::from(PyExpr)
.add_subclass(PyBoolExpr(Box::new(BoolExpr::Next(argus::expr::Next { arg }))))
.add_subclass(Self)
}
}
#[pyclass(extends=PyBoolExpr, module = "argus")]
pub struct Oracle;
#[pymethods]
impl Oracle {
#[new]
fn new(arg: PyBoolExpr, steps: usize) -> PyClassInitializer<Self> {
let arg = arg.0;
PyClassInitializer::from(PyExpr)
.add_subclass(PyBoolExpr(Box::new(BoolExpr::Oracle(argus::expr::Oracle {
arg,
steps,
}))))
.add_subclass(Self)
} }
} }
@ -361,7 +478,7 @@ pub struct Always;
impl Always { impl Always {
#[new] #[new]
#[pyo3(signature = (arg, *, interval=(None, None)))] #[pyo3(signature = (arg, *, interval=(None, None)))]
fn new(arg: PyBoolExpr, interval: (Option<f64>, Option<f64>)) -> (Self, PyBoolExpr) { fn new(arg: PyBoolExpr, interval: (Option<f64>, Option<f64>)) -> PyClassInitializer<Self> {
let arg = arg.0; let arg = arg.0;
let interval: Interval = match interval { let interval: Interval = match interval {
(None, None) => (..).into(), (None, None) => (..).into(),
@ -369,10 +486,12 @@ impl Always {
(Some(a), None) => (Duration::from_secs_f64(a)..).into(), (Some(a), None) => (Duration::from_secs_f64(a)..).into(),
(Some(a), Some(b)) => (Duration::from_secs_f64(a)..Duration::from_secs_f64(b)).into(), (Some(a), Some(b)) => (Duration::from_secs_f64(a)..Duration::from_secs_f64(b)).into(),
}; };
( PyClassInitializer::from(PyExpr)
Self, .add_subclass(PyBoolExpr(Box::new(BoolExpr::Always(argus::expr::Always {
PyBoolExpr(Box::new(BoolExpr::Always(argus::expr::Always { arg, interval }))), arg,
) interval,
}))))
.add_subclass(Self)
} }
} }
@ -383,7 +502,7 @@ pub struct Eventually;
impl Eventually { impl Eventually {
#[new] #[new]
#[pyo3(signature = (arg, *, interval=(None, None)))] #[pyo3(signature = (arg, *, interval=(None, None)))]
fn new(arg: PyBoolExpr, interval: (Option<f64>, Option<f64>)) -> (Self, PyBoolExpr) { fn new(arg: PyBoolExpr, interval: (Option<f64>, Option<f64>)) -> PyClassInitializer<Self> {
let arg = arg.0; let arg = arg.0;
let interval: Interval = match interval { let interval: Interval = match interval {
(None, None) => (..).into(), (None, None) => (..).into(),
@ -391,13 +510,12 @@ impl Eventually {
(Some(a), None) => (Duration::from_secs_f64(a)..).into(), (Some(a), None) => (Duration::from_secs_f64(a)..).into(),
(Some(a), Some(b)) => (Duration::from_secs_f64(a)..Duration::from_secs_f64(b)).into(), (Some(a), Some(b)) => (Duration::from_secs_f64(a)..Duration::from_secs_f64(b)).into(),
}; };
( PyClassInitializer::from(PyExpr)
Self, .add_subclass(PyBoolExpr(Box::new(BoolExpr::Eventually(argus::expr::Eventually {
PyBoolExpr(Box::new(BoolExpr::Eventually(argus::expr::Eventually {
arg, arg,
interval, interval,
}))), }))))
) .add_subclass(Self)
} }
} }
@ -408,7 +526,7 @@ pub struct Until;
impl Until { impl Until {
#[new] #[new]
#[pyo3(signature = (lhs, rhs, *, interval=(None, None)))] #[pyo3(signature = (lhs, rhs, *, interval=(None, None)))]
fn new(lhs: PyBoolExpr, rhs: PyBoolExpr, interval: (Option<f64>, Option<f64>)) -> (Self, PyBoolExpr) { fn new(lhs: PyBoolExpr, rhs: PyBoolExpr, interval: (Option<f64>, Option<f64>)) -> PyClassInitializer<Self> {
let lhs = lhs.0; let lhs = lhs.0;
let rhs = rhs.0; let rhs = rhs.0;
let interval: Interval = match interval { let interval: Interval = match interval {
@ -417,14 +535,19 @@ impl Until {
(Some(a), None) => (Duration::from_secs_f64(a)..).into(), (Some(a), None) => (Duration::from_secs_f64(a)..).into(),
(Some(a), Some(b)) => (Duration::from_secs_f64(a)..Duration::from_secs_f64(b)).into(), (Some(a), Some(b)) => (Duration::from_secs_f64(a)..Duration::from_secs_f64(b)).into(),
}; };
( PyClassInitializer::from(PyExpr)
Self, .add_subclass(PyBoolExpr(Box::new(BoolExpr::Until(argus::expr::Until {
PyBoolExpr(Box::new(BoolExpr::Until(argus::expr::Until { lhs, rhs, interval }))), lhs,
) rhs,
interval,
}))))
.add_subclass(Self)
} }
} }
pub fn init(_py: Python, m: &PyModule) -> PyResult<()> { pub fn init(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<PyExpr>()?;
m.add_class::<PyNumExpr>()?; m.add_class::<PyNumExpr>()?;
m.add_class::<ConstInt>()?; m.add_class::<ConstInt>()?;
m.add_class::<ConstUInt>()?; m.add_class::<ConstUInt>()?;

View file

@ -3,6 +3,8 @@ mod semantics;
mod signals; mod signals;
use argus::Error as ArgusError; use argus::Error as ArgusError;
use ariadne::Source;
use expr::PyExpr;
use pyo3::exceptions::{PyKeyError, PyRuntimeError, PyTypeError, PyValueError}; use pyo3::exceptions::{PyKeyError, PyRuntimeError, PyTypeError, PyValueError};
use pyo3::prelude::*; use pyo3::prelude::*;
use pyo3::types::{PyBool, PyFloat, PyInt, PyType}; use pyo3::types::{PyBool, PyFloat, PyInt, PyType};
@ -68,12 +70,47 @@ impl DType {
} }
} }
/// Parse a string expression into a concrete Argus expression.
#[pyfunction]
fn parse_expr(expr_str: &str) -> PyResult<PyObject> {
use ariadne::{Color, Label, Report, ReportKind};
match argus::parse_str(expr_str) {
Ok(expr) => Python::with_gil(|py| PyExpr::from_expr(py, expr)),
Err(errs) => {
let mut buf = Vec::new();
{
errs.into_iter().for_each(|e| {
Report::build(ReportKind::Error, (), e.span().start)
.with_message(e.to_string())
.with_label(
Label::new(e.span().into_range())
.with_message(e.reason().to_string())
.with_color(Color::Red),
)
.with_labels(e.contexts().map(|(label, span)| {
Label::new(span.into_range())
.with_message(format!("while parsing this {}", label))
.with_color(Color::Yellow)
}))
.finish()
.write(Source::from(expr_str.to_owned()), &mut buf)
.unwrap()
});
}
let output = std::str::from_utf8(buf.as_slice())?.to_owned();
Err(PyValueError::new_err(output))
}
}
}
#[pymodule] #[pymodule]
#[pyo3(name = "_argus")] #[pyo3(name = "_argus")]
fn pyargus(py: Python, m: &PyModule) -> PyResult<()> { fn pyargus(py: Python, m: &PyModule) -> PyResult<()> {
pyo3_log::init(); pyo3_log::init();
m.add_class::<DType>()?; m.add_class::<DType>()?;
m.add_function(wrap_pyfunction!(parse_expr, m)?)?;
expr::init(py, m)?; expr::init(py, m)?;
signals::init(py, m)?; signals::init(py, m)?;