feat!(argus-parser): complete parser
This changes the API for `ExprBuilder`, but that is OK.
This commit is contained in:
parent
17042a2544
commit
2319668e2b
9 changed files with 545 additions and 146 deletions
|
|
@ -1,7 +1,305 @@
|
|||
//! # Argus logic syntax
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use argus_core::ExprBuilder;
|
||||
mod lexer;
|
||||
mod parser;
|
||||
|
||||
use chumsky::prelude::Rich;
|
||||
pub use lexer::{lexer, Error as LexError, Span, Token};
|
||||
pub use parser::{parser, Expr, Interval};
|
||||
|
||||
pub fn parse_str(src: &str) -> Result<argus_core::Expr, Vec<Rich<'_, String>>> {
|
||||
use chumsky::prelude::{Input, Parser};
|
||||
|
||||
let (tokens, lex_errors) = lexer().parse(src.clone()).into_output_errors();
|
||||
|
||||
let (parsed, parse_errors) = if let Some(tokens) = &tokens {
|
||||
parser()
|
||||
.parse(tokens.as_slice().spanned((src.len()..src.len()).into()))
|
||||
.into_output_errors()
|
||||
} else {
|
||||
(None, Vec::new())
|
||||
};
|
||||
|
||||
let (expr, expr_errors) = if let Some((ast, span)) = parsed {
|
||||
let mut expr_builder = ExprBuilder::new();
|
||||
let result = ast_to_expr(&ast, span, &mut expr_builder);
|
||||
match result {
|
||||
Ok(expr) => (Some(expr), vec![]),
|
||||
Err(err) => (None, vec![err]),
|
||||
}
|
||||
} else {
|
||||
(None, vec![])
|
||||
};
|
||||
|
||||
let errors: Vec<_> = lex_errors
|
||||
.into_iter()
|
||||
.map(|e| e.map_token(|c| c.to_string()))
|
||||
.chain(parse_errors.into_iter().map(|e| e.map_token(|tok| tok.to_string())))
|
||||
.chain(expr_errors.into_iter().map(|e| e.map_token(|tok| tok.to_string())))
|
||||
.map(|e| e.into_owned())
|
||||
.collect();
|
||||
if !errors.is_empty() {
|
||||
Err(errors)
|
||||
} else {
|
||||
expr.ok_or(errors)
|
||||
}
|
||||
}
|
||||
|
||||
fn interval_convert(interval: &Interval<'_>) -> argus_core::Interval {
|
||||
use core::ops::Bound;
|
||||
let a = if let Some(a) = &interval.a {
|
||||
match &a.0 {
|
||||
Expr::UInt(value) => Bound::Included(Duration::from_secs(*value)),
|
||||
Expr::Float(value) => Bound::Included(Duration::from_secs_f64(*value)),
|
||||
_ => unreachable!("must be valid numeric literal."),
|
||||
}
|
||||
} else {
|
||||
Bound::Unbounded
|
||||
};
|
||||
let b = if let Some(b) = &interval.b {
|
||||
match &b.0 {
|
||||
Expr::UInt(value) => Bound::Included(Duration::from_secs(*value)),
|
||||
Expr::Float(value) => Bound::Included(Duration::from_secs_f64(*value)),
|
||||
_ => unreachable!("must be valid numeric literal."),
|
||||
}
|
||||
} else {
|
||||
Bound::Unbounded
|
||||
};
|
||||
argus_core::Interval::new(a, b)
|
||||
}
|
||||
|
||||
/// Convert a parsed [`Expr`] into an [Argus `Expr`](argus_core::Expr)
|
||||
fn ast_to_expr<'tokens, 'src: 'tokens>(
|
||||
ast: &Expr<'src>,
|
||||
span: lexer::Span,
|
||||
ctx: &mut ExprBuilder,
|
||||
) -> Result<argus_core::Expr, Rich<'tokens, Token<'src>, lexer::Span>> {
|
||||
match ast {
|
||||
Expr::Error => unreachable!("Errors should have been caught by parser"),
|
||||
Expr::Bool(value) => Ok(ctx.bool_const(*value).into()),
|
||||
Expr::Int(value) => Ok(ctx.int_const(*value).into()),
|
||||
Expr::UInt(value) => Ok(ctx.uint_const(*value).into()),
|
||||
Expr::Float(value) => Ok(ctx.float_const(*value).into()),
|
||||
Expr::Var { name, kind } => match kind {
|
||||
parser::Type::Unknown => Err(Rich::custom(span, "All variables must have defined type by now.")),
|
||||
parser::Type::Bool => ctx
|
||||
.bool_var(name.to_string())
|
||||
.map(|var| var.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string())),
|
||||
parser::Type::UInt => ctx
|
||||
.uint_var(name.to_string())
|
||||
.map(|var| var.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string())),
|
||||
parser::Type::Int => ctx
|
||||
.int_var(name.to_string())
|
||||
.map(|var| var.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string())),
|
||||
parser::Type::Float => ctx
|
||||
.float_var(name.to_string())
|
||||
.map(|var| var.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string())),
|
||||
},
|
||||
Expr::Unary { op, interval, arg } => {
|
||||
let arg = ast_to_expr(&arg.0, arg.1, ctx)?;
|
||||
let interval = interval.as_ref().map(|(i, span)| (interval_convert(i), span));
|
||||
match op {
|
||||
parser::UnaryOps::Neg => {
|
||||
assert!(interval.is_none());
|
||||
let argus_core::Expr::Num(arg) = arg else {
|
||||
unreachable!("- must have numeric expression argument");
|
||||
};
|
||||
Ok(ctx.make_neg(Box::new(arg)).into())
|
||||
}
|
||||
parser::UnaryOps::Not => {
|
||||
assert!(interval.is_none());
|
||||
let argus_core::Expr::Bool(arg) = arg else {
|
||||
unreachable!("`Not` must have boolean expression argument");
|
||||
};
|
||||
Ok(ctx.make_not(Box::new(arg)).into())
|
||||
}
|
||||
parser::UnaryOps::Next => {
|
||||
use core::ops::Bound;
|
||||
let argus_core::Expr::Bool(arg) = arg else {
|
||||
unreachable!("`Next` must have boolean expression argument");
|
||||
};
|
||||
match interval {
|
||||
Some((interval, ispan)) => {
|
||||
let steps: usize = match (interval.start, interval.end) {
|
||||
(Bound::Included(start), Bound::Included(end)) => (end - start).as_secs() as usize,
|
||||
_ => {
|
||||
return Err(Rich::custom(
|
||||
*ispan,
|
||||
"Oracle operation (X[..]) cannot have unbounded intervals",
|
||||
))
|
||||
}
|
||||
};
|
||||
Ok(ctx.make_oracle(steps, Box::new(arg)).into())
|
||||
}
|
||||
None => Ok(ctx.make_next(Box::new(arg)).into()),
|
||||
}
|
||||
}
|
||||
parser::UnaryOps::Always => {
|
||||
let argus_core::Expr::Bool(arg) = arg else {
|
||||
unreachable!("`Always` must have boolean expression argument");
|
||||
};
|
||||
match interval {
|
||||
Some((interval, _)) => Ok(ctx.make_timed_always(interval, Box::new(arg)).into()),
|
||||
None => Ok(ctx.make_always(Box::new(arg)).into()),
|
||||
}
|
||||
}
|
||||
parser::UnaryOps::Eventually => {
|
||||
let argus_core::Expr::Bool(arg) = arg else {
|
||||
unreachable!("`Eventually` must have boolean expression argument");
|
||||
};
|
||||
match interval {
|
||||
Some((interval, _)) => Ok(ctx.make_timed_eventually(interval, Box::new(arg)).into()),
|
||||
None => Ok(ctx.make_eventually(Box::new(arg)).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Expr::Binary {
|
||||
op,
|
||||
interval,
|
||||
args: (lhs, rhs),
|
||||
} => {
|
||||
let lhs = ast_to_expr(&lhs.0, lhs.1, ctx)?;
|
||||
let rhs = ast_to_expr(&rhs.0, rhs.1, ctx)?;
|
||||
let interval = interval.as_ref().map(|(i, span)| (interval_convert(i), span));
|
||||
|
||||
match op {
|
||||
parser::BinaryOps::Add => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Add` must have numeric expression arguments");
|
||||
};
|
||||
ctx.make_add([lhs, rhs])
|
||||
.map(|ex| ex.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string()))
|
||||
}
|
||||
parser::BinaryOps::Sub => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Sub` must have numeric expression arguments");
|
||||
};
|
||||
Ok(ctx.make_sub(Box::new(lhs), Box::new(rhs)).into())
|
||||
}
|
||||
parser::BinaryOps::Mul => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Mul` must have numeric expression arguments");
|
||||
};
|
||||
ctx.make_mul([lhs, rhs])
|
||||
.map(|ex| ex.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string()))
|
||||
}
|
||||
parser::BinaryOps::Div => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Div` must have numeric expression arguments");
|
||||
};
|
||||
Ok(ctx.make_div(Box::new(lhs), Box::new(rhs)).into())
|
||||
}
|
||||
parser::BinaryOps::Lt => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("Relational operation must have numeric expression arguments");
|
||||
};
|
||||
Ok(ctx.make_lt(Box::new(lhs), Box::new(rhs)).into())
|
||||
}
|
||||
parser::BinaryOps::Le => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("Relational operation must have numeric expression arguments");
|
||||
};
|
||||
Ok(ctx.make_le(Box::new(lhs), Box::new(rhs)).into())
|
||||
}
|
||||
parser::BinaryOps::Gt => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("Relational operation must have numeric expression arguments");
|
||||
};
|
||||
Ok(ctx.make_gt(Box::new(lhs), Box::new(rhs)).into())
|
||||
}
|
||||
parser::BinaryOps::Ge => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("Relational operation must have numeric expression arguments");
|
||||
};
|
||||
Ok(ctx.make_ge(Box::new(lhs), Box::new(rhs)).into())
|
||||
}
|
||||
parser::BinaryOps::Eq => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("Relational operation must have numeric expression arguments");
|
||||
};
|
||||
Ok(ctx.make_eq(Box::new(lhs), Box::new(rhs)).into())
|
||||
}
|
||||
parser::BinaryOps::Neq => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Num(lhs), argus_core::Expr::Num(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("Relational operation must have numeric expression arguments");
|
||||
};
|
||||
Ok(ctx.make_neq(Box::new(lhs), Box::new(rhs)).into())
|
||||
}
|
||||
parser::BinaryOps::And => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Bool(lhs), argus_core::Expr::Bool(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`And` must have boolean expression arguments");
|
||||
};
|
||||
ctx.make_and([lhs, rhs])
|
||||
.map(|ex| ex.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string()))
|
||||
}
|
||||
parser::BinaryOps::Or => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Bool(lhs), argus_core::Expr::Bool(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Or` must have boolean expression arguments");
|
||||
};
|
||||
ctx.make_or([lhs, rhs])
|
||||
.map(|ex| ex.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string()))
|
||||
}
|
||||
parser::BinaryOps::Implies => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Bool(lhs), argus_core::Expr::Bool(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Implies` must have boolean expression arguments");
|
||||
};
|
||||
ctx.make_implies(Box::new(lhs), Box::new(rhs))
|
||||
.map(|ex| ex.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string()))
|
||||
}
|
||||
parser::BinaryOps::Equiv => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Bool(lhs), argus_core::Expr::Bool(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Equiv` must have boolean expression arguments");
|
||||
};
|
||||
ctx.make_equiv(Box::new(lhs), Box::new(rhs))
|
||||
.map(|ex| ex.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string()))
|
||||
}
|
||||
parser::BinaryOps::Xor => {
|
||||
assert!(interval.is_none());
|
||||
let (argus_core::Expr::Bool(lhs), argus_core::Expr::Bool(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Xor` must have boolean expression arguments");
|
||||
};
|
||||
ctx.make_xor(Box::new(lhs), Box::new(rhs))
|
||||
.map(|ex| ex.into())
|
||||
.map_err(|err| Rich::custom(span, err.to_string()))
|
||||
}
|
||||
parser::BinaryOps::Until => {
|
||||
let (argus_core::Expr::Bool(lhs), argus_core::Expr::Bool(rhs)) = (lhs, rhs) else {
|
||||
unreachable!("`Until` must have boolean expression arguments");
|
||||
};
|
||||
match interval {
|
||||
Some((interval, _)) => Ok(ctx.make_timed_until(interval, Box::new(lhs), Box::new(rhs)).into()),
|
||||
None => Ok(ctx.make_until(Box::new(lhs), Box::new(rhs)).into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,8 +37,8 @@ impl Type {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub struct Interval<'src> {
|
||||
a: Box<Spanned<Expr<'src>>>,
|
||||
b: Box<Spanned<Expr<'src>>>,
|
||||
pub a: Option<Box<Spanned<Expr<'src>>>>,
|
||||
pub b: Option<Box<Spanned<Expr<'src>>>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
|
|
@ -134,38 +134,40 @@ impl<'src> Expr<'src> {
|
|||
}
|
||||
|
||||
/// Make untyped (`Type::Unknown`) expressions into the given type.
|
||||
/// Returns a boolean flag to denote successful transformation or not.
|
||||
fn make_typed(&mut self, ty: Type) -> bool {
|
||||
match self {
|
||||
Expr::Var { name: _, kind } => {
|
||||
*kind = ty;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
fn make_typed(mut self, ty: Type) -> Self {
|
||||
if let Expr::Var { name: _, kind } = &mut self {
|
||||
*kind = ty;
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
fn unary_op(op: UnaryOps, arg: Box<Spanned<Self>>, interval: Option<Spanned<Interval<'src>>>) -> Self {
|
||||
let mut arg = arg;
|
||||
(*arg).0.make_typed(op.default_type());
|
||||
fn unary_op(op: UnaryOps, arg: Spanned<Self>, interval: Option<Spanned<Interval<'src>>>) -> Self {
|
||||
let arg = Box::new((arg.0.make_typed(op.default_type()), arg.1));
|
||||
Self::Unary { op, interval, arg }
|
||||
}
|
||||
|
||||
fn binary_op(
|
||||
op: BinaryOps,
|
||||
args: (Box<Spanned<Self>>, Box<Spanned<Self>>),
|
||||
args: (Spanned<Self>, Spanned<Self>),
|
||||
interval: Option<Spanned<Interval<'src>>>,
|
||||
) -> Self {
|
||||
let mut args = args;
|
||||
|
||||
let lhs = &mut (*args.0).0;
|
||||
let rhs = &mut (*args.1).0;
|
||||
let (lhs, lspan) = args.0;
|
||||
let (rhs, rspan) = args.1;
|
||||
|
||||
let common_type = lhs.get_type().get_common_cast(rhs.get_type());
|
||||
lhs.make_typed(common_type);
|
||||
rhs.make_typed(common_type);
|
||||
let common_type = if Type::Unknown == common_type {
|
||||
op.default_type()
|
||||
} else {
|
||||
common_type
|
||||
};
|
||||
let lhs = Box::new((lhs.make_typed(common_type), lspan));
|
||||
let rhs = Box::new((rhs.make_typed(common_type), rspan));
|
||||
|
||||
Self::Binary { op, interval, args }
|
||||
Self::Binary {
|
||||
op,
|
||||
interval,
|
||||
args: (lhs, rhs),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +181,7 @@ pub type Error<'tokens, 'src> = extra::Err<Rich<'tokens, Token<'src>, Span>>;
|
|||
// to understand.
|
||||
type ParserInput<'tokens, 'src> = SpannedInput<Token<'src>, Span, &'tokens [(Token<'src>, Span)]>;
|
||||
|
||||
pub fn num_expr_parser<'tokens, 'src: 'tokens>(
|
||||
fn num_expr_parser<'tokens, 'src: 'tokens>(
|
||||
) -> impl Parser<'tokens, ParserInput<'tokens, 'src>, Spanned<Expr<'src>>, Error<'tokens, 'src>> + Clone {
|
||||
recursive(|num_expr| {
|
||||
let var = select! { Token::Ident(name) => Expr::Var{ name, kind: Type::default()} }.labelled("variable");
|
||||
|
|
@ -210,7 +212,7 @@ pub fn num_expr_parser<'tokens, 'src: 'tokens>(
|
|||
.repeated()
|
||||
.foldr(num_atom, |op, rhs| {
|
||||
let span = op.1.start..rhs.1.end;
|
||||
(Expr::unary_op(op.0, Box::new(rhs), None), span.into())
|
||||
(Expr::unary_op(op.0, rhs, None), span.into())
|
||||
});
|
||||
|
||||
// Product ops (multiply and divide) have equal precedence
|
||||
|
|
@ -223,7 +225,7 @@ pub fn num_expr_parser<'tokens, 'src: 'tokens>(
|
|||
.clone()
|
||||
.foldl(op.then(neg_op).repeated(), |a, (op, b)| {
|
||||
let span = a.1.start..b.1.end;
|
||||
(Expr::binary_op(op, (Box::new(a), Box::new(b)), None), span.into())
|
||||
(Expr::binary_op(op, (a, b), None), span.into())
|
||||
})
|
||||
.boxed()
|
||||
};
|
||||
|
|
@ -238,7 +240,7 @@ pub fn num_expr_parser<'tokens, 'src: 'tokens>(
|
|||
.clone()
|
||||
.foldl(op.then(product_op).repeated(), |a, (op, b)| {
|
||||
let span = a.1.start..b.1.end;
|
||||
(Expr::binary_op(op, (Box::new(a), Box::new(b)), None), span.into())
|
||||
(Expr::binary_op(op, (a, b), None), span.into())
|
||||
})
|
||||
.boxed()
|
||||
};
|
||||
|
|
@ -251,7 +253,6 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
) -> impl Parser<'tokens, ParserInput<'tokens, 'src>, Spanned<Expr<'src>>, Error<'tokens, 'src>> + Clone {
|
||||
let interval = {
|
||||
let num_literal = select! {
|
||||
Token::Int(val) => Expr::Int(val),
|
||||
Token::UInt(val) => Expr::UInt(val),
|
||||
Token::Float(val) => Expr::Float(val),
|
||||
}
|
||||
|
|
@ -259,17 +260,17 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
let sep = just(Token::Comma).or(just(Token::DotDot));
|
||||
|
||||
num_literal
|
||||
.or_not()
|
||||
.then_ignore(sep)
|
||||
.then(num_literal)
|
||||
.then(num_literal.or_not())
|
||||
.delimited_by(just(Token::LBracket), just(Token::RBracket))
|
||||
.map(|(a, b)| {
|
||||
let span = a.1.start..b.1.end;
|
||||
.map_with_span(|(a, b), span| {
|
||||
(
|
||||
Interval {
|
||||
a: Box::new(a),
|
||||
b: Box::new(b),
|
||||
a: a.map(Box::new),
|
||||
b: b.map(Box::new),
|
||||
},
|
||||
span.into(),
|
||||
span,
|
||||
)
|
||||
})
|
||||
.boxed()
|
||||
|
|
@ -300,7 +301,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
.then(op.then(num_expr))
|
||||
.map(|(a, (op, b))| {
|
||||
let span = a.1.start..b.1.end;
|
||||
(Expr::binary_op(op, (Box::new(a), Box::new(b)), None), span.into())
|
||||
(Expr::binary_op(op, (a, b), None), span.into())
|
||||
})
|
||||
.boxed()
|
||||
}
|
||||
|
|
@ -324,7 +325,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
.repeated()
|
||||
.foldr(atom, |op, rhs| {
|
||||
let span = op.1.start..rhs.1.end;
|
||||
(Expr::unary_op(op.0, Box::new(rhs), None), span.into())
|
||||
(Expr::unary_op(op.0, rhs, None), span.into())
|
||||
})
|
||||
.boxed();
|
||||
|
||||
|
|
@ -339,7 +340,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
.repeated()
|
||||
.foldr(not_op, |(op, interval), rhs| {
|
||||
let span = op.1.start..rhs.1.end;
|
||||
(Expr::unary_op(op.0, Box::new(rhs), interval), span.into())
|
||||
(Expr::unary_op(op.0, rhs, interval), span.into())
|
||||
})
|
||||
.boxed()
|
||||
};
|
||||
|
|
@ -351,10 +352,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
.foldr(unary_temporal_op, |(lhs, (op, interval)), rhs| {
|
||||
let span = lhs.1.start..rhs.1.end;
|
||||
assert_eq!(op, BinaryOps::Until);
|
||||
(
|
||||
Expr::binary_op(op, (Box::new(lhs), Box::new(rhs)), interval),
|
||||
span.into(),
|
||||
)
|
||||
(Expr::binary_op(op, (lhs, rhs), interval), span.into())
|
||||
})
|
||||
.boxed();
|
||||
|
||||
|
|
@ -364,7 +362,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
.clone()
|
||||
.foldl(op.then(binary_temporal_op).repeated(), |a, (op, b)| {
|
||||
let span = a.1.start..b.1.end;
|
||||
(Expr::binary_op(op, (Box::new(a), Box::new(b)), None), span.into())
|
||||
(Expr::binary_op(op, (a, b), None), span.into())
|
||||
})
|
||||
.boxed()
|
||||
};
|
||||
|
|
@ -375,7 +373,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
.clone()
|
||||
.foldl(op.then(and_op).repeated(), |a, (op, b)| {
|
||||
let span = a.1.start..b.1.end;
|
||||
(Expr::binary_op(op, (Box::new(a), Box::new(b)), None), span.into())
|
||||
(Expr::binary_op(op, (a, b), None), span.into())
|
||||
})
|
||||
.boxed()
|
||||
};
|
||||
|
|
@ -386,7 +384,7 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
.clone()
|
||||
.foldl(op.then(or_op).repeated(), |a, (op, b)| {
|
||||
let span = a.1.start..b.1.end;
|
||||
(Expr::binary_op(op, (Box::new(a), Box::new(b)), None), span.into())
|
||||
(Expr::binary_op(op, (a, b), None), span.into())
|
||||
})
|
||||
.boxed()
|
||||
};
|
||||
|
|
@ -399,11 +397,14 @@ pub fn parser<'tokens, 'src: 'tokens>(
|
|||
.clone()
|
||||
.foldl(op.then(xor_op).repeated(), |a, (op, b)| {
|
||||
let span = a.1.start..b.1.end;
|
||||
(Expr::binary_op(op, (Box::new(a), Box::new(b)), None), span.into())
|
||||
(Expr::binary_op(op, (a, b), None), span.into())
|
||||
})
|
||||
.boxed()
|
||||
};
|
||||
|
||||
implies_equiv_op.labelled("boolean expression").as_context()
|
||||
implies_equiv_op
|
||||
.map(|(expr, span)| (expr.make_typed(Type::Bool), span))
|
||||
.labelled("boolean expression")
|
||||
.as_context()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
1
argus-parser/src/util.rs
Normal file
1
argus-parser/src/util.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
|
||||
Loading…
Add table
Add a link
Reference in a new issue