Tcl Source Code

Check-in [8bc0f9df3c]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Rewrite of parts of the switch compiler to better use the powers of TclFindElement() and do less parsing on its own.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | core-8-5-branch
Files: files | file ages | folders
SHA1: 8bc0f9df3cd5d8c0da106dac9082fab1673e2cf4
User & Date: dgp 2011-05-02 20:34:04
Context
2011-05-03
17:34
Tighten SetDictFromAny(). check-in: 6a588e6fc4 user: dgp tags: core-8-5-branch
2011-05-02
21:12
Rewrite of parts of the switch compiler to better use the powers of TclFindElement() and do less par... check-in: 30dad692de user: dgp tags: trunk
20:34
Rewrite of parts of the switch compiler to better use the powers of TclFindElement() and do less par... check-in: 8bc0f9df3c user: dgp tags: core-8-5-branch
20:28
Drop old code. Closed-Leaf check-in: cc7e00bc14 user: dgp tags: dgp-switch-compile
20:10
Tests for expanded literals quoting detection. check-in: 52e2ce6ae1 user: dgp tags: core-8-5-branch
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to ChangeLog.

15
16
17
18
19
20
21



22
23
24
25





26
27
28
29
30
31
32
	***POTENTIAL INCOMPATIBILITY***
	For any callers calling in via the internal stubs table who really
	do use the final argument explicitly to check for the enclosing brace
	scenario.  Simply looking for the braces where they must be is the
	revision available to those callers, and it will backport cleanly.

	* tests/parse.test:	Tests for expanded literals quoting detection.




	* generic/tclInt.h:	Replace TclCountSpaceRuns() with
	* generic/tclListObj.c:	TclMaxListLength() which is the function we
	* generic/tclUtil.c:	actually want.






2011-04-28  Don Porter  <[email protected]>

	* generic/tclInt.h:	New utility routines:
	* generic/tclParse.c:	TclIsSpaceProc() and
	* generic/tclUtil.c:	TclCountSpaceRuns()








>
>
>




>
>
>
>
>







15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
	***POTENTIAL INCOMPATIBILITY***
	For any callers calling in via the internal stubs table who really
	do use the final argument explicitly to check for the enclosing brace
	scenario.  Simply looking for the braces where they must be is the
	revision available to those callers, and it will backport cleanly.

	* tests/parse.test:	Tests for expanded literals quoting detection.

	* generic/tclCompCmds.c:	New TclFindElement() is also a better
	fit for the [switch] compiler.

	* generic/tclInt.h:	Replace TclCountSpaceRuns() with
	* generic/tclListObj.c:	TclMaxListLength() which is the function we
	* generic/tclUtil.c:	actually want.
	* generic/tclCompCmds.c:

	* generic/tclCompCmds.c: Rewrite of parts of the switch compiler to
	better use the powers of TclFindElement() and do less parsing on
	its own.

2011-04-28  Don Porter  <[email protected]>

	* generic/tclInt.h:	New utility routines:
	* generic/tclParse.c:	TclIsSpaceProc() and
	* generic/tclUtil.c:	TclCountSpaceRuns()

Changes to generic/tclCompCmds.c.

