wasCSharpSQLite – Blame information for rev
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* |
2 | * Expression.java |
||
3 | * |
||
4 | * Copyright (c) 1997 Cornell University. |
||
5 | * Copyright (c) 1997 Sun Microsystems, Inc. |
||
6 | * |
||
7 | * See the file "license.terms" for information on usage and |
||
8 | * redistribution of this file, and for a DISCLAIMER OF ALL |
||
9 | * WARRANTIES. |
||
10 | * |
||
11 | * Included in SQLite3 port to C# for use in testharness only; 2008 Noah B Hart |
||
12 | * |
||
13 | * RCS @(#) $Id: Expression.java,v 1.10 2003/02/04 00:35:41 mdejong Exp $ |
||
14 | * |
||
15 | */ |
||
16 | using System; |
||
17 | using System.Collections; |
||
18 | |||
19 | namespace tcl.lang |
||
20 | { |
||
21 | |||
22 | /// <summary> This class handles Tcl expressions.</summary> |
||
23 | class Expression |
||
24 | { |
||
25 | |||
26 | // The token types are defined below. In addition, there is a |
||
27 | // table associating a precedence with each operator. The order |
||
28 | // of types is important. Consult the code before changing it. |
||
29 | |||
30 | internal const int VALUE = 0; |
||
31 | internal const int OPEN_PAREN = 1; |
||
32 | internal const int CLOSE_PAREN = 2; |
||
33 | internal const int COMMA = 3; |
||
34 | internal const int END = 4; |
||
35 | internal const int UNKNOWN = 5; |
||
36 | |||
37 | // Binary operators: |
||
38 | |||
39 | internal const int MULT = 8; |
||
40 | internal const int DIVIDE = 9; |
||
41 | internal const int MOD = 10; |
||
42 | internal const int PLUS = 11; |
||
43 | internal const int MINUS = 12; |
||
44 | internal const int LEFT_SHIFT = 13; |
||
45 | internal const int RIGHT_SHIFT = 14; |
||
46 | internal const int LESS = 15; |
||
47 | internal const int GREATER = 16; |
||
48 | internal const int LEQ = 17; |
||
49 | internal const int GEQ = 18; |
||
50 | internal const int EQUAL = 19; |
||
51 | internal const int NEQ = 20; |
||
52 | internal const int BIT_AND = 21; |
||
53 | internal const int BIT_XOR = 22; |
||
54 | internal const int BIT_OR = 23; |
||
55 | internal const int AND = 24; |
||
56 | internal const int OR = 25; |
||
57 | internal const int QUESTY = 26; |
||
58 | internal const int COLON = 27; |
||
59 | |||
60 | // Unary operators: |
||
61 | |||
62 | internal const int UNARY_MINUS = 28; |
||
63 | internal const int UNARY_PLUS = 29; |
||
64 | internal const int NOT = 30; |
||
65 | internal const int BIT_NOT = 31; |
||
66 | internal const int EQ = 32; |
||
67 | internal const int NE = 33; |
||
68 | |||
69 | // Precedence table. The values for non-operator token types are ignored. |
||
70 | |||
71 | internal static int[] precTable = new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 12, 11, 11, 10, 10, 9, 9, 9, 9, 8, 8, 7, 6, 5, 4, 3, 2, 1, 13, 13, 13, 13 }; |
||
72 | |||
73 | // Mapping from operator numbers to strings; used for error messages. |
||
74 | |||
75 | internal static string[] operatorStrings = new string[] { "VALUE", "(", ")", ",", "END", "UNKNOWN", "6", "7", "*", "/", "%", "+", "-", "<<", ">>", "<", ">", "<=", ">=", "==", "!=", "&", "^", "|", "&&", "||", "?", ":", "-", "+", "!", "~", "eq", "ne" }; |
||
76 | |||
77 | internal Hashtable mathFuncTable; |
||
78 | |||
79 | /// <summary> The entire expression, as originally passed to eval et al.</summary> |
||
80 | private string m_expr; |
||
81 | |||
82 | /// <summary> Length of the expression.</summary> |
||
83 | private int m_len; |
||
84 | |||
85 | /// <summary> Type of the last token to be parsed from the expression. |
||
86 | /// Corresponds to the characters just before expr. |
||
87 | /// </summary> |
||
88 | internal int m_token; |
||
89 | |||
90 | /// <summary> Position to the next character to be scanned from the expression |
||
91 | /// string. |
||
92 | /// </summary> |
||
93 | private int m_ind; |
||
94 | |||
95 | /// <summary> Evaluate a Tcl expression. |
||
96 | /// |
||
97 | /// </summary> |
||
98 | /// <param name="interp">the context in which to evaluate the expression. |
||
99 | /// </param> |
||
100 | /// <param name="string">expression to evaluate. |
||
101 | /// </param> |
||
102 | /// <returns> the value of the expression. |
||
103 | /// </returns> |
||
104 | /// <exception cref=""> TclException for malformed expressions. |
||
105 | /// </exception> |
||
106 | |||
107 | internal TclObject eval( Interp interp, string inString ) |
||
108 | { |
||
109 | ExprValue value = ExprTopLevel( interp, inString ); |
||
110 | switch ( value.type ) |
||
111 | { |
||
112 | |||
113 | case ExprValue.INT: |
||
114 | return TclInteger.newInstance( (int)value.intValue ); |
||
115 | |||
116 | case ExprValue.DOUBLE: |
||
117 | return TclDouble.newInstance( value.doubleValue ); |
||
118 | |||
119 | case ExprValue.STRING: |
||
120 | return TclString.newInstance( value.stringValue ); |
||
121 | |||
122 | default: |
||
123 | throw new TclRuntimeError( "internal error: expression, unknown" ); |
||
124 | |||
125 | } |
||
126 | } |
||
127 | |||
128 | /// <summary> Evaluate an Tcl expression.</summary> |
||
129 | /// <param name="interp">the context in which to evaluate the expression. |
||
130 | /// </param> |
||
131 | /// <param name="string">expression to evaluate. |
||
132 | /// </param> |
||
133 | /// <exception cref=""> TclException for malformed expressions. |
||
134 | /// </exception> |
||
135 | /// <returns> the value of the expression in boolean. |
||
136 | /// </returns> |
||
137 | internal bool evalBoolean( Interp interp, string inString ) |
||
138 | { |
||
139 | ExprValue value = ExprTopLevel( interp, inString ); |
||
140 | switch ( value.type ) |
||
141 | { |
||
142 | |||
143 | case ExprValue.INT: |
||
144 | return ( value.intValue != 0 ); |
||
145 | |||
146 | case ExprValue.DOUBLE: |
||
147 | return ( value.doubleValue != 0.0 ); |
||
148 | |||
149 | case ExprValue.STRING: |
||
150 | return Util.getBoolean( interp, value.stringValue ); |
||
151 | |||
152 | default: |
||
153 | throw new TclRuntimeError( "internal error: expression, unknown" ); |
||
154 | |||
155 | } |
||
156 | } |
||
157 | |||
158 | /// <summary> Constructor.</summary> |
||
159 | internal Expression() |
||
160 | { |
||
161 | mathFuncTable = new Hashtable(); |
||
162 | |||
163 | // rand -- needs testing |
||
164 | // srand -- needs testing |
||
165 | // hypot -- needs testing |
||
166 | // fmod -- needs testing |
||
167 | // try [expr fmod(4.67, 2.2)] |
||
168 | // the answer should be .27, but I got .2699999999999996 |
||
169 | |||
170 | SupportClass.PutElement( mathFuncTable, "atan2", new Atan2Function() ); |
||
171 | SupportClass.PutElement( mathFuncTable, "pow", new PowFunction() ); |
||
172 | SupportClass.PutElement( mathFuncTable, "acos", new AcosFunction() ); |
||
173 | SupportClass.PutElement( mathFuncTable, "asin", new AsinFunction() ); |
||
174 | SupportClass.PutElement( mathFuncTable, "atan", new AtanFunction() ); |
||
175 | SupportClass.PutElement( mathFuncTable, "ceil", new CeilFunction() ); |
||
176 | SupportClass.PutElement( mathFuncTable, "cos", new CosFunction() ); |
||
177 | SupportClass.PutElement( mathFuncTable, "cosh", new CoshFunction() ); |
||
178 | SupportClass.PutElement( mathFuncTable, "exp", new ExpFunction() ); |
||
179 | SupportClass.PutElement( mathFuncTable, "floor", new FloorFunction() ); |
||
180 | SupportClass.PutElement( mathFuncTable, "fmod", new FmodFunction() ); |
||
181 | SupportClass.PutElement( mathFuncTable, "hypot", new HypotFunction() ); |
||
182 | SupportClass.PutElement( mathFuncTable, "log", new LogFunction() ); |
||
183 | SupportClass.PutElement( mathFuncTable, "log10", new Log10Function() ); |
||
184 | SupportClass.PutElement( mathFuncTable, "rand", new RandFunction() ); |
||
185 | SupportClass.PutElement( mathFuncTable, "sin", new SinFunction() ); |
||
186 | SupportClass.PutElement( mathFuncTable, "sinh", new SinhFunction() ); |
||
187 | SupportClass.PutElement( mathFuncTable, "sqrt", new SqrtFunction() ); |
||
188 | SupportClass.PutElement( mathFuncTable, "srand", new SrandFunction() ); |
||
189 | SupportClass.PutElement( mathFuncTable, "tan", new TanFunction() ); |
||
190 | SupportClass.PutElement( mathFuncTable, "tanh", new TanhFunction() ); |
||
191 | |||
192 | SupportClass.PutElement( mathFuncTable, "abs", new AbsFunction() ); |
||
193 | SupportClass.PutElement( mathFuncTable, "double", new DoubleFunction() ); |
||
194 | SupportClass.PutElement( mathFuncTable, "int", new IntFunction() ); |
||
195 | SupportClass.PutElement( mathFuncTable, "round", new RoundFunction() ); |
||
196 | SupportClass.PutElement( mathFuncTable, "wide", new WideFunction() ); |
||
197 | |||
198 | m_expr = null; |
||
199 | m_ind = 0; |
||
200 | m_len = 0; |
||
201 | m_token = UNKNOWN; |
||
202 | } |
||
203 | |||
204 | /// <summary> Provides top-level functionality shared by procedures like ExprInt, |
||
205 | /// ExprDouble, etc. |
||
206 | /// </summary> |
||
207 | /// <param name="interp">the context in which to evaluate the expression. |
||
208 | /// </param> |
||
209 | /// <param name="string">the expression. |
||
210 | /// </param> |
||
211 | /// <exception cref=""> TclException for malformed expressions. |
||
212 | /// </exception> |
||
213 | /// <returns> the value of the expression. |
||
214 | /// </returns> |
||
215 | private ExprValue ExprTopLevel( Interp interp, string inString ) |
||
216 | { |
||
217 | |||
218 | // Saved the state variables so that recursive calls to expr |
||
219 | // can work: |
||
220 | // expr {[expr 1+2] + 3} |
||
221 | |||
222 | string m_expr_saved = m_expr; |
||
223 | int m_len_saved = m_len; |
||
224 | int m_token_saved = m_token; |
||
225 | int m_ind_saved = m_ind; |
||
226 | |||
227 | try |
||
228 | { |
||
229 | m_expr = inString; |
||
230 | m_ind = 0; |
||
231 | m_len = inString.Length; |
||
232 | m_token = UNKNOWN; |
||
233 | |||
234 | ExprValue val = ExprGetValue( interp, -1 ); |
||
235 | if ( m_token != END ) |
||
236 | { |
||
237 | SyntaxError( interp ); |
||
238 | } |
||
239 | return val; |
||
240 | } |
||
241 | finally |
||
242 | { |
||
243 | m_expr = m_expr_saved; |
||
244 | m_len = m_len_saved; |
||
245 | m_token = m_token_saved; |
||
246 | m_ind = m_ind_saved; |
||
247 | } |
||
248 | } |
||
249 | |||
250 | internal static void IllegalType( Interp interp, int badType, int Operator ) |
||
251 | { |
||
252 | throw new TclException( interp, "can't use " + ( ( badType == ExprValue.DOUBLE ) ? "floating-point value" : "non-numeric string" ) + " as operand of \"" + operatorStrings[Operator] + "\"" ); |
||
253 | } |
||
254 | |||
255 | internal void SyntaxError( Interp interp ) |
||
256 | { |
||
257 | throw new TclException( interp, "syntax error in expression \"" + m_expr + "\"" ); |
||
258 | } |
||
259 | |||
260 | internal static void DivideByZero( Interp interp ) |
||
261 | { |
||
262 | interp.setErrorCode( TclString.newInstance( "ARITH DIVZERO {divide by zero}" ) ); |
||
263 | throw new TclException( interp, "divide by zero" ); |
||
264 | } |
||
265 | |||
266 | internal static void IntegerTooLarge( Interp interp ) |
||
267 | { |
||
268 | interp.setErrorCode( TclString.newInstance( "ARITH IOVERFLOW {integer value too large to represent}" ) ); |
||
269 | throw new TclException( interp, "integer value too large to represent" ); |
||
270 | } |
||
271 | |||
272 | internal static void WideTooLarge( Interp interp ) |
||
273 | { |
||
274 | interp.setErrorCode( TclString.newInstance( "ARITH IOVERFLOW {wide value too large to represent}" ) ); |
||
275 | throw new TclException( interp, "wide value too large to represent" ); |
||
276 | } |
||
277 | |||
278 | internal static void DoubleTooLarge( Interp interp ) |
||
279 | { |
||
280 | interp.setErrorCode( TclString.newInstance( "ARITH OVERFLOW {floating-point value too large to represent}" ) ); |
||
281 | throw new TclException( interp, "floating-point value too large to represent" ); |
||
282 | } |
||
283 | |||
284 | internal static void DoubleTooSmall( Interp interp ) |
||
285 | { |
||
286 | interp.setErrorCode( TclString.newInstance( "ARITH UNDERFLOW {floating-point value too small to represent}" ) ); |
||
287 | throw new TclException( interp, "floating-point value too small to represent" ); |
||
288 | } |
||
289 | |||
290 | internal static void DomainError( Interp interp ) |
||
291 | { |
||
292 | interp.setErrorCode( TclString.newInstance( "ARITH DOMAIN {domain error: argument not in valid range}" ) ); |
||
293 | throw new TclException( interp, "domain error: argument not in valid range" ); |
||
294 | } |
||
295 | |||
296 | /// <summary> Given a string (such as one coming from command or variable |
||
297 | /// substitution), make a Value based on the string. The value |
||
298 | /// be a floating-point or integer, if possible, or else it |
||
299 | /// just be a copy of the string. |
||
300 | /// |
||
301 | /// </summary> |
||
302 | /// <param name="interp">the context in which to evaluate the expression. |
||
303 | /// </param> |
||
304 | /// <param name="s">the string to parse. |
||
305 | /// </param> |
||
306 | /// <exception cref=""> TclException for malformed expressions. |
||
307 | /// </exception> |
||
308 | /// <returns> the value of the expression. |
||
309 | /// </returns> |
||
310 | |||
311 | private ExprValue ExprParseString( Interp interp, string s ) |
||
312 | { |
||
313 | |||
314 | int len = s.Length; |
||
315 | |||
316 | /* |
||
317 | System.out.println("now to ExprParseString ->" + s + |
||
318 | "<- of length " + len);*/ |
||
319 | |||
320 | // Take shortcut when string is of length 0, as there is |
||
321 | // only a string rep for an empty string (no int or double rep) |
||
322 | // this will happend a lot so this shortcut will speed things up! |
||
323 | |||
324 | if ( len == 0 ) |
||
325 | { |
||
326 | return new ExprValue( s ); |
||
327 | } |
||
328 | |||
329 | // The strings "0" and "1" are going to occure a lot |
||
330 | // it might be wise to include shortcuts for these cases |
||
331 | |||
332 | |||
333 | int i; |
||
334 | if ( looksLikeInt( s, len, 0 ) ) |
||
335 | { |
||
336 | //System.out.println("string looks like an int"); |
||
337 | |||
338 | // Note: use strtoul instead of strtol for integer conversions |
||
339 | // to allow full-size unsigned numbers, but don't depend on |
||
340 | // strtoul to handle sign characters; it won't in some |
||
341 | // implementations. |
||
342 | |||
343 | for ( i = 0; System.Char.IsWhiteSpace( s[i] ); i++ ) |
||
344 | { |
||
345 | // Empty loop body. |
||
346 | } |
||
347 | |||
348 | StrtoulResult res; |
||
349 | if ( s[i] == '-' ) |
||
350 | { |
||
351 | i++; |
||
352 | res = Util.strtoul( s, i, 0 ); |
||
353 | res.value = -res.value; |
||
354 | } |
||
355 | else if ( s[i] == '+' ) |
||
356 | { |
||
357 | i++; |
||
358 | res = Util.strtoul( s, i, 0 ); |
||
359 | } |
||
360 | else |
||
361 | { |
||
362 | res = Util.strtoul( s, i, 0 ); |
||
363 | } |
||
364 | |||
365 | if ( res.errno == 0 ) |
||
366 | { |
||
367 | // We treat this string as a number if all the charcters |
||
368 | // following the parsed number are a whitespace char |
||
369 | // E.g.: " 1", "1", "1 ", and " 1 " are all good numbers |
||
370 | |||
371 | bool trailing_blanks = true; |
||
372 | |||
373 | for ( i = res.index; i < len; i++ ) |
||
374 | { |
||
375 | if ( !System.Char.IsWhiteSpace( s[i] ) ) |
||
376 | { |
||
377 | trailing_blanks = false; |
||
378 | } |
||
379 | } |
||
380 | |||
381 | if ( trailing_blanks ) |
||
382 | { |
||
383 | //System.out.println("string is an Integer of value " + res.value); |
||
384 | m_token = VALUE; |
||
385 | return new ExprValue( res.value ); |
||
386 | } |
||
387 | } |
||
388 | else if ( res.errno == TCL.INTEGER_RANGE ) |
||
389 | { |
||
390 | IntegerTooLarge( interp ); |
||
391 | } |
||
392 | |||
393 | |||
394 | /* |
||
395 | if (res.index == len) { |
||
396 | // We treat this string as a number only if the number |
||
397 | // ends at the end of the string. E.g.: " 1", "1" are |
||
398 | // good numbers but "1 " is not. |
||
399 | |||
400 | if (res.errno == TCL.INTEGER_RANGE) { |
||
401 | IntegerTooLarge(interp); |
||
402 | } else { |
||
403 | m_token = VALUE; |
||
404 | return new ExprValue(res.value); |
||
405 | } |
||
406 | }*/ |
||
407 | } |
||
408 | else |
||
409 | { |
||
410 | //System.out.println("string does not look like an int, checking for Double"); |
||
411 | |||
412 | StrtodResult res = Util.strtod( s, 0 ); |
||
413 | |||
414 | if ( res.errno == 0 ) |
||
415 | { |
||
416 | // Trailing whitespaces are treated just like the Integer case |
||
417 | |||
418 | bool trailing_blanks = true; |
||
419 | |||
420 | for ( i = res.index; i < len; i++ ) |
||
421 | { |
||
422 | if ( !System.Char.IsWhiteSpace( s[i] ) ) |
||
423 | { |
||
424 | trailing_blanks = false; |
||
425 | } |
||
426 | } |
||
427 | |||
428 | if ( trailing_blanks ) |
||
429 | { |
||
430 | //System.out.println("string is a Double of value " + res.value); |
||
431 | m_token = VALUE; |
||
432 | return new ExprValue( res.value ); |
||
433 | } |
||
434 | } |
||
435 | else if ( res.errno == TCL.DOUBLE_RANGE ) |
||
436 | { |
||
437 | if ( res.value != 0 ) |
||
438 | { |
||
439 | DoubleTooLarge( interp ); |
||
440 | } |
||
441 | else |
||
442 | { |
||
443 | DoubleTooSmall( interp ); |
||
444 | } |
||
445 | } |
||
446 | // if res.errno is any other value (like TCL.INVALID_DOUBLE) |
||
447 | // just fall through and use the string rep |
||
448 | |||
449 | |||
450 | /* |
||
451 | if (res.index == len) { |
||
452 | |||
453 | if (res.errno == 0) { |
||
454 | //System.out.println("string is a Double of value " + res.value); |
||
455 | m_token = VALUE; |
||
456 | return new ExprValue(res.value); |
||
457 | } else if (res.errno == TCL.DOUBLE_RANGE) { |
||
458 | DoubleTooLarge(interp); |
||
459 | } |
||
460 | }*/ |
||
461 | } |
||
462 | |||
463 | //System.out.println("string is not a valid number, returning as string"); |
||
464 | |||
465 | // Not a valid number. Save a string value (but don't do anything |
||
466 | // if it's already the value). |
||
467 | |||
468 | return new ExprValue( s ); |
||
469 | } |
||
470 | |||
471 | /// <summary> Parse a "value" from the remainder of the expression. |
||
472 | /// |
||
473 | /// </summary> |
||
474 | /// <param name="interp">the context in which to evaluate the expression. |
||
475 | /// </param> |
||
476 | /// <param name="prec">treat any un-parenthesized operator with precedence |
||
477 | /// <= this as the end of the expression. |
||
478 | /// </param> |
||
479 | /// <exception cref=""> TclException for malformed expressions. |
||
480 | /// </exception> |
||
481 | /// <returns> the value of the expression. |
||
482 | /// </returns> |
||
483 | private ExprValue ExprGetValue( Interp interp, int prec ) |
||
484 | { |
||
485 | int Operator; |
||
486 | bool gotOp = false; // True means already lexed the |
||
487 | // operator (while picking up value |
||
488 | // for unary operator). Don't lex |
||
489 | // again. |
||
490 | ExprValue value, value2; |
||
491 | |||
492 | // There are two phases to this procedure. First, pick off an |
||
493 | // initial value. Then, parse (binary operator, value) pairs |
||
494 | // until done. |
||
495 | |||
496 | value = ExprLex( interp ); |
||
497 | |||
498 | if ( m_token == OPEN_PAREN ) |
||
499 | { |
||
500 | |||
501 | // Parenthesized sub-expression. |
||
502 | |||
503 | value = ExprGetValue( interp, -1 ); |
||
504 | if ( m_token != CLOSE_PAREN ) |
||
505 | { |
||
506 | SyntaxError( interp ); |
||
507 | } |
||
508 | } |
||
509 | else |
||
510 | { |
||
511 | if ( m_token == MINUS ) |
||
512 | { |
||
513 | m_token = UNARY_MINUS; |
||
514 | } |
||
515 | if ( m_token == PLUS ) |
||
516 | { |
||
517 | m_token = UNARY_PLUS; |
||
518 | } |
||
519 | if ( m_token >= UNARY_MINUS ) |
||
520 | { |
||
521 | |||
522 | // Process unary operators. |
||
523 | |||
524 | Operator = m_token; |
||
525 | value = ExprGetValue( interp, precTable[m_token] ); |
||
526 | |||
527 | if ( interp.noEval == 0 ) |
||
528 | { |
||
529 | switch ( Operator ) |
||
530 | { |
||
531 | |||
532 | case UNARY_MINUS: |
||
533 | if ( value.type == ExprValue.INT ) |
||
534 | { |
||
535 | value.intValue = -value.intValue; |
||
536 | } |
||
537 | else if ( value.type == ExprValue.DOUBLE ) |
||
538 | { |
||
539 | value.doubleValue = -value.doubleValue; |
||
540 | } |
||
541 | else |
||
542 | { |
||
543 | IllegalType( interp, value.type, Operator ); |
||
544 | } |
||
545 | break; |
||
546 | |||
547 | case UNARY_PLUS: |
||
548 | if ( ( value.type != ExprValue.INT ) && ( value.type != ExprValue.DOUBLE ) ) |
||
549 | { |
||
550 | IllegalType( interp, value.type, Operator ); |
||
551 | } |
||
552 | break; |
||
553 | |||
554 | case NOT: |
||
555 | if ( value.type == ExprValue.INT ) |
||
556 | { |
||
557 | if ( value.intValue != 0 ) |
||
558 | { |
||
559 | value.intValue = 0; |
||
560 | } |
||
561 | else |
||
562 | { |
||
563 | value.intValue = 1; |
||
564 | } |
||
565 | } |
||
566 | else if ( value.type == ExprValue.DOUBLE ) |
||
567 | { |
||
568 | if ( value.doubleValue == 0.0 ) |
||
569 | { |
||
570 | value.intValue = 1; |
||
571 | } |
||
572 | else |
||
573 | { |
||
574 | value.intValue = 0; |
||
575 | } |
||
576 | value.type = ExprValue.INT; |
||
577 | } |
||
578 | else |
||
579 | { |
||
580 | IllegalType( interp, value.type, Operator ); |
||
581 | } |
||
582 | break; |
||
583 | |||
584 | case BIT_NOT: |
||
585 | if ( value.type == ExprValue.INT ) |
||
586 | { |
||
587 | value.intValue = ~value.intValue; |
||
588 | } |
||
589 | else |
||
590 | { |
||
591 | IllegalType( interp, value.type, Operator ); |
||
592 | } |
||
593 | break; |
||
594 | } |
||
595 | } |
||
596 | gotOp = true; |
||
597 | } |
||
598 | else if ( m_token == CLOSE_PAREN ) |
||
599 | { |
||
600 | // Caller needs to deal with close paren token. |
||
601 | return null; |
||
602 | } |
||
603 | else if ( m_token != VALUE ) |
||
604 | { |
||
605 | SyntaxError( interp ); |
||
606 | } |
||
607 | } |
||
608 | if ( value == null ) |
||
609 | { |
||
610 | SyntaxError( interp ); |
||
611 | } |
||
612 | |||
613 | // Got the first operand. Now fetch (operator, operand) pairs. |
||
614 | |||
615 | if ( !gotOp ) |
||
616 | { |
||
617 | value2 = ExprLex( interp ); |
||
618 | } |
||
619 | |||
620 | while ( true ) |
||
621 | { |
||
622 | Operator = m_token; |
||
623 | if ( ( Operator < MULT ) || ( Operator >= UNARY_MINUS ) ) |
||
624 | { |
||
625 | if ( ( Operator == END ) || ( Operator == CLOSE_PAREN ) || ( Operator == COMMA ) ) |
||
626 | { |
||
627 | return value; // Goto Done |
||
628 | } |
||
629 | else |
||
630 | { |
||
631 | SyntaxError( interp ); |
||
632 | } |
||
633 | } |
||
634 | if ( precTable[Operator] <= prec ) |
||
635 | { |
||
636 | return value; // (goto done) |
||
637 | } |
||
638 | |||
639 | // If we're doing an AND or OR and the first operand already |
||
640 | // determines the result, don't execute anything in the |
||
641 | // second operand: just parse. Same style for ?: pairs. |
||
642 | |||
643 | if ( ( Operator == AND ) || ( Operator == OR ) || ( Operator == QUESTY ) ) |
||
644 | { |
||
645 | |||
646 | if ( value.type == ExprValue.DOUBLE ) |
||
647 | { |
||
648 | value.intValue = ( value.doubleValue != 0 ) ? 1 : 0; |
||
649 | value.type = ExprValue.INT; |
||
650 | } |
||
651 | else if ( value.type == ExprValue.STRING ) |
||
652 | { |
||
653 | try |
||
654 | { |
||
655 | bool b = Util.getBoolean( null, value.stringValue ); |
||
656 | value = new ExprValue( b ? 1 : 0 ); |
||
657 | } |
||
658 | catch ( TclException e ) |
||
659 | { |
||
660 | if ( interp.noEval == 0 ) |
||
661 | { |
||
662 | IllegalType( interp, ExprValue.STRING, Operator ); |
||
663 | } |
||
664 | |||
665 | // Must set value.intValue to avoid referencing |
||
666 | // uninitialized memory in the "if" below; the actual |
||
667 | // value doesn't matter, since it will be ignored. |
||
668 | |||
669 | value.intValue = 0; |
||
670 | } |
||
671 | } |
||
672 | if ( ( ( Operator == AND ) && ( value.intValue == 0 ) ) || ( ( Operator == OR ) && ( value.intValue != 0 ) ) ) |
||
673 | { |
||
674 | interp.noEval++; |
||
675 | try |
||
676 | { |
||
677 | value2 = ExprGetValue( interp, precTable[Operator] ); |
||
678 | } |
||
679 | finally |
||
680 | { |
||
681 | interp.noEval--; |
||
682 | } |
||
683 | if ( Operator == OR ) |
||
684 | { |
||
685 | value.intValue = 1; |
||
686 | } |
||
687 | continue; |
||
688 | } |
||
689 | else if ( Operator == QUESTY ) |
||
690 | { |
||
691 | // Special note: ?: operators must associate right to |
||
692 | // left. To make this happen, use a precedence one lower |
||
693 | // than QUESTY when calling ExprGetValue recursively. |
||
694 | |||
695 | if ( value.intValue != 0 ) |
||
696 | { |
||
697 | value = ExprGetValue( interp, precTable[QUESTY] - 1 ); |
||
698 | if ( m_token != COLON ) |
||
699 | { |
||
700 | SyntaxError( interp ); |
||
701 | } |
||
702 | |||
703 | interp.noEval++; |
||
704 | try |
||
705 | { |
||
706 | value2 = ExprGetValue( interp, precTable[QUESTY] - 1 ); |
||
707 | } |
||
708 | finally |
||
709 | { |
||
710 | interp.noEval--; |
||
711 | } |
||
712 | } |
||
713 | else |
||
714 | { |
||
715 | interp.noEval++; |
||
716 | try |
||
717 | { |
||
718 | value2 = ExprGetValue( interp, precTable[QUESTY] - 1 ); |
||
719 | } |
||
720 | finally |
||
721 | { |
||
722 | interp.noEval--; |
||
723 | } |
||
724 | if ( m_token != COLON ) |
||
725 | { |
||
726 | SyntaxError( interp ); |
||
727 | } |
||
728 | value = ExprGetValue( interp, precTable[QUESTY] - 1 ); |
||
729 | } |
||
730 | continue; |
||
731 | } |
||
732 | else |
||
733 | { |
||
734 | value2 = ExprGetValue( interp, precTable[Operator] ); |
||
735 | } |
||
736 | } |
||
737 | else |
||
738 | { |
||
739 | value2 = ExprGetValue( interp, precTable[Operator] ); |
||
740 | } |
||
741 | |||
742 | |||
743 | if ( ( m_token < MULT ) && ( m_token != VALUE ) && ( m_token != END ) && ( m_token != COMMA ) && ( m_token != CLOSE_PAREN ) ) |
||
744 | { |
||
745 | SyntaxError( interp ); |
||
746 | } |
||
747 | |||
748 | if ( interp.noEval != 0 ) |
||
749 | { |
||
750 | continue; |
||
751 | } |
||
752 | |||
753 | // At this point we've got two values and an operator. Check |
||
754 | // to make sure that the particular data types are appropriate |
||
755 | // for the particular operator, and perform type conversion |
||
756 | // if necessary. |
||
757 | |||
758 | switch ( Operator ) |
||
759 | { |
||
760 | |||
761 | |||
762 | // For the operators below, no strings are allowed and |
||
763 | // ints get converted to floats if necessary. |
||
764 | case MULT: |
||
765 | case DIVIDE: |
||
766 | case PLUS: |
||
767 | case MINUS: |
||
768 | if ( ( value.type == ExprValue.STRING ) || ( value2.type == ExprValue.STRING ) ) |
||
769 | { |
||
770 | IllegalType( interp, ExprValue.STRING, Operator ); |
||
771 | } |
||
772 | if ( value.type == ExprValue.DOUBLE ) |
||
773 | { |
||
774 | if ( value2.type == ExprValue.INT ) |
||
775 | { |
||
776 | value2.doubleValue = value2.intValue; |
||
777 | value2.type = ExprValue.DOUBLE; |
||
778 | } |
||
779 | } |
||
780 | else if ( value2.type == ExprValue.DOUBLE ) |
||
781 | { |
||
782 | if ( value.type == ExprValue.INT ) |
||
783 | { |
||
784 | value.doubleValue = value.intValue; |
||
785 | value.type = ExprValue.DOUBLE; |
||
786 | } |
||
787 | } |
||
788 | break; |
||
789 | |||
790 | // For the operators below, only integers are allowed. |
||
791 | |||
792 | |||
793 | case MOD: |
||
794 | case LEFT_SHIFT: |
||
795 | case RIGHT_SHIFT: |
||
796 | case BIT_AND: |
||
797 | case BIT_XOR: |
||
798 | case BIT_OR: |
||
799 | if ( value.type != ExprValue.INT ) |
||
800 | { |
||
801 | IllegalType( interp, value.type, Operator ); |
||
802 | } |
||
803 | else if ( value2.type != ExprValue.INT ) |
||
804 | { |
||
805 | IllegalType( interp, value2.type, Operator ); |
||
806 | } |
||
807 | break; |
||
808 | |||
809 | // For the operators below, any type is allowed but the |
||
810 | // two operands must have the same type. Convert integers |
||
811 | // to floats and either to strings, if necessary. |
||
812 | |||
813 | |||
814 | case LESS: |
||
815 | case GREATER: |
||
816 | case LEQ: |
||
817 | case GEQ: |
||
818 | case EQUAL: |
||
819 | case EQ: |
||
820 | case NEQ: |
||
821 | case NE: |
||
822 | if ( value.type == ExprValue.STRING ) |
||
823 | { |
||
824 | if ( value2.type != ExprValue.STRING ) |
||
825 | { |
||
826 | ExprMakeString( interp, value2 ); |
||
827 | } |
||
828 | } |
||
829 | else if ( value2.type == ExprValue.STRING ) |
||
830 | { |
||
831 | if ( value.type != ExprValue.STRING ) |
||
832 | { |
||
833 | ExprMakeString( interp, value ); |
||
834 | } |
||
835 | } |
||
836 | else if ( value.type == ExprValue.DOUBLE ) |
||
837 | { |
||
838 | if ( value2.type == ExprValue.INT ) |
||
839 | { |
||
840 | value2.doubleValue = value2.intValue; |
||
841 | value2.type = ExprValue.DOUBLE; |
||
842 | } |
||
843 | } |
||
844 | else if ( value2.type == ExprValue.DOUBLE ) |
||
845 | { |
||
846 | if ( value.type == ExprValue.INT ) |
||
847 | { |
||
848 | value.doubleValue = value.intValue; |
||
849 | value.type = ExprValue.DOUBLE; |
||
850 | } |
||
851 | } |
||
852 | break; |
||
853 | |||
854 | // For the operators below, no strings are allowed, but |
||
855 | // no int->double conversions are performed. |
||
856 | |||
857 | |||
858 | case AND: |
||
859 | case OR: |
||
860 | if ( value.type == ExprValue.STRING ) |
||
861 | { |
||
862 | IllegalType( interp, value.type, Operator ); |
||
863 | } |
||
864 | if ( value2.type == ExprValue.STRING ) |
||
865 | { |
||
866 | try |
||
867 | { |
||
868 | bool b = Util.getBoolean( null, value2.stringValue ); |
||
869 | value2 = new ExprValue( b ? 1 : 0 ); |
||
870 | } |
||
871 | catch ( TclException e ) |
||
872 | { |
||
873 | IllegalType( interp, value2.type, Operator ); |
||
874 | } |
||
875 | } |
||
876 | break; |
||
877 | |||
878 | // For the operators below, type and conversions are |
||
879 | // irrelevant: they're handled elsewhere. |
||
880 | |||
881 | |||
882 | case QUESTY: |
||
883 | case COLON: |
||
884 | break; |
||
885 | |||
886 | // Any other operator is an error. |
||
887 | |||
888 | |||
889 | default: |
||
890 | throw new TclException( interp, "unknown operator in expression" ); |
||
891 | |||
892 | } |
||
893 | |||
894 | // Carry out the function of the specified operator. |
||
895 | |||
896 | switch ( Operator ) |
||
897 | { |
||
898 | |||
899 | case MULT: |
||
900 | if ( value.type == ExprValue.INT ) |
||
901 | { |
||
902 | value.intValue = value.intValue * value2.intValue; |
||
903 | } |
||
904 | else |
||
905 | { |
||
906 | value.doubleValue *= value2.doubleValue; |
||
907 | } |
||
908 | break; |
||
909 | |||
910 | case DIVIDE: |
||
911 | case MOD: |
||
912 | if ( value.type == ExprValue.INT ) |
||
913 | { |
||
914 | long divisor, quot, rem; |
||
915 | bool negative; |
||
916 | |||
917 | if ( value2.intValue == 0 ) |
||
918 | { |
||
919 | DivideByZero( interp ); |
||
920 | } |
||
921 | |||
922 | // The code below is tricky because C doesn't guarantee |
||
923 | // much about the properties of the quotient or |
||
924 | // remainder, but Tcl does: the remainder always has |
||
925 | // the same sign as the divisor and a smaller absolute |
||
926 | // value. |
||
927 | |||
928 | divisor = value2.intValue; |
||
929 | negative = false; |
||
930 | if ( divisor < 0 ) |
||
931 | { |
||
932 | divisor = -divisor; |
||
933 | value.intValue = -value.intValue; |
||
934 | negative = true; |
||
935 | } |
||
936 | quot = value.intValue / divisor; |
||
937 | rem = value.intValue % divisor; |
||
938 | if ( rem < 0 ) |
||
939 | { |
||
940 | rem += divisor; |
||
941 | quot -= 1; |
||
942 | } |
||
943 | if ( negative ) |
||
944 | { |
||
945 | rem = -rem; |
||
946 | } |
||
947 | value.intValue = ( Operator == DIVIDE ) ? quot : rem; |
||
948 | } |
||
949 | else |
||
950 | { |
||
951 | if ( value2.doubleValue == 0.0 ) |
||
952 | { |
||
953 | DivideByZero( interp ); |
||
954 | } |
||
955 | value.doubleValue /= value2.doubleValue; |
||
956 | } |
||
957 | break; |
||
958 | |||
959 | case PLUS: |
||
960 | if ( value.type == ExprValue.INT ) |
||
961 | { |
||
962 | value.intValue = value.intValue + value2.intValue; |
||
963 | } |
||
964 | else |
||
965 | { |
||
966 | value.doubleValue += value2.doubleValue; |
||
967 | } |
||
968 | break; |
||
969 | |||
970 | case MINUS: |
||
971 | if ( value.type == ExprValue.INT ) |
||
972 | { |
||
973 | value.intValue = value.intValue - value2.intValue; |
||
974 | } |
||
975 | else |
||
976 | { |
||
977 | value.doubleValue -= value2.doubleValue; |
||
978 | } |
||
979 | break; |
||
980 | |||
981 | case LEFT_SHIFT: |
||
982 | value.intValue <<= (int)value2.intValue; |
||
983 | break; |
||
984 | |||
985 | case RIGHT_SHIFT: |
||
986 | |||
987 | if ( value.intValue < 0 ) |
||
988 | { |
||
989 | value.intValue = ~( ( ~value.intValue ) >> (int)value2.intValue ); |
||
990 | } |
||
991 | else |
||
992 | { |
||
993 | value.intValue >>= (int)value2.intValue; |
||
994 | } |
||
995 | break; |
||
996 | |||
997 | case LESS: |
||
998 | if ( value.type == ExprValue.INT ) |
||
999 | { |
||
1000 | value.intValue = ( value.intValue < value2.intValue ) ? 1 : 0; |
||
1001 | } |
||
1002 | else if ( value.type == ExprValue.DOUBLE ) |
||
1003 | { |
||
1004 | value.intValue = ( value.doubleValue < value2.doubleValue ) ? 1 : 0; |
||
1005 | } |
||
1006 | else |
||
1007 | { |
||
1008 | value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) < 0 ) ? 1 : 0; |
||
1009 | } |
||
1010 | value.type = ExprValue.INT; |
||
1011 | break; |
||
1012 | |||
1013 | case GREATER: |
||
1014 | if ( value.type == ExprValue.INT ) |
||
1015 | { |
||
1016 | value.intValue = ( value.intValue > value2.intValue ) ? 1 : 0; |
||
1017 | } |
||
1018 | else if ( value.type == ExprValue.DOUBLE ) |
||
1019 | { |
||
1020 | value.intValue = ( value.doubleValue > value2.doubleValue ) ? 1 : 0; |
||
1021 | } |
||
1022 | else |
||
1023 | { |
||
1024 | value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) > 0 ) ? 1 : 0; |
||
1025 | } |
||
1026 | value.type = ExprValue.INT; |
||
1027 | break; |
||
1028 | |||
1029 | case LEQ: |
||
1030 | if ( value.type == ExprValue.INT ) |
||
1031 | { |
||
1032 | value.intValue = ( value.intValue <= value2.intValue ) ? 1 : 0; |
||
1033 | } |
||
1034 | else if ( value.type == ExprValue.DOUBLE ) |
||
1035 | { |
||
1036 | value.intValue = ( value.doubleValue <= value2.doubleValue ) ? 1 : 0; |
||
1037 | } |
||
1038 | else |
||
1039 | { |
||
1040 | value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) <= 0 ) ? 1 : 0; |
||
1041 | } |
||
1042 | value.type = ExprValue.INT; |
||
1043 | break; |
||
1044 | |||
1045 | case GEQ: |
||
1046 | if ( value.type == ExprValue.INT ) |
||
1047 | { |
||
1048 | value.intValue = ( value.intValue >= value2.intValue ) ? 1 : 0; |
||
1049 | } |
||
1050 | else if ( value.type == ExprValue.DOUBLE ) |
||
1051 | { |
||
1052 | value.intValue = ( value.doubleValue >= value2.doubleValue ) ? 1 : 0; |
||
1053 | } |
||
1054 | else |
||
1055 | { |
||
1056 | value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) >= 0 ) ? 1 : 0; |
||
1057 | } |
||
1058 | value.type = ExprValue.INT; |
||
1059 | break; |
||
1060 | |||
1061 | case EQUAL: |
||
1062 | case EQ: |
||
1063 | if ( value.type == ExprValue.INT ) |
||
1064 | { |
||
1065 | value.intValue = ( value.intValue == value2.intValue ) ? 1 : 0; |
||
1066 | } |
||
1067 | else if ( value.type == ExprValue.DOUBLE ) |
||
1068 | { |
||
1069 | value.intValue = ( value.doubleValue == value2.doubleValue ) ? 1 : 0; |
||
1070 | } |
||
1071 | else |
||
1072 | { |
||
1073 | value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) == 0 ) ? 1 : 0; |
||
1074 | } |
||
1075 | value.type = ExprValue.INT; |
||
1076 | break; |
||
1077 | |||
1078 | case NEQ: |
||
1079 | case NE: |
||
1080 | if ( value.type == ExprValue.INT ) |
||
1081 | { |
||
1082 | value.intValue = ( value.intValue != value2.intValue ) ? 1 : 0; |
||
1083 | } |
||
1084 | else if ( value.type == ExprValue.DOUBLE ) |
||
1085 | { |
||
1086 | value.intValue = ( value.doubleValue != value2.doubleValue ) ? 1 : 0; |
||
1087 | } |
||
1088 | else |
||
1089 | { |
||
1090 | value.intValue = ( value.stringValue.CompareTo( value2.stringValue ) != 0 ) ? 1 : 0; |
||
1091 | } |
||
1092 | value.type = ExprValue.INT; |
||
1093 | break; |
||
1094 | |||
1095 | case BIT_AND: |
||
1096 | value.intValue &= value2.intValue; |
||
1097 | break; |
||
1098 | |||
1099 | case BIT_XOR: |
||
1100 | value.intValue ^= value2.intValue; |
||
1101 | break; |
||
1102 | |||
1103 | case BIT_OR: |
||
1104 | value.intValue |= value2.intValue; |
||
1105 | break; |
||
1106 | |||
1107 | // For AND and OR, we know that the first value has already |
||
1108 | // been converted to an integer. Thus we need only consider |
||
1109 | // the possibility of int vs. double for the second value. |
||
1110 | |||
1111 | |||
1112 | case AND: |
||
1113 | if ( value2.type == ExprValue.DOUBLE ) |
||
1114 | { |
||
1115 | value2.intValue = ( value2.doubleValue != 0 ) ? 1 : 0; |
||
1116 | value2.type = ExprValue.INT; |
||
1117 | } |
||
1118 | value.intValue = ( ( value.intValue != 0 ) && ( value2.intValue != 0 ) ) ? 1 : 0; |
||
1119 | break; |
||
1120 | |||
1121 | case OR: |
||
1122 | if ( value2.type == ExprValue.DOUBLE ) |
||
1123 | { |
||
1124 | value2.intValue = ( value2.doubleValue != 0 ) ? 1 : 0; |
||
1125 | value2.type = ExprValue.INT; |
||
1126 | } |
||
1127 | value.intValue = ( ( value.intValue != 0 ) || ( value2.intValue != 0 ) ) ? 1 : 0; |
||
1128 | break; |
||
1129 | |||
1130 | |||
1131 | case COLON: |
||
1132 | SyntaxError( interp ); |
||
1133 | break; |
||
1134 | } |
||
1135 | } |
||
1136 | } |
||
1137 | |||
1138 | /// <summary> GetLexeme -> ExprLex |
||
1139 | /// |
||
1140 | /// Lexical analyzer for expression parser: parses a single value, |
||
1141 | /// operator, or other syntactic element from an expression string. |
||
1142 | /// |
||
1143 | /// Size effects: the "m_token" member variable is set to the value of |
||
1144 | /// the current token. |
||
1145 | /// |
||
1146 | /// </summary> |
||
1147 | /// <param name="interp">the context in which to evaluate the expression. |
||
1148 | /// </param> |
||
1149 | /// <exception cref=""> TclException for malformed expressions. |
||
1150 | /// </exception> |
||
1151 | /// <returns> the value of the expression. |
||
1152 | /// </returns> |
||
1153 | private ExprValue ExprLex( Interp interp ) |
||
1154 | { |
||
1155 | char c, c2; |
||
1156 | |||
1157 | while ( m_ind < m_len && System.Char.IsWhiteSpace( m_expr[m_ind] ) ) |
||
1158 | { |
||
1159 | m_ind++; |
||
1160 | } |
||
1161 | if ( m_ind >= m_len ) |
||
1162 | { |
||
1163 | m_token = END; |
||
1164 | return null; |
||
1165 | } |
||
1166 | |||
1167 | // First try to parse the token as an integer or |
||
1168 | // floating-point number. Don't want to check for a number if |
||
1169 | // the first character is "+" or "-". If we do, we might |
||
1170 | // treat a binary operator as unary by |
||
1171 | // mistake, which will eventually cause a syntax error. |
||
1172 | |||
1173 | c = m_expr[m_ind]; |
||
1174 | if ( m_ind < m_len - 1 ) |
||
1175 | { |
||
1176 | c2 = m_expr[m_ind + 1]; |
||
1177 | } |
||
1178 | else |
||
1179 | { |
||
1180 | c2 = '\x0000'; |
||
1181 | } |
||
1182 | |||
1183 | if ( ( c != '+' ) && ( c != '-' ) ) |
||
1184 | { |
||
1185 | bool startsWithDigit = System.Char.IsDigit( c ); |
||
1186 | if ( startsWithDigit && looksLikeInt( m_expr, m_len, m_ind ) ) |
||
1187 | { |
||
1188 | StrtoulResult res = Util.strtoul( m_expr, m_ind, 0 ); |
||
1189 | |||
1190 | if ( res.errno == 0 ) |
||
1191 | { |
||
1192 | m_ind = res.index; |
||
1193 | m_token = VALUE; |
||
1194 | return new ExprValue( res.value ); |
||
1195 | } |
||
1196 | else |
||
1197 | { |
||
1198 | if ( res.errno == TCL.INTEGER_RANGE ) |
||
1199 | { |
||
1200 | IntegerTooLarge( interp ); |
||
1201 | } |
||
1202 | } |
||
1203 | } |
||
1204 | else if ( startsWithDigit || ( c == '.' ) || ( c == 'n' ) || ( c == 'N' ) ) |
||
1205 | { |
||
1206 | StrtodResult res = Util.strtod( m_expr, m_ind ); |
||
1207 | if ( res.errno == 0 ) |
||
1208 | { |
||
1209 | m_ind = res.index; |
||
1210 | m_token = VALUE; |
||
1211 | return new ExprValue( res.value ); |
||
1212 | } |
||
1213 | else |
||
1214 | { |
||
1215 | if ( res.errno == TCL.DOUBLE_RANGE ) |
||
1216 | { |
||
1217 | if ( res.value != 0 ) |
||
1218 | { |
||
1219 | DoubleTooLarge( interp ); |
||
1220 | } |
||
1221 | else |
||
1222 | { |
||
1223 | DoubleTooSmall( interp ); |
||
1224 | } |
||
1225 | } |
||
1226 | } |
||
1227 | } |
||
1228 | } |
||
1229 | |||
1230 | ParseResult pres; |
||
1231 | ExprValue retval; |
||
1232 | m_ind += 1; // ind is advanced to point to the next token |
||
1233 | |||
1234 | switch ( c ) |
||
1235 | { |
||
1236 | |||
1237 | case '$': |
||
1238 | m_token = VALUE; |
||
1239 | pres = ParseAdaptor.parseVar( interp, m_expr, m_ind, m_len ); |
||
1240 | m_ind = pres.nextIndex; |
||
1241 | |||
1242 | if ( interp.noEval != 0 ) |
||
1243 | { |
||
1244 | retval = new ExprValue( 0 ); |
||
1245 | } |
||
1246 | else |
||
1247 | { |
||
1248 | |||
1249 | retval = ExprParseString( interp, pres.value.ToString() ); |
||
1250 | } |
||
1251 | pres.release(); |
||
1252 | return retval; |
||
1253 | |||
1254 | case '[': |
||
1255 | m_token = VALUE; |
||
1256 | pres = ParseAdaptor.parseNestedCmd( interp, m_expr, m_ind, m_len ); |
||
1257 | m_ind = pres.nextIndex; |
||
1258 | |||
1259 | if ( interp.noEval != 0 ) |
||
1260 | { |
||
1261 | retval = new ExprValue( 0 ); |
||
1262 | } |
||
1263 | else |
||
1264 | { |
||
1265 | |||
1266 | retval = ExprParseString( interp, pres.value.ToString() ); |
||
1267 | } |
||
1268 | pres.release(); |
||
1269 | return retval; |
||
1270 | |||
1271 | case '"': |
||
1272 | m_token = VALUE; |
||
1273 | |||
1274 | |||
1275 | //System.out.println("now to parse from ->" + m_expr + "<- at index " |
||
1276 | // + m_ind); |
||
1277 | |||
1278 | pres = ParseAdaptor.parseQuotes( interp, m_expr, m_ind, m_len ); |
||
1279 | m_ind = pres.nextIndex; |
||
1280 | |||
1281 | // System.out.println("after parse next index is " + m_ind); |
||
1282 | |||
1283 | if ( interp.noEval != 0 ) |
||
1284 | { |
||
1285 | // System.out.println("returning noEval zero value"); |
||
1286 | retval = new ExprValue( 0 ); |
||
1287 | } |
||
1288 | else |
||
1289 | { |
||
1290 | // System.out.println("returning value string ->" + pres.value.toString() + "<-" ); |
||
1291 | |||
1292 | retval = ExprParseString( interp, pres.value.ToString() ); |
||
1293 | } |
||
1294 | pres.release(); |
||
1295 | return retval; |
||
1296 | |||
1297 | case '{': |
||
1298 | m_token = VALUE; |
||
1299 | pres = ParseAdaptor.parseBraces( interp, m_expr, m_ind, m_len ); |
||
1300 | m_ind = pres.nextIndex; |
||
1301 | if ( interp.noEval != 0 ) |
||
1302 | { |
||
1303 | retval = new ExprValue( 0 ); |
||
1304 | } |
||
1305 | else |
||
1306 | { |
||
1307 | |||
1308 | retval = ExprParseString( interp, pres.value.ToString() ); |
||
1309 | } |
||
1310 | pres.release(); |
||
1311 | return retval; |
||
1312 | |||
1313 | case '(': |
||
1314 | m_token = OPEN_PAREN; |
||
1315 | return null; |
||
1316 | |||
1317 | |||
1318 | case ')': |
||
1319 | m_token = CLOSE_PAREN; |
||
1320 | return null; |
||
1321 | |||
1322 | |||
1323 | case ',': |
||
1324 | m_token = COMMA; |
||
1325 | return null; |
||
1326 | |||
1327 | |||
1328 | case '*': |
||
1329 | m_token = MULT; |
||
1330 | return null; |
||
1331 | |||
1332 | |||
1333 | case '/': |
||
1334 | m_token = DIVIDE; |
||
1335 | return null; |
||
1336 | |||
1337 | |||
1338 | case '%': |
||
1339 | m_token = MOD; |
||
1340 | return null; |
||
1341 | |||
1342 | |||
1343 | case '+': |
||
1344 | m_token = PLUS; |
||
1345 | return null; |
||
1346 | |||
1347 | |||
1348 | case '-': |
||
1349 | m_token = MINUS; |
||
1350 | return null; |
||
1351 | |||
1352 | |||
1353 | case '?': |
||
1354 | m_token = QUESTY; |
||
1355 | return null; |
||
1356 | |||
1357 | |||
1358 | case ':': |
||
1359 | m_token = COLON; |
||
1360 | return null; |
||
1361 | |||
1362 | |||
1363 | case '<': |
||
1364 | switch ( c2 ) |
||
1365 | { |
||
1366 | |||
1367 | case '<': |
||
1368 | m_ind += 1; |
||
1369 | m_token = LEFT_SHIFT; |
||
1370 | break; |
||
1371 | |||
1372 | case '=': |
||
1373 | m_ind += 1; |
||
1374 | m_token = LEQ; |
||
1375 | break; |
||
1376 | |||
1377 | default: |
||
1378 | m_token = LESS; |
||
1379 | break; |
||
1380 | |||
1381 | } |
||
1382 | return null; |
||
1383 | |||
1384 | |||
1385 | case '>': |
||
1386 | switch ( c2 ) |
||
1387 | { |
||
1388 | |||
1389 | case '>': |
||
1390 | m_ind += 1; |
||
1391 | m_token = RIGHT_SHIFT; |
||
1392 | break; |
||
1393 | |||
1394 | case '=': |
||
1395 | m_ind += 1; |
||
1396 | m_token = GEQ; |
||
1397 | break; |
||
1398 | |||
1399 | default: |
||
1400 | m_token = GREATER; |
||
1401 | break; |
||
1402 | |||
1403 | } |
||
1404 | return null; |
||
1405 | |||
1406 | |||
1407 | case '=': |
||
1408 | if ( c2 == '=' ) |
||
1409 | { |
||
1410 | m_ind += 1; |
||
1411 | m_token = EQUAL; |
||
1412 | } |
||
1413 | else |
||
1414 | { |
||
1415 | m_token = UNKNOWN; |
||
1416 | } |
||
1417 | return null; |
||
1418 | |||
1419 | |||
1420 | case 'e': |
||
1421 | if ( c2 == 'q' ) |
||
1422 | { |
||
1423 | m_ind += 1; |
||
1424 | m_token = EQUAL; |
||
1425 | } |
||
1426 | else |
||
1427 | { |
||
1428 | m_token = UNKNOWN; |
||
1429 | } |
||
1430 | return null; |
||
1431 | |||
1432 | |||
1433 | case 'n': |
||
1434 | if ( c2 == 'e' ) |
||
1435 | { |
||
1436 | m_ind += 1; |
||
1437 | m_token = NEQ; |
||
1438 | } |
||
1439 | else |
||
1440 | { |
||
1441 | m_token = UNKNOWN; |
||
1442 | } |
||
1443 | return null; |
||
1444 | |||
1445 | |||
1446 | case '!': |
||
1447 | if ( c2 == '=' ) |
||
1448 | { |
||
1449 | m_ind += 1; |
||
1450 | m_token = NEQ; |
||
1451 | } |
||
1452 | else |
||
1453 | { |
||
1454 | m_token = NOT; |
||
1455 | } |
||
1456 | return null; |
||
1457 | |||
1458 | |||
1459 | case '&': |
||
1460 | if ( c2 == '&' ) |
||
1461 | { |
||
1462 | m_ind += 1; |
||
1463 | m_token = AND; |
||
1464 | } |
||
1465 | else |
||
1466 | { |
||
1467 | m_token = BIT_AND; |
||
1468 | } |
||
1469 | return null; |
||
1470 | |||
1471 | |||
1472 | case '^': |
||
1473 | m_token = BIT_XOR; |
||
1474 | return null; |
||
1475 | |||
1476 | |||
1477 | case '|': |
||
1478 | if ( c2 == '|' ) |
||
1479 | { |
||
1480 | m_ind += 1; |
||
1481 | m_token = OR; |
||
1482 | } |
||
1483 | else |
||
1484 | { |
||
1485 | m_token = BIT_OR; |
||
1486 | } |
||
1487 | return null; |
||
1488 | |||
1489 | |||
1490 | case '~': |
||
1491 | m_token = BIT_NOT; |
||
1492 | return null; |
||
1493 | |||
1494 | |||
1495 | default: |
||
1496 | if ( System.Char.IsLetter( c ) ) |
||
1497 | { |
||
1498 | // Oops, re-adjust m_ind so that it points to the beginning |
||
1499 | // of the function name. |
||
1500 | |||
1501 | m_ind--; |
||
1502 | return mathFunction( interp ); |
||
1503 | } |
||
1504 | m_token = UNKNOWN; |
||
1505 | return null; |
||
1506 | |||
1507 | } |
||
1508 | } |
||
1509 | |||
1510 | /// <summary> Parses a math function from an expression string, carry out the |
||
1511 | /// function, and return the value computed. |
||
1512 | /// |
||
1513 | /// </summary> |
||
1514 | /// <param name="interp">current interpreter. |
||
1515 | /// </param> |
||
1516 | /// <returns> the value computed by the math function. |
||
1517 | /// </returns> |
||
1518 | /// <exception cref=""> TclException if any error happens. |
||
1519 | /// </exception> |
||
1520 | internal ExprValue mathFunction( Interp interp ) |
||
1521 | { |
||
1522 | int startIdx = m_ind; |
||
1523 | ExprValue value; |
||
1524 | string funcName; |
||
1525 | MathFunction mathFunc; |
||
1526 | TclObject[] argv = null; |
||
1527 | int numArgs; |
||
1528 | |||
1529 | // Find the end of the math function's name and lookup the MathFunc |
||
1530 | // record for the function. Search until the char at m_ind is not |
||
1531 | // alphanumeric or '_' |
||
1532 | |||
1533 | for ( ; m_ind < m_len; m_ind++ ) |
||
1534 | { |
||
1535 | if ( !( System.Char.IsLetterOrDigit( m_expr[m_ind] ) || m_expr[m_ind] == '_' ) ) |
||
1536 | { |
||
1537 | break; |
||
1538 | } |
||
1539 | } |
||
1540 | |||
1541 | // Get the funcName BEFORE calling ExprLex, so the funcName |
||
1542 | // will not have trailing whitespace. |
||
1543 | |||
1544 | funcName = m_expr.Substring( startIdx, ( m_ind ) - ( startIdx ) ); |
||
1545 | |||
1546 | // Parse errors are thrown BEFORE unknown function names |
||
1547 | |||
1548 | ExprLex( interp ); |
||
1549 | if ( m_token != OPEN_PAREN ) |
||
1550 | { |
||
1551 | SyntaxError( interp ); |
||
1552 | } |
||
1553 | |||
1554 | // Now test for unknown funcName. Doing the above statements |
||
1555 | // out of order will cause some tests to fail. |
||
1556 | |||
1557 | mathFunc = (MathFunction)mathFuncTable[funcName]; |
||
1558 | if ( mathFunc == null ) |
||
1559 | { |
||
1560 | throw new TclException( interp, "unknown math function \"" + funcName + "\"" ); |
||
1561 | } |
||
1562 | |||
1563 | // Scan off the arguments for the function, if there are any. |
||
1564 | |||
1565 | numArgs = mathFunc.argTypes.Length; |
||
1566 | |||
1567 | if ( numArgs == 0 ) |
||
1568 | { |
||
1569 | ExprLex( interp ); |
||
1570 | if ( m_token != CLOSE_PAREN ) |
||
1571 | { |
||
1572 | SyntaxError( interp ); |
||
1573 | } |
||
1574 | } |
||
1575 | else |
||
1576 | { |
||
1577 | argv = new TclObject[numArgs]; |
||
1578 | |||
1579 | for ( int i = 0; ; i++ ) |
||
1580 | { |
||
1581 | value = ExprGetValue( interp, -1 ); |
||
1582 | |||
1583 | // Handle close paren with no value |
||
1584 | // % expr {srand()} |
||
1585 | |||
1586 | if ( ( value == null ) && ( m_token == CLOSE_PAREN ) ) |
||
1587 | { |
||
1588 | if ( i == numArgs ) |
||
1589 | break; |
||
1590 | else |
||
1591 | throw new TclException( interp, "too few arguments for math function" ); |
||
1592 | } |
||
1593 | |||
1594 | if ( value.type == ExprValue.STRING ) |
||
1595 | { |
||
1596 | throw new TclException( interp, "argument to math function didn't have numeric value" ); |
||
1597 | } |
||
1598 | |||
1599 | // Copy the value to the argument record, converting it if |
||
1600 | // necessary. |
||
1601 | |||
1602 | if ( value.type == ExprValue.INT ) |
||
1603 | { |
||
1604 | if ( mathFunc.argTypes[i] == MathFunction.DOUBLE ) |
||
1605 | { |
||
1606 | argv[i] = TclDouble.newInstance( (int)value.intValue ); |
||
1607 | } |
||
1608 | else |
||
1609 | { |
||
1610 | argv[i] = TclLong.newInstance( value.intValue ); |
||
1611 | } |
||
1612 | } |
||
1613 | else |
||
1614 | { |
||
1615 | if ( mathFunc.argTypes[i] == MathFunction.INT ) |
||
1616 | { |
||
1617 | |||
1618 | argv[i] = TclInteger.newInstance( (int)value.doubleValue ); |
||
1619 | } |
||
1620 | else |
||
1621 | { |
||
1622 | argv[i] = TclDouble.newInstance( value.doubleValue ); |
||
1623 | } |
||
1624 | } |
||
1625 | |||
1626 | // Check for a comma separator between arguments or a |
||
1627 | // close-paren to end the argument list. |
||
1628 | |||
1629 | if ( i == ( numArgs - 1 ) ) |
||
1630 | { |
||
1631 | if ( m_token == CLOSE_PAREN ) |
||
1632 | { |
||
1633 | break; |
||
1634 | } |
||
1635 | if ( m_token == COMMA ) |
||
1636 | { |
||
1637 | throw new TclException( interp, "too many arguments for math function" ); |
||
1638 | } |
||
1639 | else |
||
1640 | { |
||
1641 | SyntaxError( interp ); |
||
1642 | } |
||
1643 | } |
||
1644 | if ( m_token != COMMA ) |
||
1645 | { |
||
1646 | if ( m_token == CLOSE_PAREN ) |
||
1647 | { |
||
1648 | throw new TclException( interp, "too few arguments for math function" ); |
||
1649 | } |
||
1650 | else |
||
1651 | { |
||
1652 | SyntaxError( interp ); |
||
1653 | } |
||
1654 | } |
||
1655 | } |
||
1656 | } |
||
1657 | |||
1658 | m_token = VALUE; |
||
1659 | if ( interp.noEval != 0 ) |
||
1660 | { |
||
1661 | return new ExprValue( 0 ); |
||
1662 | } |
||
1663 | else |
||
1664 | { |
||
1665 | /* |
||
1666 | * Invoke the function and copy its result back into valuePtr. |
||
1667 | */ |
||
1668 | return mathFunc.apply( interp, argv ); |
||
1669 | } |
||
1670 | } |
||
1671 | |||
1672 | /// <summary> This procedure decides whether the leading characters of a |
||
1673 | /// string look like an integer or something else (such as a |
||
1674 | /// floating-point number or string). |
||
1675 | /// </summary> |
||
1676 | /// <returns> a boolean value indicating if the string looks like an integer. |
||
1677 | /// </returns> |
||
1678 | |||
1679 | internal static bool looksLikeInt( string s, int len, int i ) |
||
1680 | { |
||
1681 | while ( i < len && System.Char.IsWhiteSpace( s[i] ) ) |
||
1682 | { |
||
1683 | i++; |
||
1684 | } |
||
1685 | if ( i >= len ) |
||
1686 | { |
||
1687 | return false; |
||
1688 | } |
||
1689 | char c = s[i]; |
||
1690 | if ( ( c == '+' ) || ( c == '-' ) ) |
||
1691 | { |
||
1692 | i++; |
||
1693 | if ( i >= len ) |
||
1694 | { |
||
1695 | return false; |
||
1696 | } |
||
1697 | c = s[i]; |
||
1698 | } |
||
1699 | if ( !System.Char.IsDigit( c ) ) |
||
1700 | { |
||
1701 | return false; |
||
1702 | } |
||
1703 | while ( i < len && System.Char.IsDigit( s[i] ) ) |
||
1704 | { |
||
1705 | //System.out.println("'" + s.charAt(i) + "' is a digit"); |
||
1706 | i++; |
||
1707 | } |
||
1708 | if ( i >= len ) |
||
1709 | { |
||
1710 | return true; |
||
1711 | } |
||
1712 | |||
1713 | //ported from C code |
||
1714 | c = s[i]; |
||
1715 | if ( ( c != '.' ) && ( c != 'e' ) && ( c != 'E' ) ) |
||
1716 | { |
||
1717 | return true; |
||
1718 | } |
||
1719 | |||
1720 | //original |
||
1721 | /* |
||
1722 | if (i < len) { |
||
1723 | c = s.charAt(i); |
||
1724 | if ((c == '.') || (c == 'e') || (c == 'E')) { |
||
1725 | return false; |
||
1726 | } |
||
1727 | }*/ |
||
1728 | |||
1729 | return false; |
||
1730 | } |
||
1731 | |||
1732 | /// <summary> Converts a value from int or double representation to a string.</summary> |
||
1733 | /// <param name="interp">interpreter to use for precision information. |
||
1734 | /// </param> |
||
1735 | /// <param name="value">Value to be converted. |
||
1736 | /// </param> |
||
1737 | |||
1738 | internal static void ExprMakeString( Interp interp, ExprValue value ) |
||
1739 | { |
||
1740 | if ( value.type == ExprValue.INT ) |
||
1741 | { |
||
1742 | value.stringValue = System.Convert.ToString( value.intValue ); |
||
1743 | } |
||
1744 | else if ( value.type == ExprValue.DOUBLE ) |
||
1745 | { |
||
1746 | value.stringValue = value.doubleValue.ToString(); |
||
1747 | } |
||
1748 | value.type = ExprValue.STRING; |
||
1749 | } |
||
1750 | |||
1751 | internal static void checkIntegerRange( Interp interp, double d ) |
||
1752 | { |
||
1753 | if ( d < 0 ) |
||
1754 | { |
||
1755 | |||
1756 | if ( d < ( (double)TCL.INT_MIN ) ) |
||
1757 | { |
||
1758 | Expression.IntegerTooLarge( interp ); |
||
1759 | } |
||
1760 | } |
||
1761 | else |
||
1762 | { |
||
1763 | |||
1764 | if ( d > ( (double)TCL.INT_MAX ) ) |
||
1765 | { |
||
1766 | Expression.IntegerTooLarge( interp ); |
||
1767 | } |
||
1768 | } |
||
1769 | } |
||
1770 | internal static void checkWideRange( Interp interp, double d ) |
||
1771 | { |
||
1772 | if ( d < 0 ) |
||
1773 | { |
||
1774 | if ( d < Int64.MinValue ) |
||
1775 | { |
||
1776 | Expression.WideTooLarge( interp ); |
||
1777 | } |
||
1778 | } |
||
1779 | else |
||
1780 | { |
||
1781 | if ( d > Int64.MaxValue ) |
||
1782 | { |
||
1783 | Expression.WideTooLarge( interp ); |
||
1784 | } |
||
1785 | } |
||
1786 | } |
||
1787 | |||
1788 | internal static void checkDoubleRange( Interp interp, double d ) |
||
1789 | { |
||
1790 | if ( ( d == System.Double.NaN ) || ( d == System.Double.NegativeInfinity ) || ( d == System.Double.PositiveInfinity ) ) |
||
1791 | { |
||
1792 | Expression.DoubleTooLarge( interp ); |
||
1793 | } |
||
1794 | } |
||
1795 | } |
||
1796 | |||
1797 | abstract class MathFunction |
||
1798 | { |
||
1799 | internal const int INT = 0; |
||
1800 | internal const int DOUBLE = 1; |
||
1801 | internal const int EITHER = 2; |
||
1802 | |||
1803 | internal int[] argTypes; |
||
1804 | |||
1805 | internal abstract ExprValue apply( Interp interp, TclObject[] argv ); |
||
1806 | } |
||
1807 | |||
1808 | abstract class UnaryMathFunction : MathFunction |
||
1809 | { |
||
1810 | internal UnaryMathFunction() |
||
1811 | { |
||
1812 | argTypes = new int[1]; |
||
1813 | argTypes[0] = DOUBLE; |
||
1814 | } |
||
1815 | } |
||
1816 | |||
1817 | abstract class BinaryMathFunction : MathFunction |
||
1818 | { |
||
1819 | internal BinaryMathFunction() |
||
1820 | { |
||
1821 | argTypes = new int[2]; |
||
1822 | argTypes[0] = DOUBLE; |
||
1823 | argTypes[1] = DOUBLE; |
||
1824 | } |
||
1825 | } |
||
1826 | |||
1827 | |||
1828 | abstract class NoArgMathFunction : MathFunction |
||
1829 | { |
||
1830 | internal NoArgMathFunction() |
||
1831 | { |
||
1832 | argTypes = new int[0]; |
||
1833 | } |
||
1834 | } |
||
1835 | |||
1836 | |||
1837 | class Atan2Function : BinaryMathFunction |
||
1838 | { |
||
1839 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
1840 | { |
||
1841 | return new ExprValue( System.Math.Atan2( TclDouble.get( interp, argv[0] ), TclDouble.get( interp, argv[1] ) ) ); |
||
1842 | } |
||
1843 | } |
||
1844 | |||
1845 | class AbsFunction : MathFunction |
||
1846 | { |
||
1847 | internal AbsFunction() |
||
1848 | { |
||
1849 | argTypes = new int[1]; |
||
1850 | argTypes[0] = EITHER; |
||
1851 | } |
||
1852 | |||
1853 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
1854 | { |
||
1855 | if ( argv[0].InternalRep is TclDouble ) |
||
1856 | { |
||
1857 | double d = TclDouble.get( interp, argv[0] ); |
||
1858 | if ( d > 0 ) |
||
1859 | { |
||
1860 | return new ExprValue( d ); |
||
1861 | } |
||
1862 | else |
||
1863 | { |
||
1864 | return new ExprValue( -d ); |
||
1865 | } |
||
1866 | } |
||
1867 | else |
||
1868 | { |
||
1869 | int i = TclInteger.get( interp, argv[0] ); |
||
1870 | if ( i > 0 ) |
||
1871 | { |
||
1872 | return new ExprValue( i ); |
||
1873 | } |
||
1874 | else |
||
1875 | { |
||
1876 | return new ExprValue( -i ); |
||
1877 | } |
||
1878 | } |
||
1879 | } |
||
1880 | } |
||
1881 | |||
1882 | class DoubleFunction : MathFunction |
||
1883 | { |
||
1884 | internal DoubleFunction() |
||
1885 | { |
||
1886 | argTypes = new int[1]; |
||
1887 | argTypes[0] = EITHER; |
||
1888 | } |
||
1889 | |||
1890 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
1891 | { |
||
1892 | return new ExprValue( TclDouble.get( interp, argv[0] ) ); |
||
1893 | } |
||
1894 | } |
||
1895 | |||
1896 | class IntFunction : MathFunction |
||
1897 | { |
||
1898 | internal IntFunction() |
||
1899 | { |
||
1900 | argTypes = new int[1]; |
||
1901 | argTypes[0] = EITHER; |
||
1902 | } |
||
1903 | |||
1904 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
1905 | { |
||
1906 | double d = TclDouble.get( interp, argv[0] ); |
||
1907 | Expression.checkIntegerRange( interp, d ); |
||
1908 | return new ExprValue( (int)d ); |
||
1909 | } |
||
1910 | } |
||
1911 | |||
1912 | class WideFunction : MathFunction |
||
1913 | { |
||
1914 | internal WideFunction() |
||
1915 | { |
||
1916 | argTypes = new int[1]; |
||
1917 | argTypes[0] = EITHER; |
||
1918 | } |
||
1919 | |||
1920 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
1921 | { |
||
1922 | double d = TclDouble.get( interp, argv[0] ); |
||
1923 | Expression.checkWideRange( interp, d ); |
||
1924 | return new ExprValue( (long)d ); |
||
1925 | } |
||
1926 | } |
||
1927 | class RoundFunction : |
||
1928 | MathFunction |
||
1929 | { |
||
1930 | internal RoundFunction() |
||
1931 | { |
||
1932 | argTypes = new int[1]; |
||
1933 | argTypes[0] = EITHER; |
||
1934 | } |
||
1935 | |||
1936 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
1937 | { |
||
1938 | if ( argv[0].InternalRep is TclDouble ) |
||
1939 | { |
||
1940 | double d = TclDouble.get( interp, argv[0] ); |
||
1941 | if ( d < 0 ) |
||
1942 | { |
||
1943 | Expression.checkIntegerRange( interp, d - 0.5 ); |
||
1944 | |||
1945 | return new ExprValue( (int)( d - 0.5 ) ); |
||
1946 | } |
||
1947 | else |
||
1948 | { |
||
1949 | Expression.checkIntegerRange( interp, d + 0.5 ); |
||
1950 | |||
1951 | return new ExprValue( (int)( d + 0.5 ) ); |
||
1952 | } |
||
1953 | } |
||
1954 | else |
||
1955 | { |
||
1956 | return new ExprValue( TclInteger.get( interp, argv[0] ) ); |
||
1957 | } |
||
1958 | } |
||
1959 | } |
||
1960 | |||
1961 | class PowFunction : BinaryMathFunction |
||
1962 | { |
||
1963 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
1964 | { |
||
1965 | double d = System.Math.Pow( TclDouble.get( interp, argv[0] ), TclDouble.get( interp, argv[1] ) ); |
||
1966 | Expression.checkDoubleRange( interp, d ); |
||
1967 | return new ExprValue( d ); |
||
1968 | } |
||
1969 | } |
||
1970 | |||
1971 | /* |
||
1972 | * The following section is generated by this script. |
||
1973 | * |
||
1974 | set missing {fmod} |
||
1975 | set byhand {atan2 pow} |
||
1976 | |||
1977 | |||
1978 | foreach func {Acos Asin Atan Ceil Cos Exp Floor Log Sin |
||
1979 | Sqrt Tan} { |
||
1980 | puts " |
||
1981 | class $func\Function extends UnaryMathFunction { |
||
1982 | ExprValue apply(Interp interp, TclObject argv\[\]) |
||
1983 | throws TclException { |
||
1984 | return new ExprValue(Math.[string tolower $func](TclDouble.get(interp, argv\[0\]))); |
||
1985 | } |
||
1986 | } |
||
1987 | " |
||
1988 | } |
||
1989 | |||
1990 | */ |
||
1991 | |||
1992 | class AcosFunction : UnaryMathFunction |
||
1993 | { |
||
1994 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
1995 | { |
||
1996 | double d = TclDouble.get( interp, argv[0] ); |
||
1997 | if ( ( d < -1 ) || ( d > 1 ) ) |
||
1998 | { |
||
1999 | Expression.DomainError( interp ); |
||
2000 | } |
||
2001 | return new ExprValue( System.Math.Acos( d ) ); |
||
2002 | } |
||
2003 | } |
||
2004 | |||
2005 | class AsinFunction : UnaryMathFunction |
||
2006 | { |
||
2007 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2008 | { |
||
2009 | return new ExprValue( System.Math.Asin( TclDouble.get( interp, argv[0] ) ) ); |
||
2010 | } |
||
2011 | } |
||
2012 | |||
2013 | class AtanFunction : UnaryMathFunction |
||
2014 | { |
||
2015 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2016 | { |
||
2017 | return new ExprValue( System.Math.Atan( TclDouble.get( interp, argv[0] ) ) ); |
||
2018 | } |
||
2019 | } |
||
2020 | |||
2021 | |||
2022 | class CeilFunction : UnaryMathFunction |
||
2023 | { |
||
2024 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2025 | { |
||
2026 | return new ExprValue( System.Math.Ceiling( TclDouble.get( interp, argv[0] ) ) ); |
||
2027 | } |
||
2028 | } |
||
2029 | |||
2030 | |||
2031 | class CosFunction : UnaryMathFunction |
||
2032 | { |
||
2033 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2034 | { |
||
2035 | return new ExprValue( System.Math.Cos( TclDouble.get( interp, argv[0] ) ) ); |
||
2036 | } |
||
2037 | } |
||
2038 | |||
2039 | |||
2040 | class CoshFunction : UnaryMathFunction |
||
2041 | { |
||
2042 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2043 | { |
||
2044 | double x = TclDouble.get( interp, argv[0] ); |
||
2045 | double d1 = System.Math.Pow( System.Math.E, x ); |
||
2046 | double d2 = System.Math.Pow( System.Math.E, -x ); |
||
2047 | |||
2048 | Expression.checkDoubleRange( interp, d1 ); |
||
2049 | Expression.checkDoubleRange( interp, d2 ); |
||
2050 | return new ExprValue( ( d1 + d2 ) / 2 ); |
||
2051 | } |
||
2052 | } |
||
2053 | |||
2054 | class ExpFunction : UnaryMathFunction |
||
2055 | { |
||
2056 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2057 | { |
||
2058 | double d = System.Math.Exp( TclDouble.get( interp, argv[0] ) ); |
||
2059 | if ( ( d == System.Double.NaN ) || ( d == System.Double.NegativeInfinity ) || ( d == System.Double.PositiveInfinity ) ) |
||
2060 | { |
||
2061 | Expression.DoubleTooLarge( interp ); |
||
2062 | } |
||
2063 | return new ExprValue( d ); |
||
2064 | } |
||
2065 | } |
||
2066 | |||
2067 | |||
2068 | class FloorFunction : UnaryMathFunction |
||
2069 | { |
||
2070 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2071 | { |
||
2072 | return new ExprValue( System.Math.Floor( TclDouble.get( interp, argv[0] ) ) ); |
||
2073 | } |
||
2074 | } |
||
2075 | |||
2076 | |||
2077 | class FmodFunction : BinaryMathFunction |
||
2078 | { |
||
2079 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2080 | { |
||
2081 | return new ExprValue( System.Math.IEEERemainder( TclDouble.get( interp, argv[0] ), TclDouble.get( interp, argv[1] ) ) ); |
||
2082 | } |
||
2083 | } |
||
2084 | |||
2085 | class HypotFunction : BinaryMathFunction |
||
2086 | { |
||
2087 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2088 | { |
||
2089 | double x = TclDouble.get( interp, argv[0] ); |
||
2090 | double y = TclDouble.get( interp, argv[1] ); |
||
2091 | return new ExprValue( System.Math.Sqrt( ( ( x * x ) + ( y * y ) ) ) ); |
||
2092 | } |
||
2093 | } |
||
2094 | |||
2095 | |||
2096 | class LogFunction : UnaryMathFunction |
||
2097 | { |
||
2098 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2099 | { |
||
2100 | return new ExprValue( System.Math.Log( TclDouble.get( interp, argv[0] ) ) ); |
||
2101 | } |
||
2102 | } |
||
2103 | |||
2104 | |||
2105 | class Log10Function : UnaryMathFunction |
||
2106 | { |
||
2107 | private static readonly double log10; |
||
2108 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2109 | { |
||
2110 | return new ExprValue( System.Math.Log( TclDouble.get( interp, argv[0] ) ) / log10 ); |
||
2111 | } |
||
2112 | static Log10Function() |
||
2113 | { |
||
2114 | log10 = System.Math.Log( 10 ); |
||
2115 | } |
||
2116 | } |
||
2117 | |||
2118 | |||
2119 | class SinFunction : UnaryMathFunction |
||
2120 | { |
||
2121 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2122 | { |
||
2123 | return new ExprValue( System.Math.Sin( TclDouble.get( interp, argv[0] ) ) ); |
||
2124 | } |
||
2125 | } |
||
2126 | |||
2127 | |||
2128 | class SinhFunction : UnaryMathFunction |
||
2129 | { |
||
2130 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2131 | { |
||
2132 | double x = TclDouble.get( interp, argv[0] ); |
||
2133 | |||
2134 | double d1 = System.Math.Pow( System.Math.E, x ); |
||
2135 | double d2 = System.Math.Pow( System.Math.E, -x ); |
||
2136 | |||
2137 | Expression.checkDoubleRange( interp, d1 ); |
||
2138 | Expression.checkDoubleRange( interp, d2 ); |
||
2139 | |||
2140 | return new ExprValue( ( d1 - d2 ) / 2 ); |
||
2141 | } |
||
2142 | } |
||
2143 | |||
2144 | |||
2145 | class SqrtFunction : UnaryMathFunction |
||
2146 | { |
||
2147 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2148 | { |
||
2149 | return new ExprValue( System.Math.Sqrt( TclDouble.get( interp, argv[0] ) ) ); |
||
2150 | } |
||
2151 | } |
||
2152 | |||
2153 | |||
2154 | class TanFunction : UnaryMathFunction |
||
2155 | { |
||
2156 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2157 | { |
||
2158 | return new ExprValue( System.Math.Tan( TclDouble.get( interp, argv[0] ) ) ); |
||
2159 | } |
||
2160 | } |
||
2161 | |||
2162 | class TanhFunction : UnaryMathFunction |
||
2163 | { |
||
2164 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2165 | { |
||
2166 | double x = TclDouble.get( interp, argv[0] ); |
||
2167 | if ( x == 0 ) |
||
2168 | { |
||
2169 | return new ExprValue( 0.0 ); |
||
2170 | } |
||
2171 | |||
2172 | double d1 = System.Math.Pow( System.Math.E, x ); |
||
2173 | double d2 = System.Math.Pow( System.Math.E, -x ); |
||
2174 | |||
2175 | Expression.checkDoubleRange( interp, d1 ); |
||
2176 | Expression.checkDoubleRange( interp, d2 ); |
||
2177 | |||
2178 | return new ExprValue( ( d1 - d2 ) / ( d1 + d2 ) ); |
||
2179 | } |
||
2180 | } |
||
2181 | |||
2182 | class RandFunction : NoArgMathFunction |
||
2183 | { |
||
2184 | |||
2185 | // Generate the random number using the linear congruential |
||
2186 | // generator defined by the following recurrence: |
||
2187 | // seed = ( IA * seed ) mod IM |
||
2188 | // where IA is 16807 and IM is (2^31) - 1. In order to avoid |
||
2189 | // potential problems with integer overflow, the code uses |
||
2190 | // additional constants IQ and IR such that |
||
2191 | // IM = IA*IQ + IR |
||
2192 | // For details on how this algorithm works, refer to the following |
||
2193 | // papers: |
||
2194 | // |
||
2195 | // S.K. Park & K.W. Miller, "Random number generators: good ones |
||
2196 | // are hard to find," Comm ACM 31(10):1192-1201, Oct 1988 |
||
2197 | // |
||
2198 | // W.H. Press & S.A. Teukolsky, "Portable random number |
||
2199 | // generators," Computers in Physics 6(5):522-524, Sep/Oct 1992. |
||
2200 | |||
2201 | |||
2202 | private const int randIA = 16807; |
||
2203 | private const int randIM = 2147483647; |
||
2204 | private const int randIQ = 127773; |
||
2205 | private const int randIR = 2836; |
||
2206 | private static readonly System.DateTime date = System.DateTime.Now; |
||
2207 | |||
2208 | /// <summary> Srand calls the main algorithm for rand after it sets the seed. |
||
2209 | /// To facilitate this call, the method is static and can be used |
||
2210 | /// w/o creating a new object. But we also need to maintain the |
||
2211 | /// inheritance hierarchy, thus the dynamic apply() calls the static |
||
2212 | /// statApply(). |
||
2213 | /// </summary> |
||
2214 | |||
2215 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2216 | { |
||
2217 | return ( statApply( interp, argv ) ); |
||
2218 | } |
||
2219 | |||
2220 | |||
2221 | internal static ExprValue statApply( Interp interp, TclObject[] argv ) |
||
2222 | { |
||
2223 | |||
2224 | int tmp; |
||
2225 | |||
2226 | if ( !( interp.randSeedInit ) ) |
||
2227 | { |
||
2228 | interp.randSeedInit = true; |
||
2229 | interp.randSeed = (int)date.Ticks; |
||
2230 | } |
||
2231 | |||
2232 | if ( interp.randSeed == 0 ) |
||
2233 | { |
||
2234 | // Don't allow a 0 seed, since it breaks the generator. Shift |
||
2235 | // it to some other value. |
||
2236 | |||
2237 | interp.randSeed = 123459876; |
||
2238 | } |
||
2239 | |||
2240 | tmp = (int)( interp.randSeed / randIQ ); |
||
2241 | interp.randSeed = ( ( randIA * ( interp.randSeed - tmp * randIQ ) ) - randIR * tmp ); |
||
2242 | |||
2243 | if ( interp.randSeed < 0 ) |
||
2244 | { |
||
2245 | interp.randSeed += randIM; |
||
2246 | } |
||
2247 | |||
2248 | return new ExprValue( interp.randSeed * ( 1.0 / randIM ) ); |
||
2249 | } |
||
2250 | } |
||
2251 | |||
2252 | |||
2253 | class SrandFunction : UnaryMathFunction |
||
2254 | { |
||
2255 | |||
2256 | internal override ExprValue apply( Interp interp, TclObject[] argv ) |
||
2257 | { |
||
2258 | |||
2259 | // Reset the seed. |
||
2260 | |||
2261 | interp.randSeedInit = true; |
||
2262 | |||
2263 | interp.randSeed = (long)TclDouble.get( interp, argv[0] ); |
||
2264 | |||
2265 | // To avoid duplicating the random number generation code we simply |
||
2266 | // call the static random number generator in the RandFunction |
||
2267 | // class. |
||
2268 | |||
2269 | return ( RandFunction.statApply( interp, null ) ); |
||
2270 | } |
||
2271 | } |
||
2272 | } |