fix(argus): correctly handle untyped vars and recursion

This commit is contained in:
Anand Balakrishnan 2023-10-12 16:28:22 -07:00
parent 3880356c66
commit c68620dfdd
No known key found for this signature in database
3 changed files with 51 additions and 21 deletions

View file

@ -139,8 +139,8 @@ pub fn lexer<'src>() -> impl Parser<'src, &'src str, Output<'src>, Error<'src>>
just("&&").to(Token::And),
just("&").to(Token::And),
just("\u{2227}").to(Token::And), // ∧
just("||").to(Token::And),
just("|").to(Token::And),
just("||").to(Token::Or),
just("|").to(Token::Or),
just("\u{2228}").to(Token::Or), //
just("^").to(Token::Xor),
just("-").to(Token::Minus),

View file

@ -7,6 +7,7 @@ mod lexer;
mod syntax;
use chumsky::prelude::Rich;
use itertools::Itertools;
use lexer::{lexer, Token};
use syntax::{parser, Expr, Interval};
@ -17,6 +18,10 @@ pub fn parse_str(src: &str) -> Result<crate::core::expr::Expr, Vec<Rich<'_, Stri
use chumsky::prelude::{Input, Parser};
let (tokens, lex_errors) = lexer().parse(src).into_output_errors();
log::debug!("** Tokens output **");
log::debug!("{:#?}", tokens);
log::debug!("** Lexing Errors **");
log::debug!("[{}]", lex_errors.iter().map(|e| e.to_string()).join("\n- "));
let (parsed, parse_errors) = if let Some(tokens) = &tokens {
parser()
@ -26,6 +31,11 @@ pub fn parse_str(src: &str) -> Result<crate::core::expr::Expr, Vec<Rich<'_, Stri
(None, Vec::new())
};
log::debug!("** Parse output **");
log::debug!("{:#?}", parsed);
log::debug!("** Parse Errors **");
log::debug!("[{}]", parse_errors.iter().map(|e| e.to_string()).join("\n- "));
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);
@ -37,6 +47,11 @@ pub fn parse_str(src: &str) -> Result<crate::core::expr::Expr, Vec<Rich<'_, Stri
(None, vec![])
};
log::debug!("** Final Expression **");
log::debug!("{:#?}", expr);
log::debug!("** AST to Expr Errors **");
log::debug!("[{}]", expr_errors.iter().map(|e| e.to_string()).join("\n- "));
let errors: Vec<_> = lex_errors
.into_iter()
.map(|e| e.map_token(|c| c.to_string()))

View file

@ -21,12 +21,21 @@ pub enum UnaryOps {
}
impl UnaryOps {
/// Get the default type for the *arguments* of this kind of expression.
fn default_args_type(&self) -> Type {
match self {
UnaryOps::Neg => Type::Float,
_ => Type::Bool,
}
}
/// Get the default type the expression with this operator should be
fn get_default_type(&self) -> Type {
match self {
UnaryOps::Neg => Type::Float,
_ => Type::Bool,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
@ -50,6 +59,7 @@ pub enum BinaryOps {
}
impl BinaryOps {
/// Get the default type for the *arguments* of this kind of expression.
fn default_args_type(&self) -> Type {
match self {
BinaryOps::Add
@ -65,6 +75,14 @@ impl BinaryOps {
_ => Type::Bool,
}
}
/// Get the default type the expression with this operator should be
fn get_default_type(&self) -> Type {
match self {
BinaryOps::Add | BinaryOps::Sub | BinaryOps::Mul | BinaryOps::Div => Type::Float,
_ => Type::Bool,
}
}
}
#[derive(Debug, Clone, PartialEq)]
@ -103,12 +121,12 @@ impl<'src> Expr<'src> {
op,
interval: _,
arg: _,
} => op.default_args_type(),
} => op.get_default_type(),
Expr::Binary {
op,
interval: _,
args: _,
} => op.default_args_type(),
} => op.get_default_type(),
}
}
@ -174,7 +192,7 @@ fn num_expr_parser<'tokens, 'src: 'tokens>(
let num_atom = var
.or(num_literal)
.map_with(|expr, e| (expr, e.span()))
.map_with(|e, ctx| (e, ctx.span()))
// Atoms can also just be normal expressions, but surrounded with parentheses
.or(num_expr.clone().delimited_by(just(Token::LParen), just(Token::RParen)))
// Attempt to recover anything that looks like a parenthesised expression but contains errors
@ -297,39 +315,36 @@ pub(crate) fn parser<'tokens, 'src: 'tokens>(
[],
|span| (Expr::Error, span),
)))
.or(expr)
.boxed();
let not_op = just(Token::Not)
.map_with(|_, e| (UnaryOps::Not, e.span()))
.repeated()
.foldr(atom, |op, rhs| {
let span = op.1.start..rhs.1.end;
(Expr::unary_op(op.0, rhs, None), span.into())
})
.boxed();
let unary_temporal_op = {
let unary_op = {
let op = choice((
just(Token::Not).to(UnaryOps::Not),
just(Token::Next).to(UnaryOps::Next),
just(Token::Eventually).to(UnaryOps::Eventually),
just(Token::Always).to(UnaryOps::Always),
));
op.map_with(|op, e| (op, e.span()))
.then(interval.clone().or_not())
.try_map(|(op, interval), _| match (op, interval) {
((UnaryOps::Not, _), Some((_, s))) => {
Err(Rich::custom(s, "Not (`!`) operator cannot have an interval"))
}
(o, i) => Ok((o, i)),
})
.repeated()
.foldr(not_op, |(op, interval), rhs| {
.foldr(atom, |(op, interval), rhs| {
let span = op.1.start..rhs.1.end;
(Expr::unary_op(op.0, rhs, interval), span.into())
})
.boxed()
};
let binary_temporal_op = unary_temporal_op
let until_op = unary_op
.clone()
.then(just(Token::Until).to(BinaryOps::Until).then(interval.or_not()))
.repeated()
.foldr(unary_temporal_op, |(lhs, (op, interval)), rhs| {
.foldr(unary_op, |(lhs, (op, interval)), rhs| {
let span = lhs.1.start..rhs.1.end;
assert_eq!(op, BinaryOps::Until);
(Expr::binary_op(op, (lhs, rhs), interval), span.into())
@ -338,9 +353,9 @@ pub(crate) fn parser<'tokens, 'src: 'tokens>(
let and_op = {
let op = just(Token::And).to(BinaryOps::And);
binary_temporal_op
until_op
.clone()
.foldl(op.then(binary_temporal_op).repeated(), |a, (op, b)| {
.foldl(op.then(until_op).repeated(), |a, (op, b)| {
let span = a.1.start..b.1.end;
(Expr::binary_op(op, (a, b), None), span.into())
})