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