diff options
author | Aaron Seigo <aseigo@kde.org> | 2014-12-07 10:08:07 +0100 |
---|---|---|
committer | Aaron Seigo <aseigo@kde.org> | 2014-12-11 01:07:08 +0100 |
commit | 9ee8378d393778ac67314be7ea8d5bcbaeee9ee0 (patch) | |
tree | cf93471a69f9f4bbb4940de55ae134106fcd8380 /common/unqlite/jx9_parse.c | |
parent | ee6f068dff6b15441e553ffbfb2bf8aa97b26f57 (diff) | |
download | sink-9ee8378d393778ac67314be7ea8d5bcbaeee9ee0.tar.gz sink-9ee8378d393778ac67314be7ea8d5bcbaeee9ee0.zip |
try out unqlite
Diffstat (limited to 'common/unqlite/jx9_parse.c')
-rw-r--r-- | common/unqlite/jx9_parse.c | 1177 |
1 files changed, 1177 insertions, 0 deletions
diff --git a/common/unqlite/jx9_parse.c b/common/unqlite/jx9_parse.c new file mode 100644 index 0000000..fff934e --- /dev/null +++ b/common/unqlite/jx9_parse.c | |||
@@ -0,0 +1,1177 @@ | |||
1 | /* | ||
2 | * Symisc JX9: A Highly Efficient Embeddable Scripting Engine Based on JSON. | ||
3 | * Copyright (C) 2012-2013, Symisc Systems http://jx9.symisc.net/ | ||
4 | * Version 1.7.2 | ||
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://jx9.symisc.net/ | ||
12 | */ | ||
13 | /* $SymiscID: parse.c v1.2 FreeBSD 2012-12-11 00:46 stable <chm@symisc.net> $ */ | ||
14 | #ifndef JX9_AMALGAMATION | ||
15 | #include "jx9Int.h" | ||
16 | #endif | ||
17 | /* Expression parser for the Jx9 programming language */ | ||
18 | /* Operators associativity */ | ||
19 | #define EXPR_OP_ASSOC_LEFT 0x01 /* Left associative operator */ | ||
20 | #define EXPR_OP_ASSOC_RIGHT 0x02 /* Right associative operator */ | ||
21 | #define EXPR_OP_NON_ASSOC 0x04 /* Non-associative operator */ | ||
22 | /* | ||
23 | * Operators table | ||
24 | * This table is sorted by operators priority (highest to lowest) according | ||
25 | * the JX9 language reference manual. | ||
26 | * JX9 implements all the 60 JX9 operators and have introduced the eq and ne operators. | ||
27 | * The operators precedence table have been improved dramatically so that you can do same | ||
28 | * amazing things now such as array dereferencing, on the fly function call, anonymous function | ||
29 | * as array values, object member access on instantiation and so on. | ||
30 | * Refer to the following page for a full discussion on these improvements: | ||
31 | * http://jx9.symisc.net/features.html | ||
32 | */ | ||
33 | static const jx9_expr_op aOpTable[] = { | ||
34 | /* Postfix operators */ | ||
35 | /* Precedence 2(Highest), left-associative */ | ||
36 | { {".", sizeof(char)}, EXPR_OP_DOT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_MEMBER }, | ||
37 | { {"[", sizeof(char)}, EXPR_OP_SUBSCRIPT, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_LOAD_IDX}, | ||
38 | /* Precedence 3, non-associative */ | ||
39 | { {"++", sizeof(char)*2}, EXPR_OP_INCR, 3, EXPR_OP_NON_ASSOC , JX9_OP_INCR}, | ||
40 | { {"--", sizeof(char)*2}, EXPR_OP_DECR, 3, EXPR_OP_NON_ASSOC , JX9_OP_DECR}, | ||
41 | /* Unary operators */ | ||
42 | /* Precedence 4, right-associative */ | ||
43 | { {"-", sizeof(char)}, EXPR_OP_UMINUS, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UMINUS }, | ||
44 | { {"+", sizeof(char)}, EXPR_OP_UPLUS, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_UPLUS }, | ||
45 | { {"~", sizeof(char)}, EXPR_OP_BITNOT, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_BITNOT }, | ||
46 | { {"!", sizeof(char)}, EXPR_OP_LOGNOT, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_LNOT }, | ||
47 | /* Cast operators */ | ||
48 | { {"(int)", sizeof("(int)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_INT }, | ||
49 | { {"(bool)", sizeof("(bool)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_BOOL }, | ||
50 | { {"(string)", sizeof("(string)")-1}, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_STR }, | ||
51 | { {"(float)", sizeof("(float)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_REAL }, | ||
52 | { {"(array)", sizeof("(array)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */ | ||
53 | { {"(object)", sizeof("(object)")-1 }, EXPR_OP_TYPECAST, 4, EXPR_OP_ASSOC_RIGHT, JX9_OP_CVT_ARRAY }, /* Not used, but reserved for future use */ | ||
54 | /* Binary operators */ | ||
55 | /* Precedence 7, left-associative */ | ||
56 | { {"*", sizeof(char)}, EXPR_OP_MUL, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MUL}, | ||
57 | { {"/", sizeof(char)}, EXPR_OP_DIV, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_DIV}, | ||
58 | { {"%", sizeof(char)}, EXPR_OP_MOD, 7, EXPR_OP_ASSOC_LEFT , JX9_OP_MOD}, | ||
59 | /* Precedence 8, left-associative */ | ||
60 | { {"+", sizeof(char)}, EXPR_OP_ADD, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_ADD}, | ||
61 | { {"-", sizeof(char)}, EXPR_OP_SUB, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_SUB}, | ||
62 | { {"..", sizeof(char)*2},EXPR_OP_DDOT, 8, EXPR_OP_ASSOC_LEFT, JX9_OP_CAT}, | ||
63 | /* Precedence 9, left-associative */ | ||
64 | { {"<<", sizeof(char)*2}, EXPR_OP_SHL, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHL}, | ||
65 | { {">>", sizeof(char)*2}, EXPR_OP_SHR, 9, EXPR_OP_ASSOC_LEFT, JX9_OP_SHR}, | ||
66 | /* Precedence 10, non-associative */ | ||
67 | { {"<", sizeof(char)}, EXPR_OP_LT, 10, EXPR_OP_NON_ASSOC, JX9_OP_LT}, | ||
68 | { {">", sizeof(char)}, EXPR_OP_GT, 10, EXPR_OP_NON_ASSOC, JX9_OP_GT}, | ||
69 | { {"<=", sizeof(char)*2}, EXPR_OP_LE, 10, EXPR_OP_NON_ASSOC, JX9_OP_LE}, | ||
70 | { {">=", sizeof(char)*2}, EXPR_OP_GE, 10, EXPR_OP_NON_ASSOC, JX9_OP_GE}, | ||
71 | { {"<>", sizeof(char)*2}, EXPR_OP_NE, 10, EXPR_OP_NON_ASSOC, JX9_OP_NEQ}, | ||
72 | /* Precedence 11, non-associative */ | ||
73 | { {"==", sizeof(char)*2}, EXPR_OP_EQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_EQ}, | ||
74 | { {"!=", sizeof(char)*2}, EXPR_OP_NE, 11, EXPR_OP_NON_ASSOC, JX9_OP_NEQ}, | ||
75 | { {"===", sizeof(char)*3}, EXPR_OP_TEQ, 11, EXPR_OP_NON_ASSOC, JX9_OP_TEQ}, | ||
76 | { {"!==", sizeof(char)*3}, EXPR_OP_TNE, 11, EXPR_OP_NON_ASSOC, JX9_OP_TNE}, | ||
77 | /* Precedence 12, left-associative */ | ||
78 | { {"&", sizeof(char)}, EXPR_OP_BAND, 12, EXPR_OP_ASSOC_LEFT, JX9_OP_BAND}, | ||
79 | /* Binary operators */ | ||
80 | /* Precedence 13, left-associative */ | ||
81 | { {"^", sizeof(char)}, EXPR_OP_XOR, 13, EXPR_OP_ASSOC_LEFT, JX9_OP_BXOR}, | ||
82 | /* Precedence 14, left-associative */ | ||
83 | { {"|", sizeof(char)}, EXPR_OP_BOR, 14, EXPR_OP_ASSOC_LEFT, JX9_OP_BOR}, | ||
84 | /* Precedence 15, left-associative */ | ||
85 | { {"&&", sizeof(char)*2}, EXPR_OP_LAND, 15, EXPR_OP_ASSOC_LEFT, JX9_OP_LAND}, | ||
86 | /* Precedence 16, left-associative */ | ||
87 | { {"||", sizeof(char)*2}, EXPR_OP_LOR, 16, EXPR_OP_ASSOC_LEFT, JX9_OP_LOR}, | ||
88 | /* Ternary operator */ | ||
89 | /* Precedence 17, left-associative */ | ||
90 | { {"?", sizeof(char)}, EXPR_OP_QUESTY, 17, EXPR_OP_ASSOC_LEFT, 0}, | ||
91 | /* Combined binary operators */ | ||
92 | /* Precedence 18, right-associative */ | ||
93 | { {"=", sizeof(char)}, EXPR_OP_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_STORE}, | ||
94 | { {"+=", sizeof(char)*2}, EXPR_OP_ADD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_ADD_STORE }, | ||
95 | { {"-=", sizeof(char)*2}, EXPR_OP_SUB_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SUB_STORE }, | ||
96 | { {".=", sizeof(char)*2}, EXPR_OP_DOT_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_CAT_STORE }, | ||
97 | { {"*=", sizeof(char)*2}, EXPR_OP_MUL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_MUL_STORE }, | ||
98 | { {"/=", sizeof(char)*2}, EXPR_OP_DIV_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_DIV_STORE }, | ||
99 | { {"%=", sizeof(char)*2}, EXPR_OP_MOD_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_MOD_STORE }, | ||
100 | { {"&=", sizeof(char)*2}, EXPR_OP_AND_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BAND_STORE }, | ||
101 | { {"|=", sizeof(char)*2}, EXPR_OP_OR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BOR_STORE }, | ||
102 | { {"^=", sizeof(char)*2}, EXPR_OP_XOR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_BXOR_STORE }, | ||
103 | { {"<<=", sizeof(char)*3}, EXPR_OP_SHL_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SHL_STORE }, | ||
104 | { {">>=", sizeof(char)*3}, EXPR_OP_SHR_ASSIGN, 18, EXPR_OP_ASSOC_RIGHT, JX9_OP_SHR_STORE }, | ||
105 | /* Precedence 22, left-associative [Lowest operator] */ | ||
106 | { {",", sizeof(char)}, EXPR_OP_COMMA, 22, EXPR_OP_ASSOC_LEFT, 0}, /* IMP-0139-COMMA: Symisc eXtension */ | ||
107 | }; | ||
108 | /* Function call operator need special handling */ | ||
109 | static const jx9_expr_op sFCallOp = {{"(", sizeof(char)}, EXPR_OP_FUNC_CALL, 2, EXPR_OP_ASSOC_LEFT , JX9_OP_CALL}; | ||
110 | /* | ||
111 | * Check if the given token is a potential operator or not. | ||
112 | * This function is called by the lexer each time it extract a token that may | ||
113 | * look like an operator. | ||
114 | * Return a structure [i.e: jx9_expr_op instnace ] that describe the operator on success. | ||
115 | * Otherwise NULL. | ||
116 | * Note that the function take care of handling ambiguity [i.e: whether we are dealing with | ||
117 | * a binary minus or unary minus.] | ||
118 | */ | ||
119 | JX9_PRIVATE const jx9_expr_op * jx9ExprExtractOperator(SyString *pStr, SyToken *pLast) | ||
120 | { | ||
121 | sxu32 n = 0; | ||
122 | sxi32 rc; | ||
123 | /* Do a linear lookup on the operators table */ | ||
124 | for(;;){ | ||
125 | if( n >= SX_ARRAYSIZE(aOpTable) ){ | ||
126 | break; | ||
127 | } | ||
128 | rc = SyStringCmp(pStr, &aOpTable[n].sOp, SyMemcmp); | ||
129 | if( rc == 0 ){ | ||
130 | if( aOpTable[n].sOp.nByte != sizeof(char) || (aOpTable[n].iOp != EXPR_OP_UMINUS && aOpTable[n].iOp != EXPR_OP_UPLUS) || pLast == 0 ){ | ||
131 | if( aOpTable[n].iOp == EXPR_OP_SUBSCRIPT && (pLast == 0 || (pLast->nType & (JX9_TK_ID|JX9_TK_CSB/*]*/|JX9_TK_RPAREN/*)*/)) == 0) ){ | ||
132 | /* JSON Array not subscripting, return NULL */ | ||
133 | return 0; | ||
134 | } | ||
135 | /* There is no ambiguity here, simply return the first operator seen */ | ||
136 | return &aOpTable[n]; | ||
137 | } | ||
138 | /* Handle ambiguity */ | ||
139 | if( pLast->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_COLON/*:*/|JX9_TK_COMMA/*, '*/) ){ | ||
140 | /* Unary opertors have prcedence here over binary operators */ | ||
141 | return &aOpTable[n]; | ||
142 | } | ||
143 | if( pLast->nType & JX9_TK_OP ){ | ||
144 | const jx9_expr_op *pOp = (const jx9_expr_op *)pLast->pUserData; | ||
145 | /* Ticket 1433-31: Handle the '++', '--' operators case */ | ||
146 | if( pOp->iOp != EXPR_OP_INCR && pOp->iOp != EXPR_OP_DECR ){ | ||
147 | /* Unary opertors have prcedence here over binary operators */ | ||
148 | return &aOpTable[n]; | ||
149 | } | ||
150 | |||
151 | } | ||
152 | } | ||
153 | ++n; /* Next operator in the table */ | ||
154 | } | ||
155 | /* No such operator */ | ||
156 | return 0; | ||
157 | } | ||
158 | /* | ||
159 | * Delimit a set of token stream. | ||
160 | * This function take care of handling the nesting level and stops when it hit | ||
161 | * the end of the input or the ending token is found and the nesting level is zero. | ||
162 | */ | ||
163 | JX9_PRIVATE void jx9DelimitNestedTokens(SyToken *pIn,SyToken *pEnd,sxu32 nTokStart,sxu32 nTokEnd,SyToken **ppEnd) | ||
164 | { | ||
165 | SyToken *pCur = pIn; | ||
166 | sxi32 iNest = 1; | ||
167 | for(;;){ | ||
168 | if( pCur >= pEnd ){ | ||
169 | break; | ||
170 | } | ||
171 | if( pCur->nType & nTokStart ){ | ||
172 | /* Increment nesting level */ | ||
173 | iNest++; | ||
174 | }else if( pCur->nType & nTokEnd ){ | ||
175 | /* Decrement nesting level */ | ||
176 | iNest--; | ||
177 | if( iNest <= 0 ){ | ||
178 | break; | ||
179 | } | ||
180 | } | ||
181 | /* Advance cursor */ | ||
182 | pCur++; | ||
183 | } | ||
184 | /* Point to the end of the chunk */ | ||
185 | *ppEnd = pCur; | ||
186 | } | ||
187 | /* | ||
188 | * Retrun TRUE if the given ID represent a language construct [i.e: print, print..]. FALSE otherwise. | ||
189 | * Note on reserved keywords. | ||
190 | * According to the JX9 language reference manual: | ||
191 | * These words have special meaning in JX9. Some of them represent things which look like | ||
192 | * functions, some look like constants, and so on--but they're not, really: they are language | ||
193 | * constructs. You cannot use any of the following words as constants, object names, function | ||
194 | * or method names. Using them as variable names is generally OK, but could lead to confusion. | ||
195 | */ | ||
196 | JX9_PRIVATE int jx9IsLangConstruct(sxu32 nKeyID) | ||
197 | { | ||
198 | if( nKeyID == JX9_TKWRD_PRINT || nKeyID == JX9_TKWRD_EXIT || nKeyID == JX9_TKWRD_DIE | ||
199 | || nKeyID == JX9_TKWRD_INCLUDE|| nKeyID == JX9_TKWRD_IMPORT ){ | ||
200 | return TRUE; | ||
201 | } | ||
202 | /* Not a language construct */ | ||
203 | return FALSE; | ||
204 | } | ||
205 | /* | ||
206 | * Point to the next expression that should be evaluated shortly. | ||
207 | * The cursor stops when it hit a comma ', ' or a semi-colon and the nesting | ||
208 | * level is zero. | ||
209 | */ | ||
210 | JX9_PRIVATE sxi32 jx9GetNextExpr(SyToken *pStart,SyToken *pEnd,SyToken **ppNext) | ||
211 | { | ||
212 | SyToken *pCur = pStart; | ||
213 | sxi32 iNest = 0; | ||
214 | if( pCur >= pEnd || (pCur->nType & JX9_TK_SEMI/*';'*/) ){ | ||
215 | /* Last expression */ | ||
216 | return SXERR_EOF; | ||
217 | } | ||
218 | while( pCur < pEnd ){ | ||
219 | if( (pCur->nType & (JX9_TK_COMMA/*','*/|JX9_TK_SEMI/*';'*/)) && iNest <= 0){ | ||
220 | break; | ||
221 | } | ||
222 | if( pCur->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OSB/*'['*/|JX9_TK_OCB/*'{'*/) ){ | ||
223 | iNest++; | ||
224 | }else if( pCur->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*']'*/|JX9_TK_CCB/*'}*/) ){ | ||
225 | iNest--; | ||
226 | } | ||
227 | pCur++; | ||
228 | } | ||
229 | *ppNext = pCur; | ||
230 | return SXRET_OK; | ||
231 | } | ||
232 | /* | ||
233 | * Collect and assemble tokens holding annonymous functions/closure body. | ||
234 | * When errors, JX9 take care of generating the appropriate error message. | ||
235 | * Note on annonymous functions. | ||
236 | * According to the JX9 language reference manual: | ||
237 | * Anonymous functions, also known as closures, allow the creation of functions | ||
238 | * which have no specified name. They are most useful as the value of callback | ||
239 | * parameters, but they have many other uses. | ||
240 | * Closures may also inherit variables from the parent scope. Any such variables | ||
241 | * must be declared in the function header. Inheriting variables from the parent | ||
242 | * scope is not the same as using global variables. Global variables exist in the global scope | ||
243 | * which is the same no matter what function is executing. The parent scope of a closure is the | ||
244 | * function in which the closure was declared (not necessarily the function it was called from). | ||
245 | * | ||
246 | * Some example: | ||
247 | * $greet = function($name) | ||
248 | * { | ||
249 | * printf("Hello %s\r\n", $name); | ||
250 | * }; | ||
251 | * $greet('World'); | ||
252 | * $greet('JX9'); | ||
253 | * | ||
254 | * $double = function($a) { | ||
255 | * return $a * 2; | ||
256 | * }; | ||
257 | * // This is our range of numbers | ||
258 | * $numbers = range(1, 5); | ||
259 | * // Use the Annonymous function as a callback here to | ||
260 | * // double the size of each element in our | ||
261 | * // range | ||
262 | * $new_numbers = array_map($double, $numbers); | ||
263 | * print implode(' ', $new_numbers); | ||
264 | */ | ||
265 | static sxi32 ExprAssembleAnnon(jx9_gen_state *pGen,SyToken **ppCur, SyToken *pEnd) | ||
266 | { | ||
267 | SyToken *pIn = *ppCur; | ||
268 | sxu32 nLine; | ||
269 | sxi32 rc; | ||
270 | /* Jump the 'function' keyword */ | ||
271 | nLine = pIn->nLine; | ||
272 | pIn++; | ||
273 | if( pIn < pEnd && (pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) ){ | ||
274 | pIn++; | ||
275 | } | ||
276 | if( pIn >= pEnd || (pIn->nType & JX9_TK_LPAREN) == 0 ){ | ||
277 | /* Syntax error */ | ||
278 | rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing opening parenthesis '(' while declaring annonymous function"); | ||
279 | if( rc != SXERR_ABORT ){ | ||
280 | rc = SXERR_SYNTAX; | ||
281 | } | ||
282 | goto Synchronize; | ||
283 | } | ||
284 | pIn++; /* Jump the leading parenthesis '(' */ | ||
285 | jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_LPAREN/*'('*/, JX9_TK_RPAREN/*')'*/, &pIn); | ||
286 | if( pIn >= pEnd || &pIn[1] >= pEnd ){ | ||
287 | /* Syntax error */ | ||
288 | rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function"); | ||
289 | if( rc != SXERR_ABORT ){ | ||
290 | rc = SXERR_SYNTAX; | ||
291 | } | ||
292 | goto Synchronize; | ||
293 | } | ||
294 | pIn++; /* Jump the trailing parenthesis */ | ||
295 | if( pIn->nType & JX9_TK_OCB /*'{'*/ ){ | ||
296 | pIn++; /* Jump the leading curly '{' */ | ||
297 | jx9DelimitNestedTokens(pIn, pEnd, JX9_TK_OCB/*'{'*/, JX9_TK_CCB/*'}'*/, &pIn); | ||
298 | if( pIn < pEnd ){ | ||
299 | pIn++; | ||
300 | } | ||
301 | }else{ | ||
302 | /* Syntax error */ | ||
303 | rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Syntax error while declaring annonymous function, missing '{'"); | ||
304 | if( rc == SXERR_ABORT ){ | ||
305 | return SXERR_ABORT; | ||
306 | } | ||
307 | } | ||
308 | rc = SXRET_OK; | ||
309 | Synchronize: | ||
310 | /* Synchronize pointers */ | ||
311 | *ppCur = pIn; | ||
312 | return rc; | ||
313 | } | ||
314 | /* | ||
315 | * Make sure we are dealing with a valid expression tree. | ||
316 | * This function check for balanced parenthesis, braces, brackets and so on. | ||
317 | * When errors, JX9 take care of generating the appropriate error message. | ||
318 | * Return SXRET_OK on success. Any other return value indicates syntax error. | ||
319 | */ | ||
320 | static sxi32 ExprVerifyNodes(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nNode) | ||
321 | { | ||
322 | sxi32 iParen, iSquare, iBraces; | ||
323 | sxi32 i, rc; | ||
324 | |||
325 | if( nNode > 0 && apNode[0]->pOp && (apNode[0]->pOp->iOp == EXPR_OP_ADD || apNode[0]->pOp->iOp == EXPR_OP_SUB) ){ | ||
326 | /* Fix and mark as an unary not binary plus/minus operator */ | ||
327 | apNode[0]->pOp = jx9ExprExtractOperator(&apNode[0]->pStart->sData, 0); | ||
328 | apNode[0]->pStart->pUserData = (void *)apNode[0]->pOp; | ||
329 | } | ||
330 | iParen = iSquare = iBraces = 0; | ||
331 | for( i = 0 ; i < nNode ; ++i ){ | ||
332 | if( apNode[i]->pStart->nType & JX9_TK_LPAREN /*'('*/){ | ||
333 | if( i > 0 && ( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral || | ||
334 | (apNode[i - 1]->pStart->nType & (JX9_TK_ID|JX9_TK_KEYWORD|JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_RPAREN/*')'*/|JX9_TK_CSB/*]*/))) ){ | ||
335 | /* Ticket 1433-033: Take care to ignore alpha-stream [i.e: or, xor] operators followed by an opening parenthesis */ | ||
336 | if( (apNode[i - 1]->pStart->nType & JX9_TK_OP) == 0 ){ | ||
337 | /* We are dealing with a postfix [i.e: function call] operator | ||
338 | * not a simple left parenthesis. Mark the node. | ||
339 | */ | ||
340 | apNode[i]->pStart->nType |= JX9_TK_OP; | ||
341 | apNode[i]->pStart->pUserData = (void *)&sFCallOp; /* Function call operator */ | ||
342 | apNode[i]->pOp = &sFCallOp; | ||
343 | } | ||
344 | } | ||
345 | iParen++; | ||
346 | }else if( apNode[i]->pStart->nType & JX9_TK_RPAREN/*')*/){ | ||
347 | if( iParen <= 0 ){ | ||
348 | rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ')'"); | ||
349 | if( rc != SXERR_ABORT ){ | ||
350 | rc = SXERR_SYNTAX; | ||
351 | } | ||
352 | return rc; | ||
353 | } | ||
354 | iParen--; | ||
355 | }else if( apNode[i]->pStart->nType & JX9_TK_OSB /*'['*/ && apNode[i]->xCode == 0 ){ | ||
356 | iSquare++; | ||
357 | }else if (apNode[i]->pStart->nType & JX9_TK_CSB /*']'*/){ | ||
358 | if( iSquare <= 0 ){ | ||
359 | rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token ']'"); | ||
360 | if( rc != SXERR_ABORT ){ | ||
361 | rc = SXERR_SYNTAX; | ||
362 | } | ||
363 | return rc; | ||
364 | } | ||
365 | iSquare--; | ||
366 | }else if( apNode[i]->pStart->nType & JX9_TK_OCB /*'{'*/ && apNode[i]->xCode == 0 ){ | ||
367 | iBraces++; | ||
368 | }else if (apNode[i]->pStart->nType & JX9_TK_CCB /*'}'*/){ | ||
369 | if( iBraces <= 0 ){ | ||
370 | rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[i]->pStart->nLine, "Syntax error: Unexpected token '}'"); | ||
371 | if( rc != SXERR_ABORT ){ | ||
372 | rc = SXERR_SYNTAX; | ||
373 | } | ||
374 | return rc; | ||
375 | } | ||
376 | iBraces--; | ||
377 | }else if( apNode[i]->pStart->nType & JX9_TK_OP ){ | ||
378 | const jx9_expr_op *pOp = (const jx9_expr_op *)apNode[i]->pOp; | ||
379 | if( i > 0 && (pOp->iOp == EXPR_OP_UMINUS || pOp->iOp == EXPR_OP_UPLUS)){ | ||
380 | if( apNode[i-1]->xCode == jx9CompileVariable || apNode[i-1]->xCode == jx9CompileLiteral ){ | ||
381 | sxi32 iExprOp = EXPR_OP_SUB; /* Binary minus */ | ||
382 | sxu32 n = 0; | ||
383 | if( pOp->iOp == EXPR_OP_UPLUS ){ | ||
384 | iExprOp = EXPR_OP_ADD; /* Binary plus */ | ||
385 | } | ||
386 | /* | ||
387 | * TICKET 1433-013: This is a fix around an obscure bug when the user uses | ||
388 | * a variable name which is an alpha-stream operator [i.e: $and, $xor, $eq..]. | ||
389 | */ | ||
390 | while( n < SX_ARRAYSIZE(aOpTable) && aOpTable[n].iOp != iExprOp ){ | ||
391 | ++n; | ||
392 | } | ||
393 | pOp = &aOpTable[n]; | ||
394 | /* Mark as binary '+' or '-', not an unary */ | ||
395 | apNode[i]->pOp = pOp; | ||
396 | apNode[i]->pStart->pUserData = (void *)pOp; | ||
397 | } | ||
398 | } | ||
399 | } | ||
400 | } | ||
401 | if( iParen != 0 || iSquare != 0 || iBraces != 0){ | ||
402 | rc = jx9GenCompileError(&(*pGen), E_ERROR, apNode[0]->pStart->nLine, "Syntax error, mismatched '(', '[' or '{'"); | ||
403 | if( rc != SXERR_ABORT ){ | ||
404 | rc = SXERR_SYNTAX; | ||
405 | } | ||
406 | return rc; | ||
407 | } | ||
408 | return SXRET_OK; | ||
409 | } | ||
410 | /* | ||
411 | * Extract a single expression node from the input. | ||
412 | * On success store the freshly extractd node in ppNode. | ||
413 | * When errors, JX9 take care of generating the appropriate error message. | ||
414 | * An expression node can be a variable [i.e: $var], an operator [i.e: ++] | ||
415 | * an annonymous function [i.e: function(){ return "Hello"; }, a double/single | ||
416 | * quoted string, a heredoc/nowdoc, a literal [i.e: JX9_EOL], a namespace path | ||
417 | * [i.e: namespaces\path\to..], a array/list [i.e: array(4, 5, 6)] and so on. | ||
418 | */ | ||
419 | static sxi32 ExprExtractNode(jx9_gen_state *pGen, jx9_expr_node **ppNode) | ||
420 | { | ||
421 | jx9_expr_node *pNode; | ||
422 | SyToken *pCur; | ||
423 | sxi32 rc; | ||
424 | /* Allocate a new node */ | ||
425 | pNode = (jx9_expr_node *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_expr_node)); | ||
426 | if( pNode == 0 ){ | ||
427 | /* If the supplied memory subsystem is so sick that we are unable to allocate | ||
428 | * a tiny chunk of memory, there is no much we can do here. | ||
429 | */ | ||
430 | return SXERR_MEM; | ||
431 | } | ||
432 | /* Zero the structure */ | ||
433 | SyZero(pNode, sizeof(jx9_expr_node)); | ||
434 | SySetInit(&pNode->aNodeArgs, &pGen->pVm->sAllocator, sizeof(jx9_expr_node **)); | ||
435 | /* Point to the head of the token stream */ | ||
436 | pCur = pNode->pStart = pGen->pIn; | ||
437 | /* Start collecting tokens */ | ||
438 | if( pCur->nType & JX9_TK_OP ){ | ||
439 | /* Point to the instance that describe this operator */ | ||
440 | pNode->pOp = (const jx9_expr_op *)pCur->pUserData; | ||
441 | /* Advance the stream cursor */ | ||
442 | pCur++; | ||
443 | }else if( pCur->nType & JX9_TK_DOLLAR ){ | ||
444 | /* Isolate variable */ | ||
445 | pCur++; /* Jump the dollar sign */ | ||
446 | if( pCur >= pGen->pEnd ){ | ||
447 | /* Syntax error */ | ||
448 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"Invalid variable name"); | ||
449 | if( rc != SXERR_ABORT ){ | ||
450 | rc = SXERR_SYNTAX; | ||
451 | } | ||
452 | SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); | ||
453 | return rc; | ||
454 | } | ||
455 | pCur++; /* Jump the variable name */ | ||
456 | pNode->xCode = jx9CompileVariable; | ||
457 | }else if( pCur->nType & JX9_TK_OCB /* '{' */ ){ | ||
458 | /* JSON Object, assemble tokens */ | ||
459 | pCur++; | ||
460 | jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OCB /* '[' */, JX9_TK_CCB /* ']' */, &pCur); | ||
461 | if( pCur < pGen->pEnd ){ | ||
462 | pCur++; | ||
463 | }else{ | ||
464 | /* Syntax error */ | ||
465 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Object: Missing closing braces '}'"); | ||
466 | if( rc != SXERR_ABORT ){ | ||
467 | rc = SXERR_SYNTAX; | ||
468 | } | ||
469 | SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); | ||
470 | return rc; | ||
471 | } | ||
472 | pNode->xCode = jx9CompileJsonObject; | ||
473 | }else if( pCur->nType & JX9_TK_OSB /* '[' */ && !(pCur->nType & JX9_TK_OP) ){ | ||
474 | /* JSON Array, assemble tokens */ | ||
475 | pCur++; | ||
476 | jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_OSB /* '[' */, JX9_TK_CSB /* ']' */, &pCur); | ||
477 | if( pCur < pGen->pEnd ){ | ||
478 | pCur++; | ||
479 | }else{ | ||
480 | /* Syntax error */ | ||
481 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine,"JSON Array: Missing closing square bracket ']'"); | ||
482 | if( rc != SXERR_ABORT ){ | ||
483 | rc = SXERR_SYNTAX; | ||
484 | } | ||
485 | SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); | ||
486 | return rc; | ||
487 | } | ||
488 | pNode->xCode = jx9CompileJsonArray; | ||
489 | }else if( pCur->nType & JX9_TK_KEYWORD ){ | ||
490 | int nKeyword = SX_PTR_TO_INT(pCur->pUserData); | ||
491 | if( nKeyword == JX9_TKWRD_FUNCTION ){ | ||
492 | /* Annonymous function */ | ||
493 | if( &pCur[1] >= pGen->pEnd ){ | ||
494 | /* Assume a literal */ | ||
495 | pCur++; | ||
496 | pNode->xCode = jx9CompileLiteral; | ||
497 | }else{ | ||
498 | /* Assemble annonymous functions body */ | ||
499 | rc = ExprAssembleAnnon(&(*pGen), &pCur, pGen->pEnd); | ||
500 | if( rc != SXRET_OK ){ | ||
501 | SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); | ||
502 | return rc; | ||
503 | } | ||
504 | pNode->xCode = jx9CompileAnnonFunc; | ||
505 | } | ||
506 | }else if( jx9IsLangConstruct(nKeyword) && &pCur[1] < pGen->pEnd ){ | ||
507 | /* Language constructs [i.e: print,die...] require special handling */ | ||
508 | jx9DelimitNestedTokens(pCur, pGen->pEnd, JX9_TK_LPAREN|JX9_TK_OCB|JX9_TK_OSB, JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB, &pCur); | ||
509 | pNode->xCode = jx9CompileLangConstruct; | ||
510 | }else{ | ||
511 | /* Assume a literal */ | ||
512 | pCur++; | ||
513 | pNode->xCode = jx9CompileLiteral; | ||
514 | } | ||
515 | }else if( pCur->nType & (JX9_TK_ID) ){ | ||
516 | /* Constants, function name, namespace path, object name... */ | ||
517 | pCur++; | ||
518 | pNode->xCode = jx9CompileLiteral; | ||
519 | }else{ | ||
520 | if( (pCur->nType & (JX9_TK_LPAREN|JX9_TK_RPAREN|JX9_TK_COMMA|JX9_TK_CSB|JX9_TK_OCB|JX9_TK_CCB|JX9_TK_COLON)) == 0 ){ | ||
521 | /* Point to the code generator routine */ | ||
522 | pNode->xCode = jx9GetNodeHandler(pCur->nType); | ||
523 | if( pNode->xCode == 0 ){ | ||
524 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Syntax error: Unexpected token '%z'", &pNode->pStart->sData); | ||
525 | if( rc != SXERR_ABORT ){ | ||
526 | rc = SXERR_SYNTAX; | ||
527 | } | ||
528 | SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); | ||
529 | return rc; | ||
530 | } | ||
531 | } | ||
532 | /* Advance the stream cursor */ | ||
533 | pCur++; | ||
534 | } | ||
535 | /* Point to the end of the token stream */ | ||
536 | pNode->pEnd = pCur; | ||
537 | /* Save the node for later processing */ | ||
538 | *ppNode = pNode; | ||
539 | /* Synchronize cursors */ | ||
540 | pGen->pIn = pCur; | ||
541 | return SXRET_OK; | ||
542 | } | ||
543 | /* | ||
544 | * Free an expression tree. | ||
545 | */ | ||
546 | static void ExprFreeTree(jx9_gen_state *pGen, jx9_expr_node *pNode) | ||
547 | { | ||
548 | if( pNode->pLeft ){ | ||
549 | /* Release the left tree */ | ||
550 | ExprFreeTree(&(*pGen), pNode->pLeft); | ||
551 | } | ||
552 | if( pNode->pRight ){ | ||
553 | /* Release the right tree */ | ||
554 | ExprFreeTree(&(*pGen), pNode->pRight); | ||
555 | } | ||
556 | if( pNode->pCond ){ | ||
557 | /* Release the conditional tree used by the ternary operator */ | ||
558 | ExprFreeTree(&(*pGen), pNode->pCond); | ||
559 | } | ||
560 | if( SySetUsed(&pNode->aNodeArgs) > 0 ){ | ||
561 | jx9_expr_node **apArg; | ||
562 | sxu32 n; | ||
563 | /* Release node arguments */ | ||
564 | apArg = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs); | ||
565 | for( n = 0 ; n < SySetUsed(&pNode->aNodeArgs) ; ++n ){ | ||
566 | ExprFreeTree(&(*pGen), apArg[n]); | ||
567 | } | ||
568 | SySetRelease(&pNode->aNodeArgs); | ||
569 | } | ||
570 | /* Finally, release this node */ | ||
571 | SyMemBackendPoolFree(&pGen->pVm->sAllocator, pNode); | ||
572 | } | ||
573 | /* | ||
574 | * Free an expression tree. | ||
575 | * This function is a wrapper around ExprFreeTree() defined above. | ||
576 | */ | ||
577 | JX9_PRIVATE sxi32 jx9ExprFreeTree(jx9_gen_state *pGen, SySet *pNodeSet) | ||
578 | { | ||
579 | jx9_expr_node **apNode; | ||
580 | sxu32 n; | ||
581 | apNode = (jx9_expr_node **)SySetBasePtr(pNodeSet); | ||
582 | for( n = 0 ; n < SySetUsed(pNodeSet) ; ++n ){ | ||
583 | if( apNode[n] ){ | ||
584 | ExprFreeTree(&(*pGen), apNode[n]); | ||
585 | } | ||
586 | } | ||
587 | return SXRET_OK; | ||
588 | } | ||
589 | /* | ||
590 | * Check if the given node is a modifialbe l/r-value. | ||
591 | * Return TRUE if modifiable.FALSE otherwise. | ||
592 | */ | ||
593 | static int ExprIsModifiableValue(jx9_expr_node *pNode) | ||
594 | { | ||
595 | sxi32 iExprOp; | ||
596 | if( pNode->pOp == 0 ){ | ||
597 | return pNode->xCode == jx9CompileVariable ? TRUE : FALSE; | ||
598 | } | ||
599 | iExprOp = pNode->pOp->iOp; | ||
600 | if( iExprOp == EXPR_OP_DOT /*'.' */ ){ | ||
601 | return TRUE; | ||
602 | } | ||
603 | if( iExprOp == EXPR_OP_SUBSCRIPT/*'[]'*/ ){ | ||
604 | if( pNode->pLeft->pOp ) { | ||
605 | if( pNode->pLeft->pOp->iOp != EXPR_OP_SUBSCRIPT /*'['*/ && pNode->pLeft->pOp->iOp != EXPR_OP_DOT /*'.'*/){ | ||
606 | return FALSE; | ||
607 | } | ||
608 | }else if( pNode->pLeft->xCode != jx9CompileVariable ){ | ||
609 | return FALSE; | ||
610 | } | ||
611 | return TRUE; | ||
612 | } | ||
613 | /* Not a modifiable l or r-value */ | ||
614 | return FALSE; | ||
615 | } | ||
616 | /* Forward declaration */ | ||
617 | static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken); | ||
618 | /* Macro to check if the given node is a terminal */ | ||
619 | #define NODE_ISTERM(NODE) (apNode[NODE] && (!apNode[NODE]->pOp || apNode[NODE]->pLeft )) | ||
620 | /* | ||
621 | * Buid an expression tree for each given function argument. | ||
622 | * When errors, JX9 take care of generating the appropriate error message. | ||
623 | */ | ||
624 | static sxi32 ExprProcessFuncArguments(jx9_gen_state *pGen, jx9_expr_node *pOp, jx9_expr_node **apNode, sxi32 nToken) | ||
625 | { | ||
626 | sxi32 iNest, iCur, iNode; | ||
627 | sxi32 rc; | ||
628 | /* Process function arguments from left to right */ | ||
629 | iCur = 0; | ||
630 | for(;;){ | ||
631 | if( iCur >= nToken ){ | ||
632 | /* No more arguments to process */ | ||
633 | break; | ||
634 | } | ||
635 | iNode = iCur; | ||
636 | iNest = 0; | ||
637 | while( iCur < nToken ){ | ||
638 | if( apNode[iCur] ){ | ||
639 | if( (apNode[iCur]->pStart->nType & JX9_TK_COMMA) && apNode[iCur]->pLeft == 0 && iNest <= 0 ){ | ||
640 | break; | ||
641 | }else if( apNode[iCur]->pStart->nType & (JX9_TK_LPAREN|JX9_TK_OSB|JX9_TK_OCB) ){ | ||
642 | iNest++; | ||
643 | }else if( apNode[iCur]->pStart->nType & (JX9_TK_RPAREN|JX9_TK_CCB|JX9_TK_CSB) ){ | ||
644 | iNest--; | ||
645 | } | ||
646 | } | ||
647 | iCur++; | ||
648 | } | ||
649 | if( iCur > iNode ){ | ||
650 | ExprMakeTree(&(*pGen), &apNode[iNode], iCur-iNode); | ||
651 | if( apNode[iNode] ){ | ||
652 | /* Put a pointer to the root of the tree in the arguments set */ | ||
653 | SySetPut(&pOp->aNodeArgs, (const void *)&apNode[iNode]); | ||
654 | }else{ | ||
655 | /* Empty function argument */ | ||
656 | rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Empty function argument"); | ||
657 | if( rc != SXERR_ABORT ){ | ||
658 | rc = SXERR_SYNTAX; | ||
659 | } | ||
660 | return rc; | ||
661 | } | ||
662 | }else{ | ||
663 | rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument"); | ||
664 | if( rc != SXERR_ABORT ){ | ||
665 | rc = SXERR_SYNTAX; | ||
666 | } | ||
667 | return rc; | ||
668 | } | ||
669 | /* Jump trailing comma */ | ||
670 | if( iCur < nToken && apNode[iCur] && (apNode[iCur]->pStart->nType & JX9_TK_COMMA) ){ | ||
671 | iCur++; | ||
672 | if( iCur >= nToken ){ | ||
673 | /* missing function argument */ | ||
674 | rc = jx9GenCompileError(&(*pGen), E_ERROR, pOp->pStart->nLine, "Missing function argument"); | ||
675 | if( rc != SXERR_ABORT ){ | ||
676 | rc = SXERR_SYNTAX; | ||
677 | } | ||
678 | return rc; | ||
679 | } | ||
680 | } | ||
681 | } | ||
682 | return SXRET_OK; | ||
683 | } | ||
684 | /* | ||
685 | * Create an expression tree from an array of tokens. | ||
686 | * If successful, the root of the tree is stored in apNode[0]. | ||
687 | * When errors, JX9 take care of generating the appropriate error message. | ||
688 | */ | ||
689 | static sxi32 ExprMakeTree(jx9_gen_state *pGen, jx9_expr_node **apNode, sxi32 nToken) | ||
690 | { | ||
691 | sxi32 i, iLeft, iRight; | ||
692 | jx9_expr_node *pNode; | ||
693 | sxi32 iCur; | ||
694 | sxi32 rc; | ||
695 | if( nToken <= 0 || (nToken == 1 && apNode[0]->xCode) ){ | ||
696 | /* TICKET 1433-17: self evaluating node */ | ||
697 | return SXRET_OK; | ||
698 | } | ||
699 | /* Process expressions enclosed in parenthesis first */ | ||
700 | for( iCur = 0 ; iCur < nToken ; ++iCur ){ | ||
701 | sxi32 iNest; | ||
702 | /* Note that, we use strict comparison here '!=' instead of the bitwise and '&' operator | ||
703 | * since the LPAREN token can also be an operator [i.e: Function call]. | ||
704 | */ | ||
705 | if( apNode[iCur] == 0 || apNode[iCur]->pStart->nType != JX9_TK_LPAREN ){ | ||
706 | continue; | ||
707 | } | ||
708 | iNest = 1; | ||
709 | iLeft = iCur; | ||
710 | /* Find the closing parenthesis */ | ||
711 | iCur++; | ||
712 | while( iCur < nToken ){ | ||
713 | if( apNode[iCur] ){ | ||
714 | if( apNode[iCur]->pStart->nType & JX9_TK_RPAREN /* ')' */){ | ||
715 | /* Decrement nesting level */ | ||
716 | iNest--; | ||
717 | if( iNest <= 0 ){ | ||
718 | break; | ||
719 | } | ||
720 | }else if( apNode[iCur]->pStart->nType & JX9_TK_LPAREN /* '(' */ ){ | ||
721 | /* Increment nesting level */ | ||
722 | iNest++; | ||
723 | } | ||
724 | } | ||
725 | iCur++; | ||
726 | } | ||
727 | if( iCur - iLeft > 1 ){ | ||
728 | /* Recurse and process this expression */ | ||
729 | rc = ExprMakeTree(&(*pGen), &apNode[iLeft + 1], iCur - iLeft - 1); | ||
730 | if( rc != SXRET_OK ){ | ||
731 | return rc; | ||
732 | } | ||
733 | } | ||
734 | /* Free the left and right nodes */ | ||
735 | ExprFreeTree(&(*pGen), apNode[iLeft]); | ||
736 | ExprFreeTree(&(*pGen), apNode[iCur]); | ||
737 | apNode[iLeft] = 0; | ||
738 | apNode[iCur] = 0; | ||
739 | } | ||
740 | /* Handle postfix [i.e: function call, member access] operators with precedence 2 */ | ||
741 | iLeft = -1; | ||
742 | for( iCur = 0 ; iCur < nToken ; ++iCur ){ | ||
743 | if( apNode[iCur] == 0 ){ | ||
744 | continue; | ||
745 | } | ||
746 | pNode = apNode[iCur]; | ||
747 | if( pNode->pOp && pNode->pOp->iPrec == 2 && pNode->pLeft == 0 ){ | ||
748 | if( pNode->pOp->iOp == EXPR_OP_FUNC_CALL ){ | ||
749 | /* Collect function arguments */ | ||
750 | sxi32 iPtr = 0; | ||
751 | sxi32 nFuncTok = 0; | ||
752 | while( nFuncTok + iCur < nToken ){ | ||
753 | if( apNode[nFuncTok+iCur] ){ | ||
754 | if( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_LPAREN /*'('*/ ){ | ||
755 | iPtr++; | ||
756 | }else if ( apNode[nFuncTok+iCur]->pStart->nType & JX9_TK_RPAREN /*')'*/){ | ||
757 | iPtr--; | ||
758 | if( iPtr <= 0 ){ | ||
759 | break; | ||
760 | } | ||
761 | } | ||
762 | } | ||
763 | nFuncTok++; | ||
764 | } | ||
765 | if( nFuncTok + iCur >= nToken ){ | ||
766 | /* Syntax error */ | ||
767 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Missing right parenthesis ')'"); | ||
768 | if( rc != SXERR_ABORT ){ | ||
769 | rc = SXERR_SYNTAX; | ||
770 | } | ||
771 | return rc; | ||
772 | } | ||
773 | if( iLeft < 0 || !NODE_ISTERM(iLeft) /*|| ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2)*/ ){ | ||
774 | /* Syntax error */ | ||
775 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "Invalid function name"); | ||
776 | if( rc != SXERR_ABORT ){ | ||
777 | rc = SXERR_SYNTAX; | ||
778 | } | ||
779 | return rc; | ||
780 | } | ||
781 | if( nFuncTok > 1 ){ | ||
782 | /* Process function arguments */ | ||
783 | rc = ExprProcessFuncArguments(&(*pGen), pNode, &apNode[iCur+1], nFuncTok-1); | ||
784 | if( rc != SXRET_OK ){ | ||
785 | return rc; | ||
786 | } | ||
787 | } | ||
788 | /* Link the node to the tree */ | ||
789 | pNode->pLeft = apNode[iLeft]; | ||
790 | apNode[iLeft] = 0; | ||
791 | for( iPtr = 1; iPtr <= nFuncTok ; iPtr++ ){ | ||
792 | apNode[iCur+iPtr] = 0; | ||
793 | } | ||
794 | }else if (pNode->pOp->iOp == EXPR_OP_SUBSCRIPT ){ | ||
795 | /* Subscripting */ | ||
796 | sxi32 iArrTok = iCur + 1; | ||
797 | sxi32 iNest = 1; | ||
798 | if( iLeft >= 0 && (apNode[iLeft]->xCode == jx9CompileVariable || (apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* postfix */) ) ){ | ||
799 | /* Collect index tokens */ | ||
800 | while( iArrTok < nToken ){ | ||
801 | if( apNode[iArrTok] ){ | ||
802 | if( apNode[iArrTok]->pStart->nType & JX9_TK_OSB /*'['*/){ | ||
803 | /* Increment nesting level */ | ||
804 | iNest++; | ||
805 | }else if( apNode[iArrTok]->pStart->nType & JX9_TK_CSB /*']'*/){ | ||
806 | /* Decrement nesting level */ | ||
807 | iNest--; | ||
808 | if( iNest <= 0 ){ | ||
809 | break; | ||
810 | } | ||
811 | } | ||
812 | } | ||
813 | ++iArrTok; | ||
814 | } | ||
815 | if( iArrTok > iCur + 1 ){ | ||
816 | /* Recurse and process this expression */ | ||
817 | rc = ExprMakeTree(&(*pGen), &apNode[iCur+1], iArrTok - iCur - 1); | ||
818 | if( rc != SXRET_OK ){ | ||
819 | return rc; | ||
820 | } | ||
821 | /* Link the node to it's index */ | ||
822 | SySetPut(&pNode->aNodeArgs, (const void *)&apNode[iCur+1]); | ||
823 | } | ||
824 | /* Link the node to the tree */ | ||
825 | pNode->pLeft = apNode[iLeft]; | ||
826 | pNode->pRight = 0; | ||
827 | apNode[iLeft] = 0; | ||
828 | for( iNest = iCur + 1 ; iNest <= iArrTok ; ++iNest ){ | ||
829 | apNode[iNest] = 0; | ||
830 | } | ||
831 | } | ||
832 | }else{ | ||
833 | /* Member access operators [i.e: '.' ] */ | ||
834 | iRight = iCur + 1; | ||
835 | while( iRight < nToken && apNode[iRight] == 0 ){ | ||
836 | iRight++; | ||
837 | } | ||
838 | if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ | ||
839 | /* Syntax error */ | ||
840 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid member name", &pNode->pOp->sOp); | ||
841 | if( rc != SXERR_ABORT ){ | ||
842 | rc = SXERR_SYNTAX; | ||
843 | } | ||
844 | return rc; | ||
845 | } | ||
846 | /* Link the node to the tree */ | ||
847 | pNode->pLeft = apNode[iLeft]; | ||
848 | if( pNode->pLeft->pOp == 0 && pNode->pLeft->xCode != jx9CompileVariable ){ | ||
849 | /* Syntax error */ | ||
850 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, | ||
851 | "'%z': Expecting a variable as left operand", &pNode->pOp->sOp); | ||
852 | if( rc != SXERR_ABORT ){ | ||
853 | rc = SXERR_SYNTAX; | ||
854 | } | ||
855 | return rc; | ||
856 | } | ||
857 | pNode->pRight = apNode[iRight]; | ||
858 | apNode[iLeft] = apNode[iRight] = 0; | ||
859 | } | ||
860 | } | ||
861 | iLeft = iCur; | ||
862 | } | ||
863 | /* Handle post/pre icrement/decrement [i.e: ++/--] operators with precedence 3 */ | ||
864 | iLeft = -1; | ||
865 | for( iCur = 0 ; iCur < nToken ; ++iCur ){ | ||
866 | if( apNode[iCur] == 0 ){ | ||
867 | continue; | ||
868 | } | ||
869 | pNode = apNode[iCur]; | ||
870 | if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){ | ||
871 | if( iLeft >= 0 && ((apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec == 2 /* Postfix */) | ||
872 | || apNode[iLeft]->xCode == jx9CompileVariable) ){ | ||
873 | /* Link the node to the tree */ | ||
874 | pNode->pLeft = apNode[iLeft]; | ||
875 | apNode[iLeft] = 0; | ||
876 | } | ||
877 | } | ||
878 | iLeft = iCur; | ||
879 | } | ||
880 | iLeft = -1; | ||
881 | for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){ | ||
882 | if( apNode[iCur] == 0 ){ | ||
883 | continue; | ||
884 | } | ||
885 | pNode = apNode[iCur]; | ||
886 | if( pNode->pOp && pNode->pOp->iPrec == 3 && pNode->pLeft == 0){ | ||
887 | if( iLeft < 0 || (apNode[iLeft]->pOp == 0 && apNode[iLeft]->xCode != jx9CompileVariable) | ||
888 | || ( apNode[iLeft]->pOp && apNode[iLeft]->pOp->iPrec != 2 /* Postfix */) ){ | ||
889 | /* Syntax error */ | ||
890 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z' operator needs l-value", &pNode->pOp->sOp); | ||
891 | if( rc != SXERR_ABORT ){ | ||
892 | rc = SXERR_SYNTAX; | ||
893 | } | ||
894 | return rc; | ||
895 | } | ||
896 | /* Link the node to the tree */ | ||
897 | pNode->pLeft = apNode[iLeft]; | ||
898 | apNode[iLeft] = 0; | ||
899 | /* Mark as pre-increment/decrement node */ | ||
900 | pNode->iFlags |= EXPR_NODE_PRE_INCR; | ||
901 | } | ||
902 | iLeft = iCur; | ||
903 | } | ||
904 | /* Handle right associative unary and cast operators [i.e: !, (string), ~...] with precedence 4 */ | ||
905 | iLeft = 0; | ||
906 | for( iCur = nToken - 1 ; iCur >= 0 ; iCur-- ){ | ||
907 | if( apNode[iCur] ){ | ||
908 | pNode = apNode[iCur]; | ||
909 | if( pNode->pOp && pNode->pOp->iPrec == 4 && pNode->pLeft == 0){ | ||
910 | if( iLeft > 0 ){ | ||
911 | /* Link the node to the tree */ | ||
912 | pNode->pLeft = apNode[iLeft]; | ||
913 | apNode[iLeft] = 0; | ||
914 | if( pNode->pLeft && pNode->pLeft->pOp && pNode->pLeft->pOp->iPrec > 4 ){ | ||
915 | if( pNode->pLeft->pLeft == 0 || pNode->pLeft->pRight == 0 ){ | ||
916 | /* Syntax error */ | ||
917 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pLeft->pStart->nLine, "'%z': Missing operand", &pNode->pLeft->pOp->sOp); | ||
918 | if( rc != SXERR_ABORT ){ | ||
919 | rc = SXERR_SYNTAX; | ||
920 | } | ||
921 | return rc; | ||
922 | } | ||
923 | } | ||
924 | }else{ | ||
925 | /* Syntax error */ | ||
926 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing operand", &pNode->pOp->sOp); | ||
927 | if( rc != SXERR_ABORT ){ | ||
928 | rc = SXERR_SYNTAX; | ||
929 | } | ||
930 | return rc; | ||
931 | } | ||
932 | } | ||
933 | /* Save terminal position */ | ||
934 | iLeft = iCur; | ||
935 | } | ||
936 | } | ||
937 | /* Process left and non-associative binary operators [i.e: *, /, &&, ||...]*/ | ||
938 | for( i = 7 ; i < 17 ; i++ ){ | ||
939 | iLeft = -1; | ||
940 | for( iCur = 0 ; iCur < nToken ; ++iCur ){ | ||
941 | if( apNode[iCur] == 0 ){ | ||
942 | continue; | ||
943 | } | ||
944 | pNode = apNode[iCur]; | ||
945 | if( pNode->pOp && pNode->pOp->iPrec == i && pNode->pLeft == 0 ){ | ||
946 | /* Get the right node */ | ||
947 | iRight = iCur + 1; | ||
948 | while( iRight < nToken && apNode[iRight] == 0 ){ | ||
949 | iRight++; | ||
950 | } | ||
951 | if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ | ||
952 | /* Syntax error */ | ||
953 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp); | ||
954 | if( rc != SXERR_ABORT ){ | ||
955 | rc = SXERR_SYNTAX; | ||
956 | } | ||
957 | return rc; | ||
958 | } | ||
959 | /* Link the node to the tree */ | ||
960 | pNode->pLeft = apNode[iLeft]; | ||
961 | pNode->pRight = apNode[iRight]; | ||
962 | apNode[iLeft] = apNode[iRight] = 0; | ||
963 | } | ||
964 | iLeft = iCur; | ||
965 | } | ||
966 | } | ||
967 | /* Handle the ternary operator. (expr1) ? (expr2) : (expr3) | ||
968 | * Note that we do not need a precedence loop here since | ||
969 | * we are dealing with a single operator. | ||
970 | */ | ||
971 | iLeft = -1; | ||
972 | for( iCur = 0 ; iCur < nToken ; ++iCur ){ | ||
973 | if( apNode[iCur] == 0 ){ | ||
974 | continue; | ||
975 | } | ||
976 | pNode = apNode[iCur]; | ||
977 | if( pNode->pOp && pNode->pOp->iOp == EXPR_OP_QUESTY && pNode->pLeft == 0 ){ | ||
978 | sxi32 iNest = 1; | ||
979 | if( iLeft < 0 || !NODE_ISTERM(iLeft) ){ | ||
980 | /* Missing condition */ | ||
981 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Syntax error", &pNode->pOp->sOp); | ||
982 | if( rc != SXERR_ABORT ){ | ||
983 | rc = SXERR_SYNTAX; | ||
984 | } | ||
985 | return rc; | ||
986 | } | ||
987 | /* Get the right node */ | ||
988 | iRight = iCur + 1; | ||
989 | while( iRight < nToken ){ | ||
990 | if( apNode[iRight] ){ | ||
991 | if( apNode[iRight]->pOp && apNode[iRight]->pOp->iOp == EXPR_OP_QUESTY && apNode[iRight]->pCond == 0){ | ||
992 | /* Increment nesting level */ | ||
993 | ++iNest; | ||
994 | }else if( apNode[iRight]->pStart->nType & JX9_TK_COLON /*:*/ ){ | ||
995 | /* Decrement nesting level */ | ||
996 | --iNest; | ||
997 | if( iNest <= 0 ){ | ||
998 | break; | ||
999 | } | ||
1000 | } | ||
1001 | } | ||
1002 | iRight++; | ||
1003 | } | ||
1004 | if( iRight > iCur + 1 ){ | ||
1005 | /* Recurse and process the then expression */ | ||
1006 | rc = ExprMakeTree(&(*pGen), &apNode[iCur + 1], iRight - iCur - 1); | ||
1007 | if( rc != SXRET_OK ){ | ||
1008 | return rc; | ||
1009 | } | ||
1010 | /* Link the node to the tree */ | ||
1011 | pNode->pLeft = apNode[iCur + 1]; | ||
1012 | }else{ | ||
1013 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'then' expression", &pNode->pOp->sOp); | ||
1014 | if( rc != SXERR_ABORT ){ | ||
1015 | rc = SXERR_SYNTAX; | ||
1016 | } | ||
1017 | return rc; | ||
1018 | } | ||
1019 | apNode[iCur + 1] = 0; | ||
1020 | if( iRight + 1 < nToken ){ | ||
1021 | /* Recurse and process the else expression */ | ||
1022 | rc = ExprMakeTree(&(*pGen), &apNode[iRight + 1], nToken - iRight - 1); | ||
1023 | if( rc != SXRET_OK ){ | ||
1024 | return rc; | ||
1025 | } | ||
1026 | /* Link the node to the tree */ | ||
1027 | pNode->pRight = apNode[iRight + 1]; | ||
1028 | apNode[iRight + 1] = apNode[iRight] = 0; | ||
1029 | }else{ | ||
1030 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing 'else' expression", &pNode->pOp->sOp); | ||
1031 | if( rc != SXERR_ABORT ){ | ||
1032 | rc = SXERR_SYNTAX; | ||
1033 | } | ||
1034 | return rc; | ||
1035 | } | ||
1036 | /* Point to the condition */ | ||
1037 | pNode->pCond = apNode[iLeft]; | ||
1038 | apNode[iLeft] = 0; | ||
1039 | break; | ||
1040 | } | ||
1041 | iLeft = iCur; | ||
1042 | } | ||
1043 | /* Process right associative binary operators [i.e: '=', '+=', '/='] | ||
1044 | * Note: All right associative binary operators have precedence 18 | ||
1045 | * so there is no need for a precedence loop here. | ||
1046 | */ | ||
1047 | iRight = -1; | ||
1048 | for( iCur = nToken - 1 ; iCur >= 0 ; iCur--){ | ||
1049 | if( apNode[iCur] == 0 ){ | ||
1050 | continue; | ||
1051 | } | ||
1052 | pNode = apNode[iCur]; | ||
1053 | if( pNode->pOp && pNode->pOp->iPrec == 18 && pNode->pLeft == 0 ){ | ||
1054 | /* Get the left node */ | ||
1055 | iLeft = iCur - 1; | ||
1056 | while( iLeft >= 0 && apNode[iLeft] == 0 ){ | ||
1057 | iLeft--; | ||
1058 | } | ||
1059 | if( iLeft < 0 || iRight < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ | ||
1060 | /* Syntax error */ | ||
1061 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp); | ||
1062 | if( rc != SXERR_ABORT ){ | ||
1063 | rc = SXERR_SYNTAX; | ||
1064 | } | ||
1065 | return rc; | ||
1066 | } | ||
1067 | if( ExprIsModifiableValue(apNode[iLeft]) == FALSE ){ | ||
1068 | if( pNode->pOp->iVmOp != JX9_OP_STORE ){ | ||
1069 | /* Left operand must be a modifiable l-value */ | ||
1070 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, | ||
1071 | "'%z': Left operand must be a modifiable l-value", &pNode->pOp->sOp); | ||
1072 | if( rc != SXERR_ABORT ){ | ||
1073 | rc = SXERR_SYNTAX; | ||
1074 | } | ||
1075 | return rc; | ||
1076 | } | ||
1077 | } | ||
1078 | /* Link the node to the tree (Reverse) */ | ||
1079 | pNode->pLeft = apNode[iRight]; | ||
1080 | pNode->pRight = apNode[iLeft]; | ||
1081 | apNode[iLeft] = apNode[iRight] = 0; | ||
1082 | } | ||
1083 | iRight = iCur; | ||
1084 | } | ||
1085 | /* Process the lowest precedence operator (22, comma) */ | ||
1086 | iLeft = -1; | ||
1087 | for( iCur = 0 ; iCur < nToken ; ++iCur ){ | ||
1088 | if( apNode[iCur] == 0 ){ | ||
1089 | continue; | ||
1090 | } | ||
1091 | pNode = apNode[iCur]; | ||
1092 | if( pNode->pOp && pNode->pOp->iPrec == 22 /* ',' */ && pNode->pLeft == 0 ){ | ||
1093 | /* Get the right node */ | ||
1094 | iRight = iCur + 1; | ||
1095 | while( iRight < nToken && apNode[iRight] == 0 ){ | ||
1096 | iRight++; | ||
1097 | } | ||
1098 | if( iRight >= nToken || iLeft < 0 || !NODE_ISTERM(iRight) || !NODE_ISTERM(iLeft) ){ | ||
1099 | /* Syntax error */ | ||
1100 | rc = jx9GenCompileError(pGen, E_ERROR, pNode->pStart->nLine, "'%z': Missing/Invalid operand", &pNode->pOp->sOp); | ||
1101 | if( rc != SXERR_ABORT ){ | ||
1102 | rc = SXERR_SYNTAX; | ||
1103 | } | ||
1104 | return rc; | ||
1105 | } | ||
1106 | /* Link the node to the tree */ | ||
1107 | pNode->pLeft = apNode[iLeft]; | ||
1108 | pNode->pRight = apNode[iRight]; | ||
1109 | apNode[iLeft] = apNode[iRight] = 0; | ||
1110 | } | ||
1111 | iLeft = iCur; | ||
1112 | } | ||
1113 | /* Point to the root of the expression tree */ | ||
1114 | for( iCur = 1 ; iCur < nToken ; ++iCur ){ | ||
1115 | if( apNode[iCur] ){ | ||
1116 | if( (apNode[iCur]->pOp || apNode[iCur]->xCode ) && apNode[0] != 0){ | ||
1117 | rc = jx9GenCompileError(pGen, E_ERROR, apNode[iCur]->pStart->nLine, "Unexpected token '%z'", &apNode[iCur]->pStart->sData); | ||
1118 | if( rc != SXERR_ABORT ){ | ||
1119 | rc = SXERR_SYNTAX; | ||
1120 | } | ||
1121 | return rc; | ||
1122 | } | ||
1123 | apNode[0] = apNode[iCur]; | ||
1124 | apNode[iCur] = 0; | ||
1125 | } | ||
1126 | } | ||
1127 | return SXRET_OK; | ||
1128 | } | ||
1129 | /* | ||
1130 | * Build an expression tree from the freshly extracted raw tokens. | ||
1131 | * If successful, the root of the tree is stored in ppRoot. | ||
1132 | * When errors, JX9 take care of generating the appropriate error message. | ||
1133 | * This is the public interface used by the most code generator routines. | ||
1134 | */ | ||
1135 | JX9_PRIVATE sxi32 jx9ExprMakeTree(jx9_gen_state *pGen, SySet *pExprNode, jx9_expr_node **ppRoot) | ||
1136 | { | ||
1137 | jx9_expr_node **apNode; | ||
1138 | jx9_expr_node *pNode; | ||
1139 | sxi32 rc; | ||
1140 | /* Reset node container */ | ||
1141 | SySetReset(pExprNode); | ||
1142 | pNode = 0; /* Prevent compiler warning */ | ||
1143 | /* Extract nodes one after one until we hit the end of the input */ | ||
1144 | while( pGen->pIn < pGen->pEnd ){ | ||
1145 | rc = ExprExtractNode(&(*pGen), &pNode); | ||
1146 | if( rc != SXRET_OK ){ | ||
1147 | return rc; | ||
1148 | } | ||
1149 | /* Save the extracted node */ | ||
1150 | SySetPut(pExprNode, (const void *)&pNode); | ||
1151 | } | ||
1152 | if( SySetUsed(pExprNode) < 1 ){ | ||
1153 | /* Empty expression [i.e: A semi-colon;] */ | ||
1154 | *ppRoot = 0; | ||
1155 | return SXRET_OK; | ||
1156 | } | ||
1157 | apNode = (jx9_expr_node **)SySetBasePtr(pExprNode); | ||
1158 | /* Make sure we are dealing with valid nodes */ | ||
1159 | rc = ExprVerifyNodes(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode)); | ||
1160 | if( rc != SXRET_OK ){ | ||
1161 | /* Don't worry about freeing memory, upper layer will | ||
1162 | * cleanup the mess left behind. | ||
1163 | */ | ||
1164 | *ppRoot = 0; | ||
1165 | return rc; | ||
1166 | } | ||
1167 | /* Build the tree */ | ||
1168 | rc = ExprMakeTree(&(*pGen), apNode, (sxi32)SySetUsed(pExprNode)); | ||
1169 | if( rc != SXRET_OK ){ | ||
1170 | /* Something goes wrong [i.e: Syntax error] */ | ||
1171 | *ppRoot = 0; | ||
1172 | return rc; | ||
1173 | } | ||
1174 | /* Point to the root of the tree */ | ||
1175 | *ppRoot = apNode[0]; | ||
1176 | return SXRET_OK; | ||
1177 | } | ||