@@ -71,6 +71,8 @@ import Statement = NFStatement;
7171import Sections = NFSections ;
7272import Algorithm = NFAlgorithm ;
7373import OperatorOverloading = NFOperatorOverloading ;
74+ import MetaModelica.Dangerous.listReverseInPlace ;
75+ import Array ;
7476
7577
7678public
@@ -85,12 +87,16 @@ type SlotType = enumeration(
8587 GENERIC "Accepts both positional and named arguments."
8688) "Determines which type of argument a slot accepts." ;
8789
90+ type SlotEvalStatus = enumeration(NOT_EVALUATED , EVALUATING , EVALUATED );
91+
8892uniontype Slot
8993 record SLOT
9094 String name;
9195 SlotType ty;
9296 Option < Expression > default;
9397 Option < TypedArg > arg;
98+ Integer index;
99+ SlotEvalStatus evalStatus;
94100 end SLOT ;
95101
96102 function positional
@@ -114,6 +120,12 @@ uniontype Slot
114120 else false ;
115121 end match;
116122 end named;
123+
124+ function hasName
125+ input String name;
126+ input Slot slot;
127+ output Boolean hasName = name == slot. name;
128+ end hasName;
117129end Slot ;
118130
119131public
@@ -582,55 +594,42 @@ uniontype Function
582594 output Boolean matching;
583595 protected
584596 Slot slot;
585- list< Slot > slots;
597+ list< Slot > slots, remaining_slots ;
586598 list< TypedArg > filled_named_args;
599+ array< Slot > slots_arr;
600+ Integer pos_arg_count, slot_count, index = 1 ;
587601 algorithm
588602 slots := fn. slots;
603+ pos_arg_count := listLength(posArgs);
604+ slot_count := listLength(slots);
589605
590- if listLength(posArgs) > listLength(slots) then
606+ if pos_arg_count > slot_count then
607+ // If we have too many positional arguments it can't possibly match.
591608 matching := false ;
592609 return ;
610+ elseif pos_arg_count == slot_count and listEmpty(namedArgs) then
611+ // If we have exactly as many positional arguments as slots and no named
612+ // arguments we can just return the list of arguments as it is.
613+ matching := true ;
614+ return ;
593615 end if ;
594- // Remove as many slots as there are positional arguments. We don't actually
595- // need to fill the slots, the positional arguments will always be first
596- // anyway. This makes it a bit slower to figure out what error to give if a
597- // named argument is wrong, but faster for the most common case of
598- // everything being correct.
616+
617+ slots_arr := listArray(slots);
618+
599619 for arg in args loop
600- slot :: slots : = slots ;
620+ slot := slots_arr[index] ;
601621
602622 if not Slot . positional(slot) then
603623 // Slot doesn't allow positional arguments (used for some builtin functions).
604624 matching := false ;
605625 return ;
606626 end if ;
607- end for ;
608-
609- // Fill the remaining slots with the named arguments.
610- (filled_named_args, matching) := fillNamedArgs(namedArgs, slots, fn, info);
611627
612- // Append the now ordered named arguments to the positional arguments.
613- if matching then
614- args := listAppend(posArgs, filled_named_args);
615- end if ;
616- end fillArgs;
628+ slot. arg := SOME (arg);
629+ arrayUpdate(slots_arr, index, slot);
630+ index := index + 1 ;
631+ end for ;
617632
618- function fillNamedArgs
619- "Sorts a list of named arguments based on the given slots, and returns the
620- arguments for the slots if the arguments are correct. If the arguments
621- are not correct the list of expressions returned is undefined, along with
622- the matching output being false."
623- input list< TypedNamedArg > namedArgs;
624- input list< Slot > slots;
625- input Function fn;
626- input SourceInfo info;
627- output list< TypedArg > args = {};
628- output Boolean matching = true ;
629- protected
630- array< Slot > slots_arr = listArray(slots);
631- String name;
632- Expression arg;
633- algorithm
634633 for narg in namedArgs loop
635634 (slots_arr, matching) := fillNamedArg(narg, slots_arr, fn, info);
636635
@@ -640,7 +639,7 @@ uniontype Function
640639 end for ;
641640
642641 (args, matching) := collectArgs(slots_arr, info);
643- end fillNamedArgs ;
642+ end fillArgs ;
644643
645644 function fillNamedArg
646645 "Looks up a slot with the given name and tries to fill it with the given
@@ -658,7 +657,9 @@ uniontype Function
658657 Variability var ;
659658 algorithm
660659 // Try to find a slot and fill it with the argument expression.
661- for i in 1 :arrayLength(slots) loop
660+ // Positional arguments fill the slots from the start of the array, so
661+ // searching backwards will generally be a bit more efficient.
662+ for i in arrayLength(slots):-1 :1 loop
662663 s := slots[i];
663664
664665 (argName, argExp, ty, var ) := inArg;
@@ -718,22 +719,133 @@ uniontype Function
718719 for s in slots loop
719720 SLOT (name = name, default = default, arg = arg) := s;
720721
721- args := match (default, arg)
722- case (_, SOME (a)) then a :: args; // Use the argument from the call if one was given.
723- // TODO: save this info in the defaults in slots (the type we can get from the exp manually but the variability is lost.).
724- case (SOME (e), _) then (e,Expression . typeOf(e),Variability . CONSTANT ) ::args; // Otherwise, check that a default value exists.
725- else // Give an error if no argument was given and there's no default value.
722+ args := matchcontinue arg
723+ // Use the argument from the call if one was given.
724+ case SOME (a) then a :: args;
725+
726+ // Otherwise, try to fill the slot with its default argument.
727+ case _ then fillDefaultSlot(s, slots, info) :: args;
728+
729+ else
726730 algorithm
727- Error . addSourceMessage(Error . UNFILLED_SLOT , {name}, info);
728731 matching := false ;
729732 then
730733 args;
731- end match ;
734+ end matchcontinue ;
732735 end for ;
733736
734737 args := listReverse(args);
735738 end collectArgs;
736739
740+ function fillDefaultSlot
741+ input Slot slot;
742+ input array< Slot > slots;
743+ input SourceInfo info;
744+ output TypedArg outArg;
745+ algorithm
746+ outArg := match slot
747+ // Slot already filled by function argument.
748+ case SLOT (arg = SOME (outArg)) then outArg;
749+
750+ // Slot not filled by function argument, but has default value.
751+ case SLOT (default = SOME (_))
752+ then fillDefaultSlot2(slot, slots, info);
753+
754+ // Give an error if no argument was given and there's no default argument.
755+ else
756+ algorithm
757+ Error . addSourceMessage(Error . UNFILLED_SLOT , {slot. name}, info);
758+ then
759+ fail();
760+
761+ end match;
762+ end fillDefaultSlot;
763+
764+ function fillDefaultSlot2
765+ input Slot slot;
766+ input array< Slot > slots;
767+ input SourceInfo info;
768+ output TypedArg outArg;
769+ algorithm
770+ outArg := match slot. evalStatus
771+ local
772+ Expression exp;
773+
774+ // An already evaluated slot, return its binding.
775+ case SlotEvalStatus . EVALUATED
776+ then Util . getOption(slot. arg);
777+
778+ // A slot in the process of being evaluated => cyclic bindings.
779+ case SlotEvalStatus . EVALUATING
780+ algorithm
781+ Error . addSourceMessage(Error . CYCLIC_DEFAULT_VALUE , {slot. name}, info);
782+ then
783+ fail();
784+
785+ // A slot with a not evaluated binding, evaluate the binding and return it.
786+ case SlotEvalStatus . NOT_EVALUATED
787+ algorithm
788+ slot. evalStatus := SlotEvalStatus . EVALUATING ;
789+ arrayUpdate(slots, slot. index, slot);
790+
791+ exp := evaluateSlotExp(Util . getOption(slot. default), slots, info);
792+ outArg := (exp, Expression . typeOf(exp), Expression . variability(exp));
793+
794+ slot. arg := SOME (outArg);
795+ slot. evalStatus := SlotEvalStatus . EVALUATED ;
796+ arrayUpdate(slots, slot. index, slot);
797+ then
798+ outArg;
799+
800+ end match;
801+ end fillDefaultSlot2;
802+
803+ function evaluateSlotExp
804+ input Expression exp;
805+ input array< Slot > slots;
806+ input SourceInfo info;
807+ output Expression outExp;
808+ algorithm
809+ outExp := Expression . map(exp,
810+ function evaluateSlotExp_traverser(slots = slots, info = info));
811+ end evaluateSlotExp;
812+
813+ function evaluateSlotExp_traverser
814+ input Expression exp;
815+ input array< Slot > slots;
816+ input SourceInfo info;
817+ output Expression outExp;
818+ algorithm
819+ outExp := match exp
820+ local
821+ ComponentRef cref;
822+ Option < Slot > slot;
823+
824+ case Expression . CREF (cref = cref as ComponentRef . CREF (restCref = ComponentRef . EMPTY ()))
825+ algorithm
826+ slot := lookupSlotInArray(ComponentRef . firstName(cref), slots);
827+ then
828+ if isSome(slot) then Util . tuple31(fillDefaultSlot(Util . getOption(slot), slots, info)) else exp;
829+
830+ else exp;
831+ end match;
832+ end evaluateSlotExp_traverser;
833+
834+ function lookupSlotInArray
835+ input String slotName;
836+ input array< Slot > slots;
837+ output Option < Slot > outSlot;
838+ protected
839+ Slot slot;
840+ algorithm
841+ try
842+ slot := Array . getMemberOnTrue(slotName, slots, Slot . hasName);
843+ outSlot := SOME (slot);
844+ else
845+ outSlot := NONE ();
846+ end try ;
847+ end lookupSlotInArray;
848+
737849 function matchArgsVectorize
738850 input Function func ;
739851 input output list< TypedArg > args;
@@ -1042,7 +1154,7 @@ uniontype Function
10421154 end for ;
10431155
10441156 // Make the slots and return type for the function.
1045- fn. slots := list(makeSlot(i) for i in fn. inputs);
1157+ fn. slots := makeSlots( fn. inputs);
10461158 checkParamTypes(fn);
10471159 fn. returnType := makeReturnType(fn);
10481160 end if ;
@@ -1360,8 +1472,23 @@ protected
13601472 end if ;
13611473 end paramDirection;
13621474
1475+ function makeSlots
1476+ input list< InstNode > inputs;
1477+ output list< Slot > slots = {};
1478+ protected
1479+ Integer index = 1 ;
1480+ algorithm
1481+ for i in inputs loop
1482+ slots := makeSlot(i, index) :: slots;
1483+ index := index + 1 ;
1484+ end for ;
1485+
1486+ slots := listReverseInPlace(slots);
1487+ end makeSlots;
1488+
13631489 function makeSlot
13641490 input InstNode component;
1491+ input Integer index;
13651492 output Slot slot;
13661493 protected
13671494 Component comp;
@@ -1380,7 +1507,7 @@ protected
13801507 end if ;
13811508 end if ;
13821509
1383- slot := SLOT (InstNode . name(component), SlotType . GENERIC , default, NONE ());
1510+ slot := SLOT (InstNode . name(component), SlotType . GENERIC , default, NONE (), index, SlotEvalStatus . NOT_EVALUATED );
13841511 else
13851512 Error . assertion(false , getInstanceName() + " got invalid component" , sourceInfo());
13861513 end try ;
0 commit comments