1use std::fmt;
2
3use crate::compiler::state::{TypeInfo, TypeState};
4use crate::compiler::{
5 Context, Expression, TypeDef,
6 expression::{self, Expr, Resolved},
7 parser::{Node, ast},
8 value::VrlValueArithmetic,
9};
10use crate::diagnostic::{DiagnosticMessage, Label, Note, Span, Urls};
11use crate::value::Value;
12
13#[derive(Clone, PartialEq)]
14pub struct Op {
15 pub(crate) lhs: Box<Expr>,
16 pub(crate) rhs: Box<Expr>,
17 pub(crate) opcode: ast::Opcode,
18}
19
20impl Op {
21 pub fn new(
37 lhs: Node<Expr>,
38 opcode: Node<ast::Opcode>,
39 rhs: Node<Expr>,
40 state: &TypeState,
41 ) -> Result<Self, Error> {
42 use ast::Opcode::{Eq, Ge, Gt, Le, Lt, Ne};
43
44 let mut state = state.clone();
45
46 let (op_span, opcode) = opcode.take();
47
48 let (lhs_span, lhs) = lhs.take();
49 let lhs_type_def = lhs.apply_type_info(&mut state);
50 let rhs_type_def = rhs.apply_type_info(&mut state);
51
52 let (rhs_span, rhs) = rhs.take();
53
54 if matches!(opcode, Eq | Ne | Lt | Le | Gt | Ge)
55 && let Expr::Op(op) = &lhs
56 && matches!(op.opcode, Eq | Ne | Lt | Le | Gt | Ge)
57 {
58 return Err(Error::ChainedComparison { span: op_span });
59 }
60
61 if let ast::Opcode::Err = opcode
62 && lhs_type_def.is_infallible()
63 {
64 return Err(Error::UnnecessaryCoalesce {
65 lhs_span,
66 rhs_span,
67 op_span,
68 });
69 }
70
71 if let ast::Opcode::Merge = opcode
72 && !(lhs_type_def.is_object() && rhs_type_def.is_object())
73 {
74 return Err(Error::MergeNonObjects {
75 lhs_span: if lhs_type_def.is_object() {
76 None
77 } else {
78 Some(lhs_span)
79 },
80 rhs_span: if rhs_type_def.is_object() {
81 None
82 } else {
83 Some(rhs_span)
84 },
85 });
86 }
87
88 Ok(Op {
89 lhs: Box::new(lhs),
90 rhs: Box::new(rhs),
91 opcode,
92 })
93 }
94}
95
96impl Expression for Op {
97 fn resolve(&self, ctx: &mut Context) -> Resolved {
98 use crate::value::Value::{Boolean, Null};
99 use ast::Opcode::{Add, And, Div, Eq, Err, Ge, Gt, Le, Lt, Merge, Mul, Ne, Or, Sub};
100
101 match self.opcode {
102 Err => return self.lhs.resolve(ctx).or_else(|_| self.rhs.resolve(ctx)),
103 Or => {
104 return self
105 .lhs
106 .resolve(ctx)?
107 .try_or(|| self.rhs.resolve(ctx))
108 .map_err(Into::into);
109 }
110 And => {
111 return match self.lhs.resolve(ctx)? {
112 Null | Boolean(false) => Ok(false.into()),
113 v => v.try_and(self.rhs.resolve(ctx)?).map_err(Into::into),
114 };
115 }
116 _ => (),
117 }
118
119 let lhs = self.lhs.resolve(ctx)?;
120 let rhs = self.rhs.resolve(ctx)?;
121
122 match self.opcode {
124 Mul => lhs.try_mul(rhs),
125 Div => lhs.try_div(rhs),
126 Add => lhs.try_add(rhs),
127 Sub => lhs.try_sub(rhs),
128 Eq => Ok(lhs.eq_lossy(&rhs).into()),
129 Ne => Ok((!lhs.eq_lossy(&rhs)).into()),
130 Gt => lhs.try_gt(rhs),
131 Ge => lhs.try_ge(rhs),
132 Lt => lhs.try_lt(rhs),
133 Le => lhs.try_le(rhs),
134 Merge => lhs.try_merge(rhs),
135 And | Or | Err => unreachable!(),
136 }
137 .map_err(Into::into)
138 }
139
140 #[allow(clippy::too_many_lines)]
141 fn type_info(&self, state: &TypeState) -> TypeInfo {
142 use crate::value::Kind as K;
143 use ast::Opcode::{Add, And, Div, Eq, Err, Ge, Gt, Le, Lt, Merge, Mul, Ne, Or, Sub};
144 let original_state = state.clone();
145
146 let mut state = state.clone();
147 let mut lhs_def = self.lhs.apply_type_info(&mut state);
148 let lhs_value = self.lhs.resolve_constant(&original_state);
149
150 let maybe_rhs = |state: &mut TypeState| {
158 let rhs_info = self.rhs.type_info(state);
159 *state = state.clone().merge(rhs_info.state);
160 rhs_info.result
161 };
162
163 let result = match self.opcode {
164 Err => {
165 let rhs_def = maybe_rhs(&mut state);
166
167 let fallible = lhs_def.is_fallible() && rhs_def.is_fallible();
169
170 lhs_def.union(rhs_def).maybe_fallible(fallible)
171 }
172
173 Or => {
174 if lhs_def.is_null() || lhs_value == Some(Value::Boolean(false)) {
175 self.rhs.apply_type_info(&mut state)
177 } else if !(lhs_def.contains_null() || lhs_def.contains_boolean())
178 || lhs_value == Some(Value::Boolean(true))
179 {
180 lhs_def
182 } else {
183 lhs_def.remove_null();
188
189 lhs_def.union(maybe_rhs(&mut state))
190 }
191 }
192
193 Merge => lhs_def.merge_overwrite(self.rhs.apply_type_info(&mut state)),
195
196 And => {
197 if lhs_def.is_null() || lhs_value == Some(Value::Boolean(false)) {
198 TypeDef::boolean()
200 } else if lhs_value == Some(Value::Boolean(true)) {
201 self.rhs.apply_type_info(&mut state).with_kind(K::boolean())
204 } else {
205 lhs_def
207 .fallible_unless(K::null().or_boolean())
208 .union(maybe_rhs(&mut state).fallible_unless(K::null().or_boolean()))
209 .with_kind(K::boolean())
210 }
211 }
212
213 Eq | Ne => lhs_def
216 .union(self.rhs.apply_type_info(&mut state))
217 .with_kind(K::boolean()),
218
219 Gt | Ge | Lt | Le => {
220 let rhs_def = self.rhs.apply_type_info(&mut state);
221
222 if (lhs_def.is_bytes() && rhs_def.is_bytes())
231 || (lhs_def.is_timestamp() && rhs_def.is_timestamp())
232 {
233 lhs_def.union(rhs_def).with_kind(K::boolean())
234 }
235 else {
240 lhs_def
241 .fallible_unless(K::integer().or_float())
242 .union(rhs_def.fallible_unless(K::integer().or_float()))
243 .with_kind(K::boolean())
244 }
245 }
246
247 Div => {
249 let td = TypeDef::float();
250
251 match self.rhs.resolve_constant(&state) {
253 Some(value) if lhs_def.is_float() || lhs_def.is_integer() => match value {
254 Value::Float(v) if v.is_normal() => td.infallible(),
255 Value::Integer(v) if v != 0 => td.infallible(),
256 _ => td.fallible(),
257 },
258 _ => td.fallible(),
259 }
260 }
261
262 Add | Sub | Mul => {
263 let rhs_def = self.rhs.apply_type_info(&mut state);
265
266 match self.opcode {
267 Add if lhs_def.is_bytes() || rhs_def.is_bytes() => lhs_def
270 .fallible_unless(K::bytes().or_null())
271 .union(rhs_def.fallible_unless(K::bytes().or_null()))
272 .with_kind(K::bytes()),
273
274 Add | Sub | Mul if lhs_def.is_float() || rhs_def.is_float() => lhs_def
283 .fallible_unless(K::integer().or_float())
284 .union(rhs_def.fallible_unless(K::integer().or_float()))
285 .with_kind(K::float()),
286
287 Add | Sub | Mul if lhs_def.is_integer() && rhs_def.is_integer() => {
292 lhs_def.union(rhs_def).with_kind(K::integer())
293 }
294
295 Mul if lhs_def.is_bytes() && rhs_def.is_integer() => {
297 lhs_def.union(rhs_def).with_kind(K::bytes())
298 }
299
300 Mul if lhs_def.is_integer() && rhs_def.is_bytes() => {
302 lhs_def.union(rhs_def).with_kind(K::bytes())
303 }
304
305 Add | Mul => lhs_def
308 .union(rhs_def)
309 .fallible()
310 .with_kind(K::bytes().or_integer().or_float()),
311
312 Sub => lhs_def
314 .union(rhs_def)
315 .fallible()
316 .with_kind(K::integer().or_float()),
317 _ => unreachable!("Add, Sub, or Mul operation not handled"),
318 }
319 }
320 };
321 TypeInfo::new(state, result)
322 }
323}
324
325impl fmt::Display for Op {
326 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
327 write!(f, "{} {} {}", self.lhs, self.opcode, self.rhs)
328 }
329}
330
331impl fmt::Debug for Op {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 write!(f, "Op({} {} {})", self.lhs, self.opcode, self.rhs)
334 }
335}
336
337#[derive(thiserror::Error, Debug)]
340pub enum Error {
341 #[error("comparison operators can't be chained together")]
342 ChainedComparison { span: Span },
343
344 #[error("unnecessary error coalescing operation")]
345 UnnecessaryCoalesce {
346 lhs_span: Span,
347 rhs_span: Span,
348 op_span: Span,
349 },
350
351 #[error("only objects can be merged")]
352 MergeNonObjects {
353 lhs_span: Option<Span>,
354 rhs_span: Option<Span>,
355 },
356
357 #[error("fallible operation")]
358 Expr(#[from] expression::ExpressionError),
359}
360
361impl DiagnosticMessage for Error {
362 fn code(&self) -> usize {
363 use Error::{ChainedComparison, Expr, MergeNonObjects, UnnecessaryCoalesce};
364
365 match self {
366 ChainedComparison { .. } => 650,
367 UnnecessaryCoalesce { .. } => 651,
368 MergeNonObjects { .. } => 652,
369 Expr(err) => err.code(),
370 }
371 }
372
373 fn message(&self) -> String {
374 use Error::Expr;
375
376 match self {
377 Expr(err) => err.message(),
378 err => err.to_string(),
379 }
380 }
381
382 fn labels(&self) -> Vec<Label> {
383 use Error::{ChainedComparison, Expr, MergeNonObjects, UnnecessaryCoalesce};
384
385 match self {
386 ChainedComparison { span } => vec![Label::primary("", span)],
387 UnnecessaryCoalesce {
388 lhs_span,
389 rhs_span,
390 op_span,
391 } => vec![
392 Label::primary("this expression can't fail", lhs_span),
393 Label::context("this expression never resolves", rhs_span),
394 Label::context("remove this error coalescing operation", op_span),
395 ],
396 MergeNonObjects { lhs_span, rhs_span } => {
397 let mut labels = Vec::new();
398 if let Some(lhs_span) = lhs_span {
399 labels.push(Label::primary(
400 "this expression must resolve to an object",
401 lhs_span,
402 ));
403 }
404 if let Some(rhs_span) = rhs_span {
405 labels.push(Label::primary(
406 "this expression must resolve to an object",
407 rhs_span,
408 ));
409 }
410
411 labels
412 }
413 Expr(err) => err.labels(),
414 }
415 }
416
417 fn notes(&self) -> Vec<Note> {
418 use Error::{ChainedComparison, Expr};
419
420 match self {
421 ChainedComparison { .. } => vec![Note::SeeDocs(
422 "comparisons".to_owned(),
423 Urls::expression_docs_url("#comparison"),
424 )],
425 Expr(err) => err.notes(),
426 _ => vec![],
427 }
428 }
429}
430
431#[cfg(test)]
434mod tests {
435 use std::convert::TryInto;
436
437 use chrono::Utc;
438 use ordered_float::NotNan;
439
440 use ast::{
441 Ident,
442 Opcode::{Add, And, Div, Eq, Err, Ge, Gt, Le, Lt, Mul, Ne, Or, Sub},
443 };
444
445 use crate::compiler::expression::{Block, IfStatement, Literal, Predicate, Variable};
446 use crate::test_type_def;
447
448 use super::*;
449
450 #[allow(clippy::needless_pass_by_value)]
451 fn op(
452 opcode: ast::Opcode,
453 lhs: impl TryInto<Literal> + fmt::Debug + Clone,
454 rhs: impl TryInto<Literal> + fmt::Debug + Clone,
455 ) -> Op {
456 let Ok(lhs) = lhs.clone().try_into() else {
457 panic!("not a valid lhs expression: {lhs:?}")
458 };
459
460 let Ok(rhs) = rhs.clone().try_into() else {
461 panic!("not a valid rhs expression: {rhs:?}")
462 };
463
464 Op {
465 lhs: Box::new(lhs.into()),
466 rhs: Box::new(rhs.into()),
467 opcode,
468 }
469 }
470
471 fn f(f: f64) -> NotNan<f64> {
472 NotNan::new(f).unwrap()
473 }
474
475 test_type_def![
476 or_exact {
477 expr: |_| op(Or, "foo", true),
478 want: TypeDef::bytes(),
479 }
480
481 or_null {
482 expr: |_| op(Or, (), true),
483 want: TypeDef::boolean(),
484 }
485
486 multiply_string_integer {
487 expr: |_| op(Mul, "foo", 1),
488 want: TypeDef::bytes(),
489 }
490
491 multiply_integer_string {
492 expr: |_| op(Mul, 1, "foo"),
493 want: TypeDef::bytes(),
494 }
495
496 multiply_float_integer {
497 expr: |_| op(Mul, f(1.0), 1),
498 want: TypeDef::float(),
499 }
500
501 multiply_integer_float {
502 expr: |_| op(Mul, 1, f(1.0)),
503 want: TypeDef::float(),
504 }
505
506 multiply_integer_integer {
507 expr: |_| op(Mul, 1, 1),
508 want: TypeDef::integer(),
509 }
510
511 multiply_other {
512 expr: |_| op(Mul, (), ()),
513 want: TypeDef::bytes().fallible().or_integer().or_float(),
514 }
515
516 add_string_string {
517 expr: |_| op(Add, "foo", "bar"),
518 want: TypeDef::bytes(),
519 }
520
521 add_string_null {
522 expr: |_| op(Add, "foo", ()),
523 want: TypeDef::bytes(),
524 }
525
526 add_null_string {
527 expr: |_| op(Add, (), "foo"),
528 want: TypeDef::bytes(),
529 }
530
531 add_string_bool {
532 expr: |_| op(Add, "foo", true),
533 want: TypeDef::bytes().fallible(),
534 }
535
536 add_float_integer {
537 expr: |_| op(Add, f(1.0), 1),
538 want: TypeDef::float(),
539 }
540
541 add_integer_float {
542 expr: |_| op(Add, 1, f(1.0)),
543 want: TypeDef::float(),
544 }
545
546 add_float_other {
547 expr: |_| op(Add, f(1.0), ()),
548 want: TypeDef::float().fallible(),
549 }
550
551 add_other_float {
552 expr: |_| op(Add, (), f(1.0)),
553 want: TypeDef::float().fallible(),
554 }
555
556 add_integer_integer {
557 expr: |_| op(Add, 1, 1),
558 want: TypeDef::integer(),
559 }
560
561 add_other {
562 expr: |_| op(Add, (), ()),
563 want: TypeDef::bytes().or_integer().or_float().fallible(),
564 }
565
566 subtract_integer {
567 expr: |_| op(Sub, 1, 1),
568 want: TypeDef::integer().infallible(),
569 }
570
571 subtract_float {
572 expr: |_| op(Sub, 1.0, 1.0),
573 want: TypeDef::float().infallible(),
574 }
575
576 subtract_mixed {
577 expr: |_| op(Sub, 1, 1.0),
578 want: TypeDef::float().infallible(),
579 }
580
581 subtract_other {
582 expr: |_| op(Sub, 1, ()),
583 want: TypeDef::integer().fallible().or_float(),
584 }
585
586 divide_integer_literal {
587 expr: |_| op(Div, 1, 1),
588 want: TypeDef::float().infallible(),
589 }
590
591 divide_float_literal {
592 expr: |_| op(Div, 1.0, 1.0),
593 want: TypeDef::float().infallible(),
594 }
595
596 divide_mixed_literal {
597 expr: |_| op(Div, 1, 1.0),
598 want: TypeDef::float().infallible(),
599 }
600
601 divide_float_zero_literal {
602 expr: |_| op(Div, 1, 0.0),
603 want: TypeDef::float().fallible(),
604 }
605
606 divide_integer_zero_literal {
607 expr: |_| op(Div, 1, 0),
608 want: TypeDef::float().fallible(),
609 }
610
611 divide_lhs_literal_wrong_rhs {
612 expr: |_| Op {
613 lhs: Box::new(Literal::from(true).into()),
614 rhs: Box::new(Literal::from(NotNan::new(1.0).unwrap()).into()),
615 opcode: Div,
616 },
617 want: TypeDef::float().fallible(),
618 }
619
620 divide_dynamic_rhs {
621 expr: |state: &mut TypeState| {
622 state.local.insert_variable(Ident::new("foo"), crate::compiler::type_def::Details {
623 type_def: TypeDef::null(),
624 value: None,
625 });
626
627 Op {
628 lhs: Box::new(Literal::from(1).into()),
629 rhs: Box::new(Variable::new(Span::default(), Ident::new("foo"), &state.local).unwrap().into()),
630 opcode: Div,
631 }
632 },
633 want: TypeDef::float().fallible(),
634 }
635
636 divide_other {
637 expr: |_| op(Div, 1.0, ()),
638 want: TypeDef::float().fallible(),
639 }
640
641 and_null {
642 expr: |_| op(And, (), ()),
643 want: TypeDef::boolean().infallible(),
644 }
645
646 and_boolean {
647 expr: |_| op(And, true, true),
648 want: TypeDef::boolean().infallible(),
649 }
650
651 and_mixed {
652 expr: |_| op(And, (), true),
653 want: TypeDef::boolean().infallible(),
654 }
655
656 and_other {
657 expr: |_| op(And, (), "bar"),
658 want: TypeDef::boolean().infallible(),
659 }
660
661 equal {
662 expr: |_| op(Eq, (), ()),
663 want: TypeDef::boolean().infallible(),
664 }
665
666 not_equal {
667 expr: |_| op(Ne, (), "foo"),
668 want: TypeDef::boolean().infallible(),
669 }
670
671 greater_integer {
672 expr: |_| op(Gt, 1, 1),
673 want: TypeDef::boolean().infallible(),
674 }
675
676 greater_float {
677 expr: |_| op(Gt, 1.0, 1.0),
678 want: TypeDef::boolean().infallible(),
679 }
680
681 greater_mixed {
682 expr: |_| op(Gt, 1, 1.0),
683 want: TypeDef::boolean().infallible(),
684 }
685
686 greater_bytes {
687 expr: |_| op(Gt, "c", "b"),
688 want: TypeDef::boolean().infallible(),
689 }
690
691 greater_timestamps {
692 expr: |_| op(Gt, Utc::now(), Utc::now()),
693 want: TypeDef::boolean().infallible(),
694 }
695
696 greater_other {
697 expr: |_| op(Gt, 1, "foo"),
698 want: TypeDef::boolean().fallible(),
699 }
700
701 greater_or_equal_integer {
702 expr: |_| op(Ge, 1, 1),
703 want: TypeDef::boolean().infallible(),
704 }
705
706 greater_or_equal_float {
707 expr: |_| op(Ge, 1.0, 1.0),
708 want: TypeDef::boolean().infallible(),
709 }
710
711 greater_or_equal_mixed {
712 expr: |_| op(Ge, 1, 1.0),
713 want: TypeDef::boolean().infallible(),
714 }
715
716 greater_or_equal_bytes {
717 expr: |_| op(Ge, "foo", "foo"),
718 want: TypeDef::boolean().infallible(),
719 }
720
721 greater_or_equal_timestamps {
722 expr: |_| op(Ge, Utc::now(), Utc::now()),
723 want: TypeDef::boolean().infallible(),
724 }
725
726 greater_or_equal_other {
727 expr: |_| op(Ge, 1, "foo"),
728 want: TypeDef::boolean().fallible(),
729 }
730
731 less_integer {
732 expr: |_| op(Lt, 1, 1),
733 want: TypeDef::boolean().infallible(),
734 }
735
736 less_float {
737 expr: |_| op(Lt, 1.0, 1.0),
738 want: TypeDef::boolean().infallible(),
739 }
740
741 less_mixed {
742 expr: |_| op(Lt, 1, 1.0),
743 want: TypeDef::boolean().infallible(),
744 }
745
746 less_bytes {
747 expr: |_| op(Lt, "bar", "foo"),
748 want: TypeDef::boolean().infallible(),
749 }
750
751 less_timestamps {
752 expr: |_| op(Lt, Utc::now(), Utc::now()),
753 want: TypeDef::boolean().infallible(),
754 }
755
756 less_other {
757 expr: |_| op(Lt, 1, "foo"),
758 want: TypeDef::boolean().fallible(),
759 }
760
761 less_or_equal_integer {
762 expr: |_| op(Le, 1, 1),
763 want: TypeDef::boolean().infallible(),
764 }
765
766 less_or_equal_float {
767 expr: |_| op(Le, 1.0, 1.0),
768 want: TypeDef::boolean().infallible(),
769 }
770
771 less_or_equal_mixed {
772 expr: |_| op(Le, 1, 1.0),
773 want: TypeDef::boolean().infallible(),
774 }
775
776 less_or_equal_bytes {
777 expr: |_| op(Le, "bar", "bar"),
778 want: TypeDef::boolean().infallible(),
779 }
780
781 less_or_equal_timestamps {
782 expr: |_| op(Le, Utc::now(), Utc::now()),
783 want: TypeDef::boolean().infallible(),
784 }
785
786 less_or_equal_other {
787 expr: |_| op(Le, 1, "baz"),
788 want: TypeDef::boolean().fallible(),
789 }
790
791 error_or_rhs_infallible {
792 expr: |_| Op {
793 lhs: Box::new(Op {
794 lhs: Box::new(Literal::from("foo").into()),
795 rhs: Box::new(Literal::from(1).into()),
796 opcode: Div,
797 }.into()),
798 rhs: Box::new(Literal::from(true).into()),
799 opcode: Err,
800 },
801 want: TypeDef::float().or_boolean(),
802 }
803
804 error_or_fallible {
805 expr: |_| Op {
806 lhs: Box::new(Op {
807 lhs: Box::new(Literal::from("foo").into()),
808 rhs: Box::new(Literal::from(NotNan::new(0.0).unwrap()).into()),
809 opcode: Div,
810 }.into()),
811 rhs: Box::new(Op {
812 lhs: Box::new(Literal::from(true).into()),
813 rhs: Box::new(Literal::from(NotNan::new(0.0).unwrap()).into()),
814 opcode: Div,
815 }.into()),
816 opcode: Err,
817 },
818 want: TypeDef::float().fallible(),
819 }
820
821 error_or_nested_infallible {
822 expr: |_| Op {
823 lhs: Box::new(Op {
824 lhs: Box::new(Literal::from("foo").into()),
825 rhs: Box::new(Literal::from(1).into()),
826 opcode: Div,
827 }.into()),
828 rhs: Box::new(Op {
829 lhs: Box::new(Op {
830 lhs: Box::new(Literal::from(true).into()),
831 rhs: Box::new(Literal::from(1).into()),
832 opcode: Div,
833 }.into()),
834 rhs: Box::new(Literal::from("foo").into()),
835 opcode: Err,
836 }.into()),
837 opcode: Err,
838 },
839 want: TypeDef::float().or_bytes(),
840 }
841
842 or_nullable {
843 expr: |_| Op {
844 lhs: Box::new(
845 IfStatement {
846 predicate: Predicate::new_unchecked(vec![Literal::from(true).into()]),
847 if_block: Block::new_scoped(vec![Literal::from("string").into()]),
848 else_block: None,
849 }.into()),
850 rhs: Box::new(Literal::from("another string").into()),
851 opcode: Or,
852 },
853 want: TypeDef::bytes(),
854 }
855
856 or_not_nullable {
857 expr: |_| Op {
858 lhs: Box::new(
859 IfStatement {
860 predicate: Predicate::new_unchecked(vec![Literal::from(true).into()]),
861 if_block: Block::new_scoped(vec![Literal::from("string").into()]),
862 else_block: Some(Block::new_scoped(vec![Literal::from(42).into()]))
863 }.into()),
864 rhs: Box::new(Literal::from("another string").into()),
865 opcode: Or,
866 },
867 want: TypeDef::bytes().or_integer(),
868 }
869 ];
870}