params pool

main
Ziyang Hu 2 years ago
parent 6362083faf
commit 1f5639e8e3

@ -9,6 +9,7 @@ COMMENT = _{(BLOCK_COMMENT | LINE_COMMENT)}
prog_entry = {"?"} prog_entry = {"?"}
var = @{"?" ~ (XID_CONTINUE | "_")*} var = @{"?" ~ (XID_CONTINUE | "_")*}
param = @{"$" ~ (XID_CONTINUE | "_")*}
ident = @{XID_START ~ ("_" | XID_CONTINUE)*} ident = @{XID_START ~ ("_" | XID_CONTINUE)*}
compound_ident = {ident ~ ("." ~ ident)?} compound_ident = {ident ~ ("." ~ ident)?}
@ -56,7 +57,7 @@ unary_op = _{ minus | negate }
minus = { "-" } minus = { "-" }
negate = { "!" } negate = { "!" }
term = _{ var | grouping | apply | list | literal } term = _{ var | param | grouping | apply | list | literal }
list = { "[" ~ (expr ~ ",")* ~ expr? ~ "]" } list = { "[" ~ (expr ~ ",")* ~ expr? ~ "]" }
grouping = { "(" ~ expr ~ ")" } grouping = { "(" ~ expr ~ ")" }

@ -3,7 +3,7 @@ use std::fmt::{Debug, Formatter};
use std::mem; use std::mem;
use std::ops::Rem; use std::ops::Rem;
use anyhow::{bail, Result}; use anyhow::{anyhow, bail, Result};
use itertools::Itertools; use itertools::Itertools;
use crate::data::symb::Symbol; use crate::data::symb::Symbol;
@ -13,6 +13,7 @@ use crate::data::value::{DataValue, Number};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) enum Expr { pub(crate) enum Expr {
Binding(Symbol, Option<usize>), Binding(Symbol, Option<usize>),
Param(Symbol),
Const(DataValue), Const(DataValue),
Apply(&'static Op, Box<[Expr]>), Apply(&'static Op, Box<[Expr]>),
} }
@ -30,7 +31,7 @@ impl Expr {
let found_idx = *binding_map.get(k).unwrap(); let found_idx = *binding_map.get(k).unwrap();
*idx = Some(found_idx) *idx = Some(found_idx)
} }
Expr::Const(_) => {} Expr::Const(_) | Expr::Param(_) => {}
Expr::Apply(_, args) => { Expr::Apply(_, args) => {
for arg in args.iter_mut() { for arg in args.iter_mut() {
arg.fill_binding_indices(binding_map); arg.fill_binding_indices(binding_map);
@ -38,11 +39,24 @@ impl Expr {
} }
} }
} }
pub(crate) fn partial_eval(&mut self) -> Result<()> { pub(crate) fn partial_eval(&mut self, param_pool: &BTreeMap<Symbol, DataValue>) -> Result<()> {
let found_val = if let Expr::Param(s) = self {
Some(
param_pool
.get(s)
.ok_or_else(|| anyhow!("input parameter {} not found", s))?,
)
} else {
None
};
if let Some(found_val) = found_val {
*self = Expr::Const(found_val.clone());
return Ok(());
}
if let Expr::Apply(_, args) = self { if let Expr::Apply(_, args) = self {
let mut all_evaluated = true; let mut all_evaluated = true;
for arg in args.iter_mut() { for arg in args.iter_mut() {
arg.partial_eval()?; arg.partial_eval(param_pool)?;
all_evaluated = all_evaluated && matches!(arg, Expr::Const(_)); all_evaluated = all_evaluated && matches!(arg, Expr::Const(_));
} }
if all_evaluated { if all_evaluated {
@ -73,7 +87,7 @@ impl Expr {
Expr::Binding(b, _) => { Expr::Binding(b, _) => {
coll.insert(b.clone()); coll.insert(b.clone());
} }
Expr::Const(_) => {} Expr::Const(_) | Expr::Param(_) => {}
Expr::Apply(_, args) => { Expr::Apply(_, args) => {
for arg in args.iter() { for arg in args.iter() {
arg.collect_bindings(coll) arg.collect_bindings(coll)
@ -89,6 +103,7 @@ impl Expr {
let args: Box<[DataValue]> = args.iter().map(|v| v.eval(bindings)).try_collect()?; let args: Box<[DataValue]> = args.iter().map(|v| v.eval(bindings)).try_collect()?;
(op.inner)(&args) (op.inner)(&args)
} }
Expr::Param(s) => bail!("input var {} not bound", s),
} }
} }
pub(crate) fn eval_pred(&self, bindings: &Tuple) -> Result<bool> { pub(crate) fn eval_pred(&self, bindings: &Tuple) -> Result<bool> {

@ -367,6 +367,7 @@ fn build_unary(pair: Pair<'_>) -> Result<JsonValue> {
Ok(match op { Ok(match op {
Rule::term => build_unary(p)?, Rule::term => build_unary(p)?,
Rule::var => json!(s), Rule::var => json!(s),
Rule::param => json!({"param": s}),
Rule::minus => { Rule::minus => {
let inner = build_unary(inner.next().unwrap())?; let inner = build_unary(inner.next().unwrap())?;
json!({"op": "Minus", "args": [inner]}) json!({"op": "Minus", "args": [inner]})

@ -75,6 +75,7 @@ impl SessionTx {
pub(crate) fn parse_query( pub(crate) fn parse_query(
&mut self, &mut self,
payload: &JsonValue, payload: &JsonValue,
params_pool: &BTreeMap<Symbol, DataValue>,
) -> Result<(InputProgram, QueryOutOptions, ConstRules)> { ) -> Result<(InputProgram, QueryOutOptions, ConstRules)> {
let vld = match payload.get("since") { let vld = match payload.get("since") {
None => Validity::current(), None => Validity::current(),
@ -89,9 +90,9 @@ impl SessionTx {
ensure!(!rules_payload.is_empty(), "no rules in {}", payload); ensure!(!rules_payload.is_empty(), "no rules in {}", payload);
let input_prog = if rules_payload.first().unwrap().is_array() { let input_prog = if rules_payload.first().unwrap().is_array() {
let q = json!([{"rule": "?", "args": rules_payload}]); let q = json!([{"rule": "?", "args": rules_payload}]);
self.parse_input_rule_sets(&q, vld)? self.parse_input_rule_sets(&q, vld, &params_pool)?
} else { } else {
self.parse_input_rule_sets(q, vld)? self.parse_input_rule_sets(q, vld, &params_pool)?
}; };
let entry_bindings = &input_prog let entry_bindings = &input_prog
.prog .prog
@ -131,7 +132,7 @@ impl SessionTx {
anyhow!("data in rule is expected to be an array, got {}", v) anyhow!("data in rule is expected to be an array, got {}", v)
})? })?
.iter() .iter()
.map(|v| Self::parse_const_expr(v)) .map(|v| Self::parse_const_expr(v, &params_pool))
.try_collect()?; .try_collect()?;
Ok(Tuple(tuple)) Ok(Tuple(tuple))
}) })
@ -282,12 +283,13 @@ impl SessionTx {
&mut self, &mut self,
payload: &JsonValue, payload: &JsonValue,
default_vld: Validity, default_vld: Validity,
params_pool: &BTreeMap<Symbol, DataValue>,
) -> Result<InputProgram> { ) -> Result<InputProgram> {
let rules = payload let rules = payload
.as_array() .as_array()
.ok_or_else(|| anyhow!("expect array for rules, got {}", payload))? .ok_or_else(|| anyhow!("expect array for rules, got {}", payload))?
.iter() .iter()
.map(|o| self.parse_input_rule_definition(o, default_vld)); .map(|o| self.parse_input_rule_definition(o, default_vld, params_pool));
let mut collected: BTreeMap<Symbol, Vec<InputRule>> = BTreeMap::new(); let mut collected: BTreeMap<Symbol, Vec<InputRule>> = BTreeMap::new();
for res in rules { for res in rules {
let (name, rule) = res?; let (name, rule) = res?;
@ -325,7 +327,10 @@ impl SessionTx {
} }
} }
} }
fn parse_input_predicate_atom(payload: &Map<String, JsonValue>) -> Result<InputAtom> { fn parse_input_predicate_atom(
payload: &Map<String, JsonValue>,
param_pool: &BTreeMap<Symbol, DataValue>,
) -> Result<InputAtom> {
let mut pred = Self::parse_apply_expr(payload)?; let mut pred = Self::parse_apply_expr(payload)?;
if let Expr::Apply(op, _) = &pred { if let Expr::Apply(op, _) = &pred {
ensure!( ensure!(
@ -334,7 +339,7 @@ impl SessionTx {
op.name op.name
); );
} }
pred.partial_eval()?; pred.partial_eval(param_pool)?;
Ok(InputAtom::Predicate(pred)) Ok(InputAtom::Predicate(pred))
} }
fn parse_unification(payload: &Map<String, JsonValue>) -> Result<InputAtom> { fn parse_unification(payload: &Map<String, JsonValue>) -> Result<InputAtom> {
@ -356,6 +361,18 @@ impl SessionTx {
Ok(InputAtom::Unification(Unification { binding, expr })) Ok(InputAtom::Unification(Unification { binding, expr }))
} }
fn parse_apply_expr(payload: &Map<String, JsonValue>) -> Result<Expr> { fn parse_apply_expr(payload: &Map<String, JsonValue>) -> Result<Expr> {
if let Some(name) = payload.get("param") {
let name = name
.as_str()
.ok_or_else(|| anyhow!("input var cannot be specified as {}", name))?;
ensure!(
name.starts_with("$") && name.len() > 1,
"wrong input var format: {}",
name
);
return Ok(Expr::Param(Symbol::from(name)));
}
let name = payload let name = payload
.get("op") .get("op")
.ok_or_else(|| anyhow!("expect expression to have key 'pred'"))? .ok_or_else(|| anyhow!("expect expression to have key 'pred'"))?
@ -393,15 +410,21 @@ impl SessionTx {
Ok(Expr::Apply(op, args)) Ok(Expr::Apply(op, args))
} }
fn parse_const_expr(payload: &JsonValue) -> Result<DataValue> { fn parse_const_expr(
payload: &JsonValue,
params_pool: &BTreeMap<Symbol, DataValue>,
) -> Result<DataValue> {
Ok(match payload { Ok(match payload {
Value::Array(arr) => { Value::Array(arr) => {
let exprs: Vec<_> = arr.iter().map(Self::parse_const_expr).try_collect()?; let exprs: Vec<_> = arr
.iter()
.map(|v| Self::parse_const_expr(v, params_pool))
.try_collect()?;
DataValue::List(exprs.into()) DataValue::List(exprs.into())
} }
Value::Object(m) => { Value::Object(m) => {
let mut ex = Self::parse_apply_expr(m)?; let mut ex = Self::parse_apply_expr(m)?;
ex.partial_eval()?; ex.partial_eval(params_pool)?;
match ex { match ex {
Expr::Const(c) => c, Expr::Const(c) => c,
v => bail!("failed to convert {:?} to constant", v), v => bail!("failed to convert {:?} to constant", v),
@ -482,6 +505,7 @@ impl SessionTx {
&mut self, &mut self,
payload: &JsonValue, payload: &JsonValue,
default_vld: Validity, default_vld: Validity,
params_pool: &BTreeMap<Symbol, DataValue>,
) -> Result<(Symbol, InputRule)> { ) -> Result<(Symbol, InputRule)> {
let rule_name = payload let rule_name = payload
.get("rule") .get("rule")
@ -542,7 +566,7 @@ impl SessionTx {
} }
} }
let rule_body: Vec<InputAtom> = args let rule_body: Vec<InputAtom> = args
.map(|el| self.parse_input_atom(el, default_vld)) .map(|el| self.parse_input_atom(el, default_vld, params_pool))
.try_collect()?; .try_collect()?;
ensure!( ensure!(
@ -561,7 +585,12 @@ impl SessionTx {
}, },
)) ))
} }
fn parse_input_atom(&mut self, payload: &JsonValue, vld: Validity) -> Result<InputAtom> { fn parse_input_atom(
&mut self,
payload: &JsonValue,
vld: Validity,
params_pool: &BTreeMap<Symbol, DataValue>,
) -> Result<InputAtom> {
match payload { match payload {
JsonValue::Array(arr) => match arr as &[JsonValue] { JsonValue::Array(arr) => match arr as &[JsonValue] {
[entity_rep, attr_rep, value_rep] => { [entity_rep, attr_rep, value_rep] => {
@ -573,7 +602,7 @@ impl SessionTx {
if map.contains_key("rule") { if map.contains_key("rule") {
self.parse_input_rule_atom(map, vld) self.parse_input_rule_atom(map, vld)
} else if map.contains_key("op") { } else if map.contains_key("op") {
Self::parse_input_predicate_atom(map) Self::parse_input_predicate_atom(map, params_pool)
} else if map.contains_key("unify") { } else if map.contains_key("unify") {
Self::parse_unification(map) Self::parse_unification(map)
} else if map.contains_key("conj") } else if map.contains_key("conj")
@ -585,7 +614,7 @@ impl SessionTx {
"arity mismatch for atom definition {:?}: expect only one key", "arity mismatch for atom definition {:?}: expect only one key",
map map
); );
self.parse_input_logical_atom(map, vld) self.parse_input_logical_atom(map, vld, params_pool)
} else { } else {
bail!("unexpected atom definition {:?}", map); bail!("unexpected atom definition {:?}", map);
} }
@ -597,11 +626,12 @@ impl SessionTx {
&mut self, &mut self,
map: &Map<String, JsonValue>, map: &Map<String, JsonValue>,
vld: Validity, vld: Validity,
params_pool: &BTreeMap<Symbol, DataValue>,
) -> Result<InputAtom> { ) -> Result<InputAtom> {
let (k, v) = map.iter().next().unwrap(); let (k, v) = map.iter().next().unwrap();
Ok(match k as &str { Ok(match k as &str {
"not_exists" => { "not_exists" => {
let arg = self.parse_input_atom(v, vld)?; let arg = self.parse_input_atom(v, vld, params_pool)?;
InputAtom::Negation(Box::new(arg)) InputAtom::Negation(Box::new(arg))
} }
n @ ("conj" | "disj") => { n @ ("conj" | "disj") => {
@ -609,7 +639,7 @@ impl SessionTx {
.as_array() .as_array()
.ok_or_else(|| anyhow!("expect array argument for atom {}", n))? .ok_or_else(|| anyhow!("expect array argument for atom {}", n))?
.iter() .iter()
.map(|a| self.parse_input_atom(a, vld)) .map(|a| self.parse_input_atom(a, vld, params_pool))
.try_collect()?; .try_collect()?;
if k == "conj" { if k == "conj" {
InputAtom::Conjunction(args) InputAtom::Conjunction(args)

@ -107,7 +107,7 @@ impl InputAtom {
InputAtom::AttrTriple(a) => a.normalize(false, gen), InputAtom::AttrTriple(a) => a.normalize(false, gen),
InputAtom::Rule(r) => r.normalize(false, gen), InputAtom::Rule(r) => r.normalize(false, gen),
InputAtom::Predicate(mut p) => { InputAtom::Predicate(mut p) => {
p.partial_eval()?; p.partial_eval(&Default::default())?;
Disjunction::singlet(NormalFormAtom::Predicate(p)) Disjunction::singlet(NormalFormAtom::Predicate(p))
} }
InputAtom::Negation(n) => match *n { InputAtom::Negation(n) => match *n {

@ -302,7 +302,8 @@ impl Db {
} }
pub fn run_query(&self, payload: &JsonValue) -> Result<JsonValue> { pub fn run_query(&self, payload: &JsonValue) -> Result<JsonValue> {
let mut tx = self.transact()?; let mut tx = self.transact()?;
let (input_program, out_opts, const_rules) = tx.parse_query(payload)?; let (input_program, out_opts, const_rules) =
tx.parse_query(payload, &Default::default())?;
let entry_head = &input_program.prog.get(&PROG_ENTRY).unwrap()[0].head.clone(); let entry_head = &input_program.prog.get(&PROG_ENTRY).unwrap()[0].head.clone();
let program = input_program let program = input_program
.to_normalized_program()? .to_normalized_program()?
@ -344,7 +345,8 @@ impl Db {
} }
pub fn explain_query(&self, payload: &JsonValue) -> Result<JsonValue> { pub fn explain_query(&self, payload: &JsonValue) -> Result<JsonValue> {
let mut tx = self.transact()?; let mut tx = self.transact()?;
let (input_program, _out_opts, const_rules) = tx.parse_query(payload)?; let (input_program, _out_opts, const_rules) =
tx.parse_query(payload, &Default::default())?;
let normalized_program = input_program.to_normalized_program()?; let normalized_program = input_program.to_normalized_program()?;
let stratified_program = normalized_program.stratify()?; let stratified_program = normalized_program.stratify()?;
let magic_program = stratified_program.magic_sets_rewrite(); let magic_program = stratified_program.magic_sets_rewrite();

Loading…
Cancel
Save