Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | TIP#457: Update named group ending |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | tip-457 |
Files: | files | file ages | folders |
SHA1: |
be60dadb167c3a43a6f1ee59daf9a8d6 |
User & Date: | mlafon 2017-05-23 22:36:27 |
Context
2017-05-23
| ||
22:36 | TIP#457: Update named group ending Leaf check-in: be60dadb16 user: mlafon tags: tip-457 | |
22:27 | merge trunk check-in: 340fa5f4a8 user: mlafon tags: tip-457 | |
Changes
Changes to doc/proc.n.
︙ | ︙ | |||
147 148 149 150 151 152 153 | .SS "NAMED ARGUMENTS HANDLING" .VS .PP Named argument are arguments defined using the \fB-name\fR or \fB-switch\fR extended argument specifiers. They have a special handling on call-site. All contiguous named arguments, called named group, are handled together, they are not required to be declared in order, can be omited or declared | > > | | | > > | | > > > > | > > > > | 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | .SS "NAMED ARGUMENTS HANDLING" .VS .PP Named argument are arguments defined using the \fB-name\fR or \fB-switch\fR extended argument specifiers. They have a special handling on call-site. All contiguous named arguments, called named group, are handled together, they are not required to be declared in order, can be omited or declared multiple times. .PP The handling of a named group is started when previous formal arguments have been assigned. Each named parameter uses a string starting with a dash character, followed by the name of the related argument. If it is not a switch argument, it is followed by the value to assign to the formal argument. .PP The handling of a named group is ended if the next parameter does not start with a dash or is the special \fB--\fR keyword. In the case where the arguments after the named group are all non-optional positional arguments and do not end with \fBargs\fR, the handling of the named group will also be ended when the number of remaining parameters will be equal to the number of the remaining positional arguments. When the handling of named argument has been ended, remaining parameters, except the \fB--\fR keyword if used, are then assigned to following positional arguments using the default handling. .PP It is recommended to explicitely use the \fB--\fR keyword if the next parameter following the named group is a variable which may start with a dash, or if it is an object which can be expensive to stringify. .VE .SH EXAMPLES .PP This is a procedure that takes two arguments and prints both their sum and their product. It also returns the string .QW OK to the caller as an explicit result. |
︙ | ︙ | |||
217 218 219 220 221 222 223 | {level -name level -switch {{debug 0} {error 3}} -default 1} {time -name {time timestamp}} {message} } { if {![info exists time]} {set time [clock seconds]} array set levels {0 DEBUG 1 INFO 2 WARN 3 ERROR} puts "[clock format $time] : $levels($level) : $message" } | < < < < < | 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | {level -name level -switch {{debug 0} {error 3}} -default 1} {time -name {time timestamp}} {message} } { if {![info exists time]} {set time [clock seconds]} array set levels {0 DEBUG 1 INFO 2 WARN 3 ERROR} puts "[clock format $time] : $levels($level) : $message" } .CE .SH "SEE ALSO" info(n), unknown(n) .SH KEYWORDS argument, procedure '\" Local Variables: '\" mode: nroff '\" End: |
Changes to generic/tclInt.h.
︙ | ︙ | |||
943 944 945 946 947 948 949 | * The structure below describes an extended argument specification applied * on a proc argument (see TIP#457). */ typedef struct ExtendedArgSpec { struct NamedGroupEntry *firstNamedEntryPtr; /* Pointer to the first named parameter entry | | < < < > > > > > | 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 | * The structure below describes an extended argument specification applied * on a proc argument (see TIP#457). */ typedef struct ExtendedArgSpec { struct NamedGroupEntry *firstNamedEntryPtr; /* Pointer to the first named parameter entry * defined on this proc argument. */ Tcl_HashTable *namedHashTable; /* Pointer to the hash table created for a * fast lookup of named entry. The pointer is * only set on the first local of a named * group. */ int remainAfterNamedGroup; /* If the number of arguments after a named * group is fixed and non-optional, this will * contain that number. Otherwise, will be set * to -1. This is only set on the first local * of a named group. */ Tcl_Obj *upvarLevelPtr; /* Pointer to the level value specified using * the -upvar specifier. */ } ExtendedArgSpec; /* * The variable-length structure below describes a local variable of a * procedure that was recognized by the compiler. These variables have a name, |
︙ | ︙ | |||
1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 | /* * Flag bits for procedures. * * PROC_HAS_EXT_ARG_SPEC 1 means that the procedure has at least one * argument defined using an extended argument * specification. */ #define PROC_HAS_EXT_ARG_SPEC 0x01 /* * The type of functions called to process errors found during the execution * of a procedure (or lambda term or ...). */ typedef void (ProcErrorProc)(Tcl_Interp *interp, Tcl_Obj *procNameObj); | > > > | 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 | /* * Flag bits for procedures. * * PROC_HAS_EXT_ARG_SPEC 1 means that the procedure has at least one * argument defined using an extended argument * specification. * PROC_HAS_NAMED_GROUP 1 means that the procedure has at least one * named group. */ #define PROC_HAS_EXT_ARG_SPEC 0x01 #define PROC_HAS_NAMED_GROUP 0x02 /* * The type of functions called to process errors found during the execution * of a procedure (or lambda term or ...). */ typedef void (ProcErrorProc)(Tcl_Interp *interp, Tcl_Obj *procNameObj); |
︙ | ︙ |
Changes to generic/tclProc.c.
︙ | ︙ | |||
46 47 48 49 50 51 52 53 54 55 56 57 58 59 | static void ProcBodyFree(Tcl_Obj *objPtr); static int ProcWrongNumArgs(Tcl_Interp *interp, int skip); static void MakeProcError(Tcl_Interp *interp, Tcl_Obj *procNameObj); static void MakeLambdaError(Tcl_Interp *interp, Tcl_Obj *procNameObj); static int SetLambdaFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static int ProcParseArgSpec(Tcl_Interp *interp, const char *argSpec, int argIdx, int isLastArg, CompiledLocal **localPtrPtr, Tcl_HashTable **namedHashPtrPtr); static inline int ProcCheckScalarArg(const char *arg, const char **err); static void ProcCompiledLocalsFree(CompiledLocal *localPtr); | > > > > | 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | static void ProcBodyFree(Tcl_Obj *objPtr); static int ProcWrongNumArgs(Tcl_Interp *interp, int skip); static void MakeProcError(Tcl_Interp *interp, Tcl_Obj *procNameObj); static void MakeLambdaError(Tcl_Interp *interp, Tcl_Obj *procNameObj); static int SetLambdaFromAny(Tcl_Interp *interp, Tcl_Obj *objPtr); static int ProcAddNamedGroupEntry(Tcl_Interp *interp, CompiledLocal *localPtr, const char *argName, Tcl_Obj *valuePtr, NamedGroupEntry **lastEntryPtr, Tcl_HashTable **namedHashPtrPtr); static int ProcParseArgSpec(Tcl_Interp *interp, const char *argSpec, int argIdx, int isLastArg, CompiledLocal **localPtrPtr, Tcl_HashTable **namedHashPtrPtr); static inline int ProcCheckScalarArg(const char *arg, const char **err); static void ProcCompiledLocalsFree(CompiledLocal *localPtr); |
︙ | ︙ | |||
581 582 583 584 585 586 587 | } else { procPtr->lastLocalPtr->nextPtr = newLocalPtr; } procPtr->lastLocalPtr = newLocalPtr; if (TclIsVarWithExtArgs(newLocalPtr)) { procPtr->flags |= PROC_HAS_EXT_ARG_SPEC; | > > | | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 | } else { procPtr->lastLocalPtr->nextPtr = newLocalPtr; } procPtr->lastLocalPtr = newLocalPtr; if (TclIsVarWithExtArgs(newLocalPtr)) { procPtr->flags |= PROC_HAS_EXT_ARG_SPEC; if (newLocalPtr->flags & VAR_NAMED_GROUP) { procPtr->flags |= PROC_HAS_NAMED_GROUP; } } } } if ((procPtr->flags & PROC_HAS_NAMED_GROUP) && !(procPtr->lastLocalPtr->flags & (VAR_IS_ARGS|VAR_ARG_OPTIONAL)) && (procPtr->lastLocalPtr->defValuePtr == NULL)) { /* proc with at least one named group and a non-optional and non-args * last variable. Find and store the number of arguments after the * last named group, this will be used to end the named group handling * and ensure the last input arguments can be assigned. */ CompiledLocal *lastNamedGroupPtr = NULL; for (localPtr = procPtr->firstLocalPtr; localPtr != NULL; localPtr = localPtr->nextPtr) { if ((localPtr->argSpecPtr != NULL) && (localPtr->argSpecPtr->namedHashTable != NULL)) { lastNamedGroupPtr = localPtr; } } /* find last entry of the named group */ for (localPtr = lastNamedGroupPtr; localPtr != NULL; localPtr = localPtr->nextPtr) { if ((localPtr->nextPtr == NULL) || !(localPtr->nextPtr->flags & VAR_NAMED_GROUP)) { break; } } if (localPtr != NULL) { lastNamedGroupPtr->argSpecPtr->remainAfterNamedGroup = procPtr->lastLocalPtr->frameIndex - localPtr->frameIndex; } } *procPtrPtr = procPtr; ckfree(argArray); return TCL_OK; procError: |
︙ | ︙ | |||
671 672 673 674 675 676 677 | * Results: * Returns TCL_OK on success and TCL_ERROR if anything goes wrong. * * Side effects: * If anything goes wrong, this function returns an error message in the * interpreter. On success, memory is allocated and linked into the * ExtendedArgSpec structure. A Tcl_HashTable is allocated when | | > > > > > | 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 | * Results: * Returns TCL_OK on success and TCL_ERROR if anything goes wrong. * * Side effects: * If anything goes wrong, this function returns an error message in the * interpreter. On success, memory is allocated and linked into the * ExtendedArgSpec structure. A Tcl_HashTable is allocated when * parsing the first entry of a named group entry. The variable pointed * by lastEntryPtr will be updated to always contain the pointer * of the last entry. * *---------------------------------------------------------------------- */ static int ProcAddNamedGroupEntry( Tcl_Interp *interp, /* Interpreter containing proc. */ CompiledLocal *localPtr, /* Related CompiledLocal */ const char *argName, /* Name string to add */ Tcl_Obj *valuePtr, /* Value to add when related to a -switch * argspec (NULL for -name argspec) */ NamedGroupEntry **lastEntryPtr, /* Pointer to a variable which contain the current * last named entry for this local. */ Tcl_HashTable **namedHashPtrPtr) /* Shared Tcl_HashTable created on the first * named parameter entry to speedup lookups * of named parameters. */ { NamedGroupEntry *entryPtr; ExtendedArgSpec *argSpecPtr = localPtr->argSpecPtr; |
︙ | ︙ | |||
730 731 732 733 734 735 736 | if (entryPtr->valuePtr != NULL) { Tcl_IncrRefCount(entryPtr->valuePtr); } if (argSpecPtr->firstNamedEntryPtr == NULL) { argSpecPtr->firstNamedEntryPtr = entryPtr; } else { | | | | 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 | if (entryPtr->valuePtr != NULL) { Tcl_IncrRefCount(entryPtr->valuePtr); } if (argSpecPtr->firstNamedEntryPtr == NULL) { argSpecPtr->firstNamedEntryPtr = entryPtr; } else { (*lastEntryPtr)->nextPtr = entryPtr; } *lastEntryPtr = entryPtr; /* Either use existing hash table or create a new one */ if (*namedHashPtrPtr != NULL) { namedHashPtr = *namedHashPtrPtr; } else { |
︙ | ︙ | |||
795 796 797 798 799 800 801 802 803 804 805 806 807 808 | Tcl_HashTable **namedHashPtrPtr) /* Shared Tcl_HashTable created on the first * named parameter entry to speedup lookups * of named parameters. */ { CompiledLocal *localPtr = NULL; ExtendedArgSpec *argSpecPtr; const char **fieldValues = NULL; int fieldCount, length; const char *err; int required = -1, hasSwitch = 0; int result, i, j; /* | > | 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 | Tcl_HashTable **namedHashPtrPtr) /* Shared Tcl_HashTable created on the first * named parameter entry to speedup lookups * of named parameters. */ { CompiledLocal *localPtr = NULL; ExtendedArgSpec *argSpecPtr; NamedGroupEntry *lastNamedEntryPtr = NULL; const char **fieldValues = NULL; int fieldCount, length; const char *err; int required = -1, hasSwitch = 0; int result, i, j; /* |
︙ | ︙ | |||
869 870 871 872 873 874 875 | /* * Check and handle each extended argument specification. */ argSpecPtr = ckalloc(sizeof(ExtendedArgSpec)); argSpecPtr->firstNamedEntryPtr = NULL; | < > | 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 | /* * Check and handle each extended argument specification. */ argSpecPtr = ckalloc(sizeof(ExtendedArgSpec)); argSpecPtr->firstNamedEntryPtr = NULL; argSpecPtr->namedHashTable = NULL; argSpecPtr->remainAfterNamedGroup = -1; argSpecPtr->upvarLevelPtr = NULL; localPtr->argSpecPtr = argSpecPtr; for (i = 1 ; i < fieldCount ; i += 2) { length = strlen(fieldValues[i]); if (*fieldValues[i] != '-') { |
︙ | ︙ | |||
917 918 919 920 921 922 923 | &nameValues); if (result != TCL_OK) { goto parseError; } for (j = 0; j < nameCount; j++) { result = ProcAddNamedGroupEntry(interp, localPtr, | | | 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 | &nameValues); if (result != TCL_OK) { goto parseError; } for (j = 0; j < nameCount; j++) { result = ProcAddNamedGroupEntry(interp, localPtr, nameValues[j], NULL, &lastNamedEntryPtr, namedHashPtrPtr); if (result != TCL_OK) { ckfree(nameValues); goto parseError; } } ckfree(nameValues); |
︙ | ︙ | |||
955 956 957 958 959 960 961 | goto parseError; } if (swEntCount == 1) { /* one field, use switch name as value */ result = ProcAddNamedGroupEntry(interp, localPtr, swEntValues[0], Tcl_NewStringObj(swEntValues[0], -1), | | | | 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 | goto parseError; } if (swEntCount == 1) { /* one field, use switch name as value */ result = ProcAddNamedGroupEntry(interp, localPtr, swEntValues[0], Tcl_NewStringObj(swEntValues[0], -1), &lastNamedEntryPtr, namedHashPtrPtr); } else if (swEntCount == 2) { /* two fields, use second one as value */ result = ProcAddNamedGroupEntry(interp, localPtr, swEntValues[0], Tcl_NewStringObj(swEntValues[1], -1), &lastNamedEntryPtr, namedHashPtrPtr); } else { /* invalid number of fields */ Tcl_SetObjResult(interp, Tcl_ObjPrintf( "incorrect switch value \"%s\"", swValues[j])); result = TCL_ERROR; } ckfree(swEntValues); |
︙ | ︙ | |||
2060 2061 2062 2063 2064 2065 2066 | /* * Start of a named argument group. Handle all of them together. */ for (; iArg < argCt; iArg++) { | | > | > > | < > | 2107 2108 2109 2110 2111 2112 2113 2114 2115 2116 2117 2118 2119 2120 2121 2122 2123 2124 2125 2126 2127 | /* * Start of a named argument group. Handle all of them together. */ for (; iArg < argCt; iArg++) { if ((localPtr->argSpecPtr != NULL) && (localPtr->argSpecPtr->remainAfterNamedGroup > 0) && ((argCt - iArg) <= localPtr->argSpecPtr->remainAfterNamedGroup)) { /* Remaining arguments must be assigned to remaining * positional locals after named group, end handling */ break; } optStr = TclGetString(argObjs[iArg]); if (*optStr != '-') { /* argument without leading dash, end named group */ |
︙ | ︙ |
Changes to tests/proc-enh.test.
︙ | ︙ | |||
176 177 178 179 180 181 182 | } {{0 B 5 D E} {0 0 foo 0 E}} test proc-enh-2.14 {correct usage: named arg without required is optionnal} { proc p {{a -name A}} { if {[info exists a]} { return $a } else { return unset } }; list [p] [p -A 2] } {unset 2} | | | | | | | 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | } {{0 B 5 D E} {0 0 foo 0 E}} test proc-enh-2.14 {correct usage: named arg without required is optionnal} { proc p {{a -name A}} { if {[info exists a]} { return $a } else { return unset } }; list [p] [p -A 2] } {unset 2} test proc-enh-2.15 {correct usage: fixed number of arguments after named group, automatically ended} { proc p {{a -name A -default 0} {b -name B -default 0} c} { list $a $b $c }; set l [list -Z 1] list [p -2] [p -A 1 -5] [p -- -3] [p --] } {{0 0 -2} {1 0 -5} {0 0 -3} {0 0 --}} # proc-enh-3.x: wrong # args test proc-enh-3.1 {wrong # args: -name arg without value} { proc p {{a -name A} {b -name B -default 1}} { } list [catch { p -A } msg] $msg $errorCode } {1 {wrong # args: should be "p ?|-A a|? ?|-B b|?"} {TCL WRONGARGS}} test proc-enh-3.2 {wrong # args: named group ended by an arg with leading dash} { |
︙ | ︙ |