3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
				 * there aren't any. */
    int contFixCount;		/* Number of continuation bodies pointing to
				 * the current (or next) real body. */

    int savedStackDepth = envPtr->currStackDepth;
    int noCase;			/* Has the -nocase flag been given? */
    int foundMode = 0;		/* Have we seen a mode flag yet? */
    int isListedArms = 0;
    int i, valueIndex;
    DefineLineInformation;	/* TIP #280 */
    int* clNext = envPtr->clNext;

    /*
     * Only handle the following versions:
     *   switch         ?--? word {pattern body ...}







<







3903
3904
3905
3906
3907
3908
3909

3910
3911
3912
3913
3914
3915
3916
				 * there aren't any. */
    int contFixCount;		/* Number of continuation bodies pointing to
				 * the current (or next) real body. */

    int savedStackDepth = envPtr->currStackDepth;
    int noCase;			/* Has the -nocase flag been given? */
    int foundMode = 0;		/* Have we seen a mode flag yet? */

    int i, valueIndex;
    DefineLineInformation;	/* TIP #280 */
    int* clNext = envPtr->clNext;

    /*
     * Only handle the following versions:
     *   switch         ?--? word {pattern body ...}
4043
4044
4045
4046
4047
4048
4049
4050
4051

4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
4067
4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106
4107
4108
4109
4110

4111
4112
4113
4114
4115
4116
4117

4118
4119
4120
4121
4122
4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
4136
4137
4138
4139




4140
4141
4142
4143
4144
4145
4146
4147
4148
4149
4150
4151
4152
4153
4154
4155
4156
4157
4158
4159

4160
4161
4162
4163
4164
4165
4166
4167
4168
4169
4170
4171
4172
4173
4174
4175
4176
4177
4178
4179
4180
4181
4182
4183
4184
4185
4186
4187
4188
     * that in the case of the quoted bodies, this is tricky as we cannot use
     * copies of the string from the input token for the generated tokens (it
     * causes a crash during exception handling). When multiple tokens are
     * available at this point, this is pretty easy.
     */

    if (numWords == 1) {
	Tcl_DString bodyList;
	const char **argv = NULL, *tokenStartPtr, *p;

	int bline;		/* TIP #280: line of the pattern/action list,
				 * and start of list for when tracking the
				 * location. This list comes immediately after
				 * the value we switch on. */
	int isTokenBraced;

	/*
	 * Test that we've got a suitable body list as a simple (i.e. braced)
	 * word, and that the elements of the body are simple words too. This
	 * is really rather nasty indeed.
	 */

	if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
	    return TCL_ERROR;
	}

	Tcl_DStringInit(&bodyList);
	Tcl_DStringAppend(&bodyList, tokenPtr[1].start, tokenPtr[1].size);
	if (Tcl_SplitList(NULL, Tcl_DStringValue(&bodyList), &numWords,
		&argv) != TCL_OK) {
	    Tcl_DStringFree(&bodyList);
	    return TCL_ERROR;
	}
	Tcl_DStringFree(&bodyList);

	/*
	 * Now we know what the switch arms are, we've got to see whether we
	 * can synthesize tokens for the arms. First check whether we've got a
	 * valid number of arms since we can do that now.
	 */

	if (numWords == 0 || numWords % 2) {
	    ckfree((char *) argv);
	    return TCL_ERROR;
	}

	isListedArms = 1;
	bodyTokenArray = (Tcl_Token *) ckalloc(sizeof(Tcl_Token) * numWords);
	bodyToken = (Tcl_Token **) ckalloc(sizeof(Tcl_Token *) * numWords);
	bodyLines = (int *) ckalloc(sizeof(int) * numWords);
	bodyNext  = (int **) ckalloc(sizeof(int*) * numWords);

	/*
	 * Locate the start of the arms within the overall word.
	 */

	bline = mapPtr->loc[eclIndex].line[valueIndex+1];
	p = tokenStartPtr = tokenPtr[1].start;
	while (isspace(UCHAR(*tokenStartPtr))) {
	    tokenStartPtr++;
	}
	if (*tokenStartPtr == '{') {
	    tokenStartPtr++;
	    isTokenBraced = 1;
	} else {
	    isTokenBraced = 0;
	}

	/*

	 * TIP #280: Count lines within the literal list.
	 */

	for (i=0 ; i<numWords ; i++) {
	    bodyTokenArray[i].type = TCL_TOKEN_TEXT;
	    bodyTokenArray[i].start = tokenStartPtr;
	    bodyTokenArray[i].size = strlen(argv[i]);

	    bodyTokenArray[i].numComponents = 0;
	    bodyToken[i] = bodyTokenArray+i;
	    tokenStartPtr += bodyTokenArray[i].size;

	    /*
	     * Test to see if we have guessed the end of the word correctly;
	     * if not, we can't feed the real string to the sub-compilation
	     * engine, and we're then stuck and so have to punt out to doing
	     * everything at runtime.
	     */

	    if ((isTokenBraced && *(tokenStartPtr++) != '}') ||
		    (tokenStartPtr < tokenPtr[1].start+tokenPtr[1].size
		    && !isspace(UCHAR(*tokenStartPtr)))) {
		ckfree((char *) argv);
		ckfree((char *) bodyToken);
		ckfree((char *) bodyTokenArray);
		ckfree((char *) bodyLines);
		ckfree((char *) bodyNext);
		return TCL_ERROR;
	    }





	    /*
	     * TIP #280: Now determine the line the list element starts on
	     * (there is no need to do it earlier, due to the possibility of
	     * aborting, see above).
	     */

	    TclAdvanceLines(&bline, p, bodyTokenArray[i].start);
	    TclAdvanceContinuations (&bline, &clNext,
				 bodyTokenArray[i].start - envPtr->source);
	    bodyLines[i] = bline;
	    bodyNext[i] = clNext;
	    p = bodyTokenArray[i].start;

	    while (isspace(UCHAR(*tokenStartPtr))) {
		tokenStartPtr++;
		if (tokenStartPtr >= tokenPtr[1].start+tokenPtr[1].size) {
		    break;
		}
	    }
	    if (*tokenStartPtr == '{') {

		tokenStartPtr++;
		isTokenBraced = 1;
	    } else {
		isTokenBraced = 0;
	    }
	}
	ckfree((char *) argv);

	/*
	 * Check that we've parsed everything we thought we were going to
	 * parse. If not, something odd is going on (I believe it is possible
	 * to defeat the code above) and we should bail out.
	 */

	if (tokenStartPtr != tokenPtr[1].start+tokenPtr[1].size) {
	    ckfree((char *) bodyToken);
	    ckfree((char *) bodyTokenArray);
	    ckfree((char *) bodyLines);
	    ckfree((char *) bodyNext);
	    return TCL_ERROR;
	}

    } else if (numWords % 2 || numWords == 0) {
	/*
	 * Odd number of words (>1) available, or no words at all available.
	 * Both are error cases, so punt and let the interpreted-version
	 * generate the error message. Note that the second case probably
	 * should get caught earlier, but it's easy to check here again anyway
	 * because it'd cause a nasty crash otherwise.







<
|
>




<

<
<
<
<
<
<



|
<
|
<
<
<
<
|
<
|
<
<
<
<
<
|
|
<


<
<
|
|
|
|
<
<
<
<


<
<
<
<
<
<
<
<
|
|
|
<
>
|
<

<
<
<
<
>
|
<
<
|
<
<
<
<
<
<
|
<
<
<
<







>
>
>
>






|

|
|
|
|
|
<
<
<
<
|
<
<
>
|
<
<
<
|
<
<
|
<
<
<
<
<
|
<
<
<
<
<
<

<







4042
4043
4044
4045
4046
4047
4048

4049
4050
4051
4052
4053
4054

4055






4056
4057
4058
4059

4060




4061

4062





4063
4064

4065
4066


4067
4068
4069
4070




4071
4072








4073
4074
4075

4076
4077

4078




4079
4080


4081






4082




4083
4084
4085
4086
4087
4088
4089
4090
4091
4092
4093
4094
4095
4096
4097
4098
4099
4100
4101
4102
4103
4104
4105
4106




4107


4108
4109



4110


4111





4112






4113

4114
4115
4116
4117
4118
4119
4120
     * that in the case of the quoted bodies, this is tricky as we cannot use
     * copies of the string from the input token for the generated tokens (it
     * causes a crash during exception handling). When multiple tokens are
     * available at this point, this is pretty easy.
     */

    if (numWords == 1) {

	CONST char *bytes;
	int maxLen, numBytes;
	int bline;		/* TIP #280: line of the pattern/action list,
				 * and start of list for when tracking the
				 * location. This list comes immediately after
				 * the value we switch on. */








	if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {
	    return TCL_ERROR;
	}
	bytes = tokenPtr[1].start;

	numBytes = tokenPtr[1].size;






	/* Allocate enough space to work in. */





	maxLen = TclMaxListLength(bytes, numBytes, NULL);
	if (maxLen < 2) {

	    return TCL_ERROR;
	}


	bodyTokenArray = (Tcl_Token *) ckalloc(sizeof(Tcl_Token) * maxLen);
	bodyToken = (Tcl_Token **) ckalloc(sizeof(Tcl_Token *) * maxLen);
	bodyLines = (int *) ckalloc(sizeof(int) * maxLen);
	bodyNext  = (int **) ckalloc(sizeof(int*) * maxLen);





	bline = mapPtr->loc[eclIndex].line[valueIndex+1];








	numWords = 0;

	while (numBytes > 0) {

	    CONST char *prevBytes = bytes;
	    int literal;






	    if (TCL_OK != TclFindElement(NULL, bytes, numBytes,
		    &(bodyTokenArray[numWords].start), &bytes,


		    &(bodyTokenArray[numWords].size), &literal) || !literal) {






	    abort:




		ckfree((char *) bodyToken);
		ckfree((char *) bodyTokenArray);
		ckfree((char *) bodyLines);
		ckfree((char *) bodyNext);
		return TCL_ERROR;
	    }

	    bodyTokenArray[numWords].type = TCL_TOKEN_TEXT;
	    bodyTokenArray[numWords].numComponents = 0;
	    bodyToken[numWords] = bodyTokenArray + numWords;

	    /*
	     * TIP #280: Now determine the line the list element starts on
	     * (there is no need to do it earlier, due to the possibility of
	     * aborting, see above).
	     */

	    TclAdvanceLines(&bline, prevBytes, bodyTokenArray[numWords].start);
	    TclAdvanceContinuations (&bline, &clNext,
		    bodyTokenArray[numWords].start - envPtr->source);
	    bodyLines[numWords] = bline;
	    bodyNext[numWords] = clNext;
	    TclAdvanceLines(&bline, bodyTokenArray[numWords].start, bytes);
	    TclAdvanceContinuations (&bline, &clNext, bytes - envPtr->source);







	    numBytes -= (bytes - prevBytes);
	    numWords++;



	}


	if (numWords % 2) {





	    goto abort;






	}

    } else if (numWords % 2 || numWords == 0) {
	/*
	 * Odd number of words (>1) available, or no words at all available.
	 * Both are error cases, so punt and let the interpreted-version
	 * generate the error message. Note that the second case probably
	 * should get caught earlier, but it's easy to check here again anyway
	 * because it'd cause a nasty crash otherwise.
4201
4202
4203
4204
4205
4206
4207
4208
4209
4210
4211
4212
4213
4214
4215
4216
	for (i=0 ; i<numWords ; i++) {
	    /*
	     * We only handle the very simplest case. Anything more complex is
	     * a good reason to go to the interpreted case anyway due to
	     * traces, etc.
	     */

	    if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD ||
		    tokenPtr->numComponents != 1) {
		ckfree((char *) bodyToken);
		ckfree((char *) bodyLines);
		ckfree((char *) bodyNext);
		return TCL_ERROR;
	    }
	    bodyToken[i] = tokenPtr+1;








|
<







4133
4134
4135
4136
4137
4138
4139
4140

4141
4142
4143
4144
4145
4146
4147
	for (i=0 ; i<numWords ; i++) {
	    /*
	     * We only handle the very simplest case. Anything more complex is
	     * a good reason to go to the interpreted case anyway due to
	     * traces, etc.
	     */

	    if (tokenPtr->type != TCL_TOKEN_SIMPLE_WORD) {

		ckfree((char *) bodyToken);
		ckfree((char *) bodyLines);
		ckfree((char *) bodyNext);
		return TCL_ERROR;
	    }
	    bodyToken[i] = tokenPtr+1;

4251
4252
4253
4254
4255
4256
4257
4258
4259
4260
4261
4262
4263
4264
4265
    /*
     * Check if we can generate a jump table, since if so that's faster than
     * doing an explicit compare with each body. Note that we're definitely
     * over-conservative with determining whether we can do the jump table,
     * but it handles the most common case well enough.
     */

    if (isListedArms && mode == Switch_Exact && !noCase) {
	JumptableInfo *jtPtr;
	int infoIndex, isNew, *finalFixups, numRealBodies = 0, jumpLocation;
	int mustGenerate, jumpToDefault;
	Tcl_DString buffer;
	Tcl_HashEntry *hPtr;

	/*







|







4182
4183
4184
4185
4186
4187
4188
4189
4190
4191
4192
4193
4194
4195
4196
    /*
     * Check if we can generate a jump table, since if so that's faster than
     * doing an explicit compare with each body. Note that we're definitely
     * over-conservative with determining whether we can do the jump table,
     * but it handles the most common case well enough.
     */

    if (mode == Switch_Exact) {
	JumptableInfo *jtPtr;
	int infoIndex, isNew, *finalFixups, numRealBodies = 0, jumpLocation;
	int mustGenerate, jumpToDefault;
	Tcl_DString buffer;
	Tcl_HashEntry *hPtr;

	/*