1use crate::ast::*;
19use crate::bytecode::*;
20use crate::compiler::exprs::{compile_expr, compile_expr_as_type};
21use crate::compiler::{Error, ExprType, Fixup, Result, SymbolPrototype, SymbolsTable};
22use crate::exec::ValueTag;
23use crate::reader::LineCol;
24use crate::syms::CallableMetadata;
25use crate::syms::SymbolKey;
26use std::borrow::Cow;
27use std::collections::HashMap;
28use std::ops::RangeInclusive;
29
30#[derive(Clone, Debug)]
32pub struct RequiredValueSyntax {
33 pub name: Cow<'static, str>,
35
36 pub vtype: ExprType,
38}
39
40#[derive(Clone, Debug)]
42pub struct RequiredRefSyntax {
43 pub name: Cow<'static, str>,
45
46 pub require_array: bool,
48
49 pub define_undefined: bool,
53}
54
55#[derive(Clone, Debug)]
59pub struct OptionalValueSyntax {
60 pub name: Cow<'static, str>,
62
63 pub vtype: ExprType,
65
66 pub missing_value: i32,
68
69 pub present_value: i32,
72}
73
74#[derive(Clone, Debug)]
76pub enum RepeatedTypeSyntax {
77 AnyValue,
80
81 TypedValue(ExprType),
83
84 VariableRef,
86}
87
88#[derive(Clone, Debug)]
92pub struct RepeatedSyntax {
93 pub name: Cow<'static, str>,
95
96 pub type_syn: RepeatedTypeSyntax,
98
99 pub sep: ArgSepSyntax,
102
103 pub require_one: bool,
105
106 pub allow_missing: bool,
108}
109
110impl RepeatedSyntax {
111 fn describe(&self, output: &mut String, last_singular_sep: Option<&ArgSepSyntax>) {
116 if !self.require_one {
117 output.push('[');
118 }
119
120 if let Some(sep) = last_singular_sep {
121 sep.describe(output);
122 }
123
124 output.push_str(&self.name);
125 output.push('1');
126 if let RepeatedTypeSyntax::TypedValue(vtype) = self.type_syn {
127 output.push(vtype.annotation());
128 }
129
130 if self.require_one {
131 output.push('[');
132 }
133
134 self.sep.describe(output);
135 output.push_str("..");
136 self.sep.describe(output);
137
138 output.push_str(&self.name);
139 output.push('N');
140 if let RepeatedTypeSyntax::TypedValue(vtype) = self.type_syn {
141 output.push(vtype.annotation());
142 }
143
144 output.push(']');
145 }
146}
147
148#[derive(Clone, Debug)]
150pub struct AnyValueSyntax {
151 pub name: Cow<'static, str>,
153
154 pub allow_missing: bool,
156}
157
158#[derive(Copy, Clone, Debug, PartialEq)]
160pub enum ArgSepSyntax {
161 Exactly(ArgSep),
163
164 OneOf(ArgSep, ArgSep),
166
167 End,
169}
170
171impl ArgSepSyntax {
172 fn describe(&self, output: &mut String) {
174 match self {
175 ArgSepSyntax::Exactly(sep) => {
176 let (text, needs_space) = sep.describe();
177
178 if !text.is_empty() && needs_space {
179 output.push(' ');
180 }
181 output.push_str(text);
182 if !text.is_empty() {
183 output.push(' ');
184 }
185 }
186
187 ArgSepSyntax::OneOf(sep1, sep2) => {
188 let (text1, _needs_space1) = sep1.describe();
189 let (text2, _needs_space2) = sep2.describe();
190
191 output.push(' ');
192 output.push_str(&format!("<{}|{}>", text1, text2));
193 output.push(' ');
194 }
195
196 ArgSepSyntax::End => (),
197 };
198 }
199}
200
201#[derive(Clone, Debug)]
206pub enum SingularArgSyntax {
207 RequiredValue(RequiredValueSyntax, ArgSepSyntax),
209
210 RequiredRef(RequiredRefSyntax, ArgSepSyntax),
212
213 OptionalValue(OptionalValueSyntax, ArgSepSyntax),
215
216 AnyValue(AnyValueSyntax, ArgSepSyntax),
218}
219
220#[derive(Clone, Debug)]
228pub(crate) struct CallableSyntax {
229 singular: Cow<'static, [SingularArgSyntax]>,
231
232 repeated: Option<Cow<'static, RepeatedSyntax>>,
234}
235
236impl CallableSyntax {
237 pub(crate) fn new_static(
240 singular: &'static [SingularArgSyntax],
241 repeated: Option<&'static RepeatedSyntax>,
242 ) -> Self {
243 Self { singular: Cow::Borrowed(singular), repeated: repeated.map(Cow::Borrowed) }
244 }
245
246 pub(crate) fn new_dynamic(
249 singular: Vec<SingularArgSyntax>,
250 repeated: Option<RepeatedSyntax>,
251 ) -> Self {
252 Self { singular: Cow::Owned(singular), repeated: repeated.map(Cow::Owned) }
253 }
254
255 fn expected_nargs(&self) -> RangeInclusive<usize> {
257 let mut min = self.singular.len();
258 let mut max = self.singular.len();
259 if let Some(syn) = self.repeated.as_ref() {
260 if syn.require_one {
261 min += 1;
262 }
263 max = usize::MAX;
264 }
265 min..=max
266 }
267
268 pub(crate) fn is_empty(&self) -> bool {
270 self.singular.is_empty() && self.repeated.is_none()
271 }
272
273 pub(crate) fn describe(&self) -> String {
275 let mut description = String::new();
276 let mut last_singular_sep = None;
277 for (i, s) in self.singular.iter().enumerate() {
278 let sep = match s {
279 SingularArgSyntax::RequiredValue(details, sep) => {
280 description.push_str(&details.name);
281 description.push(details.vtype.annotation());
282 sep
283 }
284
285 SingularArgSyntax::RequiredRef(details, sep) => {
286 description.push_str(&details.name);
287 sep
288 }
289
290 SingularArgSyntax::OptionalValue(details, sep) => {
291 description.push('[');
292 description.push_str(&details.name);
293 description.push(details.vtype.annotation());
294 description.push(']');
295 sep
296 }
297
298 SingularArgSyntax::AnyValue(details, sep) => {
299 if details.allow_missing {
300 description.push('[');
301 }
302 description.push_str(&details.name);
303 if details.allow_missing {
304 description.push(']');
305 }
306 sep
307 }
308 };
309
310 if self.repeated.is_none() || i < self.singular.len() - 1 {
311 sep.describe(&mut description);
312 }
313 if i == self.singular.len() - 1 {
314 last_singular_sep = Some(sep);
315 }
316 }
317
318 if let Some(syn) = &self.repeated {
319 syn.describe(&mut description, last_singular_sep);
320 }
321
322 description
323 }
324}
325
326fn compile_required_ref(
332 instrs: &mut Vec<Instruction>,
333 md: &CallableMetadata,
334 pos: LineCol,
335 symtable: &SymbolsTable,
336 require_array: bool,
337 define_undefined: bool,
338 expr: Option<Expr>,
339) -> Result<Option<(SymbolKey, SymbolPrototype)>> {
340 match expr {
341 Some(Expr::Symbol(span)) => {
342 let key = SymbolKey::from(&span.vref.name);
343 match symtable.get(&key) {
344 None => {
345 if !define_undefined {
346 return Err(Error::UndefinedSymbol(span.pos, span.vref));
347 }
348 debug_assert!(!require_array);
349
350 let vtype = span.vref.ref_type.unwrap_or(ExprType::Integer);
351
352 if !span.vref.accepts(vtype) {
353 return Err(Error::IncompatibleTypeAnnotationInReference(
354 span.pos, span.vref,
355 ));
356 }
357
358 instrs.push(Instruction::LoadRef(key.clone(), vtype, span.pos));
359 Ok(Some((key, SymbolPrototype::Variable(vtype))))
360 }
361
362 Some(SymbolPrototype::Array(vtype, _)) => {
363 let vtype = *vtype;
364
365 if !span.vref.accepts(vtype) {
366 return Err(Error::IncompatibleTypeAnnotationInReference(
367 span.pos, span.vref,
368 ));
369 }
370
371 if !require_array {
372 return Err(Error::NotAReference(span.pos));
373 }
374
375 instrs.push(Instruction::LoadRef(key, vtype, span.pos));
376 Ok(None)
377 }
378
379 Some(SymbolPrototype::Variable(vtype)) => {
380 let vtype = *vtype;
381
382 if !span.vref.accepts(vtype) {
383 return Err(Error::IncompatibleTypeAnnotationInReference(
384 span.pos, span.vref,
385 ));
386 }
387
388 if require_array {
389 return Err(Error::NotAReference(span.pos));
390 }
391
392 instrs.push(Instruction::LoadRef(key, vtype, span.pos));
393 Ok(None)
394 }
395
396 Some(SymbolPrototype::BuiltinCallable(md, _))
397 | Some(SymbolPrototype::Callable(md)) => {
398 if !span.vref.accepts_callable(md.return_type()) {
399 return Err(Error::IncompatibleTypeAnnotationInReference(
400 span.pos, span.vref,
401 ));
402 }
403
404 Err(Error::NotArrayOrFunction(span.pos, span.vref))
405 }
406 }
407 }
408
409 Some(expr) => Err(Error::NotAReference(expr.start_pos())),
410
411 None => Err(Error::CallableSyntaxError(pos, md.clone())),
412 }
413}
414
415fn find_syntax(md: &CallableMetadata, pos: LineCol, nargs: usize) -> Result<&CallableSyntax> {
419 let mut matches = md.syntaxes().iter().filter(|s| s.expected_nargs().contains(&nargs));
420 let syntax = matches.next();
421 match syntax {
422 Some(syntax) => {
423 debug_assert!(matches.next().is_none(), "Ambiguous syntax definitions");
424 Ok(syntax)
425 }
426 None => Err(Error::CallableSyntaxError(pos, md.clone())),
427 }
428}
429
430#[allow(clippy::too_many_arguments)]
442fn compile_syn_argsep(
443 instrs: &mut Vec<Instruction>,
444 md: &CallableMetadata,
445 syn: &ArgSepSyntax,
446 is_last: bool,
447 sep: ArgSep,
448 sep_pos: LineCol,
449 sep_tag_pc: Address,
450) -> Result<usize> {
451 debug_assert!(
452 (!is_last || sep == ArgSep::End) && (is_last || sep != ArgSep::End),
453 "Parser can only supply an End separator in the last argument"
454 );
455
456 match syn {
457 ArgSepSyntax::Exactly(exp_sep) => {
458 debug_assert!(*exp_sep != ArgSep::End, "Use ArgSepSyntax::End");
459 if sep != ArgSep::End && sep != *exp_sep {
460 return Err(Error::CallableSyntaxError(sep_pos, md.clone()));
461 }
462 Ok(0)
463 }
464
465 ArgSepSyntax::OneOf(exp_sep1, exp_sep2) => {
466 debug_assert!(*exp_sep1 != ArgSep::End, "Use ArgSepSyntax::End");
467 debug_assert!(*exp_sep2 != ArgSep::End, "Use ArgSepSyntax::End");
468 if sep == ArgSep::End {
469 Ok(0)
470 } else {
471 if sep != *exp_sep1 && sep != *exp_sep2 {
472 return Err(Error::CallableSyntaxError(sep_pos, md.clone()));
473 }
474 instrs.insert(sep_tag_pc, Instruction::PushInteger(sep as i32, sep_pos));
475 Ok(1)
476 }
477 }
478
479 ArgSepSyntax::End => {
480 debug_assert!(is_last);
481 Ok(0)
482 }
483 }
484}
485
486fn compile_args(
491 md: &CallableMetadata,
492 instrs: &mut Vec<Instruction>,
493 fixups: &mut HashMap<Address, Fixup>,
494 symtable: &SymbolsTable,
495 pos: LineCol,
496 args: Vec<ArgSpan>,
497) -> Result<(usize, Vec<(SymbolKey, SymbolPrototype)>)> {
498 let syntax = find_syntax(md, pos, args.len())?;
499
500 let input_nargs = args.len();
501 let mut aiter = args.into_iter().rev();
502
503 let mut nargs = 0;
504 let mut to_insert = vec![];
505
506 let mut remaining;
507 if let Some(syn) = syntax.repeated.as_ref() {
508 let mut min_nargs = syntax.singular.len();
509 if syn.require_one {
510 min_nargs += 1;
511 }
512 if input_nargs < min_nargs {
513 return Err(Error::CallableSyntaxError(pos, md.clone()));
514 }
515
516 let need_tags = syn.allow_missing || matches!(syn.type_syn, RepeatedTypeSyntax::AnyValue);
517
518 remaining = input_nargs;
519 while remaining > syntax.singular.len() {
520 let span = aiter.next().expect("Args and their syntax must advance in unison");
521
522 let sep_tag_pc = instrs.len();
523
524 match span.expr {
525 Some(expr) => {
526 let pos = expr.start_pos();
527 match syn.type_syn {
528 RepeatedTypeSyntax::AnyValue => {
529 debug_assert!(need_tags);
530 let etype = compile_expr(instrs, fixups, symtable, expr, false)?;
531 instrs
532 .push(Instruction::PushInteger(ValueTag::from(etype) as i32, pos));
533 nargs += 2;
534 }
535
536 RepeatedTypeSyntax::VariableRef => {
537 let to_insert_one = compile_required_ref(
538 instrs,
539 md,
540 pos,
541 symtable,
542 false,
543 true,
544 Some(expr),
545 )?;
546 if let Some(to_insert_one) = to_insert_one {
547 to_insert.push(to_insert_one);
548 }
549 nargs += 1;
550 }
551
552 RepeatedTypeSyntax::TypedValue(vtype) => {
553 compile_expr_as_type(instrs, fixups, symtable, expr, vtype)?;
554 if need_tags {
555 instrs.push(Instruction::PushInteger(
556 ValueTag::from(vtype) as i32,
557 pos,
558 ));
559 nargs += 2;
560 } else {
561 nargs += 1;
562 }
563 }
564 }
565 }
566 None => {
567 if !syn.allow_missing {
568 return Err(Error::CallableSyntaxError(pos, md.clone()));
569 }
570 instrs.push(Instruction::PushInteger(ValueTag::Missing as i32, span.sep_pos));
571 nargs += 1;
572 }
573 }
574
575 nargs += compile_syn_argsep(
576 instrs,
577 md,
578 &syn.sep,
579 input_nargs == remaining,
580 span.sep,
581 span.sep_pos,
582 sep_tag_pc,
583 )?;
584
585 remaining -= 1;
586 }
587 } else {
588 remaining = syntax.singular.len();
589 }
590
591 for syn in syntax.singular.iter().rev() {
592 let span = aiter.next().expect("Args and their syntax must advance in unison");
593
594 let sep_tag_pc = instrs.len();
595
596 let exp_sep = match syn {
597 SingularArgSyntax::RequiredValue(details, sep) => {
598 match span.expr {
599 Some(expr) => {
600 compile_expr_as_type(instrs, fixups, symtable, expr, details.vtype)?;
601 nargs += 1;
602 }
603 None => return Err(Error::CallableSyntaxError(pos, md.clone())),
604 }
605 sep
606 }
607
608 SingularArgSyntax::RequiredRef(details, sep) => {
609 let to_insert_one = compile_required_ref(
610 instrs,
611 md,
612 pos,
613 symtable,
614 details.require_array,
615 details.define_undefined,
616 span.expr,
617 )?;
618 if let Some(to_insert_one) = to_insert_one {
619 to_insert.push(to_insert_one);
620 }
621 nargs += 1;
622 sep
623 }
624
625 SingularArgSyntax::OptionalValue(details, sep) => {
626 let (tag, pos) = match span.expr {
627 Some(expr) => {
628 let pos = expr.start_pos();
629 compile_expr_as_type(instrs, fixups, symtable, expr, details.vtype)?;
630 nargs += 1;
631 (details.present_value, pos)
632 }
633 None => (details.missing_value, span.sep_pos),
634 };
635 instrs.push(Instruction::PushInteger(tag, pos));
636 nargs += 1;
637 sep
638 }
639
640 SingularArgSyntax::AnyValue(details, sep) => {
641 let (tag, pos) = match span.expr {
642 Some(expr) => {
643 let pos = expr.start_pos();
644 let etype = compile_expr(instrs, fixups, symtable, expr, false)?;
645 nargs += 2;
646 (ValueTag::from(etype), pos)
647 }
648 None => {
649 if !details.allow_missing {
650 return Err(Error::CallableSyntaxError(span.sep_pos, md.clone()));
651 }
652 nargs += 1;
653 (ValueTag::Missing, span.sep_pos)
654 }
655 };
656 instrs.push(Instruction::PushInteger(tag as i32, pos));
657 sep
658 }
659 };
660
661 nargs += compile_syn_argsep(
662 instrs,
663 md,
664 exp_sep,
665 input_nargs == remaining,
666 span.sep,
667 span.sep_pos,
668 sep_tag_pc,
669 )?;
670
671 remaining -= 1;
672 }
673
674 Ok((nargs, to_insert))
675}
676
677pub(super) fn compile_command_args(
682 md: &CallableMetadata,
683 instrs: &mut Vec<Instruction>,
684 fixups: &mut HashMap<Address, Fixup>,
685 symtable: &mut SymbolsTable,
686 pos: LineCol,
687 args: Vec<ArgSpan>,
688) -> Result<usize> {
689 let (nargs, to_insert) = compile_args(md, instrs, fixups, symtable, pos, args)?;
690 for (key, proto) in to_insert {
691 if !symtable.contains_key(&key) {
692 symtable.insert(key, proto);
693 }
694 }
695 Ok(nargs)
696}
697
698pub(super) fn compile_function_args(
703 md: &CallableMetadata,
704 instrs: &mut Vec<Instruction>,
705 fixups: &mut HashMap<Address, Fixup>,
706 symtable: &SymbolsTable,
707 pos: LineCol,
708 args: Vec<ArgSpan>,
709) -> Result<usize> {
710 let (nargs, to_insert) = compile_args(md, instrs, fixups, symtable, pos, args)?;
711 debug_assert!(to_insert.is_empty());
712 Ok(nargs)
713}
714
715#[cfg(test)]
716mod testutils {
717 use crate::syms::CallableMetadataBuilder;
718
719 use super::*;
720 use std::collections::HashMap;
721
722 pub(super) fn lc(line: usize, col: usize) -> LineCol {
724 LineCol { line, col }
725 }
726
727 #[derive(Default)]
729 #[must_use]
730 pub(super) struct Tester {
731 syntaxes: Vec<CallableSyntax>,
732 symtable: SymbolsTable,
733 }
734
735 impl Tester {
736 pub(super) fn syntax(
738 mut self,
739 singular: &'static [SingularArgSyntax],
740 repeated: Option<&'static RepeatedSyntax>,
741 ) -> Self {
742 self.syntaxes.push(CallableSyntax::new_static(singular, repeated));
743 self
744 }
745
746 pub(super) fn symbol(mut self, key: &str, proto: SymbolPrototype) -> Self {
748 self.symtable.insert(SymbolKey::from(key), proto);
749 self
750 }
751
752 pub(super) fn compile_command<A: Into<Vec<ArgSpan>>>(mut self, args: A) -> Checker {
755 let args = args.into();
756 let mut instrs = vec![
757 Instruction::Nop,
759 ];
760 let mut fixups = HashMap::default();
761 let md = CallableMetadataBuilder::new("TEST").with_syntaxes(self.syntaxes).test_build();
762 let result = compile_command_args(
763 &md,
764 &mut instrs,
765 &mut fixups,
766 &mut self.symtable,
767 lc(1000, 2000),
768 args,
769 );
770 Checker {
771 result,
772 instrs,
773 fixups,
774 symtable: self.symtable,
775 exp_result: Ok(0),
776 exp_instrs: vec![Instruction::Nop],
777 exp_fixups: HashMap::default(),
778 exp_vars: HashMap::default(),
779 }
780 }
781 }
782
783 #[must_use]
785 pub(super) struct Checker {
786 result: Result<usize>,
787 instrs: Vec<Instruction>,
788 fixups: HashMap<Address, Fixup>,
789 symtable: SymbolsTable,
790 exp_result: Result<usize>,
791 exp_instrs: Vec<Instruction>,
792 exp_fixups: HashMap<Address, Fixup>,
793 exp_vars: HashMap<SymbolKey, ExprType>,
794 }
795
796 impl Checker {
797 pub(super) fn exp_nargs(mut self, nargs: usize) -> Self {
799 self.exp_result = Ok(nargs);
800 self
801 }
802
803 pub(super) fn exp_error(mut self, error: Error) -> Self {
805 self.exp_result = Err(error);
806 self
807 }
808
809 pub(super) fn exp_instr(mut self, instr: Instruction) -> Self {
811 self.exp_instrs.push(instr);
812 self
813 }
814
815 pub(super) fn exp_symbol<K: AsRef<str>>(mut self, key: K, etype: ExprType) -> Self {
817 self.exp_vars.insert(SymbolKey::from(key), etype);
818 self
819 }
820
821 pub(super) fn check(self) {
823 let is_ok = self.result.is_ok();
824 assert_eq!(
825 self.exp_result.map_err(|e| format!("{}", e)),
826 self.result.map_err(|e| format!("{}", e)),
827 );
828
829 if !is_ok {
830 return;
831 }
832
833 assert_eq!(self.exp_instrs, self.instrs);
834 assert_eq!(self.exp_fixups, self.fixups);
835
836 let mut exp_keys = self.symtable.keys();
837 for (key, exp_etype) in &self.exp_vars {
838 match self.symtable.get(key) {
839 Some(SymbolPrototype::Variable(etype)) => {
840 assert_eq!(
841 exp_etype, etype,
842 "Variable {} was defined with the wrong type",
843 key
844 );
845 }
846 Some(_) => panic!("Symbol {} was defined but not as a variable", key),
847 None => panic!("Symbol {} was not defined", key),
848 }
849 exp_keys.insert(key);
850 }
851
852 assert_eq!(exp_keys, self.symtable.keys(), "Unexpected variables defined");
853 }
854 }
855}
856
857#[cfg(test)]
858mod description_tests {
859 use super::*;
860
861 #[test]
862 fn test_no_args() {
863 assert_eq!("", CallableSyntax::new_static(&[], None).describe());
864 }
865
866 #[test]
867 fn test_singular_required_value() {
868 assert_eq!(
869 "the-arg%",
870 CallableSyntax::new_static(
871 &[SingularArgSyntax::RequiredValue(
872 RequiredValueSyntax {
873 name: Cow::Borrowed("the-arg"),
874 vtype: ExprType::Integer
875 },
876 ArgSepSyntax::End,
877 )],
878 None,
879 )
880 .describe(),
881 );
882 }
883
884 #[test]
885 fn test_singular_required_ref() {
886 assert_eq!(
887 "the-arg",
888 CallableSyntax::new_static(
889 &[SingularArgSyntax::RequiredRef(
890 RequiredRefSyntax {
891 name: Cow::Borrowed("the-arg"),
892 require_array: false,
893 define_undefined: false
894 },
895 ArgSepSyntax::End,
896 )],
897 None,
898 )
899 .describe()
900 );
901 }
902
903 #[test]
904 fn test_singular_optional_value() {
905 assert_eq!(
906 "[the-arg%]",
907 CallableSyntax::new_static(
908 &[SingularArgSyntax::OptionalValue(
909 OptionalValueSyntax {
910 name: Cow::Borrowed("the-arg"),
911 vtype: ExprType::Integer,
912 missing_value: 0,
913 present_value: 1,
914 },
915 ArgSepSyntax::End,
916 )],
917 None,
918 )
919 .describe()
920 );
921 }
922
923 #[test]
924 fn test_singular_any_value_required() {
925 assert_eq!(
926 "the-arg",
927 CallableSyntax::new_static(
928 &[SingularArgSyntax::AnyValue(
929 AnyValueSyntax { name: Cow::Borrowed("the-arg"), allow_missing: false },
930 ArgSepSyntax::End,
931 )],
932 None,
933 )
934 .describe()
935 );
936 }
937
938 #[test]
939 fn test_singular_any_value_optional() {
940 assert_eq!(
941 "[the-arg]",
942 CallableSyntax::new_static(
943 &[SingularArgSyntax::AnyValue(
944 AnyValueSyntax { name: Cow::Borrowed("the-arg"), allow_missing: true },
945 ArgSepSyntax::End,
946 )],
947 None,
948 )
949 .describe()
950 );
951 }
952
953 #[test]
954 fn test_singular_exactly_separators() {
955 assert_eq!(
956 "a; b AS c, d",
957 CallableSyntax::new_static(
958 &[
959 SingularArgSyntax::AnyValue(
960 AnyValueSyntax { name: Cow::Borrowed("a"), allow_missing: false },
961 ArgSepSyntax::Exactly(ArgSep::Short),
962 ),
963 SingularArgSyntax::AnyValue(
964 AnyValueSyntax { name: Cow::Borrowed("b"), allow_missing: false },
965 ArgSepSyntax::Exactly(ArgSep::As),
966 ),
967 SingularArgSyntax::AnyValue(
968 AnyValueSyntax { name: Cow::Borrowed("c"), allow_missing: false },
969 ArgSepSyntax::Exactly(ArgSep::Long),
970 ),
971 SingularArgSyntax::AnyValue(
972 AnyValueSyntax { name: Cow::Borrowed("d"), allow_missing: false },
973 ArgSepSyntax::Exactly(ArgSep::End),
974 ),
975 ],
976 None,
977 )
978 .describe()
979 );
980 }
981
982 #[test]
983 fn test_singular_oneof_separators() {
984 assert_eq!(
985 "a <;|,> b <AS|,> c",
986 CallableSyntax::new_static(
987 &[
988 SingularArgSyntax::AnyValue(
989 AnyValueSyntax { name: Cow::Borrowed("a"), allow_missing: false },
990 ArgSepSyntax::OneOf(ArgSep::Short, ArgSep::Long),
991 ),
992 SingularArgSyntax::AnyValue(
993 AnyValueSyntax { name: Cow::Borrowed("b"), allow_missing: false },
994 ArgSepSyntax::OneOf(ArgSep::As, ArgSep::Long),
995 ),
996 SingularArgSyntax::AnyValue(
997 AnyValueSyntax { name: Cow::Borrowed("c"), allow_missing: false },
998 ArgSepSyntax::Exactly(ArgSep::End),
999 ),
1000 ],
1001 None,
1002 )
1003 .describe()
1004 );
1005 }
1006
1007 #[test]
1008 fn test_repeated_require_one() {
1009 assert_eq!(
1010 "rep1[; ..; repN]",
1011 CallableSyntax::new_static(
1012 &[],
1013 Some(&RepeatedSyntax {
1014 name: Cow::Borrowed("rep"),
1015 type_syn: RepeatedTypeSyntax::AnyValue,
1016 sep: ArgSepSyntax::Exactly(ArgSep::Short),
1017 require_one: true,
1018 allow_missing: false,
1019 }),
1020 )
1021 .describe()
1022 );
1023 }
1024
1025 #[test]
1026 fn test_repeated_allow_missing() {
1027 assert_eq!(
1028 "[rep1, .., repN]",
1029 CallableSyntax::new_static(
1030 &[],
1031 Some(&RepeatedSyntax {
1032 name: Cow::Borrowed("rep"),
1033 type_syn: RepeatedTypeSyntax::AnyValue,
1034 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1035 require_one: false,
1036 allow_missing: true,
1037 }),
1038 )
1039 .describe()
1040 );
1041 }
1042
1043 #[test]
1044 fn test_repeated_value() {
1045 assert_eq!(
1046 "rep1$[ AS .. AS repN$]",
1047 CallableSyntax::new_static(
1048 &[],
1049 Some(&RepeatedSyntax {
1050 name: Cow::Borrowed("rep"),
1051 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Text),
1052 sep: ArgSepSyntax::Exactly(ArgSep::As),
1053 require_one: true,
1054 allow_missing: false,
1055 }),
1056 )
1057 .describe()
1058 );
1059 }
1060
1061 #[test]
1062 fn test_repeated_ref() {
1063 assert_eq!(
1064 "rep1[ AS .. AS repN]",
1065 CallableSyntax::new_static(
1066 &[],
1067 Some(&RepeatedSyntax {
1068 name: Cow::Borrowed("rep"),
1069 type_syn: RepeatedTypeSyntax::VariableRef,
1070 sep: ArgSepSyntax::Exactly(ArgSep::As),
1071 require_one: true,
1072 allow_missing: false,
1073 }),
1074 )
1075 .describe()
1076 );
1077 }
1078
1079 #[test]
1080 fn test_singular_and_repeated() {
1081 assert_eq!(
1082 "arg%[, rep1 <;|,> .. <;|,> repN]",
1083 CallableSyntax::new_static(
1084 &[SingularArgSyntax::RequiredValue(
1085 RequiredValueSyntax { name: Cow::Borrowed("arg"), vtype: ExprType::Integer },
1086 ArgSepSyntax::Exactly(ArgSep::Long),
1087 )],
1088 Some(&RepeatedSyntax {
1089 name: Cow::Borrowed("rep"),
1090 type_syn: RepeatedTypeSyntax::AnyValue,
1091 sep: ArgSepSyntax::OneOf(ArgSep::Short, ArgSep::Long),
1092 require_one: false,
1093 allow_missing: false,
1094 }),
1095 )
1096 .describe()
1097 );
1098 }
1099}
1100
1101#[cfg(test)]
1102mod compile_tests {
1103 use super::testutils::*;
1104 use super::*;
1105 use crate::syms::CallableMetadataBuilder;
1106
1107 #[test]
1108 fn test_no_args_ok() {
1109 Tester::default().syntax(&[], None).compile_command([]).check();
1110 }
1111
1112 #[test]
1113 fn test_no_args_mismatch() {
1114 Tester::default()
1115 .syntax(&[], None)
1116 .compile_command([ArgSpan {
1117 expr: Some(Expr::Integer(IntegerSpan { value: 3, pos: lc(1, 2) })),
1118 sep: ArgSep::End,
1119 sep_pos: lc(1, 3),
1120 }])
1121 .exp_error(Error::CallableSyntaxError(
1122 lc(1000, 2000),
1123 CallableMetadataBuilder::new("TEST").test_build(),
1124 ))
1125 .check();
1126 }
1127
1128 #[test]
1129 fn test_one_required_value_ok() {
1130 Tester::default()
1131 .syntax(
1132 &[SingularArgSyntax::RequiredValue(
1133 RequiredValueSyntax { name: Cow::Borrowed("arg1"), vtype: ExprType::Integer },
1134 ArgSepSyntax::End,
1135 )],
1136 None,
1137 )
1138 .compile_command([ArgSpan {
1139 expr: Some(Expr::Integer(IntegerSpan { value: 3, pos: lc(1, 2) })),
1140 sep: ArgSep::End,
1141 sep_pos: lc(1, 3),
1142 }])
1143 .exp_instr(Instruction::PushInteger(3, lc(1, 2)))
1144 .exp_nargs(1)
1145 .check();
1146 }
1147
1148 #[test]
1149 fn test_one_required_value_type_promotion() {
1150 Tester::default()
1151 .syntax(
1152 &[SingularArgSyntax::RequiredValue(
1153 RequiredValueSyntax { name: Cow::Borrowed("arg1"), vtype: ExprType::Integer },
1154 ArgSepSyntax::End,
1155 )],
1156 None,
1157 )
1158 .compile_command([ArgSpan {
1159 expr: Some(Expr::Double(DoubleSpan { value: 3.0, pos: lc(1, 2) })),
1160 sep: ArgSep::End,
1161 sep_pos: lc(1, 5),
1162 }])
1163 .exp_instr(Instruction::PushDouble(3.0, lc(1, 2)))
1164 .exp_instr(Instruction::DoubleToInteger)
1165 .exp_nargs(1)
1166 .check();
1167 }
1168
1169 #[test]
1170 fn test_one_required_ref_variable_ok() {
1171 Tester::default()
1172 .symbol("foo", SymbolPrototype::Variable(ExprType::Text))
1173 .syntax(
1174 &[SingularArgSyntax::RequiredRef(
1175 RequiredRefSyntax {
1176 name: Cow::Borrowed("ref"),
1177 require_array: false,
1178 define_undefined: false,
1179 },
1180 ArgSepSyntax::End,
1181 )],
1182 None,
1183 )
1184 .compile_command([ArgSpan {
1185 expr: Some(Expr::Symbol(SymbolSpan {
1186 vref: VarRef::new("foo", None),
1187 pos: lc(1, 2),
1188 })),
1189 sep: ArgSep::End,
1190 sep_pos: lc(1, 5),
1191 }])
1192 .exp_instr(Instruction::LoadRef(SymbolKey::from("foo"), ExprType::Text, lc(1, 2)))
1193 .exp_nargs(1)
1194 .check();
1195 }
1196
1197 #[test]
1198 fn test_one_required_ref_variable_not_defined() {
1199 Tester::default()
1200 .syntax(
1201 &[SingularArgSyntax::RequiredRef(
1202 RequiredRefSyntax {
1203 name: Cow::Borrowed("ref"),
1204 require_array: false,
1205 define_undefined: false,
1206 },
1207 ArgSepSyntax::End,
1208 )],
1209 None,
1210 )
1211 .compile_command([ArgSpan {
1212 expr: Some(Expr::Symbol(SymbolSpan {
1213 vref: VarRef::new("foo", None),
1214 pos: lc(1, 2),
1215 })),
1216 sep: ArgSep::End,
1217 sep_pos: lc(1, 5),
1218 }])
1219 .exp_error(Error::UndefinedSymbol(lc(1, 2), VarRef::new("foo", None)))
1220 .check();
1221 }
1222
1223 #[test]
1224 fn test_one_required_ref_variable_disallow_value() {
1225 Tester::default()
1226 .syntax(
1227 &[SingularArgSyntax::RequiredRef(
1228 RequiredRefSyntax {
1229 name: Cow::Borrowed("ref"),
1230 require_array: false,
1231 define_undefined: false,
1232 },
1233 ArgSepSyntax::End,
1234 )],
1235 None,
1236 )
1237 .compile_command([ArgSpan {
1238 expr: Some(Expr::Integer(IntegerSpan { value: 5, pos: lc(1, 2) })),
1239 sep: ArgSep::End,
1240 sep_pos: lc(1, 5),
1241 }])
1242 .exp_error(Error::NotAReference(lc(1, 2)))
1243 .check();
1244 }
1245
1246 #[test]
1247 fn test_one_required_ref_variable_wrong_type() {
1248 Tester::default()
1249 .symbol("foo", SymbolPrototype::Array(ExprType::Text, 1))
1250 .syntax(
1251 &[SingularArgSyntax::RequiredRef(
1252 RequiredRefSyntax {
1253 name: Cow::Borrowed("ref"),
1254 require_array: false,
1255 define_undefined: false,
1256 },
1257 ArgSepSyntax::End,
1258 )],
1259 None,
1260 )
1261 .compile_command([ArgSpan {
1262 expr: Some(Expr::Symbol(SymbolSpan {
1263 vref: VarRef::new("foo", None),
1264 pos: lc(1, 2),
1265 })),
1266 sep: ArgSep::End,
1267 sep_pos: lc(1, 5),
1268 }])
1269 .exp_error(Error::NotAReference(lc(1, 2)))
1270 .check();
1271 }
1272
1273 #[test]
1274 fn test_one_required_ref_variable_wrong_annotation() {
1275 Tester::default()
1276 .symbol("foo", SymbolPrototype::Variable(ExprType::Text))
1277 .syntax(
1278 &[SingularArgSyntax::RequiredRef(
1279 RequiredRefSyntax {
1280 name: Cow::Borrowed("ref"),
1281 require_array: false,
1282 define_undefined: false,
1283 },
1284 ArgSepSyntax::End,
1285 )],
1286 None,
1287 )
1288 .compile_command([ArgSpan {
1289 expr: Some(Expr::Symbol(SymbolSpan {
1290 vref: VarRef::new("foo", Some(ExprType::Integer)),
1291 pos: lc(1, 2),
1292 })),
1293 sep: ArgSep::End,
1294 sep_pos: lc(1, 5),
1295 }])
1296 .exp_error(Error::IncompatibleTypeAnnotationInReference(
1297 lc(1, 2),
1298 VarRef::new("foo", Some(ExprType::Integer)),
1299 ))
1300 .check();
1301 }
1302
1303 #[test]
1304 fn test_one_required_ref_variable_define_undefined_default_type() {
1305 Tester::default()
1306 .syntax(
1307 &[SingularArgSyntax::RequiredRef(
1308 RequiredRefSyntax {
1309 name: Cow::Borrowed("ref"),
1310 require_array: false,
1311 define_undefined: true,
1312 },
1313 ArgSepSyntax::End,
1314 )],
1315 None,
1316 )
1317 .compile_command([ArgSpan {
1318 expr: Some(Expr::Symbol(SymbolSpan {
1319 vref: VarRef::new("foo", None),
1320 pos: lc(1, 2),
1321 })),
1322 sep: ArgSep::End,
1323 sep_pos: lc(1, 5),
1324 }])
1325 .exp_instr(Instruction::LoadRef(SymbolKey::from("foo"), ExprType::Integer, lc(1, 2)))
1326 .exp_nargs(1)
1327 .exp_symbol("foo", ExprType::Integer)
1328 .check();
1329 }
1330
1331 #[test]
1332 fn test_one_required_ref_variable_define_undefined_explicit_type() {
1333 Tester::default()
1334 .syntax(
1335 &[SingularArgSyntax::RequiredRef(
1336 RequiredRefSyntax {
1337 name: Cow::Borrowed("ref"),
1338 require_array: false,
1339 define_undefined: true,
1340 },
1341 ArgSepSyntax::End,
1342 )],
1343 None,
1344 )
1345 .compile_command([ArgSpan {
1346 expr: Some(Expr::Symbol(SymbolSpan {
1347 vref: VarRef::new("foo", Some(ExprType::Text)),
1348 pos: lc(1, 2),
1349 })),
1350 sep: ArgSep::End,
1351 sep_pos: lc(1, 6),
1352 }])
1353 .exp_instr(Instruction::LoadRef(SymbolKey::from("foo"), ExprType::Text, lc(1, 2)))
1354 .exp_nargs(1)
1355 .exp_symbol("foo", ExprType::Text)
1356 .check();
1357 }
1358
1359 #[test]
1360 fn test_multiple_required_ref_variable_define_undefined_repeated_ok() {
1361 Tester::default()
1362 .syntax(
1363 &[
1364 SingularArgSyntax::RequiredRef(
1365 RequiredRefSyntax {
1366 name: Cow::Borrowed("ref1"),
1367 require_array: false,
1368 define_undefined: true,
1369 },
1370 ArgSepSyntax::Exactly(ArgSep::Long),
1371 ),
1372 SingularArgSyntax::RequiredRef(
1373 RequiredRefSyntax {
1374 name: Cow::Borrowed("ref2"),
1375 require_array: false,
1376 define_undefined: true,
1377 },
1378 ArgSepSyntax::End,
1379 ),
1380 ],
1381 None,
1382 )
1383 .compile_command([
1384 ArgSpan {
1385 expr: Some(Expr::Symbol(SymbolSpan {
1386 vref: VarRef::new("foo", None),
1387 pos: lc(1, 2),
1388 })),
1389 sep: ArgSep::Long,
1390 sep_pos: lc(1, 5),
1391 },
1392 ArgSpan {
1393 expr: Some(Expr::Symbol(SymbolSpan {
1394 vref: VarRef::new("foo", None),
1395 pos: lc(1, 2),
1396 })),
1397 sep: ArgSep::End,
1398 sep_pos: lc(1, 5),
1399 },
1400 ])
1401 .exp_instr(Instruction::LoadRef(SymbolKey::from("foo"), ExprType::Integer, lc(1, 2)))
1402 .exp_instr(Instruction::LoadRef(SymbolKey::from("foo"), ExprType::Integer, lc(1, 2)))
1403 .exp_nargs(2)
1404 .exp_symbol("foo", ExprType::Integer)
1405 .check();
1406 }
1407
1408 #[test]
1409 fn test_one_required_ref_array_ok() {
1410 Tester::default()
1411 .symbol("foo", SymbolPrototype::Array(ExprType::Text, 0))
1412 .syntax(
1413 &[SingularArgSyntax::RequiredRef(
1414 RequiredRefSyntax {
1415 name: Cow::Borrowed("ref"),
1416 require_array: true,
1417 define_undefined: false,
1418 },
1419 ArgSepSyntax::End,
1420 )],
1421 None,
1422 )
1423 .compile_command([ArgSpan {
1424 expr: Some(Expr::Symbol(SymbolSpan {
1425 vref: VarRef::new("foo", None),
1426 pos: lc(1, 2),
1427 })),
1428 sep: ArgSep::End,
1429 sep_pos: lc(1, 5),
1430 }])
1431 .exp_instr(Instruction::LoadRef(SymbolKey::from("foo"), ExprType::Text, lc(1, 2)))
1432 .exp_nargs(1)
1433 .check();
1434 }
1435
1436 #[test]
1437 fn test_one_required_ref_array_not_defined() {
1438 Tester::default()
1439 .syntax(
1440 &[SingularArgSyntax::RequiredRef(
1441 RequiredRefSyntax {
1442 name: Cow::Borrowed("ref"),
1443 require_array: true,
1444 define_undefined: false,
1445 },
1446 ArgSepSyntax::End,
1447 )],
1448 None,
1449 )
1450 .compile_command([ArgSpan {
1451 expr: Some(Expr::Symbol(SymbolSpan {
1452 vref: VarRef::new("foo", None),
1453 pos: lc(1, 2),
1454 })),
1455 sep: ArgSep::End,
1456 sep_pos: lc(1, 5),
1457 }])
1458 .exp_error(Error::UndefinedSymbol(lc(1, 2), VarRef::new("foo", None)))
1459 .check();
1460 }
1461
1462 #[test]
1463 fn test_one_required_ref_array_disallow_value() {
1464 Tester::default()
1465 .syntax(
1466 &[SingularArgSyntax::RequiredRef(
1467 RequiredRefSyntax {
1468 name: Cow::Borrowed("ref"),
1469 require_array: true,
1470 define_undefined: false,
1471 },
1472 ArgSepSyntax::End,
1473 )],
1474 None,
1475 )
1476 .compile_command([ArgSpan {
1477 expr: Some(Expr::Integer(IntegerSpan { value: 5, pos: lc(1, 2) })),
1478 sep: ArgSep::End,
1479 sep_pos: lc(1, 5),
1480 }])
1481 .exp_error(Error::NotAReference(lc(1, 2)))
1482 .check();
1483 }
1484
1485 #[test]
1486 fn test_one_required_ref_array_wrong_type() {
1487 Tester::default()
1488 .symbol("foo", SymbolPrototype::Variable(ExprType::Text))
1489 .syntax(
1490 &[SingularArgSyntax::RequiredRef(
1491 RequiredRefSyntax {
1492 name: Cow::Borrowed("ref"),
1493 require_array: true,
1494 define_undefined: false,
1495 },
1496 ArgSepSyntax::End,
1497 )],
1498 None,
1499 )
1500 .compile_command([ArgSpan {
1501 expr: Some(Expr::Symbol(SymbolSpan {
1502 vref: VarRef::new("foo", None),
1503 pos: lc(1, 2),
1504 })),
1505 sep: ArgSep::End,
1506 sep_pos: lc(1, 5),
1507 }])
1508 .exp_error(Error::NotAReference(lc(1, 2)))
1509 .check();
1510 }
1511
1512 #[test]
1513 fn test_one_required_ref_array_wrong_annotation() {
1514 Tester::default()
1515 .symbol("foo", SymbolPrototype::Array(ExprType::Text, 0))
1516 .syntax(
1517 &[SingularArgSyntax::RequiredRef(
1518 RequiredRefSyntax {
1519 name: Cow::Borrowed("ref"),
1520 require_array: true,
1521 define_undefined: false,
1522 },
1523 ArgSepSyntax::End,
1524 )],
1525 None,
1526 )
1527 .compile_command([ArgSpan {
1528 expr: Some(Expr::Symbol(SymbolSpan {
1529 vref: VarRef::new("foo", Some(ExprType::Integer)),
1530 pos: lc(1, 2),
1531 })),
1532 sep: ArgSep::End,
1533 sep_pos: lc(1, 5),
1534 }])
1535 .exp_error(Error::IncompatibleTypeAnnotationInReference(
1536 lc(1, 2),
1537 VarRef::new("foo", Some(ExprType::Integer)),
1538 ))
1539 .check();
1540 }
1541
1542 #[test]
1543 fn test_one_optional_value_ok_is_present() {
1544 Tester::default()
1545 .syntax(
1546 &[SingularArgSyntax::OptionalValue(
1547 OptionalValueSyntax {
1548 name: Cow::Borrowed("ref"),
1549 vtype: ExprType::Double,
1550 missing_value: 10,
1551 present_value: 20,
1552 },
1553 ArgSepSyntax::End,
1554 )],
1555 None,
1556 )
1557 .compile_command([ArgSpan {
1558 expr: Some(Expr::Double(DoubleSpan { value: 3.0, pos: lc(1, 2) })),
1559 sep: ArgSep::End,
1560 sep_pos: lc(1, 5),
1561 }])
1562 .exp_instr(Instruction::PushDouble(3.0, lc(1, 2)))
1563 .exp_instr(Instruction::PushInteger(20, lc(1, 2)))
1564 .exp_nargs(2)
1565 .check();
1566 }
1567
1568 #[test]
1569 fn test_one_optional_value_ok_is_missing() {
1570 Tester::default()
1571 .syntax(
1572 &[SingularArgSyntax::OptionalValue(
1573 OptionalValueSyntax {
1574 name: Cow::Borrowed("ref"),
1575 vtype: ExprType::Double,
1576 missing_value: 10,
1577 present_value: 20,
1578 },
1579 ArgSepSyntax::End,
1580 )],
1581 None,
1582 )
1583 .compile_command([ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 2) }])
1584 .exp_instr(Instruction::PushInteger(10, lc(1, 2)))
1585 .exp_nargs(1)
1586 .check();
1587 }
1588
1589 #[test]
1590 fn test_multiple_any_value_ok() {
1591 Tester::default()
1592 .syntax(
1593 &[
1594 SingularArgSyntax::AnyValue(
1595 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: false },
1596 ArgSepSyntax::Exactly(ArgSep::Long),
1597 ),
1598 SingularArgSyntax::AnyValue(
1599 AnyValueSyntax { name: Cow::Borrowed("arg2"), allow_missing: false },
1600 ArgSepSyntax::Exactly(ArgSep::Long),
1601 ),
1602 SingularArgSyntax::AnyValue(
1603 AnyValueSyntax { name: Cow::Borrowed("arg3"), allow_missing: false },
1604 ArgSepSyntax::Exactly(ArgSep::Long),
1605 ),
1606 SingularArgSyntax::AnyValue(
1607 AnyValueSyntax { name: Cow::Borrowed("arg4"), allow_missing: false },
1608 ArgSepSyntax::End,
1609 ),
1610 ],
1611 None,
1612 )
1613 .compile_command([
1614 ArgSpan {
1615 expr: Some(Expr::Boolean(BooleanSpan { value: false, pos: lc(1, 2) })),
1616 sep: ArgSep::Long,
1617 sep_pos: lc(1, 3),
1618 },
1619 ArgSpan {
1620 expr: Some(Expr::Double(DoubleSpan { value: 2.0, pos: lc(1, 4) })),
1621 sep: ArgSep::Long,
1622 sep_pos: lc(1, 5),
1623 },
1624 ArgSpan {
1625 expr: Some(Expr::Integer(IntegerSpan { value: 3, pos: lc(1, 6) })),
1626 sep: ArgSep::Long,
1627 sep_pos: lc(1, 7),
1628 },
1629 ArgSpan {
1630 expr: Some(Expr::Text(TextSpan { value: "foo".to_owned(), pos: lc(1, 8) })),
1631 sep: ArgSep::End,
1632 sep_pos: lc(1, 9),
1633 },
1634 ])
1635 .exp_instr(Instruction::PushString("foo".to_owned(), lc(1, 8)))
1636 .exp_instr(Instruction::PushInteger(ValueTag::Text as i32, lc(1, 8)))
1637 .exp_instr(Instruction::PushInteger(3, lc(1, 6)))
1638 .exp_instr(Instruction::PushInteger(ValueTag::Integer as i32, lc(1, 6)))
1639 .exp_instr(Instruction::PushDouble(2.0, lc(1, 4)))
1640 .exp_instr(Instruction::PushInteger(ValueTag::Double as i32, lc(1, 4)))
1641 .exp_instr(Instruction::PushBoolean(false, lc(1, 2)))
1642 .exp_instr(Instruction::PushInteger(ValueTag::Boolean as i32, lc(1, 2)))
1643 .exp_nargs(8)
1644 .check();
1645 }
1646
1647 #[test]
1648 fn test_one_any_value_expr_error() {
1649 Tester::default()
1650 .symbol("foo", SymbolPrototype::Variable(ExprType::Double))
1651 .syntax(
1652 &[SingularArgSyntax::AnyValue(
1653 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: false },
1654 ArgSepSyntax::End,
1655 )],
1656 None,
1657 )
1658 .compile_command([ArgSpan {
1659 expr: Some(Expr::Symbol(SymbolSpan {
1660 vref: VarRef::new("foo", Some(ExprType::Boolean)),
1661 pos: lc(1, 2),
1662 })),
1663 sep: ArgSep::End,
1664 sep_pos: lc(1, 3),
1665 }])
1666 .exp_error(Error::IncompatibleTypeAnnotationInReference(
1667 lc(1, 2),
1668 VarRef::new("foo", Some(ExprType::Boolean)),
1669 ))
1670 .check();
1671 }
1672
1673 #[test]
1674 fn test_one_any_value_disallow_missing() {
1675 Tester::default()
1676 .symbol("foo", SymbolPrototype::Variable(ExprType::Double))
1677 .syntax(
1678 &[SingularArgSyntax::AnyValue(
1679 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: false },
1680 ArgSepSyntax::End,
1681 )],
1682 None,
1683 )
1684 .compile_command([ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 3) }])
1685 .exp_error(Error::CallableSyntaxError(
1686 lc(1, 3),
1687 CallableMetadataBuilder::new("TEST")
1688 .with_syntax(&[(
1689 &[SingularArgSyntax::AnyValue(
1690 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: false },
1691 ArgSepSyntax::End,
1692 )],
1693 None,
1694 )])
1695 .test_build(),
1696 ))
1697 .check();
1698 }
1699
1700 #[test]
1701 fn test_one_any_value_allow_missing() {
1702 Tester::default()
1703 .syntax(
1704 &[SingularArgSyntax::AnyValue(
1705 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: true },
1706 ArgSepSyntax::End,
1707 )],
1708 None,
1709 )
1710 .compile_command([ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 3) }])
1711 .exp_instr(Instruction::PushInteger(ValueTag::Missing as i32, lc(1, 3)))
1712 .exp_nargs(1)
1713 .check();
1714 }
1715
1716 #[test]
1717 fn test_multiple_separator_types_ok() {
1718 Tester::default()
1719 .syntax(
1720 &[
1721 SingularArgSyntax::AnyValue(
1722 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: true },
1723 ArgSepSyntax::Exactly(ArgSep::As),
1724 ),
1725 SingularArgSyntax::AnyValue(
1726 AnyValueSyntax { name: Cow::Borrowed("arg2"), allow_missing: true },
1727 ArgSepSyntax::OneOf(ArgSep::Long, ArgSep::Short),
1728 ),
1729 SingularArgSyntax::AnyValue(
1730 AnyValueSyntax { name: Cow::Borrowed("arg3"), allow_missing: true },
1731 ArgSepSyntax::OneOf(ArgSep::Long, ArgSep::Short),
1732 ),
1733 SingularArgSyntax::AnyValue(
1734 AnyValueSyntax { name: Cow::Borrowed("arg4"), allow_missing: true },
1735 ArgSepSyntax::End,
1736 ),
1737 ],
1738 None,
1739 )
1740 .compile_command([
1741 ArgSpan { expr: None, sep: ArgSep::As, sep_pos: lc(1, 1) },
1742 ArgSpan { expr: None, sep: ArgSep::Long, sep_pos: lc(1, 2) },
1743 ArgSpan { expr: None, sep: ArgSep::Short, sep_pos: lc(1, 3) },
1744 ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 4) },
1745 ])
1746 .exp_instr(Instruction::PushInteger(ValueTag::Missing as i32, lc(1, 4)))
1747 .exp_instr(Instruction::PushInteger(ArgSep::Short as i32, lc(1, 3)))
1748 .exp_instr(Instruction::PushInteger(ValueTag::Missing as i32, lc(1, 3)))
1749 .exp_instr(Instruction::PushInteger(ArgSep::Long as i32, lc(1, 2)))
1750 .exp_instr(Instruction::PushInteger(ValueTag::Missing as i32, lc(1, 2)))
1751 .exp_instr(Instruction::PushInteger(ValueTag::Missing as i32, lc(1, 1)))
1752 .exp_nargs(6)
1753 .check();
1754 }
1755
1756 #[test]
1757 fn test_multiple_separator_exactly_mismatch() {
1758 Tester::default()
1759 .syntax(
1760 &[
1761 SingularArgSyntax::AnyValue(
1762 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: true },
1763 ArgSepSyntax::Exactly(ArgSep::As),
1764 ),
1765 SingularArgSyntax::AnyValue(
1766 AnyValueSyntax { name: Cow::Borrowed("arg2"), allow_missing: true },
1767 ArgSepSyntax::End,
1768 ),
1769 ],
1770 None,
1771 )
1772 .compile_command([
1773 ArgSpan { expr: None, sep: ArgSep::Short, sep_pos: lc(1, 1) },
1774 ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 4) },
1775 ])
1776 .exp_error(Error::CallableSyntaxError(
1777 lc(1, 1),
1778 CallableMetadataBuilder::new("TEST")
1779 .with_syntax(&[(
1780 &[
1781 SingularArgSyntax::AnyValue(
1782 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: true },
1783 ArgSepSyntax::Exactly(ArgSep::As),
1784 ),
1785 SingularArgSyntax::AnyValue(
1786 AnyValueSyntax { name: Cow::Borrowed("arg2"), allow_missing: true },
1787 ArgSepSyntax::End,
1788 ),
1789 ],
1790 None,
1791 )])
1792 .test_build(),
1793 ))
1794 .check();
1795 }
1796
1797 #[test]
1798 fn test_multiple_separator_oneof_mismatch() {
1799 Tester::default()
1800 .syntax(
1801 &[
1802 SingularArgSyntax::AnyValue(
1803 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: true },
1804 ArgSepSyntax::OneOf(ArgSep::Short, ArgSep::Long),
1805 ),
1806 SingularArgSyntax::AnyValue(
1807 AnyValueSyntax { name: Cow::Borrowed("arg2"), allow_missing: true },
1808 ArgSepSyntax::End,
1809 ),
1810 ],
1811 None,
1812 )
1813 .compile_command([
1814 ArgSpan { expr: None, sep: ArgSep::As, sep_pos: lc(1, 1) },
1815 ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 4) },
1816 ])
1817 .exp_error(Error::CallableSyntaxError(
1818 lc(1, 1),
1819 CallableMetadataBuilder::new("TEST")
1820 .with_syntax(&[(
1821 &[
1822 SingularArgSyntax::AnyValue(
1823 AnyValueSyntax { name: Cow::Borrowed("arg1"), allow_missing: true },
1824 ArgSepSyntax::OneOf(ArgSep::Short, ArgSep::Long),
1825 ),
1826 SingularArgSyntax::AnyValue(
1827 AnyValueSyntax { name: Cow::Borrowed("arg2"), allow_missing: true },
1828 ArgSepSyntax::End,
1829 ),
1830 ],
1831 None,
1832 )])
1833 .test_build(),
1834 ))
1835 .check();
1836 }
1837
1838 #[test]
1839 fn test_repeated_none() {
1840 Tester::default()
1841 .syntax(
1842 &[],
1843 Some(&RepeatedSyntax {
1844 name: Cow::Borrowed("arg"),
1845 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
1846 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1847 allow_missing: false,
1848 require_one: false,
1849 }),
1850 )
1851 .compile_command([])
1852 .exp_nargs(0)
1853 .check();
1854 }
1855
1856 #[test]
1857 fn test_repeated_multiple_and_cast() {
1858 Tester::default()
1859 .syntax(
1860 &[],
1861 Some(&RepeatedSyntax {
1862 name: Cow::Borrowed("arg"),
1863 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
1864 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1865 allow_missing: false,
1866 require_one: false,
1867 }),
1868 )
1869 .compile_command([
1870 ArgSpan {
1871 expr: Some(Expr::Double(DoubleSpan { value: 3.0, pos: lc(1, 2) })),
1872 sep: ArgSep::Long,
1873 sep_pos: lc(1, 2),
1874 },
1875 ArgSpan {
1876 expr: Some(Expr::Integer(IntegerSpan { value: 5, pos: lc(1, 4) })),
1877 sep: ArgSep::End,
1878 sep_pos: lc(1, 3),
1879 },
1880 ])
1881 .exp_instr(Instruction::PushInteger(5, lc(1, 4)))
1882 .exp_instr(Instruction::PushDouble(3.0, lc(1, 2)))
1883 .exp_instr(Instruction::DoubleToInteger)
1884 .exp_nargs(2)
1885 .check();
1886 }
1887
1888 #[test]
1889 fn test_repeated_require_one_just_one() {
1890 Tester::default()
1891 .syntax(
1892 &[],
1893 Some(&RepeatedSyntax {
1894 name: Cow::Borrowed("arg"),
1895 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
1896 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1897 allow_missing: false,
1898 require_one: true,
1899 }),
1900 )
1901 .compile_command([ArgSpan {
1902 expr: Some(Expr::Integer(IntegerSpan { value: 5, pos: lc(1, 2) })),
1903 sep: ArgSep::End,
1904 sep_pos: lc(1, 2),
1905 }])
1906 .exp_instr(Instruction::PushInteger(5, lc(1, 2)))
1907 .exp_nargs(1)
1908 .check();
1909 }
1910
1911 #[test]
1912 fn test_repeated_require_one_missing() {
1913 Tester::default()
1914 .syntax(
1915 &[],
1916 Some(&RepeatedSyntax {
1917 name: Cow::Borrowed("arg"),
1918 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
1919 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1920 allow_missing: false,
1921 require_one: true,
1922 }),
1923 )
1924 .compile_command([])
1925 .exp_error(Error::CallableSyntaxError(
1926 lc(1000, 2000),
1927 CallableMetadataBuilder::new("TEST")
1928 .with_syntax(&[(
1929 &[],
1930 Some(&RepeatedSyntax {
1931 name: Cow::Borrowed("arg"),
1932 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
1933 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1934 allow_missing: false,
1935 require_one: true,
1936 }),
1937 )])
1938 .test_build(),
1939 ))
1940 .check();
1941 }
1942
1943 #[test]
1944 fn test_repeated_require_one_ref_ok() {
1945 Tester::default()
1946 .syntax(
1947 &[],
1948 Some(&RepeatedSyntax {
1949 name: Cow::Borrowed("arg"),
1950 type_syn: RepeatedTypeSyntax::VariableRef,
1951 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1952 allow_missing: false,
1953 require_one: true,
1954 }),
1955 )
1956 .compile_command([ArgSpan {
1957 expr: Some(Expr::Symbol(SymbolSpan {
1958 vref: VarRef::new("foo", Some(ExprType::Text)),
1959 pos: lc(1, 2),
1960 })),
1961 sep: ArgSep::End,
1962 sep_pos: lc(1, 2),
1963 }])
1964 .exp_instr(Instruction::LoadRef(SymbolKey::from("foo"), ExprType::Text, lc(1, 2)))
1965 .exp_nargs(1)
1966 .check();
1967 }
1968
1969 #[test]
1970 fn test_repeated_require_one_ref_error() {
1971 Tester::default()
1972 .syntax(
1973 &[],
1974 Some(&RepeatedSyntax {
1975 name: Cow::Borrowed("arg"),
1976 type_syn: RepeatedTypeSyntax::VariableRef,
1977 sep: ArgSepSyntax::Exactly(ArgSep::Long),
1978 allow_missing: false,
1979 require_one: true,
1980 }),
1981 )
1982 .compile_command([ArgSpan {
1983 expr: Some(Expr::Integer(IntegerSpan { value: 5, pos: lc(1, 2) })),
1984 sep: ArgSep::End,
1985 sep_pos: lc(1, 2),
1986 }])
1987 .exp_error(Error::NotAReference(lc(1, 2)))
1988 .check();
1989 }
1990
1991 #[test]
1992 fn test_repeated_oneof_separator() {
1993 Tester::default()
1994 .syntax(
1995 &[],
1996 Some(&RepeatedSyntax {
1997 name: Cow::Borrowed("arg"),
1998 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Double),
1999 sep: ArgSepSyntax::OneOf(ArgSep::Long, ArgSep::Short),
2000 allow_missing: false,
2001 require_one: false,
2002 }),
2003 )
2004 .compile_command([
2005 ArgSpan {
2006 expr: Some(Expr::Double(DoubleSpan { value: 3.0, pos: lc(1, 2) })),
2007 sep: ArgSep::Short,
2008 sep_pos: lc(1, 3),
2009 },
2010 ArgSpan {
2011 expr: Some(Expr::Double(DoubleSpan { value: 5.0, pos: lc(1, 4) })),
2012 sep: ArgSep::Long,
2013 sep_pos: lc(1, 5),
2014 },
2015 ArgSpan {
2016 expr: Some(Expr::Double(DoubleSpan { value: 2.0, pos: lc(1, 6) })),
2017 sep: ArgSep::End,
2018 sep_pos: lc(1, 7),
2019 },
2020 ])
2021 .exp_instr(Instruction::PushDouble(2.0, lc(1, 6)))
2022 .exp_instr(Instruction::PushInteger(ArgSep::Long as i32, lc(1, 5)))
2023 .exp_instr(Instruction::PushDouble(5.0, lc(1, 4)))
2024 .exp_instr(Instruction::PushInteger(ArgSep::Short as i32, lc(1, 3)))
2025 .exp_instr(Instruction::PushDouble(3.0, lc(1, 2)))
2026 .exp_nargs(5)
2027 .check();
2028 }
2029
2030 #[test]
2031 fn test_repeated_oneof_separator_and_missing_in_last_position() {
2032 Tester::default()
2033 .syntax(
2034 &[],
2035 Some(&RepeatedSyntax {
2036 name: Cow::Borrowed("arg"),
2037 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Double),
2038 sep: ArgSepSyntax::OneOf(ArgSep::Long, ArgSep::Short),
2039 allow_missing: true,
2040 require_one: false,
2041 }),
2042 )
2043 .compile_command([
2044 ArgSpan {
2045 expr: Some(Expr::Double(DoubleSpan { value: 3.0, pos: lc(1, 2) })),
2046 sep: ArgSep::Short,
2047 sep_pos: lc(1, 3),
2048 },
2049 ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 4) },
2050 ])
2051 .exp_instr(Instruction::PushInteger(ValueTag::Missing as i32, lc(1, 4)))
2052 .exp_instr(Instruction::PushInteger(ArgSep::Short as i32, lc(1, 3)))
2053 .exp_instr(Instruction::PushDouble(3.0, lc(1, 2)))
2054 .exp_instr(Instruction::PushInteger(ValueTag::Double as i32, lc(1, 2)))
2055 .exp_nargs(4)
2056 .check();
2057 }
2058
2059 #[test]
2060 fn test_repeated_any_value() {
2061 Tester::default()
2062 .syntax(
2063 &[],
2064 Some(&RepeatedSyntax {
2065 name: Cow::Borrowed("arg"),
2066 type_syn: RepeatedTypeSyntax::AnyValue,
2067 sep: ArgSepSyntax::Exactly(ArgSep::Long),
2068 allow_missing: true,
2069 require_one: false,
2070 }),
2071 )
2072 .compile_command([
2073 ArgSpan {
2074 expr: Some(Expr::Boolean(BooleanSpan { value: false, pos: lc(1, 2) })),
2075 sep: ArgSep::Long,
2076 sep_pos: lc(1, 3),
2077 },
2078 ArgSpan {
2079 expr: Some(Expr::Double(DoubleSpan { value: 2.0, pos: lc(1, 4) })),
2080 sep: ArgSep::Long,
2081 sep_pos: lc(1, 5),
2082 },
2083 ArgSpan {
2084 expr: Some(Expr::Integer(IntegerSpan { value: 3, pos: lc(1, 6) })),
2085 sep: ArgSep::Long,
2086 sep_pos: lc(1, 7),
2087 },
2088 ArgSpan {
2089 expr: Some(Expr::Text(TextSpan { value: "foo".to_owned(), pos: lc(1, 8) })),
2090 sep: ArgSep::Long,
2091 sep_pos: lc(1, 9),
2092 },
2093 ArgSpan { expr: None, sep: ArgSep::End, sep_pos: lc(1, 10) },
2094 ])
2095 .exp_instr(Instruction::PushInteger(ValueTag::Missing as i32, lc(1, 10)))
2096 .exp_instr(Instruction::PushString("foo".to_owned(), lc(1, 8)))
2097 .exp_instr(Instruction::PushInteger(ValueTag::Text as i32, lc(1, 8)))
2098 .exp_instr(Instruction::PushInteger(3, lc(1, 6)))
2099 .exp_instr(Instruction::PushInteger(ValueTag::Integer as i32, lc(1, 6)))
2100 .exp_instr(Instruction::PushDouble(2.0, lc(1, 4)))
2101 .exp_instr(Instruction::PushInteger(ValueTag::Double as i32, lc(1, 4)))
2102 .exp_instr(Instruction::PushBoolean(false, lc(1, 2)))
2103 .exp_instr(Instruction::PushInteger(ValueTag::Boolean as i32, lc(1, 2)))
2104 .exp_nargs(9)
2105 .check();
2106 }
2107
2108 #[test]
2109 fn test_singular_and_repeated() {
2110 Tester::default()
2111 .syntax(
2112 &[SingularArgSyntax::RequiredValue(
2113 RequiredValueSyntax { name: Cow::Borrowed("arg"), vtype: ExprType::Double },
2114 ArgSepSyntax::Exactly(ArgSep::Short),
2115 )],
2116 Some(&RepeatedSyntax {
2117 name: Cow::Borrowed("rep"),
2118 type_syn: RepeatedTypeSyntax::TypedValue(ExprType::Integer),
2119 sep: ArgSepSyntax::Exactly(ArgSep::Long),
2120 allow_missing: false,
2121 require_one: false,
2122 }),
2123 )
2124 .compile_command([
2125 ArgSpan {
2126 expr: Some(Expr::Double(DoubleSpan { value: 4.0, pos: lc(1, 2) })),
2127 sep: ArgSep::Short,
2128 sep_pos: lc(1, 2),
2129 },
2130 ArgSpan {
2131 expr: Some(Expr::Integer(IntegerSpan { value: 5, pos: lc(1, 5) })),
2132 sep: ArgSep::Long,
2133 sep_pos: lc(1, 2),
2134 },
2135 ArgSpan {
2136 expr: Some(Expr::Integer(IntegerSpan { value: 6, pos: lc(1, 7) })),
2137 sep: ArgSep::End,
2138 sep_pos: lc(1, 2),
2139 },
2140 ])
2141 .exp_nargs(3)
2142 .exp_instr(Instruction::PushInteger(6, lc(1, 7)))
2143 .exp_instr(Instruction::PushInteger(5, lc(1, 5)))
2144 .exp_instr(Instruction::PushDouble(4.0, lc(1, 2)))
2145 .check();
2146 }
2147}