mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
FIX: Treat is as a regular binary operator
do syntax checks post parse. Also to avoid collisions with keywords require quoting the types.
This commit is contained in:
parent
1dba5f66ce
commit
eaa3e84179
@ -164,9 +164,19 @@ Type test expressions
|
|||||||
---------------------
|
---------------------
|
||||||
|
|
||||||
ucg has the `is` operator for testing that something is of a given base type.
|
ucg has the `is` operator for testing that something is of a given base type.
|
||||||
|
The type must be a string literal matching one of:
|
||||||
|
|
||||||
|
* `"null"`
|
||||||
|
* `"str"`
|
||||||
|
* `"int"`
|
||||||
|
* `"float"`
|
||||||
|
* `"tuple"`
|
||||||
|
* `"list"`
|
||||||
|
* `"macro"`
|
||||||
|
* `"module"`
|
||||||
|
|
||||||
```
|
```
|
||||||
("foo" is str) == true;
|
("foo" is "str") == true;
|
||||||
```
|
```
|
||||||
|
|
||||||
Copy Expressions
|
Copy Expressions
|
||||||
|
@ -1,51 +1,63 @@
|
|||||||
let t = import "std/testing.ucg".asserts{};
|
let t = import "std/testing.ucg".asserts{};
|
||||||
|
|
||||||
assert t.ok{
|
assert t.ok{
|
||||||
test = "foo" is str,
|
test = "foo" is "str",
|
||||||
desc = "foo is a str",
|
desc = "foo is a str",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.not_ok{
|
assert t.not_ok{
|
||||||
test = "foo" is int,
|
test = "foo" is "int",
|
||||||
desc = "foo is not an int",
|
desc = "foo is not an int",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.ok{
|
assert t.ok{
|
||||||
test = {foo="bar"} is tuple,
|
test = {foo="bar"} is "tuple",
|
||||||
desc = "found a tuple",
|
desc = "found a tuple",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.not_ok{
|
assert t.not_ok{
|
||||||
test = {foo="bar"} is str,
|
test = {foo="bar"} is "str",
|
||||||
desc = "a tuple is not a str",
|
desc = "a tuple is not a str",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.ok{
|
assert t.ok{
|
||||||
test = [1, 2] is list,
|
test = [1, 2] is "list",
|
||||||
desc = "found a list",
|
desc = "found a list",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.not_ok{
|
assert t.not_ok{
|
||||||
test = [1, 2] is tuple,
|
test = [1, 2] is "tuple",
|
||||||
desc = "list is not a tuple",
|
desc = "list is not a tuple",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.ok{
|
assert t.ok{
|
||||||
test = (macro(arg) => arg) is macro,
|
test = (macro(arg) => arg) is "macro",
|
||||||
desc = "found a macro",
|
desc = "found a macro",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.not_ok{
|
assert t.not_ok{
|
||||||
test = (macro(arg) => arg) is list,
|
test = (macro(arg) => arg) is "list",
|
||||||
desc = "a macro is not a list",
|
desc = "a macro is not a list",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.ok{
|
assert t.ok{
|
||||||
test = (module{} => {}) is module,
|
test = (module{} => {}) is "module",
|
||||||
desc = "found a module",
|
desc = "found a module",
|
||||||
};
|
};
|
||||||
|
|
||||||
assert t.not_ok{
|
assert t.not_ok{
|
||||||
test = module{} => {} is macro,
|
test = module{} => {} is "macro",
|
||||||
desc = "a module is not a macro",
|
desc = "a module is not a macro",
|
||||||
|
};
|
||||||
|
|
||||||
|
let foo_check = macro(val) => (foo in val) && (val.foo is "str");
|
||||||
|
|
||||||
|
assert t.ok{
|
||||||
|
test = foo_check({foo="bar"}),
|
||||||
|
desc = "we can check for foo string fields",
|
||||||
|
};
|
||||||
|
|
||||||
|
assert t.not_ok{
|
||||||
|
test = foo_check({bar="foo"}),
|
||||||
|
desc = "we can check for absent foo string fields",
|
||||||
};
|
};
|
@ -405,6 +405,7 @@ pub enum BinaryExprType {
|
|||||||
REMatch,
|
REMatch,
|
||||||
NotREMatch,
|
NotREMatch,
|
||||||
IN,
|
IN,
|
||||||
|
IS,
|
||||||
// Selector operator
|
// Selector operator
|
||||||
DOT,
|
DOT,
|
||||||
}
|
}
|
||||||
@ -424,18 +425,19 @@ impl BinaryExprType {
|
|||||||
BinaryExprType::LT => 1,
|
BinaryExprType::LT => 1,
|
||||||
BinaryExprType::REMatch => 1,
|
BinaryExprType::REMatch => 1,
|
||||||
BinaryExprType::NotREMatch => 1,
|
BinaryExprType::NotREMatch => 1,
|
||||||
BinaryExprType::IN => 1,
|
BinaryExprType::IN => 2,
|
||||||
|
BinaryExprType::IS => 2,
|
||||||
// Sum operators are next least tightly bound
|
// Sum operators are next least tightly bound
|
||||||
BinaryExprType::Add => 2,
|
BinaryExprType::Add => 3,
|
||||||
BinaryExprType::Sub => 2,
|
BinaryExprType::Sub => 3,
|
||||||
// Product operators are next tightly bound
|
// Product operators are next tightly bound
|
||||||
BinaryExprType::Mul => 3,
|
BinaryExprType::Mul => 4,
|
||||||
BinaryExprType::Div => 3,
|
BinaryExprType::Div => 4,
|
||||||
// Boolean operators bind tighter than math
|
// Boolean operators bind tighter than math
|
||||||
BinaryExprType::AND => 4,
|
BinaryExprType::AND => 5,
|
||||||
BinaryExprType::OR => 4,
|
BinaryExprType::OR => 5,
|
||||||
// Dot operators are most tightly bound.
|
// Dot operators are most tightly bound.
|
||||||
BinaryExprType::DOT => 5,
|
BinaryExprType::DOT => 6,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -602,9 +604,6 @@ pub enum Expression {
|
|||||||
// Binary expressions
|
// Binary expressions
|
||||||
Binary(BinaryOpDef),
|
Binary(BinaryOpDef),
|
||||||
|
|
||||||
// Type tests
|
|
||||||
IS(IsDef),
|
|
||||||
|
|
||||||
// Complex Expressions
|
// Complex Expressions
|
||||||
Copy(CopyDef),
|
Copy(CopyDef),
|
||||||
Range(RangeDef),
|
Range(RangeDef),
|
||||||
@ -637,7 +636,6 @@ impl Expression {
|
|||||||
&Expression::FuncOp(ref def) => def.pos(),
|
&Expression::FuncOp(ref def) => def.pos(),
|
||||||
&Expression::Include(ref def) => &def.pos,
|
&Expression::Include(ref def) => &def.pos,
|
||||||
&Expression::Import(ref def) => &def.pos,
|
&Expression::Import(ref def) => &def.pos,
|
||||||
&Expression::IS(ref def) => &def.pos,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -681,9 +679,6 @@ impl fmt::Display for Expression {
|
|||||||
&Expression::Include(_) => {
|
&Expression::Include(_) => {
|
||||||
write!(w, "<Include>")?;
|
write!(w, "<Include>")?;
|
||||||
}
|
}
|
||||||
&Expression::IS(_) => {
|
|
||||||
write!(w, "<IS>")?;
|
|
||||||
}
|
|
||||||
&Expression::Import(_) => {
|
&Expression::Import(_) => {
|
||||||
write!(w, "<Include>")?;
|
write!(w, "<Include>")?;
|
||||||
}
|
}
|
||||||
|
@ -103,9 +103,6 @@ impl<'a> AstWalker<'a> {
|
|||||||
self.walk_expression(expr.as_mut());
|
self.walk_expression(expr.as_mut());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expression::IS(ref mut def) => {
|
|
||||||
self.walk_expression(def.target.as_mut());
|
|
||||||
}
|
|
||||||
Expression::Select(ref mut def) => {
|
Expression::Select(ref mut def) => {
|
||||||
self.walk_expression(def.default.as_mut());
|
self.walk_expression(def.default.as_mut());
|
||||||
self.walk_expression(def.val.as_mut());
|
self.walk_expression(def.val.as_mut());
|
||||||
|
@ -350,7 +350,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
fn check_reserved_word(name: &str) -> bool {
|
fn check_reserved_word(name: &str) -> bool {
|
||||||
match name {
|
match name {
|
||||||
"self" | "assert" | "true" | "false" | "let" | "import" | "as" | "select" | "macro"
|
"self" | "assert" | "true" | "false" | "let" | "import" | "as" | "select" | "macro"
|
||||||
| "module" | "env" | "map" | "filter" | "NULL" | "out" => true,
|
| "module" | "env" | "map" | "filter" | "NULL" | "out" | "in" | "is" => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -952,7 +952,10 @@ impl<'a> FileBuilder<'a> {
|
|||||||
if let &BinaryExprType::IN = kind {
|
if let &BinaryExprType::IN = kind {
|
||||||
// TODO Should we support this operation on strings too?
|
// TODO Should we support this operation on strings too?
|
||||||
return self.do_element_check(&def.left, &def.right, scope);
|
return self.do_element_check(&def.left, &def.right, scope);
|
||||||
};
|
}
|
||||||
|
if let &BinaryExprType::IS = kind {
|
||||||
|
return self.eval_is_check(def, scope);
|
||||||
|
}
|
||||||
match kind {
|
match kind {
|
||||||
// We special case the boolean operators because we want them to short circuit.
|
// We special case the boolean operators because we want them to short circuit.
|
||||||
&BinaryExprType::AND | &BinaryExprType::OR => {
|
&BinaryExprType::AND | &BinaryExprType::OR => {
|
||||||
@ -994,6 +997,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
self.eval_re_match(left, def.left.pos(), right, def.right.pos(), true)
|
self.eval_re_match(left, def.left.pos(), right, def.right.pos(), true)
|
||||||
}
|
}
|
||||||
&BinaryExprType::IN
|
&BinaryExprType::IN
|
||||||
|
| &BinaryExprType::IS
|
||||||
| &BinaryExprType::DOT
|
| &BinaryExprType::DOT
|
||||||
| &BinaryExprType::AND
|
| &BinaryExprType::AND
|
||||||
| &BinaryExprType::OR => panic!("Unreachable"),
|
| &BinaryExprType::OR => panic!("Unreachable"),
|
||||||
@ -1702,9 +1706,23 @@ impl<'a> FileBuilder<'a> {
|
|||||||
Ok(Rc::new(Val::List(vec)))
|
Ok(Rc::new(Val::List(vec)))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn eval_is_check(&self, def: &IsDef, scope: &Scope) -> Result<Rc<Val>, Box<dyn Error>> {
|
pub fn eval_is_check(
|
||||||
let val = self.eval_expr(def.target.as_ref(), scope)?;
|
&self,
|
||||||
let result = match def.typ.fragment.as_str() {
|
def: &BinaryOpDef,
|
||||||
|
scope: &Scope,
|
||||||
|
) -> Result<Rc<Val>, Box<dyn Error>> {
|
||||||
|
let typ = match def.right.as_ref() {
|
||||||
|
Expression::Simple(Value::Str(ref s)) => s.val.clone(),
|
||||||
|
_ => {
|
||||||
|
return Err(Box::new(error::BuildError::new(
|
||||||
|
format!("Expected string expression but got {}", def.right),
|
||||||
|
error::ErrorType::TypeFail,
|
||||||
|
def.right.pos().clone(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let val = self.eval_expr(def.left.as_ref(), scope)?;
|
||||||
|
let result = match typ.as_str() {
|
||||||
"str" => val.is_str(),
|
"str" => val.is_str(),
|
||||||
"bool" => val.is_bool(),
|
"bool" => val.is_bool(),
|
||||||
"null" => val.is_empty(),
|
"null" => val.is_empty(),
|
||||||
@ -1718,7 +1736,7 @@ impl<'a> FileBuilder<'a> {
|
|||||||
return Err(Box::new(error::BuildError::new(
|
return Err(Box::new(error::BuildError::new(
|
||||||
format!("Expected valid type name but got {}", other),
|
format!("Expected valid type name but got {}", other),
|
||||||
error::ErrorType::TypeFail,
|
error::ErrorType::TypeFail,
|
||||||
def.pos.clone(),
|
def.right.pos().clone(),
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -1748,7 +1766,6 @@ impl<'a> FileBuilder<'a> {
|
|||||||
&Expression::FuncOp(ref def) => self.eval_func_op(def, scope),
|
&Expression::FuncOp(ref def) => self.eval_func_op(def, scope),
|
||||||
&Expression::Include(ref def) => self.eval_include(def),
|
&Expression::Include(ref def) => self.eval_include(def),
|
||||||
&Expression::Import(ref def) => self.eval_import(def),
|
&Expression::Import(ref def) => self.eval_import(def),
|
||||||
&Expression::IS(ref def) => self.eval_is_check(def, scope),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -650,21 +650,6 @@ make_fn!(
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
make_fn!(
|
|
||||||
is_type_expression<SliceIter<Token>, Expression>,
|
|
||||||
do_each!(
|
|
||||||
pos => pos,
|
|
||||||
expr => non_op_expression,
|
|
||||||
_ => word!("is"),
|
|
||||||
typ => must!(match_type!(BAREWORD)),
|
|
||||||
(Expression::IS(IsDef{
|
|
||||||
pos: pos,
|
|
||||||
target: Box::new(expr),
|
|
||||||
typ: typ,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
fn unprefixed_expression(input: SliceIter<Token>) -> ParseResult<Expression> {
|
fn unprefixed_expression(input: SliceIter<Token>) -> ParseResult<Expression> {
|
||||||
let _input = input.clone();
|
let _input = input.clone();
|
||||||
either!(
|
either!(
|
||||||
@ -693,7 +678,7 @@ make_fn!(
|
|||||||
|
|
||||||
fn expression(input: SliceIter<Token>) -> ParseResult<Expression> {
|
fn expression(input: SliceIter<Token>) -> ParseResult<Expression> {
|
||||||
let _input = input.clone();
|
let _input = input.clone();
|
||||||
match trace_parse!(_input, either!(is_type_expression, op_expression)) {
|
match trace_parse!(_input, op_expression) {
|
||||||
Result::Incomplete(i) => Result::Incomplete(i),
|
Result::Incomplete(i) => Result::Incomplete(i),
|
||||||
Result::Fail(_) => trace_parse!(input, non_op_expression),
|
Result::Fail(_) => trace_parse!(input, non_op_expression),
|
||||||
Result::Abort(e) => Result::Abort(e),
|
Result::Abort(e) => Result::Abort(e),
|
||||||
|
@ -20,6 +20,17 @@ use abortable_parser::{Error, Peekable, Result, SliceIter};
|
|||||||
use super::{non_op_expression, ParseResult};
|
use super::{non_op_expression, ParseResult};
|
||||||
use crate::ast::*;
|
use crate::ast::*;
|
||||||
|
|
||||||
|
macro_rules! abort_on_end {
|
||||||
|
($i:expr) => {{
|
||||||
|
if eoi($i.clone()).is_complete() {
|
||||||
|
return Result::Fail(Error::new(
|
||||||
|
format!("Expected Expression found End Of Input"),
|
||||||
|
Box::new($i.clone()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
/// Defines the intermediate stages of our bottom up parser for precedence parsing.
|
/// Defines the intermediate stages of our bottom up parser for precedence parsing.
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub enum Element {
|
pub enum Element {
|
||||||
@ -66,12 +77,7 @@ make_fn!(
|
|||||||
|
|
||||||
fn parse_expression(i: SliceIter<Element>) -> Result<SliceIter<Element>, Expression> {
|
fn parse_expression(i: SliceIter<Element>) -> Result<SliceIter<Element>, Expression> {
|
||||||
let mut i_ = i.clone();
|
let mut i_ = i.clone();
|
||||||
if eoi(i_.clone()).is_complete() {
|
abort_on_end!(i_);
|
||||||
return Result::Abort(Error::new(
|
|
||||||
"Expected Expression found End Of Input",
|
|
||||||
Box::new(i_),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let el = i_.next();
|
let el = i_.next();
|
||||||
if let Some(&Element::Expr(ref expr)) = el {
|
if let Some(&Element::Expr(ref expr)) = el {
|
||||||
return Result::Complete(i_.clone(), expr.clone());
|
return Result::Complete(i_.clone(), expr.clone());
|
||||||
@ -87,12 +93,7 @@ fn parse_expression(i: SliceIter<Element>) -> Result<SliceIter<Element>, Express
|
|||||||
|
|
||||||
fn parse_bool_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
fn parse_bool_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
||||||
let mut i_ = i.clone();
|
let mut i_ = i.clone();
|
||||||
if eoi(i_.clone()).is_complete() {
|
abort_on_end!(i_);
|
||||||
return Result::Fail(Error::new(
|
|
||||||
format!("Expected Expression found End Of Input"),
|
|
||||||
Box::new(i_),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let el = i_.next();
|
let el = i_.next();
|
||||||
if let Some(&Element::Op(ref op)) = el {
|
if let Some(&Element::Op(ref op)) = el {
|
||||||
match op {
|
match op {
|
||||||
@ -115,12 +116,7 @@ fn parse_bool_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, Bina
|
|||||||
|
|
||||||
fn parse_dot_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
fn parse_dot_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
||||||
let mut i_ = i.clone();
|
let mut i_ = i.clone();
|
||||||
if eoi(i_.clone()).is_complete() {
|
abort_on_end!(i_);
|
||||||
return Result::Fail(Error::new(
|
|
||||||
format!("Expected Expression found End Of Input"),
|
|
||||||
Box::new(i_),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let el = i_.next();
|
let el = i_.next();
|
||||||
if let Some(&Element::Op(ref op)) = el {
|
if let Some(&Element::Op(ref op)) = el {
|
||||||
match op {
|
match op {
|
||||||
@ -143,12 +139,7 @@ fn parse_dot_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, Binar
|
|||||||
|
|
||||||
fn parse_sum_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
fn parse_sum_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
||||||
let mut i_ = i.clone();
|
let mut i_ = i.clone();
|
||||||
if eoi(i_.clone()).is_complete() {
|
abort_on_end!(i_);
|
||||||
return Result::Fail(Error::new(
|
|
||||||
format!("Expected Expression found End Of Input"),
|
|
||||||
Box::new(i_),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let el = i_.next();
|
let el = i_.next();
|
||||||
if let Some(&Element::Op(ref op)) = el {
|
if let Some(&Element::Op(ref op)) = el {
|
||||||
match op {
|
match op {
|
||||||
@ -174,12 +165,7 @@ fn parse_sum_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, Binar
|
|||||||
|
|
||||||
fn parse_product_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
fn parse_product_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
||||||
let mut i_ = i.clone();
|
let mut i_ = i.clone();
|
||||||
if eoi(i_.clone()).is_complete() {
|
abort_on_end!(i_);
|
||||||
return Result::Fail(Error::new(
|
|
||||||
format!("Expected Expression found End Of Input"),
|
|
||||||
Box::new(i_),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let el = i_.next();
|
let el = i_.next();
|
||||||
if let Some(&Element::Op(ref op)) = el {
|
if let Some(&Element::Op(ref op)) = el {
|
||||||
match op {
|
match op {
|
||||||
@ -214,18 +200,14 @@ make_fn!(
|
|||||||
do_each!(_ => punct!(">="), (Element::Op(BinaryExprType::GTEqual))),
|
do_each!(_ => punct!(">="), (Element::Op(BinaryExprType::GTEqual))),
|
||||||
do_each!(_ => punct!("<"), (Element::Op(BinaryExprType::LT))),
|
do_each!(_ => punct!("<"), (Element::Op(BinaryExprType::LT))),
|
||||||
do_each!(_ => punct!(">"), (Element::Op(BinaryExprType::GT))),
|
do_each!(_ => punct!(">"), (Element::Op(BinaryExprType::GT))),
|
||||||
do_each!(_ => word!("in"), (Element::Op(BinaryExprType::IN)))
|
do_each!(_ => word!("in"), (Element::Op(BinaryExprType::IN))),
|
||||||
|
do_each!(_ => word!("is"), (Element::Op(BinaryExprType::IS)))
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
fn parse_compare_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
fn parse_compare_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, BinaryExprType> {
|
||||||
let mut i_ = i.clone();
|
let mut i_ = i.clone();
|
||||||
if eoi(i_.clone()).is_complete() {
|
abort_on_end!(i_);
|
||||||
return Result::Fail(Error::new(
|
|
||||||
format!("Expected Expression found End Of Input"),
|
|
||||||
Box::new(i_),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let el = i_.next();
|
let el = i_.next();
|
||||||
if let Some(&Element::Op(ref op)) = el {
|
if let Some(&Element::Op(ref op)) = el {
|
||||||
match op {
|
match op {
|
||||||
@ -237,6 +219,7 @@ fn parse_compare_operator(i: SliceIter<Element>) -> Result<SliceIter<Element>, B
|
|||||||
| &BinaryExprType::REMatch
|
| &BinaryExprType::REMatch
|
||||||
| &BinaryExprType::NotREMatch
|
| &BinaryExprType::NotREMatch
|
||||||
| &BinaryExprType::Equal
|
| &BinaryExprType::Equal
|
||||||
|
| &BinaryExprType::IS
|
||||||
| &BinaryExprType::IN => {
|
| &BinaryExprType::IN => {
|
||||||
return Result::Complete(i_.clone(), op.clone());
|
return Result::Complete(i_.clone(), op.clone());
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user