summaryrefslogtreecommitdiffstats
path: root/common/unqlite/jx9_compile.c
diff options
context:
space:
mode:
authorAaron Seigo <aseigo@kde.org>2014-12-14 12:00:05 +0100
committerAaron Seigo <aseigo@kde.org>2014-12-14 12:00:05 +0100
commit7cc25005b8c46d1fa783d33def2c6923e8ef8469 (patch)
tree64fa59d17af29838396cf37b912b3babd885e5dd /common/unqlite/jx9_compile.c
parentbfc32f265e8ad72823db960fed371d72596003b7 (diff)
parenta6ed70495f9f3ecb21c26860dda16aadcdc91c3a (diff)
downloadsink-7cc25005b8c46d1fa783d33def2c6923e8ef8469.tar.gz
sink-7cc25005b8c46d1fa783d33def2c6923e8ef8469.zip
Merge branch 'unqlite'
Diffstat (limited to 'common/unqlite/jx9_compile.c')
-rw-r--r--common/unqlite/jx9_compile.c3671
1 files changed, 3671 insertions, 0 deletions
diff --git a/common/unqlite/jx9_compile.c b/common/unqlite/jx9_compile.c
new file mode 100644
index 0000000..a7c9916
--- /dev/null
+++ b/common/unqlite/jx9_compile.c
@@ -0,0 +1,3671 @@
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: compile.c v1.7 FreeBSD 2012-12-11 21:46 stable <chm@symisc.net> $ */
14#ifndef JX9_AMALGAMATION
15#include "jx9Int.h"
16#endif
17/*
18 * This file implement a thread-safe and full-reentrant compiler for the JX9 engine.
19 * That is, routines defined in this file takes a stream of tokens and output
20 * JX9 bytecode instructions.
21 */
22/* Forward declaration */
23typedef struct LangConstruct LangConstruct;
24typedef struct JumpFixup JumpFixup;
25/* Block [i.e: set of statements] control flags */
26#define GEN_BLOCK_LOOP 0x001 /* Loop block [i.e: for, while, ...] */
27#define GEN_BLOCK_PROTECTED 0x002 /* Protected block */
28#define GEN_BLOCK_COND 0x004 /* Conditional block [i.e: if(condition){} ]*/
29#define GEN_BLOCK_FUNC 0x008 /* Function body */
30#define GEN_BLOCK_GLOBAL 0x010 /* Global block (always set)*/
31#define GEN_BLOC_NESTED_FUNC 0x020 /* Nested function body */
32#define GEN_BLOCK_EXPR 0x040 /* Expression */
33#define GEN_BLOCK_STD 0x080 /* Standard block */
34#define GEN_BLOCK_SWITCH 0x100 /* Switch statement */
35/*
36 * Compilation of some JX9 constructs such as if, for, while, the logical or
37 * (||) and logical and (&&) operators in expressions requires the
38 * generation of forward jumps.
39 * Since the destination PC target of these jumps isn't known when the jumps
40 * are emitted, we record each forward jump in an instance of the following
41 * structure. Those jumps are fixed later when the jump destination is resolved.
42 */
43struct JumpFixup
44{
45 sxi32 nJumpType; /* Jump type. Either TRUE jump, FALSE jump or Unconditional jump */
46 sxu32 nInstrIdx; /* Instruction index to fix later when the jump destination is resolved. */
47};
48/*
49 * Each language construct is represented by an instance
50 * of the following structure.
51 */
52struct LangConstruct
53{
54 sxu32 nID; /* Language construct ID [i.e: JX9_TKWRD_WHILE, JX9_TKWRD_FOR, JX9_TKWRD_IF...] */
55 ProcLangConstruct xConstruct; /* C function implementing the language construct */
56};
57/* Compilation flags */
58#define JX9_COMPILE_SINGLE_STMT 0x001 /* Compile a single statement */
59/* Token stream synchronization macros */
60#define SWAP_TOKEN_STREAM(GEN, START, END)\
61 pTmp = GEN->pEnd;\
62 pGen->pIn = START;\
63 pGen->pEnd = END
64#define UPDATE_TOKEN_STREAM(GEN)\
65 if( GEN->pIn < pTmp ){\
66 GEN->pIn++;\
67 }\
68 GEN->pEnd = pTmp
69#define SWAP_DELIMITER(GEN, START, END)\
70 pTmpIn = GEN->pIn;\
71 pTmpEnd = GEN->pEnd;\
72 GEN->pIn = START;\
73 GEN->pEnd = END
74#define RE_SWAP_DELIMITER(GEN)\
75 GEN->pIn = pTmpIn;\
76 GEN->pEnd = pTmpEnd
77/* Flags related to expression compilation */
78#define EXPR_FLAG_LOAD_IDX_STORE 0x001 /* Set the iP2 flag when dealing with the LOAD_IDX instruction */
79#define EXPR_FLAG_RDONLY_LOAD 0x002 /* Read-only load, refer to the 'JX9_OP_LOAD' VM instruction for more information */
80#define EXPR_FLAG_COMMA_STATEMENT 0x004 /* Treat comma expression as a single statement (used by object attributes) */
81/* Forward declaration */
82static sxi32 jx9CompileExpr(
83 jx9_gen_state *pGen, /* Code generator state */
84 sxi32 iFlags, /* Control flags */
85 sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
86 );
87
88/*
89 * Recover from a compile-time error. In other words synchronize
90 * the token stream cursor with the first semi-colon seen.
91 */
92static sxi32 jx9ErrorRecover(jx9_gen_state *pGen)
93{
94 /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
95 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI /*';'*/) == 0){
96 pGen->pIn++;
97 }
98 return SXRET_OK;
99}
100/*
101 * Check if the given identifier name is reserved or not.
102 * Return TRUE if reserved.FALSE otherwise.
103 */
104static int GenStateIsReservedID(SyString *pName)
105{
106 if( pName->nByte == sizeof("null") - 1 ){
107 if( SyStrnicmp(pName->zString, "null", sizeof("null")-1) == 0 ){
108 return TRUE;
109 }else if( SyStrnicmp(pName->zString, "true", sizeof("true")-1) == 0 ){
110 return TRUE;
111 }
112 }else if( pName->nByte == sizeof("false") - 1 ){
113 if( SyStrnicmp(pName->zString, "false", sizeof("false")-1) == 0 ){
114 return TRUE;
115 }
116 }
117 /* Not a reserved constant */
118 return FALSE;
119}
120/*
121 * Check if a given token value is installed in the literal table.
122 */
123static sxi32 GenStateFindLiteral(jx9_gen_state *pGen, const SyString *pValue, sxu32 *pIdx)
124{
125 SyHashEntry *pEntry;
126 pEntry = SyHashGet(&pGen->hLiteral, (const void *)pValue->zString, pValue->nByte);
127 if( pEntry == 0 ){
128 return SXERR_NOTFOUND;
129 }
130 *pIdx = (sxu32)SX_PTR_TO_INT(pEntry->pUserData);
131 return SXRET_OK;
132}
133/*
134 * Install a given constant index in the literal table.
135 * In order to be installed, the jx9_value must be of type string.
136 */
137static sxi32 GenStateInstallLiteral(jx9_gen_state *pGen,jx9_value *pObj, sxu32 nIdx)
138{
139 if( SyBlobLength(&pObj->sBlob) > 0 ){
140 SyHashInsert(&pGen->hLiteral, SyBlobData(&pObj->sBlob), SyBlobLength(&pObj->sBlob), SX_INT_TO_PTR(nIdx));
141 }
142 return SXRET_OK;
143}
144/*
145 * Generate a fatal error.
146 */
147static sxi32 GenStateOutOfMem(jx9_gen_state *pGen)
148{
149 jx9GenCompileError(pGen,E_ERROR,1,"Fatal, Jx9 compiler is running out of memory");
150 /* Abort compilation immediately */
151 return SXERR_ABORT;
152}
153/*
154 * Fetch a block that correspond to the given criteria from the stack of
155 * compiled blocks.
156 * Return a pointer to that block on success. NULL otherwise.
157 */
158static GenBlock * GenStateFetchBlock(GenBlock *pCurrent, sxi32 iBlockType, sxi32 iCount)
159{
160 GenBlock *pBlock = pCurrent;
161 for(;;){
162 if( pBlock->iFlags & iBlockType ){
163 iCount--; /* Decrement nesting level */
164 if( iCount < 1 ){
165 /* Block meet with the desired criteria */
166 return pBlock;
167 }
168 }
169 /* Point to the upper block */
170 pBlock = pBlock->pParent;
171 if( pBlock == 0 || (pBlock->iFlags & (GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC)) ){
172 /* Forbidden */
173 break;
174 }
175 }
176 /* No such block */
177 return 0;
178}
179/*
180 * Initialize a freshly allocated block instance.
181 */
182static void GenStateInitBlock(
183 jx9_gen_state *pGen, /* Code generator state */
184 GenBlock *pBlock, /* Target block */
185 sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/
186 sxu32 nFirstInstr, /* First instruction to compile */
187 void *pUserData /* Upper layer private data */
188 )
189{
190 /* Initialize block fields */
191 pBlock->nFirstInstr = nFirstInstr;
192 pBlock->pUserData = pUserData;
193 pBlock->pGen = pGen;
194 pBlock->iFlags = iType;
195 pBlock->pParent = 0;
196 pBlock->bPostContinue = 0;
197 SySetInit(&pBlock->aJumpFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
198 SySetInit(&pBlock->aPostContFix, &pGen->pVm->sAllocator, sizeof(JumpFixup));
199}
200/*
201 * Allocate a new block instance.
202 * Return SXRET_OK and write a pointer to the new instantiated block
203 * on success.Otherwise generate a compile-time error and abort
204 * processing on failure.
205 */
206static sxi32 GenStateEnterBlock(
207 jx9_gen_state *pGen, /* Code generator state */
208 sxi32 iType, /* Block type [i.e: loop, conditional, function body, etc.]*/
209 sxu32 nFirstInstr, /* First instruction to compile */
210 void *pUserData, /* Upper layer private data */
211 GenBlock **ppBlock /* OUT: instantiated block */
212 )
213{
214 GenBlock *pBlock;
215 /* Allocate a new block instance */
216 pBlock = (GenBlock *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(GenBlock));
217 if( pBlock == 0 ){
218 /* If the supplied memory subsystem is so sick that we are unable to allocate
219 * a tiny chunk of memory, there is no much we can do here.
220 */
221 return GenStateOutOfMem(pGen);
222 }
223 /* Zero the structure */
224 SyZero(pBlock, sizeof(GenBlock));
225 GenStateInitBlock(&(*pGen), pBlock, iType, nFirstInstr, pUserData);
226 /* Link to the parent block */
227 pBlock->pParent = pGen->pCurrent;
228 /* Mark as the current block */
229 pGen->pCurrent = pBlock;
230 if( ppBlock ){
231 /* Write a pointer to the new instance */
232 *ppBlock = pBlock;
233 }
234 return SXRET_OK;
235}
236/*
237 * Release block fields without freeing the whole instance.
238 */
239static void GenStateReleaseBlock(GenBlock *pBlock)
240{
241 SySetRelease(&pBlock->aPostContFix);
242 SySetRelease(&pBlock->aJumpFix);
243}
244/*
245 * Release a block.
246 */
247static void GenStateFreeBlock(GenBlock *pBlock)
248{
249 jx9_gen_state *pGen = pBlock->pGen;
250 GenStateReleaseBlock(&(*pBlock));
251 /* Free the instance */
252 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pBlock);
253}
254/*
255 * POP and release a block from the stack of compiled blocks.
256 */
257static sxi32 GenStateLeaveBlock(jx9_gen_state *pGen, GenBlock **ppBlock)
258{
259 GenBlock *pBlock = pGen->pCurrent;
260 if( pBlock == 0 ){
261 /* No more block to pop */
262 return SXERR_EMPTY;
263 }
264 /* Point to the upper block */
265 pGen->pCurrent = pBlock->pParent;
266 if( ppBlock ){
267 /* Write a pointer to the popped block */
268 *ppBlock = pBlock;
269 }else{
270 /* Safely release the block */
271 GenStateFreeBlock(&(*pBlock));
272 }
273 return SXRET_OK;
274}
275/*
276 * Emit a forward jump.
277 * Notes on forward jumps
278 * Compilation of some JX9 constructs such as if, for, while and the logical or
279 * (||) and logical and (&&) operators in expressions requires the
280 * generation of forward jumps.
281 * Since the destination PC target of these jumps isn't known when the jumps
282 * are emitted, we record each forward jump in an instance of the following
283 * structure. Those jumps are fixed later when the jump destination is resolved.
284 */
285static sxi32 GenStateNewJumpFixup(GenBlock *pBlock, sxi32 nJumpType, sxu32 nInstrIdx)
286{
287 JumpFixup sJumpFix;
288 sxi32 rc;
289 /* Init the JumpFixup structure */
290 sJumpFix.nJumpType = nJumpType;
291 sJumpFix.nInstrIdx = nInstrIdx;
292 /* Insert in the jump fixup table */
293 rc = SySetPut(&pBlock->aJumpFix,(const void *)&sJumpFix);
294 return rc;
295}
296/*
297 * Fix a forward jump now the jump destination is resolved.
298 * Return the total number of fixed jumps.
299 * Notes on forward jumps:
300 * Compilation of some JX9 constructs such as if, for, while and the logical or
301 * (||) and logical and (&&) operators in expressions requires the
302 * generation of forward jumps.
303 * Since the destination PC target of these jumps isn't known when the jumps
304 * are emitted, we record each forward jump in an instance of the following
305 * structure.Those jumps are fixed later when the jump destination is resolved.
306 */
307static sxu32 GenStateFixJumps(GenBlock *pBlock, sxi32 nJumpType, sxu32 nJumpDest)
308{
309 JumpFixup *aFix;
310 VmInstr *pInstr;
311 sxu32 nFixed;
312 sxu32 n;
313 /* Point to the jump fixup table */
314 aFix = (JumpFixup *)SySetBasePtr(&pBlock->aJumpFix);
315 /* Fix the desired jumps */
316 for( nFixed = n = 0 ; n < SySetUsed(&pBlock->aJumpFix) ; ++n ){
317 if( aFix[n].nJumpType < 0 ){
318 /* Already fixed */
319 continue;
320 }
321 if( nJumpType > 0 && aFix[n].nJumpType != nJumpType ){
322 /* Not of our interest */
323 continue;
324 }
325 /* Point to the instruction to fix */
326 pInstr = jx9VmGetInstr(pBlock->pGen->pVm, aFix[n].nInstrIdx);
327 if( pInstr ){
328 pInstr->iP2 = nJumpDest;
329 nFixed++;
330 /* Mark as fixed */
331 aFix[n].nJumpType = -1;
332 }
333 }
334 /* Total number of fixed jumps */
335 return nFixed;
336}
337/*
338 * Reserve a room for a numeric constant [i.e: 64-bit integer or real number]
339 * in the constant table.
340 */
341static jx9_value * GenStateInstallNumLiteral(jx9_gen_state *pGen, sxu32 *pIdx)
342{
343 jx9_value *pObj;
344 sxu32 nIdx = 0; /* cc warning */
345 /* Reserve a new constant */
346 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
347 if( pObj == 0 ){
348 GenStateOutOfMem(pGen);
349 return 0;
350 }
351 *pIdx = nIdx;
352 /* TODO(chems): Create a numeric table (64bit int keys) same as
353 * the constant string iterals table [optimization purposes].
354 */
355 return pObj;
356}
357/*
358 * Compile a numeric [i.e: integer or real] literal.
359 * Notes on the integer type.
360 * According to the JX9 language reference manual
361 * Integers can be specified in decimal (base 10), hexadecimal (base 16), octal (base 8)
362 * or binary (base 2) notation, optionally preceded by a sign (- or +).
363 * To use octal notation, precede the number with a 0 (zero). To use hexadecimal
364 * notation precede the number with 0x. To use binary notation precede the number with 0b.
365 */
366static sxi32 jx9CompileNumLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
367{
368 SyToken *pToken = pGen->pIn; /* Raw token */
369 sxu32 nIdx = 0;
370 if( pToken->nType & JX9_TK_INTEGER ){
371 jx9_value *pObj;
372 sxi64 iValue;
373 iValue = jx9TokenValueToInt64(&pToken->sData);
374 pObj = GenStateInstallNumLiteral(&(*pGen), &nIdx);
375 if( pObj == 0 ){
376 SXUNUSED(iCompileFlag); /* cc warning */
377 return SXERR_ABORT;
378 }
379 jx9MemObjInitFromInt(pGen->pVm, pObj, iValue);
380 }else{
381 /* Real number */
382 jx9_value *pObj;
383 /* Reserve a new constant */
384 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
385 if( pObj == 0 ){
386 return GenStateOutOfMem(pGen);
387 }
388 jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
389 jx9MemObjToReal(pObj);
390 }
391 /* Emit the load constant instruction */
392 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
393 /* Node successfully compiled */
394 return SXRET_OK;
395}
396/*
397 * Compile a nowdoc string.
398 * According to the JX9 language reference manual:
399 *
400 * Nowdocs are to single-quoted strings what heredocs are to double-quoted strings.
401 * A nowdoc is specified similarly to a heredoc, but no parsing is done inside a nowdoc.
402 * The construct is ideal for embedding JX9 code or other large blocks of text without the
403 * need for escaping. It shares some features in common with the SGML <![CDATA[ ]]>
404 * construct, in that it declares a block of text which is not for parsing.
405 * A nowdoc is identified with the same <<< sequence used for heredocs, but the identifier
406 * which follows is enclosed in single quotes, e.g. <<<'EOT'. All the rules for heredoc
407 * identifiers also apply to nowdoc identifiers, especially those regarding the appearance
408 * of the closing identifier.
409 */
410static sxi32 jx9CompileNowdoc(jx9_gen_state *pGen,sxi32 iCompileFlag)
411{
412 SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
413 jx9_value *pObj;
414 sxu32 nIdx;
415 nIdx = 0; /* Prevent compiler warning */
416 if( pStr->nByte <= 0 ){
417 /* Empty string, load NULL */
418 jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC, 0, 0, 0, 0);
419 return SXRET_OK;
420 }
421 /* Reserve a new constant */
422 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
423 if( pObj == 0 ){
424 jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "JX9 engine is running out of memory");
425 SXUNUSED(iCompileFlag); /* cc warning */
426 return SXERR_ABORT;
427 }
428 /* No processing is done here, simply a memcpy() operation */
429 jx9MemObjInitFromString(pGen->pVm, pObj, pStr);
430 /* Emit the load constant instruction */
431 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
432 /* Node successfully compiled */
433 return SXRET_OK;
434}
435/*
436 * Compile a single quoted string.
437 * According to the JX9 language reference manual:
438 *
439 * The simplest way to specify a string is to enclose it in single quotes (the character ' ).
440 * To specify a literal single quote, escape it with a backslash (\). To specify a literal
441 * backslash, double it (\\). All other instances of backslash will be treated as a literal
442 * backslash: this means that the other escape sequences you might be used to, such as \r
443 * or \n, will be output literally as specified rather than having any special meaning.
444 *
445 */
446JX9_PRIVATE sxi32 jx9CompileSimpleString(jx9_gen_state *pGen, sxi32 iCompileFlag)
447{
448 SyString *pStr = &pGen->pIn->sData; /* Constant string literal */
449 const char *zIn, *zCur, *zEnd;
450 jx9_value *pObj;
451 sxu32 nIdx;
452 nIdx = 0; /* Prevent compiler warning */
453 /* Delimit the string */
454 zIn = pStr->zString;
455 zEnd = &zIn[pStr->nByte];
456 if( zIn >= zEnd ){
457 /* Empty string, load NULL */
458 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
459 return SXRET_OK;
460 }
461 if( SXRET_OK == GenStateFindLiteral(&(*pGen), pStr, &nIdx) ){
462 /* Already processed, emit the load constant instruction
463 * and return.
464 */
465 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
466 return SXRET_OK;
467 }
468 /* Reserve a new constant */
469 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
470 if( pObj == 0 ){
471 jx9GenCompileError(&(*pGen), E_ERROR, 1, "JX9 engine is running out of memory");
472 SXUNUSED(iCompileFlag); /* cc warning */
473 return SXERR_ABORT;
474 }
475 jx9MemObjInitFromString(pGen->pVm, pObj, 0);
476 /* Compile the node */
477 for(;;){
478 if( zIn >= zEnd ){
479 /* End of input */
480 break;
481 }
482 zCur = zIn;
483 while( zIn < zEnd && zIn[0] != '\\' ){
484 zIn++;
485 }
486 if( zIn > zCur ){
487 /* Append raw contents*/
488 jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
489 }
490 zIn++;
491 if( zIn < zEnd ){
492 if( zIn[0] == '\\' ){
493 /* A literal backslash */
494 jx9MemObjStringAppend(pObj, "\\", sizeof(char));
495 }else if( zIn[0] == '\'' ){
496 /* A single quote */
497 jx9MemObjStringAppend(pObj, "'", sizeof(char));
498 }else{
499 /* verbatim copy */
500 zIn--;
501 jx9MemObjStringAppend(pObj, zIn, sizeof(char)*2);
502 zIn++;
503 }
504 }
505 /* Advance the stream cursor */
506 zIn++;
507 }
508 /* Emit the load constant instruction */
509 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
510 if( pStr->nByte < 1024 ){
511 /* Install in the literal table */
512 GenStateInstallLiteral(pGen, pObj, nIdx);
513 }
514 /* Node successfully compiled */
515 return SXRET_OK;
516}
517/*
518 * Process variable expression [i.e: "$var", "${var}"] embedded in a double quoted/heredoc string.
519 * According to the JX9 language reference manual
520 * When a string is specified in double quotes or with heredoc, variables are parsed within it.
521 * There are two types of syntax: a simple one and a complex one. The simple syntax is the most
522 * common and convenient. It provides a way to embed a variable, an array value, or an object
523 * property in a string with a minimum of effort.
524 * Simple syntax
525 * If a dollar sign ($) is encountered, the parser will greedily take as many tokens as possible
526 * to form a valid variable name. Enclose the variable name in curly braces to explicitly specify
527 * the end of the name.
528 * Similarly, an array index or an object property can be parsed. With array indices, the closing
529 * square bracket (]) marks the end of the index. The same rules apply to object properties
530 * as to simple variables.
531 * Complex (curly) syntax
532 * This isn't called complex because the syntax is complex, but because it allows for the use
533 * of complex expressions.
534 * Any scalar variable, array element or object property with a string representation can be
535 * included via this syntax. Simply write the expression the same way as it would appear outside
536 * the string, and then wrap it in { and }. Since { can not be escaped, this syntax will only
537 * be recognised when the $ immediately follows the {. Use {\$ to get a literal {$
538 */
539static sxi32 GenStateProcessStringExpression(
540 jx9_gen_state *pGen, /* Code generator state */
541 const char *zIn, /* Raw expression */
542 const char *zEnd /* End of the expression */
543 )
544{
545 SyToken *pTmpIn, *pTmpEnd;
546 SySet sToken;
547 sxi32 rc;
548 /* Initialize the token set */
549 SySetInit(&sToken, &pGen->pVm->sAllocator, sizeof(SyToken));
550 /* Preallocate some slots */
551 SySetAlloc(&sToken, 0x08);
552 /* Tokenize the text */
553 jx9Tokenize(zIn,(sxu32)(zEnd-zIn),&sToken);
554 /* Swap delimiter */
555 pTmpIn = pGen->pIn;
556 pTmpEnd = pGen->pEnd;
557 pGen->pIn = (SyToken *)SySetBasePtr(&sToken);
558 pGen->pEnd = &pGen->pIn[SySetUsed(&sToken)];
559 /* Compile the expression */
560 rc = jx9CompileExpr(&(*pGen), 0, 0);
561 /* Restore token stream */
562 pGen->pIn = pTmpIn;
563 pGen->pEnd = pTmpEnd;
564 /* Release the token set */
565 SySetRelease(&sToken);
566 /* Compilation result */
567 return rc;
568}
569/*
570 * Reserve a new constant for a double quoted/heredoc string.
571 */
572static jx9_value * GenStateNewStrObj(jx9_gen_state *pGen,sxi32 *pCount)
573{
574 jx9_value *pConstObj;
575 sxu32 nIdx = 0;
576 /* Reserve a new constant */
577 pConstObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
578 if( pConstObj == 0 ){
579 GenStateOutOfMem(&(*pGen));
580 return 0;
581 }
582 (*pCount)++;
583 jx9MemObjInitFromString(pGen->pVm, pConstObj, 0);
584 /* Emit the load constant instruction */
585 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
586 return pConstObj;
587}
588/*
589 * Compile a double quoted/heredoc string.
590 * According to the JX9 language reference manual
591 * Heredoc
592 * A third way to delimit strings is the heredoc syntax: <<<. After this operator, an identifier
593 * is provided, then a newline. The string itself follows, and then the same identifier again
594 * to close the quotation.
595 * The closing identifier must begin in the first column of the line. Also, the identifier must
596 * follow the same naming rules as any other label in JX9: it must contain only alphanumeric
597 * characters and underscores, and must start with a non-digit character or underscore.
598 * Warning
599 * It is very important to note that the line with the closing identifier must contain
600 * no other characters, except possibly a semicolon (;). That means especially that the identifier
601 * may not be indented, and there may not be any spaces or tabs before or after the semicolon.
602 * It's also important to realize that the first character before the closing identifier must
603 * be a newline as defined by the local operating system. This is \n on UNIX systems, including Mac OS X.
604 * The closing delimiter (possibly followed by a semicolon) must also be followed by a newline.
605 * If this rule is broken and the closing identifier is not "clean", it will not be considered a closing
606 * identifier, and JX9 will continue looking for one. If a proper closing identifier is not found before
607 * the end of the current file, a parse error will result at the last line.
608 * Heredocs can not be used for initializing object properties.
609 * Double quoted
610 * If the string is enclosed in double-quotes ("), JX9 will interpret more escape sequences for special characters:
611 * Escaped characters Sequence Meaning
612 * \n linefeed (LF or 0x0A (10) in ASCII)
613 * \r carriage return (CR or 0x0D (13) in ASCII)
614 * \t horizontal tab (HT or 0x09 (9) in ASCII)
615 * \v vertical tab (VT or 0x0B (11) in ASCII)
616 * \f form feed (FF or 0x0C (12) in ASCII)
617 * \\ backslash
618 * \$ dollar sign
619 * \" double-quote
620 * \[0-7]{1, 3} the sequence of characters matching the regular expression is a character in octal notation
621 * \x[0-9A-Fa-f]{1, 2} the sequence of characters matching the regular expression is a character in hexadecimal notation
622 * As in single quoted strings, escaping any other character will result in the backslash being printed too.
623 * The most important feature of double-quoted strings is the fact that variable names will be expanded.
624 * See string parsing for details.
625 */
626static sxi32 GenStateCompileString(jx9_gen_state *pGen)
627{
628 SyString *pStr = &pGen->pIn->sData; /* Raw token value */
629 const char *zIn, *zCur, *zEnd;
630 jx9_value *pObj = 0;
631 sxi32 iCons;
632 sxi32 rc;
633 /* Delimit the string */
634 zIn = pStr->zString;
635 zEnd = &zIn[pStr->nByte];
636 if( zIn >= zEnd ){
637 /* Empty string, load NULL */
638 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
639 return SXRET_OK;
640 }
641 zCur = 0;
642 /* Compile the node */
643 iCons = 0;
644 for(;;){
645 zCur = zIn;
646 while( zIn < zEnd && zIn[0] != '\\' ){
647 if(zIn[0] == '$' && &zIn[1] < zEnd &&
648 (((unsigned char)zIn[1] >= 0xc0 || SyisAlpha(zIn[1]) || zIn[1] == '_')) ){
649 break;
650 }
651 zIn++;
652 }
653 if( zIn > zCur ){
654 if( pObj == 0 ){
655 pObj = GenStateNewStrObj(&(*pGen), &iCons);
656 if( pObj == 0 ){
657 return SXERR_ABORT;
658 }
659 }
660 jx9MemObjStringAppend(pObj, zCur, (sxu32)(zIn-zCur));
661 }
662 if( zIn >= zEnd ){
663 break;
664 }
665 if( zIn[0] == '\\' ){
666 const char *zPtr = 0;
667 sxu32 n;
668 zIn++;
669 if( zIn >= zEnd ){
670 break;
671 }
672 if( pObj == 0 ){
673 pObj = GenStateNewStrObj(&(*pGen), &iCons);
674 if( pObj == 0 ){
675 return SXERR_ABORT;
676 }
677 }
678 n = sizeof(char); /* size of conversion */
679 switch( zIn[0] ){
680 case '$':
681 /* Dollar sign */
682 jx9MemObjStringAppend(pObj, "$", sizeof(char));
683 break;
684 case '\\':
685 /* A literal backslash */
686 jx9MemObjStringAppend(pObj, "\\", sizeof(char));
687 break;
688 case 'a':
689 /* The "alert" character (BEL)[ctrl+g] ASCII code 7 */
690 jx9MemObjStringAppend(pObj, "\a", sizeof(char));
691 break;
692 case 'b':
693 /* Backspace (BS)[ctrl+h] ASCII code 8 */
694 jx9MemObjStringAppend(pObj, "\b", sizeof(char));
695 break;
696 case 'f':
697 /* Form-feed (FF)[ctrl+l] ASCII code 12 */
698 jx9MemObjStringAppend(pObj, "\f", sizeof(char));
699 break;
700 case 'n':
701 /* Line feed(new line) (LF)[ctrl+j] ASCII code 10 */
702 jx9MemObjStringAppend(pObj, "\n", sizeof(char));
703 break;
704 case 'r':
705 /* Carriage return (CR)[ctrl+m] ASCII code 13 */
706 jx9MemObjStringAppend(pObj, "\r", sizeof(char));
707 break;
708 case 't':
709 /* Horizontal tab (HT)[ctrl+i] ASCII code 9 */
710 jx9MemObjStringAppend(pObj, "\t", sizeof(char));
711 break;
712 case 'v':
713 /* Vertical tab(VT)[ctrl+k] ASCII code 11 */
714 jx9MemObjStringAppend(pObj, "\v", sizeof(char));
715 break;
716 case '\'':
717 /* Single quote */
718 jx9MemObjStringAppend(pObj, "'", sizeof(char));
719 break;
720 case '"':
721 /* Double quote */
722 jx9MemObjStringAppend(pObj, "\"", sizeof(char));
723 break;
724 case '0':
725 /* NUL byte */
726 jx9MemObjStringAppend(pObj, "\0", sizeof(char));
727 break;
728 case 'x':
729 if((unsigned char)zIn[1] < 0xc0 && SyisHex(zIn[1]) ){
730 int c;
731 /* Hex digit */
732 c = SyHexToint(zIn[1]) << 4;
733 if( &zIn[2] < zEnd ){
734 c += SyHexToint(zIn[2]);
735 }
736 /* Output char */
737 jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
738 n += sizeof(char) * 2;
739 }else{
740 /* Output literal character */
741 jx9MemObjStringAppend(pObj, "x", sizeof(char));
742 }
743 break;
744 case 'o':
745 if( &zIn[1] < zEnd && (unsigned char)zIn[1] < 0xc0 && SyisDigit(zIn[1]) && (zIn[1] - '0') < 8 ){
746 /* Octal digit stream */
747 int c;
748 c = 0;
749 zIn++;
750 for( zPtr = zIn ; zPtr < &zIn[3*sizeof(char)] ; zPtr++ ){
751 if( zPtr >= zEnd || (unsigned char)zPtr[0] >= 0xc0 || !SyisDigit(zPtr[0]) || (zPtr[0] - '0') > 7 ){
752 break;
753 }
754 c = c * 8 + (zPtr[0] - '0');
755 }
756 if ( c > 0 ){
757 jx9MemObjStringAppend(pObj, (const char *)&c, sizeof(char));
758 }
759 n = (sxu32)(zPtr-zIn);
760 }else{
761 /* Output literal character */
762 jx9MemObjStringAppend(pObj, "o", sizeof(char));
763 }
764 break;
765 default:
766 /* Output without a slash */
767 jx9MemObjStringAppend(pObj, zIn, sizeof(char));
768 break;
769 }
770 /* Advance the stream cursor */
771 zIn += n;
772 continue;
773 }
774 if( zIn[0] == '{' ){
775 /* Curly syntax */
776 const char *zExpr;
777 sxi32 iNest = 1;
778 zIn++;
779 zExpr = zIn;
780 /* Synchronize with the next closing curly braces */
781 while( zIn < zEnd ){
782 if( zIn[0] == '{' ){
783 /* Increment nesting level */
784 iNest++;
785 }else if(zIn[0] == '}' ){
786 /* Decrement nesting level */
787 iNest--;
788 if( iNest <= 0 ){
789 break;
790 }
791 }
792 zIn++;
793 }
794 /* Process the expression */
795 rc = GenStateProcessStringExpression(&(*pGen),zExpr,zIn);
796 if( rc == SXERR_ABORT ){
797 return SXERR_ABORT;
798 }
799 if( rc != SXERR_EMPTY ){
800 ++iCons;
801 }
802 if( zIn < zEnd ){
803 /* Jump the trailing curly */
804 zIn++;
805 }
806 }else{
807 /* Simple syntax */
808 const char *zExpr = zIn;
809 /* Assemble variable name */
810 for(;;){
811 /* Jump leading dollars */
812 while( zIn < zEnd && zIn[0] == '$' ){
813 zIn++;
814 }
815 for(;;){
816 while( zIn < zEnd && (unsigned char)zIn[0] < 0xc0 && (SyisAlphaNum(zIn[0]) || zIn[0] == '_' ) ){
817 zIn++;
818 }
819 if((unsigned char)zIn[0] >= 0xc0 ){
820 /* UTF-8 stream */
821 zIn++;
822 while( zIn < zEnd && (((unsigned char)zIn[0] & 0xc0) == 0x80) ){
823 zIn++;
824 }
825 continue;
826 }
827 break;
828 }
829 if( zIn >= zEnd ){
830 break;
831 }
832 if( zIn[0] == '[' ){
833 sxi32 iSquare = 1;
834 zIn++;
835 while( zIn < zEnd ){
836 if( zIn[0] == '[' ){
837 iSquare++;
838 }else if (zIn[0] == ']' ){
839 iSquare--;
840 if( iSquare <= 0 ){
841 break;
842 }
843 }
844 zIn++;
845 }
846 if( zIn < zEnd ){
847 zIn++;
848 }
849 break;
850 }else if( zIn[0] == '.' ){
851 /* Member access operator '.' */
852 zIn++;
853 }else{
854 break;
855 }
856 }
857 /* Process the expression */
858 rc = GenStateProcessStringExpression(&(*pGen),zExpr, zIn);
859 if( rc == SXERR_ABORT ){
860 return SXERR_ABORT;
861 }
862 if( rc != SXERR_EMPTY ){
863 ++iCons;
864 }
865 }
866 /* Invalidate the previously used constant */
867 pObj = 0;
868 }/*for(;;)*/
869 if( iCons > 1 ){
870 /* Concatenate all compiled constants */
871 jx9VmEmitInstr(pGen->pVm, JX9_OP_CAT, iCons, 0, 0, 0);
872 }
873 /* Node successfully compiled */
874 return SXRET_OK;
875}
876/*
877 * Compile a double quoted string.
878 * See the block-comment above for more information.
879 */
880JX9_PRIVATE sxi32 jx9CompileString(jx9_gen_state *pGen, sxi32 iCompileFlag)
881{
882 sxi32 rc;
883 rc = GenStateCompileString(&(*pGen));
884 SXUNUSED(iCompileFlag); /* cc warning */
885 /* Compilation result */
886 return rc;
887}
888/*
889 * Compile a literal which is an identifier(name) for simple values.
890 */
891JX9_PRIVATE sxi32 jx9CompileLiteral(jx9_gen_state *pGen,sxi32 iCompileFlag)
892{
893 SyToken *pToken = pGen->pIn;
894 jx9_value *pObj;
895 SyString *pStr;
896 sxu32 nIdx;
897 /* Extract token value */
898 pStr = &pToken->sData;
899 /* Deal with the reserved literals [i.e: null, false, true, ...] first */
900 if( pStr->nByte == sizeof("NULL") - 1 ){
901 if( SyStrnicmp(pStr->zString, "null", sizeof("NULL")-1) == 0 ){
902 /* NULL constant are always indexed at 0 */
903 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
904 return SXRET_OK;
905 }else if( SyStrnicmp(pStr->zString, "true", sizeof("TRUE")-1) == 0 ){
906 /* TRUE constant are always indexed at 1 */
907 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1, 0, 0);
908 return SXRET_OK;
909 }
910 }else if (pStr->nByte == sizeof("FALSE") - 1 &&
911 SyStrnicmp(pStr->zString, "false", sizeof("FALSE")-1) == 0 ){
912 /* FALSE constant are always indexed at 2 */
913 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 2, 0, 0);
914 return SXRET_OK;
915 }else if(pStr->nByte == sizeof("__LINE__") - 1 &&
916 SyMemcmp(pStr->zString, "__LINE__", sizeof("__LINE__")-1) == 0 ){
917 /* TICKET 1433-004: __LINE__ constant must be resolved at compile time, not run time */
918 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
919 if( pObj == 0 ){
920 SXUNUSED(iCompileFlag); /* cc warning */
921 return GenStateOutOfMem(pGen);
922 }
923 jx9MemObjInitFromInt(pGen->pVm, pObj, pToken->nLine);
924 /* Emit the load constant instruction */
925 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
926 return SXRET_OK;
927 }else if( pStr->nByte == sizeof("__FUNCTION__") - 1 &&
928 SyMemcmp(pStr->zString, "__FUNCTION__", sizeof("__FUNCTION__")-1) == 0 ){
929 GenBlock *pBlock = pGen->pCurrent;
930 /* TICKET 1433-004: __FUNCTION__/__METHOD__ constants must be resolved at compile time, not run time */
931 while( pBlock && (pBlock->iFlags & GEN_BLOCK_FUNC) == 0 ){
932 /* Point to the upper block */
933 pBlock = pBlock->pParent;
934 }
935 if( pBlock == 0 ){
936 /* Called in the global scope, load NULL */
937 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 0, 0, 0);
938 }else{
939 /* Extract the target function/method */
940 jx9_vm_func *pFunc = (jx9_vm_func *)pBlock->pUserData;
941 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
942 if( pObj == 0 ){
943 return GenStateOutOfMem(pGen);
944 }
945 jx9MemObjInitFromString(pGen->pVm, pObj, &pFunc->sName);
946 /* Emit the load constant instruction */
947 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
948 }
949 return SXRET_OK;
950 }
951 /* Query literal table */
952 if( SXRET_OK != GenStateFindLiteral(&(*pGen), &pToken->sData, &nIdx) ){
953 jx9_value *pObj;
954 /* Unknown literal, install it in the literal table */
955 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
956 if( pObj == 0 ){
957 return GenStateOutOfMem(pGen);
958 }
959 jx9MemObjInitFromString(pGen->pVm, pObj, &pToken->sData);
960 GenStateInstallLiteral(&(*pGen), pObj, nIdx);
961 }
962 /* Emit the load constant instruction */
963 jx9VmEmitInstr(pGen->pVm,JX9_OP_LOADC,1,nIdx, 0, 0);
964 /* Node successfully compiled */
965 return SXRET_OK;
966}
967/*
968 * Compile an array entry whether it is a key or a value.
969 */
970static sxi32 GenStateCompileJSONEntry(
971 jx9_gen_state *pGen, /* Code generator state */
972 SyToken *pIn, /* Token stream */
973 SyToken *pEnd, /* End of the token stream */
974 sxi32 iFlags, /* Compilation flags */
975 sxi32 (*xValidator)(jx9_gen_state *,jx9_expr_node *) /* Expression tree validator callback */
976 )
977{
978 SyToken *pTmpIn, *pTmpEnd;
979 sxi32 rc;
980 /* Swap token stream */
981 SWAP_DELIMITER(pGen, pIn, pEnd);
982 /* Compile the expression*/
983 rc = jx9CompileExpr(&(*pGen), iFlags, xValidator);
984 /* Restore token stream */
985 RE_SWAP_DELIMITER(pGen);
986 return rc;
987}
988/*
989 * Compile a Jx9 JSON Array.
990 */
991JX9_PRIVATE sxi32 jx9CompileJsonArray(jx9_gen_state *pGen, sxi32 iCompileFlag)
992{
993 sxi32 nPair = 0;
994 SyToken *pCur;
995 sxi32 rc;
996
997 pGen->pIn++; /* Jump the open square bracket '['*/
998 pGen->pEnd--;
999 SXUNUSED(iCompileFlag); /* cc warning */
1000 for(;;){
1001 /* Jump leading commas */
1002 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
1003 pGen->pIn++;
1004 }
1005 pCur = pGen->pIn;
1006 if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
1007 /* No more entry to process */
1008 break;
1009 }
1010 /* Compile entry */
1011 rc = GenStateCompileJSONEntry(&(*pGen),pCur,pGen->pIn,EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
1012 if( rc == SXERR_ABORT ){
1013 return SXERR_ABORT;
1014 }
1015 nPair++;
1016 }
1017 /* Emit the load map instruction */
1018 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP,nPair,0,0,0);
1019 /* Node successfully compiled */
1020 return SXRET_OK;
1021}
1022/*
1023 * Node validator for a given JSON key.
1024 */
1025static sxi32 GenStateJSONObjectKeyNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
1026{
1027 sxi32 rc = SXRET_OK;
1028 if( pRoot->xCode != jx9CompileVariable && pRoot->xCode != jx9CompileString
1029 && pRoot->xCode != jx9CompileSimpleString && pRoot->xCode != jx9CompileLiteral ){
1030 /* Unexpected expression */
1031 rc = jx9GenCompileError(&(*pGen), E_ERROR, pRoot->pStart? pRoot->pStart->nLine : 0,
1032 "JSON Object: Unexpected expression, key must be of type string, literal or simple variable");
1033 if( rc != SXERR_ABORT ){
1034 rc = SXERR_INVALID;
1035 }
1036 }
1037 return rc;
1038}
1039/*
1040 * Compile a Jx9 JSON Object
1041 */
1042JX9_PRIVATE sxi32 jx9CompileJsonObject(jx9_gen_state *pGen, sxi32 iCompileFlag)
1043{
1044 SyToken *pKey, *pCur;
1045 sxi32 nPair = 0;
1046 sxi32 rc;
1047
1048 pGen->pIn++; /* Jump the open querly braces '{'*/
1049 pGen->pEnd--;
1050 SXUNUSED(iCompileFlag); /* cc warning */
1051 for(;;){
1052 /* Jump leading commas */
1053 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_COMMA) ){
1054 pGen->pIn++;
1055 }
1056 pCur = pGen->pIn;
1057 if( SXRET_OK != jx9GetNextExpr(pGen->pIn, pGen->pEnd, &pGen->pIn) ){
1058 /* No more entry to process */
1059 break;
1060 }
1061 /* Compile the key */
1062 pKey = pCur;
1063 while( pCur < pGen->pIn ){
1064 if( pCur->nType & JX9_TK_COLON /*':'*/ ){
1065 break;
1066 }
1067 pCur++;
1068 }
1069 rc = SXERR_EMPTY;
1070 if( pCur < pGen->pIn ){
1071 if( &pCur[1] >= pGen->pIn ){
1072 /* Missing value */
1073 rc = jx9GenCompileError(&(*pGen), E_ERROR, pCur->nLine, "JSON Object: Missing entry value");
1074 if( rc == SXERR_ABORT ){
1075 return SXERR_ABORT;
1076 }
1077 return SXRET_OK;
1078 }
1079 /* Compile the expression holding the key */
1080 rc = GenStateCompileJSONEntry(&(*pGen), pKey, pCur,
1081 EXPR_FLAG_RDONLY_LOAD /* Do not create the variable if inexistant */,
1082 GenStateJSONObjectKeyNodeValidator /* Node validator callback */
1083 );
1084 if( rc == SXERR_ABORT ){
1085 return SXERR_ABORT;
1086 }
1087 pCur++; /* Jump the double colon ':' */
1088 }else if( pKey == pCur ){
1089 /* Key is omitted, emit an error */
1090 jx9GenCompileError(&(*pGen),E_ERROR, pCur->nLine, "JSON Object: Missing entry key");
1091 pCur++; /* Jump the double colon ':' */
1092 }else{
1093 /* Reset back the cursor and point to the entry value */
1094 pCur = pKey;
1095 }
1096 /* Compile indice value */
1097 rc = GenStateCompileJSONEntry(&(*pGen), pCur, pGen->pIn, EXPR_FLAG_RDONLY_LOAD/*Do not create the variable if inexistant*/,0);
1098 if( rc == SXERR_ABORT ){
1099 return SXERR_ABORT;
1100 }
1101 nPair++;
1102 }
1103 /* Emit the load map instruction */
1104 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD_MAP, nPair * 2, 1, 0, 0);
1105 /* Node successfully compiled */
1106 return SXRET_OK;
1107}
1108/*
1109 * Compile a function [i.e: print, exit(), include(), ...] which is a langauge
1110 * construct.
1111 */
1112JX9_PRIVATE sxi32 jx9CompileLangConstruct(jx9_gen_state *pGen,sxi32 iCompileFlag)
1113{
1114 SyString *pName;
1115 sxu32 nKeyID;
1116 sxi32 rc;
1117 /* Name of the language construct [i.e: print, die...]*/
1118 pName = &pGen->pIn->sData;
1119 nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
1120 pGen->pIn++; /* Jump the language construct keyword */
1121 if( nKeyID == JX9_TKWRD_PRINT ){
1122 SyToken *pTmp, *pNext = 0;
1123 /* Compile arguments one after one */
1124 pTmp = pGen->pEnd;
1125 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, 1 /* Boolean true index */, 0, 0);
1126 while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
1127 if( pGen->pIn < pNext ){
1128 pGen->pEnd = pNext;
1129 rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
1130 if( rc == SXERR_ABORT ){
1131 return SXERR_ABORT;
1132 }
1133 if( rc != SXERR_EMPTY ){
1134 /* Ticket 1433-008: Optimization #1: Consume input directly
1135 * without the overhead of a function call.
1136 * This is a very powerful optimization that improve
1137 * performance greatly.
1138 */
1139 jx9VmEmitInstr(pGen->pVm,JX9_OP_CONSUME,1,0,0,0);
1140 }
1141 }
1142 /* Jump trailing commas */
1143 while( pNext < pTmp && (pNext->nType & JX9_TK_COMMA) ){
1144 pNext++;
1145 }
1146 pGen->pIn = pNext;
1147 }
1148 /* Restore token stream */
1149 pGen->pEnd = pTmp;
1150 }else{
1151 sxi32 nArg = 0;
1152 sxu32 nIdx = 0;
1153 rc = jx9CompileExpr(&(*pGen), EXPR_FLAG_RDONLY_LOAD/* Do not create variable if inexistant */, 0);
1154 if( rc == SXERR_ABORT ){
1155 return SXERR_ABORT;
1156 }else if(rc != SXERR_EMPTY ){
1157 nArg = 1;
1158 }
1159 if( SXRET_OK != GenStateFindLiteral(&(*pGen), pName, &nIdx) ){
1160 jx9_value *pObj;
1161 /* Emit the call instruction */
1162 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
1163 if( pObj == 0 ){
1164 SXUNUSED(iCompileFlag); /* cc warning */
1165 return GenStateOutOfMem(pGen);
1166 }
1167 jx9MemObjInitFromString(pGen->pVm, pObj, pName);
1168 /* Install in the literal table */
1169 GenStateInstallLiteral(&(*pGen), pObj, nIdx);
1170 }
1171 /* Emit the call instruction */
1172 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
1173 jx9VmEmitInstr(pGen->pVm, JX9_OP_CALL, nArg, 0, 0, 0);
1174 }
1175 /* Node successfully compiled */
1176 return SXRET_OK;
1177}
1178/*
1179 * Compile a node holding a variable declaration.
1180 * According to the J9X language reference
1181 * Variables in JX9 are represented by a dollar sign followed by the name of the variable.
1182 * The variable name is case-sensitive.
1183 * Variable names follow the same rules as other labels in JX9. A valid variable name
1184 * starts with a letter, underscore or any UTF-8 stream, followed by any number of letters
1185 * numbers, or underscores.
1186 * By default, variables are always assigned by value unless the target value is a JSON
1187 * array or a JSON object which is passed by reference.
1188 */
1189JX9_PRIVATE sxi32 jx9CompileVariable(jx9_gen_state *pGen,sxi32 iCompileFlag)
1190{
1191 sxu32 nLine = pGen->pIn->nLine;
1192 SyHashEntry *pEntry;
1193 SyString *pName;
1194 char *zName = 0;
1195 sxi32 iP1;
1196 void *p3;
1197 sxi32 rc;
1198
1199 pGen->pIn++; /* Jump the dollar sign '$' */
1200 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
1201 /* Invalid variable name */
1202 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Invalid variable name");
1203 if( rc == SXERR_ABORT ){
1204 /* Error count limit reached, abort immediately */
1205 return SXERR_ABORT;
1206 }
1207 return SXRET_OK;
1208 }
1209 /* Extract variable name */
1210 pName = &pGen->pIn->sData;
1211 /* Advance the stream cursor */
1212 pGen->pIn++;
1213 pEntry = SyHashGet(&pGen->hVar, (const void *)pName->zString, pName->nByte);
1214 if( pEntry == 0 ){
1215 /* Duplicate name */
1216 zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
1217 if( zName == 0 ){
1218 return GenStateOutOfMem(pGen);
1219 }
1220 /* Install in the hashtable */
1221 SyHashInsert(&pGen->hVar, zName, pName->nByte, zName);
1222 }else{
1223 /* Name already available */
1224 zName = (char *)pEntry->pUserData;
1225 }
1226 p3 = (void *)zName;
1227 iP1 = 0;
1228 if( iCompileFlag & EXPR_FLAG_RDONLY_LOAD ){
1229 if( (iCompileFlag & EXPR_FLAG_LOAD_IDX_STORE) == 0 ){
1230 /* Read-only load.In other words do not create the variable if inexistant */
1231 iP1 = 1;
1232 }
1233 }
1234 /* Emit the load instruction */
1235 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOAD, iP1, 0, p3, 0);
1236 /* Node successfully compiled */
1237 return SXRET_OK;
1238}
1239/* Forward declaration */
1240static sxi32 GenStateCompileFunc(jx9_gen_state *pGen,SyString *pName,sxi32 iFlags,jx9_vm_func **ppFunc);
1241/*
1242 * Compile an annoynmous function or a closure.
1243 * According to the JX9 language reference
1244 * Anonymous functions, also known as closures, allow the creation of functions
1245 * which have no specified name. They are most useful as the value of callback
1246 * parameters, but they have many other uses. Closures can also be used as
1247 * the values of variables; Assigning a closure to a variable uses the same
1248 * syntax as any other assignment, including the trailing semicolon:
1249 * Example Anonymous function variable assignment example
1250 * $greet = function($name)
1251 * {
1252 * printf("Hello %s\r\n", $name);
1253 * };
1254 * $greet('World');
1255 * $greet('JX9');
1256 * Note that the implementation of annoynmous function and closure under
1257 * JX9 is completely different from the one used by the engine.
1258 */
1259JX9_PRIVATE sxi32 jx9CompileAnnonFunc(jx9_gen_state *pGen,sxi32 iCompileFlag)
1260{
1261 jx9_vm_func *pAnnonFunc; /* Annonymous function body */
1262 char zName[512]; /* Unique lambda name */
1263 static int iCnt = 1; /* There is no worry about thread-safety here, because only
1264 * one thread is allowed to compile the script.
1265 */
1266 jx9_value *pObj;
1267 SyString sName;
1268 sxu32 nIdx;
1269 sxu32 nLen;
1270 sxi32 rc;
1271
1272 pGen->pIn++; /* Jump the 'function' keyword */
1273 if( pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
1274 pGen->pIn++;
1275 }
1276 /* Reserve a constant for the lambda */
1277 pObj = jx9VmReserveConstObj(pGen->pVm, &nIdx);
1278 if( pObj == 0 ){
1279 GenStateOutOfMem(pGen);
1280 SXUNUSED(iCompileFlag); /* cc warning */
1281 return SXERR_ABORT;
1282 }
1283 /* Generate a unique name */
1284 nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
1285 /* Make sure the generated name is unique */
1286 while( SyHashGet(&pGen->pVm->hFunction, zName, nLen) != 0 && nLen < sizeof(zName) - 2 ){
1287 nLen = SyBufferFormat(zName, sizeof(zName), "[lambda_%d]", iCnt++);
1288 }
1289 SyStringInitFromBuf(&sName, zName, nLen);
1290 jx9MemObjInitFromString(pGen->pVm, pObj, &sName);
1291 /* Compile the lambda body */
1292 rc = GenStateCompileFunc(&(*pGen),&sName,0,&pAnnonFunc);
1293 if( rc == SXERR_ABORT ){
1294 return SXERR_ABORT;
1295 }
1296 /* Emit the load constant instruction */
1297 jx9VmEmitInstr(pGen->pVm, JX9_OP_LOADC, 0, nIdx, 0, 0);
1298 /* Node successfully compiled */
1299 return SXRET_OK;
1300}
1301/*
1302 * Compile the 'continue' statement.
1303 * According to the JX9 language reference
1304 * continue is used within looping structures to skip the rest of the current loop iteration
1305 * and continue execution at the condition evaluation and then the beginning of the next
1306 * iteration.
1307 * Note: Note that in JX9 the switch statement is considered a looping structure for
1308 * the purposes of continue.
1309 * continue accepts an optional numeric argument which tells it how many levels
1310 * of enclosing loops it should skip to the end of.
1311 * Note:
1312 * continue 0; and continue 1; is the same as running continue;.
1313 */
1314static sxi32 jx9CompileContinue(jx9_gen_state *pGen)
1315{
1316 GenBlock *pLoop; /* Target loop */
1317 sxi32 iLevel; /* How many nesting loop to skip */
1318 sxu32 nLine;
1319 sxi32 rc;
1320 nLine = pGen->pIn->nLine;
1321 iLevel = 0;
1322 /* Jump the 'continue' keyword */
1323 pGen->pIn++;
1324 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
1325 /* optional numeric argument which tells us how many levels
1326 * of enclosing loops we should skip to the end of.
1327 */
1328 iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
1329 if( iLevel < 2 ){
1330 iLevel = 0;
1331 }
1332 pGen->pIn++; /* Jump the optional numeric argument */
1333 }
1334 /* Point to the target loop */
1335 pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
1336 if( pLoop == 0 ){
1337 /* Illegal continue */
1338 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "A 'continue' statement may only be used within a loop or switch");
1339 if( rc == SXERR_ABORT ){
1340 /* Error count limit reached, abort immediately */
1341 return SXERR_ABORT;
1342 }
1343 }else{
1344 sxu32 nInstrIdx = 0;
1345 /* Emit the unconditional jump to the beginning of the target loop */
1346 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pLoop->nFirstInstr, 0, &nInstrIdx);
1347 if( pLoop->bPostContinue == TRUE ){
1348 JumpFixup sJumpFix;
1349 /* Post-continue */
1350 sJumpFix.nJumpType = JX9_OP_JMP;
1351 sJumpFix.nInstrIdx = nInstrIdx;
1352 SySetPut(&pLoop->aPostContFix, (const void *)&sJumpFix);
1353 }
1354 }
1355 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
1356 /* Not so fatal, emit a warning only */
1357 jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'continue' statement");
1358 }
1359 /* Statement successfully compiled */
1360 return SXRET_OK;
1361}
1362/*
1363 * Compile the 'break' statement.
1364 * According to the JX9 language reference
1365 * break ends execution of the current for, foreach, while, do-while or switch
1366 * structure.
1367 * break accepts an optional numeric argument which tells it how many nested
1368 * enclosing structures are to be broken out of.
1369 */
1370static sxi32 jx9CompileBreak(jx9_gen_state *pGen)
1371{
1372 GenBlock *pLoop; /* Target loop */
1373 sxi32 iLevel; /* How many nesting loop to skip */
1374 sxu32 nLine;
1375 sxi32 rc;
1376 nLine = pGen->pIn->nLine;
1377 iLevel = 0;
1378 /* Jump the 'break' keyword */
1379 pGen->pIn++;
1380 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_NUM) ){
1381 /* optional numeric argument which tells us how many levels
1382 * of enclosing loops we should skip to the end of.
1383 */
1384 iLevel = (sxi32)jx9TokenValueToInt64(&pGen->pIn->sData);
1385 if( iLevel < 2 ){
1386 iLevel = 0;
1387 }
1388 pGen->pIn++; /* Jump the optional numeric argument */
1389 }
1390 /* Extract the target loop */
1391 pLoop = GenStateFetchBlock(pGen->pCurrent, GEN_BLOCK_LOOP, iLevel);
1392 if( pLoop == 0 ){
1393 /* Illegal break */
1394 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "A 'break' statement may only be used within a loop or switch");
1395 if( rc == SXERR_ABORT ){
1396 /* Error count limit reached, abort immediately */
1397 return SXERR_ABORT;
1398 }
1399 }else{
1400 sxu32 nInstrIdx;
1401 rc = jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nInstrIdx);
1402 if( rc == SXRET_OK ){
1403 /* Fix the jump later when the jump destination is resolved */
1404 GenStateNewJumpFixup(pLoop, JX9_OP_JMP, nInstrIdx);
1405 }
1406 }
1407 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
1408 /* Not so fatal, emit a warning only */
1409 jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Expected semi-colon ';' after 'break' statement");
1410 }
1411 /* Statement successfully compiled */
1412 return SXRET_OK;
1413}
1414/* Forward declaration */
1415static sxi32 GenStateCompileChunk(jx9_gen_state *pGen,sxi32 iFlags);
1416/*
1417 * Compile a JX9 block.
1418 * A block is simply one or more JX9 statements and expressions to compile
1419 * optionally delimited by braces {}.
1420 * Return SXRET_OK on success. Any other return value indicates failure
1421 * and this function takes care of generating the appropriate error
1422 * message.
1423 */
1424static sxi32 jx9CompileBlock(
1425 jx9_gen_state *pGen /* Code generator state */
1426 )
1427{
1428 sxi32 rc;
1429 if( pGen->pIn->nType & JX9_TK_OCB /* '{' */ ){
1430 sxu32 nLine = pGen->pIn->nLine;
1431 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_STD, jx9VmInstrLength(pGen->pVm), 0, 0);
1432 if( rc != SXRET_OK ){
1433 return SXERR_ABORT;
1434 }
1435 pGen->pIn++;
1436 /* Compile until we hit the closing braces '}' */
1437 for(;;){
1438 if( pGen->pIn >= pGen->pEnd ){
1439 /* No more token to process. Missing closing braces */
1440 jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Missing closing braces '}'");
1441 break;
1442 }
1443 if( pGen->pIn->nType & JX9_TK_CCB/*'}'*/ ){
1444 /* Closing braces found, break immediately*/
1445 pGen->pIn++;
1446 break;
1447 }
1448 /* Compile a single statement */
1449 rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
1450 if( rc == SXERR_ABORT ){
1451 return SXERR_ABORT;
1452 }
1453 }
1454 GenStateLeaveBlock(&(*pGen), 0);
1455 }else{
1456 /* Compile a single statement */
1457 rc = GenStateCompileChunk(&(*pGen),JX9_COMPILE_SINGLE_STMT);
1458 if( rc == SXERR_ABORT ){
1459 return SXERR_ABORT;
1460 }
1461 }
1462 /* Jump trailing semi-colons ';' */
1463 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
1464 pGen->pIn++;
1465 }
1466 return SXRET_OK;
1467}
1468/*
1469 * Compile the gentle 'while' statement.
1470 * According to the JX9 language reference
1471 * while loops are the simplest type of loop in JX9.They behave just like their C counterparts.
1472 * The basic form of a while statement is:
1473 * while (expr)
1474 * statement
1475 * The meaning of a while statement is simple. It tells JX9 to execute the nested statement(s)
1476 * repeatedly, as long as the while expression evaluates to TRUE. The value of the expression
1477 * is checked each time at the beginning of the loop, so even if this value changes during
1478 * the execution of the nested statement(s), execution will not stop until the end of the iteration
1479 * (each time JX9 runs the statements in the loop is one iteration). Sometimes, if the while
1480 * expression evaluates to FALSE from the very beginning, the nested statement(s) won't even be run once.
1481 * Like with the if statement, you can group multiple statements within the same while loop by surrounding
1482 * a group of statements with curly braces, or by using the alternate syntax:
1483 * while (expr):
1484 * statement
1485 * endwhile;
1486 */
1487static sxi32 jx9CompileWhile(jx9_gen_state *pGen)
1488{
1489 GenBlock *pWhileBlock = 0;
1490 SyToken *pTmp, *pEnd = 0;
1491 sxu32 nFalseJump;
1492 sxu32 nLine;
1493 sxi32 rc;
1494 nLine = pGen->pIn->nLine;
1495 /* Jump the 'while' keyword */
1496 pGen->pIn++;
1497 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
1498 /* Syntax error */
1499 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'while' keyword");
1500 if( rc == SXERR_ABORT ){
1501 /* Error count limit reached, abort immediately */
1502 return SXERR_ABORT;
1503 }
1504 goto Synchronize;
1505 }
1506 /* Jump the left parenthesis '(' */
1507 pGen->pIn++;
1508 /* Create the loop block */
1509 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pWhileBlock);
1510 if( rc != SXRET_OK ){
1511 return SXERR_ABORT;
1512 }
1513 /* Delimit the condition */
1514 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
1515 if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
1516 /* Empty expression */
1517 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'while' keyword");
1518 if( rc == SXERR_ABORT ){
1519 /* Error count limit reached, abort immediately */
1520 return SXERR_ABORT;
1521 }
1522 }
1523 /* Swap token streams */
1524 pTmp = pGen->pEnd;
1525 pGen->pEnd = pEnd;
1526 /* Compile the expression */
1527 rc = jx9CompileExpr(&(*pGen), 0, 0);
1528 if( rc == SXERR_ABORT ){
1529 /* Expression handler request an operation abort [i.e: Out-of-memory] */
1530 return SXERR_ABORT;
1531 }
1532 /* Update token stream */
1533 while(pGen->pIn < pEnd ){
1534 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
1535 if( rc == SXERR_ABORT ){
1536 return SXERR_ABORT;
1537 }
1538 pGen->pIn++;
1539 }
1540 /* Synchronize pointers */
1541 pGen->pIn = &pEnd[1];
1542 pGen->pEnd = pTmp;
1543 /* Emit the false jump */
1544 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
1545 /* Save the instruction index so we can fix it later when the jump destination is resolved */
1546 GenStateNewJumpFixup(pWhileBlock, JX9_OP_JZ, nFalseJump);
1547 /* Compile the loop body */
1548 rc = jx9CompileBlock(&(*pGen));
1549 if( rc == SXERR_ABORT ){
1550 return SXERR_ABORT;
1551 }
1552 /* Emit the unconditional jump to the start of the loop */
1553 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pWhileBlock->nFirstInstr, 0, 0);
1554 /* Fix all jumps now the destination is resolved */
1555 GenStateFixJumps(pWhileBlock, -1, jx9VmInstrLength(pGen->pVm));
1556 /* Release the loop block */
1557 GenStateLeaveBlock(pGen, 0);
1558 /* Statement successfully compiled */
1559 return SXRET_OK;
1560Synchronize:
1561 /* Synchronize with the first semi-colon ';' so we can avoid
1562 * compiling this erroneous block.
1563 */
1564 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
1565 pGen->pIn++;
1566 }
1567 return SXRET_OK;
1568}
1569/*
1570 * Compile the complex and powerful 'for' statement.
1571 * According to the JX9 language reference
1572 * for loops are the most complex loops in JX9. They behave like their C counterparts.
1573 * The syntax of a for loop is:
1574 * for (expr1; expr2; expr3)
1575 * statement
1576 * The first expression (expr1) is evaluated (executed) once unconditionally at
1577 * the beginning of the loop.
1578 * In the beginning of each iteration, expr2 is evaluated. If it evaluates to
1579 * TRUE, the loop continues and the nested statement(s) are executed. If it evaluates
1580 * to FALSE, the execution of the loop ends.
1581 * At the end of each iteration, expr3 is evaluated (executed).
1582 * Each of the expressions can be empty or contain multiple expressions separated by commas.
1583 * In expr2, all expressions separated by a comma are evaluated but the result is taken
1584 * from the last part. expr2 being empty means the loop should be run indefinitely
1585 * (JX9 implicitly considers it as TRUE, like C). This may not be as useless as you might
1586 * think, since often you'd want to end the loop using a conditional break statement instead
1587 * of using the for truth expression.
1588 */
1589static sxi32 jx9CompileFor(jx9_gen_state *pGen)
1590{
1591 SyToken *pTmp, *pPostStart, *pEnd = 0;
1592 GenBlock *pForBlock = 0;
1593 sxu32 nFalseJump;
1594 sxu32 nLine;
1595 sxi32 rc;
1596 nLine = pGen->pIn->nLine;
1597 /* Jump the 'for' keyword */
1598 pGen->pIn++;
1599 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
1600 /* Syntax error */
1601 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'for' keyword");
1602 if( rc == SXERR_ABORT ){
1603 /* Error count limit reached, abort immediately */
1604 return SXERR_ABORT;
1605 }
1606 return SXRET_OK;
1607 }
1608 /* Jump the left parenthesis '(' */
1609 pGen->pIn++;
1610 /* Delimit the init-expr;condition;post-expr */
1611 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
1612 if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
1613 /* Empty expression */
1614 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "for: Invalid expression");
1615 if( rc == SXERR_ABORT ){
1616 /* Error count limit reached, abort immediately */
1617 return SXERR_ABORT;
1618 }
1619 /* Synchronize */
1620 pGen->pIn = pEnd;
1621 if( pGen->pIn < pGen->pEnd ){
1622 pGen->pIn++;
1623 }
1624 return SXRET_OK;
1625 }
1626 /* Swap token streams */
1627 pTmp = pGen->pEnd;
1628 pGen->pEnd = pEnd;
1629 /* Compile initialization expressions if available */
1630 rc = jx9CompileExpr(&(*pGen), 0, 0);
1631 /* Pop operand lvalues */
1632 if( rc == SXERR_ABORT ){
1633 /* Expression handler request an operation abort [i.e: Out-of-memory] */
1634 return SXERR_ABORT;
1635 }else if( rc != SXERR_EMPTY ){
1636 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
1637 }
1638 if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
1639 /* Syntax error */
1640 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
1641 "for: Expected ';' after initialization expressions");
1642 if( rc == SXERR_ABORT ){
1643 /* Error count limit reached, abort immediately */
1644 return SXERR_ABORT;
1645 }
1646 return SXRET_OK;
1647 }
1648 /* Jump the trailing ';' */
1649 pGen->pIn++;
1650 /* Create the loop block */
1651 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForBlock);
1652 if( rc != SXRET_OK ){
1653 return SXERR_ABORT;
1654 }
1655 /* Deffer continue jumps */
1656 pForBlock->bPostContinue = TRUE;
1657 /* Compile the condition */
1658 rc = jx9CompileExpr(&(*pGen), 0, 0);
1659 if( rc == SXERR_ABORT ){
1660 /* Expression handler request an operation abort [i.e: Out-of-memory] */
1661 return SXERR_ABORT;
1662 }else if( rc != SXERR_EMPTY ){
1663 /* Emit the false jump */
1664 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nFalseJump);
1665 /* Save the instruction index so we can fix it later when the jump destination is resolved */
1666 GenStateNewJumpFixup(pForBlock, JX9_OP_JZ, nFalseJump);
1667 }
1668 if( (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
1669 /* Syntax error */
1670 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
1671 "for: Expected ';' after conditionals expressions");
1672 if( rc == SXERR_ABORT ){
1673 /* Error count limit reached, abort immediately */
1674 return SXERR_ABORT;
1675 }
1676 return SXRET_OK;
1677 }
1678 /* Jump the trailing ';' */
1679 pGen->pIn++;
1680 /* Save the post condition stream */
1681 pPostStart = pGen->pIn;
1682 /* Compile the loop body */
1683 pGen->pIn = &pEnd[1]; /* Jump the trailing parenthesis ')' */
1684 pGen->pEnd = pTmp;
1685 rc = jx9CompileBlock(&(*pGen));
1686 if( rc == SXERR_ABORT ){
1687 return SXERR_ABORT;
1688 }
1689 /* Fix post-continue jumps */
1690 if( SySetUsed(&pForBlock->aPostContFix) > 0 ){
1691 JumpFixup *aPost;
1692 VmInstr *pInstr;
1693 sxu32 nJumpDest;
1694 sxu32 n;
1695 aPost = (JumpFixup *)SySetBasePtr(&pForBlock->aPostContFix);
1696 nJumpDest = jx9VmInstrLength(pGen->pVm);
1697 for( n = 0 ; n < SySetUsed(&pForBlock->aPostContFix) ; ++n ){
1698 pInstr = jx9VmGetInstr(pGen->pVm, aPost[n].nInstrIdx);
1699 if( pInstr ){
1700 /* Fix jump */
1701 pInstr->iP2 = nJumpDest;
1702 }
1703 }
1704 }
1705 /* compile the post-expressions if available */
1706 while( pPostStart < pEnd && (pPostStart->nType & JX9_TK_SEMI) ){
1707 pPostStart++;
1708 }
1709 if( pPostStart < pEnd ){
1710 SyToken *pTmpIn, *pTmpEnd;
1711 SWAP_DELIMITER(pGen, pPostStart, pEnd);
1712 rc = jx9CompileExpr(&(*pGen), 0, 0);
1713 if( pGen->pIn < pGen->pEnd ){
1714 /* Syntax error */
1715 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "for: Expected ')' after post-expressions");
1716 if( rc == SXERR_ABORT ){
1717 /* Error count limit reached, abort immediately */
1718 return SXERR_ABORT;
1719 }
1720 return SXRET_OK;
1721 }
1722 RE_SWAP_DELIMITER(pGen);
1723 if( rc == SXERR_ABORT ){
1724 /* Expression handler request an operation abort [i.e: Out-of-memory] */
1725 return SXERR_ABORT;
1726 }else if( rc != SXERR_EMPTY){
1727 /* Pop operand lvalue */
1728 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
1729 }
1730 }
1731 /* Emit the unconditional jump to the start of the loop */
1732 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForBlock->nFirstInstr, 0, 0);
1733 /* Fix all jumps now the destination is resolved */
1734 GenStateFixJumps(pForBlock, -1, jx9VmInstrLength(pGen->pVm));
1735 /* Release the loop block */
1736 GenStateLeaveBlock(pGen, 0);
1737 /* Statement successfully compiled */
1738 return SXRET_OK;
1739}
1740/* Expression tree validator callback used by the 'foreach' statement.
1741 * Note that only variable expression [i.e: $x; ${'My'.'Var'}; ${$a['key]};...]
1742 * are allowed.
1743 */
1744static sxi32 GenStateForEachNodeValidator(jx9_gen_state *pGen,jx9_expr_node *pRoot)
1745{
1746 sxi32 rc = SXRET_OK; /* Assume a valid expression tree */
1747 if( pRoot->xCode != jx9CompileVariable ){
1748 /* Unexpected expression */
1749 rc = jx9GenCompileError(&(*pGen),
1750 E_ERROR,
1751 pRoot->pStart? pRoot->pStart->nLine : 0,
1752 "foreach: Expecting a variable name"
1753 );
1754 if( rc != SXERR_ABORT ){
1755 rc = SXERR_INVALID;
1756 }
1757 }
1758 return rc;
1759}
1760/*
1761 * Compile the 'foreach' statement.
1762 * According to the JX9 language reference
1763 * The foreach construct simply gives an easy way to iterate over arrays. foreach works
1764 * only on arrays (and objects), and will issue an error when you try to use it on a variable
1765 * with a different data type or an uninitialized variable. There are two syntaxes; the second
1766 * is a minor but useful extension of the first:
1767 * foreach (json_array_json_object as $value)
1768 * statement
1769 * foreach (json_array_json_objec as $key,$value)
1770 * statement
1771 * The first form loops over the array given by array_expression. On each loop, the value
1772 * of the current element is assigned to $value and the internal array pointer is advanced
1773 * by one (so on the next loop, you'll be looking at the next element).
1774 * The second form does the same thing, except that the current element's key will be assigned
1775 * to the variable $key on each loop.
1776 * Note:
1777 * When foreach first starts executing, the internal array pointer is automatically reset to the
1778 * first element of the array. This means that you do not need to call reset() before a foreach loop.
1779 */
1780static sxi32 jx9CompileForeach(jx9_gen_state *pGen)
1781{
1782 SyToken *pCur, *pTmp, *pEnd = 0;
1783 GenBlock *pForeachBlock = 0;
1784 jx9_foreach_info *pInfo;
1785 sxu32 nFalseJump;
1786 VmInstr *pInstr;
1787 sxu32 nLine;
1788 sxi32 rc;
1789 nLine = pGen->pIn->nLine;
1790 /* Jump the 'foreach' keyword */
1791 pGen->pIn++;
1792 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
1793 /* Syntax error */
1794 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Expected '('");
1795 if( rc == SXERR_ABORT ){
1796 /* Error count limit reached, abort immediately */
1797 return SXERR_ABORT;
1798 }
1799 goto Synchronize;
1800 }
1801 /* Jump the left parenthesis '(' */
1802 pGen->pIn++;
1803 /* Create the loop block */
1804 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP, jx9VmInstrLength(pGen->pVm), 0, &pForeachBlock);
1805 if( rc != SXRET_OK ){
1806 return SXERR_ABORT;
1807 }
1808 /* Delimit the expression */
1809 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
1810 if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
1811 /* Empty expression */
1812 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "foreach: Missing expression");
1813 if( rc == SXERR_ABORT ){
1814 /* Error count limit reached, abort immediately */
1815 return SXERR_ABORT;
1816 }
1817 /* Synchronize */
1818 pGen->pIn = pEnd;
1819 if( pGen->pIn < pGen->pEnd ){
1820 pGen->pIn++;
1821 }
1822 return SXRET_OK;
1823 }
1824 /* Compile the array expression */
1825 pCur = pGen->pIn;
1826 while( pCur < pEnd ){
1827 if( pCur->nType & JX9_TK_KEYWORD ){
1828 sxi32 nKeywrd = SX_PTR_TO_INT(pCur->pUserData);
1829 if( nKeywrd == JX9_TKWRD_AS ){
1830 /* Break with the first 'as' found */
1831 break;
1832 }
1833 }
1834 /* Advance the stream cursor */
1835 pCur++;
1836 }
1837 if( pCur <= pGen->pIn ){
1838 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
1839 "foreach: Missing array/object expression");
1840 if( rc == SXERR_ABORT ){
1841 /* Don't worry about freeing memory, everything will be released shortly */
1842 return SXERR_ABORT;
1843 }
1844 goto Synchronize;
1845 }
1846 /* Swap token streams */
1847 pTmp = pGen->pEnd;
1848 pGen->pEnd = pCur;
1849 rc = jx9CompileExpr(&(*pGen), 0, 0);
1850 if( rc == SXERR_ABORT ){
1851 /* Expression handler request an operation abort [i.e: Out-of-memory] */
1852 return SXERR_ABORT;
1853 }
1854 /* Update token stream */
1855 while(pGen->pIn < pCur ){
1856 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Unexpected token '%z'", &pGen->pIn->sData);
1857 if( rc == SXERR_ABORT ){
1858 /* Don't worry about freeing memory, everything will be released shortly */
1859 return SXERR_ABORT;
1860 }
1861 pGen->pIn++;
1862 }
1863 pCur++; /* Jump the 'as' keyword */
1864 pGen->pIn = pCur;
1865 if( pGen->pIn >= pEnd ){
1866 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key => $value pair");
1867 if( rc == SXERR_ABORT ){
1868 return SXERR_ABORT;
1869 }
1870 }
1871 /* Create the foreach context */
1872 pInfo = (jx9_foreach_info *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_foreach_info));
1873 if( pInfo == 0 ){
1874 jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Fatal, JX9 engine is running out-of-memory");
1875 return SXERR_ABORT;
1876 }
1877 /* Zero the structure */
1878 SyZero(pInfo, sizeof(jx9_foreach_info));
1879 /* Initialize structure fields */
1880 SySetInit(&pInfo->aStep, &pGen->pVm->sAllocator, sizeof(jx9_foreach_step *));
1881 /* Check if we have a key field */
1882 while( pCur < pEnd && (pCur->nType & JX9_TK_COMMA) == 0 ){
1883 pCur++;
1884 }
1885 if( pCur < pEnd ){
1886 /* Compile the expression holding the key name */
1887 if( pGen->pIn >= pCur ){
1888 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $key");
1889 if( rc == SXERR_ABORT ){
1890 /* Don't worry about freeing memory, everything will be released shortly */
1891 return SXERR_ABORT;
1892 }
1893 }else{
1894 pGen->pEnd = pCur;
1895 rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
1896 if( rc == SXERR_ABORT ){
1897 /* Don't worry about freeing memory, everything will be released shortly */
1898 return SXERR_ABORT;
1899 }
1900 pInstr = jx9VmPopInstr(pGen->pVm);
1901 if( pInstr->p3 ){
1902 /* Record key name */
1903 SyStringInitFromBuf(&pInfo->sKey, pInstr->p3, SyStrlen((const char *)pInstr->p3));
1904 }
1905 pInfo->iFlags |= JX9_4EACH_STEP_KEY;
1906 }
1907 pGen->pIn = &pCur[1]; /* Jump the arrow */
1908 }
1909 pGen->pEnd = pEnd;
1910 if( pGen->pIn >= pEnd ){
1911 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "foreach: Missing $value");
1912 if( rc == SXERR_ABORT ){
1913 /* Don't worry about freeing memory, everything will be released shortly */
1914 return SXERR_ABORT;
1915 }
1916 goto Synchronize;
1917 }
1918 /* Compile the expression holding the value name */
1919 rc = jx9CompileExpr(&(*pGen), 0, GenStateForEachNodeValidator);
1920 if( rc == SXERR_ABORT ){
1921 /* Don't worry about freeing memory, everything will be released shortly */
1922 return SXERR_ABORT;
1923 }
1924 pInstr = jx9VmPopInstr(pGen->pVm);
1925 if( pInstr->p3 ){
1926 /* Record value name */
1927 SyStringInitFromBuf(&pInfo->sValue, pInstr->p3, SyStrlen((const char *)pInstr->p3));
1928 }
1929 /* Emit the 'FOREACH_INIT' instruction */
1930 jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_INIT, 0, 0, pInfo, &nFalseJump);
1931 /* Save the instruction index so we can fix it later when the jump destination is resolved */
1932 GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_INIT, nFalseJump);
1933 /* Record the first instruction to execute */
1934 pForeachBlock->nFirstInstr = jx9VmInstrLength(pGen->pVm);
1935 /* Emit the FOREACH_STEP instruction */
1936 jx9VmEmitInstr(pGen->pVm, JX9_OP_FOREACH_STEP, 0, 0, pInfo, &nFalseJump);
1937 /* Save the instruction index so we can fix it later when the jump destination is resolved */
1938 GenStateNewJumpFixup(pForeachBlock, JX9_OP_FOREACH_STEP, nFalseJump);
1939 /* Compile the loop body */
1940 pGen->pIn = &pEnd[1];
1941 pGen->pEnd = pTmp;
1942 rc = jx9CompileBlock(&(*pGen));
1943 if( rc == SXERR_ABORT ){
1944 /* Don't worry about freeing memory, everything will be released shortly */
1945 return SXERR_ABORT;
1946 }
1947 /* Emit the unconditional jump to the start of the loop */
1948 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, pForeachBlock->nFirstInstr, 0, 0);
1949 /* Fix all jumps now the destination is resolved */
1950 GenStateFixJumps(pForeachBlock, -1,jx9VmInstrLength(pGen->pVm));
1951 /* Release the loop block */
1952 GenStateLeaveBlock(pGen, 0);
1953 /* Statement successfully compiled */
1954 return SXRET_OK;
1955Synchronize:
1956 /* Synchronize with the first semi-colon ';' so we can avoid
1957 * compiling this erroneous block.
1958 */
1959 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
1960 pGen->pIn++;
1961 }
1962 return SXRET_OK;
1963}
1964/*
1965 * Compile the infamous if/elseif/else if/else statements.
1966 * According to the JX9 language reference
1967 * The if construct is one of the most important features of many languages JX9 included.
1968 * It allows for conditional execution of code fragments. JX9 features an if structure
1969 * that is similar to that of C:
1970 * if (expr)
1971 * statement
1972 * else construct:
1973 * Often you'd want to execute a statement if a certain condition is met, and a different
1974 * statement if the condition is not met. This is what else is for. else extends an if statement
1975 * to execute a statement in case the expression in the if statement evaluates to FALSE.
1976 * For example, the following code would display a is greater than b if $a is greater than
1977 * $b, and a is NOT greater than b otherwise.
1978 * The else statement is only executed if the if expression evaluated to FALSE, and if there
1979 * were any elseif expressions - only if they evaluated to FALSE as well
1980 * elseif
1981 * elseif, as its name suggests, is a combination of if and else. Like else, it extends
1982 * an if statement to execute a different statement in case the original if expression evaluates
1983 * to FALSE. However, unlike else, it will execute that alternative expression only if the elseif
1984 * conditional expression evaluates to TRUE. For example, the following code would display a is bigger
1985 * than b, a equal to b or a is smaller than b:
1986 * if ($a > $b) {
1987 * print "a is bigger than b";
1988 * } elseif ($a == $b) {
1989 * print "a is equal to b";
1990 * } else {
1991 * print "a is smaller than b";
1992 * }
1993 */
1994static sxi32 jx9CompileIf(jx9_gen_state *pGen)
1995{
1996 SyToken *pToken, *pTmp, *pEnd = 0;
1997 GenBlock *pCondBlock = 0;
1998 sxu32 nJumpIdx;
1999 sxu32 nKeyID;
2000 sxi32 rc;
2001 /* Jump the 'if' keyword */
2002 pGen->pIn++;
2003 pToken = pGen->pIn;
2004 /* Create the conditional block */
2005 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_COND, jx9VmInstrLength(pGen->pVm), 0, &pCondBlock);
2006 if( rc != SXRET_OK ){
2007 return SXERR_ABORT;
2008 }
2009 /* Process as many [if/else if/elseif/else] blocks as we can */
2010 for(;;){
2011 if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_LPAREN) == 0 ){
2012 /* Syntax error */
2013 if( pToken >= pGen->pEnd ){
2014 pToken--;
2015 }
2016 rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing '('");
2017 if( rc == SXERR_ABORT ){
2018 /* Error count limit reached, abort immediately */
2019 return SXERR_ABORT;
2020 }
2021 goto Synchronize;
2022 }
2023 /* Jump the left parenthesis '(' */
2024 pToken++;
2025 /* Delimit the condition */
2026 jx9DelimitNestedTokens(pToken, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
2027 if( pToken >= pEnd || (pEnd->nType & JX9_TK_RPAREN) == 0 ){
2028 /* Syntax error */
2029 if( pToken >= pGen->pEnd ){
2030 pToken--;
2031 }
2032 rc = jx9GenCompileError(pGen, E_ERROR, pToken->nLine, "if/else/elseif: Missing ')'");
2033 if( rc == SXERR_ABORT ){
2034 /* Error count limit reached, abort immediately */
2035 return SXERR_ABORT;
2036 }
2037 goto Synchronize;
2038 }
2039 /* Swap token streams */
2040 SWAP_TOKEN_STREAM(pGen, pToken, pEnd);
2041 /* Compile the condition */
2042 rc = jx9CompileExpr(&(*pGen), 0, 0);
2043 /* Update token stream */
2044 while(pGen->pIn < pEnd ){
2045 jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
2046 pGen->pIn++;
2047 }
2048 pGen->pIn = &pEnd[1];
2049 pGen->pEnd = pTmp;
2050 if( rc == SXERR_ABORT ){
2051 /* Expression handler request an operation abort [i.e: Out-of-memory] */
2052 return SXERR_ABORT;
2053 }
2054 /* Emit the false jump */
2055 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJumpIdx);
2056 /* Save the instruction index so we can fix it later when the jump destination is resolved */
2057 GenStateNewJumpFixup(pCondBlock, JX9_OP_JZ, nJumpIdx);
2058 /* Compile the body */
2059 rc = jx9CompileBlock(&(*pGen));
2060 if( rc == SXERR_ABORT ){
2061 return SXERR_ABORT;
2062 }
2063 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
2064 break;
2065 }
2066 /* Ensure that the keyword ID is 'else if' or 'else' */
2067 nKeyID = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
2068 if( (nKeyID & (JX9_TKWRD_ELSE|JX9_TKWRD_ELIF)) == 0 ){
2069 break;
2070 }
2071 /* Emit the unconditional jump */
2072 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJumpIdx);
2073 /* Save the instruction index so we can fix it later when the jump destination is resolved */
2074 GenStateNewJumpFixup(pCondBlock, JX9_OP_JMP, nJumpIdx);
2075 if( nKeyID & JX9_TKWRD_ELSE ){
2076 pToken = &pGen->pIn[1];
2077 if( pToken >= pGen->pEnd || (pToken->nType & JX9_TK_KEYWORD) == 0 ||
2078 SX_PTR_TO_INT(pToken->pUserData) != JX9_TKWRD_IF ){
2079 break;
2080 }
2081 pGen->pIn++; /* Jump the 'else' keyword */
2082 }
2083 pGen->pIn++; /* Jump the 'elseif/if' keyword */
2084 /* Synchronize cursors */
2085 pToken = pGen->pIn;
2086 /* Fix the false jump */
2087 GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
2088 } /* For(;;) */
2089 /* Fix the false jump */
2090 GenStateFixJumps(pCondBlock, JX9_OP_JZ, jx9VmInstrLength(pGen->pVm));
2091 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_KEYWORD) &&
2092 (SX_PTR_TO_INT(pGen->pIn->pUserData) & JX9_TKWRD_ELSE) ){
2093 /* Compile the else block */
2094 pGen->pIn++;
2095 rc = jx9CompileBlock(&(*pGen));
2096 if( rc == SXERR_ABORT ){
2097
2098 return SXERR_ABORT;
2099 }
2100 }
2101 nJumpIdx = jx9VmInstrLength(pGen->pVm);
2102 /* Fix all unconditional jumps now the destination is resolved */
2103 GenStateFixJumps(pCondBlock, JX9_OP_JMP, nJumpIdx);
2104 /* Release the conditional block */
2105 GenStateLeaveBlock(pGen, 0);
2106 /* Statement successfully compiled */
2107 return SXRET_OK;
2108Synchronize:
2109 /* Synchronize with the first semi-colon ';' so we can avoid compiling this erroneous block.
2110 */
2111 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
2112 pGen->pIn++;
2113 }
2114 return SXRET_OK;
2115}
2116/*
2117 * Compile the return statement.
2118 * According to the JX9 language reference
2119 * If called from within a function, the return() statement immediately ends execution
2120 * of the current function, and returns its argument as the value of the function call.
2121 * return() will also end the execution of an eval() statement or script file.
2122 * If called from the global scope, then execution of the current script file is ended.
2123 * If the current script file was include()ed or require()ed, then control is passed back
2124 * to the calling file. Furthermore, if the current script file was include()ed, then the value
2125 * given to return() will be returned as the value of the include() call. If return() is called
2126 * from within the main script file, then script execution end.
2127 * Note that since return() is a language construct and not a function, the parentheses
2128 * surrounding its arguments are not required. It is common to leave them out, and you actually
2129 * should do so as JX9 has less work to do in this case.
2130 * Note: If no parameter is supplied, then the parentheses must be omitted and JX9 is returning NULL instead..
2131 */
2132static sxi32 jx9CompileReturn(jx9_gen_state *pGen)
2133{
2134 sxi32 nRet = 0; /* TRUE if there is a return value */
2135 sxi32 rc;
2136 /* Jump the 'return' keyword */
2137 pGen->pIn++;
2138 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
2139 /* Compile the expression */
2140 rc = jx9CompileExpr(&(*pGen), 0, 0);
2141 if( rc == SXERR_ABORT ){
2142 return SXERR_ABORT;
2143 }else if(rc != SXERR_EMPTY ){
2144 nRet = 1;
2145 }
2146 }
2147 /* Emit the done instruction */
2148 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, nRet, 0, 0, 0);
2149 return SXRET_OK;
2150}
2151/*
2152 * Compile the die/exit language construct.
2153 * The role of these constructs is to terminate execution of the script.
2154 * Shutdown functions will always be executed even if exit() is called.
2155 */
2156static sxi32 jx9CompileHalt(jx9_gen_state *pGen)
2157{
2158 sxi32 nExpr = 0;
2159 sxi32 rc;
2160 /* Jump the die/exit keyword */
2161 pGen->pIn++;
2162 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
2163 /* Compile the expression */
2164 rc = jx9CompileExpr(&(*pGen), 0, 0);
2165 if( rc == SXERR_ABORT ){
2166 return SXERR_ABORT;
2167 }else if(rc != SXERR_EMPTY ){
2168 nExpr = 1;
2169 }
2170 }
2171 /* Emit the HALT instruction */
2172 jx9VmEmitInstr(pGen->pVm, JX9_OP_HALT, nExpr, 0, 0, 0);
2173 return SXRET_OK;
2174}
2175/*
2176 * Compile the static statement.
2177 * According to the JX9 language reference
2178 * Another important feature of variable scoping is the static variable.
2179 * A static variable exists only in a local function scope, but it does not lose its value
2180 * when program execution leaves this scope.
2181 * Static variables also provide one way to deal with recursive functions.
2182 */
2183static sxi32 jx9CompileStatic(jx9_gen_state *pGen)
2184{
2185 jx9_vm_func_static_var sStatic; /* Structure describing the static variable */
2186 jx9_vm_func *pFunc; /* Enclosing function */
2187 GenBlock *pBlock;
2188 SyString *pName;
2189 char *zDup;
2190 sxu32 nLine;
2191 sxi32 rc;
2192 /* Jump the static keyword */
2193 nLine = pGen->pIn->nLine;
2194 pGen->pIn++;
2195 /* Extract the enclosing function if any */
2196 pBlock = pGen->pCurrent;
2197 while( pBlock ){
2198 if( pBlock->iFlags & GEN_BLOCK_FUNC){
2199 break;
2200 }
2201 /* Point to the upper block */
2202 pBlock = pBlock->pParent;
2203 }
2204 if( pBlock == 0 ){
2205 /* Static statement, called outside of a function body, treat it as a simple variable. */
2206 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
2207 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
2208 if( rc == SXERR_ABORT ){
2209 return SXERR_ABORT;
2210 }
2211 goto Synchronize;
2212 }
2213 /* Compile the expression holding the variable */
2214 rc = jx9CompileExpr(&(*pGen), 0, 0);
2215 if( rc == SXERR_ABORT ){
2216 return SXERR_ABORT;
2217 }else if( rc != SXERR_EMPTY ){
2218 /* Emit the POP instruction */
2219 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
2220 }
2221 return SXRET_OK;
2222 }
2223 pFunc = (jx9_vm_func *)pBlock->pUserData;
2224 /* Make sure we are dealing with a valid statement */
2225 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 || &pGen->pIn[1] >= pGen->pEnd ||
2226 (pGen->pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
2227 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Expected variable after 'static' keyword");
2228 if( rc == SXERR_ABORT ){
2229 return SXERR_ABORT;
2230 }
2231 goto Synchronize;
2232 }
2233 pGen->pIn++;
2234 /* Extract variable name */
2235 pName = &pGen->pIn->sData;
2236 pGen->pIn++; /* Jump the var name */
2237 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_EQUAL/*'='*/)) == 0 ){
2238 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "static: Unexpected token '%z'", &pGen->pIn->sData);
2239 goto Synchronize;
2240 }
2241 /* Initialize the structure describing the static variable */
2242 SySetInit(&sStatic.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
2243 sStatic.nIdx = SXU32_HIGH; /* Not yet created */
2244 /* Duplicate variable name */
2245 zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
2246 if( zDup == 0 ){
2247 jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Fatal, JX9 engine is running out of memory");
2248 return SXERR_ABORT;
2249 }
2250 SyStringInitFromBuf(&sStatic.sName, zDup, pName->nByte);
2251 /* Check if we have an expression to compile */
2252 if( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_EQUAL) ){
2253 SySet *pInstrContainer;
2254 pGen->pIn++; /* Jump the equal '=' sign */
2255 /* Swap bytecode container */
2256 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
2257 jx9VmSetByteCodeContainer(pGen->pVm, &sStatic.aByteCode);
2258 /* Compile the expression */
2259 rc = jx9CompileExpr(&(*pGen), 0, 0);
2260 /* Emit the done instruction */
2261 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
2262 /* Restore default bytecode container */
2263 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
2264 }
2265 /* Finally save the compiled static variable in the appropriate container */
2266 SySetPut(&pFunc->aStatic, (const void *)&sStatic);
2267 return SXRET_OK;
2268Synchronize:
2269 /* Synchronize with the first semi-colon ';', so we can avoid compiling this erroneous
2270 * statement.
2271 */
2272 while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
2273 pGen->pIn++;
2274 }
2275 return SXRET_OK;
2276}
2277/*
2278 * Compile the 'const' statement.
2279 * According to the JX9 language reference
2280 * A constant is an identifier (name) for a simple value. As the name suggests, that value
2281 * cannot change during the execution of the script (except for magic constants, which aren't actually constants).
2282 * A constant is case-sensitive by default. By convention, constant identifiers are always uppercase.
2283 * The name of a constant follows the same rules as any label in JX9. A valid constant name starts
2284 * with a letter or underscore, followed by any number of letters, numbers, or underscores.
2285 * As a regular expression it would be expressed thusly: [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*
2286 * Syntax
2287 * You can define a constant by using the define()-function or by using the const keyword outside
2288 * a object definition. Once a constant is defined, it can never be changed or undefined.
2289 * You can get the value of a constant by simply specifying its name. Unlike with variables
2290 * you should not prepend a constant with a $. You can also use the function constant() to read
2291 * a constant's value if you wish to obtain the constant's name dynamically. Use get_defined_constants()
2292 * to get a list of all defined constants.
2293 */
2294static sxi32 jx9CompileConstant(jx9_gen_state *pGen)
2295{
2296 SySet *pConsCode, *pInstrContainer;
2297 sxu32 nLine = pGen->pIn->nLine;
2298 SyString *pName;
2299 sxi32 rc;
2300 pGen->pIn++; /* Jump the 'const' keyword */
2301 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_SSTR|JX9_TK_DSTR|JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
2302 /* Invalid constant name */
2303 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Invalid constant name");
2304 if( rc == SXERR_ABORT ){
2305 /* Error count limit reached, abort immediately */
2306 return SXERR_ABORT;
2307 }
2308 goto Synchronize;
2309 }
2310 /* Peek constant name */
2311 pName = &pGen->pIn->sData;
2312 /* Make sure the constant name isn't reserved */
2313 if( GenStateIsReservedID(pName) ){
2314 /* Reserved constant */
2315 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Cannot redeclare a reserved constant '%z'", pName);
2316 if( rc == SXERR_ABORT ){
2317 /* Error count limit reached, abort immediately */
2318 return SXERR_ABORT;
2319 }
2320 goto Synchronize;
2321 }
2322 pGen->pIn++;
2323 if(pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_EQUAL /* '=' */) == 0 ){
2324 /* Invalid statement*/
2325 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "const: Expected '=' after constant name");
2326 if( rc == SXERR_ABORT ){
2327 /* Error count limit reached, abort immediately */
2328 return SXERR_ABORT;
2329 }
2330 goto Synchronize;
2331 }
2332 pGen->pIn++; /*Jump the equal sign */
2333 /* Allocate a new constant value container */
2334 pConsCode = (SySet *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(SySet));
2335 if( pConsCode == 0 ){
2336 return GenStateOutOfMem(pGen);
2337 }
2338 SySetInit(pConsCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
2339 /* Swap bytecode container */
2340 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
2341 jx9VmSetByteCodeContainer(pGen->pVm, pConsCode);
2342 /* Compile constant value */
2343 rc = jx9CompileExpr(&(*pGen), 0, 0);
2344 /* Emit the done instruction */
2345 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
2346 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
2347 if( rc == SXERR_ABORT ){
2348 /* Don't worry about freeing memory, everything will be released shortly */
2349 return SXERR_ABORT;
2350 }
2351 SySetSetUserData(pConsCode, pGen->pVm);
2352 /* Register the constant */
2353 rc = jx9VmRegisterConstant(pGen->pVm, pName, jx9VmExpandConstantValue, pConsCode);
2354 if( rc != SXRET_OK ){
2355 SySetRelease(pConsCode);
2356 SyMemBackendPoolFree(&pGen->pVm->sAllocator, pConsCode);
2357 }
2358 return SXRET_OK;
2359Synchronize:
2360 /* Synchronize with the next-semi-colon and avoid compiling this erroneous statement */
2361 while(pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
2362 pGen->pIn++;
2363 }
2364 return SXRET_OK;
2365}
2366/*
2367 * Compile the uplink construct.
2368 * According to the JX9 language reference
2369 * In JX9 global variables must be declared uplink inside a function if they are going
2370 * to be used in that function.
2371 * Example #1 Using global
2372 * $a = 1;
2373 * $b = 2;
2374 * function Sum()
2375 * {
2376 * uplink $a, $b;
2377 * $b = $a + $b;
2378 * }
2379 * Sum();
2380 * print $b;
2381 * ?>
2382 * The above script will output 3. By declaring $a and $b global within the function
2383 * all references to either variable will refer to the global version. There is no limit
2384 * to the number of global variables that can be manipulated by a function.
2385 */
2386static sxi32 jx9CompileUplink(jx9_gen_state *pGen)
2387{
2388 SyToken *pTmp, *pNext = 0;
2389 sxi32 nExpr;
2390 sxi32 rc;
2391 /* Jump the 'uplink' keyword */
2392 pGen->pIn++;
2393 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_SEMI) ){
2394 /* Nothing to process */
2395 return SXRET_OK;
2396 }
2397 pTmp = pGen->pEnd;
2398 nExpr = 0;
2399 while( SXRET_OK == jx9GetNextExpr(pGen->pIn, pTmp, &pNext) ){
2400 if( pGen->pIn < pNext ){
2401 pGen->pEnd = pNext;
2402 if( (pGen->pIn->nType & JX9_TK_DOLLAR) == 0 ){
2403 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "uplink: Expected variable name");
2404 if( rc == SXERR_ABORT ){
2405 return SXERR_ABORT;
2406 }
2407 }else{
2408 pGen->pIn++;
2409 if( pGen->pIn >= pGen->pEnd ){
2410 /* Emit a warning */
2411 jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn[-1].nLine, "uplink: Empty variable name");
2412 }else{
2413 rc = jx9CompileExpr(&(*pGen), 0, 0);
2414 if( rc == SXERR_ABORT ){
2415 return SXERR_ABORT;
2416 }else if(rc != SXERR_EMPTY ){
2417 nExpr++;
2418 }
2419 }
2420 }
2421 }
2422 /* Next expression in the stream */
2423 pGen->pIn = pNext;
2424 /* Jump trailing commas */
2425 while( pGen->pIn < pTmp && (pGen->pIn->nType & JX9_TK_COMMA) ){
2426 pGen->pIn++;
2427 }
2428 }
2429 /* Restore token stream */
2430 pGen->pEnd = pTmp;
2431 if( nExpr > 0 ){
2432 /* Emit the uplink instruction */
2433 jx9VmEmitInstr(pGen->pVm, JX9_OP_UPLINK, nExpr, 0, 0, 0);
2434 }
2435 return SXRET_OK;
2436}
2437/*
2438 * Compile a switch block.
2439 * (See block-comment below for more information)
2440 */
2441static sxi32 GenStateCompileSwitchBlock(jx9_gen_state *pGen,sxu32 *pBlockStart)
2442{
2443 sxi32 rc = SXRET_OK;
2444 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*':'*/)) == 0 ){
2445 /* Unexpected token */
2446 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Unexpected token '%z'", &pGen->pIn->sData);
2447 if( rc == SXERR_ABORT ){
2448 return SXERR_ABORT;
2449 }
2450 pGen->pIn++;
2451 }
2452 pGen->pIn++;
2453 /* First instruction to execute in this block. */
2454 *pBlockStart = jx9VmInstrLength(pGen->pVm);
2455 /* Compile the block until we hit a case/default/endswitch keyword
2456 * or the '}' token */
2457 for(;;){
2458 if( pGen->pIn >= pGen->pEnd ){
2459 /* No more input to process */
2460 break;
2461 }
2462 rc = SXRET_OK;
2463 if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
2464 if( pGen->pIn->nType & JX9_TK_CCB /*'}' */ ){
2465 rc = SXERR_EOF;
2466 break;
2467 }
2468 }else{
2469 sxi32 nKwrd;
2470 /* Extract the keyword */
2471 nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
2472 if( nKwrd == JX9_TKWRD_CASE || nKwrd == JX9_TKWRD_DEFAULT ){
2473 break;
2474 }
2475 }
2476 /* Compile block */
2477 rc = jx9CompileBlock(&(*pGen));
2478 if( rc == SXERR_ABORT ){
2479 return SXERR_ABORT;
2480 }
2481 }
2482 return rc;
2483}
2484/*
2485 * Compile a case eXpression.
2486 * (See block-comment below for more information)
2487 */
2488static sxi32 GenStateCompileCaseExpr(jx9_gen_state *pGen, jx9_case_expr *pExpr)
2489{
2490 SySet *pInstrContainer;
2491 SyToken *pEnd, *pTmp;
2492 sxi32 iNest = 0;
2493 sxi32 rc;
2494 /* Delimit the expression */
2495 pEnd = pGen->pIn;
2496 while( pEnd < pGen->pEnd ){
2497 if( pEnd->nType & JX9_TK_LPAREN /*(*/ ){
2498 /* Increment nesting level */
2499 iNest++;
2500 }else if( pEnd->nType & JX9_TK_RPAREN /*)*/ ){
2501 /* Decrement nesting level */
2502 iNest--;
2503 }else if( pEnd->nType & (JX9_TK_SEMI/*';'*/|JX9_TK_COLON/*;'*/) && iNest < 1 ){
2504 break;
2505 }
2506 pEnd++;
2507 }
2508 if( pGen->pIn >= pEnd ){
2509 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine, "Empty case expression");
2510 if( rc == SXERR_ABORT ){
2511 /* Error count limit reached, abort immediately */
2512 return SXERR_ABORT;
2513 }
2514 }
2515 /* Swap token stream */
2516 pTmp = pGen->pEnd;
2517 pGen->pEnd = pEnd;
2518 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
2519 jx9VmSetByteCodeContainer(pGen->pVm, &pExpr->aByteCode);
2520 rc = jx9CompileExpr(&(*pGen), 0, 0);
2521 /* Emit the done instruction */
2522 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
2523 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
2524 /* Update token stream */
2525 pGen->pIn = pEnd;
2526 pGen->pEnd = pTmp;
2527 if( rc == SXERR_ABORT ){
2528 return SXERR_ABORT;
2529 }
2530 return SXRET_OK;
2531}
2532/*
2533 * Compile the smart switch statement.
2534 * According to the JX9 language reference manual
2535 * The switch statement is similar to a series of IF statements on the same expression.
2536 * In many occasions, you may want to compare the same variable (or expression) with many
2537 * different values, and execute a different piece of code depending on which value it equals to.
2538 * This is exactly what the switch statement is for.
2539 * Note: Note that unlike some other languages, the continue statement applies to switch and acts
2540 * similar to break. If you have a switch inside a loop and wish to continue to the next iteration
2541 * of the outer loop, use continue 2.
2542 * Note that switch/case does loose comparision.
2543 * It is important to understand how the switch statement is executed in order to avoid mistakes.
2544 * The switch statement executes line by line (actually, statement by statement).
2545 * In the beginning, no code is executed. Only when a case statement is found with a value that
2546 * matches the value of the switch expression does JX9 begin to execute the statements.
2547 * JX9 continues to execute the statements until the end of the switch block, or the first time
2548 * it sees a break statement. If you don't write a break statement at the end of a case's statement list.
2549 * In a switch statement, the condition is evaluated only once and the result is compared to each
2550 * case statement. In an elseif statement, the condition is evaluated again. If your condition
2551 * is more complicated than a simple compare and/or is in a tight loop, a switch may be faster.
2552 * The statement list for a case can also be empty, which simply passes control into the statement
2553 * list for the next case.
2554 * The case expression may be any expression that evaluates to a simple type, that is, integer
2555 * or floating-point numbers and strings.
2556 */
2557static sxi32 jx9CompileSwitch(jx9_gen_state *pGen)
2558{
2559 GenBlock *pSwitchBlock;
2560 SyToken *pTmp, *pEnd;
2561 jx9_switch *pSwitch;
2562 sxu32 nLine;
2563 sxi32 rc;
2564 nLine = pGen->pIn->nLine;
2565 /* Jump the 'switch' keyword */
2566 pGen->pIn++;
2567 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
2568 /* Syntax error */
2569 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after 'switch' keyword");
2570 if( rc == SXERR_ABORT ){
2571 /* Error count limit reached, abort immediately */
2572 return SXERR_ABORT;
2573 }
2574 goto Synchronize;
2575 }
2576 /* Jump the left parenthesis '(' */
2577 pGen->pIn++;
2578 pEnd = 0; /* cc warning */
2579 /* Create the loop block */
2580 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_LOOP|GEN_BLOCK_SWITCH,
2581 jx9VmInstrLength(pGen->pVm), 0, &pSwitchBlock);
2582 if( rc != SXRET_OK ){
2583 return SXERR_ABORT;
2584 }
2585 /* Delimit the condition */
2586 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
2587 if( pGen->pIn == pEnd || pEnd >= pGen->pEnd ){
2588 /* Empty expression */
2589 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected expression after 'switch' keyword");
2590 if( rc == SXERR_ABORT ){
2591 /* Error count limit reached, abort immediately */
2592 return SXERR_ABORT;
2593 }
2594 }
2595 /* Swap token streams */
2596 pTmp = pGen->pEnd;
2597 pGen->pEnd = pEnd;
2598 /* Compile the expression */
2599 rc = jx9CompileExpr(&(*pGen), 0, 0);
2600 if( rc == SXERR_ABORT ){
2601 /* Expression handler request an operation abort [i.e: Out-of-memory] */
2602 return SXERR_ABORT;
2603 }
2604 /* Update token stream */
2605 while(pGen->pIn < pEnd ){
2606 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine,
2607 "Switch: Unexpected token '%z'", &pGen->pIn->sData);
2608 if( rc == SXERR_ABORT ){
2609 return SXERR_ABORT;
2610 }
2611 pGen->pIn++;
2612 }
2613 pGen->pIn = &pEnd[1];
2614 pGen->pEnd = pTmp;
2615 if( pGen->pIn >= pGen->pEnd || &pGen->pIn[1] >= pGen->pEnd ||
2616 (pGen->pIn->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_COLON/*:*/)) == 0 ){
2617 pTmp = pGen->pIn;
2618 if( pTmp >= pGen->pEnd ){
2619 pTmp--;
2620 }
2621 /* Unexpected token */
2622 rc = jx9GenCompileError(&(*pGen), E_ERROR, pTmp->nLine, "Switch: Unexpected token '%z'", &pTmp->sData);
2623 if( rc == SXERR_ABORT ){
2624 return SXERR_ABORT;
2625 }
2626 goto Synchronize;
2627 }
2628 pGen->pIn++; /* Jump the leading curly braces/colons */
2629 /* Create the switch blocks container */
2630 pSwitch = (jx9_switch *)SyMemBackendAlloc(&pGen->pVm->sAllocator, sizeof(jx9_switch));
2631 if( pSwitch == 0 ){
2632 /* Abort compilation */
2633 return GenStateOutOfMem(pGen);
2634 }
2635 /* Zero the structure */
2636 SyZero(pSwitch, sizeof(jx9_switch));
2637 /* Initialize fields */
2638 SySetInit(&pSwitch->aCaseExpr, &pGen->pVm->sAllocator, sizeof(jx9_case_expr));
2639 /* Emit the switch instruction */
2640 jx9VmEmitInstr(pGen->pVm, JX9_OP_SWITCH, 0, 0, pSwitch, 0);
2641 /* Compile case blocks */
2642 for(;;){
2643 sxu32 nKwrd;
2644 if( pGen->pIn >= pGen->pEnd ){
2645 /* No more input to process */
2646 break;
2647 }
2648 if( (pGen->pIn->nType & JX9_TK_KEYWORD) == 0 ){
2649 if( (pGen->pIn->nType & JX9_TK_CCB /*}*/) == 0 ){
2650 /* Unexpected token */
2651 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
2652 &pGen->pIn->sData);
2653 if( rc == SXERR_ABORT ){
2654 return SXERR_ABORT;
2655 }
2656 /* FALL THROUGH */
2657 }
2658 /* Block compiled */
2659 break;
2660 }
2661 /* Extract the keyword */
2662 nKwrd = SX_PTR_TO_INT(pGen->pIn->pUserData);
2663 if( nKwrd == JX9_TKWRD_DEFAULT ){
2664 /*
2665 * Accroding to the JX9 language reference manual
2666 * A special case is the default case. This case matches anything
2667 * that wasn't matched by the other cases.
2668 */
2669 if( pSwitch->nDefault > 0 ){
2670 /* Default case already compiled */
2671 rc = jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine, "Switch: 'default' case already compiled");
2672 if( rc == SXERR_ABORT ){
2673 return SXERR_ABORT;
2674 }
2675 }
2676 pGen->pIn++; /* Jump the 'default' keyword */
2677 /* Compile the default block */
2678 rc = GenStateCompileSwitchBlock(pGen,&pSwitch->nDefault);
2679 if( rc == SXERR_ABORT){
2680 return SXERR_ABORT;
2681 }else if( rc == SXERR_EOF ){
2682 break;
2683 }
2684 }else if( nKwrd == JX9_TKWRD_CASE ){
2685 jx9_case_expr sCase;
2686 /* Standard case block */
2687 pGen->pIn++; /* Jump the 'case' keyword */
2688 /* initialize the structure */
2689 SySetInit(&sCase.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
2690 /* Compile the case expression */
2691 rc = GenStateCompileCaseExpr(pGen, &sCase);
2692 if( rc == SXERR_ABORT ){
2693 return SXERR_ABORT;
2694 }
2695 /* Compile the case block */
2696 rc = GenStateCompileSwitchBlock(pGen,&sCase.nStart);
2697 /* Insert in the switch container */
2698 SySetPut(&pSwitch->aCaseExpr, (const void *)&sCase);
2699 if( rc == SXERR_ABORT){
2700 return SXERR_ABORT;
2701 }else if( rc == SXERR_EOF ){
2702 break;
2703 }
2704 }else{
2705 /* Unexpected token */
2706 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Switch: Unexpected token '%z'",
2707 &pGen->pIn->sData);
2708 if( rc == SXERR_ABORT ){
2709 return SXERR_ABORT;
2710 }
2711 break;
2712 }
2713 }
2714 /* Fix all jumps now the destination is resolved */
2715 pSwitch->nOut = jx9VmInstrLength(pGen->pVm);
2716 GenStateFixJumps(pSwitchBlock, -1, jx9VmInstrLength(pGen->pVm));
2717 /* Release the loop block */
2718 GenStateLeaveBlock(pGen, 0);
2719 if( pGen->pIn < pGen->pEnd ){
2720 /* Jump the trailing curly braces */
2721 pGen->pIn++;
2722 }
2723 /* Statement successfully compiled */
2724 return SXRET_OK;
2725Synchronize:
2726 /* Synchronize with the first semi-colon */
2727 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) == 0 ){
2728 pGen->pIn++;
2729 }
2730 return SXRET_OK;
2731}
2732/*
2733 * Process default argument values. That is, a function may define C++-style default value
2734 * as follows:
2735 * function makecoffee($type = "cappuccino")
2736 * {
2737 * return "Making a cup of $type.\n";
2738 * }
2739 * Some features:
2740 * 1 -) Default arguments value can be any complex expression [i.e: function call, annynoymous
2741 * functions, array member, ..]
2742 * 2 -) Full type hinting: (Arguments are automatically casted to the desired type)
2743 * Example:
2744 * function a(string $a){} function b(int $a, string $c, float $d){}
2745 * 3 -) Function overloading!!
2746 * Example:
2747 * function foo($a) {
2748 * return $a.JX9_EOL;
2749 * }
2750 * function foo($a, $b) {
2751 * return $a + $b;
2752 * }
2753 * print foo(5); // Prints "5"
2754 * print foo(5, 2); // Prints "7"
2755 * // Same arg
2756 * function foo(string $a)
2757 * {
2758 * print "a is a string\n";
2759 * dump($a);
2760 * }
2761 * function foo(int $a)
2762 * {
2763 * print "a is integer\n";
2764 * dump($a);
2765 * }
2766 * function foo(array $a)
2767 * {
2768 * print "a is an array\n";
2769 * dump($a);
2770 * }
2771 * foo('This is a great feature'); // a is a string [first foo]
2772 * foo(52); // a is integer [second foo]
2773 * foo(array(14, __TIME__, __DATE__)); // a is an array [third foo]
2774 * Please refer to the official documentation for more information on the powerful extension
2775 * introduced by the JX9 engine.
2776 */
2777static sxi32 GenStateProcessArgValue(jx9_gen_state *pGen, jx9_vm_func_arg *pArg, SyToken *pIn, SyToken *pEnd)
2778{
2779 SyToken *pTmpIn, *pTmpEnd;
2780 SySet *pInstrContainer;
2781 sxi32 rc;
2782 /* Swap token stream */
2783 SWAP_DELIMITER(pGen, pIn, pEnd);
2784 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
2785 jx9VmSetByteCodeContainer(pGen->pVm, &pArg->aByteCode);
2786 /* Compile the expression holding the argument value */
2787 rc = jx9CompileExpr(&(*pGen), 0, 0);
2788 /* Emit the done instruction */
2789 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, (rc != SXERR_EMPTY ? 1 : 0), 0, 0, 0);
2790 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
2791 RE_SWAP_DELIMITER(pGen);
2792 if( rc == SXERR_ABORT ){
2793 return SXERR_ABORT;
2794 }
2795 return SXRET_OK;
2796}
2797/*
2798 * Collect function arguments one after one.
2799 * According to the JX9 language reference manual.
2800 * Information may be passed to functions via the argument list, which is a comma-delimited
2801 * list of expressions.
2802 * JX9 supports passing arguments by value (the default), passing by reference
2803 * and default argument values. Variable-length argument lists are also supported,
2804 * see also the function references for func_num_args(), func_get_arg(), and func_get_args()
2805 * for more information.
2806 * Example #1 Passing arrays to functions
2807 * <?jx9
2808 * function takes_array($input)
2809 * {
2810 * print "$input[0] + $input[1] = ", $input[0]+$input[1];
2811 * }
2812 * ?>
2813 * Making arguments be passed by reference
2814 * By default, function arguments are passed by value (so that if the value of the argument
2815 * within the function is changed, it does not get changed outside of the function).
2816 * To allow a function to modify its arguments, they must be passed by reference.
2817 * To have an argument to a function always passed by reference, prepend an ampersand (&)
2818 * to the argument name in the function definition:
2819 * Example #2 Passing function parameters by reference
2820 * <?jx9
2821 * function add_some_extra(&$string)
2822 * {
2823 * $string .= 'and something extra.';
2824 * }
2825 * $str = 'This is a string, ';
2826 * add_some_extra($str);
2827 * print $str; // outputs 'This is a string, and something extra.'
2828 * ?>
2829 *
2830 * JX9 have introduced powerful extension including full type hinting, function overloading
2831 * complex agrument values.Please refer to the official documentation for more information
2832 * on these extension.
2833 */
2834static sxi32 GenStateCollectFuncArgs(jx9_vm_func *pFunc, jx9_gen_state *pGen, SyToken *pEnd)
2835{
2836 jx9_vm_func_arg sArg; /* Current processed argument */
2837 SyToken *pCur, *pIn; /* Token stream */
2838 SyBlob sSig; /* Function signature */
2839 char *zDup; /* Copy of argument name */
2840 sxi32 rc;
2841
2842 pIn = pGen->pIn;
2843 pCur = 0;
2844 SyBlobInit(&sSig, &pGen->pVm->sAllocator);
2845 /* Process arguments one after one */
2846 for(;;){
2847 if( pIn >= pEnd ){
2848 /* No more arguments to process */
2849 break;
2850 }
2851 SyZero(&sArg, sizeof(jx9_vm_func_arg));
2852 SySetInit(&sArg.aByteCode, &pGen->pVm->sAllocator, sizeof(VmInstr));
2853 if( pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD) ){
2854 if( pIn->nType & JX9_TK_KEYWORD ){
2855 sxu32 nKey = (sxu32)(SX_PTR_TO_INT(pIn->pUserData));
2856 if( nKey & JX9_TKWRD_BOOL ){
2857 sArg.nType = MEMOBJ_BOOL;
2858 }else if( nKey & JX9_TKWRD_INT ){
2859 sArg.nType = MEMOBJ_INT;
2860 }else if( nKey & JX9_TKWRD_STRING ){
2861 sArg.nType = MEMOBJ_STRING;
2862 }else if( nKey & JX9_TKWRD_FLOAT ){
2863 sArg.nType = MEMOBJ_REAL;
2864 }else{
2865 jx9GenCompileError(&(*pGen), E_WARNING, pGen->pIn->nLine,
2866 "Invalid argument type '%z', Automatic cast will not be performed",
2867 &pIn->sData);
2868 }
2869 }
2870 pIn++;
2871 }
2872 if( pIn >= pEnd ){
2873 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Missing argument name");
2874 return rc;
2875 }
2876 if( pIn >= pEnd || (pIn->nType & JX9_TK_DOLLAR) == 0 || &pIn[1] >= pEnd || (pIn[1].nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
2877 /* Invalid argument */
2878 rc = jx9GenCompileError(&(*pGen), E_ERROR, pGen->pIn->nLine, "Invalid argument name");
2879 return rc;
2880 }
2881 pIn++; /* Jump the dollar sign */
2882 /* Copy argument name */
2883 zDup = SyMemBackendStrDup(&pGen->pVm->sAllocator, SyStringData(&pIn->sData), SyStringLength(&pIn->sData));
2884 if( zDup == 0 ){
2885 return GenStateOutOfMem(pGen);
2886 }
2887 SyStringInitFromBuf(&sArg.sName, zDup, SyStringLength(&pIn->sData));
2888 pIn++;
2889 if( pIn < pEnd ){
2890 if( pIn->nType & JX9_TK_EQUAL ){
2891 SyToken *pDefend;
2892 sxi32 iNest = 0;
2893 pIn++; /* Jump the equal sign */
2894 pDefend = pIn;
2895 /* Process the default value associated with this argument */
2896 while( pDefend < pEnd ){
2897 if( (pDefend->nType & JX9_TK_COMMA) && iNest <= 0 ){
2898 break;
2899 }
2900 if( pDefend->nType & (JX9_TK_LPAREN/*'('*/|JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*[*/) ){
2901 /* Increment nesting level */
2902 iNest++;
2903 }else if( pDefend->nType & (JX9_TK_RPAREN/*')'*/|JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*]*/) ){
2904 /* Decrement nesting level */
2905 iNest--;
2906 }
2907 pDefend++;
2908 }
2909 if( pIn >= pDefend ){
2910 rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Missing argument default value");
2911 return rc;
2912 }
2913 /* Process default value */
2914 rc = GenStateProcessArgValue(&(*pGen), &sArg, pIn, pDefend);
2915 if( rc != SXRET_OK ){
2916 return rc;
2917 }
2918 /* Point beyond the default value */
2919 pIn = pDefend;
2920 }
2921 if( pIn < pEnd && (pIn->nType & JX9_TK_COMMA) == 0 ){
2922 rc = jx9GenCompileError(&(*pGen), E_ERROR, pIn->nLine, "Unexpected token '%z'", &pIn->sData);
2923 return rc;
2924 }
2925 pIn++; /* Jump the trailing comma */
2926 }
2927 /* Append argument signature */
2928 if( sArg.nType > 0 ){
2929 int c;
2930 c = 'n'; /* cc warning */
2931 /* Type leading character */
2932 switch(sArg.nType){
2933 case MEMOBJ_HASHMAP:
2934 /* Hashmap aka 'array' */
2935 c = 'h';
2936 break;
2937 case MEMOBJ_INT:
2938 /* Integer */
2939 c = 'i';
2940 break;
2941 case MEMOBJ_BOOL:
2942 /* Bool */
2943 c = 'b';
2944 break;
2945 case MEMOBJ_REAL:
2946 /* Float */
2947 c = 'f';
2948 break;
2949 case MEMOBJ_STRING:
2950 /* String */
2951 c = 's';
2952 break;
2953 default:
2954 break;
2955 }
2956 SyBlobAppend(&sSig, (const void *)&c, sizeof(char));
2957 }
2958 /* Save in the argument set */
2959 SySetPut(&pFunc->aArgs, (const void *)&sArg);
2960 }
2961 if( SyBlobLength(&sSig) > 0 ){
2962 /* Save function signature */
2963 SyStringInitFromBuf(&pFunc->sSignature, SyBlobData(&sSig), SyBlobLength(&sSig));
2964 }
2965 return SXRET_OK;
2966}
2967/*
2968 * Compile function [i.e: standard function, annonymous function or closure ] body.
2969 * Return SXRET_OK on success. Any other return value indicates failure
2970 * and this routine takes care of generating the appropriate error message.
2971 */
2972static sxi32 GenStateCompileFuncBody(
2973 jx9_gen_state *pGen, /* Code generator state */
2974 jx9_vm_func *pFunc /* Function state */
2975 )
2976{
2977 SySet *pInstrContainer; /* Instruction container */
2978 GenBlock *pBlock;
2979 sxi32 rc;
2980 /* Attach the new function */
2981 rc = GenStateEnterBlock(&(*pGen), GEN_BLOCK_PROTECTED|GEN_BLOCK_FUNC,jx9VmInstrLength(pGen->pVm), pFunc, &pBlock);
2982 if( rc != SXRET_OK ){
2983 return GenStateOutOfMem(pGen);
2984 }
2985 /* Swap bytecode containers */
2986 pInstrContainer = jx9VmGetByteCodeContainer(pGen->pVm);
2987 jx9VmSetByteCodeContainer(pGen->pVm, &pFunc->aByteCode);
2988 /* Compile the body */
2989 jx9CompileBlock(&(*pGen));
2990 /* Emit the final return if not yet done */
2991 jx9VmEmitInstr(pGen->pVm, JX9_OP_DONE, 0, 0, 0, 0);
2992 /* Restore the default container */
2993 jx9VmSetByteCodeContainer(pGen->pVm, pInstrContainer);
2994 /* Leave function block */
2995 GenStateLeaveBlock(&(*pGen), 0);
2996 if( rc == SXERR_ABORT ){
2997 /* Don't worry about freeing memory, everything will be released shortly */
2998 return SXERR_ABORT;
2999 }
3000 /* All done, function body compiled */
3001 return SXRET_OK;
3002}
3003/*
3004 * Compile a JX9 function whether is a Standard or Annonymous function.
3005 * According to the JX9 language reference manual.
3006 * Function names follow the same rules as other labels in JX9. A valid function name
3007 * starts with a letter or underscore, followed by any number of letters, numbers, or
3008 * underscores. As a regular expression, it would be expressed thus:
3009 * [a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*.
3010 * Functions need not be defined before they are referenced.
3011 * All functions and objectes in JX9 have the global scope - they can be called outside
3012 * a function even if they were defined inside and vice versa.
3013 * It is possible to call recursive functions in JX9. However avoid recursive function/method
3014 * calls with over 32-64 recursion levels.
3015 *
3016 * JX9 have introduced powerful extension including full type hinting, function overloading,
3017 * complex agrument values and more. Please refer to the official documentation for more information
3018 * on these extension.
3019 */
3020static sxi32 GenStateCompileFunc(
3021 jx9_gen_state *pGen, /* Code generator state */
3022 SyString *pName, /* Function name. NULL otherwise */
3023 sxi32 iFlags, /* Control flags */
3024 jx9_vm_func **ppFunc /* OUT: function state */
3025 )
3026{
3027 jx9_vm_func *pFunc;
3028 SyToken *pEnd;
3029 sxu32 nLine;
3030 char *zName;
3031 sxi32 rc;
3032 /* Extract line number */
3033 nLine = pGen->pIn->nLine;
3034 /* Jump the left parenthesis '(' */
3035 pGen->pIn++;
3036 /* Delimit the function signature */
3037 jx9DelimitNestedTokens(pGen->pIn, pGen->pEnd, JX9_TK_LPAREN /* '(' */, JX9_TK_RPAREN /* ')' */, &pEnd);
3038 if( pEnd >= pGen->pEnd ){
3039 /* Syntax error */
3040 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Missing ')' after function '%z' signature", pName);
3041 if( rc == SXERR_ABORT ){
3042 /* Error count limit reached, abort immediately */
3043 return SXERR_ABORT;
3044 }
3045 pGen->pIn = pGen->pEnd;
3046 return SXRET_OK;
3047 }
3048 /* Create the function state */
3049 pFunc = (jx9_vm_func *)SyMemBackendPoolAlloc(&pGen->pVm->sAllocator, sizeof(jx9_vm_func));
3050 if( pFunc == 0 ){
3051 goto OutOfMem;
3052 }
3053 /* function ID */
3054 zName = SyMemBackendStrDup(&pGen->pVm->sAllocator, pName->zString, pName->nByte);
3055 if( zName == 0 ){
3056 /* Don't worry about freeing memory, everything will be released shortly */
3057 goto OutOfMem;
3058 }
3059 /* Initialize the function state */
3060 jx9VmInitFuncState(pGen->pVm, pFunc, zName, pName->nByte, iFlags, 0);
3061 if( pGen->pIn < pEnd ){
3062 /* Collect function arguments */
3063 rc = GenStateCollectFuncArgs(pFunc, &(*pGen), pEnd);
3064 if( rc == SXERR_ABORT ){
3065 /* Don't worry about freeing memory, everything will be released shortly */
3066 return SXERR_ABORT;
3067 }
3068 }
3069 /* Compile function body */
3070 pGen->pIn = &pEnd[1];
3071 /* Compile the body */
3072 rc = GenStateCompileFuncBody(&(*pGen), pFunc);
3073 if( rc == SXERR_ABORT ){
3074 return SXERR_ABORT;
3075 }
3076 if( ppFunc ){
3077 *ppFunc = pFunc;
3078 }
3079 /* Finally register the function */
3080 rc = jx9VmInstallUserFunction(pGen->pVm, pFunc, 0);
3081 return rc;
3082 /* Fall through if something goes wrong */
3083OutOfMem:
3084 /* If the supplied memory subsystem is so sick that we are unable to allocate
3085 * a tiny chunk of memory, there is no much we can do here.
3086 */
3087 return GenStateOutOfMem(pGen);
3088}
3089/*
3090 * Compile a standard JX9 function.
3091 * Refer to the block-comment above for more information.
3092 */
3093static sxi32 jx9CompileFunction(jx9_gen_state *pGen)
3094{
3095 SyString *pName;
3096 sxi32 iFlags;
3097 sxu32 nLine;
3098 sxi32 rc;
3099
3100 nLine = pGen->pIn->nLine;
3101 pGen->pIn++; /* Jump the 'function' keyword */
3102 iFlags = 0;
3103 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & (JX9_TK_ID|JX9_TK_KEYWORD)) == 0 ){
3104 /* Invalid function name */
3105 rc = jx9GenCompileError(&(*pGen), E_ERROR, nLine, "Invalid function name");
3106 if( rc == SXERR_ABORT ){
3107 return SXERR_ABORT;
3108 }
3109 /* Sychronize with the next semi-colon or braces*/
3110 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
3111 pGen->pIn++;
3112 }
3113 return SXRET_OK;
3114 }
3115 pName = &pGen->pIn->sData;
3116 nLine = pGen->pIn->nLine;
3117 /* Jump the function name */
3118 pGen->pIn++;
3119 if( pGen->pIn >= pGen->pEnd || (pGen->pIn->nType & JX9_TK_LPAREN) == 0 ){
3120 /* Syntax error */
3121 rc = jx9GenCompileError(pGen, E_ERROR, nLine, "Expected '(' after function name '%z'", pName);
3122 if( rc == SXERR_ABORT ){
3123 /* Error count limit reached, abort immediately */
3124 return SXERR_ABORT;
3125 }
3126 /* Sychronize with the next semi-colon or '{' */
3127 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & (JX9_TK_SEMI|JX9_TK_OCB)) == 0 ){
3128 pGen->pIn++;
3129 }
3130 return SXRET_OK;
3131 }
3132 /* Compile function body */
3133 rc = GenStateCompileFunc(&(*pGen),pName,iFlags,0);
3134 return rc;
3135}
3136/*
3137 * Generate bytecode for a given expression tree.
3138 * If something goes wrong while generating bytecode
3139 * for the expression tree (A very unlikely scenario)
3140 * this function takes care of generating the appropriate
3141 * error message.
3142 */
3143static sxi32 GenStateEmitExprCode(
3144 jx9_gen_state *pGen, /* Code generator state */
3145 jx9_expr_node *pNode, /* Root of the expression tree */
3146 sxi32 iFlags /* Control flags */
3147 )
3148{
3149 VmInstr *pInstr;
3150 sxu32 nJmpIdx;
3151 sxi32 iP1 = 0;
3152 sxu32 iP2 = 0;
3153 void *p3 = 0;
3154 sxi32 iVmOp;
3155 sxi32 rc;
3156 if( pNode->xCode ){
3157 SyToken *pTmpIn, *pTmpEnd;
3158 /* Compile node */
3159 SWAP_DELIMITER(pGen, pNode->pStart, pNode->pEnd);
3160 rc = pNode->xCode(&(*pGen), iFlags);
3161 RE_SWAP_DELIMITER(pGen);
3162 return rc;
3163 }
3164 if( pNode->pOp == 0 ){
3165 jx9GenCompileError(&(*pGen), E_ERROR, pNode->pStart->nLine,
3166 "Invalid expression node, JX9 is aborting compilation");
3167 return SXERR_ABORT;
3168 }
3169 iVmOp = pNode->pOp->iVmOp;
3170 if( pNode->pOp->iOp == EXPR_OP_QUESTY ){
3171 sxu32 nJz, nJmp;
3172 /* Ternary operator require special handling */
3173 /* Phase#1: Compile the condition */
3174 rc = GenStateEmitExprCode(&(*pGen), pNode->pCond, iFlags);
3175 if( rc != SXRET_OK ){
3176 return rc;
3177 }
3178 nJz = nJmp = 0; /* cc -O6 warning */
3179 /* Phase#2: Emit the false jump */
3180 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 0, 0, 0, &nJz);
3181 if( pNode->pLeft ){
3182 /* Phase#3: Compile the 'then' expression */
3183 rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
3184 if( rc != SXRET_OK ){
3185 return rc;
3186 }
3187 }
3188 /* Phase#4: Emit the unconditional jump */
3189 jx9VmEmitInstr(pGen->pVm, JX9_OP_JMP, 0, 0, 0, &nJmp);
3190 /* Phase#5: Fix the false jump now the jump destination is resolved. */
3191 pInstr = jx9VmGetInstr(pGen->pVm, nJz);
3192 if( pInstr ){
3193 pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
3194 }
3195 /* Phase#6: Compile the 'else' expression */
3196 if( pNode->pRight ){
3197 rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
3198 if( rc != SXRET_OK ){
3199 return rc;
3200 }
3201 }
3202 if( nJmp > 0 ){
3203 /* Phase#7: Fix the unconditional jump */
3204 pInstr = jx9VmGetInstr(pGen->pVm, nJmp);
3205 if( pInstr ){
3206 pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
3207 }
3208 }
3209 /* All done */
3210 return SXRET_OK;
3211 }
3212 /* Generate code for the left tree */
3213 if( pNode->pLeft ){
3214 if( iVmOp == JX9_OP_CALL ){
3215 jx9_expr_node **apNode;
3216 sxi32 n;
3217 /* Recurse and generate bytecodes for function arguments */
3218 apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
3219 /* Read-only load */
3220 iFlags |= EXPR_FLAG_RDONLY_LOAD;
3221 for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
3222 rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
3223 if( rc != SXRET_OK ){
3224 return rc;
3225 }
3226 }
3227 /* Total number of given arguments */
3228 iP1 = (sxi32)SySetUsed(&pNode->aNodeArgs);
3229 /* Remove stale flags now */
3230 iFlags &= ~EXPR_FLAG_RDONLY_LOAD;
3231 }
3232 rc = GenStateEmitExprCode(&(*pGen), pNode->pLeft, iFlags);
3233 if( rc != SXRET_OK ){
3234 return rc;
3235 }
3236 if( iVmOp == JX9_OP_CALL ){
3237 pInstr = jx9VmPeekInstr(pGen->pVm);
3238 if( pInstr ){
3239 if ( pInstr->iOp == JX9_OP_LOADC ){
3240 /* Prevent constant expansion */
3241 pInstr->iP1 = 0;
3242 }else if( pInstr->iOp == JX9_OP_MEMBER /* $a.b(1, 2, 3) */ ){
3243 /* Annonymous function call, flag that */
3244 pInstr->iP2 = 1;
3245 }
3246 }
3247 }else if( iVmOp == JX9_OP_LOAD_IDX ){
3248 jx9_expr_node **apNode;
3249 sxi32 n;
3250 /* Recurse and generate bytecodes for array index */
3251 apNode = (jx9_expr_node **)SySetBasePtr(&pNode->aNodeArgs);
3252 for( n = 0 ; n < (sxi32)SySetUsed(&pNode->aNodeArgs) ; ++n ){
3253 rc = GenStateEmitExprCode(&(*pGen), apNode[n], iFlags&~EXPR_FLAG_LOAD_IDX_STORE);
3254 if( rc != SXRET_OK ){
3255 return rc;
3256 }
3257 }
3258 if( SySetUsed(&pNode->aNodeArgs) > 0 ){
3259 iP1 = 1; /* Node have an index associated with it */
3260 }
3261 if( iFlags & EXPR_FLAG_LOAD_IDX_STORE ){
3262 /* Create an empty entry when the desired index is not found */
3263 iP2 = 1;
3264 }
3265 }else if( pNode->pOp->iOp == EXPR_OP_COMMA ){
3266 /* POP the left node */
3267 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
3268 }
3269 }
3270 rc = SXRET_OK;
3271 nJmpIdx = 0;
3272 /* Generate code for the right tree */
3273 if( pNode->pRight ){
3274 if( iVmOp == JX9_OP_LAND ){
3275 /* Emit the false jump so we can short-circuit the logical and */
3276 jx9VmEmitInstr(pGen->pVm, JX9_OP_JZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
3277 }else if (iVmOp == JX9_OP_LOR ){
3278 /* Emit the true jump so we can short-circuit the logical or*/
3279 jx9VmEmitInstr(pGen->pVm, JX9_OP_JNZ, 1/* Keep the value on the stack */, 0, 0, &nJmpIdx);
3280 }else if( pNode->pOp->iPrec == 18 /* Combined binary operators [i.e: =, '.=', '+=', *=' ...] precedence */ ){
3281 iFlags |= EXPR_FLAG_LOAD_IDX_STORE;
3282 }
3283 rc = GenStateEmitExprCode(&(*pGen), pNode->pRight, iFlags);
3284 if( iVmOp == JX9_OP_STORE ){
3285 pInstr = jx9VmPeekInstr(pGen->pVm);
3286 if( pInstr ){
3287 if(pInstr->iOp == JX9_OP_MEMBER ){
3288 /* Perform a member store operation [i.e: $this.x = 50] */
3289 iP2 = 1;
3290 }else{
3291 if( pInstr->iOp == JX9_OP_LOAD_IDX ){
3292 /* Transform the STORE instruction to STORE_IDX instruction */
3293 iVmOp = JX9_OP_STORE_IDX;
3294 iP1 = pInstr->iP1;
3295 }else{
3296 p3 = pInstr->p3;
3297 }
3298 /* POP the last dynamic load instruction */
3299 (void)jx9VmPopInstr(pGen->pVm);
3300 }
3301 }
3302 }
3303 }
3304 if( iVmOp > 0 ){
3305 if( iVmOp == JX9_OP_INCR || iVmOp == JX9_OP_DECR ){
3306 if( pNode->iFlags & EXPR_NODE_PRE_INCR ){
3307 /* Pre-increment/decrement operator [i.e: ++$i, --$j ] */
3308 iP1 = 1;
3309 }
3310 }
3311 /* Finally, emit the VM instruction associated with this operator */
3312 jx9VmEmitInstr(pGen->pVm, iVmOp, iP1, iP2, p3, 0);
3313 if( nJmpIdx > 0 ){
3314 /* Fix short-circuited jumps now the destination is resolved */
3315 pInstr = jx9VmGetInstr(pGen->pVm, nJmpIdx);
3316 if( pInstr ){
3317 pInstr->iP2 = jx9VmInstrLength(pGen->pVm);
3318 }
3319 }
3320 }
3321 return rc;
3322}
3323/*
3324 * Compile a JX9 expression.
3325 * According to the JX9 language reference manual:
3326 * Expressions are the most important building stones of JX9.
3327 * In JX9, almost anything you write is an expression.
3328 * The simplest yet most accurate way to define an expression
3329 * is "anything that has a value".
3330 * If something goes wrong while compiling the expression, this
3331 * function takes care of generating the appropriate error
3332 * message.
3333 */
3334static sxi32 jx9CompileExpr(
3335 jx9_gen_state *pGen, /* Code generator state */
3336 sxi32 iFlags, /* Control flags */
3337 sxi32 (*xTreeValidator)(jx9_gen_state *, jx9_expr_node *) /* Node validator callback.NULL otherwise */
3338 )
3339{
3340 jx9_expr_node *pRoot;
3341 SySet sExprNode;
3342 SyToken *pEnd;
3343 sxi32 nExpr;
3344 sxi32 iNest;
3345 sxi32 rc;
3346 /* Initialize worker variables */
3347 nExpr = 0;
3348 pRoot = 0;
3349 SySetInit(&sExprNode, &pGen->pVm->sAllocator, sizeof(jx9_expr_node *));
3350 SySetAlloc(&sExprNode, 0x10);
3351 rc = SXRET_OK;
3352 /* Delimit the expression */
3353 pEnd = pGen->pIn;
3354 iNest = 0;
3355 while( pEnd < pGen->pEnd ){
3356 if( pEnd->nType & JX9_TK_OCB /* '{' */ ){
3357 /* Ticket 1433-30: Annonymous/Closure functions body */
3358 iNest++;
3359 }else if(pEnd->nType & JX9_TK_CCB /* '}' */ ){
3360 iNest--;
3361 }else if( pEnd->nType & JX9_TK_SEMI /* ';' */ ){
3362 if( iNest <= 0 ){
3363 break;
3364 }
3365 }
3366 pEnd++;
3367 }
3368 if( iFlags & EXPR_FLAG_COMMA_STATEMENT ){
3369 SyToken *pEnd2 = pGen->pIn;
3370 iNest = 0;
3371 /* Stop at the first comma */
3372 while( pEnd2 < pEnd ){
3373 if( pEnd2->nType & (JX9_TK_OCB/*'{'*/|JX9_TK_OSB/*'['*/|JX9_TK_LPAREN/*'('*/) ){
3374 iNest++;
3375 }else if(pEnd2->nType & (JX9_TK_CCB/*'}'*/|JX9_TK_CSB/*']'*/|JX9_TK_RPAREN/*')'*/)){
3376 iNest--;
3377 }else if( pEnd2->nType & JX9_TK_COMMA /*','*/ ){
3378 if( iNest <= 0 ){
3379 break;
3380 }
3381 }
3382 pEnd2++;
3383 }
3384 if( pEnd2 <pEnd ){
3385 pEnd = pEnd2;
3386 }
3387 }
3388 if( pEnd > pGen->pIn ){
3389 SyToken *pTmp = pGen->pEnd;
3390 /* Swap delimiter */
3391 pGen->pEnd = pEnd;
3392 /* Try to get an expression tree */
3393 rc = jx9ExprMakeTree(&(*pGen), &sExprNode, &pRoot);
3394 if( rc == SXRET_OK && pRoot ){
3395 rc = SXRET_OK;
3396 if( xTreeValidator ){
3397 /* Call the upper layer validator callback */
3398 rc = xTreeValidator(&(*pGen), pRoot);
3399 }
3400 if( rc != SXERR_ABORT ){
3401 /* Generate code for the given tree */
3402 rc = GenStateEmitExprCode(&(*pGen), pRoot, iFlags);
3403 }
3404 nExpr = 1;
3405 }
3406 /* Release the whole tree */
3407 jx9ExprFreeTree(&(*pGen), &sExprNode);
3408 /* Synchronize token stream */
3409 pGen->pEnd = pTmp;
3410 pGen->pIn = pEnd;
3411 if( rc == SXERR_ABORT ){
3412 SySetRelease(&sExprNode);
3413 return SXERR_ABORT;
3414 }
3415 }
3416 SySetRelease(&sExprNode);
3417 return nExpr > 0 ? SXRET_OK : SXERR_EMPTY;
3418}
3419/*
3420 * Return a pointer to the node construct handler associated
3421 * with a given node type [i.e: string, integer, float, ...].
3422 */
3423JX9_PRIVATE ProcNodeConstruct jx9GetNodeHandler(sxu32 nNodeType)
3424{
3425 if( nNodeType & JX9_TK_NUM ){
3426 /* Numeric literal: Either real or integer */
3427 return jx9CompileNumLiteral;
3428 }else if( nNodeType & JX9_TK_DSTR ){
3429 /* Double quoted string */
3430 return jx9CompileString;
3431 }else if( nNodeType & JX9_TK_SSTR ){
3432 /* Single quoted string */
3433 return jx9CompileSimpleString;
3434 }else if( nNodeType & JX9_TK_NOWDOC ){
3435 /* Nowdoc */
3436 return jx9CompileNowdoc;
3437 }
3438 return 0;
3439}
3440/*
3441 * Jx9 Language construct table.
3442 */
3443static const LangConstruct aLangConstruct[] = {
3444 { JX9_TKWRD_IF, jx9CompileIf },
3445 { JX9_TKWRD_FUNCTION, jx9CompileFunction },
3446 { JX9_TKWRD_FOREACH, jx9CompileForeach },
3447 { JX9_TKWRD_WHILE, jx9CompileWhile },
3448 { JX9_TKWRD_FOR, jx9CompileFor },
3449 { JX9_TKWRD_SWITCH, jx9CompileSwitch },
3450 { JX9_TKWRD_DIE, jx9CompileHalt },
3451 { JX9_TKWRD_EXIT, jx9CompileHalt },
3452 { JX9_TKWRD_RETURN, jx9CompileReturn },
3453 { JX9_TKWRD_BREAK, jx9CompileBreak },
3454 { JX9_TKWRD_CONTINUE, jx9CompileContinue },
3455 { JX9_TKWRD_STATIC, jx9CompileStatic },
3456 { JX9_TKWRD_UPLINK, jx9CompileUplink },
3457 { JX9_TKWRD_CONST, jx9CompileConstant },
3458};
3459/*
3460 * Return a pointer to the statement handler routine associated
3461 * with a given JX9 keyword [i.e: if, for, while, ...].
3462 */
3463static ProcLangConstruct GenStateGetStatementHandler(
3464 sxu32 nKeywordID /* Keyword ID*/
3465 )
3466{
3467 sxu32 n = 0;
3468 for(;;){
3469 if( n >= SX_ARRAYSIZE(aLangConstruct) ){
3470 break;
3471 }
3472 if( aLangConstruct[n].nID == nKeywordID ){
3473 /* Return a pointer to the handler.
3474 */
3475 return aLangConstruct[n].xConstruct;
3476 }
3477 n++;
3478 }
3479 /* Not a language construct */
3480 return 0;
3481}
3482/*
3483 * Compile a jx9 program.
3484 * If something goes wrong while compiling the Jx9 chunk, this function
3485 * takes care of generating the appropriate error message.
3486 */
3487static sxi32 GenStateCompileChunk(
3488 jx9_gen_state *pGen, /* Code generator state */
3489 sxi32 iFlags /* Compile flags */
3490 )
3491{
3492 ProcLangConstruct xCons;
3493 sxi32 rc;
3494 rc = SXRET_OK; /* Prevent compiler warning */
3495 for(;;){
3496 if( pGen->pIn >= pGen->pEnd ){
3497 /* No more input to process */
3498 break;
3499 }
3500 xCons = 0;
3501 if( pGen->pIn->nType & JX9_TK_KEYWORD ){
3502 sxu32 nKeyword = (sxu32)SX_PTR_TO_INT(pGen->pIn->pUserData);
3503 /* Try to extract a language construct handler */
3504 xCons = GenStateGetStatementHandler(nKeyword);
3505 if( xCons == 0 && !jx9IsLangConstruct(nKeyword) ){
3506 rc = jx9GenCompileError(pGen, E_ERROR, pGen->pIn->nLine,
3507 "Syntax error: Unexpected keyword '%z'",
3508 &pGen->pIn->sData);
3509 if( rc == SXERR_ABORT ){
3510 break;
3511 }
3512 /* Synchronize with the first semi-colon and avoid compiling
3513 * this erroneous statement.
3514 */
3515 xCons = jx9ErrorRecover;
3516 }
3517 }
3518 if( xCons == 0 ){
3519 /* Assume an expression an try to compile it */
3520 rc = jx9CompileExpr(&(*pGen), 0, 0);
3521 if( rc != SXERR_EMPTY ){
3522 /* Pop l-value */
3523 jx9VmEmitInstr(pGen->pVm, JX9_OP_POP, 1, 0, 0, 0);
3524 }
3525 }else{
3526 /* Go compile the sucker */
3527 rc = xCons(&(*pGen));
3528 }
3529 if( rc == SXERR_ABORT ){
3530 /* Request to abort compilation */
3531 break;
3532 }
3533 /* Ignore trailing semi-colons ';' */
3534 while( pGen->pIn < pGen->pEnd && (pGen->pIn->nType & JX9_TK_SEMI) ){
3535 pGen->pIn++;
3536 }
3537 if( iFlags & JX9_COMPILE_SINGLE_STMT ){
3538 /* Compile a single statement and return */
3539 break;
3540 }
3541 /* LOOP ONE */
3542 /* LOOP TWO */
3543 /* LOOP THREE */
3544 /* LOOP FOUR */
3545 }
3546 /* Return compilation status */
3547 return rc;
3548}
3549/*
3550 * Compile a raw chunk. The raw chunk can contain JX9 code embedded
3551 * in HTML, XML and so on. This function handle all the stuff.
3552 * This is the only compile interface exported from this file.
3553 */
3554JX9_PRIVATE sxi32 jx9CompileScript(
3555 jx9_vm *pVm, /* Generate JX9 bytecodes for this Virtual Machine */
3556 SyString *pScript, /* Script to compile */
3557 sxi32 iFlags /* Compile flags */
3558 )
3559{
3560 jx9_gen_state *pGen;
3561 SySet aToken;
3562 sxi32 rc;
3563 if( pScript->nByte < 1 ){
3564 /* Nothing to compile */
3565 return JX9_OK;
3566 }
3567 /* Initialize the tokens containers */
3568 SySetInit(&aToken, &pVm->sAllocator, sizeof(SyToken));
3569 SySetAlloc(&aToken, 0xc0);
3570 pGen = &pVm->sCodeGen;
3571 rc = JX9_OK;
3572 /* Tokenize the JX9 chunk first */
3573 jx9Tokenize(pScript->zString,pScript->nByte,&aToken);
3574 if( SySetUsed(&aToken) < 1 ){
3575 return SXERR_EMPTY;
3576 }
3577 /* Point to the head and tail of the token stream. */
3578 pGen->pIn = (SyToken *)SySetBasePtr(&aToken);
3579 pGen->pEnd = &pGen->pIn[SySetUsed(&aToken)];
3580 /* Compile the chunk */
3581 rc = GenStateCompileChunk(pGen,iFlags);
3582 /* Cleanup */
3583 SySetRelease(&aToken);
3584 return rc;
3585}
3586/*
3587 * Utility routines.Initialize the code generator.
3588 */
3589JX9_PRIVATE sxi32 jx9InitCodeGenerator(
3590 jx9_vm *pVm, /* Target VM */
3591 ProcConsumer xErr, /* Error log consumer callabck */
3592 void *pErrData /* Last argument to xErr() */
3593 )
3594{
3595 jx9_gen_state *pGen = &pVm->sCodeGen;
3596 /* Zero the structure */
3597 SyZero(pGen, sizeof(jx9_gen_state));
3598 /* Initial state */
3599 pGen->pVm = &(*pVm);
3600 pGen->xErr = xErr;
3601 pGen->pErrData = pErrData;
3602 SyHashInit(&pGen->hLiteral, &pVm->sAllocator, 0, 0);
3603 SyHashInit(&pGen->hVar, &pVm->sAllocator, 0, 0);
3604 /* Create the global scope */
3605 GenStateInitBlock(pGen, &pGen->sGlobal,GEN_BLOCK_GLOBAL,jx9VmInstrLength(&(*pVm)), 0);
3606 /* Point to the global scope */
3607 pGen->pCurrent = &pGen->sGlobal;
3608 return SXRET_OK;
3609}
3610/*
3611 * Utility routines. Reset the code generator to it's initial state.
3612 */
3613JX9_PRIVATE sxi32 jx9ResetCodeGenerator(
3614 jx9_vm *pVm, /* Target VM */
3615 ProcConsumer xErr, /* Error log consumer callabck */
3616 void *pErrData /* Last argument to xErr() */
3617 )
3618{
3619 jx9_gen_state *pGen = &pVm->sCodeGen;
3620 GenBlock *pBlock, *pParent;
3621 /* Point to the global scope */
3622 pBlock = pGen->pCurrent;
3623 while( pBlock->pParent != 0 ){
3624 pParent = pBlock->pParent;
3625 GenStateFreeBlock(pBlock);
3626 pBlock = pParent;
3627 }
3628 pGen->xErr = xErr;
3629 pGen->pErrData = pErrData;
3630 pGen->pCurrent = &pGen->sGlobal;
3631 pGen->pIn = pGen->pEnd = 0;
3632 pGen->nErr = 0;
3633 return SXRET_OK;
3634}
3635/*
3636 * Generate a compile-time error message.
3637 * If the error count limit is reached (usually 15 error message)
3638 * this function return SXERR_ABORT.In that case upper-layers must
3639 * abort compilation immediately.
3640 */
3641JX9_PRIVATE sxi32 jx9GenCompileError(jx9_gen_state *pGen,sxi32 nErrType,sxu32 nLine,const char *zFormat,...)
3642{
3643 SyBlob *pWorker = &pGen->pVm->pEngine->xConf.sErrConsumer;
3644 const char *zErr = "Error";
3645 va_list ap;
3646 if( nErrType == E_ERROR ){
3647 /* Increment the error counter */
3648 pGen->nErr++;
3649 if( pGen->nErr > 15 ){
3650 /* Error count limit reached */
3651 SyBlobFormat(pWorker, "%u Error count limit reached, JX9 is aborting compilation\n", nLine);
3652 /* Abort immediately */
3653 return SXERR_ABORT;
3654 }
3655 }
3656 switch(nErrType){
3657 case E_WARNING: zErr = "Warning"; break;
3658 case E_PARSE: zErr = "Parse error"; break;
3659 case E_NOTICE: zErr = "Notice"; break;
3660 default:
3661 break;
3662 }
3663 /* Format the error message */
3664 SyBlobFormat(pWorker, "%u %s: ", nLine, zErr);
3665 va_start(ap, zFormat);
3666 SyBlobFormatAp(pWorker, zFormat, ap);
3667 va_end(ap);
3668 /* Append a new line */
3669 SyBlobAppend(pWorker, (const void *)"\n", sizeof(char));
3670 return JX9_OK;
3671}