summaryrefslogtreecommitdiffstats
path: root/common/unqlite/jx9_parse.c
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2014-12-07 10:08:07 +0100
committerAaron Seigo <aseigo@kde.org>2014-12-11 01:07:08 +0100
commit9ee8378d393778ac67314be7ea8d5bcbaeee9ee0 (patch)
treecf93471a69f9f4bbb4940de55ae134106fcd8380 /common/unqlite/jx9_parse.c
parentee6f068dff6b15441e553ffbfb2bf8aa97b26f57 (diff)
downloadsink-9ee8378d393778ac67314be7ea8d5bcbaeee9ee0.tar.gz
sink-9ee8378d393778ac67314be7ea8d5bcbaeee9ee0.zip
try out unqlite
Diffstat (limited to 'common/unqlite/jx9_parse.c')
-rw-r--r--common/unqlite/jx9_parse.c1177
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 */
33static 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 */
109static 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 */
119JX9_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 */
163JX9_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 */
196JX9_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 */
210JX9_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 */
265static 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;
309Synchronize:
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 */
320static 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 */
419static 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 */
546static 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 */
577JX9_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 */
593static 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 */
617static 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 */
624static 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 */
1135JX9_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}