Aer Interpreter Source
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1563 lines
60 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
  1. /*
  2. * Symisc PH7: An embeddable bytecode compiler and a virtual machine for the PHP(5) programming language.
  3. * Copyright (C) 2011-2012, Symisc Systems http://ph7.symisc.net/
  4. * Version 2.1.4
  5. * For information on licensing,redistribution of this file,and for a DISCLAIMER OF ALL WARRANTIES
  6. * please contact Symisc Systems via:
  7. * legal@symisc.net
  8. * licensing@symisc.net
  9. * contact@symisc.net
  10. * or visit:
  11. * http://ph7.symisc.net/
  12. */
  13. /* $SymiscID: parse.c v3.7 FreeBSD 2011-12-20 22:46 stable <chm@symisc.net> $ */
  14. #include "ph7int.h"
  15. /*
  16. * This file implement a hand-coded, thread-safe, full-reentrant and highly-efficient
  17. * expression parser for the PH7 engine.
  18. * Besides from the one introduced by PHP (Over 60), the PH7 engine have introduced three new
  19. * operators. These are 'eq', 'ne' and the comma operator ','.
  20. * The eq and ne operators are borrowed from the Perl world. They are used for strict
  21. * string comparison. The reason why they have been implemented in the PH7 engine
  22. * and introduced as an extension to the PHP programming language is due to the confusion
  23. * introduced by the standard PHP comparison operators ('==' or '===') especially if you
  24. * are comparing strings with numbers.
  25. * Take the following example:
  26. * var_dump( 0xFF == '255' ); // bool(true) ???
  27. * // use the type equal operator by adding a single space to one of the operand
  28. * var_dump( '255 ' === '255' ); //bool(true) depending on the PHP version
  29. * That is, if one of the operand looks like a number (either integer or float) then PHP
  30. * will internally convert the two operands to numbers and then a numeric comparison is performed.
  31. * This is what the PHP language reference manual says:
  32. * If you compare a number with a string or the comparison involves numerical strings, then each
  33. * string is converted to a number and the comparison performed numerically.
  34. * Bummer, if you ask me,this is broken, badly broken. I mean,the programmer cannot dictate
  35. * it's comparison rule, it's the underlying engine who decides in it's place and perform
  36. * the internal conversion. In most cases,PHP developers wants simple string comparison and they
  37. * are stuck to use the ugly and inefficient strcmp() function and it's variants instead.
  38. * This is the big reason why we have introduced these two operators.
  39. * The eq operator is used to compare two strings byte per byte. If you came from the C/C++ world
  40. * think of this operator as a barebone implementation of the memcmp() C standard library function.
  41. * Keep in mind that if you are comparing two ASCII strings then the capital letters and their lowercase
  42. * letters are completely different and so this example will output false.
  43. * var_dump('allo' eq 'Allo'); //bool(FALSE)
  44. * The ne operator perform the opposite operation of the eq operator and is used to test for string
  45. * inequality. This example will output true
  46. * var_dump('allo' ne 'Allo'); //bool(TRUE) unequal strings
  47. * The eq operator return a Boolean true if and only if the two strings are identical while the
  48. * ne operator return a Boolean true if and only if the two strings are different. Otherwise
  49. * a Boolean false is returned (equal strings).
  50. * Note that the comparison is performed only if the two strings are of the same length.
  51. * Otherwise the eq and ne operators return a Boolean false without performing any comparison
  52. * and avoid us wasting CPU time for nothing.
  53. * Again remember that we talk about a low level byte per byte comparison and nothing else.
  54. * Also remember that zero length strings are always equal.
  55. *
  56. * Again, another powerful mechanism borrowed from the C/C++ world and introduced as an extension
  57. * to the PHP programming language.
  58. * A comma expression contains two operands of any type separated by a comma and has left-to-right
  59. * associativity. The left operand is fully evaluated, possibly producing side effects, and its
  60. * value, if there is one, is discarded. The right operand is then evaluated. The type and value
  61. * of the result of a comma expression are those of its right operand, after the usual unary conversions.
  62. * Any number of expressions separated by commas can form a single expression because the comma operator
  63. * is associative. The use of the comma operator guarantees that the sub-expressions will be evaluated
  64. * in left-to-right order, and the value of the last becomes the value of the entire expression.
  65. * The following example assign the value 25 to the variable $a, multiply the value of $a with 2
  66. * and assign the result to variable $b and finally we call a test function to output the value
  67. * of $a and $b. Keep-in mind that all theses operations are done in a single expression using
  68. * the comma operator to create side effect.
  69. * $a = 25,$b = $a << 1 ,test();
  70. * //Output the value of $a and $b
  71. * function test(){
  72. * global $a,$b;
  73. * echo "\$a = $a \$b= $b\n"; // You should see: $a = 25 $b = 50
  74. * }
  75. *
  76. * For a full discussions on these extensions, please refer to official
  77. * documentation(http://ph7.symisc.net/features.html) or visit the official forums
  78. * (http://forums.symisc.net/) if you want to share your point of view.
  79. *
  80. * Expressions: According to the PHP language reference manual
  81. *
  82. * Expressions are the most important building stones of PHP. In PHP, almost anything you write is an expression.
  83. * The simplest yet most accurate way to define an expression is "anything that has a value".
  84. * The most basic forms of expressions are constants and variables. When you type "$a = 5", you're assigning
  85. * '5' into $a. '5', obviously, has the value 5, or in other words '5' is an expression with the value of 5
  86. * (in this case, '5' is an integer constant).
  87. * After this assignment, you'd expect $a's value to be 5 as well, so if you wrote $b = $a, you'd expect
  88. * it to behave just as if you wrote $b = 5. In other words, $a is an expression with the value of 5 as well.
  89. * If everything works right, this is exactly what will happen.
  90. * Slightly more complex examples for expressions are functions. For instance, consider the following function:
  91. * <?php
  92. * function foo ()
  93. * {
  94. * return 5;
  95. * }
  96. * ?>
  97. * Assuming you're familiar with the concept of functions (if you're not, take a look at the chapter about functions)
  98. * you'd assume that typing $c = foo() is essentially just like writing $c = 5, and you're right.
  99. * Functions are expressions with the value of their return value. Since foo() returns 5, the value of the expression
  100. * 'foo()' is 5. Usually functions don't just return a static value but compute something.
  101. * Of course, values in PHP don't have to be integers, and very often they aren't.
  102. * PHP supports four scalar value types: integer values, floating point values (float), string values and boolean values
  103. * (scalar values are values that you can't 'break' into smaller pieces, unlike arrays, for instance).
  104. * PHP also supports two composite (non-scalar) types: arrays and objects. Each of these value types can be assigned
  105. * into variables or returned from functions.
  106. * PHP takes expressions much further, in the same way many other languages do. PHP is an expression-oriented language
  107. * in the sense that almost everything is an expression. Consider the example we've already dealt with, '$a = 5'.
  108. * It's easy to see that there are two values involved here, the value of the integer constant '5', and the value
  109. * of $a which is being updated to 5 as well. But the truth is that there's one additional value involved here
  110. * and that's the value of the assignment itself. The assignment itself evaluates to the assigned value, in this case 5.
  111. * In practice, it means that '$a = 5', regardless of what it does, is an expression with the value 5. Thus, writing
  112. * something like '$b = ($a = 5)' is like writing '$a = 5; $b = 5;' (a semicolon marks the end of a statement).
  113. * Since assignments are parsed in a right to left order, you can also write '$b = $a = 5'.
  114. * Another good example of expression orientation is pre- and post-increment and decrement.
  115. * Users of PHP and many other languages may be familiar with the notation of variable++ and variable--.
  116. * These are increment and decrement operators. In PHP, like in C, there are two types of increment - pre-increment
  117. * and post-increment. Both pre-increment and post-increment essentially increment the variable, and the effect
  118. * on the variable is identical. The difference is with the value of the increment expression. Pre-increment, which is written
  119. * '++$variable', evaluates to the incremented value (PHP increments the variable before reading its value, thus the name 'pre-increment').
  120. * Post-increment, which is written '$variable++' evaluates to the original value of $variable, before it was incremented
  121. * (PHP increments the variable after reading its value, thus the name 'post-increment').
  122. * A very common type of expressions are comparison expressions. These expressions evaluate to either FALSE or TRUE.
  123. * PHP supports > (bigger than), >= (bigger than or equal to), == (equal), != (not equal), < (smaller than) and <= (smaller than or equal to).
  124. * The language also supports a set of strict equivalence operators: === (equal to and same type) and !== (not equal to or not same type).
  125. * These expressions are most commonly used inside conditional execution, such as if statements.
  126. * The last example of expressions we'll deal with here is combined operator-assignment expressions.
  127. * You already know that if you want to increment $a by 1, you can simply write '$a++' or '++$a'.
  128. * But what if you want to add more than one to it, for instance 3? You could write '$a++' multiple times, but this is obviously not a very
  129. * efficient or comfortable way. A much more common practice is to write '$a = $a + 3'. '$a + 3' evaluates to the value of $a plus 3
  130. * and is assigned back into $a, which results in incrementing $a by 3. In PHP, as in several other languages like C, you can write
  131. * this in a shorter way, which with time would become clearer and quicker to understand as well. Adding 3 to the current value of $a
  132. * can be written '$a += 3'. This means exactly "take the value of $a, add 3 to it, and assign it back into $a".
  133. * In addition to being shorter and clearer, this also results in faster execution. The value of '$a += 3', like the value of a regular
  134. * assignment, is the assigned value. Notice that it is NOT 3, but the combined value of $a plus 3 (this is the value that's assigned into $a).
  135. * Any two-place operator can be used in this operator-assignment mode, for example '$a -= 5' (subtract 5 from the value of $a), '$b *= 7'
  136. * (multiply the value of $b by 7), etc.
  137. * There is one more expression that may seem odd if you haven't seen it in other languages, the ternary conditional operator:
  138. * <?php
  139. * $first ? $second : $third
  140. * ?>
  141. * If the value of the first subexpression is TRUE (non-zero), then the second subexpression is evaluated, and that is the result
  142. * of the conditional expression. Otherwise, the third subexpression is evaluated, and that is the value.
  143. */
  144. /* Operators associativity */
  145. #define EXPR_OP_ASSOC_LEFT 0x01 /* Left associative operator */
  146. #define EXPR_OP_ASSOC_RIGHT 0x02 /* Right associative operator */
  147. #define EXPR_OP_NON_ASSOC 0x04 /* Non-associative operator */
  148. /*
  149. * Operators table
  150. * This table is sorted by operators priority (highest to lowest) according
  151. * the PHP language reference manual.
  152. * PH7 implements all the 60 PHP operators and have introduced the eq and ne operators.
  153. * The operators precedence table have been improved dramatically so that you can do same
  154. * amazing things now such as array dereferencing,on the fly function call,anonymous function
  155. * as array values,class member access on instantiation and so on.
  156. * Refer to the following page for a full discussion on these improvements:
  157. * http://ph7.symisc.net/features.html#improved_precedence
  158. */
  159. static const ph7_expr_op aOpTable[] = {
  160. /* Precedence 1: non-associative */
  161. { {"new", sizeof("new") - 1}, EXPR_OP_NEW, 1, EXPR_OP_NON_ASSOC, PH7_OP_NEW },
  162. { {"clone", sizeof("clone") - 1}, EXPR_OP_CLONE, 1, EXPR_OP_NON_ASSOC, PH7_OP_CLONE},
  163. /* Postfix operators */
  164. /* Precedence 2(Highest),left-associative */
  165. { {"->", sizeof(char) * 2}, EXPR_OP_ARROW, 2, EXPR_OP_ASSOC_LEFT, PH7_OP_MEMBER},
  166. { {"::", sizeof(char) * 2}, EXPR_OP_DC, 2, EXPR_OP_ASSOC_LEFT, PH7_OP_MEMBER},
  167. { {"[", sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT, PH7_OP_LOAD_IDX},
  168. /* Precedence 3,non-associative */
  169. { {"++", sizeof(char) * 2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC, PH7_OP_INCR},
  170. { {"--", sizeof(char) * 2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC, PH7_OP_DECR},
  171. /* Unary operators */
  172. /* Precedence 4,right-associative */
  173. { {"-", sizeof(char)}, EXPR_OP_UMINUS, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_UMINUS },
  174. { {"+", sizeof(char)}, EXPR_OP_UPLUS, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_UPLUS },
  175. { {"~", sizeof(char)}, EXPR_OP_BITNOT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_BITNOT },
  176. { {"!", sizeof(char)}, EXPR_OP_LOGNOT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_LNOT },
  177. { {"@", sizeof(char)}, EXPR_OP_ALT, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_ERR_CTRL},
  178. /* Cast operators */
  179. { {"(int)", sizeof("(int)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_INT },
  180. { {"(bool)", sizeof("(bool)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_BOOL },
  181. { {"(char)", sizeof("(char)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_CHAR },
  182. { {"(string)", sizeof("(string)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_STR },
  183. { {"(float)", sizeof("(float)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_REAL },
  184. { {"(array)", sizeof("(array)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_ARRAY},
  185. { {"(object)", sizeof("(object)") - 1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_OBJ },
  186. { {"(void)", sizeof("(void)") - 1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, PH7_OP_CVT_VOID },
  187. /* Binary operators */
  188. /* Precedence 7,left-associative */
  189. { {"instanceof", sizeof("instanceof") - 1}, EXPR_OP_INSTOF, 7, EXPR_OP_NON_ASSOC, PH7_OP_IS_A},
  190. { {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT, PH7_OP_MUL},
  191. { {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT, PH7_OP_DIV},
  192. { {"%", sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT, PH7_OP_MOD},
  193. /* Precedence 8,left-associative */
  194. { {"+", sizeof(char)}, EXPR_OP_ADD, 8, EXPR_OP_ASSOC_LEFT, PH7_OP_ADD},
  195. { {"-", sizeof(char)}, EXPR_OP_SUB, 8, EXPR_OP_ASSOC_LEFT, PH7_OP_SUB},
  196. /* Precedence 9,left-associative */
  197. { {"<<", sizeof(char) * 2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, PH7_OP_SHL},
  198. { {">>", sizeof(char) * 2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, PH7_OP_SHR},
  199. /* Precedence 10,non-associative */
  200. { {"<", sizeof(char)}, EXPR_OP_LT, 10, EXPR_OP_NON_ASSOC, PH7_OP_LT},
  201. { {">", sizeof(char)}, EXPR_OP_GT, 10, EXPR_OP_NON_ASSOC, PH7_OP_GT},
  202. { {"<=", sizeof(char) * 2}, EXPR_OP_LE, 10, EXPR_OP_NON_ASSOC, PH7_OP_LE},
  203. { {">=", sizeof(char) * 2}, EXPR_OP_GE, 10, EXPR_OP_NON_ASSOC, PH7_OP_GE},
  204. { {"<>", sizeof(char) * 2}, EXPR_OP_NE, 10, EXPR_OP_NON_ASSOC, PH7_OP_NEQ},
  205. /* Precedence 11,non-associative */
  206. { {"==", sizeof(char) * 2}, EXPR_OP_EQ, 11, EXPR_OP_NON_ASSOC, PH7_OP_EQ},
  207. { {"!=", sizeof(char) * 2}, EXPR_OP_NE, 11, EXPR_OP_NON_ASSOC, PH7_OP_NEQ},
  208. { {"===", sizeof(char) * 3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, PH7_OP_TEQ},
  209. { {"!==", sizeof(char) * 3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, PH7_OP_TNE},
  210. /* Precedence 12,left-associative */
  211. { {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_BAND},
  212. /* Precedence 12,left-associative */
  213. /* { {"=&", sizeof(char) * 2}, EXPR_OP_REF, 12, EXPR_OP_ASSOC_LEFT, PH7_OP_STORE_REF}, */
  214. /* Binary operators */
  215. /* Precedence 13,left-associative */
  216. { {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, PH7_OP_BXOR},
  217. /* Precedence 14,left-associative */
  218. { {"|", sizeof(char)}, EXPR_OP_BOR, 14, EXPR_OP_ASSOC_LEFT, PH7_OP_BOR},
  219. /* Precedence 15,left-associative */
  220. { {"&&", sizeof(char) * 2}, EXPR_OP_LAND, 15, EXPR_OP_ASSOC_LEFT, PH7_OP_LAND},
  221. /* Precedence 16,left-associative */
  222. { {"^^", sizeof(char) * 2}, EXPR_OP_LXOR, 16, EXPR_OP_ASSOC_LEFT, PH7_OP_LXOR},
  223. /* Precedence 17,left-associative */
  224. { {"||", sizeof(char) * 2}, EXPR_OP_LOR, 17, EXPR_OP_ASSOC_LEFT, PH7_OP_LOR},
  225. /* Ternary operator */
  226. /* Precedence 18,left-associative */
  227. { {"?", sizeof(char)}, EXPR_OP_QUESTY, 18, EXPR_OP_ASSOC_LEFT, 0},
  228. /* Combined binary operators */
  229. /* Precedence 19,right-associative */
  230. { {"=", sizeof(char)}, EXPR_OP_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_STORE},
  231. { {"+=", sizeof(char) * 2}, EXPR_OP_ADD_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_ADD_STORE },
  232. { {"-=", sizeof(char) * 2}, EXPR_OP_SUB_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_SUB_STORE },
  233. { {"*=", sizeof(char) * 2}, EXPR_OP_MUL_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_MUL_STORE },
  234. { {"/=", sizeof(char) * 2}, EXPR_OP_DIV_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_DIV_STORE },
  235. { {"%=", sizeof(char) * 2}, EXPR_OP_MOD_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_MOD_STORE },
  236. { {"&=", sizeof(char) * 2}, EXPR_OP_AND_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_BAND_STORE },
  237. { {"|=", sizeof(char) * 2}, EXPR_OP_OR_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_BOR_STORE },
  238. { {"^=", sizeof(char) * 2}, EXPR_OP_XOR_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_BXOR_STORE },
  239. { {"<<=", sizeof(char) * 3}, EXPR_OP_SHL_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_SHL_STORE },
  240. { {">>=", sizeof(char) * 3}, EXPR_OP_SHR_ASSIGN, 19, EXPR_OP_ASSOC_RIGHT, PH7_OP_SHR_STORE },
  241. /* Precedence 23,left-associative [Lowest operator] */
  242. { {",", sizeof(char)}, EXPR_OP_COMMA, 23, EXPR_OP_ASSOC_LEFT, 0},
  243. };
  244. /* Function call operator need special handling */
  245. static const ph7_expr_op sFCallOp = {{"(", sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT, PH7_OP_CALL};
  246. /*
  247. * Check if the given token is a potential operator or not.
  248. * This function is called by the lexer each time it extract a token that may
  249. * look like an operator.
  250. * Return a structure [i.e: ph7_expr_op instance ] that describe the operator on success.
  251. * Otherwise NULL.
  252. * Note that the function take care of handling ambiguity [i.e: whether we are dealing with
  253. * a binary minus or unary minus.]
  254. */
  255. PH7_PRIVATE const ph7_expr_op *PH7_ExprExtractOperator(SyString *pStr, SyToken *pLast) {
  256. sxu32 n = 0;
  257. sxi32 rc;
  258. /* Do a linear lookup on the operators table */
  259. for(;;) {
  260. if(n >= SX_ARRAYSIZE(aOpTable)) {
  261. break;
  262. }
  263. if(SyisAlpha(aOpTable[n].sOp.zString[0])) {
  264. /* TICKET 1433-012: Alpha stream operators [i.e: new, clone, instanceof] */
  265. rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyStrnicmp);
  266. } else {
  267. rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyMemcmp);
  268. }
  269. if(rc == 0) {
  270. if(aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0) {
  271. /* There is no ambiguity here,simply return the first operator seen */
  272. return &aOpTable[n];
  273. }
  274. /* Handle ambiguity */
  275. if(pLast->nType & (PH7_TK_LPAREN/*'('*/ | PH7_TK_OCB/*'{'*/ | PH7_TK_OSB/*'['*/ | PH7_TK_COLON/*:*/ | PH7_TK_COMMA/*,'*/)) {
  276. /* Unary operators have precedence here over binary operators */
  277. return &aOpTable[n];
  278. }
  279. if(pLast->nType & PH7_TK_OP) {
  280. const ph7_expr_op *pOp = (const ph7_expr_op *)pLast->pUserData;
  281. /* Ticket 1433-31: Handle the '++','--' operators case */
  282. if(pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR) {
  283. /* Unary operators have precedence here over binary operators */
  284. return &aOpTable[n];
  285. }
  286. }
  287. }
  288. ++n; /* Next operator in the table */
  289. }
  290. /* No such operator */
  291. return 0;
  292. }
  293. /*
  294. * Delimit a set of token stream.
  295. * This function take care of handling the nesting level and stops when it hit
  296. * the end of the input or the ending token is found and the nesting level is zero.
  297. */
  298. PH7_PRIVATE void PH7_DelimitNestedTokens(SyToken *pIn, SyToken *pEnd, sxu32 nTokStart, sxu32 nTokEnd, SyToken **ppEnd) {
  299. SyToken *pCur = pIn;
  300. sxi32 iNest = 1;
  301. for(;;) {
  302. if(pCur >= pEnd) {
  303. break;
  304. }
  305. if(pCur->nType & nTokStart) {
  306. /* Increment nesting level */
  307. iNest++;
  308. } else if(pCur->nType & nTokEnd) {
  309. /* Decrement nesting level */
  310. iNest--;
  311. if(iNest <= 0) {
  312. break;
  313. }
  314. }
  315. /* Advance cursor */
  316. pCur++;
  317. }
  318. /* Point to the end of the chunk */
  319. *ppEnd = pCur;
  320. }
  321. /*
  322. * Make sure we are dealing with a valid expression tree.
  323. * This function check for balanced parenthesis,braces,brackets and so on.
  324. * When errors,PH7 take care of generating the appropriate error message.
  325. * Return SXRET_OK on success. Any other return value indicates syntax error.
  326. */
  327. static sxi32 ExprVerifyNodes(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 nNode) {
  328. sxi32 iParen, iSquare, iQuesty, iBraces;
  329. sxi32 i, rc;
  330. if(nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB)) {
  331. /* Fix and mark as an unary not binary plus/minus operator */
  332. apNode[0]->pOp = PH7_ExprExtractOperator(&apNode[0]->pStart->sData, 0);
  333. apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp;
  334. }
  335. iParen = iSquare = iQuesty = iBraces = 0;
  336. for(i = 0 ; i < nNode ; ++i) {
  337. if(apNode[i]->pStart->nType & PH7_TK_LPAREN /*'('*/) {
  338. if(i > 0 && (apNode[i - 1]->xCode == PH7_CompileVariable || apNode[i - 1]->xCode == PH7_CompileLiteral ||
  339. (apNode[i - 1]->pStart->nType & (PH7_TK_ID | PH7_TK_KEYWORD | PH7_TK_SSTR | PH7_TK_DSTR | PH7_TK_RPAREN/*')'*/ | PH7_TK_CSB/*']'*/ | PH7_TK_CCB/*'}'*/)))) {
  340. /* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or,xor] operators followed by an opening parenthesis */
  341. if((apNode[i - 1]->pStart->nType & PH7_TK_OP) == 0) {
  342. /* We are dealing with a postfix [i.e: function call] operator
  343. * not a simple left parenthesis. Mark the node.
  344. */
  345. apNode[i]->pStart->nType |= PH7_TK_OP;
  346. apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */
  347. apNode[i]->pOp = &sFCallOp;
  348. }
  349. }
  350. iParen++;
  351. } else if(apNode[i]->pStart->nType & PH7_TK_RPAREN/*')*/) {
  352. if(iParen <= 0) {
  353. rc = PH7_GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ')'");
  354. if(rc != SXERR_ABORT) {
  355. rc = SXERR_SYNTAX;
  356. }
  357. return rc;
  358. }
  359. iParen--;
  360. } else if(apNode[i]->pStart->nType & PH7_TK_OSB /*'['*/) {
  361. iSquare++;
  362. } else if(apNode[i]->pStart->nType & PH7_TK_CSB /*']'*/) {
  363. if(iSquare <= 0) {
  364. rc = PH7_GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ']'");
  365. if(rc != SXERR_ABORT) {
  366. rc = SXERR_SYNTAX;
  367. }
  368. return rc;
  369. }
  370. iSquare--;
  371. } else if(apNode[i]->pStart->nType & PH7_TK_OCB /*'{'*/) {
  372. iBraces++;
  373. if(i > 0 && (apNode[i - 1]->xCode == PH7_CompileVariable || (apNode[i - 1]->pStart->nType & PH7_TK_CSB/*]*/))) {
  374. const ph7_expr_op *pOp, *pEnd;
  375. int iNest = 1;
  376. sxi32 j = i + 1;
  377. /*
  378. * Dirty Hack: $a{'x'} == > $a['x']
  379. */
  380. apNode[i]->pStart->nType &= ~PH7_TK_OCB /*'{'*/;
  381. apNode[i]->pStart->nType |= PH7_TK_OSB /*'['*/;
  382. pOp = aOpTable;
  383. pEnd = &pOp[sizeof(aOpTable)];
  384. while(pOp < pEnd) {
  385. if(pOp->iOp == EXPR_OP_SUBSCRIPT) {
  386. break;
  387. }
  388. pOp++;
  389. }
  390. if(pOp >= pEnd) {
  391. pOp = 0;
  392. }
  393. if(pOp) {
  394. apNode[i]->pOp = pOp;
  395. apNode[i]->pStart->nType |= PH7_TK_OP;
  396. }
  397. iBraces--;
  398. iSquare++;
  399. while(j < nNode) {
  400. if(apNode[j]->pStart->nType & PH7_TK_OCB /*{*/) {
  401. /* Increment nesting level */
  402. iNest++;
  403. } else if(apNode[j]->pStart->nType & PH7_TK_CCB/*}*/) {
  404. /* Decrement nesting level */
  405. iNest--;
  406. if(iNest < 1) {
  407. break;
  408. }
  409. }
  410. j++;
  411. }
  412. if(j < nNode) {
  413. apNode[j]->pStart->nType &= ~PH7_TK_CCB /*'}'*/;
  414. apNode[j]->pStart->nType |= PH7_TK_CSB /*']'*/;
  415. }
  416. }
  417. } else if(apNode[i]->pStart->nType & PH7_TK_CCB /*'}'*/) {
  418. if(iBraces <= 0) {
  419. rc = PH7_GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token '}'");
  420. if(rc != SXERR_ABORT) {
  421. rc = SXERR_SYNTAX;
  422. }
  423. return rc;
  424. }
  425. iBraces--;
  426. } else if(apNode[i]->pStart->nType & PH7_TK_COLON) {
  427. if(iQuesty <= 0) {
  428. rc = PH7_GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ':'");
  429. if(rc != SXERR_ABORT) {
  430. rc = SXERR_SYNTAX;
  431. }
  432. return rc;
  433. }
  434. iQuesty--;
  435. } else if(apNode[i]->pStart->nType & PH7_TK_OP) {
  436. const ph7_expr_op *pOp = (const ph7_expr_op *)apNode[i]->pOp;
  437. if(pOp->iOp == EXPR_OP_QUESTY) {
  438. iQuesty++;
  439. } else if(i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)) {
  440. if(apNode[i - 1]->xCode == PH7_CompileVariable || apNode[i - 1]->xCode == PH7_CompileLiteral) {
  441. sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */
  442. sxu32 n = 0;
  443. if(pOp->iOp == EXPR_OP_UPLUS) {
  444. iExprOp = EXPR_OP_ADD; /* Binary plus */
  445. }
  446. /*
  447. * TICKET 1433-013: This is a fix around an obscure bug when the user uses
  448. * a variable name which is an alpha-stream operator [i.e: $and,$xor,$eq..].
  449. */
  450. while(n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp) {
  451. ++n;
  452. }
  453. pOp = &aOpTable[n];
  454. /* Mark as binary '+' or '-',not an unary */
  455. apNode[i]->pOp = pOp;
  456. apNode[i]->pStart->pUserData = (void *)pOp;
  457. }
  458. }
  459. }
  460. }
  461. if(iParen != 0 || iSquare != 0 || iQuesty != 0 || iBraces != 0) {
  462. rc = PH7_GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error,mismatched '(','[','{' or '?'");
  463. if(rc != SXERR_ABORT) {
  464. rc = SXERR_SYNTAX;
  465. }
  466. return rc;
  467. }
  468. return SXRET_OK;
  469. }
  470. /*
  471. * Collect and assemble tokens holding a namespace path [i.e: namespace\to\const]
  472. * or a simple literal [i.e: PHP_EOL].
  473. */
  474. static void ExprAssembleLiteral(SyToken **ppCur, SyToken *pEnd) {
  475. SyToken *pIn = *ppCur;
  476. /* Jump the first literal seen */
  477. if((pIn->nType & PH7_TK_NSSEP) == 0) {
  478. pIn++;
  479. }
  480. for(;;) {
  481. if(pIn < pEnd && (pIn->nType & PH7_TK_NSSEP)) {
  482. pIn++;
  483. if(pIn < pEnd && (pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD))) {
  484. pIn++;
  485. }
  486. } else {
  487. break;
  488. }
  489. }
  490. /* Synchronize pointers */
  491. *ppCur = pIn;
  492. }
  493. /*
  494. * Collect and assemble tokens holding closure body.
  495. * When errors,PH7 take care of generating the appropriate error message.
  496. * Anonymous functions, also known as closures, allow the creation of functions
  497. * which have no specified name. They are most useful as the value of callback
  498. * parameters, but they have many other uses.
  499. * Closures may also inherit variables from the parent scope. Any such variables
  500. * must be declared in the function header. Inheriting variables from the parent
  501. * scope is not the same as using global variables. Global variables exist in the global scope
  502. * which is the same no matter what function is executing. The parent scope of a closure is the
  503. * function in which the closure was declared (not necessarily the function it was called from).
  504. *
  505. * Some example:
  506. * $greet = function($name)
  507. * {
  508. * printf("Hello %s\r\n", $name);
  509. * };
  510. * $greet('World');
  511. * $greet('AerScript');
  512. *
  513. * $double = function($a) {
  514. * return $a * 2;
  515. * };
  516. * // This is our range of numbers
  517. * $numbers = range(1, 5);
  518. * // Use the Anonymous function as a callback here to
  519. * // double the size of each element in our
  520. * // range
  521. * $new_numbers = array_map($double, $numbers);
  522. * print(implode(' ', $new_numbers));
  523. */
  524. static sxi32 ExprAssembleClosure(ph7_gen_state *pGen, SyToken **ppCur, SyToken *pEnd) {
  525. SyToken *pIn = *ppCur;
  526. sxu32 nLine;
  527. sxi32 rc;
  528. /* Jump the 'function' keyword */
  529. nLine = pIn->nLine;
  530. pIn++;
  531. if(pIn < pEnd && (pIn->nType & (PH7_TK_ID | PH7_TK_KEYWORD))) {
  532. pIn++;
  533. }
  534. if(pIn >= pEnd || (pIn->nType & PH7_TK_LPAREN) == 0) {
  535. /* Syntax error */
  536. rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Missing opening parenthesis '(' while declaring anonymous function");
  537. if(rc != SXERR_ABORT) {
  538. rc = SXERR_SYNTAX;
  539. }
  540. goto Synchronize;
  541. }
  542. pIn++; /* Jump the leading parenthesis '(' */
  543. PH7_DelimitNestedTokens(pIn, pEnd, PH7_TK_LPAREN/*'('*/, PH7_TK_RPAREN/*')'*/, &pIn);
  544. if(pIn >= pEnd || &pIn[1] >= pEnd) {
  545. /* Syntax error */
  546. rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring anonymous function");
  547. if(rc != SXERR_ABORT) {
  548. rc = SXERR_SYNTAX;
  549. }
  550. goto Synchronize;
  551. }
  552. pIn++; /* Jump the trailing parenthesis */
  553. if(pIn->nType & PH7_TK_KEYWORD) {
  554. sxu32 nKey = SX_PTR_TO_INT(pIn->pUserData);
  555. /* Check if we are dealing with a closure */
  556. if(nKey == PH7_KEYWORD_USING) {
  557. pIn++; /* Jump the 'using' keyword */
  558. if(pIn >= pEnd || (pIn->nType & PH7_TK_LPAREN) == 0) {
  559. /* Syntax error */
  560. rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring anonymous function");
  561. if(rc != SXERR_ABORT) {
  562. rc = SXERR_SYNTAX;
  563. }
  564. goto Synchronize;
  565. }
  566. pIn++; /* Jump the leading parenthesis '(' */
  567. PH7_DelimitNestedTokens(pIn, pEnd, PH7_TK_LPAREN/*'('*/, PH7_TK_RPAREN/*')'*/, &pIn);
  568. if(pIn >= pEnd || &pIn[1] >= pEnd) {
  569. /* Syntax error */
  570. rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring anonymous function");
  571. if(rc != SXERR_ABORT) {
  572. rc = SXERR_SYNTAX;
  573. }
  574. goto Synchronize;
  575. }
  576. pIn++;
  577. } else {
  578. /* Syntax error */
  579. rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring anonymous function");
  580. if(rc != SXERR_ABORT) {
  581. rc = SXERR_SYNTAX;
  582. }
  583. goto Synchronize;
  584. }
  585. }
  586. if(pIn->nType & PH7_TK_OCB /*'{'*/) {
  587. pIn++; /* Jump the leading curly '{' */
  588. PH7_DelimitNestedTokens(pIn, pEnd, PH7_TK_OCB/*'{'*/, PH7_TK_CCB/*'}'*/, &pIn);
  589. if(pIn < pEnd) {
  590. pIn++;
  591. }
  592. } else {
  593. /* Syntax error */
  594. rc = PH7_GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring anonymous function,missing '{'");
  595. if(rc == SXERR_ABORT) {
  596. return SXERR_ABORT;
  597. }
  598. }
  599. rc = SXRET_OK;
  600. Synchronize:
  601. /* Synchronize pointers */
  602. *ppCur = pIn;
  603. return rc;
  604. }
  605. /*
  606. * Extract a single expression node from the input.
  607. * On success store the freshly extracted node in ppNode.
  608. * When errors,PH7 take care of generating the appropriate error message.
  609. * An expression node can be a variable [i.e: $var],an operator [i.e: ++]
  610. * an anonymous function [i.e: function(){ return "Hello"; }, a double/single
  611. * quoted string, a literal [i.e: PHP_EOL],a namespace path
  612. * [i.e: namespaces\path\to..],a array/list [i.e: array(4,5,6)] and so on.
  613. */
  614. static sxi32 ExprExtractNode(ph7_gen_state *pGen, ph7_expr_node **ppNode) {
  615. ph7_expr_node *pNode;
  616. SyToken *pCur;
  617. sxi32 rc;
  618. /* Allocate a new node */
  619. pNode = (ph7_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(ph7_expr_node));
  620. if(pNode == 0) {
  621. /* If the supplied memory subsystem is so sick that we are unable to allocate
  622. * a tiny chunk of memory, there is no much we can do here.
  623. */
  624. return SXERR_MEM;
  625. }
  626. /* Zero the structure */
  627. SyZero(pNode, sizeof(ph7_expr_node));
  628. SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(ph7_expr_node **));
  629. /* Point to the head of the token stream */
  630. pCur = pNode->pStart = pGen->pIn;
  631. /* Start collecting tokens */
  632. if(pCur->nType & PH7_TK_OP) {
  633. /* Point to the instance that describe this operator */
  634. pNode->pOp = (const ph7_expr_op *)pCur->pUserData;
  635. /* Advance the stream cursor */
  636. pCur++;
  637. } else if(pCur->nType & PH7_TK_DOLLAR) {
  638. /* Isolate variable */
  639. while(pCur < pGen->pEnd && (pCur->nType & PH7_TK_DOLLAR)) {
  640. pCur++; /* Variable variable */
  641. }
  642. if(pCur < pGen->pEnd) {
  643. if(pCur->nType & (PH7_TK_ID | PH7_TK_KEYWORD)) {
  644. /* Variable name */
  645. pCur++;
  646. } else if(pCur->nType & PH7_TK_OCB /* '{' */) {
  647. pCur++;
  648. /* Dynamic variable name,Collect until the next non nested '}' */
  649. PH7_DelimitNestedTokens(pCur, pGen->pEnd, PH7_TK_OCB, PH7_TK_CCB, &pCur);
  650. if(pCur < pGen->pEnd) {
  651. pCur++;
  652. } else {
  653. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Missing closing brace '}'");
  654. if(rc != SXERR_ABORT) {
  655. rc = SXERR_SYNTAX;
  656. }
  657. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  658. return rc;
  659. }
  660. }
  661. }
  662. pNode->xCode = PH7_CompileVariable;
  663. } else if(pCur->nType & PH7_TK_KEYWORD) {
  664. sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pCur->pUserData);
  665. if(nKeyword == PH7_KEYWORD_ARRAY || nKeyword == PH7_KEYWORD_LIST) {
  666. /* List/Array node */
  667. if(&pCur[1] >= pGen->pEnd || (pCur[1].nType & PH7_TK_LPAREN) == 0) {
  668. /* Assume a literal */
  669. ExprAssembleLiteral(&pCur, pGen->pEnd);
  670. pNode->xCode = PH7_CompileLiteral;
  671. } else {
  672. pCur += 2;
  673. /* Collect array/list tokens */
  674. PH7_DelimitNestedTokens(pCur, pGen->pEnd, PH7_TK_LPAREN /* '(' */, PH7_TK_RPAREN /* ')' */, &pCur);
  675. if(pCur < pGen->pEnd) {
  676. pCur++;
  677. } else {
  678. /* Syntax error */
  679. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
  680. "%s: Missing closing parenthesis ')'", nKeyword == PH7_KEYWORD_LIST ? "list" : "array");
  681. if(rc != SXERR_ABORT) {
  682. rc = SXERR_SYNTAX;
  683. }
  684. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  685. return rc;
  686. }
  687. pNode->xCode = (nKeyword == PH7_KEYWORD_LIST) ? PH7_CompileList : PH7_CompileArray;
  688. if(pNode->xCode == PH7_CompileList) {
  689. ph7_expr_op *pOp = (pCur < pGen->pEnd) ? (ph7_expr_op *)pCur->pUserData : 0;
  690. if(pCur >= pGen->pEnd || (pCur->nType & PH7_TK_OP) == 0 || pOp == 0 || pOp->iVmOp != PH7_OP_STORE /*'='*/) {
  691. /* Syntax error */
  692. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "list(): expecting '=' after construct");
  693. if(rc != SXERR_ABORT) {
  694. rc = SXERR_SYNTAX;
  695. }
  696. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  697. return rc;
  698. }
  699. }
  700. }
  701. } else if(nKeyword == PH7_KEYWORD_FUNCTION) {
  702. /* Anonymous function */
  703. if(&pCur[1] >= pGen->pEnd) {
  704. /* Assume a literal */
  705. ExprAssembleLiteral(&pCur, pGen->pEnd);
  706. pNode->xCode = PH7_CompileLiteral;
  707. } else {
  708. /* Assemble anonymous functions body */
  709. rc = ExprAssembleClosure(&(*pGen), &pCur, pGen->pEnd);
  710. if(rc != SXRET_OK) {
  711. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  712. return rc;
  713. }
  714. pNode->xCode = PH7_CompileClosure;
  715. }
  716. } else {
  717. /* Assume a literal */
  718. ExprAssembleLiteral(&pCur, pGen->pEnd);
  719. pNode->xCode = PH7_CompileLiteral;
  720. }
  721. } else if(pCur->nType & (PH7_TK_NSSEP | PH7_TK_ID)) {
  722. /* Constants,function name,namespace path,class name... */
  723. ExprAssembleLiteral(&pCur, pGen->pEnd);
  724. pNode->xCode = PH7_CompileLiteral;
  725. } else {
  726. if((pCur->nType & (PH7_TK_LPAREN | PH7_TK_RPAREN | PH7_TK_COMMA | PH7_TK_COLON | PH7_TK_CSB | PH7_TK_OCB | PH7_TK_CCB)) == 0) {
  727. /* Point to the code generator routine */
  728. pNode->xCode = PH7_GetNodeHandler(pCur->nType);
  729. if(pNode->xCode == 0) {
  730. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData);
  731. if(rc != SXERR_ABORT) {
  732. rc = SXERR_SYNTAX;
  733. }
  734. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  735. return rc;
  736. }
  737. }
  738. /* Advance the stream cursor */
  739. pCur++;
  740. }
  741. /* Point to the end of the token stream */
  742. pNode->pEnd = pCur;
  743. /* Save the node for later processing */
  744. *ppNode = pNode;
  745. /* Synchronize cursors */
  746. pGen->pIn = pCur;
  747. return SXRET_OK;
  748. }
  749. /*
  750. * Point to the next expression that should be evaluated shortly.
  751. * The cursor stops when it hit a comma ',' or a semi-colon and the nesting
  752. * level is zero.
  753. */
  754. PH7_PRIVATE sxi32 PH7_GetNextExpr(SyToken *pStart, SyToken *pEnd, SyToken **ppNext) {
  755. SyToken *pCur = pStart;
  756. sxi32 iNest = 0;
  757. if(pCur >= pEnd || (pCur->nType & PH7_TK_SEMI/*';'*/)) {
  758. /* Last expression */
  759. return SXERR_EOF;
  760. }
  761. while(pCur < pEnd) {
  762. if((pCur->nType & (PH7_TK_COMMA/*','*/ | PH7_TK_SEMI/*';'*/)) && iNest <= 0) {
  763. break;
  764. }
  765. if(pCur->nType & (PH7_TK_LPAREN/*'('*/ | PH7_TK_OSB/*'['*/ | PH7_TK_OCB/*'{'*/)) {
  766. iNest++;
  767. } else if(pCur->nType & (PH7_TK_RPAREN/*')'*/ | PH7_TK_CSB/*']'*/ | PH7_TK_CCB/*'}*/)) {
  768. iNest--;
  769. }
  770. pCur++;
  771. }
  772. *ppNext = pCur;
  773. return SXRET_OK;
  774. }
  775. /*
  776. * Free an expression tree.
  777. */
  778. static void ExprFreeTree(ph7_gen_state *pGen, ph7_expr_node *pNode) {
  779. if(pNode->pLeft) {
  780. /* Release the left tree */
  781. ExprFreeTree(&(*pGen), pNode->pLeft);
  782. }
  783. if(pNode->pRight) {
  784. /* Release the right tree */
  785. ExprFreeTree(&(*pGen), pNode->pRight);
  786. }
  787. if(pNode->pCond) {
  788. /* Release the conditional tree used by the ternary operator */
  789. ExprFreeTree(&(*pGen), pNode->pCond);
  790. }
  791. if(SySetUsed(&pNode->aNodeArgs) > 0) {
  792. ph7_expr_node **apArg;
  793. sxu32 n;
  794. /* Release node arguments */
  795. apArg = (ph7_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
  796. for(n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n) {
  797. ExprFreeTree(&(*pGen), apArg[n]);
  798. }
  799. SySetRelease(&pNode->aNodeArgs);
  800. }
  801. /* Finally,release this node */
  802. SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode);
  803. }
  804. /*
  805. * Free an expression tree.
  806. * This function is a wrapper around ExprFreeTree() defined above.
  807. */
  808. PH7_PRIVATE sxi32 PH7_ExprFreeTree(ph7_gen_state *pGen, SySet *pNodeSet) {
  809. ph7_expr_node **apNode;
  810. sxu32 n;
  811. apNode = (ph7_expr_node **)SySetBasePtr(pNodeSet);
  812. for(n = 0 ; n < SySetUsed(pNodeSet) ; ++n) {
  813. if(apNode[n]) {
  814. ExprFreeTree(&(*pGen), apNode[n]);
  815. }
  816. }
  817. return SXRET_OK;
  818. }
  819. /*
  820. * Check if the given node is a modifiable l/r-value.
  821. * Return TRUE if modifiable.FALSE otherwise.
  822. */
  823. static int ExprIsModifiableValue(ph7_expr_node *pNode, sxu8 bFunc) {
  824. sxi32 iExprOp;
  825. if(pNode->pOp == 0) {
  826. return pNode->xCode == PH7_CompileVariable ? TRUE : FALSE;
  827. }
  828. iExprOp = pNode->pOp->iOp;
  829. if(iExprOp == EXPR_OP_ARROW /*'->' */ || iExprOp == EXPR_OP_DC /*'::'*/) {
  830. return TRUE;
  831. }
  832. if(iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/) {
  833. if(pNode->pLeft->pOp) {
  834. if(pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_ARROW /*'->'*/
  835. && pNode->pLeft->pOp->iOp != EXPR_OP_DC /*'::'*/) {
  836. return FALSE;
  837. }
  838. } else if(pNode->pLeft->xCode != PH7_CompileVariable) {
  839. return FALSE;
  840. }
  841. return TRUE;
  842. }
  843. if(bFunc && iExprOp == EXPR_OP_FUNC_CALL) {
  844. return TRUE;
  845. }
  846. /* Not a modifiable l or r-value */
  847. return FALSE;
  848. }
  849. /* Forward declaration */
  850. static sxi32 ExprMakeTree(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 nToken);
  851. /* Macro to check if the given node is a terminal */
  852. #define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft ))
  853. /*
  854. * Build an expression tree for each given function argument.
  855. * When errors,PH7 take care of generating the appropriate error message.
  856. */
  857. static sxi32 ExprProcessFuncArguments(ph7_gen_state *pGen, ph7_expr_node *pOp, ph7_expr_node **apNode, sxi32 nToken) {
  858. sxi32 iNest, iCur, iNode;
  859. sxi32 rc;
  860. /* Process function arguments from left to right */
  861. iCur = 0;
  862. for(;;) {
  863. if(iCur >= nToken) {
  864. /* No more arguments to process */
  865. break;
  866. }
  867. iNode = iCur;
  868. iNest = 0;
  869. while(iCur < nToken) {
  870. if(apNode[iCur]) {
  871. if((apNode[iCur]->pStart->nType & PH7_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0) {
  872. break;
  873. } else if(apNode[iCur]->pStart->nType & (PH7_TK_LPAREN | PH7_TK_OSB | PH7_TK_OCB)) {
  874. iNest++;
  875. } else if(apNode[iCur]->pStart->nType & (PH7_TK_RPAREN | PH7_TK_CCB | PH7_TK_CSB)) {
  876. iNest--;
  877. }
  878. }
  879. iCur++;
  880. }
  881. if(iCur > iNode) {
  882. if(apNode[iNode] && (apNode[iNode]->pStart->nType & PH7_TK_AMPER /*'&'*/) && ((iCur - iNode) == 2)
  883. && apNode[iNode + 1]->xCode == PH7_CompileVariable) {
  884. PH7_GenCompileError(&(*pGen), E_WARNING, apNode[iNode]->pStart->nLine,
  885. "call-time pass-by-reference is deprecated");
  886. ExprFreeTree(&(*pGen), apNode[iNode]);
  887. apNode[iNode] = 0;
  888. }
  889. ExprMakeTree(&(*pGen), &apNode[iNode], iCur - iNode);
  890. if(apNode[iNode]) {
  891. /* Put a pointer to the root of the tree in the arguments set */
  892. SySetPut(&pOp->aNodeArgs, (const void *)&apNode[iNode]);
  893. } else {
  894. /* Empty function argument */
  895. rc = PH7_GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Empty function argument");
  896. if(rc != SXERR_ABORT) {
  897. rc = SXERR_SYNTAX;
  898. }
  899. return rc;
  900. }
  901. } else {
  902. rc = PH7_GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
  903. if(rc != SXERR_ABORT) {
  904. rc = SXERR_SYNTAX;
  905. }
  906. return rc;
  907. }
  908. /* Jump trailing comma */
  909. if(iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & PH7_TK_COMMA)) {
  910. iCur++;
  911. if(iCur >= nToken) {
  912. /* missing function argument */
  913. rc = PH7_GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument");
  914. if(rc != SXERR_ABORT) {
  915. rc = SXERR_SYNTAX;
  916. }
  917. return rc;
  918. }
  919. }
  920. }
  921. return SXRET_OK;
  922. }
  923. /*
  924. * Create an expression tree from an array of tokens.
  925. * If successful, the root of the tree is stored in apNode[0].
  926. * When errors,PH7 take care of generating the appropriate error message.
  927. */
  928. static sxi32 ExprMakeTree(ph7_gen_state *pGen, ph7_expr_node **apNode, sxi32 nToken) {
  929. sxi32 i, iLeft, iRight;
  930. ph7_expr_node *pNode;
  931. sxi32 iCur;
  932. sxi32 rc;
  933. if(nToken <= 0 || (nToken == 1 && apNode[0]->xCode)) {
  934. /* TICKET 1433-17: self evaluating node */
  935. return SXRET_OK;
  936. }
  937. /* Process expressions enclosed in parenthesis first */
  938. for(iCur = 0 ; iCur < nToken ; ++iCur) {
  939. sxi32 iNest;
  940. /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator
  941. * since the LPAREN token can also be an operator [i.e: Function call].
  942. */
  943. if(apNode[iCur] == 0 || apNode[iCur]->pStart->nType != PH7_TK_LPAREN) {
  944. continue;
  945. }
  946. iNest = 1;
  947. iLeft = iCur;
  948. /* Find the closing parenthesis */
  949. iCur++;
  950. while(iCur < nToken) {
  951. if(apNode[iCur]) {
  952. if(apNode[iCur]->pStart->nType & PH7_TK_RPAREN /* ')' */) {
  953. /* Decrement nesting level */
  954. iNest--;
  955. if(iNest <= 0) {
  956. break;
  957. }
  958. } else if(apNode[iCur]->pStart->nType & PH7_TK_LPAREN /* '(' */) {
  959. /* Increment nesting level */
  960. iNest++;
  961. }
  962. }
  963. iCur++;
  964. }
  965. if(iCur - iLeft > 1) {
  966. /* Recurse and process this expression */
  967. rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1);
  968. if(rc != SXRET_OK) {
  969. return rc;
  970. }
  971. }
  972. /* Free the left and right nodes */
  973. ExprFreeTree(&(*pGen), apNode[iLeft]);
  974. ExprFreeTree(&(*pGen), apNode[iCur]);
  975. apNode[iLeft] = 0;
  976. apNode[iCur] = 0;
  977. }
  978. /* Process expressions enclosed in braces */
  979. for(iCur = 0 ; iCur < nToken ; ++iCur) {
  980. sxi32 iNest;
  981. /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator
  982. * since the OCB '{' token can also be an operator [i.e: subscripting].
  983. */
  984. if(apNode[iCur] == 0 || apNode[iCur]->pStart->nType != PH7_TK_OCB) {
  985. continue;
  986. }
  987. iNest = 1;
  988. iLeft = iCur;
  989. /* Find the closing parenthesis */
  990. iCur++;
  991. while(iCur < nToken) {
  992. if(apNode[iCur]) {
  993. if(apNode[iCur]->pStart->nType & PH7_TK_CCB/*'}'*/) {
  994. /* Decrement nesting level */
  995. iNest--;
  996. if(iNest <= 0) {
  997. break;
  998. }
  999. } else if(apNode[iCur]->pStart->nType & PH7_TK_OCB /*'{'*/) {
  1000. /* Increment nesting level */
  1001. iNest++;
  1002. }
  1003. }
  1004. iCur++;
  1005. }
  1006. if(iCur - iLeft > 1) {
  1007. /* Recurse and process this expression */
  1008. rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1);
  1009. if(rc != SXRET_OK) {
  1010. return rc;
  1011. }
  1012. }
  1013. /* Free the left and right nodes */
  1014. ExprFreeTree(&(*pGen), apNode[iLeft]);
  1015. ExprFreeTree(&(*pGen), apNode[iCur]);
  1016. apNode[iLeft] = 0;
  1017. apNode[iCur] = 0;
  1018. }
  1019. /* Handle postfix [i.e: function call,subscripting,member access] operators with precedence 2 */
  1020. iLeft = -1;
  1021. for(iCur = 0 ; iCur < nToken ; ++iCur) {
  1022. if(apNode[iCur] == 0) {
  1023. continue;
  1024. }
  1025. pNode = apNode[iCur];
  1026. if(pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0) {
  1027. if(pNode->pOp->iOp == EXPR_OP_FUNC_CALL) {
  1028. /* Collect function arguments */
  1029. sxi32 iPtr = 0;
  1030. sxi32 nFuncTok = 0;
  1031. while(nFuncTok + iCur < nToken) {
  1032. if(apNode[nFuncTok + iCur]) {
  1033. if(apNode[nFuncTok + iCur]->pStart->nType & PH7_TK_LPAREN /*'('*/) {
  1034. iPtr++;
  1035. } else if(apNode[nFuncTok + iCur]->pStart->nType & PH7_TK_RPAREN /*')'*/) {
  1036. iPtr--;
  1037. if(iPtr <= 0) {
  1038. break;
  1039. }
  1040. }
  1041. }
  1042. nFuncTok++;
  1043. }
  1044. if(nFuncTok + iCur >= nToken) {
  1045. /* Syntax error */
  1046. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Missing right parenthesis ')'");
  1047. if(rc != SXERR_ABORT) {
  1048. rc = SXERR_SYNTAX;
  1049. }
  1050. return rc;
  1051. }
  1052. if(iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/) {
  1053. /* Syntax error */
  1054. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid function name");
  1055. if(rc != SXERR_ABORT) {
  1056. rc = SXERR_SYNTAX;
  1057. }
  1058. return rc;
  1059. }
  1060. if(nFuncTok > 1) {
  1061. /* Process function arguments */
  1062. rc = ExprProcessFuncArguments(&(*pGen), pNode, &apNode[iCur + 1], nFuncTok - 1);
  1063. if(rc != SXRET_OK) {
  1064. return rc;
  1065. }
  1066. }
  1067. /* Link the node to the tree */
  1068. pNode->pLeft = apNode[iLeft];
  1069. apNode[iLeft] = 0;
  1070. for(iPtr = 1; iPtr <= nFuncTok ; iPtr++) {
  1071. apNode[iCur + iPtr] = 0;
  1072. }
  1073. } else if(pNode->pOp->iOp == EXPR_OP_SUBSCRIPT) {
  1074. /* Subscripting */
  1075. sxi32 iArrTok = iCur + 1;
  1076. sxi32 iNest = 1;
  1077. if(iLeft < 0 || apNode[iLeft] == 0 || (apNode[iLeft]->pOp == 0 && (apNode[iLeft]->xCode != PH7_CompileVariable &&
  1078. apNode[iLeft]->xCode != PH7_CompileSimpleString && apNode[iLeft]->xCode != PH7_CompileString)) ||
  1079. (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* postfix */)) {
  1080. /* Syntax error */
  1081. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid array name");
  1082. if(rc != SXERR_ABORT) {
  1083. rc = SXERR_SYNTAX;
  1084. }
  1085. return rc;
  1086. }
  1087. /* Collect index tokens */
  1088. while(iArrTok < nToken) {
  1089. if(apNode[iArrTok]) {
  1090. if(apNode[iArrTok]->pOp && apNode[iArrTok]->pOp->iOp == EXPR_OP_SUBSCRIPT && apNode[iArrTok]->pLeft == 0) {
  1091. /* Increment nesting level */
  1092. iNest++;
  1093. } else if(apNode[iArrTok]->pStart->nType & PH7_TK_CSB /*']'*/) {
  1094. /* Decrement nesting level */
  1095. iNest--;
  1096. if(iNest <= 0) {
  1097. break;
  1098. }
  1099. }
  1100. }
  1101. ++iArrTok;
  1102. }
  1103. if(iArrTok > iCur + 1) {
  1104. /* Recurse and process this expression */
  1105. rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iArrTok - iCur - 1);
  1106. if(rc != SXRET_OK) {
  1107. return rc;
  1108. }
  1109. /* Link the node to it's index */
  1110. SySetPut(&pNode->aNodeArgs, (const void *)&apNode[iCur + 1]);
  1111. }
  1112. /* Link the node to the tree */
  1113. pNode->pLeft = apNode[iLeft];
  1114. pNode->pRight = 0;
  1115. apNode[iLeft] = 0;
  1116. for(iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest) {
  1117. apNode[iNest] = 0;
  1118. }
  1119. } else {
  1120. /* Member access operators [i.e: '->','::'] */
  1121. iRight = iCur + 1;
  1122. while(iRight < nToken && apNode[iRight] == 0) {
  1123. iRight++;
  1124. }
  1125. if(iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft)) {
  1126. /* Syntax error */
  1127. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid member name", &pNode->pOp->sOp);
  1128. if(rc != SXERR_ABORT) {
  1129. rc = SXERR_SYNTAX;
  1130. }
  1131. return rc;
  1132. }
  1133. /* Link the node to the tree */
  1134. pNode->pLeft = apNode[iLeft];
  1135. if(pNode->pOp->iOp == EXPR_OP_ARROW /*'->'*/ && pNode->pLeft->pOp == 0 &&
  1136. pNode->pLeft->xCode != PH7_CompileVariable) {
  1137. /* Syntax error */
  1138. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
  1139. "'%z': Expecting a variable as left operand", &pNode->pOp->sOp);
  1140. if(rc != SXERR_ABORT) {
  1141. rc = SXERR_SYNTAX;
  1142. }
  1143. return rc;
  1144. }
  1145. pNode->pRight = apNode[iRight];
  1146. apNode[iLeft] = apNode[iRight] = 0;
  1147. }
  1148. }
  1149. iLeft = iCur;
  1150. }
  1151. /* Handle left associative (new, clone) operators */
  1152. for(iCur = 0 ; iCur < nToken ; ++iCur) {
  1153. if(apNode[iCur] == 0) {
  1154. continue;
  1155. }
  1156. pNode = apNode[iCur];
  1157. if(pNode->pOp && pNode->pOp->iPrec == 1 && pNode->pLeft == 0) {
  1158. SyToken *pToken;
  1159. /* Get the left node */
  1160. iLeft = iCur + 1;
  1161. while(iLeft < nToken && apNode[iLeft] == 0) {
  1162. iLeft++;
  1163. }
  1164. if(iLeft >= nToken || !NODE_ISTERM(iLeft)) {
  1165. /* Syntax error */
  1166. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Expecting class constructor call",
  1167. &pNode->pOp->sOp);
  1168. if(rc != SXERR_ABORT) {
  1169. rc = SXERR_SYNTAX;
  1170. }
  1171. return rc;
  1172. }
  1173. /* Make sure the operand are of a valid type */
  1174. if(pNode->pOp->iOp == EXPR_OP_CLONE) {
  1175. /* Clone:
  1176. * Symisc eXtension: 'clone' accepts now as it's left operand:
  1177. * ++ function call (including anonymous)
  1178. * ++ array member
  1179. * ++ 'new' operator
  1180. * Example:
  1181. * clone $pObj;
  1182. * clone obj(); // function obj(){ return new Class(); }
  1183. * clone $a['object']; // $a = array('object' => new Class());
  1184. */
  1185. if(apNode[iLeft]->pOp == 0) {
  1186. if(apNode[iLeft]->xCode != PH7_CompileVariable) {
  1187. pToken = apNode[iLeft]->pStart;
  1188. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Unexpected token '%z'",
  1189. &pNode->pOp->sOp, &pToken->sData);
  1190. if(rc != SXERR_ABORT) {
  1191. rc = SXERR_SYNTAX;
  1192. }
  1193. return rc;
  1194. }
  1195. }
  1196. } else {
  1197. /* New */
  1198. if(apNode[iLeft]->pOp == 0) {
  1199. ProcNodeConstruct xCons = apNode[iLeft]->xCode;
  1200. if(xCons != PH7_CompileVariable && xCons != PH7_CompileLiteral && xCons != PH7_CompileSimpleString) {
  1201. pToken = apNode[iLeft]->pStart;
  1202. /* Syntax error */
  1203. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
  1204. "'%z': Unexpected token '%z', expecting literal, variable or constructor call",
  1205. &pNode->pOp->sOp, &pToken->sData);
  1206. if(rc != SXERR_ABORT) {
  1207. rc = SXERR_SYNTAX;
  1208. }
  1209. return rc;
  1210. }
  1211. }
  1212. }
  1213. /* Link the node to the tree */
  1214. pNode->pLeft = apNode[iLeft];
  1215. apNode[iLeft] = 0;
  1216. pNode->pRight = 0; /* Paranoid */
  1217. }
  1218. }
  1219. /* Handle post/pre increment/decrement [i.e: ++/--] operators with precedence 3 */
  1220. iLeft = -1;
  1221. for(iCur = 0 ; iCur < nToken ; ++iCur) {
  1222. if(apNode[iCur] == 0) {
  1223. continue;
  1224. }
  1225. pNode = apNode[iCur];
  1226. if(pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0) {
  1227. if(iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */)
  1228. || apNode[iLeft]->xCode == PH7_CompileVariable)) {
  1229. /* Link the node to the tree */
  1230. pNode->pLeft = apNode[iLeft];
  1231. apNode[iLeft] = 0;
  1232. }
  1233. }
  1234. iLeft = iCur;
  1235. }
  1236. iLeft = -1;
  1237. for(iCur = nToken - 1 ; iCur >= 0 ; iCur--) {
  1238. if(apNode[iCur] == 0) {
  1239. continue;
  1240. }
  1241. pNode = apNode[iCur];
  1242. if(pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0) {
  1243. if(iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != PH7_CompileVariable)
  1244. || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */)) {
  1245. /* Syntax error */
  1246. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z' operator needs l-value", &pNode->pOp->sOp);
  1247. if(rc != SXERR_ABORT) {
  1248. rc = SXERR_SYNTAX;
  1249. }
  1250. return rc;
  1251. }
  1252. /* Link the node to the tree */
  1253. pNode->pLeft = apNode[iLeft];
  1254. apNode[iLeft] = 0;
  1255. /* Mark as pre-increment/decrement node */
  1256. pNode->iFlags |= EXPR_NODE_PRE_INCR;
  1257. }
  1258. iLeft = iCur;
  1259. }
  1260. /* Handle right associative unary and cast operators [i.e: !,(string),~...] with precedence 4*/
  1261. iLeft = 0;
  1262. for(iCur = nToken - 1 ; iCur >= 0 ; iCur--) {
  1263. if(apNode[iCur]) {
  1264. pNode = apNode[iCur];
  1265. if(pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0) {
  1266. if(iLeft > 0) {
  1267. /* Link the node to the tree */
  1268. pNode->pLeft = apNode[iLeft];
  1269. apNode[iLeft] = 0;
  1270. if(pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4) {
  1271. if(pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0) {
  1272. /* Syntax error */
  1273. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pLeft->pStart->nLine, "'%z': Missing operand", &pNode->pLeft->pOp->sOp);
  1274. if(rc != SXERR_ABORT) {
  1275. rc = SXERR_SYNTAX;
  1276. }
  1277. return rc;
  1278. }
  1279. }
  1280. } else {
  1281. /* Syntax error */
  1282. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing operand", &pNode->pOp->sOp);
  1283. if(rc != SXERR_ABORT) {
  1284. rc = SXERR_SYNTAX;
  1285. }
  1286. return rc;
  1287. }
  1288. }
  1289. /* Save terminal position */
  1290. iLeft = iCur;
  1291. }
  1292. }
  1293. /* Process left and non-associative binary operators [i.e: *,/,&&,||...]*/
  1294. for(i = 7 ; i < 18 ; i++) {
  1295. iLeft = -1;
  1296. for(iCur = 0 ; iCur < nToken ; ++iCur) {
  1297. if(apNode[iCur] == 0) {
  1298. continue;
  1299. }
  1300. pNode = apNode[iCur];
  1301. if(pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0) {
  1302. /* Get the right node */
  1303. iRight = iCur + 1;
  1304. while(iRight < nToken && apNode[iRight] == 0) {
  1305. iRight++;
  1306. }
  1307. if(iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft)) {
  1308. /* Syntax error */
  1309. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
  1310. if(rc != SXERR_ABORT) {
  1311. rc = SXERR_SYNTAX;
  1312. }
  1313. return rc;
  1314. }
  1315. if(pNode->pOp->iOp == EXPR_OP_REF) {
  1316. sxi32 iTmp;
  1317. /* Reference operator [i.e: '&=' ]*/
  1318. if(ExprIsModifiableValue(apNode[iLeft], FALSE) == FALSE || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iVmOp == PH7_OP_MEMBER /*->,::*/)) {
  1319. /* Left operand must be a modifiable l-value */
  1320. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'&': Left operand must be a modifiable l-value");
  1321. if(rc != SXERR_ABORT) {
  1322. rc = SXERR_SYNTAX;
  1323. }
  1324. return rc;
  1325. }
  1326. if(apNode[iLeft]->pOp == 0 || apNode[iLeft]->pOp->iOp != EXPR_OP_SUBSCRIPT /*$a[] =& 14*/) {
  1327. if(ExprIsModifiableValue(apNode[iRight], TRUE) == FALSE) {
  1328. if(apNode[iRight]->pOp == 0 || (apNode[iRight]->pOp->iOp != EXPR_OP_NEW /* new */
  1329. && apNode[iRight]->pOp->iOp != EXPR_OP_CLONE /* clone */)) {
  1330. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
  1331. "Reference operator '&' require a variable not a constant expression as it's right operand");
  1332. if(rc != SXERR_ABORT) {
  1333. rc = SXERR_SYNTAX;
  1334. }
  1335. return rc;
  1336. }
  1337. }
  1338. }
  1339. /* Swap operands */
  1340. iTmp = iRight;
  1341. iRight = iLeft;
  1342. iLeft = iTmp;
  1343. }
  1344. /* Link the node to the tree */
  1345. pNode->pLeft = apNode[iLeft];
  1346. pNode->pRight = apNode[iRight];
  1347. apNode[iLeft] = apNode[iRight] = 0;
  1348. }
  1349. iLeft = iCur;
  1350. }
  1351. }
  1352. /* Handle the ternary operator. (expr1) ? (expr2) : (expr3)
  1353. * Note that we do not need a precedence loop here since
  1354. * we are dealing with a single operator.
  1355. */
  1356. iLeft = -1;
  1357. for(iCur = 0 ; iCur < nToken ; ++iCur) {
  1358. if(apNode[iCur] == 0) {
  1359. continue;
  1360. }
  1361. pNode = apNode[iCur];
  1362. if(pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0) {
  1363. sxi32 iNest = 1;
  1364. if(iLeft < 0 || !NODE_ISTERM(iLeft)) {
  1365. /* Missing condition */
  1366. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Syntax error", &pNode->pOp->sOp);
  1367. if(rc != SXERR_ABORT) {
  1368. rc = SXERR_SYNTAX;
  1369. }
  1370. return rc;
  1371. }
  1372. /* Get the right node */
  1373. iRight = iCur + 1;
  1374. while(iRight < nToken) {
  1375. if(apNode[iRight]) {
  1376. if(apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0) {
  1377. /* Increment nesting level */
  1378. ++iNest;
  1379. } else if(apNode[iRight]->pStart->nType & PH7_TK_COLON /*:*/) {
  1380. /* Decrement nesting level */
  1381. --iNest;
  1382. if(iNest <= 0) {
  1383. break;
  1384. }
  1385. }
  1386. }
  1387. iRight++;
  1388. }
  1389. if(iRight > iCur + 1) {
  1390. /* Recurse and process the then expression */
  1391. rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iRight - iCur - 1);
  1392. if(rc != SXRET_OK) {
  1393. return rc;
  1394. }
  1395. /* Link the node to the tree */
  1396. pNode->pLeft = apNode[iCur + 1];
  1397. } else {
  1398. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'then' expression", &pNode->pOp->sOp);
  1399. if(rc != SXERR_ABORT) {
  1400. rc = SXERR_SYNTAX;
  1401. }
  1402. return rc;
  1403. }
  1404. apNode[iCur + 1] = 0;
  1405. if(iRight + 1 < nToken) {
  1406. /* Recurse and process the else expression */
  1407. rc = ExprMakeTree(&(*pGen), &apNode[iRight + 1], nToken - iRight - 1);
  1408. if(rc != SXRET_OK) {
  1409. return rc;
  1410. }
  1411. /* Link the node to the tree */
  1412. pNode->pRight = apNode[iRight + 1];
  1413. apNode[iRight + 1] = apNode[iRight] = 0;
  1414. } else {
  1415. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'else' expression", &pNode->pOp->sOp);
  1416. if(rc != SXERR_ABORT) {
  1417. rc = SXERR_SYNTAX;
  1418. }
  1419. return rc;
  1420. }
  1421. /* Point to the condition */
  1422. pNode->pCond = apNode[iLeft];
  1423. apNode[iLeft] = 0;
  1424. break;
  1425. }
  1426. iLeft = iCur;
  1427. }
  1428. /* Process right associative binary operators [i.e: '=','+=','/=']
  1429. * Note: All right associative binary operators have precedence 18
  1430. * so there is no need for a precedence loop here.
  1431. */
  1432. iRight = -1;
  1433. for(iCur = nToken - 1 ; iCur >= 0 ; iCur--) {
  1434. if(apNode[iCur] == 0) {
  1435. continue;
  1436. }
  1437. pNode = apNode[iCur];
  1438. if(pNode->pOp && pNode->pOp->iPrec == 19 && pNode->pLeft == 0) {
  1439. /* Get the left node */
  1440. iLeft = iCur - 1;
  1441. while(iLeft >= 0 && apNode[iLeft] == 0) {
  1442. iLeft--;
  1443. }
  1444. if(iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft)) {
  1445. /* Syntax error */
  1446. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
  1447. if(rc != SXERR_ABORT) {
  1448. rc = SXERR_SYNTAX;
  1449. }
  1450. return rc;
  1451. }
  1452. if(ExprIsModifiableValue(apNode[iLeft], FALSE) == FALSE) {
  1453. if(pNode->pOp->iVmOp != PH7_OP_STORE || apNode[iLeft]->xCode != PH7_CompileList) {
  1454. /* Left operand must be a modifiable l-value */
  1455. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,
  1456. "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp);
  1457. if(rc != SXERR_ABORT) {
  1458. rc = SXERR_SYNTAX;
  1459. }
  1460. return rc;
  1461. }
  1462. }
  1463. /* Link the node to the tree (Reverse) */
  1464. pNode->pLeft = apNode[iRight];
  1465. pNode->pRight = apNode[iLeft];
  1466. apNode[iLeft] = apNode[iRight] = 0;
  1467. }
  1468. iRight = iCur;
  1469. }
  1470. /* Process left associative binary operators that have the lowest precedence [i.e: and,or,xor] */
  1471. for(i = 20 ; i < 24 ; i++) {
  1472. iLeft = -1;
  1473. for(iCur = 0 ; iCur < nToken ; ++iCur) {
  1474. if(apNode[iCur] == 0) {
  1475. continue;
  1476. }
  1477. pNode = apNode[iCur];
  1478. if(pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0) {
  1479. /* Get the right node */
  1480. iRight = iCur + 1;
  1481. while(iRight < nToken && apNode[iRight] == 0) {
  1482. iRight++;
  1483. }
  1484. if(iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft)) {
  1485. /* Syntax error */
  1486. rc = PH7_GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp);
  1487. if(rc != SXERR_ABORT) {
  1488. rc = SXERR_SYNTAX;
  1489. }
  1490. return rc;
  1491. }
  1492. /* Link the node to the tree */
  1493. pNode->pLeft = apNode[iLeft];
  1494. pNode->pRight = apNode[iRight];
  1495. apNode[iLeft] = apNode[iRight] = 0;
  1496. }
  1497. iLeft = iCur;
  1498. }
  1499. }
  1500. /* Point to the root of the expression tree */
  1501. for(iCur = 1 ; iCur < nToken ; ++iCur) {
  1502. if(apNode[iCur]) {
  1503. if((apNode[iCur]->pOp || apNode[iCur]->xCode) && apNode[0] != 0) {
  1504. rc = PH7_GenCompileError(pGen, E_ERROR, apNode[iCur]->pStart->nLine, "Unexpected token '%z'", &apNode[iCur]->pStart->sData);
  1505. if(rc != SXERR_ABORT) {
  1506. rc = SXERR_SYNTAX;
  1507. }
  1508. return rc;
  1509. }
  1510. apNode[0] = apNode[iCur];
  1511. apNode[iCur] = 0;
  1512. }
  1513. }
  1514. return SXRET_OK;
  1515. }
  1516. /*
  1517. * Build an expression tree from the freshly extracted raw tokens.
  1518. * If successful, the root of the tree is stored in ppRoot.
  1519. * When errors,PH7 take care of generating the appropriate error message.
  1520. * This is the public interface used by the most code generator routines.
  1521. */
  1522. PH7_PRIVATE sxi32 PH7_ExprMakeTree(ph7_gen_state *pGen, SySet *pExprNode, ph7_expr_node **ppRoot) {
  1523. ph7_expr_node **apNode;
  1524. ph7_expr_node *pNode;
  1525. sxi32 rc;
  1526. /* Reset node container */
  1527. SySetReset(pExprNode);
  1528. pNode = 0; /* Prevent compiler warning */
  1529. /* Extract nodes one after one until we hit the end of the input */
  1530. while(pGen->pIn < pGen->pEnd) {
  1531. rc = ExprExtractNode(&(*pGen), &pNode);
  1532. if(rc != SXRET_OK) {
  1533. return rc;
  1534. }
  1535. /* Save the extracted node */
  1536. SySetPut(pExprNode, (const void *)&pNode);
  1537. }
  1538. if(SySetUsed(pExprNode) < 1) {
  1539. /* Empty expression [i.e: A semi-colon;] */
  1540. *ppRoot = 0;
  1541. return SXRET_OK;
  1542. }
  1543. apNode = (ph7_expr_node **)SySetBasePtr(pExprNode);
  1544. /* Make sure we are dealing with valid nodes */
  1545. rc = ExprVerifyNodes(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
  1546. if(rc != SXRET_OK) {
  1547. /* Don't worry about freeing memory,upper layer will
  1548. * cleanup the mess left behind.
  1549. */
  1550. *ppRoot = 0;
  1551. return rc;
  1552. }
  1553. /* Build the tree */
  1554. rc = ExprMakeTree(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode));
  1555. if(rc != SXRET_OK) {
  1556. /* Something goes wrong [i.e: Syntax error] */
  1557. *ppRoot = 0;
  1558. return rc;
  1559. }
  1560. /* Point to the root of the tree */
  1561. *ppRoot = apNode[0];
  1562. return SXRET_OK;
  1563. }