MuckyFoot-UrbanChaos/MuckyBasic/cg.cpp
2017-05-20 11:14:17 +10:00

2728 lines
54 KiB
C++

//
// The code generator. Converts the output of the parse into
// an executable.
//
#include "always.h"
#include "cg.h"
#include "lex.h"
#include "link.h"
#include "ml.h"
#include "parse.h"
#include "sysvar.h"
#include "st.h"
//
// The result of code generation.
//
CBYTE *CG_output;
SLONG CG_num_errors;
SLONG CG_num_warnings;
//
// The instructions
//
#define CG_MAX_INSTRUCTIONS 65536
SLONG CG_instruction[CG_MAX_INSTRUCTIONS];
SLONG CG_instruction_upto;
//
// The line we are currently generating code for.
//
SLONG CG_generating_line;
//
// The functions.
//
typedef struct
{
CBYTE *name; // The function name...
SLONG debug_name; // Index into the debug data.
SLONG num_args;
SLONG arg_upto;
SLONG line; // The entrypoint of the function.
SLONG endline; // The last line of the function.
SLONG num_locals; // How many of the args are actually locals!
} CG_Func;
#define CG_MAX_FUNCS 4096
CG_Func CG_func[CG_MAX_FUNCS];
SLONG CG_func_upto;
//
// For each line of code.
//
typedef struct
{
SLONG code; // Index into the instruction array. Where each line's code is.
} CG_Line;
#define CG_MAX_LINES 16384
CG_Line CG_line[CG_MAX_LINES];
SLONG CG_line_upto;
//
// Line numbers that must be converted to instruction
// indices.
//
#define CG_FORWARD_GOTO_TYPE_GOTO 0
#define CG_FORWARD_GOTO_TYPE_GOSUB 1
#define CG_FORWARD_GOTO_TYPE_CALL 2
typedef struct
{
SLONG type;
SLONG instruction;
} CG_Forward_goto;
#define CG_MAX_FORWARD_GOTOS 4096
CG_Forward_goto CG_forward_goto[CG_MAX_FORWARD_GOTOS];
SLONG CG_forward_goto_upto;
//
// Single-line IF statements.
//
typedef struct
{
SLONG ifcode;
SLONG instruction; // The instruction containing the forward jump of the IF condition that jumps over the THEN statement code.
} CG_If;
#define CG_MAX_IFS_PER_LINE 256
CG_If CG_if[CG_MAX_IFS_PER_LINE];
SLONG CG_if_upto;
//
// Multi-line IF statements.
//
#define CG_MIF_FLAG_FOUND_MELSE (1 << 0)
#define CG_MIF_FLAG_FOUND_MENDIF (1 << 1)
typedef struct
{
ULONG flag;
SLONG iffalsejump;
SLONG afterthenjump;
} CG_Mif;
#define CG_MAX_MIFS 4096
CG_Mif CG_mif[CG_MAX_MIFS];
SLONG CG_mif_upto;
//
// All the FOR-NEXT loops...
//
#define CG_FOR_TYPE_ANON 0 // No lvalue associated with this FOR-NEXT loop.
#define CG_FOR_TYPE_LVALUE 1
typedef struct
{
SLONG type;
PARSE_Node *lvalue; // The lvalue that this FOR-NEXT loop is acting on
SLONG loopto; // The instruction that corresponding NEXT statements should jump to.
SLONG overstep; // The instruction which should contain the address of the instruction after the STEP statement.
SLONG forcode; // The unique code identifying this FOR loop.
SLONG afternext; // The instruction which should contain the address of the instruction after the NEXT statement.
} CG_For;
#define CG_MAX_FORS 4096
CG_For CG_for[CG_MAX_FORS];
SLONG CG_for_upto;
//
// The while loops.
//
#define CG_WHILE_FLAG_FOUND_LOOP (1 << 0)
typedef struct
{
SLONG flag;
SLONG iffalsejump;
SLONG whilecode;
SLONG loopto;
} CG_While;
#define CG_MAX_WHILES 4096
CG_While CG_while[CG_MAX_WHILES];
SLONG CG_while_upto;
//
// The number of globals used by the program.
//
SLONG CG_global_upto;
//
// The number of field_ids used so far.
//
SLONG CG_field_id_upto;
//
// The string table.
//
SLONG CG_data_table_max;
UBYTE *CG_data_table;
SLONG CG_data_table_upto;
//
// The function we are currently generating code for.
//
SLONG CG_in_function;
//
// Every jump instruction's target address.
//
typedef struct
{
SLONG instruction;
} CG_Jump;
#define CG_MAX_JUMPS 16384
CG_Jump CG_jump[CG_MAX_JUMPS];
SLONG CG_jump_upto;
//
// Every reference to a global.
//
typedef struct
{
SLONG instruction;
} CG_Globalref;
#define CG_MAX_GLOBALREFS 16384
CG_Globalref CG_globalref[CG_MAX_GLOBALREFS];
SLONG CG_globalref_upto;
//
// Every refrence to a field.
//
typedef struct
{
SLONG instruction;
} CG_Fieldref;
#define CG_MAX_FIELDREFS 16384
CG_Fieldref CG_fieldref[CG_MAX_FIELDREFS];
SLONG CG_fieldref_upto;
//
// Every reference to an undefined function.
//
typedef struct
{
SLONG name; // Index into the debug data array.
SLONG instruction;
} CG_Undefref;
#define CG_MAX_UNDEFREFS 16384
CG_Undefref CG_undefref[CG_MAX_UNDEFREFS];
SLONG CG_undefref_upto;
//
// Every reference into the data table.
//
typedef struct
{
SLONG instruction;
} CG_Datatableref;
#define CG_MAX_DATATABLEREFS 16384
CG_Datatableref CG_datatableref[CG_MAX_DATATABLEREFS];
SLONG CG_datatableref_upto;
//
// Debug data.
//
#define CG_MAX_DEBUG_DATA 65536
CBYTE CG_debug_data[CG_MAX_DEBUG_DATA];
SLONG CG_debug_data_upto;
//
// Adds a string to the debug data and returns
// an index to it.
//
SLONG CG_add_string_to_debug_data(CBYTE *string)
{
SLONG length = strlen(string) + 1; // + 1 to include the terminating NULL
if (CG_debug_data_upto + length > CG_MAX_DEBUG_DATA)
{
//
// ERROR!
//
ASSERT(0);
}
SLONG ans = CG_debug_data_upto;
strcpy(CG_debug_data + CG_debug_data_upto, string);
CG_debug_data_upto += length;
return ans;
}
//
// Adds a string to the string table and returns the index
// into the string table for where it was stored.
//
SLONG CG_add_string(CBYTE *string)
{
SLONG length = strlen(string) + 1; // + 1 to include the terminating NULL.
if (CG_data_table_upto + length > CG_data_table_max)
{
CG_data_table_max *= 2;
CG_data_table = (UBYTE *) realloc(CG_data_table, sizeof(UBYTE) * CG_data_table_max);
}
SLONG ans = CG_data_table_upto;
strcpy(((CBYTE *) CG_data_table) + CG_data_table_upto, string);
CG_data_table_upto += length;
return ans;
}
//
// The callback function for adding labels and variables to the
// symbol table.
//
SLONG CG_callback_add_labels_and_variables(PARSE_Node *pn)
{
switch(pn->type)
{
case PARSE_NODE_TYPE_LABEL:
if (ST_find(pn->label))
{
//
// ERROR!
//
ASSERT(0);
return FALSE;
}
else
{
ST_add(ST_TABLE_GLOBAL, pn->label, CG_generating_line, 0);
}
break;
case PARSE_NODE_TYPE_VAR_ADDRESS:
case PARSE_NODE_TYPE_VAR_VALUE:
if (ST_find(pn->variable))
{
//
// We've already added this variable to the symbol table
// and assigned it a 'global' memory number.
//
}
else
{
ST_add(ST_TABLE_GLOBAL, pn->variable, CG_global_upto++, 0);
}
break;
case PARSE_NODE_TYPE_FIELD:
if (ST_find(pn->field))
{
//
// Already assigned this structure field a field_id.
//
}
else
{
ST_add(ST_TABLE_GLOBAL, pn->field, CG_field_id_upto++, 0);
}
break;
case PARSE_NODE_TYPE_FUNCTION:
if (CG_in_function)
{
//
// ERROR! Two function definitions in a row without an ENDFUNC.
//
ASSERT(0);
}
CG_in_function = CG_func_upto;
//
// Allocate a new function structure.
//
ASSERT(WITHIN(CG_func_upto, 1, CG_MAX_FUNCS - 1));
CG_func[CG_func_upto].name = pn->variable;
CG_func[CG_func_upto].num_args = 0;
CG_func[CG_func_upto].line = CG_generating_line;
//
// Add the function to the string table.
//
ST_add(ST_TABLE_GLOBAL, pn->variable, CG_func_upto, 0);
CG_func_upto += 1;
break;
case PARSE_NODE_TYPE_ARGUMENT:
//
// We must be inside a function!
//
if (!CG_in_function)
{
//
// ERROR!
//
ASSERT(0);
}
ASSERT(CG_in_function == CG_func_upto - 1);
//
// Add this argument to the local variables for this function.
//
if (ST_find(pn->variable))
{
if (ST_found_table != ST_TABLE_LOCAL)
{
//
// Add this variable to the local symbol table.
//
ST_add(ST_TABLE_LOCAL, pn->variable, 0, 0);
CG_func[CG_in_function].num_args += 1;
}
else
if (ST_found_table == ST_TABLE_LOCAL)
{
//
// ERROR! Mutliple defined arguements!
//
ASSERT(0);
}
}
else
{
//
// Add this variable to the local symbol table.
//
ST_add(ST_TABLE_LOCAL, pn->variable, 0, 0);
CG_func[CG_in_function].num_args += 1;
}
break;
case PARSE_NODE_TYPE_ENDFUNC:
//
// Remember the line where this function ends.
//
ASSERT(WITHIN(CG_in_function, 1, CG_MAX_FUNCS - 1));
CG_func[CG_in_function].endline = CG_generating_line;
//
// Not in a function any more.
//
CG_in_function = FALSE;
//
// We've lost all the symbols in the local symbol table.
//
ST_clear(ST_TABLE_LOCAL);
break;
case PARSE_NODE_TYPE_EXPORT:
//
// Add this variable (manged with a "->" on the front) to
// the symbol, to denote that it should be exported in the
// object file.
//
{
CBYTE export[LEX_MAX_STRING_LENGTH + 2];
sprintf(export, "->%s", pn->variable);
ST_add(ST_TABLE_GLOBAL, export, 0, 0);
}
break;
case PARSE_NODE_TYPE_LOCAL:
if (CG_in_function)
{
//
// Add this local to the function.
//
ASSERT(WITHIN(CG_in_function, 1, CG_MAX_FUNCS - 1));
if (ST_find(pn->variable))
{
if (ST_found_table == ST_TABLE_LOCAL)
{
//
// ERROR! Multiply defined locals/args
//
ASSERT(0);
}
else
{
//
// Add this local to the funcion.
//
CG_func[CG_in_function].num_args += 1;
CG_func[CG_in_function].num_locals += 1;
//
// Add this variable to the local symbol table.
//
ST_add(ST_TABLE_LOCAL, pn->variable, 0, 0);
}
}
else
{
//
// Add this local to the funcion.
//
CG_func[CG_in_function].num_args += 1;
CG_func[CG_in_function].num_locals += 1;
//
// Add this variable to the local symbol table.
//
ST_add(ST_TABLE_LOCAL, pn->variable, 0, 0);
}
}
else
{
//
// A local declaration outside of a function
// means that the variable is local to the file.
// Add it to the symbol table with "<-" on the front
// if it.
//
CBYTE local[LEX_MAX_STRING_LENGTH + 2];
sprintf(local, "<-%s", pn->variable);
ST_add(ST_TABLE_GLOBAL, local, 0, 0);
}
break;
default:
break;
}
return TRUE;
}
//
// The code generation callback function.
//
SLONG CG_callback_generate_code(PARSE_Node *pn)
{
//
// Always make sure we have 3 SLONGS worth of instructions spare.
//
if (!WITHIN(CG_instruction_upto, 0, CG_MAX_INSTRUCTIONS - 3))
{
//
// Out of memory!
//
return FALSE;
}
switch(pn->type)
{
case PARSE_NODE_TYPE_NOP:
break;
case PARSE_NODE_TYPE_EQUALS:
CG_instruction[CG_instruction_upto++] = ML_DO_EQUALS;
break;
case PARSE_NODE_TYPE_PLUS:
CG_instruction[CG_instruction_upto++] = ML_DO_ADD;
break;
case PARSE_NODE_TYPE_MINUS:
CG_instruction[CG_instruction_upto++] = ML_DO_MINUS;
break;
case PARSE_NODE_TYPE_UMINUS:
CG_instruction[CG_instruction_upto++] = ML_DO_UMINUS;
break;
case PARSE_NODE_TYPE_TIMES:
CG_instruction[CG_instruction_upto++] = ML_DO_TIMES;
break;
case PARSE_NODE_TYPE_DIVIDE:
CG_instruction[CG_instruction_upto++] = ML_DO_DIVIDE;
break;
case PARSE_NODE_TYPE_MOD:
CG_instruction[CG_instruction_upto++] = ML_DO_MOD;
break;
case PARSE_NODE_TYPE_SLUMBER:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_SLUMBER;
CG_instruction[CG_instruction_upto++] = pn->slumber;
break;
case PARSE_NODE_TYPE_FLUMBER:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_FLUMBER;
((float *) CG_instruction)[CG_instruction_upto++] = pn->flumber;
break;
case PARSE_NODE_TYPE_STRING:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_STRCONST;
CG_instruction[CG_instruction_upto++] = CG_add_string(pn->string);
//
// Remember the reference into the data table.
//
ASSERT(WITHIN(CG_datatableref_upto, 0, CG_MAX_DATATABLEREFS - 1));
CG_datatableref[CG_datatableref_upto++].instruction = CG_instruction_upto - 1;
break;
case PARSE_NODE_TYPE_VAR_VALUE:
if (!ST_find(pn->variable))
{
ASSERT(0);
}
else
{
SLONG local = (ST_found_table == ST_TABLE_LOCAL);
SLONG quick = (pn->flag & PARSE_NODE_FLAG_EXTRACT);
SLONG instruction;
//
// Can't be both these types...
//
if (local)
{
if (quick)
{
instruction = ML_DO_PUSH_LOCAL_QUICK;
}
else
{
instruction = ML_DO_PUSH_LOCAL_VALUE;
}
}
else
{
if (quick)
{
instruction = ML_DO_PUSH_GLOBAL_QUICK;
}
else
{
instruction = ML_DO_PUSH_GLOBAL_VALUE;
}
//
// Remember the reference to the global.
//
ASSERT(WITHIN(CG_globalref_upto, 0, CG_MAX_GLOBALREFS - 1));
CG_globalref[CG_globalref_upto++].instruction = CG_instruction_upto + 1;
}
CG_instruction[CG_instruction_upto++] = instruction;
CG_instruction[CG_instruction_upto++] = ST_found_value;
}
break;
case PARSE_NODE_TYPE_IF:
//
// Store where this if instruction is so we can set the
// if condition goto to be after the 'then' code.
//
ASSERT(WITHIN(CG_if_upto, 0, CG_MAX_IFS_PER_LINE - 1));
CG_if[CG_if_upto].ifcode = pn->ifcode;
CG_if[CG_if_upto].instruction = CG_instruction_upto + 1;
CG_if_upto++;
CG_instruction[CG_instruction_upto++] = ML_DO_IF_FALSE_GOTO;
CG_instruction[CG_instruction_upto++] = 0; // To be filled in when we get the corresponding PARSE_NODE_TYPE_FAKE_THEN node.
//
// Remember the jump instruction.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
break;
case PARSE_NODE_TYPE_FAKE_THEN:
//
// Look for the corresponding IF.
//
{
SLONG i;
for (i = 0; i < CG_if_upto; i++)
{
if (CG_if[i].ifcode == pn->ifcode)
{
//
// Found it! Make the IF_FALSE_GOTO point to the current instruction.
//
ASSERT(WITHIN(CG_if[i].instruction, 0, CG_instruction_upto - 1));
CG_instruction[CG_if[i].instruction] = CG_instruction_upto;
break;
}
}
//
// THEN found without matching IF?
//
ASSERT(i != CG_if_upto);
}
break;
case PARSE_NODE_TYPE_FAKE_ELSE:
//
// Look for the corresponding IF.
//
{
SLONG i;
for (i = 0; i < CG_if_upto; i++)
{
if (CG_if[i].ifcode == pn->ifcode)
{
//
// Found it! This IF_FALSE_GOTO instruction must
// jump an instruction further to skip over the
// goto we are just about to put in...
//
ASSERT(WITHIN(CG_if[i].instruction, 0, CG_instruction_upto - 1));
CG_instruction[CG_if[i].instruction] = CG_instruction_upto + 2;
//
// Insert a GOTO so that the THEN code doesn't
// leak into the ELSE code.
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOTO;
CG_instruction[CG_instruction_upto++] = 0; // To be filled in when we get the corresponding PARSE_NODE_TYPE_FAKE_ENDIF node.
CG_if[i].instruction = CG_instruction_upto - 1;
//
// Remember where the jump instruction is.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
break;
}
}
//
// ELSE found without matching IF?
//
ASSERT(i != CG_if_upto);
}
break;
case PARSE_NODE_TYPE_FAKE_END_ELSE:
//
// Look for the corresponding IF.
//
{
SLONG i;
for (i = 0; i < CG_if_upto; i++)
{
if (CG_if[i].ifcode == pn->ifcode)
{
//
// Found it! Make the GOTO after the 'THEN' code point to the current instruction.
//
ASSERT(WITHIN(CG_if[i].instruction, 0, CG_instruction_upto - 1));
CG_instruction[CG_if[i].instruction] = CG_instruction_upto;
break;
}
}
//
// ENDELSE found without matching IF?
//
ASSERT(i != CG_if_upto);
}
break;
case PARSE_NODE_TYPE_GOTO:
case PARSE_NODE_TYPE_GOSUB:
if (!ST_find(pn->label))
{
//
// ERROR!
//
ASSERT(0);
return FALSE;
}
else
{
switch(pn->type)
{
case PARSE_NODE_TYPE_GOTO: CG_instruction[CG_instruction_upto++] = ML_DO_GOTO; break;
case PARSE_NODE_TYPE_GOSUB: CG_instruction[CG_instruction_upto++] = ML_DO_GOSUB; break;
default:
ASSERT(0);
break;
}
//
// If this is a backwards goto or gosub then we can put the instruction
// index here.
//
if (ST_found_value <= CG_generating_line)
{
CG_instruction[CG_instruction_upto++] = CG_line[ST_found_value].code;
}
else
{
//
// The line number to be converted to instruction after code generation.
//
CG_instruction[CG_instruction_upto++] = ST_found_value;
//
// Remember all the line numbers we must convert.
//
ASSERT(WITHIN(CG_forward_goto_upto, 0, CG_MAX_FORWARD_GOTOS - 1));
CG_forward_goto[CG_forward_goto_upto].instruction = CG_instruction_upto - 1;
switch(pn->type)
{
case PARSE_NODE_TYPE_GOTO: CG_forward_goto[CG_forward_goto_upto].type = CG_FORWARD_GOTO_TYPE_GOTO; break;
case PARSE_NODE_TYPE_GOSUB: CG_forward_goto[CG_forward_goto_upto].type = CG_FORWARD_GOTO_TYPE_GOSUB; break;
default:
ASSERT(0);
break;
}
CG_forward_goto_upto++;
}
//
// Remember where the jump instruction is.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
}
break;
case PARSE_NODE_TYPE_LABEL:
break;
case PARSE_NODE_TYPE_GT:
CG_instruction[CG_instruction_upto++] = ML_DO_GT;
break;
case PARSE_NODE_TYPE_LT:
CG_instruction[CG_instruction_upto++] = ML_DO_LT;
break;
case PARSE_NODE_TYPE_GTEQ:
CG_instruction[CG_instruction_upto++] = ML_DO_GTEQ;
break;
case PARSE_NODE_TYPE_LTEQ:
CG_instruction[CG_instruction_upto++] = ML_DO_LTEQ;
break;
case PARSE_NODE_TYPE_AND:
CG_instruction[CG_instruction_upto++] = ML_DO_AND;
break;
case PARSE_NODE_TYPE_OR:
CG_instruction[CG_instruction_upto++] = ML_DO_OR;
break;
case PARSE_NODE_TYPE_NOT:
CG_instruction[CG_instruction_upto++] = ML_DO_NOT;
break;
case PARSE_NODE_TYPE_DOT:
break;
case PARSE_NODE_TYPE_LOCAL:
//
// LOCAL statement outside of functions have already been
// handled (they've added their variable with "<-" at the start
// to the symbol table).
//
if (CG_in_function)
{
//
// There shouldn't already be a local with this name.
//
ASSERT(!(ST_find(pn->variable) && ST_found_table == ST_TABLE_LOCAL));
//
// Add the local to the symbol table.
//
ST_add(ST_TABLE_LOCAL, pn->variable, CG_func[CG_in_function].arg_upto, 0);
CG_func[CG_in_function].arg_upto += 1;
}
break;
case PARSE_NODE_TYPE_PRINT:
CG_instruction[CG_instruction_upto++] = ML_DO_PRINT;
break;
case PARSE_NODE_TYPE_ASSIGN:
CG_instruction[CG_instruction_upto++] = ML_DO_ASSIGN;
break;
case PARSE_NODE_TYPE_VAR_ADDRESS:
if (!ST_find(pn->variable))
{
ASSERT(0);
}
else
{
CG_instruction[CG_instruction_upto++] = (ST_found_table == ST_TABLE_LOCAL) ? ML_DO_PUSH_LOCAL_ADDRESS : ML_DO_PUSH_GLOBAL_ADDRESS;
CG_instruction[CG_instruction_upto++] = ST_found_value;
if (ST_found_table != ST_TABLE_LOCAL)
{
//
// Remember the reference to the global.
//
ASSERT(WITHIN(CG_globalref_upto, 0, CG_MAX_GLOBALREFS - 1));
CG_globalref[CG_globalref_upto++].instruction = CG_instruction_upto - 1;
}
}
break;
case PARSE_NODE_TYPE_BOOLEAN:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_BOOLEAN;
CG_instruction[CG_instruction_upto++] = pn->boolean;
break;
case PARSE_NODE_TYPE_SQRT:
CG_instruction[CG_instruction_upto++] = ML_DO_SQRT;
break;
case PARSE_NODE_TYPE_NEWLINE:
CG_instruction[CG_instruction_upto++] = ML_DO_NEWLINE;
break;
case PARSE_NODE_TYPE_ABS:
CG_instruction[CG_instruction_upto++] = ML_DO_ABS;
break;
case PARSE_NODE_TYPE_PUSH_FIELD_ADDRESS:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_FIELD_ADDRESS;
break;
case PARSE_NODE_TYPE_FIELD:
if (!ST_find(pn->field))
{
ASSERT(0);
}
else
{
CG_instruction[CG_instruction_upto++] = ST_found_value;
//
// Remember the reference to the field.
//
ASSERT(WITHIN(CG_fieldref_upto, 0, CG_MAX_FIELDREFS - 1));
CG_fieldref[CG_fieldref_upto++].instruction = CG_instruction_upto - 1;
}
break;
case PARSE_NODE_TYPE_PUSH_FIELD_VALUE:
if (pn->flag & PARSE_NODE_FLAG_EXTRACT)
{
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_FIELD_QUICK;
}
else
{
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_FIELD_VALUE;
}
break;
case PARSE_NODE_TYPE_EXP_LIST:
break;
case PARSE_NODE_TYPE_PUSH_ARRAY_ADDRESS:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_ARRAY_ADDRESS;
CG_instruction[CG_instruction_upto++] = pn->dimensions;
break;
case PARSE_NODE_TYPE_PUSH_ARRAY_VALUE:
if (pn->flag & PARSE_NODE_FLAG_EXTRACT)
{
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_ARRAY_QUICK;
}
else
{
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_ARRAY_VALUE;
}
CG_instruction[CG_instruction_upto++] = pn->dimensions;
break;
case PARSE_NODE_TYPE_INPUT:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_INPUT;
break;
case PARSE_NODE_TYPE_UNDEFINED:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_UNDEFINED;
CG_instruction[CG_instruction_upto++] = 0;
break;
case PARSE_NODE_TYPE_STATEMENT_LIST:
break;
case PARSE_NODE_TYPE_EXIT:
CG_instruction[CG_instruction_upto++] = ML_DO_EXIT;
break;
case PARSE_NODE_TYPE_RETURN:
if (CG_in_function)
{
ASSERT(WITHIN(CG_in_function, 0, CG_func_upto - 1));
if (pn->child1 == NULL)
{
//
// The function doesn't return anything. Make it
// return UNDEFINED.
//
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_UNDEFINED;
CG_instruction[CG_instruction_upto++] = 0;
}
//
// A return from function rather than a simple GOSUB.
//
CG_instruction[CG_instruction_upto++] = ML_DO_ENDFUNC;
CG_instruction[CG_instruction_upto++] = CG_func[CG_in_function].num_args;
}
else
{
CG_instruction[CG_instruction_upto++] = ML_DO_RETURN;
if (pn->child1)
{
//
// ERROR! Only RETURNs inside functions can return values.
//
ASSERT(0);
}
}
break;
case PARSE_NODE_TYPE_XOR:
CG_instruction[CG_instruction_upto++] = ML_DO_XOR;
break;
case PARSE_NODE_TYPE_FOR:
//
// This is a forward goto so we skip over the STEP code.
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOTO;
CG_instruction[CG_instruction_upto++] = 0; // To be filled in later by the FAKE_ENDFOR parse node.
//
// Remember where the jump instruction is.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
//
// This is where we GOTO to for another iteration.
//
ASSERT(WITHIN(CG_for_upto, 0, CG_MAX_FORS - 1));
CG_for[CG_for_upto].type = CG_FOR_TYPE_ANON;
CG_for[CG_for_upto].lvalue = NULL;
CG_for[CG_for_upto].loopto = CG_instruction_upto;
CG_for[CG_for_upto].overstep = CG_instruction_upto - 1;
CG_for[CG_for_upto].forcode = pn->forcode;
if (pn->lvalue)
{
CG_for[CG_for_upto].type = CG_FOR_TYPE_LVALUE;
CG_for[CG_for_upto].lvalue = pn->lvalue;
}
CG_for_upto += 1;
break;
case PARSE_NODE_TYPE_FAKE_END_FOR:
//
// Find the FOR statement corresponding to this ENDFOR node.
//
{
SLONG i;
for (i = CG_for_upto - 1; i >= 0; i--)
{
if (CG_for[i].forcode == pn->forcode)
{
ASSERT(WITHIN(CG_for[i].overstep, 0, CG_instruction_upto - 1));
CG_instruction[CG_for[i].overstep] = CG_instruction_upto;
goto found_corresponding_for;
}
}
ASSERT(0);
found_corresponding_for:;
}
break;
case PARSE_NODE_TYPE_NEXT:
{
SLONG i;
for (i = CG_for_upto - 1; i >= 0; i--)
{
if (CG_for[i].forcode == 0)
{
//
// This FOR has alreay been matched by a next...
//
continue;
}
if (pn->lvalue == NULL)
{
//
// Match this FOR loop...
//
}
else
{
//
// Only match a FOR loop with the correct variable.
//
if (CG_for[i].type == CG_FOR_TYPE_LVALUE && PARSE_trees_the_same(CG_for[i].lvalue, pn->lvalue))
{
//
// This is our FOR loop!
//
}
else
{
//
// This is not our FOR loop.
//
continue;
}
}
//
// This is the FOR-LOOP to match up with- free it up.
//
CG_for[i].forcode = 0;
//
// Loop back to the increment and condition.
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOTO;
CG_instruction[CG_instruction_upto++] = CG_for[i].loopto;
//
// Remember where the jump instruction is.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
//
// Fill in the jump for the condition.
//
ASSERT(WITHIN(CG_for[i].afternext, 0, CG_instruction_upto - 1));
CG_instruction[CG_for[i].afternext] = CG_instruction_upto;
//
// Get rid of the lvalue node so the tree traversal doesn't
// generate code for it.
//
pn->lvalue = NULL;
goto found_matching_for;
}
//
// ERROR! No matching FOR loop for the NEXT statement.
//
ASSERT(0);
found_matching_for:;
}
break;
case PARSE_NODE_TYPE_FAKE_FOR_COND:
//
// Find the FOR statement corresponding to this FOR_COND node.
//
{
SLONG i;
for (i = CG_for_upto - 1; i >= 0; i--)
{
if (CG_for[i].forcode == pn->forcode)
{
//
// Generate code to jump to after the NEXT in the
// condition is TRUE.
//
CG_instruction[CG_instruction_upto++] = ML_DO_IF_TRUE_GOTO;
CG_instruction[CG_instruction_upto++] = 0;
CG_for[i].afternext = CG_instruction_upto - 1;
//
// Remember the jump instruction.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
goto found_the_for;
}
}
//
// ERROR! No matching FOR loop for the NEXT statement.
//
ASSERT(0);
found_the_for:;
}
break;
case PARSE_NODE_TYPE_NOTEQUAL:
CG_instruction[CG_instruction_upto++] = ML_DO_NOTEQUAL;
break;
case PARSE_NODE_TYPE_RANDOM:
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_RANDOM_SLUMBER;
break;
case PARSE_NODE_TYPE_SWAP:
CG_instruction[CG_instruction_upto++] = ML_DO_SWAP;
break;
case PARSE_NODE_TYPE_MIF:
//
// Create code to jump over the THEN code if the condition
// code evaluates to FALSE.
//
CG_instruction[CG_instruction_upto++] = ML_DO_IF_FALSE_GOTO;
CG_instruction[CG_instruction_upto++] = 0; // To be filled in later when we get the MELSE or MENDIF node.
//
// Remember the jump instruction.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
//
// Create a new entry in the multi-line IF array.
//
ASSERT(WITHIN(CG_mif_upto, 0, CG_MAX_MIFS - 1));
CG_mif[CG_mif_upto].flag = 0;
CG_mif[CG_mif_upto].iffalsejump = CG_instruction_upto - 1;
CG_mif[CG_mif_upto].afterthenjump = 0;
CG_mif_upto += 1;
break;
case PARSE_NODE_TYPE_MELSE:
//
// An ELSE instruction in a multi-line IF. Match it up with
// the nearest MIF.
//
{
SLONG i;
for (i = CG_mif_upto - 1; i >= 0; i--)
{
if (CG_mif[i].flag & CG_MIF_FLAG_FOUND_MENDIF)
{
//
// This MIF has already been matched with an ENDIF.
//
}
else
{
//
// This is our MIF instruction.
//
if (CG_mif[i].flag & CG_MIF_FLAG_FOUND_MELSE)
{
//
// ERROR! Badly formed, nested multi-line ifs...
//
ASSERT(0);
}
//
// Add instructions to jump over the ELSE code to
// the end of the THEN code.
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOTO;
CG_instruction[CG_instruction_upto++] = 0; // To be filled in later when we get the MENDIF node.
//
// Remember where the jump instruction is.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
//
// Update the multiline-if structure now we have found the else.
//
CG_mif[i].flag |= CG_MIF_FLAG_FOUND_MELSE;
CG_mif[i].afterthenjump = CG_instruction_upto - 1;
//
// Set the target of the IF_FALSE_GOTO jump instruction
// inserted by the MIF node.
//
ASSERT(WITHIN(CG_mif[i].iffalsejump, 0, CG_instruction_upto - 2));
CG_instruction[CG_mif[i].iffalsejump] = CG_instruction_upto;
goto found_mif_for_melse;
}
}
//
// ERROR!
//
ASSERT(0);
found_mif_for_melse:;
}
break;
case PARSE_NODE_TYPE_MENDIF:
//
// An ENDIF instruction in a multi-line IF. Match it up with
// the nearest MIF.
//
{
SLONG i;
for (i = CG_mif_upto - 1; i >= 0; i--)
{
if (CG_mif[i].flag & CG_MIF_FLAG_FOUND_MENDIF)
{
//
// This MIF has already been matched with an ENDIF.
//
}
else
{
CG_mif[i].flag |= CG_MIF_FLAG_FOUND_MENDIF;
if (CG_mif[i].flag & CG_MIF_FLAG_FOUND_MELSE)
{
//
// Set the target of the jump put in by the MELSE node.
//
ASSERT(WITHIN(CG_mif[i].afterthenjump, 0, CG_instruction_upto - 2));
CG_instruction[CG_mif[i].afterthenjump] = CG_instruction_upto;
}
else
{
//
// Set the target of the jump put in by the MIF node.
//
ASSERT(WITHIN(CG_mif[i].iffalsejump, 0, CG_instruction_upto - 2));
CG_instruction[CG_mif[i].iffalsejump] = CG_instruction_upto;
}
goto found_mif_for_endif;
}
}
//
// ERROR!
//
ASSERT(0);
found_mif_for_endif:;
}
break;
case PARSE_NODE_TYPE_WHILE:
//
// Create a new entry in the while array.
//
ASSERT(WITHIN(CG_while_upto, 0, CG_MAX_WHILES - 1));
CG_while[CG_while_upto].flag = 0;
CG_while[CG_while_upto].iffalsejump = 0;
CG_while[CG_while_upto].whilecode = pn->whilecode;
CG_while[CG_while_upto].loopto = CG_instruction_upto;
CG_while_upto += 1;
break;
case PARSE_NODE_TYPE_FAKE_WHILE_COND:
//
// Create code to jump over the body of the While-loop if the condition
// code evaluates to FALSE.
//
CG_instruction[CG_instruction_upto++] = ML_DO_IF_FALSE_GOTO;
CG_instruction[CG_instruction_upto++] = 0; // To be filled in later when we LOOP node.
//
// Remember the jump instruction.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
//
// Find our while loop.
//
{
SLONG i;
for (i = CG_while_upto - 1; i >= 0; i--)
{
if (CG_while[i].whilecode == pn->whilecode)
{
//
// Found our while loop!
//
CG_while[i].iffalsejump = CG_instruction_upto - 1;
goto found_our_while_loop;
}
}
//
// Oh dear...
//
ASSERT(0);
found_our_while_loop:;
}
break;
case PARSE_NODE_TYPE_LOOP:
//
// Look for the nearest unmatched while loop.
//
{
SLONG i;
for (i = CG_while_upto - 1; i >= 0; i--)
{
if (CG_while[i].flag & CG_WHILE_FLAG_FOUND_LOOP)
{
//
// Already been matched up.
//
}
else
{
ASSERT(WITHIN(CG_while[i].loopto , 0, CG_instruction_upto - 1));
ASSERT(WITHIN(CG_while[i].iffalsejump, 0, CG_instruction_upto - 1));
CG_while[i].flag |= CG_WHILE_FLAG_FOUND_LOOP;
//
// Loop back to the beginning of the while loop.
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOTO;
CG_instruction[CG_instruction_upto++] = CG_while[i].loopto;
//
// Remember where the jump instruction is.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
//
// Set the target of the jump put in by the WHILE node.
//
CG_instruction[CG_while[i].iffalsejump] = CG_instruction_upto;
goto found_while;
}
}
//
// ERROR!
//
ASSERT(0);
found_while:;
}
break;
case PARSE_NODE_TYPE_FUNCTION:
if (CG_in_function)
{
//
// ERROR!
//
ASSERT(0);
}
else
{
//
// Find our function.
//
if (!ST_find(pn->variable))
{
//
// ERROR!
//
ASSERT(0);
}
else
{
CG_in_function = ST_found_value;
ASSERT(WITHIN(CG_in_function, 0, CG_func_upto - 1));
ASSERT(strcmp(CG_func[CG_in_function].name, pn->variable) == 0);
//
// Code to jump over the body of the function if execution
// leaks down to the function.
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOTO;
CG_instruction[CG_instruction_upto++] = CG_func[CG_in_function].endline + 1;
//
// Remember where the jump instruction is.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
//
// This is a forward goto..
//
ASSERT(WITHIN(CG_forward_goto_upto, 0, CG_MAX_FORWARD_GOTOS - 1));
CG_forward_goto[CG_forward_goto_upto].type = CG_FORWARD_GOTO_TYPE_GOTO;
CG_forward_goto[CG_forward_goto_upto].instruction = CG_instruction_upto - 1;
CG_forward_goto_upto++;
//
// The numbering of our arugments and local variables
//
CG_func[CG_in_function].arg_upto = 0;
//
// Enter the function. Easy!
//
CG_instruction[CG_instruction_upto++] = ML_DO_ENTERFUNC;
CG_instruction[CG_instruction_upto++] = CG_func[CG_in_function].num_args;
}
}
break;
case PARSE_NODE_TYPE_ARGUMENT:
//
// We must be inside a function!
//
if (!CG_in_function)
{
//
// ERROR!
//
ASSERT(0);
}
//
// There shouldn't already be a local with this name.
//
ASSERT(!(ST_find(pn->variable) && ST_found_table == ST_TABLE_LOCAL));
//
// Add this argument to the local variables for this function.
//
ST_add(ST_TABLE_LOCAL, pn->variable, CG_func[CG_in_function].arg_upto, 0);
CG_func[CG_in_function].arg_upto += 1;
break;
case PARSE_NODE_TYPE_ENDFUNC:
//
// The function doesn't return anything. Make it
// return UNDEFINED.
//
ASSERT(WITHIN(CG_in_function, 0, CG_func_upto - 1));
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_UNDEFINED;
CG_instruction[CG_instruction_upto++] = 0;
CG_instruction[CG_instruction_upto++] = ML_DO_ENDFUNC;
CG_instruction[CG_instruction_upto++] = CG_func[CG_in_function].num_args;
//
// Not in a function any more.
//
CG_in_function = FALSE;
//
// We've lost all the symbols in the local symbol table.
//
ST_clear(ST_TABLE_LOCAL);
break;
case PARSE_NODE_TYPE_CALL:
//
// Push the number of arguments in the function call onto the stack.
// The function will check for the correct number and insert
// extra UNDEFINED locals as required.
//
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_NUM_ARGS;
CG_instruction[CG_instruction_upto++] = pn->args;
if (!ST_find(pn->variable))
{
//
// This is an undeclared function.
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOSUB;
CG_instruction[CG_instruction_upto++] = 0;
//
// Remember where the reference to the function is.
//
ASSERT(WITHIN(CG_undefref_upto, 0, CG_MAX_UNDEFREFS - 1));
CG_undefref[CG_undefref_upto].name = CG_add_string_to_debug_data(pn->variable);
CG_undefref[CG_undefref_upto].instruction = CG_instruction_upto - 1;
CG_undefref_upto += 1;
}
else
{
ASSERT(WITHIN(ST_found_value, 0, CG_func_upto - 1));
ASSERT(strcmp(CG_func[ST_found_value].name, pn->variable) == 0);
if (CG_generating_line > CG_func[ST_found_value].line)
{
//
// This is a backwards goto so we can fill in the instruction
// right away. We add 2 to the instruction to jump over the GOTO
// over the function...
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOSUB;
CG_instruction[CG_instruction_upto++] = CG_line[CG_func[ST_found_value].line].code + 2;
}
else
{
//
// This is a forward goto.
//
CG_instruction[CG_instruction_upto++] = ML_DO_GOSUB;
CG_instruction[CG_instruction_upto++] = CG_func[ST_found_value].line;
ASSERT(WITHIN(CG_forward_goto_upto, 0, CG_MAX_FORWARD_GOTOS - 1));
CG_forward_goto[CG_forward_goto_upto].type = CG_FORWARD_GOTO_TYPE_CALL;
CG_forward_goto[CG_forward_goto_upto].instruction = CG_instruction_upto - 1;
CG_forward_goto_upto++;
}
//
// Remember where the jump instruction is.
//
ASSERT(WITHIN(CG_jump_upto, 0, CG_MAX_JUMPS - 1));
CG_jump[CG_jump_upto++].instruction = CG_instruction_upto - 1;
}
if (!(pn->flag & PARSE_NODE_FLAG_EXPRESSION))
{
//
// If this function call isn't part of an expression, the
// return value isn't needed.
//
CG_instruction[CG_instruction_upto++] = ML_DO_POP;
}
break;
case PARSE_NODE_TYPE_TEXTURE:
case PARSE_NODE_TYPE_BUFFER:
case PARSE_NODE_TYPE_DRAW:
case PARSE_NODE_TYPE_CLS:
{
SLONG num_args;
SLONG func_code;
//
// How many arguments do we want and what is the function code?
//
switch(pn->type)
{
case PARSE_NODE_TYPE_TEXTURE: num_args = 2; func_code = ML_DO_TEXTURE; break;
case PARSE_NODE_TYPE_BUFFER: num_args = 4; func_code = ML_DO_BUFFER; break;
case PARSE_NODE_TYPE_DRAW: num_args = 3; func_code = ML_DO_DRAW; break;
case PARSE_NODE_TYPE_CLS: num_args = 2; func_code = ML_DO_CLS; break;
default:
ASSERT(0);
break;
}
//
// Do we have the right number of arguments for the function?
//
if (num_args == pn->args)
{
//
// The right number of arguements!
//
}
else
if (num_args > pn->args)
{
//
// Need to push some undefined members onto the stack.
//
SLONG i;
for (i = pn->args; i < num_args; i++)
{
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_UNDEFINED;
CG_instruction[CG_instruction_upto++] = 0;
}
}
else
{
//
// ERROR! More arguments than the function needs?!
//
ASSERT(0);
}
//
// The function code.
//
CG_instruction[CG_instruction_upto++] = func_code;
}
break;
case PARSE_NODE_TYPE_FLIP:
CG_instruction[CG_instruction_upto++] = ML_DO_FLIP;
break;
case PARSE_NODE_TYPE_KEY_VALUE:
CG_instruction[CG_instruction_upto++] = ML_DO_KEY_VALUE;
break;
case PARSE_NODE_TYPE_KEY_ASSIGN:
CG_instruction[CG_instruction_upto++] = ML_DO_KEY_ASSIGN;
break;
case PARSE_NODE_TYPE_INKEY_VALUE:
CG_instruction[CG_instruction_upto++] = ML_DO_INKEY_VALUE;
break;
case PARSE_NODE_TYPE_INKEY_ASSIGN:
CG_instruction[CG_instruction_upto++] = ML_DO_INKEY_ASSIGN;
break;
case PARSE_NODE_TYPE_TIMER:
CG_instruction[CG_instruction_upto++] = ML_DO_TIMER;
break;
case PARSE_NODE_TYPE_SIN:
CG_instruction[CG_instruction_upto++] = ML_DO_SIN;
break;
case PARSE_NODE_TYPE_COS:
CG_instruction[CG_instruction_upto++] = ML_DO_COS;
break;
case PARSE_NODE_TYPE_TAN:
CG_instruction[CG_instruction_upto++] = ML_DO_TAN;
break;
case PARSE_NODE_TYPE_ASIN:
CG_instruction[CG_instruction_upto++] = ML_DO_ASIN;
break;
case PARSE_NODE_TYPE_ACOS:
CG_instruction[CG_instruction_upto++] = ML_DO_ACOS;
break;
case PARSE_NODE_TYPE_ATAN:
CG_instruction[CG_instruction_upto++] = ML_DO_ATAN;
break;
case PARSE_NODE_TYPE_ATAN2:
CG_instruction[CG_instruction_upto++] = ML_DO_ATAN2;
break;
case PARSE_NODE_TYPE_EXPORT:
break;
case PARSE_NODE_TYPE_LEFT:
if (pn->child2 == NULL)
{
//
// This is a one-argument version. Push the constant 1 onto the stack.
//
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_SLUMBER;
CG_instruction[CG_instruction_upto++] = 1;
}
CG_instruction[CG_instruction_upto++] = ML_DO_LEFT;
break;
case PARSE_NODE_TYPE_RIGHT:
if (pn->child2 == NULL)
{
//
// This is a one-argument version. Push the constant 1 onto the stack.
//
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_SLUMBER;
CG_instruction[CG_instruction_upto++] = 1;
}
CG_instruction[CG_instruction_upto++] = ML_DO_RIGHT;
break;
case PARSE_NODE_TYPE_MID:
if (pn->child3 == NULL)
{
//
// This is a two-argument version. Push the constant 1 onto the stack.
//
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_CONSTANT;
CG_instruction[CG_instruction_upto++] = ML_TYPE_SLUMBER;
CG_instruction[CG_instruction_upto++] = 1;
}
CG_instruction[CG_instruction_upto++] = ML_DO_MID;
break;
case PARSE_NODE_TYPE_LEN:
CG_instruction[CG_instruction_upto++] = ML_DO_LEN;
break;
case PARSE_NODE_TYPE_MATRIX:
if (pn->child1)
{
//
// Constructing a matrix from three vectors.
//
CG_instruction[CG_instruction_upto++] = ML_DO_MATRIX;
}
else
{
//
// The identity matrix.
//
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_IDENTITY_MATRIX;
}
break;
case PARSE_NODE_TYPE_VECTOR:
if (pn->child1)
{
//
// Constructing a vector from three numbers.
//
CG_instruction[CG_instruction_upto++] = ML_DO_VECTOR;
}
else
{
//
// The zero vector.
//
CG_instruction[CG_instruction_upto++] = ML_DO_PUSH_ZERO_VECTOR;
}
break;
default:
ASSERT(0);
break;
}
return TRUE;
}
SLONG CG_do(CBYTE *fname, SLONG output)
{
SLONG i;
PARSE_Node *pn;
FILE *handle;
ML_Header mh;
LINK_Header lh;
CG_Func *cf;
LINK_Global *lg;
LINK_Function lf;
LINK_Field *ld;
CBYTE export_string [LEX_MAX_STRING_LENGTH + 2];
CBYTE local_string [LEX_MAX_STRING_LENGTH + 2];
CBYTE string_backup [LEX_MAX_STRING_LENGTH + 2];
SLONG value_backup;
SLONG export;
SLONG local;
#ifdef _DEBUG
SLONG globals_found;
SLONG fields_found;
#endif
//
// Clear symbol tables and add the system variables.
//
ST_clear_all();
SYSVAR_init();
//
// Initialise output.
//
CG_output = NULL;
CG_num_errors = 0;
CG_num_warnings = 0;
memset(CG_instruction, 0, sizeof(CG_instruction ));
memset(CG_line, 0, sizeof(CG_line ));
memset(CG_func, 0, sizeof(CG_func ));
memset(CG_for, 0, sizeof(CG_for ));
memset(CG_forward_goto, 0, sizeof(CG_forward_goto));
memset(CG_if, 0, sizeof(CG_if ));
memset(CG_mif, 0, sizeof(CG_mif ));
memset(CG_while, 0, sizeof(CG_while ));
memset(CG_jump, 0, sizeof(CG_jump ));
memset(CG_globalref, 0, sizeof(CG_globalref ));
memset(CG_fieldref, 0, sizeof(CG_fieldref ));
memset(CG_undefref, 0, sizeof(CG_undefref ));
memset(CG_datatableref, 0, sizeof(CG_datatableref));
memset(CG_debug_data, 0, sizeof(CG_debug_data ));
CG_instruction_upto = 0;
CG_line_upto = 0;
CG_field_id_upto = SYSVAR_FIELD_NUMBER;
CG_for_upto = 0;
CG_mif_upto = 0;
CG_while_upto = 0;
CG_func_upto = 1; // 0 func => top level code.
CG_forward_goto_upto = 0;
CG_generating_line = 0;
CG_global_upto = 0;
CG_jump_upto = 0;
CG_globalref_upto = 0;
CG_fieldref_upto = 0;
CG_undefref_upto = 0;
CG_datatableref_upto = 0;
CG_debug_data_upto = 0;
//
// The string table.
//
CG_data_table_max = 512;
CG_data_table = (UBYTE *) malloc(sizeof(UBYTE) * CG_data_table_max);
CG_data_table_upto = 0;
//
// Go through the PARSE tree adding all labels and variables
// to the symbol table and finding our functions.
//
CG_in_function = FALSE; // Not in a function when we start...
for (i = 0; i < PARSE_line_upto; i++)
{
pn = PARSE_line[i];
CG_generating_line = i;
PARSE_traverse(pn, CG_callback_add_labels_and_variables);
}
//
// Generate code!
//
CG_in_function = FALSE; // Not in a function when we start...
for (i = 0; i < PARSE_line_upto; i++)
{
pn = PARSE_line[i];
//
// Each line knows where it's instructions are.
//
ASSERT(WITHIN(i, 0, CG_MAX_LINES - 1));
CG_line[i].code = CG_instruction_upto;
CG_line_upto = i + 1;
//
// Clear the Ifs-per-line array.
//
CG_if_upto = 0;
//
// Generate code for this line.
//
CG_generating_line = i;
PARSE_traverse(pn, CG_callback_generate_code);
}
//
// The pretend last line that holds the EXIT instruction.
//
ASSERT(WITHIN(CG_line_upto, 0, CG_MAX_LINES - 1));
CG_line[CG_line_upto++].code = CG_instruction_upto;
//
// And write out an EXIT instruction.
//
if (!(WITHIN(CG_instruction_upto, 0, CG_MAX_INSTRUCTIONS - 1)))
{
//
// This is a bit unlucky! ERROR!
//
ASSERT(0);
return FALSE;
}
CG_instruction[CG_instruction_upto++] = ML_DO_EXIT;
//
// Replace all GOTO addresses with the instruction index rather
// than the line number.
//
for (i = 0; i < CG_forward_goto_upto; i++)
{
ASSERT(WITHIN(CG_forward_goto[i].instruction, 0, CG_instruction_upto - 1));
ASSERT(WITHIN(CG_instruction[CG_forward_goto[i].instruction], 0, CG_line_upto - 1));
CG_instruction[CG_forward_goto[i].instruction] = CG_line[CG_instruction[CG_forward_goto[i].instruction]].code;
if (CG_forward_goto[i].type == CG_FORWARD_GOTO_TYPE_CALL)
{
//
// The first couple of instructions of a function are
// a GOTO over the body of the function- incase the
// functions are defined at the top of the file. So
// when we call the function, we must jump to a couple
// of instructions ahead.
//
CG_instruction[CG_forward_goto[i].instruction] += 2;
}
}
//
// Open our output filename.
//
handle = fopen(fname, "wb");
if (!handle)
{
//
// ERROR!
//
ASSERT(0);
return FALSE;
}
//
// Write out the file type.
//
if (output & CG_OUTPUT_EXECUTABLE)
{
if (CG_undefref_upto != 0)
{
//
// ERROR! You can't have undefined functions when you
// output an executable.
//
ASSERT(0);
}
//
// The header of an executable.
//
mh.version = ML_VERSION_NUMBER;
mh.instructions_memory_in_bytes = CG_instruction_upto * sizeof(SLONG);
mh.data_table_length_in_bytes = CG_data_table_upto;
mh.num_globals = CG_global_upto;
if (fwrite(&mh, sizeof(mh), 1, handle) != 1) goto file_error;
//
// The instructions.
//
if (fwrite(CG_instruction, sizeof(SLONG), CG_instruction_upto, handle) != CG_instruction_upto) goto file_error;
//
// The string table.
//
if (fwrite(CG_data_table, sizeof(UBYTE), CG_data_table_upto, handle) != CG_data_table_upto) goto file_error;
fclose(handle);
//
// Find all our symbols.
//
ST_find_all_start();
while(ST_find_all_next())
{
OS_string("Symbol \"%s\" = %d\n", ST_found_string, ST_found_value);
}
return TRUE;
}
else
{
//
// Writing out the magic number after each block.
//
SLONG magic = 12345678;
#define CG_WRITE_MAGIC() {if (fwrite(&magic, sizeof(SLONG), 1, handle) != 1) goto file_error;}
//
// The header of the object file.
//
lh.version = 1;
lh.num_instructions = CG_instruction_upto;
lh.data_table_length_in_bytes = CG_data_table_upto;
lh.num_globals = CG_global_upto;
lh.num_functions = CG_func_upto - 1; // Function 0 is not used
lh.num_lines = CG_line_upto;
lh.num_jumps = CG_jump_upto;
lh.num_fields = CG_field_id_upto;
lh.num_global_refs = CG_globalref_upto;
lh.num_undef_refs = CG_undefref_upto;
lh.num_field_refs = CG_fieldref_upto;
lh.num_data_table_refs = CG_datatableref_upto;
if (fwrite(&lh, sizeof(lh), 1, handle) != 1) goto file_error;
//
// Each block of data.
//
if (fwrite(CG_instruction, sizeof(SLONG), CG_instruction_upto, handle) != CG_instruction_upto) goto file_error; CG_WRITE_MAGIC();
if (fwrite(CG_data_table, sizeof(UBYTE), CG_data_table_upto, handle) != CG_data_table_upto ) goto file_error; CG_WRITE_MAGIC();
//
// Write out the globals sorted by their global_id.
//
{
lg = (LINK_Global *) malloc(sizeof(LINK_Global) * CG_global_upto);
#ifdef _DEBUG
globals_found = 0;
#endif
ST_find_all_start();
while(ST_find_all_next())
{
if (ST_found_string[0] == '.')
{
//
// This is a field.
//
}
else
if (ST_found_string[0] == '(')
{
//
// This is a function.
//
}
else
if (ST_found_string[0] == '-')
{
//
// This is an export directive.
//
}
else
if (ST_found_string[0] == '<')
{
//
// This is a local directive.
//
}
else
{
//
// This must be a global. Backup ST_found_string and
// ST_found_value because we are going to access the
// symbol table again to find out if this global
// should be exported or not.
//
strcpy(string_backup, ST_found_string);
value_backup = ST_found_value;
//
// Should we export it?
//
sprintf(export_string, "->%s", string_backup);
export = ST_find(export_string);
//
// Should it be a local?
//
sprintf(local_string, "<-%s", string_backup);
local = ST_find(local_string);
//
// Can't be both local and exported!
//
ASSERT(!(local && export));
//
// Write out the global.
//
ASSERT(WITHIN(value_backup, 0, CG_global_upto - 1));
lg[value_backup].export = export;
lg[value_backup].local = local;
lg[value_backup].index = value_backup;
lg[value_backup].name = CG_add_string_to_debug_data(string_backup);
#ifdef _DEBUG
globals_found += 1;
#endif
}
}
ASSERT(globals_found == CG_global_upto);
//
// Write out the global array.
//
if (fwrite(lg, sizeof(LINK_Global), CG_global_upto, handle) != CG_global_upto) goto file_error; CG_WRITE_MAGIC();
//
// Free memory.
//
free(lg);
}
//
// Write out all the defined functions.
//
for (i = 1; i < CG_func_upto; i++)
{
cf = &CG_func[i];
//
// Is this function exported?
//
sprintf(export_string, "->%s", cf->name + 2); // + 2 skips over the () at the beginning of the function name.
export = ST_find(export_string);
//
// Write out the function.
//
lf.name = CG_add_string_to_debug_data(cf->name);
lf.line_start = cf->line;
lf.line_end = cf->endline;
lf.num_args = cf->num_args;
lf.export = export;
if (fwrite(&lf, sizeof(lf), 1, handle) != 1) goto file_error;
}
CG_WRITE_MAGIC();
//
// Write out each line and each jump.
//
if (fwrite(CG_line, sizeof(CG_Line), CG_line_upto, handle) != CG_line_upto) goto file_error; CG_WRITE_MAGIC();
if (fwrite(CG_jump, sizeof(CG_Jump), CG_jump_upto, handle) != CG_jump_upto) goto file_error; CG_WRITE_MAGIC();
//
// Write out all the fields sorted by their field_id
//
{
ld = (LINK_Field *) malloc(sizeof(LINK_Field) * CG_field_id_upto);
#ifdef _DEBUG
fields_found = 0;
#endif
ST_find_all_start();
while(ST_find_all_next())
{
if (ST_found_string[0] == '.')
{
ASSERT(WITHIN(ST_found_value, 0, CG_field_id_upto - 1));
//
// This is a field.
//
ld[ST_found_value].index = ST_found_value;
ld[ST_found_value].name = CG_add_string_to_debug_data(ST_found_string);
#ifdef _DEBUG
fields_found += 1;
#endif
}
}
ASSERT(fields_found == CG_field_id_upto);
//
// Write out all the fields.
//
if (fwrite(ld, sizeof(LINK_Field), CG_field_id_upto, handle) != CG_field_id_upto) goto file_error; CG_WRITE_MAGIC();
//
// Free memory.
//
free(ld);
}
//
// The references to each global, the undefined function references
// and the field references.
//
ASSERT(sizeof(CG_Globalref) == sizeof(LINK_Globalref));
ASSERT(sizeof(CG_Undefref ) == sizeof(LINK_Undefref ));
ASSERT(sizeof(CG_Fieldref ) == sizeof(LINK_Fieldref ));
if (fwrite(CG_globalref, sizeof(CG_Globalref ), CG_globalref_upto, handle) != CG_globalref_upto ) goto file_error; CG_WRITE_MAGIC();
if (fwrite(CG_undefref, sizeof(CG_Undefref ), CG_undefref_upto, handle) != CG_undefref_upto ) goto file_error; CG_WRITE_MAGIC();
if (fwrite(CG_fieldref, sizeof(CG_Fieldref ), CG_fieldref_upto, handle) != CG_fieldref_upto ) goto file_error; CG_WRITE_MAGIC();
if (fwrite(CG_datatableref, sizeof(CG_Datatableref), CG_datatableref_upto, handle) != CG_datatableref_upto) goto file_error; CG_WRITE_MAGIC();
//
// The debug data.
//
if (fwrite(&CG_debug_data_upto, sizeof(SLONG), 1, handle) != 1 ) goto file_error;
if (fwrite( CG_debug_data, sizeof(CBYTE), CG_debug_data_upto, handle) != CG_debug_data_upto) goto file_error;
//
// All done.
//
fclose(handle);
return TRUE;
}
file_error:;
//
// ERROR!
//
ASSERT(0);
fclose(handle);
return FALSE;
}