parsing validity spec

main
Ziyang Hu 2 years ago
parent 3049226df3
commit 438c466f1b

@ -6,6 +6,7 @@
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
use std::cmp::Reverse;
use std::collections::BTreeSet;
use std::ops::{Div, Rem};
use std::str::FromStr;
@ -1030,9 +1031,9 @@ pub(crate) fn op_haversine(args: &[DataValue]) -> Result<DataValue> {
let lon2 = args[3].get_float().ok_or_else(miette)?;
let ret = 2.
* f64::asin(f64::sqrt(
f64::sin((lat1 - lat2) / 2.).powi(2)
+ f64::cos(lat1) * f64::cos(lat2) * f64::sin((lon1 - lon2) / 2.).powi(2),
));
f64::sin((lat1 - lat2) / 2.).powi(2)
+ f64::cos(lat1) * f64::cos(lat2) * f64::sin((lon1 - lon2) / 2.).powi(2),
));
Ok(DataValue::from(ret))
}
@ -1045,9 +1046,9 @@ pub(crate) fn op_haversine_deg_input(args: &[DataValue]) -> Result<DataValue> {
let lon2 = args[3].get_float().ok_or_else(miette)? * f64::PI() / 180.;
let ret = 2.
* f64::asin(f64::sqrt(
f64::sin((lat1 - lat2) / 2.).powi(2)
+ f64::cos(lat1) * f64::cos(lat2) * f64::sin((lon1 - lon2) / 2.).powi(2),
));
f64::sin((lat1 - lat2) / 2.).powi(2)
+ f64::cos(lat1) * f64::cos(lat2) * f64::sin((lon1 - lon2) / 2.).powi(2),
));
Ok(DataValue::from(ret))
}
@ -1283,13 +1284,13 @@ pub(crate) fn op_to_unity(args: &[DataValue]) -> Result<DataValue> {
DataValue::Null => 0,
DataValue::Bool(b) => *b as i64,
DataValue::Num(n) => (n.get_float() != 0.) as i64,
DataValue::Str(s) => if s.is_empty() {0} else { 1},
DataValue::Bytes(b) => if b.is_empty() {0} else { 1},
DataValue::Uuid(u) => if u.0.is_nil() {0} else { 1 },
DataValue::Regex(r) => if r.0.as_str().is_empty() {0 } else { 1},
DataValue::List(l) => if l.is_empty() {0} else {1},
DataValue::Set(s) => if s.is_empty() {0} else {1},
DataValue::Validity(vld) => if vld.is_assert {1} else {0},
DataValue::Str(s) => if s.is_empty() { 0 } else { 1 },
DataValue::Bytes(b) => if b.is_empty() { 0 } else { 1 },
DataValue::Uuid(u) => if u.0.is_nil() { 0 } else { 1 },
DataValue::Regex(r) => if r.0.as_str().is_empty() { 0 } else { 1 },
DataValue::List(l) => if l.is_empty() { 0 } else { 1 },
DataValue::Set(s) => if s.is_empty() { 0 } else { 1 },
DataValue::Validity(vld) => if vld.is_assert { 1 } else { 0 },
DataValue::Bot => 0,
}))
}
@ -1469,6 +1470,7 @@ pub(crate) fn op_now(_args: &[DataValue]) -> Result<DataValue> {
let d: f64 = Date::now() / 1000.;
Ok(DataValue::from(d))
}
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub(crate) fn op_now(_args: &[DataValue]) -> Result<DataValue> {
let now = SystemTime::now();
@ -1477,15 +1479,31 @@ pub(crate) fn op_now(_args: &[DataValue]) -> Result<DataValue> {
))
}
pub(crate) fn current_validity() -> Reverse<i64> {
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
let ts_millis = {
let now = SystemTime::now();
(now.duration_since(UNIX_EPOCH).unwrap().as_secs_f64() * 1000.) as i64
};
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
let ts_millis = {
Date::now() as i64
};
Reverse(ts_millis)
}
pub(crate) const MAX_VALIDITY: Reverse<i64> = Reverse(i64::MAX);
define_op!(OP_FORMAT_TIMESTAMP, 1, true);
pub(crate) fn op_format_timestamp(args: &[DataValue]) -> Result<DataValue> {
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
let dt = Utc
let dt = Utc
.timestamp_millis_opt(Date::now() as i64)
.latest()
.ok_or_else(|| miette!("bad time input"))?;
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
let dt = {
let dt = {
let f = args[0]
.get_float()
.ok_or_else(|| miette!("'format_timestamp' expects a number"))?;
@ -1524,19 +1542,26 @@ pub(crate) fn op_parse_timestamp(args: &[DataValue]) -> Result<DataValue> {
))
}
pub(crate) fn str2vld(s: &str) -> Result<Reverse<i64>> {
let dt = DateTime::parse_from_rfc3339(s).map_err(|_| miette!("bad datetime: {}", s))?;
let st: SystemTime = dt.into();
let millis = st.duration_since(UNIX_EPOCH).unwrap().as_secs_f64() * 1000.;
Ok(Reverse(millis as i64))
}
define_op!(OP_RAND_UUID_V1, 0, false);
pub(crate) fn op_rand_uuid_v1(_args: &[DataValue]) -> Result<DataValue> {
let mut rng = rand::thread_rng();
let uuid_ctx = uuid::v1::Context::new(rng.gen());
#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
let ts = {
let ts = {
let since_epoch: f64 = Date::now();
let seconds = since_epoch.floor();
let fractional = (since_epoch - seconds) * 1.0e9;
Timestamp::from_unix(uuid_ctx, seconds as u64, fractional as u32)
};
#[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
let ts = {
let ts = {
let now = SystemTime::now();
let since_epoch = now.duration_since(UNIX_EPOCH).unwrap();
Timestamp::from_unix(uuid_ctx, since_epoch.as_secs(), since_epoch.subsec_nanos())

@ -6,6 +6,7 @@
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
use std::cmp::Reverse;
use std::collections::btree_map::Entry;
use std::collections::{BTreeMap, BTreeSet};
use std::fmt::{Debug, Display, Formatter};
@ -324,13 +325,13 @@ pub(crate) enum FixedRuleArg {
Stored {
name: Symbol,
bindings: Vec<Symbol>,
// valid_at: Option<Expr>,
valid_at: Option<Reverse<i64>>,
span: SourceSpan,
},
NamedStored {
name: Symbol,
bindings: BTreeMap<SmartString<LazyCompact>, Symbol>,
// valid_at: Option<Expr>,
valid_at: Option<Reverse<i64>>,
span: SourceSpan,
},
}
@ -375,6 +376,7 @@ pub(crate) enum MagicFixedRuleRuleArg {
Stored {
name: Symbol,
bindings: Vec<Symbol>,
valid_at: Option<Reverse<i64>>,
span: SourceSpan,
},
}
@ -993,6 +995,7 @@ pub(crate) struct InputRuleApplyAtom {
pub(crate) struct InputNamedFieldRelationApplyAtom {
pub(crate) name: Symbol,
pub(crate) args: BTreeMap<SmartString<LazyCompact>, Expr>,
pub(crate) valid_at: Option<Reverse<i64>>,
pub(crate) span: SourceSpan,
}
@ -1000,7 +1003,7 @@ pub(crate) struct InputNamedFieldRelationApplyAtom {
pub(crate) struct InputRelationApplyAtom {
pub(crate) name: Symbol,
pub(crate) args: Vec<Expr>,
// pub(crate) valid_at: Option<Expr>,
pub(crate) valid_at: Option<Reverse<i64>>,
pub(crate) span: SourceSpan,
}
@ -1015,6 +1018,7 @@ pub(crate) struct NormalFormRuleApplyAtom {
pub(crate) struct NormalFormRelationApplyAtom {
pub(crate) name: Symbol,
pub(crate) args: Vec<Symbol>,
pub(crate) valid_at: Option<Reverse<i64>>,
pub(crate) span: SourceSpan,
}
@ -1029,6 +1033,7 @@ pub(crate) struct MagicRuleApplyAtom {
pub(crate) struct MagicRelationApplyAtom {
pub(crate) name: Symbol,
pub(crate) args: Vec<Symbol>,
pub(crate) valid_at: Option<Reverse<i64>>,
pub(crate) span: SourceSpan,
}

@ -14,10 +14,11 @@ use miette::{bail, ensure, Diagnostic, IntoDiagnostic, Result};
use pest::error::InputLocation;
use pest::Parser;
use thiserror::Error;
use crate::data::functions::current_validity;
use crate::data::program::InputProgram;
use crate::data::relation::NullableColType;
use crate::data::value::DataValue;
use crate::data::value::{DataValue};
use crate::parse::query::parse_query;
use crate::parse::schema::parse_nullable_type;
use crate::parse::sys::{parse_sys, SysOp};
@ -59,7 +60,7 @@ impl CozoScript {
}
#[derive(
Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize, Copy, Clone, Default,
Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize, Copy, Clone, Default,
)]
pub struct SourceSpan(pub(crate) usize, pub(crate) usize);
@ -118,16 +119,17 @@ pub(crate) fn parse_script(
})?
.next()
.unwrap();
let cur_vld = current_validity();
Ok(match parsed.as_rule() {
Rule::query_script => {
let q = parse_query(parsed.into_inner(), param_pool, algorithms)?;
let q = parse_query(parsed.into_inner(), param_pool, algorithms, cur_vld)?;
CozoScript::Multi(vec![q])
}
Rule::multi_script => {
let mut qs = vec![];
for pair in parsed.into_inner() {
if pair.as_rule() != Rule::EOI {
qs.push(parse_query(pair.into_inner(), param_pool, algorithms)?);
qs.push(parse_query(pair.into_inner(), param_pool, algorithms, cur_vld)?);
}
}
CozoScript::Multi(qs)

@ -6,6 +6,7 @@
* You can obtain one at https://mozilla.org/MPL/2.0/.
*/
use std::cmp::Reverse;
use std::collections::btree_map::Entry;
use std::collections::BTreeMap;
use std::error::Error;
@ -21,6 +22,7 @@ use thiserror::Error;
use crate::data::aggr::{parse_aggr, Aggregation};
use crate::data::expr::Expr;
use crate::data::functions::{MAX_VALIDITY, str2vld};
use crate::data::program::{
FixedRuleApply, FixedRuleArg, InputAtom, InputInlineRule, InputInlineRulesOrFixed,
InputNamedFieldRelationApplyAtom, InputProgram, InputRelationApplyAtom, InputRuleApplyAtom,
@ -76,7 +78,7 @@ impl Diagnostic for MultipleRuleDefinitionError {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
Some(Box::new("parser::mult_rule_def"))
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
fn labels(&self) -> Option<Box<dyn Iterator<Item=LabeledSpan> + '_>> {
Some(Box::new(
self.1.iter().map(|s| LabeledSpan::new_with_span(None, s)),
))
@ -95,6 +97,7 @@ pub(crate) fn parse_query(
src: Pairs<'_>,
param_pool: &BTreeMap<String, DataValue>,
fixedrithms: &BTreeMap<String, Arc<Box<dyn FixedRule>>>,
cur_vld: Reverse<i64>,
) -> Result<InputProgram> {
let mut progs: BTreeMap<Symbol, InputInlineRulesOrFixed> = Default::default();
let mut out_opts: QueryOutOptions = Default::default();
@ -103,7 +106,7 @@ pub(crate) fn parse_query(
for pair in src {
match pair.as_rule() {
Rule::rule => {
let (name, rule) = parse_rule(pair, param_pool)?;
let (name, rule) = parse_rule(pair, param_pool, cur_vld)?;
match progs.entry(name) {
Entry::Vacant(e) => {
@ -147,7 +150,7 @@ pub(crate) fn parse_query(
}
Rule::fixed_rule => {
let rule_span = pair.extract_span();
let (name, apply) = parse_fixed_rule(pair, param_pool, fixedrithms)?;
let (name, apply) = parse_fixed_rule(pair, param_pool, fixedrithms, cur_vld)?;
match progs.entry(name) {
Entry::Vacant(e) => {
@ -351,13 +354,13 @@ pub(crate) fn parse_query(
if prog.prog.is_empty() {
if let Some((
InputRelationHandle {
key_bindings,
dep_bindings,
..
},
RelationOp::Create,
)) = &prog.out_opts.store_relation
InputRelationHandle {
key_bindings,
dep_bindings,
..
},
RelationOp::Create,
)) = &prog.out_opts.store_relation
{
let mut bindings = key_bindings.clone();
bindings.extend_from_slice(dep_bindings);
@ -432,6 +435,7 @@ pub(crate) fn parse_query(
fn parse_rule(
src: Pair<'_>,
param_pool: &BTreeMap<String, DataValue>,
cur_vld: Reverse<i64>,
) -> Result<(Symbol, InputInlineRule)> {
let span = src.extract_span();
let mut src = src.into_inner();
@ -448,7 +452,7 @@ fn parse_rule(
let body = src.next().unwrap();
let mut body_clauses = vec![];
for atom_src in body.into_inner() {
body_clauses.push(parse_disjunction(atom_src, param_pool)?)
body_clauses.push(parse_disjunction(atom_src, param_pool, cur_vld)?)
}
Ok((
@ -465,11 +469,12 @@ fn parse_rule(
fn parse_disjunction(
pair: Pair<'_>,
param_pool: &BTreeMap<String, DataValue>,
cur_vld: Reverse<i64>,
) -> Result<InputAtom> {
let span = pair.extract_span();
let res: Vec<_> = pair
.into_inner()
.map(|v| parse_atom(v, param_pool))
.map(|v| parse_atom(v, param_pool, cur_vld))
.try_collect()?;
Ok(if res.len() == 1 {
res.into_iter().next().unwrap()
@ -478,23 +483,23 @@ fn parse_disjunction(
})
}
fn parse_atom(src: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Result<InputAtom> {
fn parse_atom(src: Pair<'_>, param_pool: &BTreeMap<String, DataValue>, cur_vld: Reverse<i64>) -> Result<InputAtom> {
Ok(match src.as_rule() {
Rule::rule_body => {
let span = src.extract_span();
let grouped: Vec<_> = src
.into_inner()
.map(|v| parse_disjunction(v, param_pool))
.map(|v| parse_disjunction(v, param_pool, cur_vld))
.try_collect()?;
InputAtom::Conjunction {
inner: grouped,
span,
}
}
Rule::disjunction => parse_disjunction(src, param_pool)?,
Rule::disjunction => parse_disjunction(src, param_pool, cur_vld)?,
Rule::negation => {
let span = src.extract_span();
let inner = parse_atom(src.into_inner().next().unwrap(), param_pool)?;
let inner = parse_atom(src.into_inner().next().unwrap(), param_pool, cur_vld)?;
InputAtom::Negation {
inner: inner.into(),
span,
@ -560,14 +565,18 @@ fn parse_atom(src: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Result
.into_inner()
.map(|v| build_expr(v, param_pool))
.try_collect()?;
// let validity = match src.next() {
// None => None,
// Some(vld_clause) => todo!()
// };
let valid_at = match src.next() {
None => None,
Some(vld_clause) => {
let vld_expr = build_expr(vld_clause.into_inner().next().unwrap(), param_pool)?;
Some(expr2vld_spec(vld_expr, cur_vld)?)
}
};
InputAtom::Relation {
inner: InputRelationApplyAtom {
name: Symbol::new(&name.as_str()[1..], name.extract_span()),
args,
valid_at,
span,
},
}
@ -595,12 +604,15 @@ fn parse_atom(src: Pair<'_>, param_pool: &BTreeMap<String, DataValue>) -> Result
Ok((name, arg))
})
.try_collect()?;
// let validity = match src.next() {
// None => None,
// Some(vld_clause) => todo!()
// };
let valid_at = match src.next() {
None => None,
Some(vld_clause) => {
let vld_expr = build_expr(vld_clause.into_inner().next().unwrap(), param_pool)?;
Some(expr2vld_spec(vld_expr, cur_vld)?)
}
};
InputAtom::NamedFieldRelation {
inner: InputNamedFieldRelationApplyAtom { name, args, span },
inner: InputNamedFieldRelationApplyAtom { name, args, span, valid_at },
}
}
rule => unreachable!("{:?}", rule),
@ -661,16 +673,22 @@ fn parse_rule_head_arg(
})
}
#[derive(Debug, Error, Diagnostic)]
#[error("bad specification of validity")]
#[diagnostic(code(parser::bad_validity_spec))]
struct BadValiditySpecification(#[label] SourceSpan);
fn parse_fixed_rule(
src: Pair<'_>,
param_pool: &BTreeMap<String, DataValue>,
fixedrithms: &BTreeMap<String, Arc<Box<dyn FixedRule>>>,
cur_vld: Reverse<i64>,
) -> Result<(Symbol, FixedRuleApply)> {
let mut src = src.into_inner();
let (out_symbol, head, aggr) = parse_rule_head(src.next().unwrap(), param_pool)?;
#[derive(Debug, Error, Diagnostic)]
#[error("fixedrithm rule cannot be combined with aggregation")]
#[error("fixed rule cannot be combined with aggregation")]
#[diagnostic(code(parser::fixed_aggr_conflict))]
struct AggrInfixedError(#[label] SourceSpan);
@ -707,12 +725,17 @@ fn parse_fixed_rule(
let mut els = inner.into_inner();
let name = els.next().unwrap();
let mut bindings = vec![];
let mut valid_at = None;
for v in els {
match v.as_rule() {
Rule::var => {
bindings.push(Symbol::new(v.as_str(), v.extract_span()))
}
Rule::validity_clause => todo!(),
Rule::validity_clause => {
let vld_inner = v.into_inner().next().unwrap();
let vld_expr = build_expr(vld_inner, param_pool)?;
valid_at = Some(expr2vld_spec(vld_expr, cur_vld)?)
}
_ => unreachable!(),
}
}
@ -722,6 +745,7 @@ fn parse_fixed_rule(
name.extract_span(),
),
bindings,
valid_at,
span,
})
}
@ -729,6 +753,7 @@ fn parse_fixed_rule(
let mut els = inner.into_inner();
let name = els.next().unwrap();
let mut bindings = BTreeMap::new();
let mut valid_at = None;
for p in els {
match p.as_rule() {
Rule::fixed_named_relation_arg_pair => {
@ -741,7 +766,11 @@ fn parse_fixed_rule(
};
bindings.insert(k, v);
}
Rule::validity_clause => todo!(),
Rule::validity_clause => {
let vld_inner = p.into_inner().next().unwrap();
let vld_expr = build_expr(vld_inner, param_pool)?;
valid_at = Some(expr2vld_spec(vld_expr, cur_vld)?)
}
_ => unreachable!(),
}
}
@ -752,6 +781,7 @@ fn parse_fixed_rule(
name.extract_span(),
),
bindings,
valid_at,
span,
})
}
@ -834,3 +864,31 @@ fn make_empty_const_rule(prog: &mut InputProgram, bindings: &[Symbol]) {
},
);
}
fn expr2vld_spec(expr: Expr, cur_vld: Reverse<i64>) -> Result<Reverse<i64>> {
let vld_span = expr.span();
match expr.eval_to_const()? {
DataValue::Num(n) => {
let f = n.get_float();
let f = f * 1000.;
if !f.is_finite() {
bail!(BadValiditySpecification(vld_span))
}
Ok(Reverse(f as i64))
}
DataValue::Str(s) => {
match &s as &str {
"now" => {
Ok(cur_vld)
}
"max" => { Ok(MAX_VALIDITY) }
s => {
Ok(str2vld(s).map_err(|_| BadValiditySpecification(vld_span))?)
}
}
}
_ => {
bail!(BadValiditySpecification(vld_span))
}
}
}

@ -12,6 +12,7 @@ use std::sync::Arc;
use itertools::Itertools;
use miette::{Diagnostic, Result};
use thiserror::Error;
use crate::data::functions::{current_validity};
use crate::data::program::InputProgram;
use crate::data::symb::Symbol;
@ -45,6 +46,7 @@ pub(crate) fn parse_sys(
param_pool: &BTreeMap<String, DataValue>,
algorithms: &BTreeMap<String, Arc<Box<dyn FixedRule>>>,
) -> Result<SysOp> {
let cur_vld = current_validity();
let inner = src.next().unwrap();
Ok(match inner.as_rule() {
Rule::compact_op => SysOp::Compact,
@ -62,6 +64,7 @@ pub(crate) fn parse_sys(
inner.into_inner().next().unwrap().into_inner(),
param_pool,
algorithms,
cur_vld,
)?;
SysOp::Explain(Box::new(prog))
}
@ -126,7 +129,7 @@ pub(crate) fn parse_sys(
let op = clause_inner.next().unwrap();
let script = clause_inner.next().unwrap();
let script_str = script.as_str();
parse_query(script.into_inner(), &Default::default(), algorithms)?;
parse_query(script.into_inner(), &Default::default(), algorithms, cur_vld)?;
match op.as_rule() {
Rule::trigger_put => puts.push(script_str.to_string()),
Rule::trigger_rm => rms.push(script_str.to_string()),

@ -99,7 +99,7 @@ impl InputAtom {
inner: Box::new(a),
span,
}
.negation_normal_form()
.negation_normal_form()
})
.try_collect()?,
span,
@ -113,7 +113,7 @@ impl InputAtom {
inner: Box::new(a),
span,
}
.negation_normal_form()
.negation_normal_form()
})
.try_collect()?,
span,
@ -135,6 +135,7 @@ impl InputAtom {
InputNamedFieldRelationApplyAtom {
name,
mut args,
valid_at,
span,
}: InputNamedFieldRelationApplyAtom,
gen: &mut TempSymbGen,
@ -171,6 +172,7 @@ impl InputAtom {
name,
args: new_args,
span,
valid_at
})
}
@ -329,12 +331,14 @@ impl InputRelationApplyAtom {
NormalFormAtom::NegatedRelation(NormalFormRelationApplyAtom {
name: self.name,
args,
valid_at: self.valid_at,
span: self.span,
})
} else {
NormalFormAtom::Relation(NormalFormRelationApplyAtom {
name: self.name,
args,
valid_at: self.valid_at,
span: self.span,
})
});

@ -332,14 +332,17 @@ impl NormalFormProgram {
name,
bindings,
span,
valid_at
} => MagicFixedRuleRuleArg::Stored {
name: name.clone(),
bindings: bindings.clone(),
valid_at: *valid_at,
span: *span,
},
FixedRuleArg::NamedStored {
name,
bindings,
valid_at,
span,
} => {
let relation = tx.get_relation(name, false)?;
@ -377,6 +380,7 @@ impl NormalFormProgram {
MagicFixedRuleRuleArg::Stored {
name: name.clone(),
bindings: new_bindings,
valid_at: *valid_at,
span: *span,
}
}
@ -457,6 +461,7 @@ impl NormalFormAtom {
let v = MagicRelationApplyAtom {
name: v.name.clone(),
args: v.args.clone(),
valid_at: v.valid_at,
span: v.span,
};
for arg in v.args.iter() {
@ -511,6 +516,7 @@ impl NormalFormAtom {
MagicAtom::NegatedRelation(MagicRelationApplyAtom {
name: nv.name.clone(),
args: nv.args.clone(),
valid_at: nv.valid_at,
span: nv.span,
})
}

Loading…
Cancel
Save