use stack-based bytecodes instead of tree-walking for evaluating expressions

main
Ziyang Hu 2 years ago
parent 985c0d758a
commit c110f3be45

@ -214,7 +214,7 @@ goto_stmt = {"%goto" ~ ident}
return_stmt = {"%return" ~ query_script_inner?} return_stmt = {"%return" ~ query_script_inner?}
while_block = {"%while" ~ query_script_inner ~ "%loop" ~ imperative_block ~ "%end"} while_block = {"%while" ~ query_script_inner ~ "%loop" ~ imperative_block ~ "%end"}
do_while_block = {"%loop" ~ imperative_block ~ "%while" ~ query_script_inner ~ "%end"} do_while_block = {"%loop" ~ imperative_block ~ "%while" ~ query_script_inner ~ "%end"}
temp_swap {"%swap" ~ ident ~ ident} temp_swap = {"%swap" ~ underscore_ident ~ underscore_ident}
/* /*

@ -21,8 +21,132 @@ use thiserror::Error;
use crate::data::functions::*; use crate::data::functions::*;
use crate::data::symb::Symbol; use crate::data::symb::Symbol;
use crate::data::value::{DataValue, LARGEST_UTF_CHAR}; use crate::data::value::{DataValue, LARGEST_UTF_CHAR};
use crate::parse::expr::expr2bytecode;
use crate::parse::SourceSpan; use crate::parse::SourceSpan;
#[derive(Clone, PartialEq, Eq, serde_derive::Serialize, serde_derive::Deserialize, Debug)]
pub enum ExprByteCode {
/// push 1
Binding {
var: Symbol,
tuple_pos: Option<usize>,
},
/// push 1
Const {
val: DataValue,
#[serde(skip)]
span: SourceSpan,
},
/// pop n, push 1
Apply {
op: &'static Op,
arity: usize,
#[serde(skip)]
span: SourceSpan,
},
/// pop 1
JumpIfFalse {
jump_to: usize,
#[serde(skip)]
span: SourceSpan,
},
/// unchanged
Goto {
jump_to: usize,
#[serde(skip)]
span: SourceSpan,
},
}
#[derive(Error, Diagnostic, Debug)]
#[error("The variable '{0}' is unbound")]
#[diagnostic(code(eval::unbound))]
struct UnboundVariableError(String, #[label] SourceSpan);
#[derive(Error, Diagnostic, Debug)]
#[error("The tuple bound by variable '{0}' is too short: index is {1}, length is {2}")]
#[diagnostic(help("This is definitely a bug. Please report it."))]
#[diagnostic(code(eval::tuple_too_short))]
struct TupleTooShortError(String, usize, usize, #[label] SourceSpan);
pub(crate) fn eval_bytecode_pred(
bytecodes: &[ExprByteCode],
bindings: impl AsRef<[DataValue]>,
stack: &mut Vec<DataValue>,
span: SourceSpan,
) -> Result<bool> {
match eval_bytecode(bytecodes, bindings, stack)? {
DataValue::Bool(b) => Ok(b),
v => bail!(PredicateTypeError(span, v)),
}
}
pub(crate) fn eval_bytecode(
bytecodes: &[ExprByteCode],
bindings: impl AsRef<[DataValue]>,
stack: &mut Vec<DataValue>,
) -> Result<DataValue> {
stack.clear();
let mut pointer = 0;
loop {
if pointer >= bytecodes.len() {
break;
}
let current_instruction = &bytecodes[pointer];
match current_instruction {
ExprByteCode::Binding { var, tuple_pos, .. } => match tuple_pos {
None => {
bail!(UnboundVariableError(var.name.to_string(), var.span))
}
Some(i) => {
let val = bindings
.as_ref()
.get(*i)
.ok_or_else(|| {
TupleTooShortError(
var.name.to_string(),
*i,
bindings.as_ref().len(),
var.span,
)
})?
.clone();
stack.push(val);
pointer += 1;
}
},
ExprByteCode::Const { val, .. } => {
stack.push(val.clone());
pointer += 1;
}
ExprByteCode::Apply { op, arity, span } => {
let frame_start = stack.len() - *arity;
let args_frame = &stack[frame_start..];
let result = (op.inner)(args_frame)
.map_err(|err| EvalRaisedError(*span, err.to_string()))?;
stack.truncate(frame_start);
stack.push(result);
pointer += 1;
}
ExprByteCode::JumpIfFalse { jump_to, span } => {
let val = stack.pop().unwrap();
let cond = val
.get_bool()
.ok_or_else(|| PredicateTypeError(*span, val))?;
if cond {
pointer += 1;
} else {
pointer = *jump_to;
}
}
ExprByteCode::Goto { jump_to, .. } => {
pointer = *jump_to;
}
}
}
Ok(stack.pop().unwrap())
}
#[derive(Clone, PartialEq, Eq, serde_derive::Serialize, serde_derive::Deserialize)] #[derive(Clone, PartialEq, Eq, serde_derive::Serialize, serde_derive::Deserialize)]
pub enum Expr { pub enum Expr {
Binding { Binding {
@ -45,11 +169,11 @@ pub enum Expr {
#[serde(skip)] #[serde(skip)]
span: SourceSpan, span: SourceSpan,
}, },
Try { // Try {
clauses: Vec<Expr>, // clauses: Vec<Expr>,
#[serde(skip)] // #[serde(skip)]
span: SourceSpan, // span: SourceSpan,
}, // },
} }
impl Debug for Expr { impl Debug for Expr {
@ -83,13 +207,6 @@ impl Display for Expr {
} }
writer.finish() writer.finish()
} }
Expr::Try { clauses, .. } => {
let mut writer = f.debug_tuple("try");
for clause in clauses {
writer.field(clause);
}
writer.finish()
}
} }
} }
} }
@ -111,13 +228,15 @@ struct BadEntityId(DataValue, #[label] SourceSpan);
struct EvalRaisedError(#[label] SourceSpan, #[help] String); struct EvalRaisedError(#[label] SourceSpan, #[help] String);
impl Expr { impl Expr {
pub(crate) fn compile(&self) -> Vec<ExprByteCode> {
let mut collector = vec![];
expr2bytecode(self, &mut collector);
collector
}
pub(crate) fn span(&self) -> SourceSpan { pub(crate) fn span(&self) -> SourceSpan {
match self { match self {
Expr::Binding { var, .. } => var.span, Expr::Binding { var, .. } => var.span,
Expr::Const { span, .. } Expr::Const { span, .. } | Expr::Apply { span, .. } | Expr::Cond { span, .. } => *span,
| Expr::Apply { span, .. }
| Expr::Cond { span, .. }
| Expr::Try { span, .. } => *span,
} }
} }
pub(crate) fn get_binding(&self) -> Option<&Symbol> { pub(crate) fn get_binding(&self) -> Option<&Symbol> {
@ -190,11 +309,6 @@ impl Expr {
val.fill_binding_indices(binding_map)?; val.fill_binding_indices(binding_map)?;
} }
} }
Expr::Try { clauses, .. } => {
for clause in clauses {
clause.fill_binding_indices(binding_map)?;
}
}
} }
Ok(()) Ok(())
} }
@ -223,12 +337,11 @@ impl Expr {
cond.do_binding_indices(coll); cond.do_binding_indices(coll);
val.do_binding_indices(coll) val.do_binding_indices(coll)
} }
} } // Expr::Try { clauses, .. } => {
Expr::Try { clauses, .. } => { // for clause in clauses {
for clause in clauses { // clause.do_binding_indices(coll)
clause.do_binding_indices(coll) // }
} // }
}
} }
} }
pub(crate) fn eval_to_const(mut self) -> Result<DataValue> { pub(crate) fn eval_to_const(mut self) -> Result<DataValue> {
@ -301,32 +414,15 @@ impl Expr {
val.collect_bindings(coll) val.collect_bindings(coll)
} }
} }
Expr::Try { clauses, .. } => {
for clause in clauses {
clause.collect_bindings(coll);
}
}
} }
} }
pub(crate) fn eval(&self, bindings: impl AsRef<[DataValue]>) -> Result<DataValue> { pub(crate) fn eval(&self, bindings: impl AsRef<[DataValue]>) -> Result<DataValue> {
match self { match self {
Expr::Binding { var, tuple_pos, .. } => match tuple_pos { Expr::Binding { var, tuple_pos, .. } => match tuple_pos {
None => { None => {
#[derive(Error, Diagnostic, Debug)]
#[error("The variable '{0}' is unbound")]
#[diagnostic(code(eval::unbound))]
struct UnboundVariableError(String, #[label] SourceSpan);
bail!(UnboundVariableError(var.name.to_string(), var.span)) bail!(UnboundVariableError(var.name.to_string(), var.span))
} }
Some(i) => { Some(i) => Ok(bindings
#[derive(Error, Diagnostic, Debug)]
#[error("The tuple bound by variable '{0}' is too short: index is {1}, length is {2}")]
#[diagnostic(help("This is definitely a bug. Please report it."))]
#[diagnostic(code(eval::unbound))]
struct TupleTooShortError(String, usize, usize, #[label] SourceSpan);
Ok(bindings
.as_ref() .as_ref()
.get(*i) .get(*i)
.ok_or_else(|| { .ok_or_else(|| {
@ -337,8 +433,7 @@ impl Expr {
var.span, var.span,
) )
})? })?
.clone()) .clone()),
}
}, },
Expr::Const { val, .. } => Ok(val.clone()), Expr::Const { val, .. } => Ok(val.clone()),
Expr::Apply { op, args, .. } => { Expr::Apply { op, args, .. } => {
@ -362,34 +457,11 @@ impl Expr {
} }
Ok(DataValue::Null) Ok(DataValue::Null)
} }
Expr::Try { clauses, .. } => {
if clauses.is_empty() {
Ok(DataValue::Null)
} else {
for item in clauses.iter().take(clauses.len() - 1) {
let res = item.eval(bindings.as_ref());
if res.is_ok() {
return res;
}
}
clauses[clauses.len() - 1].eval(bindings.as_ref())
}
}
}
}
pub(crate) fn eval_pred(&self, bindings: impl AsRef<[DataValue]>) -> Result<bool> {
match self.eval(bindings)? {
DataValue::Bool(b) => Ok(b),
v => {
bail!(PredicateTypeError(self.span(), v))
}
} }
} }
pub(crate) fn extract_bound(&self, target: &Symbol) -> Result<ValueRange> { pub(crate) fn extract_bound(&self, target: &Symbol) -> Result<ValueRange> {
Ok(match self { Ok(match self {
Expr::Binding { .. } | Expr::Const { .. } | Expr::Cond { .. } | Expr::Try { .. } => { Expr::Binding { .. } | Expr::Const { .. } | Expr::Cond { .. } => ValueRange::default(),
ValueRange::default()
}
Expr::Apply { op, args, .. } => match op.name { Expr::Apply { op, args, .. } => match op.name {
n if n == OP_GE.name || n == OP_GT.name => { n if n == OP_GE.name || n == OP_GT.name => {
if let Some(symb) = args[0].get_binding() { if let Some(symb) = args[0].get_binding() {

@ -14,7 +14,7 @@ use ordered_float::OrderedFloat;
use priority_queue::PriorityQueue; use priority_queue::PriorityQueue;
use smartstring::{LazyCompact, SmartString}; use smartstring::{LazyCompact, SmartString};
use crate::data::expr::Expr; use crate::data::expr::{eval_bytecode, Expr};
use crate::data::symb::Symbol; use crate::data::symb::Symbol;
use crate::data::tuple::Tuple; use crate::data::tuple::Tuple;
use crate::data::value::DataValue; use crate::data::value::DataValue;
@ -81,11 +81,13 @@ fn astar(
) -> Result<(f64, Vec<DataValue>)> { ) -> Result<(f64, Vec<DataValue>)> {
let start_node = &starting[0]; let start_node = &starting[0];
let goal_node = &goal[0]; let goal_node = &goal[0];
let eval_heuristic = |node: &Tuple| -> Result<f64> { let heuristic_bytecode = heuristic.compile();
let mut stack = vec![];
let mut eval_heuristic = |node: &Tuple| -> Result<f64> {
let mut v = node.clone(); let mut v = node.clone();
v.extend_from_slice(goal); v.extend_from_slice(goal);
let t = v; let t = v;
let cost_val = heuristic.eval(&t)?; let cost_val = eval_bytecode(&heuristic_bytecode, &t, &mut stack)?;
let cost = cost_val.get_float().ok_or_else(|| { let cost = cost_val.get_float().ok_or_else(|| {
BadExprValueError( BadExprValueError(
cost_val, cost_val,

@ -12,7 +12,7 @@ use miette::Result;
use smartstring::{LazyCompact, SmartString}; use smartstring::{LazyCompact, SmartString};
use crate::fixed_rule::{FixedRule, FixedRulePayload, NodeNotFoundError}; use crate::fixed_rule::{FixedRule, FixedRulePayload, NodeNotFoundError};
use crate::data::expr::Expr; use crate::data::expr::{eval_bytecode_pred, Expr};
use crate::data::symb::Symbol; use crate::data::symb::Symbol;
use crate::data::value::DataValue; use crate::data::value::DataValue;
use crate::parse::SourceSpan; use crate::parse::SourceSpan;
@ -35,12 +35,15 @@ impl FixedRule for Bfs {
let mut condition = payload.expr_option("condition", None)?; let mut condition = payload.expr_option("condition", None)?;
let binding_map = nodes.get_binding_map(0); let binding_map = nodes.get_binding_map(0);
condition.fill_binding_indices(&binding_map)?; condition.fill_binding_indices(&binding_map)?;
let condition_bytecode = condition.compile();
let condition_span = condition.span();
let binding_indices = condition.binding_indices(); let binding_indices = condition.binding_indices();
let skip_query_nodes = binding_indices.is_subset(&BTreeSet::from([0])); let skip_query_nodes = binding_indices.is_subset(&BTreeSet::from([0]));
let mut visited: BTreeSet<DataValue> = Default::default(); let mut visited: BTreeSet<DataValue> = Default::default();
let mut backtrace: BTreeMap<DataValue, DataValue> = Default::default(); let mut backtrace: BTreeMap<DataValue, DataValue> = Default::default();
let mut found: Vec<(DataValue, DataValue)> = vec![]; let mut found: Vec<(DataValue, DataValue)> = vec![];
let mut stack = vec![];
'outer: for node_tuple in starting_nodes.iter()? { 'outer: for node_tuple in starting_nodes.iter()? {
let node_tuple = node_tuple?; let node_tuple = node_tuple?;
@ -76,7 +79,7 @@ impl FixedRule for Bfs {
})?? })??
}; };
if condition.eval_pred(&cand_tuple)? { if eval_bytecode_pred(&condition_bytecode, &cand_tuple, &mut stack, condition_span)? {
found.push((starting_node.clone(), to_node.clone())); found.push((starting_node.clone(), to_node.clone()));
if found.len() >= limit { if found.len() >= limit {
break 'outer; break 'outer;

@ -11,10 +11,10 @@ use std::collections::{BTreeMap, BTreeSet};
use miette::Result; use miette::Result;
use smartstring::{LazyCompact, SmartString}; use smartstring::{LazyCompact, SmartString};
use crate::fixed_rule::{FixedRule, FixedRulePayload, NodeNotFoundError}; use crate::data::expr::{eval_bytecode_pred, Expr};
use crate::data::expr::Expr;
use crate::data::symb::Symbol; use crate::data::symb::Symbol;
use crate::data::value::DataValue; use crate::data::value::DataValue;
use crate::fixed_rule::{FixedRule, FixedRulePayload, NodeNotFoundError};
use crate::parse::SourceSpan; use crate::parse::SourceSpan;
use crate::runtime::db::Poison; use crate::runtime::db::Poison;
use crate::runtime::temp_store::RegularTempStore; use crate::runtime::temp_store::RegularTempStore;
@ -35,12 +35,15 @@ impl FixedRule for Dfs {
let mut condition = payload.expr_option("condition", None)?; let mut condition = payload.expr_option("condition", None)?;
let binding_map = nodes.get_binding_map(0); let binding_map = nodes.get_binding_map(0);
condition.fill_binding_indices(&binding_map)?; condition.fill_binding_indices(&binding_map)?;
let condition_bytecode = condition.compile();
let condition_span = condition.span();
let binding_indices = condition.binding_indices(); let binding_indices = condition.binding_indices();
let skip_query_nodes = binding_indices.is_subset(&BTreeSet::from([0])); let skip_query_nodes = binding_indices.is_subset(&BTreeSet::from([0]));
let mut visited: BTreeSet<DataValue> = Default::default(); let mut visited: BTreeSet<DataValue> = Default::default();
let mut backtrace: BTreeMap<DataValue, DataValue> = Default::default(); let mut backtrace: BTreeMap<DataValue, DataValue> = Default::default();
let mut found: Vec<(DataValue, DataValue)> = vec![]; let mut found: Vec<(DataValue, DataValue)> = vec![];
let mut stack = vec![];
'outer: for node_tuple in starting_nodes.iter()? { 'outer: for node_tuple in starting_nodes.iter()? {
let node_tuple = node_tuple?; let node_tuple = node_tuple?;
@ -49,10 +52,10 @@ impl FixedRule for Dfs {
continue; continue;
} }
let mut stack: Vec<DataValue> = vec![]; let mut to_visit_stack: Vec<DataValue> = vec![];
stack.push(starting_node.clone()); to_visit_stack.push(starting_node.clone());
while let Some(candidate) = stack.pop() { while let Some(candidate) = to_visit_stack.pop() {
if visited.contains(&candidate) { if visited.contains(&candidate) {
continue; continue;
} }
@ -69,7 +72,8 @@ impl FixedRule for Dfs {
})?? })??
}; };
if condition.eval_pred(&cand_tuple)? { if eval_bytecode_pred(&condition_bytecode, &cand_tuple, &mut stack, condition_span)?
{
found.push((starting_node.clone(), candidate.clone())); found.push((starting_node.clone(), candidate.clone()));
if found.len() >= limit { if found.len() >= limit {
break 'outer; break 'outer;
@ -85,7 +89,7 @@ impl FixedRule for Dfs {
continue; continue;
} }
backtrace.insert(to_node.clone(), candidate.clone()); backtrace.insert(to_node.clone(), candidate.clone());
stack.push(to_node.clone()); to_visit_stack.push(to_node.clone());
poison.check()?; poison.check()?;
} }
} }

@ -15,7 +15,7 @@ use rand::prelude::*;
use smartstring::{LazyCompact, SmartString}; use smartstring::{LazyCompact, SmartString};
use crate::fixed_rule::{FixedRule, FixedRulePayload, BadExprValueError, NodeNotFoundError}; use crate::fixed_rule::{FixedRule, FixedRulePayload, BadExprValueError, NodeNotFoundError};
use crate::data::expr::Expr; use crate::data::expr::{eval_bytecode, Expr};
use crate::data::symb::Symbol; use crate::data::symb::Symbol;
use crate::data::value::DataValue; use crate::data::value::DataValue;
use crate::parse::SourceSpan; use crate::parse::SourceSpan;
@ -38,13 +38,17 @@ impl FixedRule for RandomWalk {
let steps = payload.pos_integer_option("steps", None)?; let steps = payload.pos_integer_option("steps", None)?;
let mut maybe_weight = payload.expr_option("weight", None).ok(); let mut maybe_weight = payload.expr_option("weight", None).ok();
let mut maybe_weight_bytecode = None;
if let Some(weight) = &mut maybe_weight { if let Some(weight) = &mut maybe_weight {
let mut nodes_binding = nodes.get_binding_map(0); let mut nodes_binding = nodes.get_binding_map(0);
let nodes_arity = nodes.arity()?; let nodes_arity = nodes.arity()?;
let edges_binding = edges.get_binding_map(nodes_arity); let edges_binding = edges.get_binding_map(nodes_arity);
nodes_binding.extend(edges_binding); nodes_binding.extend(edges_binding);
weight.fill_binding_indices(&nodes_binding)?; weight.fill_binding_indices(&nodes_binding)?;
maybe_weight_bytecode = Some((weight.compile(), weight.span()));
} }
let maybe_weight_bytecode = maybe_weight_bytecode;
let mut stack = vec![];
let mut counter = 0i64; let mut counter = 0i64;
let mut rng = thread_rng(); let mut rng = thread_rng();
@ -69,20 +73,20 @@ impl FixedRule for RandomWalk {
if candidate_steps.is_empty() { if candidate_steps.is_empty() {
break; break;
} }
let next_step = if let Some(weight_expr) = &maybe_weight { let next_step = if let Some((weight_expr, span)) = &maybe_weight_bytecode {
let weights: Vec<_> = candidate_steps let weights: Vec<_> = candidate_steps
.iter() .iter()
.map(|t| -> Result<f64> { .map(|t| -> Result<f64> {
let mut cand = current_tuple.clone(); let mut cand = current_tuple.clone();
cand.extend_from_slice(t); cand.extend_from_slice(t);
Ok(match weight_expr.eval(&cand)? { Ok(match eval_bytecode(weight_expr, &cand, &mut stack)? {
DataValue::Num(n) => { DataValue::Num(n) => {
let f = n.get_float(); let f = n.get_float();
ensure!( ensure!(
f >= 0., f >= 0.,
BadExprValueError( BadExprValueError(
DataValue::from(f), DataValue::from(f),
weight_expr.span(), *span,
"'weight' must evaluate to a non-negative number" "'weight' must evaluate to a non-negative number"
.to_string() .to_string()
) )
@ -91,7 +95,7 @@ impl FixedRule for RandomWalk {
} }
v => bail!(BadExprValueError( v => bail!(BadExprValueError(
v, v,
weight_expr.span(), *span,
"'weight' must evaluate to a non-negative number" "'weight' must evaluate to a non-negative number"
.to_string() .to_string()
)), )),

@ -12,7 +12,7 @@ use itertools::Itertools;
use miette::{bail, Result}; use miette::{bail, Result};
use smartstring::{LazyCompact, SmartString}; use smartstring::{LazyCompact, SmartString};
use crate::data::expr::Expr; use crate::data::expr::{eval_bytecode, Expr};
use crate::data::functions::OP_LIST; use crate::data::functions::OP_LIST;
use crate::data::program::WrongFixedRuleOptionError; use crate::data::program::WrongFixedRuleOptionError;
use crate::data::symb::Symbol; use crate::data::symb::Symbol;
@ -72,12 +72,18 @@ impl FixedRule for ReorderSort {
for out in out_list.iter_mut() { for out in out_list.iter_mut() {
out.fill_binding_indices(&binding_map)?; out.fill_binding_indices(&binding_map)?;
} }
let out_bytecods = out_list.iter().map(|e| e.compile()).collect_vec();
let sort_by_bytecodes = sort_by.compile();
let mut stack = vec![];
let mut buffer = vec![]; let mut buffer = vec![];
for tuple in in_rel.iter()? { for tuple in in_rel.iter()? {
let tuple = tuple?; let tuple = tuple?;
let sorter = sort_by.eval(&tuple)?; let sorter = eval_bytecode(&sort_by_bytecodes, &tuple, &mut stack)?;
let mut s_tuple: Vec<_> = out_list.iter().map(|ex| ex.eval(&tuple)).try_collect()?; let mut s_tuple: Vec<_> = out_bytecods
.iter()
.map(|ex| eval_bytecode(ex, &tuple, &mut stack))
.try_collect()?;
s_tuple.push(sorter); s_tuple.push(sorter);
buffer.push(s_tuple); buffer.push(s_tuple);
poison.check()?; poison.check()?;

@ -15,7 +15,7 @@ use pest::pratt_parser::{Op, PrattParser};
use smartstring::{LazyCompact, SmartString}; use smartstring::{LazyCompact, SmartString};
use thiserror::Error; use thiserror::Error;
use crate::data::expr::{get_op, Expr}; use crate::data::expr::{get_op, Expr, ExprByteCode};
use crate::data::functions::{ use crate::data::functions::{
OP_ADD, OP_AND, OP_COALESCE, OP_CONCAT, OP_DIV, OP_EQ, OP_GE, OP_GT, OP_LE, OP_LIST, OP_LT, OP_ADD, OP_AND, OP_COALESCE, OP_CONCAT, OP_DIV, OP_EQ, OP_GE, OP_GT, OP_LE, OP_LIST, OP_LT,
OP_MINUS, OP_MOD, OP_MUL, OP_NEGATE, OP_NEQ, OP_OR, OP_POW, OP_SUB, OP_MINUS, OP_MOD, OP_MUL, OP_NEGATE, OP_NEQ, OP_OR, OP_POW, OP_SUB,
@ -53,6 +53,41 @@ lazy_static! {
#[diagnostic(code(parser::invalid_expression))] #[diagnostic(code(parser::invalid_expression))]
pub(crate) struct InvalidExpression(#[label] pub(crate) SourceSpan); pub(crate) struct InvalidExpression(#[label] pub(crate) SourceSpan);
pub(crate) fn expr2bytecode(expr: &Expr, collector: &mut Vec<ExprByteCode>) {
match expr {
Expr::Binding { var, tuple_pos } => collector.push(ExprByteCode::Binding { var: var.clone(), tuple_pos: *tuple_pos }),
Expr::Const { val, span } => collector.push(ExprByteCode::Const { val: val.clone(), span: *span }),
Expr::Apply { op, args, span } => {
let arity = args.len();
for arg in args.iter() {
expr2bytecode(arg, collector);
}
collector.push(ExprByteCode::Apply { op, arity, span: *span })
}
Expr::Cond { clauses, span } => {
let mut return_jump_pos = vec![];
for (cond, val) in clauses {
// +1
expr2bytecode(cond, collector);
// -1
collector.push(ExprByteCode::JumpIfFalse { jump_to: 0, span: *span });
let false_jump_amend_pos = collector.len();
// +1 in this branch
expr2bytecode(val, collector);
collector.push(ExprByteCode::Goto { jump_to: 0, span: *span });
return_jump_pos.push(collector.len());
collector[false_jump_amend_pos] = ExprByteCode::JumpIfFalse {
jump_to: collector.len() + 1,
span: *span,
};
}
for pos in return_jump_pos {
collector[pos] = ExprByteCode::Goto { jump_to: pos, span: *span }
}
}
}
}
pub(crate) fn build_expr(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Result<Expr> { pub(crate) fn build_expr(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Result<Expr> {
ensure!( ensure!(
pair.as_rule() == Rule::expr, pair.as_rule() == Rule::expr,
@ -231,10 +266,6 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Resul
struct FuncNotFoundError(String, #[label] SourceSpan); struct FuncNotFoundError(String, #[label] SourceSpan);
match ident { match ident {
"try" => Expr::Try {
clauses: args,
span,
},
"cond" => { "cond" => {
if args.len() & 1 == 1 { if args.len() & 1 == 1 {
args.insert( args.insert(
@ -245,10 +276,20 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Resul
}, },
) )
} }
let clauses = args let mut clauses = args
.chunks(2) .chunks(2)
.map(|pair| (pair[0].clone(), pair[1].clone())) .map(|pair| (pair[0].clone(), pair[1].clone()))
.collect_vec(); .collect_vec();
clauses.push((
Expr::Const {
val: DataValue::from(true),
span,
},
Expr::Const {
val: DataValue::Null,
span,
},
));
Expr::Cond { clauses, span } Expr::Cond { clauses, span }
} }
"if" => { "if" => {
@ -264,15 +305,16 @@ fn build_term(pair: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Resul
let cond = args.next().unwrap(); let cond = args.next().unwrap();
let then = args.next().unwrap(); let then = args.next().unwrap();
clauses.push((cond, then)); clauses.push((cond, then));
if let Some(else_clause) = args.next() {
clauses.push(( clauses.push((
Expr::Const { Expr::Const {
val: DataValue::from(true), val: DataValue::from(true),
span, span,
}, },
else_clause, args.next().unwrap_or(Expr::Const {
)) val: DataValue::from(true),
} span,
}),
));
Expr::Cond { clauses, span } Expr::Cond { clauses, span }
} }
_ => { _ => {

@ -40,6 +40,16 @@ pub(crate) enum CozoScript {
Sys(SysOp), Sys(SysOp),
} }
pub(crate) enum ImperativeElement {
JumpIfNot(usize),
Goto(usize),
}
pub(crate) struct ImperativeProgram {
}
impl CozoScript { impl CozoScript {
pub(crate) fn get_single_program(self) -> Result<InputProgram> { pub(crate) fn get_single_program(self) -> Result<InputProgram> {
#[derive(Debug, Error, Diagnostic)] #[derive(Debug, Error, Diagnostic)]

@ -124,7 +124,7 @@ impl<'a> SessionTx<'a> {
let header = &rule.head; let header = &rule.head;
let mut relation = let mut relation =
self.compile_magic_rule_body(rule, &k, &store_arities, header)?; self.compile_magic_rule_body(rule, &k, &store_arities, header)?;
relation.fill_binding_indices().with_context(|| { relation.fill_binding_indices_and_compile().with_context(|| {
format!( format!(
"error encountered when filling binding indices for {relation:#?}" "error encountered when filling binding indices for {relation:#?}"
) )

@ -16,7 +16,7 @@ use log::{debug, error};
use miette::{bail, Diagnostic, Result}; use miette::{bail, Diagnostic, Result};
use thiserror::Error; use thiserror::Error;
use crate::data::expr::{compute_bounds, Expr}; use crate::data::expr::{compute_bounds, eval_bytecode, eval_bytecode_pred, Expr, ExprByteCode};
use crate::data::program::MagicSymbol; use crate::data::program::MagicSymbol;
use crate::data::relation::{ColType, NullableColType}; use crate::data::relation::{ColType, NullableColType};
use crate::data::symb::Symbol; use crate::data::symb::Symbol;
@ -60,6 +60,7 @@ pub(crate) struct UnificationRA {
pub(crate) parent: Box<RelAlgebra>, pub(crate) parent: Box<RelAlgebra>,
pub(crate) binding: Symbol, pub(crate) binding: Symbol,
pub(crate) expr: Expr, pub(crate) expr: Expr,
pub(crate) expr_bytecode: Vec<ExprByteCode>,
pub(crate) is_multi: bool, pub(crate) is_multi: bool,
pub(crate) to_eliminate: BTreeSet<Symbol>, pub(crate) to_eliminate: BTreeSet<Symbol>,
pub(crate) span: SourceSpan, pub(crate) span: SourceSpan,
@ -88,7 +89,7 @@ fn eliminate_from_tuple(mut ret: Tuple, eliminate_indices: &BTreeSet<usize>) ->
} }
impl UnificationRA { impl UnificationRA {
fn fill_binding_indices(&mut self) -> Result<()> { fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let parent_bindings: BTreeMap<_, _> = self let parent_bindings: BTreeMap<_, _> = self
.parent .parent
.bindings_after_eliminate() .bindings_after_eliminate()
@ -96,7 +97,9 @@ impl UnificationRA {
.enumerate() .enumerate()
.map(|(a, b)| (b, a)) .map(|(a, b)| (b, a))
.collect(); .collect();
self.expr.fill_binding_indices(&parent_bindings) self.expr.fill_binding_indices(&parent_bindings)?;
self.expr_bytecode = self.expr.compile();
Ok(())
} }
pub(crate) fn do_eliminate_temp_vars(&mut self, used: &BTreeSet<Symbol>) -> Result<()> { pub(crate) fn do_eliminate_temp_vars(&mut self, used: &BTreeSet<Symbol>) -> Result<()> {
for binding in self.parent.bindings_before_eliminate() { for binding in self.parent.bindings_before_eliminate() {
@ -119,12 +122,13 @@ impl UnificationRA {
let mut bindings = self.parent.bindings_after_eliminate(); let mut bindings = self.parent.bindings_after_eliminate();
bindings.push(self.binding.clone()); bindings.push(self.binding.clone());
let eliminate_indices = get_eliminate_indices(&bindings, &self.to_eliminate); let eliminate_indices = get_eliminate_indices(&bindings, &self.to_eliminate);
let mut stack = vec![];
Ok(if self.is_multi { Ok(if self.is_multi {
let it = self let it = self
.parent .parent
.iter(tx, delta_rule, stores)? .iter(tx, delta_rule, stores)?
.map_ok(move |tuple| -> Result<Vec<Tuple>> { .map_ok(move |tuple| -> Result<Vec<Tuple>> {
let result_list = self.expr.eval(&tuple)?; let result_list = eval_bytecode(&self.expr_bytecode, &tuple, &mut stack)?;
let result_list = result_list.get_slice().ok_or_else(|| { let result_list = result_list.get_slice().ok_or_else(|| {
#[derive(Debug, Error, Diagnostic)] #[derive(Debug, Error, Diagnostic)]
#[error("Invalid spread unification")] #[error("Invalid spread unification")]
@ -152,7 +156,7 @@ impl UnificationRA {
self.parent self.parent
.iter(tx, delta_rule, stores)? .iter(tx, delta_rule, stores)?
.map_ok(move |tuple| -> Result<Tuple> { .map_ok(move |tuple| -> Result<Tuple> {
let result = self.expr.eval(&tuple)?; let result = eval_bytecode(&self.expr_bytecode, &tuple, &mut stack)?;
let mut ret = tuple; let mut ret = tuple;
ret.push(result); ret.push(result);
let ret = ret; let ret = ret;
@ -167,7 +171,8 @@ impl UnificationRA {
pub(crate) struct FilteredRA { pub(crate) struct FilteredRA {
pub(crate) parent: Box<RelAlgebra>, pub(crate) parent: Box<RelAlgebra>,
pub(crate) pred: Vec<Expr>, pub(crate) filters: Vec<Expr>,
pub(crate) filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
pub(crate) to_eliminate: BTreeSet<Symbol>, pub(crate) to_eliminate: BTreeSet<Symbol>,
pub(crate) span: SourceSpan, pub(crate) span: SourceSpan,
} }
@ -180,14 +185,14 @@ impl FilteredRA {
} }
} }
let mut nxt = used.clone(); let mut nxt = used.clone();
for e in self.pred.iter() { for e in self.filters.iter() {
nxt.extend(e.bindings()); nxt.extend(e.bindings());
} }
self.parent.eliminate_temp_vars(&nxt)?; self.parent.eliminate_temp_vars(&nxt)?;
Ok(()) Ok(())
} }
fn fill_binding_indices(&mut self) -> Result<()> { fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let parent_bindings: BTreeMap<_, _> = self let parent_bindings: BTreeMap<_, _> = self
.parent .parent
.bindings_after_eliminate() .bindings_after_eliminate()
@ -195,8 +200,9 @@ impl FilteredRA {
.enumerate() .enumerate()
.map(|(a, b)| (b, a)) .map(|(a, b)| (b, a))
.collect(); .collect();
for e in self.pred.iter_mut() { for e in self.filters.iter_mut() {
e.fill_binding_indices(&parent_bindings)?; e.fill_binding_indices(&parent_bindings)?;
self.filters_bytecodes.push((e.compile(), e.span()));
} }
Ok(()) Ok(())
} }
@ -208,13 +214,14 @@ impl FilteredRA {
) -> Result<TupleIter<'a>> { ) -> Result<TupleIter<'a>> {
let bindings = self.parent.bindings_after_eliminate(); let bindings = self.parent.bindings_after_eliminate();
let eliminate_indices = get_eliminate_indices(&bindings, &self.to_eliminate); let eliminate_indices = get_eliminate_indices(&bindings, &self.to_eliminate);
let mut stack = vec![];
Ok(Box::new( Ok(Box::new(
self.parent self.parent
.iter(tx, delta_rule, stores)? .iter(tx, delta_rule, stores)?
.filter_map(move |tuple| match tuple { .filter_map(move |tuple| match tuple {
Ok(t) => { Ok(t) => {
for p in self.pred.iter() { for (p, span) in self.filters_bytecodes.iter() {
match p.eval_pred(&t) { match eval_bytecode_pred(p, &t, &mut stack, *span) {
Ok(false) => return None, Ok(false) => return None,
Err(e) => return Some(Err(e)), Err(e) => return Some(Err(e)),
Ok(true) => {} Ok(true) => {}
@ -303,7 +310,7 @@ impl Debug for RelAlgebra {
RelAlgebra::Filter(r) => f RelAlgebra::Filter(r) => f
.debug_tuple("Filter") .debug_tuple("Filter")
.field(&bindings) .field(&bindings)
.field(&r.pred) .field(&r.filters)
.field(&r.parent) .field(&r.parent)
.finish(), .finish(),
RelAlgebra::Unification(r) => f RelAlgebra::Unification(r) => f
@ -326,35 +333,35 @@ impl Debug for RelAlgebra {
pub(crate) struct InvalidTimeTravelScanning(pub(crate) String, #[label] pub(crate) SourceSpan); pub(crate) struct InvalidTimeTravelScanning(pub(crate) String, #[label] pub(crate) SourceSpan);
impl RelAlgebra { impl RelAlgebra {
pub(crate) fn fill_binding_indices(&mut self) -> Result<()> { pub(crate) fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
match self { match self {
RelAlgebra::Fixed(_) => {} RelAlgebra::Fixed(_) => {}
RelAlgebra::TempStore(d) => { RelAlgebra::TempStore(d) => {
d.fill_binding_indices()?; d.fill_binding_indices_and_compile()?;
} }
RelAlgebra::Stored(v) => { RelAlgebra::Stored(v) => {
v.fill_binding_indices()?; v.fill_binding_indices_and_compile()?;
} }
RelAlgebra::StoredWithValidity(v) => { RelAlgebra::StoredWithValidity(v) => {
v.fill_binding_indices()?; v.fill_binding_indices_and_compile()?;
} }
RelAlgebra::Reorder(r) => { RelAlgebra::Reorder(r) => {
r.relation.fill_binding_indices()?; r.relation.fill_binding_indices_and_compile()?;
} }
RelAlgebra::Filter(f) => { RelAlgebra::Filter(f) => {
f.parent.fill_binding_indices()?; f.parent.fill_binding_indices_and_compile()?;
f.fill_binding_indices()? f.fill_binding_indices_and_compile()?
} }
RelAlgebra::NegJoin(r) => { RelAlgebra::NegJoin(r) => {
r.left.fill_binding_indices()?; r.left.fill_binding_indices_and_compile()?;
} }
RelAlgebra::Unification(u) => { RelAlgebra::Unification(u) => {
u.parent.fill_binding_indices()?; u.parent.fill_binding_indices_and_compile()?;
u.fill_binding_indices()? u.fill_binding_indices_and_compile()?
} }
RelAlgebra::Join(r) => { RelAlgebra::Join(r) => {
r.left.fill_binding_indices()?; r.left.fill_binding_indices_and_compile()?;
r.right.fill_binding_indices()?; r.right.fill_binding_indices_and_compile()?;
} }
} }
Ok(()) Ok(())
@ -381,6 +388,7 @@ impl RelAlgebra {
bindings, bindings,
storage_key, storage_key,
filters: vec![], filters: vec![],
filters_bytecodes: vec![],
span, span,
}) })
} }
@ -395,6 +403,7 @@ impl RelAlgebra {
bindings, bindings,
storage, storage,
filters: vec![], filters: vec![],
filters_bytecodes: vec![],
span, span,
})), })),
Some(vld) => { Some(vld) => {
@ -410,6 +419,7 @@ impl RelAlgebra {
bindings, bindings,
storage, storage,
filters: vec![], filters: vec![],
filters_bytecodes: vec![],
valid_at: vld, valid_at: vld,
span, span,
})) }))
@ -431,21 +441,24 @@ impl RelAlgebra {
let span = filter.span(); let span = filter.span();
RelAlgebra::Filter(FilteredRA { RelAlgebra::Filter(FilteredRA {
parent: Box::new(s), parent: Box::new(s),
pred: vec![filter], filters: vec![filter],
filters_bytecodes: vec![],
to_eliminate: Default::default(), to_eliminate: Default::default(),
span, span,
}) })
} }
RelAlgebra::Filter(FilteredRA { RelAlgebra::Filter(FilteredRA {
parent, parent,
mut pred, filters: mut pred,
filters_bytecodes,
to_eliminate, to_eliminate,
span, span,
}) => { }) => {
pred.push(filter); pred.push(filter);
RelAlgebra::Filter(FilteredRA { RelAlgebra::Filter(FilteredRA {
parent, parent,
pred, filters: pred,
filters_bytecodes,
to_eliminate, to_eliminate,
span, span,
}) })
@ -454,6 +467,7 @@ impl RelAlgebra {
bindings, bindings,
storage_key, storage_key,
mut filters, mut filters,
filters_bytecodes: filters_asm,
span, span,
}) => { }) => {
filters.push(filter); filters.push(filter);
@ -461,6 +475,7 @@ impl RelAlgebra {
bindings, bindings,
storage_key, storage_key,
filters, filters,
filters_bytecodes: filters_asm,
span, span,
}) })
} }
@ -468,6 +483,7 @@ impl RelAlgebra {
bindings, bindings,
storage, storage,
mut filters, mut filters,
filters_bytecodes,
span, span,
}) => { }) => {
filters.push(filter); filters.push(filter);
@ -475,6 +491,7 @@ impl RelAlgebra {
bindings, bindings,
storage, storage,
filters, filters,
filters_bytecodes,
span, span,
}) })
} }
@ -482,6 +499,7 @@ impl RelAlgebra {
bindings, bindings,
storage, storage,
mut filters, mut filters,
filters_bytecodes: filter_bytecodes,
span, span,
valid_at, valid_at,
}) => { }) => {
@ -492,6 +510,7 @@ impl RelAlgebra {
filters, filters,
span, span,
valid_at, valid_at,
filters_bytecodes: filter_bytecodes,
}) })
} }
RelAlgebra::Join(inner) => { RelAlgebra::Join(inner) => {
@ -532,7 +551,8 @@ impl RelAlgebra {
if !remaining.is_empty() { if !remaining.is_empty() {
joined = RelAlgebra::Filter(FilteredRA { joined = RelAlgebra::Filter(FilteredRA {
parent: Box::new(joined), parent: Box::new(joined),
pred: remaining, filters: remaining,
filters_bytecodes: vec![],
to_eliminate: Default::default(), to_eliminate: Default::default(),
span, span,
}); });
@ -552,6 +572,7 @@ impl RelAlgebra {
parent: Box::new(self), parent: Box::new(self),
binding, binding,
expr, expr,
expr_bytecode: vec![],
is_multi, is_multi,
to_eliminate: Default::default(), to_eliminate: Default::default(),
span, span,
@ -757,12 +778,13 @@ fn invert_option_err<T>(v: Result<Option<T>>) -> Option<Result<T>> {
} }
fn filter_iter( fn filter_iter(
filters: Vec<Expr>, filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
it: impl Iterator<Item = Result<Tuple>>, it: impl Iterator<Item = Result<Tuple>>,
) -> impl Iterator<Item = Result<Tuple>> { ) -> impl Iterator<Item = Result<Tuple>> {
let mut stack = vec![];
it.filter_map_ok(move |t| -> Option<Result<Tuple>> { it.filter_map_ok(move |t| -> Option<Result<Tuple>> {
for p in filters.iter() { for (p, span) in filters_bytecodes.iter() {
match p.eval_pred(&t) { match eval_bytecode_pred(p, &t, &mut stack, *span) {
Ok(false) => return None, Ok(false) => return None,
Err(e) => { Err(e) => {
debug!("{:?}", t); debug!("{:?}", t);
@ -795,6 +817,7 @@ pub(crate) struct StoredRA {
pub(crate) bindings: Vec<Symbol>, pub(crate) bindings: Vec<Symbol>,
pub(crate) storage: RelationHandle, pub(crate) storage: RelationHandle,
pub(crate) filters: Vec<Expr>, pub(crate) filters: Vec<Expr>,
pub(crate) filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
pub(crate) span: SourceSpan, pub(crate) span: SourceSpan,
} }
@ -803,12 +826,13 @@ pub(crate) struct StoredWithValidityRA {
pub(crate) bindings: Vec<Symbol>, pub(crate) bindings: Vec<Symbol>,
pub(crate) storage: RelationHandle, pub(crate) storage: RelationHandle,
pub(crate) filters: Vec<Expr>, pub(crate) filters: Vec<Expr>,
pub(crate) filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
pub(crate) valid_at: ValidityTs, pub(crate) valid_at: ValidityTs,
pub(crate) span: SourceSpan, pub(crate) span: SourceSpan,
} }
impl StoredWithValidityRA { impl StoredWithValidityRA {
fn fill_binding_indices(&mut self) -> Result<()> { fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let bindings: BTreeMap<_, _> = self let bindings: BTreeMap<_, _> = self
.bindings .bindings
.iter() .iter()
@ -818,6 +842,7 @@ impl StoredWithValidityRA {
.collect(); .collect();
for e in self.filters.iter_mut() { for e in self.filters.iter_mut() {
e.fill_binding_indices(&bindings)?; e.fill_binding_indices(&bindings)?;
self.filters_bytecodes.push((e.compile(), e.span()));
} }
Ok(()) Ok(())
} }
@ -826,7 +851,7 @@ impl StoredWithValidityRA {
Ok(if self.filters.is_empty() { Ok(if self.filters.is_empty() {
Box::new(it) Box::new(it)
} else { } else {
Box::new(filter_iter(self.filters.clone(), it)) Box::new(filter_iter(self.filters_bytecodes.clone(), it))
}) })
} }
fn prefix_join<'a>( fn prefix_join<'a>(
@ -861,6 +886,7 @@ impl StoredWithValidityRA {
if !l_bound.iter().all(|v| *v == DataValue::Null) if !l_bound.iter().all(|v| *v == DataValue::Null)
|| !u_bound.iter().all(|v| *v == DataValue::Bot) || !u_bound.iter().all(|v| *v == DataValue::Bot)
{ {
let mut stack = vec![];
return Left( return Left(
self.storage self.storage
.skip_scan_bounded_prefix( .skip_scan_bounded_prefix(
@ -872,8 +898,8 @@ impl StoredWithValidityRA {
) )
.map(move |res_found| -> Result<Option<Tuple>> { .map(move |res_found| -> Result<Option<Tuple>> {
let found = res_found?; let found = res_found?;
for p in self.filters.iter() { for (p, span) in self.filters_bytecodes.iter() {
if !p.eval_pred(&found)? { if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None); return Ok(None);
} }
} }
@ -886,13 +912,14 @@ impl StoredWithValidityRA {
} }
} }
skip_range_check = true; skip_range_check = true;
let mut stack = vec![];
Right( Right(
self.storage self.storage
.skip_scan_prefix(tx, &prefix, self.valid_at) .skip_scan_prefix(tx, &prefix, self.valid_at)
.map(move |res_found| -> Result<Option<Tuple>> { .map(move |res_found| -> Result<Option<Tuple>> {
let found = res_found?; let found = res_found?;
for p in self.filters.iter() { for (p, span) in self.filters_bytecodes.iter() {
if !p.eval_pred(&found)? { if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None); return Ok(None);
} }
} }
@ -914,7 +941,7 @@ impl StoredWithValidityRA {
} }
impl StoredRA { impl StoredRA {
fn fill_binding_indices(&mut self) -> Result<()> { fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let bindings: BTreeMap<_, _> = self let bindings: BTreeMap<_, _> = self
.bindings .bindings
.iter() .iter()
@ -924,6 +951,7 @@ impl StoredRA {
.collect(); .collect();
for e in self.filters.iter_mut() { for e in self.filters.iter_mut() {
e.fill_binding_indices(&bindings)?; e.fill_binding_indices(&bindings)?;
self.filters_bytecodes.push((e.compile(), e.span()));
} }
Ok(()) Ok(())
} }
@ -940,6 +968,7 @@ impl StoredRA {
let val_len = self.storage.metadata.non_keys.len(); let val_len = self.storage.metadata.non_keys.len();
let all_right_val_indices: BTreeSet<usize> = let all_right_val_indices: BTreeSet<usize> =
(0..val_len).map(|i| left_tuple_len + key_len + i).collect(); (0..val_len).map(|i| left_tuple_len + key_len + i).collect();
let mut stack = vec![];
if self.filters.is_empty() && eliminate_indices.is_superset(&all_right_val_indices) { if self.filters.is_empty() && eliminate_indices.is_superset(&all_right_val_indices) {
let it = left_iter let it = left_iter
.map_ok(move |tuple| -> Result<Option<Tuple>> { .map_ok(move |tuple| -> Result<Option<Tuple>> {
@ -948,8 +977,8 @@ impl StoredRA {
.map(|i| tuple[*i].clone()) .map(|i| tuple[*i].clone())
.collect_vec(); .collect_vec();
let key = &prefix[0..key_len]; let key = &prefix[0..key_len];
for p in self.filters.iter() { for (p, span) in self.filters_bytecodes.iter() {
if !p.eval_pred(key)? { if !eval_bytecode_pred(p, key, &mut stack, *span)? {
return Ok(None); return Ok(None);
} }
} }
@ -982,8 +1011,8 @@ impl StoredRA {
match self.storage.get(tx, key)? { match self.storage.get(tx, key)? {
None => Ok(None), None => Ok(None),
Some(found) => { Some(found) => {
for p in self.filters.iter() { for (p, span) in self.filters_bytecodes.iter() {
if !p.eval_pred(&found)? { if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None); return Ok(None);
} }
} }
@ -1038,6 +1067,7 @@ impl StoredRA {
.iter() .iter()
.map(|i| tuple[*i].clone()) .map(|i| tuple[*i].clone())
.collect_vec(); .collect_vec();
let mut stack = vec![];
if !skip_range_check && !self.filters.is_empty() { if !skip_range_check && !self.filters.is_empty() {
let other_bindings = &self.bindings[right_join_indices.len()..]; let other_bindings = &self.bindings[right_join_indices.len()..];
@ -1053,8 +1083,8 @@ impl StoredRA {
.scan_bounded_prefix(tx, &prefix, &l_bound, &u_bound) .scan_bounded_prefix(tx, &prefix, &l_bound, &u_bound)
.map(move |res_found| -> Result<Option<Tuple>> { .map(move |res_found| -> Result<Option<Tuple>> {
let found = res_found?; let found = res_found?;
for p in self.filters.iter() { for (p, span) in self.filters_bytecodes.iter() {
if !p.eval_pred(&found)? { if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None); return Ok(None);
} }
} }
@ -1072,8 +1102,8 @@ impl StoredRA {
.scan_prefix(tx, &prefix) .scan_prefix(tx, &prefix)
.map(move |res_found| -> Result<Option<Tuple>> { .map(move |res_found| -> Result<Option<Tuple>> {
let found = res_found?; let found = res_found?;
for p in self.filters.iter() { for (p, span) in self.filters_bytecodes.iter() {
if !p.eval_pred(&found)? { if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None); return Ok(None);
} }
} }
@ -1200,7 +1230,7 @@ impl StoredRA {
Ok(if self.filters.is_empty() { Ok(if self.filters.is_empty() {
Box::new(it) Box::new(it)
} else { } else {
Box::new(filter_iter(self.filters.clone(), it)) Box::new(filter_iter(self.filters_bytecodes.clone(), it))
}) })
} }
} }
@ -1217,11 +1247,12 @@ pub(crate) struct TempStoreRA {
pub(crate) bindings: Vec<Symbol>, pub(crate) bindings: Vec<Symbol>,
pub(crate) storage_key: MagicSymbol, pub(crate) storage_key: MagicSymbol,
pub(crate) filters: Vec<Expr>, pub(crate) filters: Vec<Expr>,
pub(crate) filters_bytecodes: Vec<(Vec<ExprByteCode>, SourceSpan)>,
pub(crate) span: SourceSpan, pub(crate) span: SourceSpan,
} }
impl TempStoreRA { impl TempStoreRA {
fn fill_binding_indices(&mut self) -> Result<()> { fn fill_binding_indices_and_compile(&mut self) -> Result<()> {
let bindings: BTreeMap<_, _> = self let bindings: BTreeMap<_, _> = self
.bindings .bindings
.iter() .iter()
@ -1231,6 +1262,7 @@ impl TempStoreRA {
.collect(); .collect();
for e in self.filters.iter_mut() { for e in self.filters.iter_mut() {
e.fill_binding_indices(&bindings)?; e.fill_binding_indices(&bindings)?;
self.filters_bytecodes.push((e.compile(), e.span()))
} }
Ok(()) Ok(())
} }
@ -1254,7 +1286,7 @@ impl TempStoreRA {
Ok(if self.filters.is_empty() { Ok(if self.filters.is_empty() {
Box::new(it) Box::new(it)
} else { } else {
Box::new(filter_iter(self.filters.clone(), it)) Box::new(filter_iter(self.filters_bytecodes.clone(), it))
}) })
} }
fn neg_join<'a>( fn neg_join<'a>(
@ -1382,6 +1414,7 @@ impl TempStoreRA {
.iter() .iter()
.map(|i| tuple[*i].clone()) .map(|i| tuple[*i].clone())
.collect_vec(); .collect_vec();
let mut stack = vec![];
if !skip_range_check && !self.filters.is_empty() { if !skip_range_check && !self.filters.is_empty() {
let other_bindings = &self.bindings[right_join_indices.len()..]; let other_bindings = &self.bindings[right_join_indices.len()..];
@ -1409,8 +1442,8 @@ impl TempStoreRA {
Ok(Some(ret)) Ok(Some(ret))
} else { } else {
let found = res_found.into_tuple(); let found = res_found.into_tuple();
for p in self.filters.iter() { for (p, span) in self.filters_bytecodes.iter() {
if !p.eval_pred(&found)? { if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None); return Ok(None);
} }
} }
@ -1439,8 +1472,8 @@ impl TempStoreRA {
Ok(Some(ret)) Ok(Some(ret))
} else { } else {
let found = res_found.into_tuple(); let found = res_found.into_tuple();
for p in self.filters.iter() { for (p, span) in self.filters_bytecodes.iter() {
if !p.eval_pred(&found)? { if !eval_bytecode_pred(p, &found, &mut stack, *span)? {
return Ok(None); return Ok(None);
} }
} }
@ -2039,7 +2072,6 @@ impl<'a> Iterator for CachedMaterializedIterator<'a> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::data::value::DataValue; use crate::data::value::DataValue;
use crate::new_cozo_mem; use crate::new_cozo_mem;
#[test] #[test]
@ -2055,6 +2087,9 @@ mod tests {
) )
.unwrap() .unwrap()
.rows; .rows;
assert_eq!(res, vec![vec![DataValue::from(1)], vec![DataValue::from(2)]]) assert_eq!(
res,
vec![vec![DataValue::from(1)], vec![DataValue::from(2)]]
)
} }
} }

@ -676,7 +676,7 @@ impl<'s, S: Storage<'s>> Db<S> {
rel_stack.push(relation); rel_stack.push(relation);
("reorder", json!(null), json!(null), json!(null)) ("reorder", json!(null), json!(null), json!(null))
} }
RelAlgebra::Filter(FilteredRA { parent, pred, .. }) => { RelAlgebra::Filter(FilteredRA { parent, filters: pred, .. }) => {
rel_stack.push(parent); rel_stack.push(parent);
( (
"filter", "filter",

Loading…
Cancel
Save