imperative scripts

main
Ziyang Hu 2 years ago
parent 8c8840957a
commit f6ac1a63d5

@ -9,6 +9,7 @@
script = _{sys_script | imperative_script | query_script}
query_script = {SOI ~ (option | rule | const_rule | fixed_rule)+ ~ EOI}
query_script_inner = {"{" ~ (option | rule | const_rule | fixed_rule)+ ~ "}"}
query_script_inner_no_bracket = { (option | rule | const_rule | fixed_rule)+ }
imperative_script = {SOI ~ imperative_stmt+ ~ EOI}
sys_script = {SOI ~ "::" ~ (compact_op | list_relations_op | list_relation_op | remove_relations_op | trigger_relation_op |
trigger_relation_show_op | rename_relations_op | running_op | kill_op | explain_op | access_level_op) ~ EOI}
@ -16,7 +17,7 @@ sys_script = {SOI ~ "::" ~ (compact_op | list_relations_op | list_relation_op |
compact_op = {"compact"}
running_op = {"running"}
kill_op = {"kill" ~ expr}
explain_op = {"explain" ~ query_script_inner}
explain_op = {"explain" ~ "{" ~ query_script_inner_no_bracket ~ "}"}
list_relations_op = {"relations"}
list_relation_op = {"columns" ~ compound_ident}
remove_relations_op = {"remove" ~ (compound_ident ~ ",")* ~ compound_ident }
@ -25,7 +26,7 @@ access_level_op = {"access_level" ~ access_level ~ (compound_ident ~ ",")* ~ com
access_level = {("normal" | "protected" | "read_only" | "hidden")}
trigger_relation_show_op = {"show_triggers" ~ compound_ident }
trigger_relation_op = {"set_triggers" ~ compound_ident ~ trigger_clause* }
trigger_clause = { "on" ~ (trigger_put | trigger_rm | trigger_replace) ~ query_script_inner }
trigger_clause = { "on" ~ (trigger_put | trigger_rm | trigger_replace) ~ "{" ~ query_script_inner_no_bracket ~ "}" }
trigger_put = {"put"}
trigger_rm = {"rm"}
trigger_replace = {"replace"}
@ -199,22 +200,24 @@ validity_type = {"Validity"}
list_type = {"[" ~ col_type ~ (";" ~ expr)? ~ "]"}
tuple_type = {"(" ~ (col_type ~ ",")* ~ col_type? ~ ")"}
imperative_stmt = {
break_stmt | continue_stmt | return_stmt |
imperative_stmt = _{
break_stmt | continue_stmt | return_stmt | debug_stmt |
query_script_inner | ignore_error_script | if_chain | while_block | do_while_block | temp_swap | remove_stmt
}
if_chain = {"%if" ~ (ident | query_script_inner)
imperative_condition = _{underscore_ident | query_script_inner}
if_chain = {"%if" ~ imperative_condition
~ "%then" ~ imperative_block
~ ("%else" ~ imperative_block)? ~ "%end" }
imperative_block = {imperative_stmt+}
break_stmt = {"%break" ~ ident?}
ignore_error_script = {"%ignore_error" ~ query_script_inner}
continue_stmt = {"%continue" ~ ident?}
return_stmt = {"%return" ~ (ident | query_script_inner)?}
while_block = {("%mark" ~ ident)? ~ "%while" ~ (ident | query_script_inner) ~ "%loop" ~ imperative_block ~ "%end"}
do_while_block = {("%mark" ~ ident)? ~ "%loop" ~ imperative_block ~ "%while" ~ (query_script_inner | ident) ~ "%end"}
return_stmt = {"%return" ~ (ident | underscore_ident | query_script_inner)?}
while_block = {("%mark" ~ ident)? ~ "%while" ~ imperative_condition ~ "%loop" ~ imperative_block ~ "%end"}
do_while_block = {("%mark" ~ ident)? ~ "%loop" ~ imperative_block ~ "%while" ~ imperative_condition ~ "%end"}
temp_swap = {"%swap" ~ underscore_ident ~ underscore_ident}
remove_stmt = {"%remove" ~ underscore_ident}
debug_stmt = {"%debug" ~ (ident | underscore_ident)}
/*

@ -8,7 +8,7 @@
*/
use crate::parse::query::parse_query;
use crate::parse::{ExtractSpan, ImperativeProgram, ImperativeStmt, Pair, Pairs, Rule, SourceSpan};
use crate::parse::{ExtractSpan, ImperativeProgram, ImperativeStmt, Pair, Rule, SourceSpan};
use crate::{DataValue, FixedRule, ValidityTs};
use either::{Left, Right};
use itertools::Itertools;
@ -18,23 +18,24 @@ use std::collections::BTreeMap;
use std::sync::Arc;
use thiserror::Error;
pub(crate) fn parse_imperative(
src: Pairs<'_>,
pub(crate) fn parse_imperative_block(
src: Pair<'_>,
param_pool: &BTreeMap<String, DataValue>,
fixed_rules: &BTreeMap<String, Arc<Box<dyn FixedRule>>>,
cur_vld: ValidityTs,
) -> Result<ImperativeProgram> {
let mut collected = vec![];
for pair in src {
if pair.as_rule() != Rule::EOI {
collected.push(parse_imperative_stmt(
pair,
param_pool,
fixed_rules,
cur_vld,
)?);
for pair in src.into_inner() {
if pair.as_rule() == Rule::EOI {
break;
}
collected.push(parse_imperative_stmt(
pair,
param_pool,
fixed_rules,
cur_vld,
)?);
}
Ok(collected)
@ -78,7 +79,7 @@ fn parse_imperative_stmt(
match pair.into_inner().next() {
None => ImperativeStmt::ReturnNil,
Some(p) => match p.as_rule() {
Rule::ident => {
Rule::ident | Rule::underscore_ident => {
let rel = SmartString::from(p.as_str());
ImperativeStmt::ReturnTemp { rel }
}
@ -95,7 +96,7 @@ fn parse_imperative_stmt(
let mut inner = pair.into_inner();
let condition = inner.next().unwrap();
let cond = match condition.as_rule() {
Rule::ident => Left(SmartString::from(condition.as_str())),
Rule::underscore_ident => Left(SmartString::from(condition.as_str())),
Rule::query_script_inner => Right(parse_query(
condition.into_inner(),
param_pool,
@ -134,7 +135,7 @@ fn parse_imperative_stmt(
nxt = inner.next().unwrap();
}
let cond = match nxt.as_rule() {
Rule::ident => Left(SmartString::from(nxt.as_str())),
Rule::underscore_ident => Left(SmartString::from(nxt.as_str())),
Rule::query_script_inner => Right(parse_query(
nxt.into_inner(),
param_pool,
@ -143,8 +144,8 @@ fn parse_imperative_stmt(
)?),
_ => unreachable!(),
};
let body = parse_imperative(
inner.next().unwrap().into_inner(),
let body = parse_imperative_block(
inner.next().unwrap(),
param_pool,
fixed_rules,
cur_vld,
@ -165,14 +166,14 @@ fn parse_imperative_stmt(
mark = Some(SmartString::from(nxt.as_str()));
nxt = inner.next().unwrap();
}
let body = parse_imperative(
inner.next().unwrap().into_inner(),
let body = parse_imperative_block(
inner.next().unwrap(),
param_pool,
fixed_rules,
cur_vld,
)?;
let cond = match nxt.as_rule() {
Rule::ident => Left(SmartString::from(nxt.as_str())),
Rule::underscore_ident => Left(SmartString::from(nxt.as_str())),
Rule::query_script_inner => Right(parse_query(
nxt.into_inner(),
param_pool,
@ -210,6 +211,15 @@ fn parse_imperative_stmt(
temp: SmartString::from(name),
}
}
Rule::debug_stmt => {
// let span = pair.extract_span();
let name_p = pair.into_inner().next().unwrap();
let name = name_p.as_str();
ImperativeStmt::TempDebug {
temp: SmartString::from(name),
}
}
Rule::query_script_inner => {
let prog = parse_query(pair.into_inner(), param_pool, fixed_rules, cur_vld)?;
ImperativeStmt::Program { prog }
@ -219,6 +229,6 @@ fn parse_imperative_stmt(
let prog = parse_query(pair.into_inner(), param_pool, fixed_rules, cur_vld)?;
ImperativeStmt::IgnoreErrorProgram { prog }
}
_ => unreachable!(),
r => unreachable!("{r:?}"),
})
}

@ -20,7 +20,7 @@ use thiserror::Error;
use crate::data::program::InputProgram;
use crate::data::relation::NullableColType;
use crate::data::value::{DataValue, ValidityTs};
use crate::parse::imperative::parse_imperative;
use crate::parse::imperative::parse_imperative_block;
use crate::parse::query::parse_query;
use crate::parse::schema::parse_nullable_type;
use crate::parse::sys::{parse_sys, SysOp};
@ -45,6 +45,7 @@ pub(crate) enum CozoScript {
Sys(SysOp),
}
#[derive(Debug)]
pub(crate) enum ImperativeStmt {
Break {
target: Option<SmartString<LazyCompact>>,
@ -60,13 +61,13 @@ pub(crate) enum ImperativeStmt {
// span: SourceSpan,
},
ReturnTemp {
rel: SmartString<LazyCompact>
rel: SmartString<LazyCompact>,
},
Program {
prog: InputProgram
prog: InputProgram,
},
IgnoreErrorProgram {
prog: InputProgram
prog: InputProgram,
},
If {
condition: ImperativeCondition,
@ -95,6 +96,9 @@ pub(crate) enum ImperativeStmt {
temp: SmartString<LazyCompact>,
// span: SourceSpan,
},
TempDebug {
temp: SmartString<LazyCompact>,
},
}
pub(crate) type ImperativeCondition = Either<SmartString<LazyCompact>, InputProgram>;
@ -130,7 +134,8 @@ impl ImperativeStmt {
ImperativeCondition::Right(prog) => prog.needs_write_tx(),
}) || body.iter().any(|p| p.needs_write_tx())
}
ImperativeStmt::ReturnTemp { .. }
ImperativeStmt::TempDebug { .. }
| ImperativeStmt::ReturnTemp { .. }
| ImperativeStmt::Break { .. }
| ImperativeStmt::Continue { .. }
| ImperativeStmt::ReturnNil { .. }
@ -222,7 +227,7 @@ pub(crate) fn parse_script(
CozoScript::Single(q)
}
Rule::imperative_script => {
let p = parse_imperative(parsed.into_inner(), param_pool, fixed_rules, cur_vld)?;
let p = parse_imperative_block(parsed, param_pool, fixed_rules, cur_vld)?;
CozoScript::Imperative(p)
}

@ -531,7 +531,7 @@ impl<'s, S: Storage<'s>> Db<S> {
if row.is_empty() {
false
} else {
op_to_bool(&row[row.len() - 2..])?
op_to_bool(&row[row.len() - 1..])?
.get_bool()
.ok_or_else(|| PredicateTypeError(span, row.last().cloned().unwrap()))?
}
@ -657,6 +657,11 @@ impl<'s, S: Storage<'s>> Db<S> {
let relation = tx.get_relation(rel, false)?;
return Ok(Right(ControlCode::Termination(relation.as_named_rows(tx)?)));
}
ImperativeStmt::TempDebug { temp, .. } => {
let relation = tx.get_relation(temp, false)?;
println!("{}: {:?}", temp, relation.as_named_rows(tx)?);
ret = NamedRows::default();
}
ImperativeStmt::Program { prog, .. } => {
ret = self.execute_single_program(prog.clone(), tx, cleanups, cur_vld)?;
}

@ -456,12 +456,18 @@ impl<'a> SessionTx<'a> {
let key = DataValue::Str(input_meta.name.name.clone());
let encoded = vec![key].encode_as_key(RelationId::SYSTEM);
if self.store_tx.exists(&encoded, true)? {
bail!(RelNameConflictError(input_meta.name.to_string()))
};
let is_temp = input_meta.name.is_temp_store_name();
if is_temp {
if self.store_tx.exists(&encoded, true)? {
bail!(RelNameConflictError(input_meta.name.to_string()))
};
} else {
if self.temp_store_tx.exists(&encoded, true)? {
bail!(RelNameConflictError(input_meta.name.to_string()))
};
}
let metadata = input_meta.metadata.clone();
let last_id = if is_temp {
self.temp_store_id.fetch_add(1, Ordering::Relaxed) as u64

@ -262,6 +262,23 @@ fn do_not_unify_underscore() {
assert_eq!(res.len(), 1);
}
#[test]
fn imperative_script() {
let db = new_cozo_mem().unwrap();
let res = db.run_script(r#"
{:create _test {a}}
%while { len[count(x)] := *_test[x]; ?[x] := len[z], x = z < 10 }
%loop
{ ?[a] := a = rand_uuid_v1(); :put _test {a} }
%debug _test
%end
%return _test
"#, Default::default()).unwrap();
assert_eq!(res.rows.len(), 10);
}
#[test]
fn returning_relations() {
let db = new_cozo_mem().unwrap();

Loading…
Cancel
Save