Add support for parameterization

next
Sayan Nandan 2 years ago
parent 3257798ee0
commit ce8bba18cd
No known key found for this signature in database
GPG Key ID: 8BC07A0A4D41DD52

@ -26,7 +26,7 @@
// TODO(@ohsayan): Change the underlying structures, there are just rudimentary ones used during integration with the QL
use super::ql::lexer::Lit;
use super::ql::lexer::{Lit, LitIR};
/// A [`DataType`] represents the underlying data-type, although this enumeration when used in a collection will always
/// be of one type.
@ -50,6 +50,8 @@ pub enum DataType {
SignedInt(i64),
/// A boolean
Boolean(bool),
/// A float (64-bit)
Float(f64),
/// A single-type list. Note, you **need** to keep up the invariant that the [`DataType`] disc. remains the same for all
/// elements to ensure correctness in this specific context
/// FIXME(@ohsayan): Try enforcing this somehow
@ -81,6 +83,16 @@ impl DataType {
Lit::Bin(l) => DataType::Binary(l.as_slice().to_owned()),
}
}
pub(super) fn clone_from_litir<'a>(lit: LitIR<'a>) -> Self {
match lit {
LitIR::Str(s) => Self::String(s.to_owned().into_boxed_str()),
LitIR::Bin(b) => Self::Binary(b.to_owned()),
LitIR::Float(f) => Self::Float(f),
LitIR::SInt(s) => Self::SignedInt(s),
LitIR::UInt(u) => Self::UnsignedInt(u),
LitIR::Bool(b) => Self::Boolean(b),
}
}
}
impl<const N: usize> From<[DataType; N]> for DataType {

@ -34,57 +34,50 @@ use {
core::{marker::PhantomData, slice},
};
/// A [`QueryInterface`] defines how a query is to be processed and how it is laid out
pub trait QueryInterface {
/// Escaped data to be supplemented
type EscData<'a>;
/// Check if atleast one element of supplementary data is available
fn has_esc_data<'a>(d: Self::EscData<'a>) -> bool;
/// Attempt to read a lit instance from an appropriate source and return the available
/// lit using default substitution, i.e `0x3F`
fn read_append_lit_def_sub<'a>(
tok: &'a [Token],
esdt: Self::EscData<'a>,
v: &mut Vec<LitIR<'a>>,
) -> bool;
pub trait QueryData<'a> {
/// Check if the given token is a lit, while also checking `self`'s data if necessary
fn can_read_lit_from(&self, tok: &Token) -> bool;
/// Read a lit using the given token, using `self`'s data as necessary
///
/// ## Safety
/// The current token **must match** the signature of a lit
unsafe fn read_lit(&mut self, tok: &'a Token) -> LitIR<'a>;
}
pub struct InplaceQueryInterface;
pub struct InplaceData;
impl InplaceData {
pub const fn new() -> Self {
Self
}
}
impl QueryInterface for InplaceQueryInterface {
type EscData<'a> = ();
#[inline(always)]
fn has_esc_data<'a>(_: ()) -> bool {
true
impl<'a> QueryData<'a> for InplaceData {
fn can_read_lit_from(&self, tok: &Token) -> bool {
tok.is_lit()
}
#[inline(always)]
fn read_append_lit_def_sub<'a>(tok: &'a [Token], _: (), v: &mut Vec<LitIR<'a>>) -> bool {
match tok[0] {
Token::Lit(ref l) => {
v.push(unsafe { l.as_ir() });
true
}
_ => false,
}
unsafe fn read_lit(&mut self, tok: &'a Token) -> LitIR<'a> {
extract!(tok, Token::Lit(l) => l.as_ir())
}
}
pub struct EscQueryInterface;
impl QueryInterface for EscQueryInterface {
type EscData<'a> = &'a [LitIR<'a>];
#[inline(always)]
fn has_esc_data<'a>(d: Self::EscData<'a>) -> bool {
d.len() != 0
pub struct SubstitutedData<'a> {
data: &'a [LitIR<'a>],
}
impl<'a> SubstitutedData<'a> {
pub const fn new(src: &'a [LitIR<'a>]) -> Self {
Self { data: src }
}
#[inline(always)]
fn read_append_lit_def_sub<'a>(
tok: &'a [Token],
d: Self::EscData<'a>,
v: &mut Vec<LitIR<'a>>,
) -> bool {
v.push(d[0]);
tok[0] == Token![?]
}
impl<'a> QueryData<'a> for SubstitutedData<'a> {
fn can_read_lit_from(&self, tok: &Token) -> bool {
Token![?].eq(tok) && !self.data.is_empty()
}
unsafe fn read_lit(&mut self, tok: &'a Token) -> LitIR<'a> {
debug_assert!(Token![?].eq(tok));
let ret = self.data[0];
self.data = &self.data[1..];
ret
}
}

@ -29,9 +29,11 @@
should augment in future revisions of the QL engine
*/
#[cfg(test)]
use super::ast::InplaceData;
use {
super::{
ast::Entity,
ast::{Entity, QueryData},
lexer::{LitIR, Symbol, Token},
LangError, LangResult, RawSlice,
},
@ -46,6 +48,11 @@ use {
},
};
#[inline(always)]
fn minidx<T>(src: &[T], index: usize) -> usize {
cmp::min(src.len() - 1, index)
}
/*
Misc
*/
@ -117,7 +124,7 @@ impl<'a> RelationalExpr<'a> {
opc
}
#[inline(always)]
fn try_parse(tok: &'a [Token], cnt: &mut usize) -> Option<Self> {
fn try_parse<Qd: QueryData<'a>>(tok: &'a [Token], d: &mut Qd, cnt: &mut usize) -> Option<Self> {
/*
Minimum length of an expression:
[lhs] [operator] [rhs]
@ -132,18 +139,19 @@ impl<'a> RelationalExpr<'a> {
// let's get ourselves the operator
let operator = Self::parse_operator(&tok[i..], &mut i, &mut okay);
okay &= i < tok.len();
let lit_idx = cmp::min(i, tok.len() - 1);
okay &= tok[lit_idx].is_lit(); // LOL, I really like saving cycles
let lit_idx = minidx(tok, i);
okay &= Qd::can_read_lit_from(d, &tok[lit_idx]); // LOL, I really like saving cycles
*cnt += i + okay as usize;
if compiler::likely(okay) {
Some(unsafe {
unsafe {
// UNSAFE(@ohsayan): tok[0] is checked for being an ident, tok[lit_idx] also checked to be a lit
Self::new(
let lit = Qd::read_lit(d, &tok[lit_idx]);
Some(Self::new(
extract!(tok[0], Token::Ident(ref id) => id.as_slice()),
extract!(tok[lit_idx], Token::Lit(ref l) => l.as_ir()),
lit,
operator,
)
})
))
}
} else {
compiler::cold_val(None)
}
@ -168,8 +176,9 @@ impl<'a> WhereClause<'a> {
/// Notes:
/// - Deny duplicate clauses
/// - No enforcement on minimum number of clauses
fn parse_where_and_append_to(
fn parse_where_and_append_to<Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
cnt: &mut usize,
c: &mut WhereClauseCollection<'a>,
) -> bool {
@ -178,10 +187,10 @@ impl<'a> WhereClause<'a> {
let mut i = 0;
let mut has_more = true;
while okay && i < l && has_more {
okay &= RelationalExpr::try_parse(&tok[i..], &mut i)
okay &= RelationalExpr::try_parse(&tok[i..], d, &mut i)
.map(|clause| c.insert(clause.lhs, clause).is_none())
.unwrap_or(false);
has_more = tok[cmp::min(i, l - 1)] == Token![and] && i < l;
has_more = tok[minidx(tok, i)] == Token![and] && i < l;
i += has_more as usize;
}
*cnt += i;
@ -192,9 +201,14 @@ impl<'a> WhereClause<'a> {
///
/// Notes:
/// - Enforce a minimum of 1 clause
pub(super) fn parse_where(tok: &'a [Token], flag: &mut bool, cnt: &mut usize) -> Self {
pub(super) fn parse_where<Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
flag: &mut bool,
cnt: &mut usize,
) -> Self {
let mut c = HashMap::with_capacity(2);
*flag &= Self::parse_where_and_append_to(tok, cnt, &mut c);
*flag &= Self::parse_where_and_append_to(tok, d, cnt, &mut c);
*flag &= !c.is_empty();
Self { c }
}
@ -204,7 +218,7 @@ impl<'a> WhereClause<'a> {
pub(super) fn parse_where_clause_full<'a>(tok: &'a [Token]) -> Option<WhereClause<'a>> {
let mut flag = true;
let mut i = 0;
let ret = WhereClause::parse_where(tok, &mut flag, &mut i);
let ret = WhereClause::parse_where(tok, &mut InplaceData::new(), &mut flag, &mut i);
assert_full_tt!(tok.len(), i);
flag.then_some(ret)
}
@ -213,7 +227,7 @@ pub(super) fn parse_where_clause_full<'a>(tok: &'a [Token]) -> Option<WhereClaus
#[inline(always)]
pub(super) fn parse_relexpr_full<'a>(tok: &'a [Token]) -> Option<RelationalExpr<'a>> {
let mut i = 0;
let okay = RelationalExpr::try_parse(tok, &mut i);
let okay = RelationalExpr::try_parse(tok, &mut InplaceData::new(), &mut i);
assert_full_tt!(tok.len(), i);
okay
}
@ -225,8 +239,9 @@ pub(super) fn parse_relexpr_full<'a>(tok: &'a [Token]) -> Option<RelationalExpr<
/// Parse a list
///
/// **NOTE:** This function will error if the `[` token is passed. Make sure this is forwarded by the caller
pub(super) fn parse_list(
tok: &[Token],
pub(super) fn parse_list<'a, Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
list: &mut Vec<DataType>,
) -> (Option<Discriminant<DataType>>, usize, bool) {
let l = tok.len();
@ -237,22 +252,23 @@ pub(super) fn parse_list(
let mut prev_nlist_dscr = None;
while i < l && okay && !stop {
let d = match &tok[i] {
Token::Lit(l) => unsafe {
// UNSAFE(@ohsayan): Token LT0 guarantees LT0 > LT1 for lit
DataType::clone_from_lit(l)
},
tok if Qd::can_read_lit_from(d, tok) => {
unsafe {
// UNSAFE(@ohsayan): Token LT0 guarantees LT0 > LT1 for lit
DataType::clone_from_litir(Qd::read_lit(d, tok))
}
}
Token::Symbol(Symbol::TtOpenSqBracket) => {
// a nested list
let mut nested_list = Vec::new();
let (nlist_dscr, nlist_i, nlist_okay) = parse_list(&tok[i + 1..], &mut nested_list);
let (nlist_dscr, nlist_i, nlist_okay) =
parse_list(&tok[i + 1..], d, &mut nested_list);
okay &= nlist_okay;
i += nlist_i;
// check type return
okay &= {
prev_nlist_dscr.is_none()
|| nlist_dscr.is_none()
|| prev_nlist_dscr == nlist_dscr
};
okay &= prev_nlist_dscr.is_none()
|| nlist_dscr.is_none()
|| prev_nlist_dscr == nlist_dscr;
if prev_nlist_dscr.is_none() && nlist_dscr.is_some() {
prev_nlist_dscr = nlist_dscr;
}
@ -279,7 +295,7 @@ pub(super) fn parse_list(
#[cfg(test)]
pub(super) fn parse_list_full(tok: &[Token]) -> Option<Vec<DataType>> {
let mut l = Vec::new();
if matches!(parse_list(tok, &mut l), (_, i, true) if i == tok.len()) {
if matches!(parse_list(tok, &mut InplaceData::new(), &mut l), (_, i, true) if i == tok.len()) {
Some(l)
} else {
None
@ -289,7 +305,10 @@ pub(super) fn parse_list_full(tok: &[Token]) -> Option<Vec<DataType>> {
/// Parse the tuple data passed in with an insert query.
///
/// **Note:** Make sure you pass the `(` token
pub(super) fn parse_data_tuple_syntax(tok: &[Token]) -> (Vec<Option<DataType>>, usize, bool) {
pub(super) fn parse_data_tuple_syntax<'a, Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
) -> (Vec<Option<DataType>>, usize, bool) {
let l = tok.len();
let mut okay = l != 0;
let mut stop = okay && tok[0] == Token::Symbol(Symbol::TtCloseParen);
@ -297,14 +316,16 @@ pub(super) fn parse_data_tuple_syntax(tok: &[Token]) -> (Vec<Option<DataType>>,
let mut data = Vec::new();
while i < l && okay && !stop {
match &tok[i] {
Token::Lit(l) => data.push(Some(unsafe {
// UNSAFE(@ohsayan): Token LT0 guarantees LT0 > LT1 for lit
DataType::clone_from_lit(l)
})),
tok if Qd::can_read_lit_from(d, tok) => {
unsafe {
// UNSAFE(@ohsayan): Token LT0 guarantees LT0 > LT1 for lit
data.push(Some(DataType::clone_from_litir(Qd::read_lit(d, tok))));
}
}
Token::Symbol(Symbol::TtOpenSqBracket) => {
// ah, a list
let mut l = Vec::new();
let (_, lst_i, lst_okay) = parse_list(&tok[i + 1..], &mut l);
let (_, lst_i, lst_okay) = parse_list(&tok[i + 1..], d, &mut l);
data.push(Some(l.into()));
i += lst_i;
okay &= lst_okay;
@ -329,7 +350,7 @@ pub(super) fn parse_data_tuple_syntax(tok: &[Token]) -> (Vec<Option<DataType>>,
#[cfg(test)]
pub(super) fn parse_data_tuple_syntax_full(tok: &[Token]) -> Option<Vec<Option<DataType>>> {
let (ret, cnt, okay) = parse_data_tuple_syntax(tok);
let (ret, cnt, okay) = parse_data_tuple_syntax(tok, &mut InplaceData::new());
assert!(cnt == tok.len(), "didn't use full length");
if okay {
Some(ret)
@ -338,8 +359,9 @@ pub(super) fn parse_data_tuple_syntax_full(tok: &[Token]) -> Option<Vec<Option<D
}
}
pub(super) fn parse_data_map_syntax<'a>(
pub(super) fn parse_data_map_syntax<'a, Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
) -> (HashMap<&'a [u8], Option<DataType>>, usize, bool) {
let l = tok.len();
let mut okay = l != 0;
@ -350,7 +372,7 @@ pub(super) fn parse_data_map_syntax<'a>(
let (field, colon, expression) = (&tok[i], &tok[i + 1], &tok[i + 2]);
okay &= colon == &Symbol::SymColon;
match (field, expression) {
(Token::Ident(id), Token::Lit(l)) => {
(Token::Ident(id), tok) if Qd::can_read_lit_from(d, tok) => {
okay &= data
.insert(
unsafe {
@ -359,7 +381,7 @@ pub(super) fn parse_data_map_syntax<'a>(
},
Some(unsafe {
// UNSAFE(@ohsayan): Token LT0 guarantees LT0 > LT1 for lit
DataType::clone_from_lit(l)
DataType::clone_from_litir(Qd::read_lit(d, tok))
}),
)
.is_none();
@ -367,7 +389,7 @@ pub(super) fn parse_data_map_syntax<'a>(
(Token::Ident(id), Token::Symbol(Symbol::TtOpenSqBracket)) => {
// ooh a list
let mut l = Vec::new();
let (_, lst_i, lst_ok) = parse_list(&tok[i + 3..], &mut l);
let (_, lst_i, lst_ok) = parse_list(&tok[i + 3..], d, &mut l);
okay &= lst_ok;
i += lst_i;
okay &= data
@ -410,7 +432,7 @@ pub(super) fn parse_data_map_syntax<'a>(
pub(super) fn parse_data_map_syntax_full(
tok: &[Token],
) -> Option<HashMap<Box<str>, Option<DataType>>> {
let (dat, i, ok) = parse_data_map_syntax(tok);
let (dat, i, ok) = parse_data_map_syntax(tok, &mut InplaceData::new());
assert!(i == tok.len(), "didn't use full length");
if ok {
Some(
@ -469,8 +491,9 @@ fn parse_entity(tok: &[Token], entity: &mut MaybeInit<Entity>, i: &mut usize) ->
is_full | is_half
}
pub(super) fn parse_insert<'a>(
pub(super) fn parse_insert<'a, Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
counter: &mut usize,
) -> LangResult<InsertStatement<'a>> {
/*
@ -495,13 +518,13 @@ pub(super) fn parse_insert<'a>(
}
match tok[i] {
Token![() open] => {
let (this_data, incr, ok) = parse_data_tuple_syntax(&tok[i + 1..]);
let (this_data, incr, ok) = parse_data_tuple_syntax(&tok[i + 1..], d);
okay &= ok;
i += incr + 1;
data = Some(InsertData::Ordered(this_data));
}
Token![open {}] => {
let (this_data, incr, ok) = parse_data_map_syntax(&tok[i + 1..]);
let (this_data, incr, ok) = parse_data_map_syntax(&tok[i + 1..], d);
okay &= ok;
i += incr + 1;
data = Some(InsertData::Map(this_data));
@ -529,7 +552,7 @@ pub(super) fn parse_insert<'a>(
#[cfg(test)]
pub(super) fn parse_insert_full<'a>(tok: &'a [Token]) -> Option<InsertStatement<'a>> {
let mut z = 0;
let s = self::parse_insert(tok, &mut z);
let s = self::parse_insert(tok, &mut InplaceData::new(), &mut z);
assert!(z == tok.len(), "didn't use full length");
s.ok()
}
@ -577,8 +600,9 @@ impl<'a> SelectStatement<'a> {
/// Parse a `select` query. The cursor should have already passed the `select` token when this
/// function is called.
pub(super) fn parse_select<'a>(
pub(super) fn parse_select<'a, Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
counter: &mut usize,
) -> LangResult<SelectStatement<'a>> {
/*
@ -604,7 +628,7 @@ pub(super) fn parse_select<'a>(
}
}
i += 1;
let nx_idx = cmp::min(i, l);
let nx_idx = minidx(tok, i);
let nx_comma = tok[nx_idx] == Token![,] && i < l;
let nx_from = tok[nx_idx] == Token![from];
okay &= nx_comma | nx_from;
@ -620,11 +644,11 @@ pub(super) fn parse_select<'a>(
// now process entity
let mut entity = MaybeInit::uninit();
okay &= process_entity(&tok[i..], &mut entity, &mut i);
let has_where = tok[cmp::min(i, l)] == Token![where];
let has_where = tok[minidx(tok, i)] == Token![where];
i += has_where as usize;
let mut clauses = <_ as Default>::default();
if has_where {
okay &= WhereClause::parse_where_and_append_to(&tok[i..], &mut i, &mut clauses);
okay &= WhereClause::parse_where_and_append_to(&tok[i..], d, &mut i, &mut clauses);
okay &= !clauses.is_empty(); // append doesn't enforce clause arity
}
*counter += i;
@ -647,7 +671,7 @@ pub(super) fn parse_select<'a>(
/// **test-mode only** parse for a `select` where the full token stream is exhausted
pub(super) fn parse_select_full<'a>(tok: &'a [Token]) -> Option<SelectStatement<'a>> {
let mut i = 0;
let r = self::parse_select(tok, &mut i);
let r = self::parse_select(tok, &mut InplaceData::new(), &mut i);
assert_full_tt!(i, tok.len());
r.ok()
}
@ -692,8 +716,9 @@ impl<'a> AssignmentExpression<'a> {
/// Attempt to parse an expression and then append it to the given vector of expressions. This will return `true`
/// if the expression was parsed correctly, otherwise `false` is returned
#[inline(always)]
fn parse_and_append_expression(
fn parse_and_append_expression<Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
expressions: &mut Vec<Self>,
counter: &mut usize,
) -> bool {
@ -728,7 +753,7 @@ impl<'a> AssignmentExpression<'a> {
okay &= single_assign_okay | double_assign_okay;
i += double_assign_okay as usize; // skip on <op>assign
let has_rhs = i < l && tok[i].is_lit();
let has_rhs = Qd::can_read_lit_from(d, &tok[minidx(tok, i)]);
okay &= has_rhs;
*counter += i + has_rhs as usize;
@ -738,9 +763,10 @@ impl<'a> AssignmentExpression<'a> {
UNSAFE(@ohsayan): tok[0] is checked for being an ident early on; second, tok[i]
is also checked for being a lit and then `okay` ensures correctness
*/
let rhs = Qd::read_lit(d, &tok[i]);
AssignmentExpression {
lhs: extract!(tok[0], Token::Ident(ref r) => r.clone()),
rhs: extract!(tok[i], Token::Lit(ref l) => l.as_ir()),
rhs,
operator_fn: OPERATOR[operator_code as usize],
}
};
@ -755,7 +781,12 @@ impl<'a> AssignmentExpression<'a> {
pub(super) fn parse_expression_full<'a>(tok: &'a [Token]) -> Option<AssignmentExpression<'a>> {
let mut i = 0;
let mut exprs = Vec::new();
if AssignmentExpression::parse_and_append_expression(tok, &mut exprs, &mut i) {
if AssignmentExpression::parse_and_append_expression(
tok,
&mut InplaceData::new(),
&mut exprs,
&mut i,
) {
assert_full_tt!(i, tok.len());
Some(exprs.remove(0))
} else {
@ -797,7 +828,11 @@ impl<'a> UpdateStatement<'a> {
}
}
#[inline(always)]
pub(super) fn parse_update(tok: &'a [Token], counter: &mut usize) -> LangResult<Self> {
pub(super) fn parse_update<Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
counter: &mut usize,
) -> LangResult<Self> {
/*
TODO(@ohsayan): Allow volcanoes
smallest tt:
@ -824,10 +859,11 @@ impl<'a> UpdateStatement<'a> {
while i < l && okay && !nx_where {
okay &= AssignmentExpression::parse_and_append_expression(
&tok[i..],
d,
&mut expressions,
&mut i,
);
let nx_idx = cmp::min(i, l);
let nx_idx = minidx(tok, i);
let nx_comma = tok[nx_idx] == Token![,] && i < l;
// NOTE: volcano
nx_where = tok[nx_idx] == Token![where] && i < l;
@ -838,7 +874,7 @@ impl<'a> UpdateStatement<'a> {
i += okay as usize;
// now process expressions
let mut clauses = <_ as Default>::default();
okay &= WhereClause::parse_where_and_append_to(&tok[i..], &mut i, &mut clauses);
okay &= WhereClause::parse_where_and_append_to(&tok[i..], d, &mut i, &mut clauses);
okay &= !clauses.is_empty(); // NOTE: volcano
*counter += i;
if okay {
@ -859,7 +895,7 @@ impl<'a> UpdateStatement<'a> {
#[cfg(test)]
pub(super) fn parse_update_full<'a>(tok: &'a [Token]) -> LangResult<UpdateStatement<'a>> {
let mut i = 0;
let r = UpdateStatement::parse_update(tok, &mut i);
let r = UpdateStatement::parse_update(tok, &mut InplaceData::new(), &mut i);
assert_full_tt!(i, tok.len());
r
}
@ -887,7 +923,11 @@ impl<'a> DeleteStatement<'a> {
pub(super) fn new_test(entity: Entity, wc: WhereClauseCollection<'a>) -> Self {
Self::new(entity, WhereClause::new(wc))
}
pub(super) fn parse_delete(tok: &'a [Token], counter: &mut usize) -> LangResult<Self> {
pub(super) fn parse_delete<Qd: QueryData<'a>>(
tok: &'a [Token],
d: &mut Qd,
counter: &mut usize,
) -> LangResult<Self> {
/*
TODO(@ohsayan): Volcano
smallest tt:
@ -912,7 +952,7 @@ impl<'a> DeleteStatement<'a> {
okay &= tok[i] == Token![where]; // NOTE: volcano
i += 1; // skip even if incorrect
let mut clauses = <_ as Default>::default();
okay &= WhereClause::parse_where_and_append_to(&tok[i..], &mut i, &mut clauses);
okay &= WhereClause::parse_where_and_append_to(&tok[i..], d, &mut i, &mut clauses);
okay &= !clauses.is_empty();
*counter += i;
if okay {
@ -932,7 +972,7 @@ impl<'a> DeleteStatement<'a> {
#[cfg(test)]
pub(super) fn parse_delete_full<'a>(tok: &'a [Token]) -> LangResult<DeleteStatement<'a>> {
let mut i = 0_usize;
let r = DeleteStatement::parse_delete(tok, &mut i);
let r = DeleteStatement::parse_delete(tok, &mut InplaceData::new(), &mut i);
assert_full_tt!(i, tok.len());
r
}

Loading…
Cancel
Save