Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions crates/arco-cli/src/inspect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,9 @@ fn collect_parameter_sets_from_expr(
Expr::Unary { expr, .. } => {
collect_parameter_sets_from_expr(expr, parameter_name, symbol_to_set, out);
}
Expr::Binary { left, right, .. } | Expr::Comparison { left, right, .. } => {
Expr::Binary { left, right, .. }
| Expr::Logical { left, right, .. }
| Expr::Comparison { left, right, .. } => {
collect_parameter_sets_from_expr(left, parameter_name, symbol_to_set, out);
collect_parameter_sets_from_expr(right, parameter_name, symbol_to_set, out);
}
Expand Down Expand Up @@ -1014,7 +1016,7 @@ fn collect_term_refs_from_expr(
}
}
}
Expr::Binary { left, right, .. } => {
Expr::Binary { left, right, .. } | Expr::Logical { left, right, .. } => {
// For multiplication etc, descend into both sides
collect_term_refs_from_expr(
left,
Expand Down Expand Up @@ -1310,7 +1312,9 @@ fn collect_indexed_targets(expr: &Expr, out: &mut BTreeSet<String>) {
}
}
Expr::Unary { expr, .. } => collect_indexed_targets(expr, out),
Expr::Binary { left, right, .. } | Expr::Comparison { left, right, .. } => {
Expr::Binary { left, right, .. }
| Expr::Logical { left, right, .. }
| Expr::Comparison { left, right, .. } => {
collect_indexed_targets(left, out);
collect_indexed_targets(right, out);
}
Expand Down
8 changes: 6 additions & 2 deletions crates/arco-kdl/src/algebra/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ fn collect_named_dependencies(
}
}
Expr::Unary { expr, .. } => collect_named_dependencies(expr, bound, names),
Expr::Binary { left, right, .. } | Expr::Comparison { left, right, .. } => {
Expr::Binary { left, right, .. }
| Expr::Logical { left, right, .. }
| Expr::Comparison { left, right, .. } => {
collect_named_dependencies(left, bound, names);
collect_named_dependencies(right, bound, names);
}
Expand Down Expand Up @@ -79,7 +81,9 @@ fn expr_mentions_previous_time(expr: &Expr) -> bool {
match expr {
Expr::Indexed { indices, .. } => indices.iter().any(index_mentions_previous_time),
Expr::Unary { expr, .. } => expr_mentions_previous_time(expr),
Expr::Binary { left, right, .. } | Expr::Comparison { left, right, .. } => {
Expr::Binary { left, right, .. }
| Expr::Logical { left, right, .. }
| Expr::Comparison { left, right, .. } => {
expr_mentions_previous_time(left) || expr_mentions_previous_time(right)
}
Expr::FunctionCall { args, .. } => args.iter().any(expr_mentions_previous_time),
Expand Down
44 changes: 41 additions & 3 deletions crates/arco-kdl/src/algebra/parser.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::algebra::error::ParseError;
use crate::algebra::tokenizer::{Token, TokenKind, is_builtin_function, tokenize};
use crate::algebra::types::{
BinaryOp, Binding, BindingPattern, ComparisonOp, ConstraintBody, Expr, ReductionExpr,
ReductionOp, UnaryOp,
BinaryOp, Binding, BindingPattern, ComparisonOp, ConstraintBody, Expr, LogicalOp,
ReductionExpr, ReductionOp, UnaryOp,
};
pub fn parse_value_formula(text: &str) -> Result<Expr, ParseError> {
parse_formula(text, |parser| parser.parse_value_expr())
Expand Down Expand Up @@ -62,6 +62,24 @@ impl<'a> Parser<'a> {
}

fn parse_value_expr(&mut self) -> Result<Expr, ParseError> {
self.parse_or_expr()
}

fn parse_or_expr(&mut self) -> Result<Expr, ParseError> {
self.parse_logical_expr(Parser::parse_and_expr, |kind| match kind {
TokenKind::KeywordOr => Some(LogicalOp::Or),
_ => None,
})
}

fn parse_and_expr(&mut self) -> Result<Expr, ParseError> {
self.parse_logical_expr(Parser::parse_comparison_expr, |kind| match kind {
TokenKind::KeywordAnd => Some(LogicalOp::And),
_ => None,
})
}

fn parse_comparison_expr(&mut self) -> Result<Expr, ParseError> {
let left = self.parse_arithmetic_expr()?;
if let Some(op) = self.maybe_comparison_operator() {
let right = self.parse_arithmetic_expr()?;
Expand Down Expand Up @@ -90,7 +108,6 @@ impl<'a> Parser<'a> {
_ => None,
})
}

fn parse_unary_expr(&mut self) -> Result<Expr, ParseError> {
if self.matches(|kind| matches!(kind, TokenKind::Minus)) {
return Ok(Expr::Unary {
Expand Down Expand Up @@ -361,6 +378,27 @@ impl<'a> Parser<'a> {
}
}

fn parse_logical_expr(
&mut self,
parse_operand: impl Fn(&mut Self) -> Result<Expr, ParseError>,
operator_for: impl Fn(&TokenKind) -> Option<LogicalOp>,
) -> Result<Expr, ParseError> {
let mut expression = parse_operand(self)?;
while let Some(token) = self.tokens.get(self.index) {
let Some(op) = operator_for(&token.kind) else {
break;
};
self.index += 1;
let right = parse_operand(self)?;
expression = Expr::Logical {
op,
left: Box::new(expression),
right: Box::new(right),
};
}
Ok(expression)
}

fn parse_binary_expr(
&mut self,
parse_operand: impl Fn(&mut Self) -> Result<Expr, ParseError>,
Expand Down
24 changes: 24 additions & 0 deletions crates/arco-kdl/src/algebra/tokenizer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ pub(super) enum TokenKind {
KeywordFor,
KeywordIf,
KeywordIn,
KeywordAnd,
KeywordOr,
}

#[derive(Debug, Clone, PartialEq, Eq)]
Expand Down Expand Up @@ -105,6 +107,26 @@ pub(super) fn tokenize(text: &str) -> Result<Vec<Token>, ParseError> {
position: index - 1,
}
}
'&' => {
if bytes.get(index + 1) != Some(&b'&') {
return Err(ParseError::new(index, "unexpected `&`"));
}
index += 1;
Token {
kind: TokenKind::KeywordAnd,
position: index - 1,
}
}
'|' => {
if bytes.get(index + 1) != Some(&b'|') {
return Err(ParseError::new(index, "unexpected `|`"));
}
index += 1;
Token {
kind: TokenKind::KeywordOr,
position: index - 1,
}
}
'<' => {
if bytes.get(index + 1) == Some(&b'=') {
index += 1;
Expand Down Expand Up @@ -192,6 +214,8 @@ pub(super) fn tokenize(text: &str) -> Result<Vec<Token>, ParseError> {
"for" => TokenKind::KeywordFor,
"if" => TokenKind::KeywordIf,
"in" => TokenKind::KeywordIn,
"and" => TokenKind::KeywordAnd,
"or" => TokenKind::KeywordOr,
_ => TokenKind::Identifier(identifier.to_string()),
};
Token {
Expand Down
41 changes: 36 additions & 5 deletions crates/arco-kdl/src/algebra/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ pub enum Expr {
left: Box<Expr>,
right: Box<Expr>,
},
Logical {
op: LogicalOp,
left: Box<Expr>,
right: Box<Expr>,
},
Comparison {
op: ComparisonOp,
left: Box<Expr>,
Expand Down Expand Up @@ -81,6 +86,12 @@ pub enum ComparisonOp {
NotEqual,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LogicalOp {
And,
Or,
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ConstraintBody {
Comparison {
Expand Down Expand Up @@ -151,6 +162,15 @@ impl Display for ComparisonOp {
}
}

impl Display for LogicalOp {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::And => "and",
Self::Or => "or",
})
}
}

fn format_expr(expr: &Expr, f: &mut Formatter<'_>, parent_precedence: u8) -> fmt::Result {
let precedence = expr_precedence(expr);
let needs_parens = precedence < parent_precedence;
Expand Down Expand Up @@ -181,6 +201,11 @@ fn format_expr(expr: &Expr, f: &mut Formatter<'_>, parent_precedence: u8) -> fmt
write!(f, " {op} ")?;
format_expr(right, f, precedence + 1)?;
}
Expr::Logical { op, left, right } => {
format_expr(left, f, precedence)?;
write!(f, " {op} ")?;
format_expr(right, f, precedence + 1)?;
}
Expr::Comparison { op, left, right } => {
format_expr(left, f, precedence)?;
write!(f, " {op} ")?;
Expand Down Expand Up @@ -232,22 +257,28 @@ fn format_expr(expr: &Expr, f: &mut Formatter<'_>, parent_precedence: u8) -> fmt

fn expr_precedence(expr: &Expr) -> u8 {
match expr {
Expr::Comparison { .. } => 1,
Expr::Logical {
op: LogicalOp::Or, ..
} => 1,
Expr::Logical {
op: LogicalOp::And, ..
} => 2,
Expr::Comparison { .. } => 3,
Expr::Binary {
op: BinaryOp::Add | BinaryOp::Subtract,
..
} => 2,
} => 4,
Expr::Binary {
op: BinaryOp::Multiply | BinaryOp::Divide,
..
} => 3,
Expr::Unary { .. } => 4,
} => 5,
Expr::Unary { .. } => 6,
Expr::Number(_)
| Expr::String(_)
| Expr::Boolean(_)
| Expr::Identifier(_)
| Expr::Indexed { .. }
| Expr::FunctionCall { .. }
| Expr::Reduction(_) => 5,
| Expr::Reduction(_) => 7,
}
}
4 changes: 3 additions & 1 deletion crates/arco-kdl/src/compile/constraints_bindings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,9 @@ fn expr_uses_free_index(expr: &Expr, name: &str, bound: &mut BTreeSet<String>) -
.iter()
.any(|index| expr_uses_free_index(index, name, bound)),
Expr::Unary { expr, .. } => expr_uses_free_index(expr, name, bound),
Expr::Binary { left, right, .. } | Expr::Comparison { left, right, .. } => {
Expr::Binary { left, right, .. }
| Expr::Logical { left, right, .. }
| Expr::Comparison { left, right, .. } => {
expr_uses_free_index(left, name, bound) || expr_uses_free_index(right, name, bound)
}
Expr::Reduction(reduction) => {
Expand Down
16 changes: 16 additions & 0 deletions crates/arco-kdl/src/compile/data_tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,22 @@ fn evaluate_data_param_filter_expr(
BinaryOp::Divide => left / right,
}))
}
Expr::Logical { op, left, right } => {
let left = evaluate_data_param_filter_expr(left, row, data_decl)?;
let left_truthy = truthy_data_param_filter_value(&left)?;
match op {
LogicalOp::And if !left_truthy => Some(FilterValue::Boolean(false)),
LogicalOp::Or if left_truthy => Some(FilterValue::Boolean(true)),
_ => {
let right = evaluate_data_param_filter_expr(right, row, data_decl)?;
let right_truthy = truthy_data_param_filter_value(&right)?;
Some(FilterValue::Boolean(match op {
LogicalOp::And => left_truthy && right_truthy,
LogicalOp::Or => left_truthy || right_truthy,
}))
}
}
}
Expr::Comparison { op, left, right } => {
let left = evaluate_data_param_filter_expr(left, row, data_decl)?;
let right = evaluate_data_param_filter_expr(right, row, data_decl)?;
Expand Down
Loading
Loading