2 ===========================================================================
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
7 This file is part of the Doom 3 GPL Source Code (?Doom 3 Source Code?).
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
26 ===========================================================================
29 #include "../../idlib/precompiled.h"
32 #include "../Game_local.h"
34 #define FUNCTION_PRIORITY 2
35 #define INT_PRIORITY 2
36 #define NOT_PRIORITY 5
37 #define TILDE_PRIORITY 5
38 #define TOP_PRIORITY 7
40 bool idCompiler::punctuationValid[ 256 ];
41 char *idCompiler::punctuation[] = {
42 "+=", "-=", "*=", "/=", "%=", "&=", "|=", "++", "--",
43 "&&", "||", "<=", ">=", "==", "!=", "::", ";", ",",
44 "~", "!", "*", "/", "%", "(", ")", "-", "+",
45 "=", "[", "]", ".", "<", ">" , "&", "|", ":", NULL
48 opcode_t idCompiler::opcodes[] = {
49 { "<RETURN>", "RETURN", -1, false, &def_void, &def_void, &def_void },
51 { "++", "UINC_F", 1, true, &def_float, &def_void, &def_void },
52 { "++", "UINCP_F", 1, true, &def_object, &def_field, &def_float },
53 { "--", "UDEC_F", 1, true, &def_float, &def_void, &def_void },
54 { "--", "UDECP_F", 1, true, &def_object, &def_field, &def_float },
56 { "~", "COMP_F", -1, false, &def_float, &def_void, &def_float },
58 { "*", "MUL_F", 3, false, &def_float, &def_float, &def_float },
59 { "*", "MUL_V", 3, false, &def_vector, &def_vector, &def_float },
60 { "*", "MUL_FV", 3, false, &def_float, &def_vector, &def_vector },
61 { "*", "MUL_VF", 3, false, &def_vector, &def_float, &def_vector },
63 { "/", "DIV", 3, false, &def_float, &def_float, &def_float },
64 { "%", "MOD_F", 3, false, &def_float, &def_float, &def_float },
66 { "+", "ADD_F", 4, false, &def_float, &def_float, &def_float },
67 { "+", "ADD_V", 4, false, &def_vector, &def_vector, &def_vector },
68 { "+", "ADD_S", 4, false, &def_string, &def_string, &def_string },
69 { "+", "ADD_FS", 4, false, &def_float, &def_string, &def_string },
70 { "+", "ADD_SF", 4, false, &def_string, &def_float, &def_string },
71 { "+", "ADD_VS", 4, false, &def_vector, &def_string, &def_string },
72 { "+", "ADD_SV", 4, false, &def_string, &def_vector, &def_string },
74 { "-", "SUB_F", 4, false, &def_float, &def_float, &def_float },
75 { "-", "SUB_V", 4, false, &def_vector, &def_vector, &def_vector },
77 { "==", "EQ_F", 5, false, &def_float, &def_float, &def_float },
78 { "==", "EQ_V", 5, false, &def_vector, &def_vector, &def_float },
79 { "==", "EQ_S", 5, false, &def_string, &def_string, &def_float },
80 { "==", "EQ_E", 5, false, &def_entity, &def_entity, &def_float },
81 { "==", "EQ_EO", 5, false, &def_entity, &def_object, &def_float },
82 { "==", "EQ_OE", 5, false, &def_object, &def_entity, &def_float },
83 { "==", "EQ_OO", 5, false, &def_object, &def_object, &def_float },
85 { "!=", "NE_F", 5, false, &def_float, &def_float, &def_float },
86 { "!=", "NE_V", 5, false, &def_vector, &def_vector, &def_float },
87 { "!=", "NE_S", 5, false, &def_string, &def_string, &def_float },
88 { "!=", "NE_E", 5, false, &def_entity, &def_entity, &def_float },
89 { "!=", "NE_EO", 5, false, &def_entity, &def_object, &def_float },
90 { "!=", "NE_OE", 5, false, &def_object, &def_entity, &def_float },
91 { "!=", "NE_OO", 5, false, &def_object, &def_object, &def_float },
93 { "<=", "LE", 5, false, &def_float, &def_float, &def_float },
94 { ">=", "GE", 5, false, &def_float, &def_float, &def_float },
95 { "<", "LT", 5, false, &def_float, &def_float, &def_float },
96 { ">", "GT", 5, false, &def_float, &def_float, &def_float },
98 { ".", "INDIRECT_F", 1, false, &def_object, &def_field, &def_float },
99 { ".", "INDIRECT_V", 1, false, &def_object, &def_field, &def_vector },
100 { ".", "INDIRECT_S", 1, false, &def_object, &def_field, &def_string },
101 { ".", "INDIRECT_E", 1, false, &def_object, &def_field, &def_entity },
102 { ".", "INDIRECT_BOOL", 1, false, &def_object, &def_field, &def_boolean },
103 { ".", "INDIRECT_OBJ", 1, false, &def_object, &def_field, &def_object },
105 { ".", "ADDRESS", 1, false, &def_entity, &def_field, &def_pointer },
107 { ".", "EVENTCALL", 2, false, &def_entity, &def_function, &def_void },
108 { ".", "OBJECTCALL", 2, false, &def_object, &def_function, &def_void },
109 { ".", "SYSCALL", 2, false, &def_void, &def_function, &def_void },
111 { "=", "STORE_F", 6, true, &def_float, &def_float, &def_float },
112 { "=", "STORE_V", 6, true, &def_vector, &def_vector, &def_vector },
113 { "=", "STORE_S", 6, true, &def_string, &def_string, &def_string },
114 { "=", "STORE_ENT", 6, true, &def_entity, &def_entity, &def_entity },
115 { "=", "STORE_BOOL", 6, true, &def_boolean, &def_boolean, &def_boolean },
116 { "=", "STORE_OBJENT", 6, true, &def_object, &def_entity, &def_object },
117 { "=", "STORE_OBJ", 6, true, &def_object, &def_object, &def_object },
118 { "=", "STORE_OBJENT", 6, true, &def_entity, &def_object, &def_object },
120 { "=", "STORE_FTOS", 6, true, &def_string, &def_float, &def_string },
121 { "=", "STORE_BTOS", 6, true, &def_string, &def_boolean, &def_string },
122 { "=", "STORE_VTOS", 6, true, &def_string, &def_vector, &def_string },
123 { "=", "STORE_FTOBOOL", 6, true, &def_boolean, &def_float, &def_boolean },
124 { "=", "STORE_BOOLTOF", 6, true, &def_float, &def_boolean, &def_float },
126 { "=", "STOREP_F", 6, true, &def_pointer, &def_float, &def_float },
127 { "=", "STOREP_V", 6, true, &def_pointer, &def_vector, &def_vector },
128 { "=", "STOREP_S", 6, true, &def_pointer, &def_string, &def_string },
129 { "=", "STOREP_ENT", 6, true, &def_pointer, &def_entity, &def_entity },
130 { "=", "STOREP_FLD", 6, true, &def_pointer, &def_field, &def_field },
131 { "=", "STOREP_BOOL", 6, true, &def_pointer, &def_boolean, &def_boolean },
132 { "=", "STOREP_OBJ", 6, true, &def_pointer, &def_object, &def_object },
133 { "=", "STOREP_OBJENT", 6, true, &def_pointer, &def_object, &def_object },
135 { "<=>", "STOREP_FTOS", 6, true, &def_pointer, &def_float, &def_string },
136 { "<=>", "STOREP_BTOS", 6, true, &def_pointer, &def_boolean, &def_string },
137 { "<=>", "STOREP_VTOS", 6, true, &def_pointer, &def_vector, &def_string },
138 { "<=>", "STOREP_FTOBOOL", 6, true, &def_pointer, &def_float, &def_boolean },
139 { "<=>", "STOREP_BOOLTOF", 6, true, &def_pointer, &def_boolean, &def_float },
141 { "*=", "UMUL_F", 6, true, &def_float, &def_float, &def_void },
142 { "*=", "UMUL_V", 6, true, &def_vector, &def_float, &def_void },
143 { "/=", "UDIV_F", 6, true, &def_float, &def_float, &def_void },
144 { "/=", "UDIV_V", 6, true, &def_vector, &def_float, &def_void },
145 { "%=", "UMOD_F", 6, true, &def_float, &def_float, &def_void },
146 { "+=", "UADD_F", 6, true, &def_float, &def_float, &def_void },
147 { "+=", "UADD_V", 6, true, &def_vector, &def_vector, &def_void },
148 { "-=", "USUB_F", 6, true, &def_float, &def_float, &def_void },
149 { "-=", "USUB_V", 6, true, &def_vector, &def_vector, &def_void },
150 { "&=", "UAND_F", 6, true, &def_float, &def_float, &def_void },
151 { "|=", "UOR_F", 6, true, &def_float, &def_float, &def_void },
153 { "!", "NOT_BOOL", -1, false, &def_boolean, &def_void, &def_float },
154 { "!", "NOT_F", -1, false, &def_float, &def_void, &def_float },
155 { "!", "NOT_V", -1, false, &def_vector, &def_void, &def_float },
156 { "!", "NOT_S", -1, false, &def_vector, &def_void, &def_float },
157 { "!", "NOT_ENT", -1, false, &def_entity, &def_void, &def_float },
159 { "<NEG_F>", "NEG_F", -1, false, &def_float, &def_void, &def_float },
160 { "<NEG_V>", "NEG_V", -1, false, &def_vector, &def_void, &def_vector },
162 { "int", "INT_F", -1, false, &def_float, &def_void, &def_float },
164 { "<IF>", "IF", -1, false, &def_float, &def_jumpoffset, &def_void },
165 { "<IFNOT>", "IFNOT", -1, false, &def_float, &def_jumpoffset, &def_void },
167 // calls returns REG_RETURN
168 { "<CALL>", "CALL", -1, false, &def_function, &def_argsize, &def_void },
169 { "<THREAD>", "THREAD", -1, false, &def_function, &def_argsize, &def_void },
170 { "<THREAD>", "OBJTHREAD", -1, false, &def_function, &def_argsize, &def_void },
172 { "<PUSH>", "PUSH_F", -1, false, &def_float, &def_float, &def_void },
173 { "<PUSH>", "PUSH_V", -1, false, &def_vector, &def_vector, &def_void },
174 { "<PUSH>", "PUSH_S", -1, false, &def_string, &def_string, &def_void },
175 { "<PUSH>", "PUSH_ENT", -1, false, &def_entity, &def_entity, &def_void },
176 { "<PUSH>", "PUSH_OBJ", -1, false, &def_object, &def_object, &def_void },
177 { "<PUSH>", "PUSH_OBJENT", -1, false, &def_entity, &def_object, &def_void },
178 { "<PUSH>", "PUSH_FTOS", -1, false, &def_string, &def_float, &def_void },
179 { "<PUSH>", "PUSH_BTOF", -1, false, &def_float, &def_boolean, &def_void },
180 { "<PUSH>", "PUSH_FTOB", -1, false, &def_boolean, &def_float, &def_void },
181 { "<PUSH>", "PUSH_VTOS", -1, false, &def_string, &def_vector, &def_void },
182 { "<PUSH>", "PUSH_BTOS", -1, false, &def_string, &def_boolean, &def_void },
184 { "<GOTO>", "GOTO", -1, false, &def_jumpoffset, &def_void, &def_void },
186 { "&&", "AND", 7, false, &def_float, &def_float, &def_float },
187 { "&&", "AND_BOOLF", 7, false, &def_boolean, &def_float, &def_float },
188 { "&&", "AND_FBOOL", 7, false, &def_float, &def_boolean, &def_float },
189 { "&&", "AND_BOOLBOOL", 7, false, &def_boolean, &def_boolean, &def_float },
190 { "||", "OR", 7, false, &def_float, &def_float, &def_float },
191 { "||", "OR_BOOLF", 7, false, &def_boolean, &def_float, &def_float },
192 { "||", "OR_FBOOL", 7, false, &def_float, &def_boolean, &def_float },
193 { "||", "OR_BOOLBOOL", 7, false, &def_boolean, &def_boolean, &def_float },
195 { "&", "BITAND", 3, false, &def_float, &def_float, &def_float },
196 { "|", "BITOR", 3, false, &def_float, &def_float, &def_float },
198 { "<BREAK>", "BREAK", -1, false, &def_float, &def_void, &def_void },
199 { "<CONTINUE>", "CONTINUE", -1, false, &def_float, &def_void, &def_void },
206 idCompiler::idCompiler()
209 idCompiler::idCompiler() {
213 // make sure we have the right # of opcodes in the table
214 assert( ( sizeof( opcodes ) / sizeof( opcodes[ 0 ] ) ) == ( NUM_OPCODES + 1 ) );
223 immediateType = NULL;
225 currentLineNumber = 0;
226 currentFileNumber = 0;
229 scope = &def_namespace;
231 memset( &immediate, 0, sizeof( immediate ) );
232 memset( punctuationValid, 0, sizeof( punctuationValid ) );
233 for( ptr = punctuation; *ptr != NULL; ptr++ ) {
234 id = parserPtr->GetPunctuationId( *ptr );
235 if ( ( id >= 0 ) && ( id < 256 ) ) {
236 punctuationValid[ id ] = true;
245 Aborts the current file load
248 void idCompiler::Error( const char *message, ... ) const {
252 va_start( argptr, message );
253 vsprintf( string, message, argptr );
256 throw idCompileError( string );
263 Prints a warning about the current line
266 void idCompiler::Warning( const char *message, ... ) const {
270 va_start( argptr, message );
271 vsprintf( string, message, argptr );
274 parserPtr->Warning( "%s", string );
279 idCompiler::VirtualFunctionConstant
281 Creates a def for an index into a virtual function table
284 ID_INLINE idVarDef *idCompiler::VirtualFunctionConstant( idVarDef *func ) {
287 memset( &eval, 0, sizeof( eval ) );
288 eval._int = func->scope->TypeDef()->GetFunctionNumber( func->value.functionPtr );
289 if ( eval._int < 0 ) {
290 Error( "Function '%s' not found in scope '%s'", func->Name(), func->scope->Name() );
293 return GetImmediate( &type_virtualfunction, &eval, "" );
298 idCompiler::SizeConstant
300 Creates a def for a size constant
303 ID_INLINE idVarDef *idCompiler::SizeConstant( int size ) {
306 memset( &eval, 0, sizeof( eval ) );
308 return GetImmediate( &type_argsize, &eval, "" );
313 idCompiler::JumpConstant
315 Creates a def for a jump constant
318 ID_INLINE idVarDef *idCompiler::JumpConstant( int value ) {
321 memset( &eval, 0, sizeof( eval ) );
323 return GetImmediate( &type_jumpoffset, &eval, "" );
330 Creates a def for a relative jump from one code location to another
333 ID_INLINE idVarDef *idCompiler::JumpDef( int jumpfrom, int jumpto ) {
334 return JumpConstant( jumpto - jumpfrom );
341 Creates a def for a relative jump from current code location
344 ID_INLINE idVarDef *idCompiler::JumpTo( int jumpto ) {
345 return JumpDef( gameLocal.program.NumStatements(), jumpto );
352 Creates a def for a relative jump from code location to current code location
355 ID_INLINE idVarDef *idCompiler::JumpFrom( int jumpfrom ) {
356 return JumpDef( jumpfrom, gameLocal.program.NumStatements() );
364 ID_INLINE float idCompiler::Divide( float numerator, float denominator ) {
365 if ( denominator == 0 ) {
366 Error( "Divide by zero" );
370 return numerator / denominator;
375 idCompiler::FindImmediate
377 tries to find an existing immediate with the same value
380 idVarDef *idCompiler::FindImmediate( const idTypeDef *type, const eval_t *eval, const char *string ) const {
384 etype = type->Type();
386 // check for a constant with the same value
387 for( def = gameLocal.program.GetDefList( "<IMMEDIATE>" ); def != NULL; def = def->Next() ) {
388 if ( def->TypeDef() != type ) {
394 if ( *def->value.intPtr == eval->_int ) {
400 if ( def->value.argSize == eval->_int ) {
406 if ( def->value.jumpOffset == eval->_int ) {
412 if ( *def->value.intPtr == eval->entity ) {
418 if ( idStr::Cmp( def->value.stringPtr, string ) == 0 ) {
424 if ( *def->value.floatPtr == eval->_float ) {
429 case ev_virtualfunction :
430 if ( def->value.virtualFunction == eval->_int ) {
437 if ( ( def->value.vectorPtr->x == eval->vector[ 0 ] ) &&
438 ( def->value.vectorPtr->y == eval->vector[ 1 ] ) &&
439 ( def->value.vectorPtr->z == eval->vector[ 2 ] ) ) {
445 Error( "weird immediate type" );
455 idCompiler::GetImmediate
457 returns an existing immediate with the same value, or allocates a new one
460 idVarDef *idCompiler::GetImmediate( idTypeDef *type, const eval_t *eval, const char *string ) {
463 def = FindImmediate( type, eval, string );
467 // allocate a new def
468 def = gameLocal.program.AllocDef( type, "<IMMEDIATE>", &def_namespace, true );
469 if ( type->Type() == ev_string ) {
470 def->SetString( string, true );
472 def->SetValue( *eval, true );
481 idCompiler::OptimizeOpcode
483 try to optimize when the operator works on constants only
486 idVarDef *idCompiler::OptimizeOpcode( const opcode_t *op, idVarDef *var_a, idVarDef *var_b ) {
490 if ( var_a && var_a->initialized != idVarDef::initializedConstant ) {
493 if ( var_b && var_b->initialized != idVarDef::initializedConstant ) {
497 idVec3 &vec_c = *reinterpret_cast<idVec3 *>( &c.vector[ 0 ] );
499 memset( &c, 0, sizeof( c ) );
500 switch( op - opcodes ) {
501 case OP_ADD_F: c._float = *var_a->value.floatPtr + *var_b->value.floatPtr; type = &type_float; break;
502 case OP_ADD_V: vec_c = *var_a->value.vectorPtr + *var_b->value.vectorPtr; type = &type_vector; break;
503 case OP_SUB_F: c._float = *var_a->value.floatPtr - *var_b->value.floatPtr; type = &type_float; break;
504 case OP_SUB_V: vec_c = *var_a->value.vectorPtr - *var_b->value.vectorPtr; type = &type_vector; break;
505 case OP_MUL_F: c._float = *var_a->value.floatPtr * *var_b->value.floatPtr; type = &type_float; break;
506 case OP_MUL_V: c._float = *var_a->value.vectorPtr * *var_b->value.vectorPtr; type = &type_float; break;
507 case OP_MUL_FV: vec_c = *var_b->value.vectorPtr * *var_a->value.floatPtr; type = &type_vector; break;
508 case OP_MUL_VF: vec_c = *var_a->value.vectorPtr * *var_b->value.floatPtr; type = &type_vector; break;
509 case OP_DIV_F: c._float = Divide( *var_a->value.floatPtr, *var_b->value.floatPtr ); type = &type_float; break;
510 case OP_MOD_F: c._float = (int)*var_a->value.floatPtr % (int)*var_b->value.floatPtr; type = &type_float; break;
511 case OP_BITAND: c._float = ( int )*var_a->value.floatPtr & ( int )*var_b->value.floatPtr; type = &type_float; break;
512 case OP_BITOR: c._float = ( int )*var_a->value.floatPtr | ( int )*var_b->value.floatPtr; type = &type_float; break;
513 case OP_GE: c._float = *var_a->value.floatPtr >= *var_b->value.floatPtr; type = &type_float; break;
514 case OP_LE: c._float = *var_a->value.floatPtr <= *var_b->value.floatPtr; type = &type_float; break;
515 case OP_GT: c._float = *var_a->value.floatPtr > *var_b->value.floatPtr; type = &type_float; break;
516 case OP_LT: c._float = *var_a->value.floatPtr < *var_b->value.floatPtr; type = &type_float; break;
517 case OP_AND: c._float = *var_a->value.floatPtr && *var_b->value.floatPtr; type = &type_float; break;
518 case OP_OR: c._float = *var_a->value.floatPtr || *var_b->value.floatPtr; type = &type_float; break;
519 case OP_NOT_BOOL: c._int = !*var_a->value.intPtr; type = &type_boolean; break;
520 case OP_NOT_F: c._float = !*var_a->value.floatPtr; type = &type_float; break;
521 case OP_NOT_V: c._float = !var_a->value.vectorPtr->x && !var_a->value.vectorPtr->y && !var_a->value.vectorPtr->z; type = &type_float; break;
522 case OP_NEG_F: c._float = -*var_a->value.floatPtr; type = &type_float; break;
523 case OP_NEG_V: vec_c = -*var_a->value.vectorPtr; type = &type_vector; break;
524 case OP_INT_F: c._float = ( int )*var_a->value.floatPtr; type = &type_float; break;
525 case OP_EQ_F: c._float = ( *var_a->value.floatPtr == *var_b->value.floatPtr ); type = &type_float; break;
526 case OP_EQ_V: c._float = var_a->value.vectorPtr->Compare( *var_b->value.vectorPtr ); type = &type_float; break;
527 case OP_EQ_E: c._float = ( *var_a->value.intPtr == *var_b->value.intPtr ); type = &type_float; break;
528 case OP_NE_F: c._float = ( *var_a->value.floatPtr != *var_b->value.floatPtr ); type = &type_float; break;
529 case OP_NE_V: c._float = !var_a->value.vectorPtr->Compare( *var_b->value.vectorPtr ); type = &type_float; break;
530 case OP_NE_E: c._float = ( *var_a->value.intPtr != *var_b->value.intPtr ); type = &type_float; break;
531 case OP_UADD_F: c._float = *var_b->value.floatPtr + *var_a->value.floatPtr; type = &type_float; break;
532 case OP_USUB_F: c._float = *var_b->value.floatPtr - *var_a->value.floatPtr; type = &type_float; break;
533 case OP_UMUL_F: c._float = *var_b->value.floatPtr * *var_a->value.floatPtr; type = &type_float; break;
534 case OP_UDIV_F: c._float = Divide( *var_b->value.floatPtr, *var_a->value.floatPtr ); type = &type_float; break;
535 case OP_UMOD_F: c._float = ( int ) *var_b->value.floatPtr % ( int )*var_a->value.floatPtr; type = &type_float; break;
536 case OP_UOR_F: c._float = ( int )*var_b->value.floatPtr | ( int )*var_a->value.floatPtr; type = &type_float; break;
537 case OP_UAND_F: c._float = ( int )*var_b->value.floatPtr & ( int )*var_a->value.floatPtr; type = &type_float; break;
538 case OP_UINC_F: c._float = *var_a->value.floatPtr + 1; type = &type_float; break;
539 case OP_UDEC_F: c._float = *var_a->value.floatPtr - 1; type = &type_float; break;
540 case OP_COMP_F: c._float = ( float )~( int )*var_a->value.floatPtr; type = &type_float; break;
541 default: type = NULL; break;
550 if ( var_a->numUsers <= 0 ) {
551 gameLocal.program.FreeDef( var_a, NULL );
556 if ( var_b->numUsers <= 0 ) {
557 gameLocal.program.FreeDef( var_b, NULL );
561 return GetImmediate( type, &c, "" );
566 idCompiler::EmitOpcode
568 Emits a primitive statement, returning the var it places it's value in
571 idVarDef *idCompiler::EmitOpcode( const opcode_t *op, idVarDef *var_a, idVarDef *var_b ) {
572 statement_t *statement;
575 var_c = OptimizeOpcode( op, var_a, var_b );
580 if ( var_a && !strcmp( var_a->Name(), RESULT_STRING ) ) {
583 if ( var_b && !strcmp( var_b->Name(), RESULT_STRING ) ) {
587 statement = gameLocal.program.AllocStatement();
588 statement->linenumber = currentLineNumber;
589 statement->file = currentFileNumber;
591 if ( ( op->type_c == &def_void ) || op->rightAssociative ) {
592 // ifs, gotos, and assignments don't need vars allocated
595 // allocate result space
596 // try to reuse result defs as much as possible
597 var_c = gameLocal.program.FindFreeResultDef( op->type_c->TypeDef(), RESULT_STRING, scope, var_a, var_b );
598 // set user count back to 1, a result def needs to be used twice before it can be reused
602 statement->op = op - opcodes;
603 statement->a = var_a;
604 statement->b = var_b;
605 statement->c = var_c;
607 if ( op->rightAssociative ) {
616 idCompiler::EmitOpcode
618 Emits a primitive statement, returning the var it places it's value in
621 ID_INLINE idVarDef *idCompiler::EmitOpcode( int op, idVarDef *var_a, idVarDef *var_b ) {
622 return EmitOpcode( &opcodes[ op ], var_a, var_b );
629 Emits an opcode to push the variable onto the stack.
632 bool idCompiler::EmitPush( idVarDef *expression, const idTypeDef *funcArg ) {
637 for( op = &opcodes[ OP_PUSH_F ]; op->name && !strcmp( op->name, "<PUSH>" ); op++ ) {
638 if ( ( funcArg->Type() == op->type_a->Type() ) && ( expression->Type() == op->type_b->Type() ) ) {
645 if ( ( expression->TypeDef() != funcArg ) && !expression->TypeDef()->Inherits( funcArg ) ) {
649 out = &opcodes[ OP_PUSH_ENT ];
652 EmitOpcode( out, expression, 0 );
659 idCompiler::NextToken
661 Sets token, immediateType, and possibly immediate
664 void idCompiler::NextToken( void ) {
668 immediateType = NULL;
669 memset( &immediate, 0, sizeof( immediate ) );
671 // Save the token's line number and filename since when we emit opcodes the current
672 // token is always the next one to be read
673 currentLineNumber = token.line;
674 currentFileNumber = gameLocal.program.GetFilenum( parserPtr->GetFileName() );
676 if ( !parserPtr->ReadToken( &token ) ) {
681 if ( currentFileNumber != gameLocal.program.GetFilenum( parserPtr->GetFileName() ) ) {
682 if ( ( braceDepth > 0 ) && ( token != "}" ) ) {
683 // missing a closing brace. try to give as much info as possible.
684 if ( scope->Type() == ev_function ) {
685 Error( "Unexpected end of file inside function '%s'. Missing closing braces.", scope->Name() );
686 } else if ( scope->Type() == ev_object ) {
687 Error( "Unexpected end of file inside object '%s'. Missing closing braces.", scope->Name() );
688 } else if ( scope->Type() == ev_namespace ) {
689 Error( "Unexpected end of file inside namespace '%s'. Missing closing braces.", scope->Name() );
691 Error( "Unexpected end of file inside braced section" );
696 switch( token.type ) {
698 // handle quoted strings as a unit
699 immediateType = &type_string;
703 // handle quoted vectors as a unit
704 immediateType = &type_vector;
705 idLexer lex( token, token.Length(), parserPtr->GetFileName(), LEXFL_NOERRORS );
707 for( i = 0; i < 3; i++ ) {
708 if ( !lex.ReadToken( &token2 ) ) {
709 Error( "Couldn't read vector. '%s' is not in the form of 'x y z'", token.c_str() );
711 if ( token2.type == TT_PUNCTUATION && token2 == "-" ) {
712 if ( !lex.CheckTokenType( TT_NUMBER, 0, &token2 ) ) {
713 Error( "expected a number following '-' but found '%s' in vector '%s'", token2.c_str(), token.c_str() );
715 immediate.vector[ i ] = -token2.GetFloatValue();
716 } else if ( token2.type == TT_NUMBER ) {
717 immediate.vector[ i ] = token2.GetFloatValue();
719 Error( "vector '%s' is not in the form of 'x y z'. expected float value, found '%s'", token.c_str(), token2.c_str() );
726 immediateType = &type_float;
727 immediate._float = token.GetFloatValue();
732 if ( token == "$" ) {
733 immediateType = &type_entity;
734 parserPtr->ReadToken( &token );
738 if ( token == "{" ) {
743 if ( token == "}" ) {
748 if ( punctuationValid[ token.subtype ] ) {
752 Error( "Unknown punctuation '%s'", token.c_str() );
759 Error( "Unknown token '%s'", token.c_str() );
765 idCompiler::ExpectToken
767 Issues an Error if the current token isn't equal to string
771 void idCompiler::ExpectToken( const char *string ) {
772 if ( token != string ) {
773 Error( "expected '%s', found '%s'", string, token.c_str() );
781 idCompiler::CheckToken
783 Returns true and gets the next token if the current token equals string
784 Returns false and does nothing otherwise
787 bool idCompiler::CheckToken( const char *string ) {
788 if ( token != string ) {
799 idCompiler::ParseName
801 Checks to see if the current token is a valid name
804 void idCompiler::ParseName( idStr &name ) {
805 if ( token.type != TT_NAME ) {
806 Error( "'%s' is not a name", token.c_str() );
815 idCompiler::SkipOutOfFunction
817 For error recovery, pops out of nested braces
820 void idCompiler::SkipOutOfFunction( void ) {
821 while( braceDepth ) {
822 parserPtr->SkipBracedSection( false );
830 idCompiler::SkipToSemicolon
835 void idCompiler::SkipToSemicolon( void ) {
837 if ( CheckToken( ";" ) ) {
847 idCompiler::CheckType
849 Parses a variable type, including functions types
852 idTypeDef *idCompiler::CheckType( void ) {
855 if ( token == "float" ) {
857 } else if ( token == "vector" ) {
859 } else if ( token == "entity" ) {
861 } else if ( token == "string" ) {
863 } else if ( token == "void" ) {
865 } else if ( token == "object" ) {
867 } else if ( token == "boolean" ) {
868 type = &type_boolean;
869 } else if ( token == "namespace" ) {
870 type = &type_namespace;
871 } else if ( token == "scriptEvent" ) {
872 type = &type_scriptevent;
874 type = gameLocal.program.FindType( token.c_str() );
875 if ( type && !type->Inherits( &type_object ) ) {
885 idCompiler::ParseType
887 Parses a variable type, including functions types
890 idTypeDef *idCompiler::ParseType( void ) {
895 Error( "\"%s\" is not a type", token.c_str() );
898 if ( ( type == &type_scriptevent ) && ( scope != &def_namespace ) ) {
899 Error( "scriptEvents can only defined in the global namespace" );
902 if ( ( type == &type_namespace ) && ( scope->Type() != ev_namespace ) ) {
903 Error( "A namespace may only be defined globally, or within another namespace" );
913 idCompiler::ParseImmediate
915 Looks for a preexisting constant
918 idVarDef *idCompiler::ParseImmediate( void ) {
921 def = GetImmediate( immediateType, &immediate, token.c_str() );
929 idCompiler::EmitFunctionParms
932 idVarDef *idCompiler::EmitFunctionParms( int op, idVarDef *func, int startarg, int startsize, idVarDef *object ) {
934 const idTypeDef *type;
935 const idTypeDef *funcArg;
937 idTypeDef *returnType;
942 type = func->TypeDef();
943 if ( func->Type() != ev_function ) {
944 Error( "'%s' is not a function", func->Name() );
947 // copy the parameters to the global parameter variables
950 if ( !CheckToken( ")" ) ) {
952 if ( arg >= type->NumParameters() ) {
953 Error( "too many parameters" );
956 e = GetExpression( TOP_PRIORITY );
958 funcArg = type->GetParmType( arg );
959 if ( !EmitPush( e, funcArg ) ) {
960 Error( "type mismatch on parm %i of call to '%s'", arg + 1, func->Name() );
963 if ( funcArg->Type() == ev_object ) {
964 size += type_object.Size();
966 size += funcArg->Size();
970 } while( CheckToken( "," ) );
975 if ( arg < type->NumParameters() ) {
976 Error( "too few parameters for function '%s'", func->Name() );
979 if ( op == OP_CALL ) {
980 EmitOpcode( op, func, 0 );
981 } else if ( ( op == OP_OBJECTCALL ) || ( op == OP_OBJTHREAD ) ) {
982 EmitOpcode( op, object, VirtualFunctionConstant( func ) );
984 // need arg size seperate since script object may be NULL
985 statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
986 statement.c = SizeConstant( func->value.functionPtr->parmTotal );
988 EmitOpcode( op, func, SizeConstant( size ) );
991 // we need to copy off the result into a temporary result location, so figure out the opcode
992 returnType = type->ReturnType();
993 if ( returnType->Type() == ev_string ) {
994 resultOp = OP_STORE_S;
995 returnDef = gameLocal.program.returnStringDef;
997 gameLocal.program.returnDef->SetTypeDef( returnType );
998 returnDef = gameLocal.program.returnDef;
1000 switch( returnType->Type() ) {
1002 resultOp = OP_STORE_F;
1006 resultOp = OP_STORE_BOOL;
1010 resultOp = OP_STORE_F;
1014 resultOp = OP_STORE_V;
1018 resultOp = OP_STORE_ENT;
1022 resultOp = OP_STORE_OBJ;
1026 Error( "Invalid return type for function '%s'", func->Name() );
1028 resultOp = OP_STORE_OBJ;
1033 if ( returnType->Type() == ev_void ) {
1034 // don't need result space since there's no result, so just return the normal result def.
1038 // allocate result space
1039 // try to reuse result defs as much as possible
1040 statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
1041 idVarDef *resultDef = gameLocal.program.FindFreeResultDef( returnType, RESULT_STRING, scope, statement.a, statement.b );
1042 // set user count back to 0, a result def needs to be used twice before it can be reused
1043 resultDef->numUsers = 0;
1045 EmitOpcode( resultOp, returnDef, resultDef );
1052 idCompiler::ParseFunctionCall
1055 idVarDef *idCompiler::ParseFunctionCall( idVarDef *funcDef ) {
1058 if ( funcDef->Type() != ev_function ) {
1059 Error( "'%s' is not a function", funcDef->Name() );
1062 if ( funcDef->initialized == idVarDef::uninitialized ) {
1063 Error( "Function '%s' has not been defined yet", funcDef->GlobalName() );
1066 assert( funcDef->value.functionPtr );
1068 if ( ( funcDef->initialized != idVarDef::uninitialized ) && funcDef->value.functionPtr->eventdef ) {
1069 Error( "Built-in functions cannot be called as threads" );
1072 return EmitFunctionParms( OP_THREAD, funcDef, 0, 0, NULL );
1074 if ( ( funcDef->initialized != idVarDef::uninitialized ) && funcDef->value.functionPtr->eventdef ) {
1075 if ( ( scope->Type() != ev_namespace ) && ( scope->scope->Type() == ev_object ) ) {
1076 // get the local object pointer
1077 idVarDef *thisdef = gameLocal.program.GetDef( scope->scope->TypeDef(), "self", scope );
1079 Error( "No 'self' within scope" );
1082 return ParseEventCall( thisdef, funcDef );
1084 Error( "Built-in functions cannot be called without an object" );
1088 return EmitFunctionParms( OP_CALL, funcDef, 0, 0, NULL );
1094 idCompiler::ParseObjectCall
1097 idVarDef *idCompiler::ParseObjectCall( idVarDef *object, idVarDef *func ) {
1098 EmitPush( object, object->TypeDef() );
1101 return EmitFunctionParms( OP_OBJTHREAD, func, 1, type_object.Size(), object );
1103 return EmitFunctionParms( OP_OBJECTCALL, func, 1, 0, object );
1109 idCompiler::ParseEventCall
1112 idVarDef *idCompiler::ParseEventCall( idVarDef *object, idVarDef *funcDef ) {
1114 Error( "Cannot call built-in functions as a thread" );
1117 if ( funcDef->Type() != ev_function ) {
1118 Error( "'%s' is not a function", funcDef->Name() );
1121 if ( !funcDef->value.functionPtr->eventdef ) {
1122 Error( "\"%s\" cannot be called with object notation", funcDef->Name() );
1125 if ( object->Type() == ev_object ) {
1126 EmitPush( object, &type_entity );
1128 EmitPush( object, object->TypeDef() );
1131 return EmitFunctionParms( OP_EVENTCALL, funcDef, 0, type_object.Size(), NULL );
1136 idCompiler::ParseSysObjectCall
1139 idVarDef *idCompiler::ParseSysObjectCall( idVarDef *funcDef ) {
1141 Error( "Cannot call built-in functions as a thread" );
1144 if ( funcDef->Type() != ev_function ) {
1145 Error( "'%s' is not a function", funcDef->Name() );
1148 if ( !funcDef->value.functionPtr->eventdef ) {
1149 Error( "\"%s\" cannot be called with object notation", funcDef->Name() );
1152 if ( !idThread::Type.RespondsTo( *funcDef->value.functionPtr->eventdef ) ) {
1153 Error( "\"%s\" is not callable as a 'sys' function", funcDef->Name() );
1156 return EmitFunctionParms( OP_SYSCALL, funcDef, 0, 0, NULL );
1161 idCompiler::LookupDef
1164 idVarDef *idCompiler::LookupDef( const char *name, const idVarDef *baseobj ) {
1171 // check if we're accessing a field
1172 if ( baseobj && ( baseobj->Type() == ev_object ) ) {
1173 const idVarDef *tdef;
1176 for( tdef = baseobj; tdef != &def_object; tdef = tdef->TypeDef()->SuperClass()->def ) {
1177 def = gameLocal.program.GetDef( NULL, name, tdef );
1183 // first look through the defs in our scope
1184 def = gameLocal.program.GetDef( NULL, name, scope );
1186 // if we're in a member function, check types local to the object
1187 if ( ( scope->Type() != ev_namespace ) && ( scope->scope->Type() == ev_object ) ) {
1188 // get the local object pointer
1189 idVarDef *thisdef = gameLocal.program.GetDef( scope->scope->TypeDef(), "self", scope );
1191 field = LookupDef( name, scope->scope->TypeDef()->def );
1193 Error( "Unknown value \"%s\"", name );
1197 type_b = field->Type();
1198 if ( field->Type() == ev_function ) {
1199 type_c = field->TypeDef()->ReturnType()->Type();
1201 type_c = field->TypeDef()->FieldType()->Type(); // field access gets type from field
1202 if ( CheckToken( "++" ) ) {
1203 if ( type_c != ev_float ) {
1204 Error( "Invalid type for ++" );
1206 def = EmitOpcode( OP_UINCP_F, thisdef, field );
1208 } else if ( CheckToken( "--" ) ) {
1209 if ( type_c != ev_float ) {
1210 Error( "Invalid type for --" );
1212 def = EmitOpcode( OP_UDECP_F, thisdef, field );
1217 op = &opcodes[ OP_INDIRECT_F ];
1218 while( ( op->type_a->Type() != ev_object )
1219 || ( type_b != op->type_b->Type() ) || ( type_c != op->type_c->Type() ) ) {
1220 if ( ( op->priority == FUNCTION_PRIORITY ) && ( op->type_a->Type() == ev_object ) && ( op->type_c->Type() == ev_void ) &&
1221 ( type_c != op->type_c->Type() ) ) {
1222 // catches object calls that return a value
1226 if ( !op->name || strcmp( op->name, "." ) ) {
1227 Error( "no valid opcode to access type '%s'", field->TypeDef()->SuperClass()->Name() );
1231 if ( ( op - opcodes ) == OP_OBJECTCALL ) {
1233 def = ParseObjectCall( thisdef, field );
1235 // emit the conversion opcode
1236 def = EmitOpcode( op, thisdef, field );
1238 // field access gets type from field
1239 def->SetTypeDef( field->TypeDef()->FieldType() );
1250 idCompiler::ParseValue
1252 Returns the def for the current token
1255 idVarDef *idCompiler::ParseValue( void ) {
1257 idVarDef *namespaceDef;
1260 if ( immediateType == &type_entity ) {
1261 // if an immediate entity ($-prefaced name) then create or lookup a def for it.
1262 // when entities are spawned, they'll lookup the def and point it to them.
1263 def = gameLocal.program.GetDef( &type_entity, "$" + token, &def_namespace );
1265 def = gameLocal.program.AllocDef( &type_entity, "$" + token, &def_namespace, true );
1269 } else if ( immediateType ) {
1270 // if the token is an immediate, allocate a constant for it
1271 return ParseImmediate();
1275 def = LookupDef( name, basetype );
1278 Error( "%s is not a member of %s", name.c_str(), basetype->TypeDef()->Name() );
1280 Error( "Unknown value \"%s\"", name.c_str() );
1282 // if namespace, then look up the variable in that namespace
1283 } else if ( def->Type() == ev_namespace ) {
1284 while( def->Type() == ev_namespace ) {
1285 ExpectToken( "::" );
1288 def = gameLocal.program.GetDef( NULL, name, namespaceDef );
1290 Error( "Unknown value \"%s::%s\"", namespaceDef->GlobalName(), name.c_str() );
1293 //def = LookupDef( name, basetype );
1304 idVarDef *idCompiler::GetTerm( void ) {
1308 if ( !immediateType && CheckToken( "~" ) ) {
1309 e = GetExpression( TILDE_PRIORITY );
1310 switch( e->Type() ) {
1316 Error( "type mismatch for ~" );
1323 return EmitOpcode( op, e, 0 );
1326 if ( !immediateType && CheckToken( "!" ) ) {
1327 e = GetExpression( NOT_PRIORITY );
1328 switch( e->Type() ) {
1350 Error( "Invalid type for !" );
1361 Error( "type mismatch for !" );
1368 return EmitOpcode( op, e, 0 );
1371 // check for negation operator
1372 if ( !immediateType && CheckToken( "-" ) ) {
1373 // constants are directly negated without an instruction
1374 if ( immediateType == &type_float ) {
1375 immediate._float = -immediate._float;
1376 return ParseImmediate();
1377 } else if ( immediateType == &type_vector ) {
1378 immediate.vector[0] = -immediate.vector[0];
1379 immediate.vector[1] = -immediate.vector[1];
1380 immediate.vector[2] = -immediate.vector[2];
1381 return ParseImmediate();
1383 e = GetExpression( NOT_PRIORITY );
1384 switch( e->Type() ) {
1393 Error( "type mismatch for -" );
1399 return EmitOpcode( &opcodes[ op ], e, 0 );
1403 if ( CheckToken( "int" ) ) {
1406 e = GetExpression( INT_PRIORITY );
1407 if ( e->Type() != ev_float ) {
1408 Error( "type mismatch for int()" );
1413 return EmitOpcode( OP_INT_F, e, 0 );
1416 if ( CheckToken( "thread" ) ) {
1418 e = GetExpression( FUNCTION_PRIORITY );
1421 Error( "Invalid thread call" );
1424 // threads return the thread number
1425 gameLocal.program.returnDef->SetTypeDef( &type_float );
1426 return gameLocal.program.returnDef;
1429 if ( !immediateType && CheckToken( "(" ) ) {
1430 e = GetExpression( TOP_PRIORITY );
1436 return ParseValue();
1441 idCompiler::TypeMatches
1444 bool idCompiler::TypeMatches( etype_t type1, etype_t type2 ) const {
1445 if ( type1 == type2 ) {
1449 //if ( ( type1 == ev_entity ) && ( type2 == ev_object ) ) {
1453 //if ( ( type2 == ev_entity ) && ( type1 == ev_object ) ) {
1462 idCompiler::GetExpression
1465 idVarDef *idCompiler::GetExpression( int priority ) {
1470 const idVarDef *oldtype;
1475 if ( priority == 0 ) {
1479 e = GetExpression( priority - 1 );
1480 if ( token == ";" ) {
1481 // save us from searching through the opcodes unneccesarily
1486 if ( ( priority == FUNCTION_PRIORITY ) && CheckToken( "(" ) ) {
1487 return ParseFunctionCall( e );
1490 // has to be a punctuation
1491 if ( immediateType ) {
1495 for( op = opcodes; op->name; op++ ) {
1496 if ( ( op->priority == priority ) && CheckToken( op->name ) ) {
1502 // next token isn't at this priority level
1506 // unary operators act only on the left operand
1507 if ( op->type_b == &def_void ) {
1508 e = EmitOpcode( op, e, 0 );
1512 // preserve our base type
1515 // field access needs scope from object
1516 if ( ( op->name[ 0 ] == '.' ) && e->TypeDef()->Inherits( &type_object ) ) {
1517 // save off what type this field is part of
1518 basetype = e->TypeDef()->def;
1521 if ( op->rightAssociative ) {
1522 // if last statement is an indirect, change it to an address of
1523 if ( gameLocal.program.NumStatements() > 0 ) {
1524 statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
1525 if ( ( statement.op >= OP_INDIRECT_F ) && ( statement.op < OP_ADDRESS ) ) {
1526 statement.op = OP_ADDRESS;
1527 type_pointer.SetPointerType( e->TypeDef() );
1528 e->SetTypeDef( &type_pointer );
1532 e2 = GetExpression( priority );
1534 e2 = GetExpression( priority - 1 );
1542 type_b = e2->Type();
1544 // field access gets type from field
1545 if ( op->name[ 0 ] == '.' ) {
1546 if ( ( e2->Type() == ev_function ) && e2->TypeDef()->ReturnType() ) {
1547 type_c = e2->TypeDef()->ReturnType()->Type();
1548 } else if ( e2->TypeDef()->FieldType() ) {
1549 type_c = e2->TypeDef()->FieldType()->Type();
1559 while( !TypeMatches( type_a, op->type_a->Type() ) || !TypeMatches( type_b, op->type_b->Type() ) ||
1560 ( ( type_c != ev_void ) && !TypeMatches( type_c, op->type_c->Type() ) ) ) {
1561 if ( ( op->priority == FUNCTION_PRIORITY ) && TypeMatches( type_a, op->type_a->Type() ) && TypeMatches( type_b, op->type_b->Type() ) ) {
1566 if ( !op->name || strcmp( op->name, oldop->name ) ) {
1567 Error( "type mismatch for '%s'", oldop->name );
1571 switch( op - opcodes ) {
1574 e = ParseSysObjectCall( e2 );
1577 case OP_OBJECTCALL :
1579 if ( ( e2->initialized != idVarDef::uninitialized ) && e2->value.functionPtr->eventdef ) {
1580 e = ParseEventCall( e, e2 );
1582 e = ParseObjectCall( e, e2 );
1588 if ( ( e2->initialized != idVarDef::uninitialized ) && e2->value.functionPtr->eventdef ) {
1589 e = ParseEventCall( e, e2 );
1591 e = ParseObjectCall( e, e2 );
1597 Error( "Expecting function call after 'thread'" );
1600 if ( ( type_a == ev_pointer ) && ( type_b != e->TypeDef()->PointerType()->Type() ) ) {
1601 // FIXME: need to make a general case for this
1602 if ( ( op - opcodes == OP_STOREP_F ) && ( e->TypeDef()->PointerType()->Type() == ev_boolean ) ) {
1603 // copy from float to boolean pointer
1604 op = &opcodes[ OP_STOREP_FTOBOOL ];
1605 } else if ( ( op - opcodes == OP_STOREP_BOOL ) && ( e->TypeDef()->PointerType()->Type() == ev_float ) ) {
1606 // copy from boolean to float pointer
1607 op = &opcodes[ OP_STOREP_BOOLTOF ];
1608 } else if ( ( op - opcodes == OP_STOREP_F ) && ( e->TypeDef()->PointerType()->Type() == ev_string ) ) {
1609 // copy from float to string pointer
1610 op = &opcodes[ OP_STOREP_FTOS ];
1611 } else if ( ( op - opcodes == OP_STOREP_BOOL ) && ( e->TypeDef()->PointerType()->Type() == ev_string ) ) {
1612 // copy from boolean to string pointer
1613 op = &opcodes[ OP_STOREP_BTOS ];
1614 } else if ( ( op - opcodes == OP_STOREP_V ) && ( e->TypeDef()->PointerType()->Type() == ev_string ) ) {
1615 // copy from vector to string pointer
1616 op = &opcodes[ OP_STOREP_VTOS ];
1617 } else if ( ( op - opcodes == OP_STOREP_ENT ) && ( e->TypeDef()->PointerType()->Type() == ev_object ) ) {
1618 // store an entity into an object pointer
1619 op = &opcodes[ OP_STOREP_OBJENT ];
1621 Error( "type mismatch for '%s'", op->name );
1625 if ( op->rightAssociative ) {
1626 e = EmitOpcode( op, e2, e );
1628 e = EmitOpcode( op, e, e2 );
1631 if ( op - opcodes == OP_STOREP_OBJENT ) {
1632 // statement.b points to type_pointer, which is just a temporary that gets its type reassigned, so we store the real type in statement.c
1633 // so that we can do a type check during run time since we don't know what type the script object is at compile time because it
1634 // comes from an entity
1635 statement_t &statement = gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 );
1636 statement.c = type_pointer.PointerType()->def;
1639 // field access gets type from field
1640 if ( type_c != ev_void ) {
1641 e->SetTypeDef( e2->TypeDef()->FieldType() );
1652 idCompiler::PatchLoop
1655 void idCompiler::PatchLoop( int start, int continuePos ) {
1659 pos = &gameLocal.program.GetStatement( start );
1660 for( i = start; i < gameLocal.program.NumStatements(); i++, pos++ ) {
1661 if ( pos->op == OP_BREAK ) {
1663 pos->a = JumpFrom( i );
1664 } else if ( pos->op == OP_CONTINUE ) {
1666 pos->a = JumpDef( i, continuePos );
1673 idCompiler::ParseReturnStatement
1676 void idCompiler::ParseReturnStatement( void ) {
1682 if ( CheckToken( ";" ) ) {
1683 if ( scope->TypeDef()->ReturnType()->Type() != ev_void ) {
1684 Error( "expecting return value" );
1687 EmitOpcode( OP_RETURN, 0, 0 );
1691 e = GetExpression( TOP_PRIORITY );
1695 type_b = scope->TypeDef()->ReturnType()->Type();
1697 if ( TypeMatches( type_a, type_b ) ) {
1698 EmitOpcode( OP_RETURN, e, 0 );
1702 for( op = opcodes; op->name; op++ ) {
1703 if ( !strcmp( op->name, "=" ) ) {
1710 while( !TypeMatches( type_a, op->type_a->Type() ) || !TypeMatches( type_b, op->type_b->Type() ) ) {
1712 if ( !op->name || strcmp( op->name, "=" ) ) {
1713 Error( "type mismatch for return value" );
1717 idTypeDef *returnType = scope->TypeDef()->ReturnType();
1718 if ( returnType->Type() == ev_string ) {
1719 EmitOpcode( op, e, gameLocal.program.returnStringDef );
1721 gameLocal.program.returnDef->SetTypeDef( returnType );
1722 EmitOpcode( op, e, gameLocal.program.returnDef );
1724 EmitOpcode( OP_RETURN, 0, 0 );
1729 idCompiler::ParseWhileStatement
1732 void idCompiler::ParseWhileStatement( void ) {
1741 patch2 = gameLocal.program.NumStatements();
1742 e = GetExpression( TOP_PRIORITY );
1745 if ( ( e->initialized == idVarDef::initializedConstant ) && ( *e->value.intPtr != 0 ) ) {
1746 //FIXME: we can completely skip generation of this code in the opposite case
1748 EmitOpcode( OP_GOTO, JumpTo( patch2 ), 0 );
1750 patch1 = gameLocal.program.NumStatements();
1751 EmitOpcode( OP_IFNOT, e, 0 );
1753 EmitOpcode( OP_GOTO, JumpTo( patch2 ), 0 );
1754 gameLocal.program.GetStatement( patch1 ).b = JumpFrom( patch1 );
1757 // fixup breaks and continues
1758 PatchLoop( patch2, patch2 );
1765 idCompiler::ParseForStatement
1767 Form of for statement with a counter:
1771 if ( !( a < 10 ) ) {
1774 goto process; << patch3
1777 increment: << patch2
1779 goto start; << goto patch4
1783 goto increment; << goto patch2
1787 Form of for statement without a counter:
1791 if ( !( a < 10 ) ) {
1797 goto start; << goto patch2
1802 void idCompiler::ParseForStatement( void ) {
1812 start = gameLocal.program.NumStatements();
1817 if ( !CheckToken( ";" ) ) {
1819 GetExpression( TOP_PRIORITY );
1820 } while( CheckToken( "," ) );
1826 patch2 = gameLocal.program.NumStatements();
1828 e = GetExpression( TOP_PRIORITY );
1831 //FIXME: add check for constant expression
1832 patch1 = gameLocal.program.NumStatements();
1833 EmitOpcode( OP_IFNOT, e, 0 );
1836 if ( !CheckToken( ")" ) ) {
1837 patch3 = gameLocal.program.NumStatements();
1838 EmitOpcode( OP_IF, e, 0 );
1841 patch2 = gameLocal.program.NumStatements();
1843 GetExpression( TOP_PRIORITY );
1844 } while( CheckToken( "," ) );
1849 EmitOpcode( OP_GOTO, JumpTo( patch4 ), 0 );
1852 gameLocal.program.GetStatement( patch3 ).b = JumpFrom( patch3 );
1858 EmitOpcode( OP_GOTO, JumpTo( patch2 ), 0 );
1861 gameLocal.program.GetStatement( patch1 ).b = JumpFrom( patch1 );
1863 // fixup breaks and continues
1864 PatchLoop( start, patch2 );
1871 idCompiler::ParseDoWhileStatement
1874 void idCompiler::ParseDoWhileStatement( void ) {
1880 patch1 = gameLocal.program.NumStatements();
1882 ExpectToken( "while" );
1884 e = GetExpression( TOP_PRIORITY );
1888 EmitOpcode( OP_IF, e, JumpTo( patch1 ) );
1890 // fixup breaks and continues
1891 PatchLoop( patch1, patch1 );
1898 idCompiler::ParseIfStatement
1901 void idCompiler::ParseIfStatement( void ) {
1907 e = GetExpression( TOP_PRIORITY );
1910 //FIXME: add check for constant expression
1911 patch1 = gameLocal.program.NumStatements();
1912 EmitOpcode( OP_IFNOT, e, 0 );
1916 if ( CheckToken( "else" ) ) {
1917 patch2 = gameLocal.program.NumStatements();
1918 EmitOpcode( OP_GOTO, 0, 0 );
1919 gameLocal.program.GetStatement( patch1 ).b = JumpFrom( patch1 );
1921 gameLocal.program.GetStatement( patch2 ).a = JumpFrom( patch2 );
1923 gameLocal.program.GetStatement( patch1 ).b = JumpFrom( patch1 );
1929 idCompiler::ParseStatement
1932 void idCompiler::ParseStatement( void ) {
1933 if ( CheckToken( ";" ) ) {
1934 // skip semicolons, which are harmless and ok syntax
1938 if ( CheckToken( "{" ) ) {
1941 } while( !CheckToken( "}" ) );
1946 if ( CheckToken( "return" ) ) {
1947 ParseReturnStatement();
1951 if ( CheckToken( "while" ) ) {
1952 ParseWhileStatement();
1956 if ( CheckToken( "for" ) ) {
1957 ParseForStatement();
1961 if ( CheckToken( "do" ) ) {
1962 ParseDoWhileStatement();
1966 if ( CheckToken( "break" ) ) {
1969 Error( "cannot break outside of a loop" );
1971 EmitOpcode( OP_BREAK, 0, 0 );
1975 if ( CheckToken( "continue" ) ) {
1978 Error( "cannot contine outside of a loop" );
1980 EmitOpcode( OP_CONTINUE, 0, 0 );
1984 if ( CheckType() != NULL ) {
1989 if ( CheckToken( "if" ) ) {
1994 GetExpression( TOP_PRIORITY );
2000 idCompiler::ParseObjectDef
2003 void idCompiler::ParseObjectDef( const char *objname ) {
2006 idTypeDef *parentType;
2007 idTypeDef *fieldtype;
2009 const char *fieldname;
2010 idTypeDef newtype( ev_field, NULL, "", 0, NULL );
2016 if ( scope->Type() != ev_namespace ) {
2017 Error( "Objects cannot be defined within functions or other objects" );
2020 // make sure it doesn't exist before we create it
2021 if ( gameLocal.program.FindType( objname ) != NULL ) {
2022 Error( "'%s' : redefinition; different basic types", objname );
2026 if ( !CheckToken( ":" ) ) {
2027 parentType = &type_object;
2029 parentType = ParseType();
2030 if ( !parentType->Inherits( &type_object ) ) {
2031 Error( "Objects may only inherit from objects." );
2035 objtype = gameLocal.program.AllocType( ev_object, NULL, objname, parentType == &type_object ? 0 : parentType->Size(), parentType );
2036 objtype->def = gameLocal.program.AllocDef( objtype, objname, scope, true );
2037 scope = objtype->def;
2039 // inherit all the functions
2040 num = parentType->NumFunctions();
2041 for( i = 0; i < parentType->NumFunctions(); i++ ) {
2042 const function_t *func = parentType->GetFunction( i );
2043 objtype->AddFunction( func );
2049 if ( CheckToken( ";" ) ) {
2050 // skip semicolons, which are harmless and ok syntax
2054 fieldtype = ParseType();
2055 newtype.SetFieldType( fieldtype );
2057 fieldname = va( "%s field", fieldtype->Name() );
2058 newtype.SetName( fieldname );
2062 // check for a function prototype or declaraction
2063 if ( CheckToken( "(" ) ) {
2064 ParseFunctionDef( newtype.FieldType(), name );
2066 type = gameLocal.program.GetType( newtype, true );
2067 assert( !type->def );
2068 gameLocal.program.AllocDef( type, name, scope, true );
2069 objtype->AddField( type, name );
2072 } while( !CheckToken( "}" ) );
2081 idCompiler::ParseFunction
2083 parse a function type
2086 idTypeDef *idCompiler::ParseFunction( idTypeDef *returnType, const char *name ) {
2087 idTypeDef newtype( ev_function, NULL, name, type_function.Size(), returnType );
2090 if ( scope->Type() != ev_namespace ) {
2091 // create self pointer
2092 newtype.AddFunctionParm( scope->TypeDef(), "self" );
2095 if ( !CheckToken( ")" ) ) {
2099 ParseName( parmName );
2100 newtype.AddFunctionParm( type, parmName );
2101 } while( CheckToken( "," ) );
2106 return gameLocal.program.GetType( newtype, true );
2111 idCompiler::ParseFunctionDef
2114 void idCompiler::ParseFunctionDef( idTypeDef *returnType, const char *name ) {
2117 const idVarDef *parm;
2121 const idTypeDef *parmType;
2125 if ( ( scope->Type() != ev_namespace ) && !scope->TypeDef()->Inherits( &type_object ) ) {
2126 Error( "Functions may not be defined within other functions" );
2129 type = ParseFunction( returnType, name );
2130 def = gameLocal.program.GetDef( type, name, scope );
2132 def = gameLocal.program.AllocDef( type, name, scope, true );
2135 func = &gameLocal.program.AllocFunction( def );
2136 if ( scope->TypeDef()->Inherits( &type_object ) ) {
2137 scope->TypeDef()->AddFunction( func );
2140 func = def->value.functionPtr;
2142 if ( func->firstStatement ) {
2143 Error( "%s redeclared", def->GlobalName() );
2147 // check if this is a prototype or declaration
2148 if ( !CheckToken( "{" ) ) {
2149 // it's just a prototype, so get the ; and move on
2154 // calculate stack space used by parms
2155 numParms = type->NumParameters();
2156 func->parmSize.SetNum( numParms );
2157 for( i = 0; i < numParms; i++ ) {
2158 parmType = type->GetParmType( i );
2159 if ( parmType->Inherits( &type_object ) ) {
2160 func->parmSize[ i ] = type_object.Size();
2162 func->parmSize[ i ] = parmType->Size();
2164 func->parmTotal += func->parmSize[ i ];
2168 for( i = 0; i < numParms; i++ ) {
2169 if ( gameLocal.program.GetDef( type->GetParmType( i ), type->GetParmName( i ), def ) ) {
2170 Error( "'%s' defined more than once in function parameters", type->GetParmName( i ) );
2172 parm = gameLocal.program.AllocDef( type->GetParmType( i ), type->GetParmName( i ), def, false );
2178 func->firstStatement = gameLocal.program.NumStatements();
2180 // check if we should call the super class constructor
2181 if ( oldscope->TypeDef()->Inherits( &type_object ) && !idStr::Icmp( name, "init" ) ) {
2182 idTypeDef *superClass;
2183 function_t *constructorFunc = NULL;
2185 // find the superclass constructor
2186 for( superClass = oldscope->TypeDef()->SuperClass(); superClass != &type_object; superClass = superClass->SuperClass() ) {
2187 constructorFunc = gameLocal.program.FindFunction( va( "%s::init", superClass->Name() ) );
2188 if ( constructorFunc ) {
2193 // emit the call to the constructor
2194 if ( constructorFunc ) {
2195 idVarDef *selfDef = gameLocal.program.GetDef( type->GetParmType( 0 ), type->GetParmName( 0 ), def );
2197 EmitPush( selfDef, selfDef->TypeDef() );
2198 EmitOpcode( &opcodes[ OP_CALL ], constructorFunc->def, 0 );
2202 // parse regular statements
2203 while( !CheckToken( "}" ) ) {
2207 // check if we should call the super class destructor
2208 if ( oldscope->TypeDef()->Inherits( &type_object ) && !idStr::Icmp( name, "destroy" ) ) {
2209 idTypeDef *superClass;
2210 function_t *destructorFunc = NULL;
2212 // find the superclass destructor
2213 for( superClass = oldscope->TypeDef()->SuperClass(); superClass != &type_object; superClass = superClass->SuperClass() ) {
2214 destructorFunc = gameLocal.program.FindFunction( va( "%s::destroy", superClass->Name() ) );
2215 if ( destructorFunc ) {
2220 if ( destructorFunc ) {
2221 if ( func->firstStatement < gameLocal.program.NumStatements() ) {
2222 // change all returns to point to the call to the destructor
2223 pos = &gameLocal.program.GetStatement( func->firstStatement );
2224 for( i = func->firstStatement; i < gameLocal.program.NumStatements(); i++, pos++ ) {
2225 if ( pos->op == OP_RETURN ) {
2227 pos->a = JumpDef( i, gameLocal.program.NumStatements() );
2232 // emit the call to the destructor
2233 idVarDef *selfDef = gameLocal.program.GetDef( type->GetParmType( 0 ), type->GetParmName( 0 ), def );
2235 EmitPush( selfDef, selfDef->TypeDef() );
2236 EmitOpcode( &opcodes[ OP_CALL ], destructorFunc->def, 0 );
2240 // Disabled code since it caused a function to fall through to the next function when last statement is in the form "if ( x ) { return; }"
2242 // don't bother adding a return opcode if the "return" statement was used.
2243 if ( ( func->firstStatement == gameLocal.program.NumStatements() ) || ( gameLocal.program.GetStatement( gameLocal.program.NumStatements() - 1 ).op != OP_RETURN ) ) {
2244 // emit an end of statements opcode
2245 EmitOpcode( OP_RETURN, 0, 0 );
2248 // always emit the return opcode
2249 EmitOpcode( OP_RETURN, 0, 0 );
2252 // record the number of statements in the function
2253 func->numStatements = gameLocal.program.NumStatements() - func->firstStatement;
2260 idCompiler::ParseVariableDef
2263 void idCompiler::ParseVariableDef( idTypeDef *type, const char *name ) {
2264 idVarDef *def, *def2;
2267 def = gameLocal.program.GetDef( type, name, scope );
2269 Error( "%s redeclared", name );
2272 def = gameLocal.program.AllocDef( type, name, scope, false );
2274 // check for an initialization
2275 if ( CheckToken( "=" ) ) {
2276 // if a local variable in a function then write out interpreter code to initialize variable
2277 if ( scope->Type() == ev_function ) {
2278 def2 = GetExpression( TOP_PRIORITY );
2279 if ( ( type == &type_float ) && ( def2->TypeDef() == &type_float ) ) {
2280 EmitOpcode( OP_STORE_F, def2, def );
2281 } else if ( ( type == &type_vector ) && ( def2->TypeDef() == &type_vector ) ) {
2282 EmitOpcode( OP_STORE_V, def2, def );
2283 } else if ( ( type == &type_string ) && ( def2->TypeDef() == &type_string ) ) {
2284 EmitOpcode( OP_STORE_S, def2, def );
2285 } else if ( ( type == &type_entity ) && ( ( def2->TypeDef() == &type_entity ) || ( def2->TypeDef()->Inherits( &type_object ) ) ) ) {
2286 EmitOpcode( OP_STORE_ENT, def2, def );
2287 } else if ( ( type->Inherits( &type_object ) ) && ( def2->TypeDef() == &type_entity ) ) {
2288 EmitOpcode( OP_STORE_OBJENT, def2, def );
2289 } else if ( ( type->Inherits( &type_object ) ) && ( def2->TypeDef()->Inherits( type ) ) ) {
2290 EmitOpcode( OP_STORE_OBJ, def2, def );
2291 } else if ( ( type == &type_boolean ) && ( def2->TypeDef() == &type_boolean ) ) {
2292 EmitOpcode( OP_STORE_BOOL, def2, def );
2293 } else if ( ( type == &type_string ) && ( def2->TypeDef() == &type_float ) ) {
2294 EmitOpcode( OP_STORE_FTOS, def2, def );
2295 } else if ( ( type == &type_string ) && ( def2->TypeDef() == &type_boolean ) ) {
2296 EmitOpcode( OP_STORE_BTOS, def2, def );
2297 } else if ( ( type == &type_string ) && ( def2->TypeDef() == &type_vector ) ) {
2298 EmitOpcode( OP_STORE_VTOS, def2, def );
2299 } else if ( ( type == &type_boolean ) && ( def2->TypeDef() == &type_float ) ) {
2300 EmitOpcode( OP_STORE_FTOBOOL, def2, def );
2301 } else if ( ( type == &type_float ) && ( def2->TypeDef() == &type_boolean ) ) {
2302 EmitOpcode( OP_STORE_BOOLTOF, def2, def );
2304 Error( "bad initialization for '%s'", name );
2307 // global variables can only be initialized with immediate values
2309 if ( token.type == TT_PUNCTUATION && token == "-" ) {
2312 if ( immediateType != &type_float ) {
2313 Error( "wrong immediate type for '-' on variable '%s'", name );
2317 if ( immediateType != type ) {
2318 Error( "wrong immediate type for '%s'", name );
2321 // global variables are initialized at start up
2322 if ( type == &type_string ) {
2323 def->SetString( token, false );
2326 immediate._float = -immediate._float;
2328 def->SetValue( immediate, false );
2332 } else if ( type == &type_string ) {
2333 // local strings on the stack are initialized in the interpreter
2334 if ( scope->Type() != ev_function ) {
2335 def->SetString( "", false );
2337 } else if ( type->Inherits( &type_object ) ) {
2338 if ( scope->Type() != ev_function ) {
2339 def->SetObject( NULL );
2346 idCompiler::GetTypeForEventArg
2349 idTypeDef *idCompiler::GetTypeForEventArg( char argType ) {
2353 case D_EVENT_INTEGER :
2354 // this will get converted to int by the interpreter
2358 case D_EVENT_FLOAT :
2362 case D_EVENT_VECTOR :
2363 type = &type_vector;
2366 case D_EVENT_STRING :
2367 type = &type_string;
2370 case D_EVENT_ENTITY :
2371 case D_EVENT_ENTITY_NULL :
2372 type = &type_entity;
2379 case D_EVENT_TRACE :
2380 // This data type isn't available from script
2395 idCompiler::ParseEventDef
2398 void idCompiler::ParseEventDef( idTypeDef *returnType, const char *name ) {
2399 const idTypeDef *expectedType;
2405 const idEventDef *ev;
2408 ev = idEventDef::FindEvent( name );
2410 Error( "Unknown event '%s'", name );
2413 // set the return type
2414 expectedType = GetTypeForEventArg( ev->GetReturnType() );
2415 if ( !expectedType ) {
2416 Error( "Invalid return type '%c' in definition of '%s' event.", ev->GetReturnType(), name );
2418 if ( returnType != expectedType ) {
2419 Error( "Return type doesn't match internal return type '%s'", expectedType->Name() );
2422 idTypeDef newtype( ev_function, NULL, name, type_function.Size(), returnType );
2426 format = ev->GetArgFormat();
2427 num = strlen( format );
2428 for( i = 0; i < num; i++ ) {
2429 expectedType = GetTypeForEventArg( format[ i ] );
2430 if ( !expectedType || ( expectedType == &type_void ) ) {
2431 Error( "Invalid parameter '%c' in definition of '%s' event.", format[ i ], name );
2434 argType = ParseType();
2435 ParseName( parmName );
2436 if ( argType != expectedType ) {
2437 Error( "The type of parm %d ('%s') does not match the internal type '%s' in definition of '%s' event.",
2438 i + 1, parmName.c_str(), expectedType->Name(), name );
2441 newtype.AddFunctionParm( argType, "" );
2443 if ( i < num - 1 ) {
2444 if ( CheckToken( ")" ) ) {
2445 Error( "Too few parameters for event definition. Internal definition has %d parameters.", num );
2450 if ( !CheckToken( ")" ) ) {
2451 Error( "Too many parameters for event definition. Internal definition has %d parameters.", num );
2455 type = gameLocal.program.FindType( name );
2457 if ( !newtype.MatchesType( *type ) || ( type->def->value.functionPtr->eventdef != ev ) ) {
2458 Error( "Type mismatch on redefinition of '%s'", name );
2461 type = gameLocal.program.AllocType( newtype );
2462 type->def = gameLocal.program.AllocDef( type, name, &def_namespace, true );
2464 function_t &func = gameLocal.program.AllocFunction( type->def );
2466 func.parmSize.SetNum( num );
2467 for( i = 0; i < num; i++ ) {
2468 argType = newtype.GetParmType( i );
2469 func.parmTotal += argType->Size();
2470 func.parmSize[ i ] = argType->Size();
2473 // mark the parms as local
2474 func.locals = func.parmTotal;
2480 idCompiler::ParseDefs
2482 Called at the outer layer and when a local statement is hit
2485 void idCompiler::ParseDefs( void ) {
2491 if ( CheckToken( ";" ) ) {
2492 // skip semicolons, which are harmless and ok syntax
2497 if ( type == &type_scriptevent ) {
2500 ParseEventDef( type, name );
2506 if ( type == &type_namespace ) {
2507 def = gameLocal.program.GetDef( type, name, scope );
2509 def = gameLocal.program.AllocDef( type, name, scope, true );
2511 ParseNamespace( def );
2512 } else if ( CheckToken( "::" ) ) {
2513 def = gameLocal.program.GetDef( NULL, name, scope );
2515 Error( "Unknown object name '%s'", name.c_str() );
2522 ParseFunctionDef( type, name.c_str() );
2524 } else if ( type == &type_object ) {
2525 ParseObjectDef( name.c_str() );
2526 } else if ( CheckToken( "(" ) ) { // check for a function prototype or declaraction
2527 ParseFunctionDef( type, name.c_str() );
2529 ParseVariableDef( type, name.c_str() );
2530 while( CheckToken( "," ) ) {
2532 ParseVariableDef( type, name.c_str() );
2540 idCompiler::ParseNamespace
2542 Parses anything within a namespace definition
2545 void idCompiler::ParseNamespace( idVarDef *newScope ) {
2549 if ( newScope != &def_namespace ) {
2557 if ( ( newScope != &def_namespace ) && CheckToken( "}" ) ) {
2569 idCompiler::CompileFile
2571 compiles the 0 terminated text, adding definitions to the program structure
2574 void idCompiler::CompileFile( const char *text, const char *filename, bool toConsole ) {
2575 idTimer compile_time;
2578 compile_time.Start();
2580 scope = &def_namespace;
2586 immediateType = NULL;
2587 currentLineNumber = 0;
2588 console = toConsole;
2590 memset( &immediate, 0, sizeof( immediate ) );
2592 parser.SetFlags( LEXFL_ALLOWMULTICHARLITERALS );
2593 parser.LoadMemory( text, strlen( text ), filename );
2594 parserPtr = &parser;
2596 // unread tokens to include script defines
2597 token = SCRIPT_DEFAULTDEFS;
2598 token.type = TT_STRING;
2599 token.subtype = token.Length();
2600 token.line = token.linesCrossed = 0;
2601 parser.UnreadToken( &token );
2604 token.type = TT_NAME;
2605 token.subtype = token.Length();
2606 token.line = token.linesCrossed = 0;
2607 parser.UnreadToken( &token );
2610 token.type = TT_PUNCTUATION;
2611 token.subtype = P_PRECOMP;
2612 token.line = token.linesCrossed = 0;
2613 parser.UnreadToken( &token );
2615 // init the current token line to be the first line so that currentLineNumber is set correctly in NextToken
2622 while( !eof && !error ) {
2623 // parse from global namespace
2624 ParseNamespace( &def_namespace );
2628 catch( idCompileError &err ) {
2632 // don't print line number of an error if were calling script from the console using the "script" command
2633 sprintf( error, "Error: %s\n", err.error );
2635 sprintf( error, "Error: file %s, line %d: %s\n", gameLocal.program.GetFilename( currentFileNumber ), currentLineNumber, err.error );
2638 parser.FreeSource();
2640 throw idCompileError( error );
2643 parser.FreeSource();
2645 compile_time.Stop();
2647 gameLocal.Printf( "Compiled '%s': %.1f ms\n", filename, compile_time.Milliseconds() );