2
PLIB - A Suite of Portable Game Libraries
3
Copyright (C) 1998,2002 Steve Baker
5
This library is free software; you can redistribute it and/or
6
modify it under the terms of the GNU Library General Public
7
License as published by the Free Software Foundation; either
8
version 2 of the License, or (at your option) any later version.
10
This library is distributed in the hope that it will be useful,
11
but WITHOUT ANY WARRANTY; without even the implied warranty of
12
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
Library General Public License for more details.
15
You should have received a copy of the GNU Library General Public
16
License along with this library; if not, write to the Free Software
17
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
For further information visit http://plib.sourceforge.net
21
$Id: pslCompiler.cxx,v 1.29 2003/01/06 05:10:13 sjbaker Exp $
29
int pslCompiler::compile ( const char *fname )
31
char filename [ 1024 ] ;
32
_pslMakeScriptPath ( filename, fname ) ;
34
FILE *fd = fopen ( filename, "ra" ) ;
39
ulSetError ( UL_WARNING, "PSL: Failed while opening '%s' for reading.",
44
return compile ( fd, fname ) ;
48
int pslCompiler::compile ( const char *memptr, const char *fname )
52
_pslPushDefaultFile ( memptr, (fname == NULL) ? progName : fname ) ;
54
_pslPopDefaultFile () ;
60
int pslCompiler::compile ( FILE *fd, const char *fname )
64
_pslPushDefaultFile ( fd, (fname == NULL) ? progName : fname ) ;
66
_pslPopDefaultFile () ;
72
int pslCompiler::cleanup ()
74
const char *dump_env = getenv ( "PSL_DUMP" ) ;
76
if ( num_errors != 0 || num_warnings != 0 )
77
fprintf ( stderr, "PSL: '%s' Compiled with %d Warnings, %d Fatal Errors\n",
78
progName, num_warnings, num_errors ) ;
80
/* If there are errors, prevent the program from running. */
82
if ( num_errors != 0 )
84
if ( dump_env != NULL &&
85
ulStrEqual ( dump_env, "on_error" ) )
89
genCodeByte ( OPCODE_HALT ) ;
92
if ( dump_env != NULL &&
93
ulStrEqual ( dump_env, "always" ) )
101
int pslCompiler::genReturnStatement ()
103
char c [ MAX_TOKEN ] ;
107
if ( c [ 0 ] == ';' ) /* Return without data == "return 0" */
110
genConstant ( "0.0" ) ;
124
/* Administer the break/continue jump addresses */
126
void pslCompiler::pushBreakToLabel ()
128
if ( next_break >= MAX_LABEL-1 )
129
error ( "Too many nested 'break' contexts" ) ;
131
breakToAddressStack [ next_break++ ] = next_tmp_label++ ;
134
void pslCompiler::pushNoContinue ()
136
continueToAddressStack [ next_continue++ ] = -1 ;
139
int pslCompiler::pushContinueToLabel ()
141
if ( next_continue >= MAX_LABEL-1 )
142
error ( "Too many nested 'continue' contexts" ) ;
144
continueToAddressStack [ next_continue++ ] = next_tmp_label++ ;
146
return next_tmp_label-1 ;
149
void pslCompiler::setContinueToLabel ( int which )
152
sprintf ( s, "L%d", which ) ;
153
setCodeSymbol ( s, next_code ) ;
156
void pslCompiler::popBreakToLabel ()
159
sprintf ( s, "L%d", breakToAddressStack[next_break-1] ) ;
160
setCodeSymbol ( s, next_code ) ;
164
void pslCompiler::popContinueToLabel ()
171
/* Implement actual break and continue statements. */
173
int pslCompiler::genBreakStatement ()
175
if ( next_break <= 0 )
176
return error ( "'break' statement is not inside a 'switch' or a loop." ) ;
179
sprintf ( s, "L%d", breakToAddressStack [ next_break-1 ] ) ;
180
genJump ( getCodeSymbol ( s, next_code+1 ) ) ;
185
int pslCompiler::genContinueStatement ()
187
if ( next_break <= 0 )
188
return error ( "'continue' statement is not inside a loop." ) ;
190
if ( continueToAddressStack [ next_continue-1 ] < 0 )
191
return error ( "'continue' statement not allowed inside a 'switch'." ) ;
194
sprintf ( s, "L%d", continueToAddressStack [ next_continue-1 ] ) ;
195
genJump ( getCodeSymbol ( s, next_code+1 ) ) ;
203
int pslCompiler::genSwitchStatement ()
205
if ( ! genExpression () )
206
return error ( "Missing control expression for 'switch'" ) ;
208
char c [ MAX_TOKEN ] ;
210
getToken ( c ) ; /* Hopefully, the word 'while' */
212
if ( c [ 0 ] != '{' )
213
return error ( "Missing '{' after 'switch'" ) ;
215
int jumpToNextCase = genJump ( 0 ) ;
216
int jumpAfterTest = 0 ;
218
pushBreakToLabel () ;
225
if ( strcmp ( c, "case" ) == 0 )
227
jumpAfterTest = genJump ( 0 ) ;
229
code [ jumpToNextCase ] = next_code & 0xFF ;
230
code [ jumpToNextCase+1 ] = ( next_code >> 8 ) & 0xFF ;
234
if ( ! genExpression () )
235
error ( "Missing expression after 'case'." ) ;
240
error ( "Missing ':' after 'case' expression." ) ;
244
jumpToNextCase = genJumpIfFalse ( 0 ) ;
246
code [ jumpAfterTest ] = next_code & 0xFF ;
247
code [ jumpAfterTest+1 ] = ( next_code >> 8 ) & 0xFF ;
250
if ( strcmp ( c, "default" ) == 0 )
252
code [ jumpToNextCase ] = next_code & 0xFF ;
253
code [ jumpToNextCase+1 ] = ( next_code >> 8 ) & 0xFF ;
258
error ( "Missing ':' after 'default'." ) ;
261
if ( strcmp ( c, "}" ) == 0 )
270
if ( ! genStatement () )
271
error ( "Missing statement within switch." ) ;
275
if ( c [ 0 ] != ';' )
276
error ( "Missing semicolon." ) ;
281
popContinueToLabel () ;
288
int pslCompiler::genDoWhileStatement ()
290
/* Remember place to jump back to */
292
int start_loc = next_code ;
294
pushBreakToLabel () ;
295
setContinueToLabel ( pushContinueToLabel () ) ;
297
if ( ! genStatement () )
298
return error ( "Missing statement for 'do/while'" ) ;
300
char c [ MAX_TOKEN ] ;
302
getToken ( c ) ; /* The final ';' of the action */
304
getToken ( c ) ; /* Hopefully, the word 'while' */
306
if ( strcmp ( c, "while" ) != 0 )
307
return error ( "Missing 'while' for 'do/while'" ) ;
309
if ( ! genExpression () )
310
return error ( "Missing expression for 'while' in a 'do/while'" ) ;
312
genJumpIfTrue ( start_loc ) ;
315
popContinueToLabel () ;
320
int pslCompiler::genForStatement ()
322
char c [ MAX_TOKEN ] ;
325
pushBreakToLabel () ;
326
int ct_lab = pushContinueToLabel () ;
328
getToken ( c ) ; /* The initial '(' of the action */
330
if ( c [ 0 ] != '(' )
333
return error ( "Missing '(' for 'for' loop" ) ;
336
if ( ! genStatement () )
339
return error ( "Missing initialiser for 'if'" ) ;
342
getToken ( c ) ; /* The ';' after the initialiser */
344
if ( c [ 0 ] != ';' )
347
return error ( "Missing ';' after 'for' loop initialisation" ) ;
350
/* Remember place to jump back to */
352
int start_loc = next_code ;
356
if ( ! genExpression () )
357
return error ( "Missing test for 'for' loop" ) ;
359
getToken ( c ) ; /* The ';' after the initialiser */
361
if ( c [ 0 ] != ';' )
364
return error ( "Missing ';' after 'for' loop test" ) ;
367
char saved [ MAX_UNGET ][ MAX_TOKEN ] ;
369
int paren_counter = 0 ;
373
getToken ( saved [ next_saved ] ) ;
375
if ( saved [ next_saved ][ 0 ] == '(' ) paren_counter++ ;
376
if ( saved [ next_saved ][ 0 ] == ')' ) paren_counter-- ;
378
if ( next_saved >= MAX_UNGET-1 )
381
return error ( "Too many tokens in 'increment' part of 'for' loop" ) ;
386
} while ( paren_counter >= 0 ) ;
388
next_saved-- ; /* Throw away the ')' */
390
int label_loc = genJumpIfFalse ( 0 ) ;
392
if ( ! genStatement () )
395
return error ( "Missing action body for 'for' loop" ) ;
398
setContinueToLabel ( ct_lab ) ;
400
getToken ( c ) ; /* Throw away the ';' */
402
/* Put the increment test back onto the token stream */
406
for ( int i = next_saved-1 ; i >= 0 ; i-- )
407
ungetToken ( saved[i] ) ;
409
if ( ! genStatement () )
412
return error ( "Missing 'increment' part of 'for' loop" ) ;
415
genJump ( start_loc ) ;
417
code [ label_loc ] = next_code & 0xFF ;
418
code [ label_loc+1 ] = ( next_code >> 8 ) & 0xFF ;
421
popContinueToLabel () ;
427
int pslCompiler::genWhileStatement ()
429
/* Remember place to jump back to */
431
pushBreakToLabel () ;
432
setContinueToLabel ( pushContinueToLabel () ) ;
434
int start_loc = next_code ;
436
if ( ! genExpression () )
437
return error ( "Missing expression for 'while'" ) ;
439
int label_loc = genJumpIfFalse ( 0 ) ;
441
if ( ! genStatement () )
442
return error ( "Missing statement for 'while'" ) ;
444
genJump ( start_loc ) ;
446
code [ label_loc ] = next_code & 0xFF ;
447
code [ label_loc+1 ] = ( next_code >> 8 ) & 0xFF ;
450
popContinueToLabel () ;
456
int pslCompiler::genIfStatement ()
458
if ( ! genExpression () )
459
return error ( "Missing expression for 'if'" ) ;
461
int else_loc = genJumpIfFalse ( 0 ) ;
463
if ( ! genStatement () )
464
return error ( "Missing statement for 'if'" ) ;
466
char c [ MAX_TOKEN ] ;
470
if ( c [ 0 ] != ';' )
473
return error ( "Missing ';' or 'else' after 'if' statement" ) ;
478
if ( strcmp ( c, "else" ) != 0 )
480
code [ else_loc ] = next_code & 0xFF ;
481
code [ else_loc+1 ] = ( next_code >> 8 ) & 0xFF ;
488
int label_loc = genJump ( 0 ) ;
490
code [ else_loc ] = next_code & 0xFF ;
491
code [ else_loc+1 ] = ( next_code >> 8 ) & 0xFF ;
493
if ( ! genStatement () )
494
return error ( "Missing statement for 'else'" ) ;
496
code [ label_loc ] = next_code & 0xFF ;
497
code [ label_loc+1 ] = ( next_code >> 8 ) & 0xFF ;
503
int pslCompiler::genFunctionCall ( const char *var )
505
char c [ MAX_TOKEN ] ;
510
'var' should be the name of a function,
515
return error ( "Missing '(' in call to '%s'", var ) ;
521
while ( c[0] != ')' )
532
return error ( "Missing ')' or ',' in call to '%s'", var ) ;
537
genCall ( var, argc ) ;
542
int pslCompiler::genCompoundStatement ()
544
char c [ MAX_TOKEN ] ;
548
while ( genStatement () )
555
return warning ( "Unexpected '%s' in Compound statement", c ) ;
570
return warning ( "Unexpected '%s' in Compound statement", c ) ;
574
int pslCompiler::genStatement ()
576
char c [ MAX_TOKEN ] ;
578
if ( generate_line_numbers )
579
genLineNumber ( _pslGetLineNo () ) ;
583
if ( strcmp ( c, "static" ) == 0 ) return genStaticVarDecl () ;
584
if ( strcmp ( c, "string" ) == 0 ) return genLocalVarDecl ( PSL_STRING) ;
585
if ( strcmp ( c, "int" ) == 0 ) return genLocalVarDecl ( PSL_INT ) ;
586
if ( strcmp ( c, "float" ) == 0 ) return genLocalVarDecl ( PSL_FLOAT ) ;
587
if ( strcmp ( c, "return" ) == 0 ) return genReturnStatement () ;
588
if ( strcmp ( c, "break" ) == 0 ) return genBreakStatement () ;
589
if ( strcmp ( c, "continue" ) == 0 ) return genContinueStatement () ;
590
if ( strcmp ( c, "pause" ) == 0 ) return genPauseStatement () ;
591
if ( strcmp ( c, "for" ) == 0 ) return genForStatement () ;
592
if ( strcmp ( c, "do" ) == 0 ) return genDoWhileStatement () ;
593
if ( strcmp ( c, "switch" ) == 0 ) return genSwitchStatement () ;
594
if ( strcmp ( c, "while" ) == 0 ) return genWhileStatement () ;
595
if ( strcmp ( c, "if" ) == 0 ) return genIfStatement () ;
596
if ( strcmp ( c, "case" ) == 0 || strcmp ( c, "default" ) == 0 )
597
return error ( "'%s' encountered - not inside 'switch' statement", c ) ;
599
if ( c [ 0 ] == '{' ) return genCompoundStatement () ;
603
if ( genExpression () )
605
genPop () ; /* Discard result */
613
void pslCompiler::genProgram ()
615
char c [ MAX_TOKEN ] ;
617
/* Compile the program */
628
genGlobalDeclaration () ;
631
/* Have the program call 'main' and then halt */
633
genIntConstant ( 0 ) ; /* No arguments to main *YET* */
635
genCodeByte ( OPCODE_CALL ) ;
636
genCodeAddr ( getCodeSymbol ( "main", next_code ) ) ;
637
genCodeByte ( 0 ) ; /* Argc */
638
genCodeByte ( OPCODE_HALT ) ;
640
checkUnresolvedSymbols () ;
645
int pslCompiler::genLocalVarDecl ( pslType t )
647
char c [ MAX_TOKEN ] ;
648
char s [ MAX_TOKEN ] ;
662
case PSL_FLOAT : genMakeFloatArray ( s ) ; break ;
663
case PSL_STRING : genMakeStringArray ( s ) ; break ;
664
default : genMakeIntArray ( s ) ; break ;
670
return error ( "Missing ']' after array declaration" ) ;
677
case PSL_FLOAT : genMakeFloatVariable ( s ) ; break ;
678
case PSL_STRING : genMakeStringVariable ( s ) ; break ;
679
default : genMakeIntVariable ( s ) ; break ;
682
if ( strcmp ( c, "=" ) == 0 )
684
genVariable ( s, FALSE ) ;
697
int pslCompiler::genStaticVarDecl ()
699
return error ( "Static Local Variables are Not Supported Yet." ) ;
704
int pslCompiler::genGlobalVarDecl ( const char *s, pslType t )
706
char c [ MAX_TOKEN ] ;
718
case PSL_FLOAT : genMakeFloatArray ( s ) ; break ;
719
case PSL_STRING : genMakeStringArray ( s ) ; break ;
720
default : genMakeIntArray ( s ) ; break ;
726
return error ( "Missing ']' after array declaration" ) ;
732
case PSL_FLOAT : genMakeFloatVariable ( s ) ; break ;
733
case PSL_STRING : genMakeStringVariable ( s ) ; break ;
734
default : genMakeIntVariable ( s ) ; break ;
737
if ( strcmp ( c, "=" ) == 0 )
739
genVariable ( s, FALSE ) ;
748
return error ( "Missing ';' after declaration of '%s'", s ) ;
755
int pslCompiler::genGlobalDeclaration ()
757
char c [ MAX_TOKEN ] ;
758
char fn [ MAX_TOKEN ] ;
762
if ( strcmp ( c, "static" ) == 0 ||
763
strcmp ( c, "extern" ) == 0 )
765
/* Something complicated should probably happen here! */
771
if ( strcmp ( c, "void" ) == 0 ) t = PSL_VOID ; else
772
if ( strcmp ( c, "int" ) == 0 ) t = PSL_INT ; else
773
if ( strcmp ( c, "float" ) == 0 ) t = PSL_FLOAT ; else
774
if ( strcmp ( c, "string" ) == 0 ) t = PSL_STRING ; else
775
return error ( "Expected declaration - but got '%s'", c ) ;
784
return genFunctionDeclaration ( fn ) ;
787
if ( c[0] == '[' || strcmp ( c, "=" ) == 0 || c[0] == ';' )
790
return genGlobalVarDecl ( fn, t ) ;
793
return error ( "Expected a declaration - but got '%s'", c);
797
int pslCompiler::genFunctionDeclaration ( const char *fn )
799
char c [ MAX_TOKEN ] ;
801
pslAddress jump_target = genJump ( 0 ) ;
803
setCodeSymbol ( fn, next_code ) ;
808
return error ( "Missing '(' in declaration of '%s'", fn ) ;
818
if ( c [ 0 ] == ')' || c [ 0 ] == '\0' )
821
char s [ MAX_TOKEN ] ;
825
pslAddress a = setVarSymbol ( s ) ;
827
if ( strcmp ( c, "int" ) == 0 ) genMakeIntVariable ( s ) ; else
828
if ( strcmp ( c, "float" ) == 0 ) genMakeFloatVariable ( s ) ; else
829
if ( strcmp ( c, "string" ) == 0 ) genMakeStringVariable ( s ) ; else
832
return error ( "Missing ')' in declaration of '%s'", fn ) ;
835
genGetParameter ( a, argpos++ ) ;
846
return error ( "Missing ',' or ')' in declaration of '%s'", fn ) ;
852
return error ( "Missing ')' in declaration of '%s'", fn ) ;
857
if ( c [ 0 ] != '{' )
860
return error ( "Missing '{' in function '%s'", fn ) ;
863
if ( ! genCompoundStatement () )
866
return error ( "Missing '}' in function '%s'", fn ) ;
871
/* If we fall off the end of the function, we still need a return value */
873
genConstant ( "0.0" ) ;
876
code [ jump_target ] = next_code & 0xFF ;
877
code [ jump_target+1 ] = (next_code >> 8) & 0xFF ;