1use std::collections::{HashMap, VecDeque};
26use std::fmt;
27
28#[derive(Debug, Clone, PartialEq)]
32pub enum ExprValue {
33 Bool(bool),
35 Integer(i64),
37 Double(f64),
39 Str(String),
41 Iri(String),
43 Blank(String),
45 Unbound,
47}
48
49impl fmt::Display for ExprValue {
50 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51 match self {
52 Self::Bool(b) => write!(f, "{b}"),
53 Self::Integer(i) => write!(f, "{i}"),
54 Self::Double(d) => write!(f, "{d}"),
55 Self::Str(s) => write!(f, "\"{s}\""),
56 Self::Iri(i) => write!(f, "<{i}>"),
57 Self::Blank(b) => write!(f, "_:{b}"),
58 Self::Unbound => write!(f, "UNBOUND"),
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Eq)]
67pub enum BinOp {
68 Add,
70 Sub,
72 Mul,
74 Div,
76 Eq,
78 Ne,
80 Lt,
82 Le,
84 Gt,
86 Ge,
88 And,
90 Or,
92}
93
94#[derive(Debug, Clone)]
98pub enum CompiledExpr {
99 Literal(String),
101 Variable(String),
103 IriRef(String),
105 Neg(Box<CompiledExpr>),
107 Not(Box<CompiledExpr>),
109 BinOp(BinOp, Box<CompiledExpr>, Box<CompiledExpr>),
111 FuncCall(String, Vec<CompiledExpr>),
113 If(Box<CompiledExpr>, Box<CompiledExpr>, Box<CompiledExpr>),
115}
116
117#[derive(Debug, Clone)]
121pub struct CompileError(pub String);
122
123impl fmt::Display for CompileError {
124 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125 write!(f, "CompileError: {}", self.0)
126 }
127}
128
129impl std::error::Error for CompileError {}
130
131#[derive(Debug, Clone)]
133pub struct EvalError(pub String);
134
135impl fmt::Display for EvalError {
136 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137 write!(f, "EvalError: {}", self.0)
138 }
139}
140
141impl std::error::Error for EvalError {}
142
143struct Parser<'a> {
147 input: &'a [u8],
148 pos: usize,
149}
150
151impl<'a> Parser<'a> {
152 fn new(s: &'a str) -> Self {
153 Self {
154 input: s.as_bytes(),
155 pos: 0,
156 }
157 }
158
159 fn peek(&self) -> Option<u8> {
160 self.input.get(self.pos).copied()
161 }
162
163 fn advance(&mut self) {
164 if self.pos < self.input.len() {
165 self.pos += 1;
166 }
167 }
168
169 fn skip_ws(&mut self) {
170 while let Some(c) = self.peek() {
171 if c == b' ' || c == b'\t' || c == b'\r' || c == b'\n' {
172 self.advance();
173 } else {
174 break;
175 }
176 }
177 }
178
179 fn expect(&mut self, ch: u8) -> Result<(), CompileError> {
180 self.skip_ws();
181 match self.peek() {
182 Some(c) if c == ch => {
183 self.advance();
184 Ok(())
185 }
186 other => Err(CompileError(format!(
187 "expected '{}' at pos {}, got {:?}",
188 ch as char,
189 self.pos,
190 other.map(|b| b as char)
191 ))),
192 }
193 }
194
195 fn parse_arg_list(&mut self) -> Result<Vec<CompiledExpr>, CompileError> {
197 self.expect(b'(')?;
198 self.skip_ws();
199 let mut args = Vec::new();
200 if self.peek() == Some(b')') {
201 self.advance();
202 return Ok(args);
203 }
204 loop {
205 args.push(self.parse_or()?);
206 self.skip_ws();
207 match self.peek() {
208 Some(b',') => {
209 self.advance();
210 }
211 Some(b')') => {
212 self.advance();
213 break;
214 }
215 other => {
216 return Err(CompileError(format!(
217 "expected ',' or ')' at pos {}, got {:?}",
218 self.pos,
219 other.map(|b| b as char)
220 )))
221 }
222 }
223 }
224 Ok(args)
225 }
226
227 fn parse_or(&mut self) -> Result<CompiledExpr, CompileError> {
237 let mut left = self.parse_and()?;
238 loop {
239 self.skip_ws();
240 if self.input.get(self.pos..self.pos + 2) == Some(b"||") {
241 self.pos += 2;
242 let right = self.parse_and()?;
243 left = CompiledExpr::BinOp(BinOp::Or, Box::new(left), Box::new(right));
244 } else {
245 break;
246 }
247 }
248 Ok(left)
249 }
250
251 fn parse_and(&mut self) -> Result<CompiledExpr, CompileError> {
252 let mut left = self.parse_cmp()?;
253 loop {
254 self.skip_ws();
255 if self.input.get(self.pos..self.pos + 2) == Some(b"&&") {
256 self.pos += 2;
257 let right = self.parse_cmp()?;
258 left = CompiledExpr::BinOp(BinOp::And, Box::new(left), Box::new(right));
259 } else {
260 break;
261 }
262 }
263 Ok(left)
264 }
265
266 fn parse_cmp(&mut self) -> Result<CompiledExpr, CompileError> {
267 let left = self.parse_add()?;
268 self.skip_ws();
269 let op = if self.input.get(self.pos..self.pos + 2) == Some(b"!=") {
270 self.pos += 2;
271 Some(BinOp::Ne)
272 } else if self.input.get(self.pos..self.pos + 2) == Some(b"<=") {
273 self.pos += 2;
274 Some(BinOp::Le)
275 } else if self.input.get(self.pos..self.pos + 2) == Some(b">=") {
276 self.pos += 2;
277 Some(BinOp::Ge)
278 } else if self.peek() == Some(b'=') {
279 self.pos += 1;
280 Some(BinOp::Eq)
281 } else if self.peek() == Some(b'<') {
282 self.pos += 1;
283 Some(BinOp::Lt)
284 } else if self.peek() == Some(b'>') {
285 self.pos += 1;
286 Some(BinOp::Gt)
287 } else {
288 None
289 };
290 if let Some(op) = op {
291 let right = self.parse_add()?;
292 Ok(CompiledExpr::BinOp(op, Box::new(left), Box::new(right)))
293 } else {
294 Ok(left)
295 }
296 }
297
298 fn parse_add(&mut self) -> Result<CompiledExpr, CompileError> {
299 let mut left = self.parse_mul()?;
300 loop {
301 self.skip_ws();
302 match self.peek() {
303 Some(b'+') => {
304 self.advance();
305 let right = self.parse_mul()?;
306 left = CompiledExpr::BinOp(BinOp::Add, Box::new(left), Box::new(right));
307 }
308 Some(b'-') => {
309 self.advance();
312 let right = self.parse_mul()?;
313 left = CompiledExpr::BinOp(BinOp::Sub, Box::new(left), Box::new(right));
314 }
315 _ => break,
316 }
317 }
318 Ok(left)
319 }
320
321 fn parse_mul(&mut self) -> Result<CompiledExpr, CompileError> {
322 let mut left = self.parse_unary()?;
323 loop {
324 self.skip_ws();
325 match self.peek() {
326 Some(b'*') => {
327 self.advance();
328 let right = self.parse_unary()?;
329 left = CompiledExpr::BinOp(BinOp::Mul, Box::new(left), Box::new(right));
330 }
331 Some(b'/') => {
332 self.advance();
333 let right = self.parse_unary()?;
334 left = CompiledExpr::BinOp(BinOp::Div, Box::new(left), Box::new(right));
335 }
336 _ => break,
337 }
338 }
339 Ok(left)
340 }
341
342 fn parse_unary(&mut self) -> Result<CompiledExpr, CompileError> {
343 self.skip_ws();
344 match self.peek() {
345 Some(b'-') => {
346 self.advance();
347 let inner = self.parse_unary()?;
348 Ok(CompiledExpr::Neg(Box::new(inner)))
349 }
350 Some(b'!') => {
351 self.advance();
352 let inner = self.parse_unary()?;
353 Ok(CompiledExpr::Not(Box::new(inner)))
354 }
355 _ => self.parse_primary(),
356 }
357 }
358
359 fn parse_primary(&mut self) -> Result<CompiledExpr, CompileError> {
360 self.skip_ws();
361 match self.peek() {
362 Some(b'"') => self.parse_string_literal(),
363 Some(b'<') => self.parse_iri(),
364 Some(b'?') => self.parse_variable(),
365 Some(b'(') => {
366 self.advance();
367 let expr = self.parse_or()?;
368 self.expect(b')')?;
369 Ok(expr)
370 }
371 Some(c) if c.is_ascii_digit() => self.parse_number(),
372 Some(c) if c.is_ascii_alphabetic() || c == b'_' => self.parse_name_or_call(),
373 other => Err(CompileError(format!(
374 "unexpected character at pos {}: {:?}",
375 self.pos,
376 other.map(|b| b as char)
377 ))),
378 }
379 }
380
381 fn parse_string_literal(&mut self) -> Result<CompiledExpr, CompileError> {
382 self.advance(); let start = self.pos;
384 while let Some(c) = self.peek() {
385 if c == b'"' {
386 break;
387 }
388 if c == b'\\' {
389 self.advance();
390 }
391 self.advance();
392 }
393 let s = std::str::from_utf8(&self.input[start..self.pos])
394 .map_err(|e| CompileError(format!("UTF-8 error in string: {e}")))?
395 .to_string();
396 self.expect(b'"')?;
397 Ok(CompiledExpr::Literal(format!("\"{s}\"")))
398 }
399
400 fn parse_iri(&mut self) -> Result<CompiledExpr, CompileError> {
401 self.advance(); let start = self.pos;
403 while let Some(c) = self.peek() {
404 if c == b'>' {
405 break;
406 }
407 self.advance();
408 }
409 let iri = std::str::from_utf8(&self.input[start..self.pos])
410 .map_err(|e| CompileError(format!("UTF-8 error in IRI: {e}")))?
411 .to_string();
412 self.expect(b'>')?;
413 Ok(CompiledExpr::IriRef(iri))
414 }
415
416 fn parse_variable(&mut self) -> Result<CompiledExpr, CompileError> {
417 self.advance(); let start = self.pos;
419 while let Some(c) = self.peek() {
420 if c.is_ascii_alphanumeric() || c == b'_' {
421 self.advance();
422 } else {
423 break;
424 }
425 }
426 let name = std::str::from_utf8(&self.input[start..self.pos])
427 .map_err(|e| CompileError(format!("UTF-8 error in variable: {e}")))?
428 .to_string();
429 Ok(CompiledExpr::Variable(name))
430 }
431
432 fn parse_number(&mut self) -> Result<CompiledExpr, CompileError> {
433 let start = self.pos;
434 let mut has_dot = false;
435 while let Some(c) = self.peek() {
436 if c.is_ascii_digit() {
437 self.advance();
438 } else if c == b'.' && !has_dot {
439 has_dot = true;
440 self.advance();
441 } else {
442 break;
443 }
444 }
445 let num_str = std::str::from_utf8(&self.input[start..self.pos])
446 .map_err(|e| CompileError(format!("UTF-8 error in number: {e}")))?;
447 Ok(CompiledExpr::Literal(num_str.to_string()))
448 }
449
450 fn parse_name_or_call(&mut self) -> Result<CompiledExpr, CompileError> {
451 let start = self.pos;
452 while let Some(c) = self.peek() {
453 if c.is_ascii_alphanumeric() || c == b'_' {
454 self.advance();
455 } else {
456 break;
457 }
458 }
459 let name = std::str::from_utf8(&self.input[start..self.pos])
460 .map_err(|e| CompileError(format!("UTF-8 error in name: {e}")))?
461 .to_string();
462
463 self.skip_ws();
464 if self.peek() == Some(b'(') {
466 let upper = name.to_uppercase();
467 if upper == "IF" {
469 let args = self.parse_arg_list()?;
470 if args.len() != 3 {
471 return Err(CompileError(format!(
472 "IF requires exactly 3 arguments, got {}",
473 args.len()
474 )));
475 }
476 let mut it = args.into_iter();
477 let cond = it.next().expect("checked len");
478 let then = it.next().expect("checked len");
479 let else_ = it.next().expect("checked len");
480 Ok(CompiledExpr::If(
481 Box::new(cond),
482 Box::new(then),
483 Box::new(else_),
484 ))
485 } else {
486 let args = self.parse_arg_list()?;
487 Ok(CompiledExpr::FuncCall(upper, args))
488 }
489 } else {
490 let lower = name.to_lowercase();
492 if lower == "true" {
493 Ok(CompiledExpr::Literal("true".to_string()))
494 } else if lower == "false" {
495 Ok(CompiledExpr::Literal("false".to_string()))
496 } else {
497 Err(CompileError(format!(
498 "unexpected bare identifier '{name}' at pos {start}"
499 )))
500 }
501 }
502 }
503}
504
505#[derive(Debug, Clone, Default)]
509pub struct ExprCompiler;
510
511impl ExprCompiler {
512 pub fn new() -> Self {
514 Self
515 }
516
517 pub fn compile(&self, expr_str: &str) -> Result<CompiledExpr, CompileError> {
519 let mut p = Parser::new(expr_str.trim());
520 let expr = p.parse_or()?;
521 p.skip_ws();
522 if p.pos != p.input.len() {
523 return Err(CompileError(format!(
524 "unexpected trailing input at pos {}: '{}'",
525 p.pos,
526 std::str::from_utf8(&p.input[p.pos..]).unwrap_or("<invalid utf8>")
527 )));
528 }
529 Ok(expr)
530 }
531
532 pub fn evaluate(
534 &self,
535 expr: &CompiledExpr,
536 bindings: &HashMap<String, ExprValue>,
537 ) -> Result<ExprValue, EvalError> {
538 match expr {
539 CompiledExpr::Literal(s) => Self::eval_literal(s),
540 CompiledExpr::Variable(name) => {
541 Ok(bindings.get(name).cloned().unwrap_or(ExprValue::Unbound))
542 }
543 CompiledExpr::IriRef(iri) => Ok(ExprValue::Iri(iri.clone())),
544 CompiledExpr::Neg(inner) => {
545 let v = self.evaluate(inner, bindings)?;
546 match v {
547 ExprValue::Integer(i) => Ok(ExprValue::Integer(-i)),
548 ExprValue::Double(d) => Ok(ExprValue::Double(-d)),
549 _ => Err(EvalError(format!("cannot negate {v}"))),
550 }
551 }
552 CompiledExpr::Not(inner) => {
553 let v = self.evaluate(inner, bindings)?;
554 match v {
555 ExprValue::Bool(b) => Ok(ExprValue::Bool(!b)),
556 _ => Err(EvalError(format!("! requires boolean, got {v}"))),
557 }
558 }
559 CompiledExpr::BinOp(op, left, right) => self.eval_binop(op, left, right, bindings),
560 CompiledExpr::FuncCall(name, args) => self.eval_func(name, args, bindings),
561 CompiledExpr::If(cond, then_expr, else_expr) => {
562 let cv = self.evaluate(cond, bindings)?;
563 match cv {
564 ExprValue::Bool(true) => self.evaluate(then_expr, bindings),
565 ExprValue::Bool(false) => self.evaluate(else_expr, bindings),
566 _ => Err(EvalError(format!("IF condition must be boolean, got {cv}"))),
567 }
568 }
569 }
570 }
571
572 fn eval_literal(s: &str) -> Result<ExprValue, EvalError> {
573 if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
575 return Ok(ExprValue::Str(s[1..s.len() - 1].to_string()));
576 }
577 if s == "true" {
579 return Ok(ExprValue::Bool(true));
580 }
581 if s == "false" {
582 return Ok(ExprValue::Bool(false));
583 }
584 if let Ok(i) = s.parse::<i64>() {
586 return Ok(ExprValue::Integer(i));
587 }
588 if let Ok(d) = s.parse::<f64>() {
590 return Ok(ExprValue::Double(d));
591 }
592 Err(EvalError(format!("cannot parse literal: '{s}'")))
593 }
594
595 fn eval_binop(
596 &self,
597 op: &BinOp,
598 left: &CompiledExpr,
599 right: &CompiledExpr,
600 bindings: &HashMap<String, ExprValue>,
601 ) -> Result<ExprValue, EvalError> {
602 if *op == BinOp::And {
604 let lv = self.evaluate(left, bindings)?;
605 match lv {
606 ExprValue::Bool(false) => return Ok(ExprValue::Bool(false)),
607 ExprValue::Bool(true) => {
608 let rv = self.evaluate(right, bindings)?;
609 return match rv {
610 ExprValue::Bool(b) => Ok(ExprValue::Bool(b)),
611 _ => Err(EvalError(format!("&& requires boolean RHS, got {rv}"))),
612 };
613 }
614 _ => return Err(EvalError(format!("&& requires boolean LHS, got {lv}"))),
615 }
616 }
617 if *op == BinOp::Or {
618 let lv = self.evaluate(left, bindings)?;
619 match lv {
620 ExprValue::Bool(true) => return Ok(ExprValue::Bool(true)),
621 ExprValue::Bool(false) => {
622 let rv = self.evaluate(right, bindings)?;
623 return match rv {
624 ExprValue::Bool(b) => Ok(ExprValue::Bool(b)),
625 _ => Err(EvalError(format!("|| requires boolean RHS, got {rv}"))),
626 };
627 }
628 _ => return Err(EvalError(format!("|| requires boolean LHS, got {lv}"))),
629 }
630 }
631
632 let lv = self.evaluate(left, bindings)?;
633 let rv = self.evaluate(right, bindings)?;
634
635 match op {
636 BinOp::Add => Self::numeric_op(&lv, &rv, |a, b| a + b, |a, b| a + b),
637 BinOp::Sub => Self::numeric_op(&lv, &rv, |a, b| a - b, |a, b| a - b),
638 BinOp::Mul => Self::numeric_op(&lv, &rv, |a, b| a * b, |a, b| a * b),
639 BinOp::Div => match (&lv, &rv) {
640 (ExprValue::Integer(_), ExprValue::Integer(0)) => {
641 Err(EvalError("division by zero".to_string()))
642 }
643 (ExprValue::Double(_), ExprValue::Double(d)) if *d == 0.0 => {
644 Err(EvalError("division by zero (double)".to_string()))
645 }
646 _ => Self::numeric_op(&lv, &rv, |a, b| a / b, |a, b| a / b),
647 },
648 BinOp::Eq => Ok(ExprValue::Bool(Self::values_equal(&lv, &rv))),
649 BinOp::Ne => Ok(ExprValue::Bool(!Self::values_equal(&lv, &rv))),
650 BinOp::Lt => Self::compare_op(&lv, &rv, std::cmp::Ordering::Less),
651 BinOp::Le => Self::compare_op_le(&lv, &rv),
652 BinOp::Gt => Self::compare_op(&lv, &rv, std::cmp::Ordering::Greater),
653 BinOp::Ge => Self::compare_op_ge(&lv, &rv),
654 BinOp::And | BinOp::Or => unreachable!("handled above"),
655 }
656 }
657
658 fn numeric_op<FI, FD>(
659 lv: &ExprValue,
660 rv: &ExprValue,
661 fi: FI,
662 fd: FD,
663 ) -> Result<ExprValue, EvalError>
664 where
665 FI: Fn(i64, i64) -> i64,
666 FD: Fn(f64, f64) -> f64,
667 {
668 match (lv, rv) {
669 (ExprValue::Integer(a), ExprValue::Integer(b)) => Ok(ExprValue::Integer(fi(*a, *b))),
670 (ExprValue::Double(a), ExprValue::Double(b)) => Ok(ExprValue::Double(fd(*a, *b))),
671 (ExprValue::Integer(a), ExprValue::Double(b)) => {
672 Ok(ExprValue::Double(fd(*a as f64, *b)))
673 }
674 (ExprValue::Double(a), ExprValue::Integer(b)) => {
675 Ok(ExprValue::Double(fd(*a, *b as f64)))
676 }
677 _ => Err(EvalError(format!(
678 "numeric operation requires numeric operands, got {lv} and {rv}"
679 ))),
680 }
681 }
682
683 fn values_equal(a: &ExprValue, b: &ExprValue) -> bool {
684 match (a, b) {
685 (ExprValue::Bool(x), ExprValue::Bool(y)) => x == y,
686 (ExprValue::Integer(x), ExprValue::Integer(y)) => x == y,
687 (ExprValue::Double(x), ExprValue::Double(y)) => x == y,
688 (ExprValue::Integer(x), ExprValue::Double(y)) => (*x as f64) == *y,
689 (ExprValue::Double(x), ExprValue::Integer(y)) => *x == (*y as f64),
690 (ExprValue::Str(x), ExprValue::Str(y)) => x == y,
691 (ExprValue::Iri(x), ExprValue::Iri(y)) => x == y,
692 (ExprValue::Blank(x), ExprValue::Blank(y)) => x == y,
693 (ExprValue::Unbound, ExprValue::Unbound) => true,
694 _ => false,
695 }
696 }
697
698 fn compare_op(
699 lv: &ExprValue,
700 rv: &ExprValue,
701 target: std::cmp::Ordering,
702 ) -> Result<ExprValue, EvalError> {
703 let ord = Self::numeric_cmp(lv, rv)?;
704 Ok(ExprValue::Bool(ord == target))
705 }
706
707 fn compare_op_le(lv: &ExprValue, rv: &ExprValue) -> Result<ExprValue, EvalError> {
708 let ord = Self::numeric_cmp(lv, rv)?;
709 Ok(ExprValue::Bool(
710 ord == std::cmp::Ordering::Less || ord == std::cmp::Ordering::Equal,
711 ))
712 }
713
714 fn compare_op_ge(lv: &ExprValue, rv: &ExprValue) -> Result<ExprValue, EvalError> {
715 let ord = Self::numeric_cmp(lv, rv)?;
716 Ok(ExprValue::Bool(
717 ord == std::cmp::Ordering::Greater || ord == std::cmp::Ordering::Equal,
718 ))
719 }
720
721 fn numeric_cmp(lv: &ExprValue, rv: &ExprValue) -> Result<std::cmp::Ordering, EvalError> {
722 match (lv, rv) {
723 (ExprValue::Integer(a), ExprValue::Integer(b)) => Ok(a.cmp(b)),
724 (ExprValue::Double(a), ExprValue::Double(b)) => a
725 .partial_cmp(b)
726 .ok_or_else(|| EvalError("NaN comparison".to_string())),
727 (ExprValue::Integer(a), ExprValue::Double(b)) => (*a as f64)
728 .partial_cmp(b)
729 .ok_or_else(|| EvalError("NaN".to_string())),
730 (ExprValue::Double(a), ExprValue::Integer(b)) => a
731 .partial_cmp(&(*b as f64))
732 .ok_or_else(|| EvalError("NaN".to_string())),
733 (ExprValue::Str(a), ExprValue::Str(b)) => Ok(a.cmp(b)),
734 _ => Err(EvalError(format!("cannot compare {lv} and {rv}"))),
735 }
736 }
737
738 fn eval_func(
739 &self,
740 name: &str,
741 args: &[CompiledExpr],
742 bindings: &HashMap<String, ExprValue>,
743 ) -> Result<ExprValue, EvalError> {
744 match name {
745 "BOUND" => {
746 if args.len() != 1 {
747 return Err(EvalError(format!(
748 "BOUND expects 1 arg, got {}",
749 args.len()
750 )));
751 }
752 let v = self.evaluate(&args[0], bindings)?;
753 Ok(ExprValue::Bool(!matches!(v, ExprValue::Unbound)))
754 }
755 "ISIRI" | "ISURI" => {
756 if args.len() != 1 {
757 return Err(EvalError(format!(
758 "{name} expects 1 arg, got {}",
759 args.len()
760 )));
761 }
762 let v = self.evaluate(&args[0], bindings)?;
763 Ok(ExprValue::Bool(matches!(v, ExprValue::Iri(_))))
764 }
765 "ISLITERAL" => {
766 if args.len() != 1 {
767 return Err(EvalError(format!(
768 "ISLITERAL expects 1 arg, got {}",
769 args.len()
770 )));
771 }
772 let v = self.evaluate(&args[0], bindings)?;
773 Ok(ExprValue::Bool(matches!(
774 v,
775 ExprValue::Str(_)
776 | ExprValue::Integer(_)
777 | ExprValue::Double(_)
778 | ExprValue::Bool(_)
779 )))
780 }
781 "ISBLANK" => {
782 if args.len() != 1 {
783 return Err(EvalError(format!(
784 "ISBLANK expects 1 arg, got {}",
785 args.len()
786 )));
787 }
788 let v = self.evaluate(&args[0], bindings)?;
789 Ok(ExprValue::Bool(matches!(v, ExprValue::Blank(_))))
790 }
791 "STR" => {
792 if args.len() != 1 {
793 return Err(EvalError(format!("STR expects 1 arg, got {}", args.len())));
794 }
795 let v = self.evaluate(&args[0], bindings)?;
796 let s = match &v {
797 ExprValue::Str(s) => s.clone(),
798 ExprValue::Iri(s) => s.clone(),
799 ExprValue::Integer(i) => i.to_string(),
800 ExprValue::Double(d) => d.to_string(),
801 ExprValue::Bool(b) => b.to_string(),
802 ExprValue::Blank(b) => b.clone(),
803 ExprValue::Unbound => return Err(EvalError("STR of UNBOUND".to_string())),
804 };
805 Ok(ExprValue::Str(s))
806 }
807 "LANG" => {
808 if args.len() != 1 {
809 return Err(EvalError(format!("LANG expects 1 arg, got {}", args.len())));
810 }
811 let v = self.evaluate(&args[0], bindings)?;
812 match v {
813 ExprValue::Str(_) => Ok(ExprValue::Str(String::new())),
814 _ => Err(EvalError(format!("LANG requires string literal, got {v}"))),
815 }
816 }
817 "DATATYPE" => {
818 if args.len() != 1 {
819 return Err(EvalError(format!(
820 "DATATYPE expects 1 arg, got {}",
821 args.len()
822 )));
823 }
824 let v = self.evaluate(&args[0], bindings)?;
825 let dt = match v {
826 ExprValue::Str(_) => "http://www.w3.org/2001/XMLSchema#string",
827 ExprValue::Integer(_) => "http://www.w3.org/2001/XMLSchema#integer",
828 ExprValue::Double(_) => "http://www.w3.org/2001/XMLSchema#double",
829 ExprValue::Bool(_) => "http://www.w3.org/2001/XMLSchema#boolean",
830 _ => return Err(EvalError("DATATYPE not applicable".to_string())),
831 };
832 Ok(ExprValue::Iri(dt.to_string()))
833 }
834 "COALESCE" => {
835 for arg in args {
836 let v = self.evaluate(arg, bindings)?;
837 if !matches!(v, ExprValue::Unbound) {
838 return Ok(v);
839 }
840 }
841 Ok(ExprValue::Unbound)
842 }
843 "IF" => {
844 if args.len() != 3 {
846 return Err(EvalError(format!("IF expects 3 args, got {}", args.len())));
847 }
848 let cv = self.evaluate(&args[0], bindings)?;
849 match cv {
850 ExprValue::Bool(true) => self.evaluate(&args[1], bindings),
851 ExprValue::Bool(false) => self.evaluate(&args[2], bindings),
852 _ => Err(EvalError(format!("IF condition must be boolean, got {cv}"))),
853 }
854 }
855 other => Err(EvalError(format!("unknown function: {other}"))),
856 }
857 }
858}
859
860pub struct ExprCache {
868 cache: HashMap<String, CompiledExpr>,
869 order: VecDeque<String>,
870 max_size: usize,
871 compiler: ExprCompiler,
872 hits: u64,
873 misses: u64,
874}
875
876impl ExprCache {
877 pub fn new(max_size: usize) -> Self {
879 Self {
880 cache: HashMap::new(),
881 order: VecDeque::new(),
882 max_size,
883 compiler: ExprCompiler::new(),
884 hits: 0,
885 misses: 0,
886 }
887 }
888
889 pub fn get_or_compile(&mut self, expr_str: &str) -> Result<&CompiledExpr, CompileError> {
891 if self.cache.contains_key(expr_str) {
892 if let Some(pos) = self.order.iter().position(|k| k == expr_str) {
894 self.order.remove(pos);
895 }
896 self.order.push_back(expr_str.to_string());
897 self.hits += 1;
898 return Ok(self.cache.get(expr_str).expect("just confirmed present"));
899 }
900
901 self.misses += 1;
903 let compiled = self.compiler.compile(expr_str)?;
904
905 if self.cache.len() >= self.max_size && !self.cache.is_empty() {
907 if let Some(oldest) = self.order.pop_front() {
908 self.cache.remove(&oldest);
909 }
910 }
911
912 self.cache.insert(expr_str.to_string(), compiled);
913 self.order.push_back(expr_str.to_string());
914 Ok(self.cache.get(expr_str).expect("just inserted"))
915 }
916
917 pub fn cache_size(&self) -> usize {
919 self.cache.len()
920 }
921
922 pub fn clear(&mut self) {
924 self.cache.clear();
925 self.order.clear();
926 self.hits = 0;
927 self.misses = 0;
928 }
929
930 pub fn hits(&self) -> u64 {
932 self.hits
933 }
934
935 pub fn misses(&self) -> u64 {
937 self.misses
938 }
939}
940
941#[cfg(test)]
944mod tests {
945 use super::*;
946
947 fn bindings(pairs: &[(&str, ExprValue)]) -> HashMap<String, ExprValue> {
948 pairs
949 .iter()
950 .map(|(k, v)| (k.to_string(), v.clone()))
951 .collect()
952 }
953
954 fn compiler() -> ExprCompiler {
955 ExprCompiler::new()
956 }
957
958 fn compile(s: &str) -> CompiledExpr {
959 compiler().compile(s).expect("compile failed")
960 }
961
962 fn eval(expr: &CompiledExpr, b: &HashMap<String, ExprValue>) -> ExprValue {
963 compiler().evaluate(expr, b).expect("eval failed")
964 }
965
966 #[test]
969 fn test_literal_integer() {
970 let e = compile("42");
971 let v = eval(&e, &bindings(&[]));
972 assert_eq!(v, ExprValue::Integer(42));
973 }
974
975 #[test]
976 fn test_literal_zero() {
977 let e = compile("0");
978 let v = eval(&e, &bindings(&[]));
979 assert_eq!(v, ExprValue::Integer(0));
980 }
981
982 #[test]
983 fn test_literal_double() {
984 let e = compile("3.15");
985 let v = eval(&e, &bindings(&[]));
986 assert_eq!(v, ExprValue::Double(3.15));
987 }
988
989 #[test]
990 fn test_literal_string() {
991 let e = compile(r#""hello world""#);
992 let v = eval(&e, &bindings(&[]));
993 assert_eq!(v, ExprValue::Str("hello world".to_string()));
994 }
995
996 #[test]
997 fn test_literal_empty_string() {
998 let e = compile(r#""""#);
999 let v = eval(&e, &bindings(&[]));
1000 assert_eq!(v, ExprValue::Str(String::new()));
1001 }
1002
1003 #[test]
1004 fn test_literal_true() {
1005 let e = compile("true");
1006 let v = eval(&e, &bindings(&[]));
1007 assert_eq!(v, ExprValue::Bool(true));
1008 }
1009
1010 #[test]
1011 fn test_literal_false() {
1012 let e = compile("false");
1013 let v = eval(&e, &bindings(&[]));
1014 assert_eq!(v, ExprValue::Bool(false));
1015 }
1016
1017 #[test]
1020 fn test_variable_bound() {
1021 let e = compile("?x");
1022 let v = eval(&e, &bindings(&[("x", ExprValue::Integer(7))]));
1023 assert_eq!(v, ExprValue::Integer(7));
1024 }
1025
1026 #[test]
1027 fn test_variable_unbound() {
1028 let e = compile("?missing");
1029 let v = eval(&e, &bindings(&[]));
1030 assert_eq!(v, ExprValue::Unbound);
1031 }
1032
1033 #[test]
1034 fn test_variable_string_value() {
1035 let e = compile("?name");
1036 let v = eval(
1037 &e,
1038 &bindings(&[("name", ExprValue::Str("Alice".to_string()))]),
1039 );
1040 assert_eq!(v, ExprValue::Str("Alice".to_string()));
1041 }
1042
1043 #[test]
1046 fn test_iri_ref() {
1047 let e = compile("<http://example.org/foo>");
1048 let v = eval(&e, &bindings(&[]));
1049 assert_eq!(v, ExprValue::Iri("http://example.org/foo".to_string()));
1050 }
1051
1052 #[test]
1055 fn test_add_integers() {
1056 let e = compile("1 + 2");
1057 let v = eval(&e, &bindings(&[]));
1058 assert_eq!(v, ExprValue::Integer(3));
1059 }
1060
1061 #[test]
1062 fn test_sub_integers() {
1063 let e = compile("10 - 3");
1064 let v = eval(&e, &bindings(&[]));
1065 assert_eq!(v, ExprValue::Integer(7));
1066 }
1067
1068 #[test]
1069 fn test_mul_integers() {
1070 let e = compile("4 * 5");
1071 let v = eval(&e, &bindings(&[]));
1072 assert_eq!(v, ExprValue::Integer(20));
1073 }
1074
1075 #[test]
1076 fn test_div_integers() {
1077 let e = compile("10 / 2");
1078 let v = eval(&e, &bindings(&[]));
1079 assert_eq!(v, ExprValue::Integer(5));
1080 }
1081
1082 #[test]
1083 fn test_add_doubles() {
1084 let e = compile("1.5 + 2.5");
1085 let v = eval(&e, &bindings(&[]));
1086 assert_eq!(v, ExprValue::Double(4.0));
1087 }
1088
1089 #[test]
1090 fn test_arithmetic_with_variable() {
1091 let e = compile("?x + 10");
1092 let v = eval(&e, &bindings(&[("x", ExprValue::Integer(5))]));
1093 assert_eq!(v, ExprValue::Integer(15));
1094 }
1095
1096 #[test]
1097 fn test_unary_neg_integer() {
1098 let e = compile("-5");
1099 let v = eval(&e, &bindings(&[]));
1100 assert_eq!(v, ExprValue::Integer(-5));
1101 }
1102
1103 #[test]
1104 fn test_unary_neg_double() {
1105 let e = compile("-3.15");
1106 let v = eval(&e, &bindings(&[]));
1107 assert_eq!(v, ExprValue::Double(-3.15));
1108 }
1109
1110 #[test]
1113 fn test_eq_true() {
1114 let e = compile("5 = 5");
1115 let v = eval(&e, &bindings(&[]));
1116 assert_eq!(v, ExprValue::Bool(true));
1117 }
1118
1119 #[test]
1120 fn test_eq_false() {
1121 let e = compile("5 = 6");
1122 let v = eval(&e, &bindings(&[]));
1123 assert_eq!(v, ExprValue::Bool(false));
1124 }
1125
1126 #[test]
1127 fn test_ne() {
1128 let e = compile("5 != 6");
1129 let v = eval(&e, &bindings(&[]));
1130 assert_eq!(v, ExprValue::Bool(true));
1131 }
1132
1133 #[test]
1134 fn test_lt() {
1135 let e = compile("3 < 5");
1136 let v = eval(&e, &bindings(&[]));
1137 assert_eq!(v, ExprValue::Bool(true));
1138 }
1139
1140 #[test]
1141 fn test_le() {
1142 let e = compile("5 <= 5");
1143 let v = eval(&e, &bindings(&[]));
1144 assert_eq!(v, ExprValue::Bool(true));
1145 }
1146
1147 #[test]
1148 fn test_gt() {
1149 let e = compile("7 > 5");
1150 let v = eval(&e, &bindings(&[]));
1151 assert_eq!(v, ExprValue::Bool(true));
1152 }
1153
1154 #[test]
1155 fn test_ge() {
1156 let e = compile("5 >= 5");
1157 let v = eval(&e, &bindings(&[]));
1158 assert_eq!(v, ExprValue::Bool(true));
1159 }
1160
1161 #[test]
1164 fn test_and_true() {
1165 let e = compile("true && true");
1166 let v = eval(&e, &bindings(&[]));
1167 assert_eq!(v, ExprValue::Bool(true));
1168 }
1169
1170 #[test]
1171 fn test_and_false() {
1172 let e = compile("true && false");
1173 let v = eval(&e, &bindings(&[]));
1174 assert_eq!(v, ExprValue::Bool(false));
1175 }
1176
1177 #[test]
1178 fn test_or_false_true() {
1179 let e = compile("false || true");
1180 let v = eval(&e, &bindings(&[]));
1181 assert_eq!(v, ExprValue::Bool(true));
1182 }
1183
1184 #[test]
1185 fn test_not_false() {
1186 let e = compile("!false");
1187 let v = eval(&e, &bindings(&[]));
1188 assert_eq!(v, ExprValue::Bool(true));
1189 }
1190
1191 #[test]
1192 fn test_not_true() {
1193 let e = compile("!true");
1194 let v = eval(&e, &bindings(&[]));
1195 assert_eq!(v, ExprValue::Bool(false));
1196 }
1197
1198 #[test]
1201 fn test_if_true_branch() {
1202 let e = compile("IF(true, 1, 2)");
1203 let v = eval(&e, &bindings(&[]));
1204 assert_eq!(v, ExprValue::Integer(1));
1205 }
1206
1207 #[test]
1208 fn test_if_false_branch() {
1209 let e = compile("IF(false, 1, 2)");
1210 let v = eval(&e, &bindings(&[]));
1211 assert_eq!(v, ExprValue::Integer(2));
1212 }
1213
1214 #[test]
1215 fn test_if_with_condition_expr() {
1216 let e = compile("IF(3 > 2, 100, 200)");
1217 let v = eval(&e, &bindings(&[]));
1218 assert_eq!(v, ExprValue::Integer(100));
1219 }
1220
1221 #[test]
1224 fn test_bound_true() {
1225 let e = compile("BOUND(?x)");
1226 let v = eval(&e, &bindings(&[("x", ExprValue::Integer(1))]));
1227 assert_eq!(v, ExprValue::Bool(true));
1228 }
1229
1230 #[test]
1231 fn test_bound_false() {
1232 let e = compile("BOUND(?missing)");
1233 let v = eval(&e, &bindings(&[]));
1234 assert_eq!(v, ExprValue::Bool(false));
1235 }
1236
1237 #[test]
1238 fn test_isiri_true() {
1239 let e = compile("ISIRI(?x)");
1240 let v = eval(
1241 &e,
1242 &bindings(&[("x", ExprValue::Iri("http://ex.org/".to_string()))]),
1243 );
1244 assert_eq!(v, ExprValue::Bool(true));
1245 }
1246
1247 #[test]
1248 fn test_isiri_false() {
1249 let e = compile("ISIRI(?x)");
1250 let v = eval(
1251 &e,
1252 &bindings(&[("x", ExprValue::Str("not-iri".to_string()))]),
1253 );
1254 assert_eq!(v, ExprValue::Bool(false));
1255 }
1256
1257 #[test]
1258 fn test_isliteral_integer() {
1259 let e = compile("ISLITERAL(?x)");
1260 let v = eval(&e, &bindings(&[("x", ExprValue::Integer(42))]));
1261 assert_eq!(v, ExprValue::Bool(true));
1262 }
1263
1264 #[test]
1265 fn test_isblank_true() {
1266 let e = compile("ISBLANK(?b)");
1267 let v = eval(&e, &bindings(&[("b", ExprValue::Blank("b1".to_string()))]));
1268 assert_eq!(v, ExprValue::Bool(true));
1269 }
1270
1271 #[test]
1272 fn test_str_integer() {
1273 let e = compile("STR(?x)");
1274 let v = eval(&e, &bindings(&[("x", ExprValue::Integer(99))]));
1275 assert_eq!(v, ExprValue::Str("99".to_string()));
1276 }
1277
1278 #[test]
1279 fn test_str_iri() {
1280 let e = compile("STR(?x)");
1281 let v = eval(
1282 &e,
1283 &bindings(&[("x", ExprValue::Iri("http://example.org/".to_string()))]),
1284 );
1285 assert_eq!(v, ExprValue::Str("http://example.org/".to_string()));
1286 }
1287
1288 #[test]
1289 fn test_lang_plain_string() {
1290 let e = compile(r#"LANG(?x)"#);
1291 let v = eval(&e, &bindings(&[("x", ExprValue::Str("hello".to_string()))]));
1292 assert_eq!(v, ExprValue::Str(String::new()));
1293 }
1294
1295 #[test]
1296 fn test_datatype_string() {
1297 let e = compile(r#"DATATYPE(?x)"#);
1298 let v = eval(&e, &bindings(&[("x", ExprValue::Str("hi".to_string()))]));
1299 assert_eq!(
1300 v,
1301 ExprValue::Iri("http://www.w3.org/2001/XMLSchema#string".to_string())
1302 );
1303 }
1304
1305 #[test]
1306 fn test_datatype_integer() {
1307 let e = compile("DATATYPE(?x)");
1308 let v = eval(&e, &bindings(&[("x", ExprValue::Integer(1))]));
1309 assert_eq!(
1310 v,
1311 ExprValue::Iri("http://www.w3.org/2001/XMLSchema#integer".to_string())
1312 );
1313 }
1314
1315 #[test]
1316 fn test_coalesce_first_bound() {
1317 let e = compile("COALESCE(?a, ?b, 42)");
1318 let v = eval(
1319 &e,
1320 &bindings(&[("a", ExprValue::Integer(1)), ("b", ExprValue::Integer(2))]),
1321 );
1322 assert_eq!(v, ExprValue::Integer(1));
1323 }
1324
1325 #[test]
1326 fn test_coalesce_skip_unbound() {
1327 let e = compile("COALESCE(?missing, 99)");
1328 let v = eval(&e, &bindings(&[]));
1329 assert_eq!(v, ExprValue::Integer(99));
1330 }
1331
1332 #[test]
1333 fn test_coalesce_all_unbound() {
1334 let e = compile("COALESCE(?a, ?b)");
1335 let v = eval(&e, &bindings(&[]));
1336 assert_eq!(v, ExprValue::Unbound);
1337 }
1338
1339 #[test]
1342 fn test_nested_arithmetic() {
1343 let e = compile("(2 + 3) * 4");
1344 let v = eval(&e, &bindings(&[]));
1345 assert_eq!(v, ExprValue::Integer(20));
1346 }
1347
1348 #[test]
1349 fn test_nested_comparison_and() {
1350 let e = compile("3 > 2 && 5 < 10");
1351 let v = eval(&e, &bindings(&[]));
1352 assert_eq!(v, ExprValue::Bool(true));
1353 }
1354
1355 #[test]
1356 fn test_complex_variable_expr() {
1357 let e = compile("?age >= 18 && ?age < 65");
1358 let v = eval(&e, &bindings(&[("age", ExprValue::Integer(30))]));
1359 assert_eq!(v, ExprValue::Bool(true));
1360 }
1361
1362 #[test]
1365 fn test_div_by_zero_error() {
1366 let e = compile("10 / 0");
1367 let result = compiler().evaluate(&e, &bindings(&[]));
1368 assert!(result.is_err());
1369 }
1370
1371 #[test]
1372 fn test_type_mismatch_neg() {
1373 let e = compile(r#"-"hello""#);
1374 let result = compiler().evaluate(&e, &bindings(&[]));
1375 assert!(result.is_err());
1376 }
1377
1378 #[test]
1379 fn test_compile_error_bare_name() {
1380 let result = ExprCompiler::new().compile("undefined_function");
1381 assert!(result.is_err());
1382 }
1383
1384 #[test]
1385 fn test_if_wrong_arg_count() {
1386 let result = ExprCompiler::new().compile("IF(true, 1)");
1387 assert!(result.is_err());
1388 }
1389
1390 #[test]
1393 fn test_cache_basic() {
1394 let mut cache = ExprCache::new(10);
1395 cache.get_or_compile("1 + 1").expect("compile ok");
1396 assert_eq!(cache.cache_size(), 1);
1397 assert_eq!(cache.misses(), 1);
1398 assert_eq!(cache.hits(), 0);
1399 }
1400
1401 #[test]
1402 fn test_cache_hit() {
1403 let mut cache = ExprCache::new(10);
1404 cache.get_or_compile("2 + 2").expect("ok");
1405 cache.get_or_compile("2 + 2").expect("ok");
1406 assert_eq!(cache.hits(), 1);
1407 assert_eq!(cache.misses(), 1);
1408 assert_eq!(cache.cache_size(), 1);
1409 }
1410
1411 #[test]
1412 fn test_cache_lru_eviction() {
1413 let mut cache = ExprCache::new(3);
1414 cache.get_or_compile("1").expect("ok"); cache.get_or_compile("2").expect("ok"); cache.get_or_compile("3").expect("ok"); cache.get_or_compile("1").expect("ok"); cache.get_or_compile("4").expect("ok"); assert_eq!(cache.cache_size(), 3);
1422 assert!(cache.cache.contains_key("1"));
1424 assert!(!cache.cache.contains_key("2"));
1425 assert!(cache.cache.contains_key("3"));
1426 assert!(cache.cache.contains_key("4"));
1427 }
1428
1429 #[test]
1430 fn test_cache_clear() {
1431 let mut cache = ExprCache::new(5);
1432 cache.get_or_compile("42").expect("ok");
1433 cache.clear();
1434 assert_eq!(cache.cache_size(), 0);
1435 assert_eq!(cache.hits(), 0);
1436 assert_eq!(cache.misses(), 0);
1437 }
1438
1439 #[test]
1440 fn test_cache_multiple_expressions() {
1441 let mut cache = ExprCache::new(10);
1442 let exprs = ["1 + 1", "2 * 3", "true && false", "?x = 5", "BOUND(?y)"];
1443 for s in &exprs {
1444 cache.get_or_compile(s).expect("ok");
1445 }
1446 assert_eq!(cache.cache_size(), exprs.len());
1447 assert_eq!(cache.misses(), exprs.len() as u64);
1448 }
1449
1450 #[test]
1451 fn test_string_comparison() {
1452 let e = compile(r#""apple" = "apple""#);
1453 let v = eval(&e, &bindings(&[]));
1454 assert_eq!(v, ExprValue::Bool(true));
1455 }
1456
1457 #[test]
1458 fn test_mixed_numeric_types() {
1459 let e = compile("3 + 1.5");
1460 let v = eval(&e, &bindings(&[]));
1461 assert_eq!(v, ExprValue::Double(4.5));
1462 }
1463}