diff --git a/src/algebra/parser.rs b/src/algebra/parser.rs index 1341c875..4fe92694 100644 --- a/src/algebra/parser.rs +++ b/src/algebra/parser.rs @@ -1,10 +1,27 @@ use std::result; -use crate::parser::{Pair, Pairs}; +use std::sync::Arc; +use crate::data::eval::EvalError; +use crate::data::expr::Expr; +use crate::data::parser::ExprParseError; +use crate::data::value::{StaticValue, Value}; +use crate::parser::{Pair, Pairs, Rule}; #[derive(thiserror::Error, Debug)] -pub enum AlgebraParseError { +pub(crate) enum AlgebraParseError { #[error("{0} cannot be chained")] - Unchainable(String) + Unchainable(String), + + #[error("wrong argument count for {0}")] + WrongArgumentCount(String), + + #[error("wrong argument type for {0}: {0}")] + WrongArgumentType(String, String), + + #[error(transparent)] + ExprParse(#[from] ExprParseError), + + #[error(transparent)] + EvalError(#[from] EvalError), } type Result = result::Result; @@ -13,62 +30,80 @@ trait RelationalAlgebra { fn name(&self) -> &str; } -const NAME_VALUES: &str = "Values"; +const NAME_RA_FROM_VALUES: &str = "Values"; -struct RaFromValues; +#[derive(Clone, Debug)] +struct RaFromValues { + values: StaticValue +} impl RaFromValues { - fn build(prev: Option<()>, args: Pairs) -> Result { - if prev!= None { - return Err(AlgebraParseError::Unchainable(NAME_VALUES.to_string())) + fn build(prev: Option>, mut args: Pairs) -> Result { + if !matches!(prev, None) { + return Err(AlgebraParseError::Unchainable(NAME_RA_FROM_VALUES.to_string())); + } + let data = args.next().unwrap().into_inner().next().unwrap(); + if data.as_rule() != Rule::expr { + return Err(AlgebraParseError::WrongArgumentType( + NAME_RA_FROM_VALUES.to_string(), + format!("{:?}", data.as_rule()))); } - dbg!(args); - todo!() + if args.next() != None { + return Err(AlgebraParseError::WrongArgumentCount(NAME_RA_FROM_VALUES.to_string())); + } + let data = Expr::try_from(data)?; + let data = data.row_eval(&())?.to_static(); + + Ok(Self { + values: data + }) } } impl RelationalAlgebra for RaFromValues { fn name(&self) -> &str { - NAME_VALUES + NAME_RA_FROM_VALUES } } const NAME_INSERT: &str = "Insert"; -fn build_ra_expr(pair: Pair) { - let built: Option<()> = None; +fn build_ra_expr(pair: Pair) -> Result> { + let mut built: Option> = None; for pair in pair.into_inner() { let mut pairs = pair.into_inner(); match pairs.next().unwrap().as_str() { NAME_INSERT => todo!(), - NAME_VALUES => { - let _res = RaFromValues::build(built, pairs); - }, + NAME_RA_FROM_VALUES => { + built = Some(Arc::new(RaFromValues::build(built, pairs)?)); + } _ => unimplemented!() } } - todo!() + Ok(built.unwrap()) } #[cfg(test)] mod tests { use crate::parser::{CozoParser, Rule}; use pest::Parser; - use crate::algebra::parser::build_ra_expr; + use super::*; #[test] - fn parse_ra() { + fn parse_ra() -> Result<()> { let s = r#" Values([{x: 1}]) - .Insert(f:Friend) + //.Insert(f:Friend) "#; - build_ra_expr(CozoParser::parse(Rule::ra_expr_all, s).unwrap().into_iter().next().unwrap()); - let s = r#" - From(f:Person-[:HasJob]->j:Job, - f.id == 101, j.id > 10) - .Select(f: {*id: f.id}) - "#; - build_ra_expr(CozoParser::parse(Rule::ra_expr_all, s).unwrap().into_iter().next().unwrap()); + build_ra_expr(CozoParser::parse(Rule::ra_expr_all, s).unwrap().into_iter().next().unwrap())?; + + // let s = r#" + // From(f:Person-[:HasJob]->j:Job, + // f.id == 101, j.id > 10) + // .Select(f: {*id: f.id}) + // "#; + // build_ra_expr(CozoParser::parse(Rule::ra_expr_all, s).unwrap().into_iter().next().unwrap()); + Ok(()) } } \ No newline at end of file diff --git a/src/data/eval.rs b/src/data/eval.rs index 860b65e7..50799197 100644 --- a/src/data/eval.rs +++ b/src/data/eval.rs @@ -52,12 +52,12 @@ impl RowEvalContext for () { } } -pub(crate) trait ExprEvalContext { +pub(crate) trait PartialEvalContext { fn resolve<'a>(&'a self, key: &str) -> Option>; fn resolve_table_col<'a>(&'a self, binding: &str, col: &str) -> Option<(TableId, ColId)>; } -impl ExprEvalContext for () { +impl PartialEvalContext for () { fn resolve<'a>(&'a self, _key: &str) -> Option> { None } @@ -79,14 +79,14 @@ fn extract_optimized_u_args(args: Vec) -> Expr { } impl<'a> Expr<'a> { - pub(crate) fn interpret_eval(self, ctx: &'a C) -> Result { + pub(crate) fn interpret_eval(self, ctx: &'a C) -> Result { match self.partial_eval(ctx)? { Expr::Const(v) => Ok(v), v => Err(EvalError::IncompleteEvaluation(format!("{:?}", v))), } } - pub(crate) fn partial_eval(self, ctx: &'a C) -> Result { + pub(crate) fn partial_eval(self, ctx: &'a C) -> Result { let res = match self { v @ (Expr::Const(_) | Expr::TableCol(_, _) | Expr::TupleSetIdx(_)) => v, Expr::List(l) => Expr::List( diff --git a/src/data/expr.rs b/src/data/expr.rs index e5ad0dba..b8caa571 100644 --- a/src/data/expr.rs +++ b/src/data/expr.rs @@ -60,6 +60,102 @@ pub(crate) enum Expr<'a> { And(Box<(Expr<'a>, Expr<'a>)>), } +impl<'a> Expr<'a> { + pub(crate) fn to_static(self) -> StaticExpr { + match self { + Expr::Const(v) => Expr::Const(v.to_static()), + Expr::List(l) => Expr::List(l.into_iter().map(|v| v.to_static()).collect()), + Expr::Dict(d) => Expr::Dict(d.into_iter().map(|(k, v)| (k, v.to_static())).collect()), + Expr::Variable(v) => Expr::Variable(v), + Expr::TableCol(tid, cid) => Expr::TableCol(tid, cid), + Expr::TupleSetIdx(idx) => Expr::TupleSetIdx(idx), + Expr::Apply(op, args) => Expr::Apply(op, + args.into_iter().map(|v| v.to_static()).collect()), + Expr::ApplyAgg(op, a_args, args) => Expr::ApplyAgg( + op, + a_args.into_iter().map(|v| v.to_static()).collect(), + args.into_iter().map(|v| v.to_static()).collect(), + ), + Expr::FieldAcc(f, arg) => Expr::FieldAcc(f, arg.to_static().into()), + Expr::IdxAcc(i, arg) => Expr::IdxAcc(i, arg.to_static().into()), + Expr::IfExpr(args) => { + let (a, b, c) = *args; + Expr::IfExpr((a.to_static(), b.to_static(), c.to_static()).into()) + } + Expr::SwitchExpr(args) => Expr::SwitchExpr(args.into_iter().map(|(a, b)| + (a.to_static(), b.to_static())).collect()), + Expr::Add(args) => { + let (a, b) = *args; + Expr::Add((a.to_static(), b.to_static()).into()) + } + Expr::Sub(args) => { + let (a, b) = *args; + Expr::Sub((a.to_static(), b.to_static()).into()) + } + Expr::Mul(args) => { + let (a, b) = *args; + Expr::Mul((a.to_static(), b.to_static()).into()) + } + Expr::Div(args) => { + let (a, b) = *args; + Expr::Div((a.to_static(), b.to_static()).into()) + } + Expr::Pow(args) => { + let (a, b) = *args; + Expr::Pow((a.to_static(), b.to_static()).into()) + } + Expr::Mod(args) => { + let (a, b) = *args; + Expr::Mod((a.to_static(), b.to_static()).into()) + } + Expr::StrCat(args) => { + let (a, b) = *args; + Expr::StrCat((a.to_static(), b.to_static()).into()) + } + Expr::Eq(args) => { + let (a, b) = *args; + Expr::Eq((a.to_static(), b.to_static()).into()) + } + Expr::Ne(args) => { + let (a, b) = *args; + Expr::Ne((a.to_static(), b.to_static()).into()) + } + Expr::Gt(args) => { + let (a, b) = *args; + Expr::Gt((a.to_static(), b.to_static()).into()) + } + Expr::Ge(args) => { + let (a, b) = *args; + Expr::Ge((a.to_static(), b.to_static()).into()) + } + Expr::Lt(args) => { + let (a, b) = *args; + Expr::Lt((a.to_static(), b.to_static()).into()) + } + Expr::Le(args) => { + let (a, b) = *args; + Expr::Le((a.to_static(), b.to_static()).into()) + } + Expr::Not(arg) => Expr::Not(arg.to_static().into()), + Expr::Minus(arg) => Expr::Minus(arg.to_static().into()), + Expr::IsNull(arg) => Expr::IsNull(arg.to_static().into()), + Expr::NotNull(arg) => Expr::NotNull(arg.to_static().into()), + Expr::Coalesce(args) => { + let (a, b) = *args; + Expr::Coalesce((a.to_static(), b.to_static()).into()) + } + Expr::Or(args) => { + let (a, b) = *args; + Expr::Or((a.to_static(), b.to_static()).into()) + } + Expr::And(args) => { + let (a, b) = *args; + Expr::And((a.to_static(), b.to_static()).into()) + } + } + } +} + impl<'a> PartialEq for Expr<'a> { fn eq(&self, other: &Self) -> bool { use Expr::*; @@ -317,7 +413,7 @@ fn build_value_from_binop<'a>(name: &str, (left, right): (Expr<'a>, Expr<'a>)) - Value::from(name.to_string()), Value::from(vec![Value::from(left), Value::from(right)]), ] - .into(), + .into(), ) } @@ -328,7 +424,7 @@ fn build_value_from_uop<'a>(name: &str, arg: Expr<'a>) -> Value<'a> { Value::from(name.to_string()), Value::from(vec![Value::from(arg)]), ] - .into(), + .into(), ) } @@ -356,7 +452,7 @@ impl<'a> From> for Value<'a> { cid.is_key.into(), Value::from(cid.id as i64), ] - .into(), + .into(), ), Expr::TupleSetIdx(sid) => build_tagged_value( "TupleSetIdx", @@ -365,7 +461,7 @@ impl<'a> From> for Value<'a> { Value::from(sid.t_set as i64), Value::from(sid.col_idx as i64), ] - .into(), + .into(), ), Expr::Add(arg) => build_value_from_binop(OpAdd.name(), *arg), Expr::Sub(arg) => build_value_from_binop(OpSub.name(), *arg), @@ -393,7 +489,7 @@ impl<'a> From> for Value<'a> { Value::from(op.name().to_string()), args.into_iter().map(Value::from).collect::>().into(), ] - .into(), + .into(), ), Expr::IfExpr(_) => { todo!() @@ -412,7 +508,7 @@ impl<'a> From> for Value<'a> { .into(), args.into_iter().map(Value::from).collect::>().into(), ] - .into(), + .into(), ), Expr::FieldAcc(f, v) => { build_tagged_value("FieldAcc", vec![f.into(), Value::from(*v)].into()) diff --git a/src/data/op.rs b/src/data/op.rs index 205660f7..60283b8b 100644 --- a/src/data/op.rs +++ b/src/data/op.rs @@ -4,6 +4,8 @@ mod combine; mod comparison; mod control; mod text; +mod uuid; +mod sequence; use crate::data::eval::EvalError; use crate::data::value::{Value}; diff --git a/src/data/op/boolean.rs b/src/data/op/boolean.rs index 7143a728..7fe30fd0 100644 --- a/src/data/op/boolean.rs +++ b/src/data/op/boolean.rs @@ -1,4 +1,4 @@ -use crate::data::eval::{EvalError, ExprEvalContext, RowEvalContext}; +use crate::data::eval::{EvalError, PartialEvalContext, RowEvalContext}; use crate::data::expr::Expr; use crate::data::op::Op; use crate::data::value::{Value}; @@ -106,7 +106,7 @@ impl Op for OpOr { } } -pub(crate) fn partial_eval_or<'a, T: ExprEvalContext + 'a>( +pub(crate) fn partial_eval_or<'a, T: PartialEvalContext + 'a>( ctx: &'a T, args: Vec>, ) -> Result> { @@ -210,7 +210,7 @@ impl Op for OpAnd { } } -pub(crate) fn partial_eval_and<'a, T: ExprEvalContext + 'a>( +pub(crate) fn partial_eval_and<'a, T: PartialEvalContext + 'a>( ctx: &'a T, args: Vec>, ) -> Result> { diff --git a/src/data/op/control.rs b/src/data/op/control.rs index 7ffc8a88..21d57d99 100644 --- a/src/data/op/control.rs +++ b/src/data/op/control.rs @@ -1,4 +1,4 @@ -use crate::data::eval::{EvalError, ExprEvalContext, RowEvalContext}; +use crate::data::eval::{EvalError, PartialEvalContext, RowEvalContext}; use crate::data::expr::Expr; use crate::data::op::Op; use crate::data::value::Value; @@ -52,7 +52,7 @@ pub(crate) fn row_eval_coalesce<'a, T: RowEvalContext + 'a>( pub(crate) const IF_NAME: &str = "if"; -pub(crate) fn partial_eval_coalesce<'a, T: ExprEvalContext + 'a>( +pub(crate) fn partial_eval_coalesce<'a, T: PartialEvalContext + 'a>( ctx: &'a T, args: Vec>, ) -> Result> { @@ -94,7 +94,7 @@ pub(crate) fn row_eval_if_expr<'a, T: RowEvalContext + 'a>( } } -pub(crate) fn partial_eval_if_expr<'a, T: ExprEvalContext + 'a>( +pub(crate) fn partial_eval_if_expr<'a, T: PartialEvalContext + 'a>( ctx: &'a T, cond: Expr<'a>, if_part: Expr<'a>, @@ -135,7 +135,7 @@ pub(crate) fn row_eval_switch_expr<'a, T: RowEvalContext + 'a>( default.row_eval(ctx) } -pub(crate) fn partial_eval_switch_expr<'a, T: ExprEvalContext + 'a>( +pub(crate) fn partial_eval_switch_expr<'a, T: PartialEvalContext + 'a>( ctx: &'a T, args: Vec<(Expr<'a>, Expr<'a>)>, ) -> Result> { diff --git a/src/data/op/sequence.rs b/src/data/op/sequence.rs new file mode 100644 index 00000000..377c7b07 --- /dev/null +++ b/src/data/op/sequence.rs @@ -0,0 +1,28 @@ +use crate::data::op::Op; +use crate::data::value::Value; + +pub(crate) struct SeqNext; + +const NAME_OP_SEQ_NEXT: &str = "seq_next"; + +impl Op for SeqNext { + fn arity(&self) -> Option { + Some(1) + } + + fn has_side_effect(&self) -> bool { + true + } + + fn name(&self) -> &str { + NAME_OP_SEQ_NEXT + } + + fn non_null_args(&self) -> bool { + true + } + + fn eval<'a>(&self, args: Vec>) -> crate::data::op::Result> { + todo!() + } +} \ No newline at end of file diff --git a/src/data/op/uuid.rs b/src/data/op/uuid.rs new file mode 100644 index 00000000..09e24f2f --- /dev/null +++ b/src/data/op/uuid.rs @@ -0,0 +1,28 @@ +use crate::data::op::Op; +use crate::data::value::Value; + +pub(crate) struct OpGenUuidV1; + +const NAME_OP_GEN_UUID_V1:&str = "gen_uuid_v1"; + +impl Op for OpGenUuidV1 { + fn arity(&self) -> Option { + Some(0) + } + + fn has_side_effect(&self) -> bool { + true + } + + fn name(&self) -> &str { + NAME_OP_GEN_UUID_V1 + } + + fn non_null_args(&self) -> bool { + true + } + + fn eval<'a>(&self, args: Vec>) -> crate::data::op::Result> { + todo!() + } +} \ No newline at end of file diff --git a/src/ddl.rs b/src/ddl.rs new file mode 100644 index 00000000..a5f93427 --- /dev/null +++ b/src/ddl.rs @@ -0,0 +1 @@ +pub(crate) mod parser; diff --git a/src/ddl/parser.rs b/src/ddl/parser.rs new file mode 100644 index 00000000..cf18a1ae --- /dev/null +++ b/src/ddl/parser.rs @@ -0,0 +1,189 @@ +use std::result; +use crate::data::expr::{Expr, StaticExpr}; +use crate::data::parser::ExprParseError; +use crate::data::typing::{Typing, TypingError}; +use crate::data::value::Value; +use crate::parser::{Pair, Rule}; +use crate::parser::text_identifier::{build_name_in_def, TextParseError}; + +#[derive(thiserror::Error, Debug)] +pub(crate) enum DdlParseError { + #[error(transparent)] + TextParse(#[from] TextParseError), + + #[error(transparent)] + Typing(#[from] TypingError), + + #[error(transparent)] + ExprParse(#[from] ExprParseError) +} + +type Result = result::Result; + +#[derive(Debug, Clone)] +pub(crate) struct ColSchema { + name: String, + typing: Typing, + default: StaticExpr, +} + +#[derive(Debug, Clone)] +pub(crate) struct NodeSchema { + name: String, + keys: Vec, + vals: Vec, +} + +#[derive(Debug, Clone)] +pub(crate) struct EdgeSchema { + name: String, + src_name: String, + dst_name: String, + keys: Vec, + vals: Vec, +} + +#[derive(Debug, Clone)] +pub(crate) struct AssocSchema { + name: String, + src_name: String, + vals: Vec, +} + +#[derive(Debug, Clone)] +pub(crate) enum IndexCol { + Simple(String), + Computed(StaticExpr), +} + +#[derive(Debug, Clone)] +pub(crate) struct IndexSchema { + name: String, + src_name: String, + index: Vec, +} + +#[derive(Debug, Clone)] +pub(crate) struct SequenceSchema { + name: String, +} + +#[derive(Debug, Clone)] +pub(crate) enum DdlSchema { + Node(NodeSchema), + Edge(EdgeSchema), + Assoc(AssocSchema), + Index(IndexSchema), + Sequence(SequenceSchema) +} + +impl<'a> TryFrom> for DdlSchema { + type Error = DdlParseError; + + fn try_from(pair: Pair<'a>) -> result::Result { + Ok(match pair.as_rule() { + Rule::node_def => DdlSchema::Node(pair.try_into()?), + _ => todo!() + }) + } +} + +impl<'a> TryFrom> for NodeSchema { + type Error = DdlParseError; + + fn try_from(pair: Pair) -> Result { + let mut pairs = pair.into_inner(); + let name = build_name_in_def(pairs.next().unwrap(), true)?; + let col_pair = pairs.next().unwrap(); + let (keys, vals) = parse_cols(col_pair)?; + Ok(Self { + name, + keys, + vals, + }) + } +} + +impl<'a> TryFrom> for EdgeSchema { + type Error = DdlParseError; + fn try_from(value: Pair) -> Result { + todo!() + } +} + +impl<'a> TryFrom> for AssocSchema { + type Error = DdlParseError; + fn try_from(value: Pair) -> Result { + todo!() + } +} + +impl<'a> TryFrom> for IndexSchema { + type Error = DdlParseError; + fn try_from(value: Pair) -> Result { + todo!() + } +} + +fn parse_cols(pair: Pair) -> Result<(Vec, Vec)> { + let mut keys = vec![]; + let mut vals = vec![]; + for pair in pair.into_inner() { + match parse_col_entry(pair)? { + (true, res) => keys.push(res), + (false, res) => vals.push(res) + } + } + Ok((keys, vals)) +} + +fn parse_col_entry(pair: Pair) -> Result<(bool, ColSchema)> { + let mut pairs = pair.into_inner(); + let (is_key, name) = parse_col_name(pairs.next().unwrap())?; + let typing = Typing::try_from(pairs.next().unwrap())?; + let default = match pairs.next() { + None => Expr::Const(Value::Null), + Some(pair) => Expr::try_from(pair)?.to_static(), + }; + Ok((is_key, ColSchema { + name, + typing, + default + })) +} + +fn parse_col_name(pair: Pair) -> Result<(bool, String)> { + let mut pairs = pair.into_inner(); + let mut nxt = pairs.next().unwrap(); + let is_key = match nxt.as_rule() { + Rule::key_marker => { + nxt = pairs.next().unwrap(); + true + }, + _ => false + }; + let name = build_name_in_def(nxt, true)?; + Ok((is_key, name)) +} + +#[cfg(test)] +mod tests { + use crate::parser::CozoParser; + use pest::Parser; + use super::*; + + #[test] + fn parse_ddl() -> Result<()> { + let s = r#" + node Job { + *id: Int, + title: Text, + min_salary: Float = 0, + max_salary: Float + } + "#; + let p = CozoParser::parse(Rule::definition_all, s).unwrap().next().unwrap(); + dbg!(DdlSchema::try_from(p)?); + Ok(()) + } +} \ No newline at end of file diff --git a/src/grammar.pest b/src/grammar.pest index 7204feea..fcd49bc0 100644 --- a/src/grammar.pest +++ b/src/grammar.pest @@ -175,10 +175,12 @@ associate_def = { "assoc" ~ name_in_def ~ ":" ~ name_in_def ~ cols_def ~ ";"? } edge_def = { "edge" ~ "(" ~ name_in_def ~ ")" ~ "-" ~ "[" ~ name_in_def ~ "]" ~ "->" ~ "(" ~ name_in_def ~ ")" ~ cols_def? ~ ";"? } -index_def = { "index" ~ (name_in_def ~ ":")? ~ name_in_def ~ col_list ~ ";"? } -type_def = { "type" ~ name_in_def ~ "=" ~ typing ~ ";"? } +index_def = { "index" ~ (name_in_def ~ ":")? ~ name_in_def ~ ("+" ~ name_in_def)* ~ "[" ~ (expr ~ ",")* ~ expr? ~ "]" ~ ";"? } +seq_def = { "seq" ~ (name_in_def) ~ ";" } +type_def = { "type" ~ name_in_def ~ "=" ~ typing ~ ";" } -definition = _{ node_def | associate_def | edge_def | type_def | index_def } +definition = _{ node_def | associate_def | edge_def | index_def | seq_def } +definition_all = _{SOI ~ definition ~ EOI} global_def = { "create" ~ definition } local_def = { "local" ~ definition } statement = _{ global_def | local_def } diff --git a/src/lib.rs b/src/lib.rs index 3cd2ae4e..8fa39622 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,5 +3,6 @@ pub(crate) mod logger; pub(crate) mod parser; pub(crate) mod runtime; pub(crate) mod algebra; +pub(crate) mod ddl; pub use runtime::instance::DbInstance;