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

3995 lines
65 KiB
C++

#include "always.h"
#include "lex.h"
#include "parse.h"
#include <setjmp.h>
//
// The nodes.
//
#define PARSE_MAX_NODES 65536
PARSE_Node PARSE_node[PARSE_MAX_NODES];
SLONG PARSE_node_upto;
PARSE_Node *PARSE_get_node()
{
ASSERT(WITHIN(PARSE_node_upto, 0, PARSE_MAX_NODES - 1));
return &PARSE_node[PARSE_node_upto++];
}
//
// Output variables and others...
//
#define PARSE_MAX_LINES 16384
#define PARSE_MAX_STRING_TABLE_SIZE 65536
#define PARSE_MAX_ERRORS 256
PARSE_Node *PARSE_line[PARSE_MAX_LINES]; // NULL value means that line was blank.
SLONG PARSE_line_upto;
CBYTE PARSE_string_table[PARSE_MAX_STRING_TABLE_SIZE];
SLONG PARSE_string_table_upto;
CBYTE *PARSE_error[PARSE_MAX_ERRORS];
SLONG PARSE_error_upto;
SLONG PARSE_ifcode; // This gets incremented every time we parse an IF statement...
SLONG PARSE_forcode; // This gets incremented every time we parse a FOR statement...
SLONG PARSE_whilecode; // This gets incremented every time we parse a WHILE statement...
//
// The error buffer.
//
#define PARSE_MAX_ERRBUF 32768
CBYTE PARSE_errbuf[PARSE_MAX_ERRBUF];
SLONG PARSE_errbuf_upto;
//
// Adds the given error. If it has run out of room, it
// returns FALSE.
//
SLONG PARSE_add_error(CBYTE *fmt, ...)
{
if (PARSE_error_upto >= PARSE_MAX_ERRORS)
{
return FALSE;
}
//
// Work out the real error.
//
CBYTE error[512];
va_list ap;
va_start(ap, fmt);
vsprintf(error, fmt, ap);
va_end (ap);
//
// Put it into the buffer.
//
SLONG len = strlen(error) + 1; // + 1 to include terminating NULL.
if (PARSE_errbuf_upto + len > PARSE_MAX_ERRBUF)
{
return FALSE;
}
strcpy(PARSE_errbuf + PARSE_errbuf_upto, error);
PARSE_error[PARSE_error_upto] = PARSE_errbuf + PARSE_errbuf_upto;
PARSE_error_upto += 1;
PARSE_errbuf_upto += len;
return TRUE;
}
//
// Where to longjmp on an error.
//
jmp_buf PARSE_error_jmp;
//
// The reason the parser jumped to PARSE_error_jmp;
//
CBYTE *PARSE_error_type;
//
// Throws up an error.
//
void PARSE_throw(CBYTE *error = "Parse error")
{
PARSE_error_type = error;
longjmp(PARSE_error_jmp, 1);
}
//
// Adds the string to PARSE_string_table and return the address
// where it was copied.
//
CBYTE *PARSE_add_string(CBYTE *string)
{
SLONG length = strlen(string) + 1; // + 1 to include the terminating NULL
if (PARSE_string_table_upto + length > PARSE_MAX_STRING_TABLE_SIZE)
{
//
// ERROR!
//
PARSE_throw("No more string constant memory");
}
CBYTE *ans = PARSE_string_table + PARSE_string_table_upto;
strcpy(ans, string);
PARSE_string_table_upto += length;
return ans;
}
//
// Sets the PARSE_NODE_FLAG_CONDITIONAL flag in the given node.
//
SLONG PARSE_set_conditional_flag(PARSE_Node *pn)
{
pn->flag |= PARSE_NODE_FLAG_CONDITIONAL;
return TRUE;
}
//
// Sets the PARSE_NODE_FLAG_EXPRESSION flag in the given node.
//
SLONG PARSE_set_expression_flag(PARSE_Node *pn)
{
pn->flag |= PARSE_NODE_FLAG_EXPRESSION;
return TRUE;
}
//
// Returns TRUE if the given expression is sure to return
// a BOOLEAN value.
//
SLONG PARSE_expression_is_boolean(PARSE_Node *exp)
{
switch(exp->type)
{
case PARSE_NODE_TYPE_EQUALS:
case PARSE_NODE_TYPE_GT:
case PARSE_NODE_TYPE_LT:
case PARSE_NODE_TYPE_GTEQ:
case PARSE_NODE_TYPE_LTEQ:
case PARSE_NODE_TYPE_AND:
case PARSE_NODE_TYPE_OR:
case PARSE_NODE_TYPE_NOT:
case PARSE_NODE_TYPE_BOOLEAN:
case PARSE_NODE_TYPE_XOR:
case PARSE_NODE_TYPE_KEY_VALUE:
return TRUE;
case PARSE_NODE_TYPE_NOP:
case PARSE_NODE_TYPE_PLUS:
case PARSE_NODE_TYPE_MINUS:
case PARSE_NODE_TYPE_UMINUS:
case PARSE_NODE_TYPE_TIMES:
case PARSE_NODE_TYPE_DIVIDE:
case PARSE_NODE_TYPE_SLUMBER:
case PARSE_NODE_TYPE_FLUMBER:
case PARSE_NODE_TYPE_STRING:
case PARSE_NODE_TYPE_VAR_VALUE:
case PARSE_NODE_TYPE_IF:
case PARSE_NODE_TYPE_GOTO:
case PARSE_NODE_TYPE_LABEL:
case PARSE_NODE_TYPE_DOT:
case PARSE_NODE_TYPE_CALL:
case PARSE_NODE_TYPE_LOCAL:
case PARSE_NODE_TYPE_PRINT:
case PARSE_NODE_TYPE_ASSIGN:
case PARSE_NODE_TYPE_VAR_ADDRESS:
case PARSE_NODE_TYPE_MOD:
case PARSE_NODE_TYPE_SQRT:
case PARSE_NODE_TYPE_NEWLINE:
case PARSE_NODE_TYPE_ABS:
case PARSE_NODE_TYPE_PUSH_FIELD_ADDRESS:
case PARSE_NODE_TYPE_FIELD:
case PARSE_NODE_TYPE_PUSH_FIELD_VALUE:
case PARSE_NODE_TYPE_EXP_LIST:
case PARSE_NODE_TYPE_PUSH_ARRAY_ADDRESS:
case PARSE_NODE_TYPE_PUSH_ARRAY_VALUE:
case PARSE_NODE_TYPE_INPUT:
case PARSE_NODE_TYPE_UNDEFINED:
case PARSE_NODE_TYPE_STATEMENT_LIST:
case PARSE_NODE_TYPE_EXIT:
case PARSE_NODE_TYPE_RETURN:
case PARSE_NODE_TYPE_GOSUB:
case PARSE_NODE_TYPE_FOR:
case PARSE_NODE_TYPE_NEXT:
case PARSE_NODE_TYPE_NOTEQUAL:
case PARSE_NODE_TYPE_RANDOM:
case PARSE_NODE_TYPE_SWAP:
case PARSE_NODE_TYPE_MIF:
case PARSE_NODE_TYPE_MELSE:
case PARSE_NODE_TYPE_MENDIF:
case PARSE_NODE_TYPE_WHILE:
case PARSE_NODE_TYPE_LOOP:
case PARSE_NODE_TYPE_FUNCTION:
case PARSE_NODE_TYPE_ARGUMENT:
case PARSE_NODE_TYPE_ENDFUNC:
case PARSE_NODE_TYPE_TEXTURE:
case PARSE_NODE_TYPE_BUFFER:
case PARSE_NODE_TYPE_DRAW:
case PARSE_NODE_TYPE_CLS:
case PARSE_NODE_TYPE_FLIP:
case PARSE_NODE_TYPE_KEY_ASSIGN:
case PARSE_NODE_TYPE_INKEY_VALUE:
case PARSE_NODE_TYPE_INKEY_ASSIGN:
case PARSE_NODE_TYPE_TIMER:
case PARSE_NODE_TYPE_SIN:
case PARSE_NODE_TYPE_COS:
case PARSE_NODE_TYPE_TAN:
case PARSE_NODE_TYPE_ASIN:
case PARSE_NODE_TYPE_ACOS:
case PARSE_NODE_TYPE_ATAN:
case PARSE_NODE_TYPE_ATAN2:
case PARSE_NODE_TYPE_EXPORT:
case PARSE_NODE_TYPE_LEFT:
case PARSE_NODE_TYPE_MID:
case PARSE_NODE_TYPE_RIGHT:
case PARSE_NODE_TYPE_MATRIX:
case PARSE_NODE_TYPE_VECTOR:
return FALSE;
default:
ASSERT(0);
break;
}
return FALSE;
}
//
// Returns a copy of the parse tree.
//
PARSE_Node *PARSE_copy_tree(PARSE_Node *tree)
{
PARSE_Node *ans = PARSE_get_node();
*ans = *tree;
if (ans->child1) {ans->child1 = PARSE_copy_tree(ans->child1);}
if (ans->child2) {ans->child2 = PARSE_copy_tree(ans->child2);}
if (ans->child3) {ans->child3 = PARSE_copy_tree(ans->child3);}
return ans;
}
//
// Converts an lvalue to an rvalue.
//
PARSE_Node *PARSE_convert_lvalue_to_rvalue(PARSE_Node *lv)
{
PARSE_Node *ans = PARSE_get_node();
*ans = *lv;
switch(lv->type)
{
case PARSE_NODE_TYPE_VAR_ADDRESS:
ans->type = PARSE_NODE_TYPE_VAR_VALUE;
break;
case PARSE_NODE_TYPE_PUSH_FIELD_ADDRESS:
ans->type = PARSE_NODE_TYPE_PUSH_FIELD_VALUE;
ans->child1 = PARSE_convert_lvalue_to_rvalue(ans->child1);
ans->child2 = PARSE_copy_tree(ans->child2);
//
// We need a 'QUICK' copy from the child
//
ans->child1->flag |= PARSE_NODE_FLAG_EXTRACT;
break;
case PARSE_NODE_TYPE_PUSH_ARRAY_ADDRESS:
ans->type = PARSE_NODE_TYPE_PUSH_ARRAY_VALUE;
ans->child1 = PARSE_convert_lvalue_to_rvalue(ans->child1);
ans->child2 = PARSE_copy_tree(ans->child2);
//
// We need a 'QUICK' copy from the child
//
ans->child1->flag |= PARSE_NODE_FLAG_EXTRACT;
break;
default:
ASSERT(0);
break;
}
return ans;
}
//
// Converts an rvalue to an lvalue. Only work on certain nodes... of course!
//
void PARSE_convert_rvalue_to_lvalue(PARSE_Node *rv)
{
switch(rv->type)
{
case PARSE_NODE_TYPE_VAR_VALUE:
rv->type = PARSE_NODE_TYPE_VAR_ADDRESS;
break;
case PARSE_NODE_TYPE_PUSH_FIELD_VALUE:
rv->type = PARSE_NODE_TYPE_PUSH_FIELD_ADDRESS;
PARSE_convert_rvalue_to_lvalue(rv->child1);
break;
case PARSE_NODE_TYPE_PUSH_ARRAY_VALUE:
rv->type = PARSE_NODE_TYPE_PUSH_ARRAY_ADDRESS;
PARSE_convert_rvalue_to_lvalue(rv->child1);
break;
default:
ASSERT(0);
break;
}
}
//
// Call this function on all top-level nodes that are in argument
// lists to function-calls. It makes sure that all variables are
// passed by reference.
//
void PARSE_convert_rvalue_to_argument(PARSE_Node *arg)
{
//
// Sometimes we must change the type of the node's children.
//
switch(arg->type)
{
case PARSE_NODE_TYPE_VAR_VALUE:
case PARSE_NODE_TYPE_PUSH_FIELD_VALUE:
case PARSE_NODE_TYPE_PUSH_ARRAY_VALUE:
PARSE_convert_rvalue_to_lvalue(arg);
break;
default:
break;
}
}
SLONG PARSE_trees_the_same(PARSE_Node *tree1, PARSE_Node *tree2)
{
//
// Make sure both tree have the same type.
//
if (tree1->type != tree2->type)
{
return FALSE;
}
//
// For nodes with data, make sure the data is the same.
//
switch(tree1->type)
{
case PARSE_NODE_TYPE_SLUMBER:
if (tree1->slumber != tree2->slumber) return FALSE;
break;
case PARSE_NODE_TYPE_FLUMBER:
if (tree1->flumber != tree2->flumber) return FALSE;
break;
case PARSE_NODE_TYPE_STRING:
if (strcmp(tree1->string, tree2->string) != 0) return FALSE;
break;
case PARSE_NODE_TYPE_VAR_VALUE:
case PARSE_NODE_TYPE_VAR_ADDRESS:
case PARSE_NODE_TYPE_CALL:
case PARSE_NODE_TYPE_FUNCTION:
case PARSE_NODE_TYPE_FIELD:
case PARSE_NODE_TYPE_ARGUMENT:
case PARSE_NODE_TYPE_LOCAL:
case PARSE_NODE_TYPE_EXPORT:
if (strcmp(tree1->variable, tree2->variable) != 0) return FALSE;
break;
case PARSE_NODE_TYPE_GOTO:
case PARSE_NODE_TYPE_LABEL:
case PARSE_NODE_TYPE_GOSUB:
if (strcmp(tree1->label, tree2->label) != 0) return FALSE;
break;
case PARSE_NODE_TYPE_BOOLEAN:
if (tree1->boolean != tree2->boolean) return FALSE;
break;
case PARSE_NODE_TYPE_NOP:
case PARSE_NODE_TYPE_EQUALS:
case PARSE_NODE_TYPE_PLUS:
case PARSE_NODE_TYPE_MINUS:
case PARSE_NODE_TYPE_UMINUS:
case PARSE_NODE_TYPE_TIMES:
case PARSE_NODE_TYPE_DIVIDE:
case PARSE_NODE_TYPE_IF:
case PARSE_NODE_TYPE_GT:
case PARSE_NODE_TYPE_LT:
case PARSE_NODE_TYPE_GTEQ:
case PARSE_NODE_TYPE_LTEQ:
case PARSE_NODE_TYPE_AND:
case PARSE_NODE_TYPE_OR:
case PARSE_NODE_TYPE_NOT:
case PARSE_NODE_TYPE_DOT:
case PARSE_NODE_TYPE_PRINT:
case PARSE_NODE_TYPE_ASSIGN:
case PARSE_NODE_TYPE_MOD:
case PARSE_NODE_TYPE_SQRT:
case PARSE_NODE_TYPE_NEWLINE:
case PARSE_NODE_TYPE_ABS:
case PARSE_NODE_TYPE_PUSH_FIELD_ADDRESS:
case PARSE_NODE_TYPE_PUSH_FIELD_VALUE:
case PARSE_NODE_TYPE_EXP_LIST:
case PARSE_NODE_TYPE_PUSH_ARRAY_ADDRESS:
case PARSE_NODE_TYPE_PUSH_ARRAY_VALUE:
case PARSE_NODE_TYPE_INPUT:
case PARSE_NODE_TYPE_UNDEFINED:
case PARSE_NODE_TYPE_STATEMENT_LIST:
case PARSE_NODE_TYPE_EXIT:
case PARSE_NODE_TYPE_RETURN:
case PARSE_NODE_TYPE_XOR:
case PARSE_NODE_TYPE_FOR:
case PARSE_NODE_TYPE_NEXT:
case PARSE_NODE_TYPE_NOTEQUAL:
case PARSE_NODE_TYPE_RANDOM:
case PARSE_NODE_TYPE_SWAP:
case PARSE_NODE_TYPE_MIF:
case PARSE_NODE_TYPE_MELSE:
case PARSE_NODE_TYPE_MENDIF:
case PARSE_NODE_TYPE_WHILE:
case PARSE_NODE_TYPE_LOOP:
case PARSE_NODE_TYPE_ENDFUNC:
case PARSE_NODE_TYPE_TEXTURE:
case PARSE_NODE_TYPE_BUFFER:
case PARSE_NODE_TYPE_DRAW:
case PARSE_NODE_TYPE_CLS:
case PARSE_NODE_TYPE_FLIP:
case PARSE_NODE_TYPE_KEY_ASSIGN:
case PARSE_NODE_TYPE_KEY_VALUE:
case PARSE_NODE_TYPE_INKEY_ASSIGN:
case PARSE_NODE_TYPE_INKEY_VALUE:
case PARSE_NODE_TYPE_TIMER:
case PARSE_NODE_TYPE_SIN:
case PARSE_NODE_TYPE_COS:
case PARSE_NODE_TYPE_TAN:
case PARSE_NODE_TYPE_ASIN:
case PARSE_NODE_TYPE_ACOS:
case PARSE_NODE_TYPE_ATAN:
case PARSE_NODE_TYPE_ATAN2:
case PARSE_NODE_TYPE_LEFT:
case PARSE_NODE_TYPE_MID:
case PARSE_NODE_TYPE_RIGHT:
case PARSE_NODE_TYPE_MATRIX:
case PARSE_NODE_TYPE_VECTOR:
break;
default:
ASSERT(0);
break;
}
//
// Make sure both trees have the same number of children.
//
if ( tree1->child1 && !tree2->child1) return FALSE;
if (!tree1->child1 && tree2->child1) return FALSE;
if ( tree1->child2 && !tree2->child2) return FALSE;
if (!tree1->child2 && tree2->child2) return FALSE;
if ( tree1->child3 && !tree2->child3) return FALSE;
if (!tree1->child3 && tree2->child3) return FALSE;
//
// The children must be the same too.
//
if (tree1->child1 && !PARSE_trees_the_same(tree1->child1, tree2->child1)) return FALSE;
if (tree1->child2 && !PARSE_trees_the_same(tree1->child2, tree2->child2)) return FALSE;
if (tree1->child3 && !PARSE_trees_the_same(tree1->child3, tree2->child3)) return FALSE;
//
// All fine!
//
return TRUE;
}
//
// Our parsing functions........
//
PARSE_Node *PARSE_labelled_statement_list();
PARSE_Node *PARSE_statement_list();
PARSE_Node *PARSE_statement();
PARSE_Node *PARSE_expression();
PARSE_Node *PARSE_expression_list();
PARSE_Node *PARSE_p1exp();
PARSE_Node *PARSE_p2exp();
PARSE_Node *PARSE_p3exp();
PARSE_Node *PARSE_p4exp();
PARSE_Node *PARSE_p5exp();
PARSE_Node *PARSE_primary();
PARSE_Node *PARSE_function_call();
PARSE_Node *PARSE_lvalue();
PARSE_Node *PARSE_var();
PARSE_Node *PARSE_struct();
PARSE_Node *PARSE_argument_definition();
SLONG PARSE_expression_list_depth(PARSE_Node *explist);
//
// Recursive descent parsing...
//
PARSE_Node *PARSE_function_call()
{
PARSE_Node *ans;
LEX_Token lt;
//
// A function call.
//
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_VARIABLE)
{
//
// ERROR! What function are we calling?
//
PARSE_throw("Expected a function name");
}
LEX_pop();
//
// Put "()" onto the front of the function name so
// we know that it's a function.
//
CBYTE name[LEX_MAX_STRING_LENGTH + 32];
sprintf(name, "()%s", lt.variable);
//
// The start of an argument list?
//
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
PARSE_throw("Expected an open bracket after the function name");
}
LEX_pop();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
//
// No argments to the function.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_CALL;
ans->child1 = NULL;
ans->args = 0;
ans->variable = PARSE_add_string(name);
return ans;
}
//
// This function call has an argument list.
//
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_CALL;
ans->child1 = PARSE_expression_list();
ans->args = PARSE_expression_list_depth(ans->child1);
ans->variable = PARSE_add_string(name);
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
PARSE_throw("No close bracket after the list of arguments");
}
LEX_pop();
//
// Descend the expression list and convert each rvalue to an arguement.
//
PARSE_Node *arg = ans->child1;
while(1)
{
if (arg->type == PARSE_NODE_TYPE_EXP_LIST)
{
PARSE_convert_rvalue_to_argument(arg->child1);
arg = arg->child2;
}
else
{
PARSE_convert_rvalue_to_argument(arg);
break;
}
}
return ans;
}
PARSE_Node *PARSE_primary()
{
PARSE_Node *ans;
LEX_Token lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_MINUS:
//
// A unary minus!
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_UMINUS;
ans->child1 = PARSE_primary();
return ans;
case LEX_TOKEN_TYPE_SLUMBER:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_SLUMBER;
ans->slumber = lt.slumber;
return ans;
case LEX_TOKEN_TYPE_FLUMBER:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_FLUMBER;
ans->flumber = lt.flumber;
return ans;
case LEX_TOKEN_TYPE_VARIABLE:
{
CBYTE *varname = PARSE_add_string(lt.variable);
//
// This could be a function call.
//
LEX_pop();
LEX_Token lookahead = LEX_get();
if (lookahead.type == LEX_TOKEN_TYPE_OPEN)
{
//
// This is a function call!
//
LEX_push(lt);
ans = PARSE_function_call();
return ans;
}
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_VAR_VALUE;
ans->variable = varname;
return ans;
}
case LEX_TOKEN_TYPE_OPEN:
LEX_pop();
ans = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
PARSE_throw("No matching close bracket found");
}
else
{
LEX_pop();
return ans;
}
case LEX_TOKEN_TYPE_TRUE:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_BOOLEAN;
ans->boolean = TRUE;
return ans;
case LEX_TOKEN_TYPE_FALSE:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_BOOLEAN;
ans->boolean = FALSE;
return ans;
case LEX_TOKEN_TYPE_NOT:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_NOT;
ans->child1 = PARSE_primary();
return ans;
case LEX_TOKEN_TYPE_STRING:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_STRING;
ans->string = PARSE_add_string(lt.string);
return ans;
case LEX_TOKEN_TYPE_INPUT:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_INPUT;
return ans;
case LEX_TOKEN_TYPE_UNDEFINED:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_UNDEFINED;
return ans;
case LEX_TOKEN_TYPE_RANDOM:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_RANDOM;
return ans;
case LEX_TOKEN_TYPE_CALL:
LEX_pop();
ans = PARSE_function_call();
return ans;
case LEX_TOKEN_TYPE_TEXTURE:
case LEX_TOKEN_TYPE_BUFFER:
//
// Creation of a texture or a buffer.
//
LEX_pop();
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_TEXTURE: ans->type = PARSE_NODE_TYPE_TEXTURE; break;
case LEX_TOKEN_TYPE_BUFFER: ans->type = PARSE_NODE_TYPE_BUFFER; break;
default:
ASSERT(0);
break;
}
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
switch(ans->type)
{
case PARSE_NODE_TYPE_TEXTURE: PARSE_throw("No open bracket after the keyword TEXTURE"); break;
case PARSE_NODE_TYPE_BUFFER: PARSE_throw("No open bracket after the keyword BUFFER"); break;
default:
ASSERT(0);
}
}
LEX_pop();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
//
// No arguments
//
LEX_pop();
ans->child1 = NULL;
ans->args = 0;
}
else
{
//
// An argument list.
//
ans->child1 = PARSE_expression_list();
ans->args = PARSE_expression_list_depth(ans->child1);
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
switch(ans->type)
{
case PARSE_NODE_TYPE_TEXTURE: PARSE_throw("No close bracket after the keyword TEXTURE"); break;
case PARSE_NODE_TYPE_BUFFER: PARSE_throw("No close bracket after the keyword BUFFER"); break;
default:
ASSERT(0);
}
}
LEX_pop();
}
return ans;
case LEX_TOKEN_TYPE_KEY:
LEX_pop();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OSQUARE)
{
//
// ERROR!
//
PARSE_throw("KEY must be accessed like an array, e.g. KEY[50] or KEY[x]");
}
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_KEY_VALUE;
ans->child1 = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CSQUARE)
{
//
// ERROR!
//
PARSE_throw("Missing close bracket for the KEY keyword");
}
LEX_pop();
return ans;
case LEX_TOKEN_TYPE_INKEY:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_INKEY_VALUE;
return ans;
case LEX_TOKEN_TYPE_TIMER:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_TIMER;
return ans;
case LEX_TOKEN_TYPE_SQRT:
case LEX_TOKEN_TYPE_ABS:
case LEX_TOKEN_TYPE_SIN:
case LEX_TOKEN_TYPE_COS:
case LEX_TOKEN_TYPE_TAN:
case LEX_TOKEN_TYPE_ASIN:
case LEX_TOKEN_TYPE_ACOS:
case LEX_TOKEN_TYPE_ATAN:
case LEX_TOKEN_TYPE_LEN:
LEX_pop();
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_SQRT: ans->type = PARSE_NODE_TYPE_SQRT; break;
case LEX_TOKEN_TYPE_ABS: ans->type = PARSE_NODE_TYPE_ABS; break;
case LEX_TOKEN_TYPE_SIN: ans->type = PARSE_NODE_TYPE_SIN; break;
case LEX_TOKEN_TYPE_COS: ans->type = PARSE_NODE_TYPE_COS; break;
case LEX_TOKEN_TYPE_TAN: ans->type = PARSE_NODE_TYPE_TAN; break;
case LEX_TOKEN_TYPE_ASIN: ans->type = PARSE_NODE_TYPE_ASIN; break;
case LEX_TOKEN_TYPE_ACOS: ans->type = PARSE_NODE_TYPE_ACOS; break;
case LEX_TOKEN_TYPE_ATAN: ans->type = PARSE_NODE_TYPE_ATAN; break;
case LEX_TOKEN_TYPE_LEN: ans->type = PARSE_NODE_TYPE_LEN; break;
default:
ASSERT(0);
break;
}
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
switch(ans->type)
{
case PARSE_NODE_TYPE_SQRT: PARSE_throw("Missing open bracket after SQRT"); break;
case PARSE_NODE_TYPE_ABS: PARSE_throw("Missing open bracket after ABS"); break;
case PARSE_NODE_TYPE_SIN: PARSE_throw("Missing open bracket after SIN"); break;
case PARSE_NODE_TYPE_COS: PARSE_throw("Missing open bracket after COS"); break;
case PARSE_NODE_TYPE_TAN: PARSE_throw("Missing open bracket after TAN"); break;
case PARSE_NODE_TYPE_ASIN: PARSE_throw("Missing open bracket after ASIN"); break;
case PARSE_NODE_TYPE_ACOS: PARSE_throw("Missing open bracket after ACOS"); break;
case PARSE_NODE_TYPE_ATAN: PARSE_throw("Missing open bracket after ATAN"); break;
case PARSE_NODE_TYPE_LEN: PARSE_throw("Missing open bracket after LEN"); break;
default:
ASSERT(0);
break;
}
}
LEX_pop();
ans->child1 = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
switch(ans->type)
{
case PARSE_NODE_TYPE_SQRT: PARSE_throw("Missing close bracket after SQRT"); break;
case PARSE_NODE_TYPE_ABS: PARSE_throw("Missing close bracket after ABS"); break;
case PARSE_NODE_TYPE_SIN: PARSE_throw("Missing close bracket after SIN"); break;
case PARSE_NODE_TYPE_COS: PARSE_throw("Missing close bracket after COS"); break;
case PARSE_NODE_TYPE_TAN: PARSE_throw("Missing close bracket after TAN"); break;
case PARSE_NODE_TYPE_ASIN: PARSE_throw("Missing close bracket after ASIN"); break;
case PARSE_NODE_TYPE_ACOS: PARSE_throw("Missing close bracket after ACOS"); break;
case PARSE_NODE_TYPE_ATAN: PARSE_throw("Missing close bracket after ATAN"); break;
case PARSE_NODE_TYPE_LEN: PARSE_throw("Missing close bracket after LEN"); break;
default:
ASSERT(0);
break;
}
}
LEX_pop();
return ans;
case LEX_TOKEN_TYPE_ATAN2:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_ATAN2;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
PARSE_throw("Missing open bracket after ATAN2");
}
LEX_pop();
ans->child1 = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_COMMA)
{
//
// ERROR!
//
PARSE_throw("Expected a comma separating the two arguments to ATAN2");
}
LEX_pop();
ans->child2 = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
PARSE_throw("Missing close bracket after ATAN2");
}
LEX_pop();
return ans;
case LEX_TOKEN_TYPE_LEFT:
case LEX_TOKEN_TYPE_RIGHT:
//
// These functions take either one or two arguments.
//
LEX_pop();
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_LEFT: ans->type = PARSE_NODE_TYPE_LEFT; break;
case LEX_TOKEN_TYPE_RIGHT: ans->type = PARSE_NODE_TYPE_RIGHT; break;
default:
ASSERT(0);
break;
}
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
switch(ans->type)
{
case PARSE_NODE_TYPE_LEFT: PARSE_throw("Missing open bracket after LEFT"); break;
case PARSE_NODE_TYPE_RIGHT: PARSE_throw("Missing open bracket after RIGHT"); break;
default:
ASSERT(0);
break;
}
}
LEX_pop();
ans->child1 = PARSE_expression();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
LEX_pop();
//
// This is a one-argugment version.
//
ans->child2 = NULL;
return ans;
}
else
if (lt.type != LEX_TOKEN_TYPE_COMMA)
{
//
// ERROR!
//
switch(ans->type)
{
case PARSE_NODE_TYPE_LEFT: PARSE_throw("Expected a comma or a close bracket after the first argument to LEFT"); break;
case PARSE_NODE_TYPE_RIGHT: PARSE_throw("Expected a comma or a close bracket after the first argument to RIGHT"); break;
default:
ASSERT(0);
break;
}
}
LEX_pop();
ans->child2 = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
switch(ans->type)
{
case PARSE_NODE_TYPE_LEFT: PARSE_throw("Expected a close bracket after LEFT"); break;
case PARSE_NODE_TYPE_RIGHT: PARSE_throw("Expected a close bracket after RIGHT"); break;
default:
ASSERT(0);
break;
}
}
LEX_pop();
return ans;
case LEX_TOKEN_TYPE_MID:
//
// A two or three arguement function.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_MID;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
PARSE_throw("Expected an open bracket after MID");
}
LEX_pop();
ans->child1 = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_COMMA)
{
//
// ERROR!
//
PARSE_throw("Expected a comma after the first argument to MID");
}
LEX_pop();
ans->child2 = PARSE_expression();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
LEX_pop();
//
// This is a two-argument version.
//
ans->child3 = NULL;
return ans;
}
else
if (lt.type != LEX_TOKEN_TYPE_COMMA)
{
//
// ERROR!
//
PARSE_throw("Expected a comma or close bracket after the second argument to MID");
}
LEX_pop();
ans->child3 = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
PARSE_throw("Expected a close bracket after the third argument to MID");
}
LEX_pop();
return ans;
case LEX_TOKEN_TYPE_MATRIX:
//
// A matrix constant.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_MATRIX;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
PARSE_throw("Expected an open bracket after MATRIX");
}
LEX_pop();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
//
// This is the identity matrix.
//
ans->child1 = NULL;
}
else
{
//
// There must be an expression list.
//
ans->child1 = PARSE_expression_list();
ans->args = PARSE_expression_list_depth(ans->child1);
//
// Right number of arguments? 3 args that must all be vectors,
//
if (ans-> args != 3)
{
//
// ERROR!
//
PARSE_throw("MATRIX wants three vector arguments");
}
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
PARSE_throw("Expected a close bracket after the argument list to MATRIX");
}
LEX_pop();
}
return ans;
case LEX_TOKEN_TYPE_VECTOR:
//
// A matrix constant.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_VECTOR;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
PARSE_throw("Expected an open bracket after VECTOR");
}
LEX_pop();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
//
// This is the zero vector (0,0,0).
//
ans->child1 = NULL;
}
else
{
//
// There must be an expression list.
//
ans->child1 = PARSE_expression_list();
ans->args = PARSE_expression_list_depth(ans->child1);
//
// Right number of arguments?
//
if (ans->args != 3)
{
//
// ERROR!
//
PARSE_throw("The VECTOR command expects either 3 arguments or an empty argument list");
}
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
PARSE_throw("Expected a close bracket after the argument list to VECTOR");
}
LEX_pop();
}
return ans;
default:
//
// ERROR!
//
PARSE_throw();
return NULL;
}
}
PARSE_Node *PARSE_p6exp()
{
LEX_Token lt;
PARSE_Node *lhs;
PARSE_Node *rhs;
PARSE_Node *ans;
lhs = PARSE_primary();
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_DOT:
LEX_pop();
rhs = PARSE_struct();
//
// Build a little tree...
//
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_PUSH_FIELD_VALUE;
ans->child1 = lhs;
ans->child2 = rhs;
lhs->flag |= PARSE_NODE_FLAG_EXTRACT;
//
// Now carry on...
//
lhs = ans;
break;
case LEX_TOKEN_TYPE_OSQUARE:
LEX_pop();
rhs = PARSE_expression_list();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_PUSH_ARRAY_VALUE;
ans->dimensions = PARSE_expression_list_depth(rhs);
ans->child1 = lhs;
ans->child2 = rhs;
lhs->flag |= PARSE_NODE_FLAG_EXTRACT;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CSQUARE)
{
//
// ERROR!
//
PARSE_throw("Missing close square bracket in an array access");
}
LEX_pop();
//
// Now carry on...
//
lhs = ans;
break;
default:
return lhs;
}
}
}
PARSE_Node *PARSE_p5exp()
{
LEX_Token lt;
PARSE_Node *lhs;
PARSE_Node *rhs;
PARSE_Node *ans;
lhs = PARSE_p6exp();
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_TIMES:
case LEX_TOKEN_TYPE_DIVIDE:
case LEX_TOKEN_TYPE_MOD:
case LEX_TOKEN_TYPE_CPROD:
LEX_pop();
rhs = PARSE_p6exp();
//
// Build a little tree...
//
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_TIMES: ans->type = PARSE_NODE_TYPE_TIMES; break;
case LEX_TOKEN_TYPE_DIVIDE: ans->type = PARSE_NODE_TYPE_DIVIDE; break;
case LEX_TOKEN_TYPE_MOD: ans->type = PARSE_NODE_TYPE_MOD; break;
default:
ASSERT(0);
break;
}
ans->child1 = lhs;
ans->child2 = rhs;
//
// Now carry on...
//
lhs = ans;
break;
default:
return lhs;
}
}
}
PARSE_Node *PARSE_p4exp()
{
LEX_Token lt;
PARSE_Node *lhs;
PARSE_Node *rhs;
PARSE_Node *ans;
lhs = PARSE_p5exp();
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_PLUS:
case LEX_TOKEN_TYPE_MINUS:
LEX_pop();
rhs = PARSE_p5exp();
//
// Build a little tree...
//
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_PLUS: ans->type = PARSE_NODE_TYPE_PLUS; break;
case LEX_TOKEN_TYPE_MINUS: ans->type = PARSE_NODE_TYPE_MINUS; break;
default:
ASSERT(0);
break;
}
ans->child1 = lhs;
ans->child2 = rhs;
//
// Now carry on...
//
lhs = ans;
break;
default:
return lhs;
}
}
}
PARSE_Node *PARSE_p3exp()
{
LEX_Token lt;
PARSE_Node *lhs;
PARSE_Node *rhs;
PARSE_Node *ans;
lhs = PARSE_p4exp();
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_LT:
case LEX_TOKEN_TYPE_GT:
case LEX_TOKEN_TYPE_LTEQ:
case LEX_TOKEN_TYPE_GTEQ:
LEX_pop();
rhs = PARSE_p4exp();
//
// Build a little tree...
//
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_LT: ans->type = PARSE_NODE_TYPE_LT; break;
case LEX_TOKEN_TYPE_GT: ans->type = PARSE_NODE_TYPE_GT; break;
case LEX_TOKEN_TYPE_LTEQ: ans->type = PARSE_NODE_TYPE_LTEQ; break;
case LEX_TOKEN_TYPE_GTEQ: ans->type = PARSE_NODE_TYPE_GTEQ; break;
default:
ASSERT(0);
break;
}
ans->child1 = lhs;
ans->child2 = rhs;
//
// Now carry on...
//
lhs = ans;
break;
default:
return lhs;
}
}
}
PARSE_Node *PARSE_p2exp()
{
LEX_Token lt;
PARSE_Node *lhs;
PARSE_Node *rhs;
PARSE_Node *ans;
lhs = PARSE_p3exp();
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_EQUALS:
case LEX_TOKEN_TYPE_NOTEQUAL:
LEX_pop();
rhs = PARSE_p3exp();
//
// Build a little tree...
//
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_EQUALS: ans->type = PARSE_NODE_TYPE_EQUALS; break;
case LEX_TOKEN_TYPE_NOTEQUAL: ans->type = PARSE_NODE_TYPE_NOTEQUAL; break;
default:
ASSERT(0);
break;
}
ans->child1 = lhs;
ans->child2 = rhs;
//
// Now carry on...
//
lhs = ans;
break;
default:
return lhs;
}
}
}
PARSE_Node *PARSE_p1exp()
{
LEX_Token lt;
PARSE_Node *lhs;
PARSE_Node *rhs;
PARSE_Node *ans;
lhs = PARSE_p2exp();
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_AND:
LEX_pop();
rhs = PARSE_p2exp();
//
// Build a little tree...
//
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_AND;
ans->child1 = lhs;
ans->child2 = rhs;
//
// Now carry on...
//
lhs = ans;
break;
default:
return lhs;
}
}
}
PARSE_Node *PARSE_expression()
{
LEX_Token lt;
PARSE_Node *lhs;
PARSE_Node *rhs;
PARSE_Node *ans;
lhs = PARSE_p1exp();
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_OR:
case LEX_TOKEN_TYPE_XOR:
LEX_pop();
rhs = PARSE_p1exp();
//
// Build a little tree...
//
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_OR: ans->type = PARSE_NODE_TYPE_OR; break;
case LEX_TOKEN_TYPE_XOR: ans->type = PARSE_NODE_TYPE_XOR; break;
default:
ASSERT(0);
break;
}
ans->child1 = lhs;
ans->child2 = rhs;
//
// Now carry on...
//
lhs = ans;
break;
default:
//
// Make this whole tree as belonging to an expression.
//
PARSE_traverse(lhs, PARSE_set_expression_flag);
return lhs;
}
}
}
PARSE_Node *PARSE_expression_list()
{
LEX_Token lt;
PARSE_Node *exp;
PARSE_Node *ans;
exp = PARSE_expression();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_COMMA)
{
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_EXP_LIST;
ans->child1 = exp;
ans->child2 = PARSE_expression_list();
return ans;
}
else
{
return exp;
}
}
//
// Returns the depth of an expressionlist.
//
SLONG PARSE_expression_list_depth(PARSE_Node *explist)
{
if (explist->type != PARSE_NODE_TYPE_EXP_LIST)
{
return 1;
}
else
{
return 1 + PARSE_expression_list_depth(explist->child2);
}
}
PARSE_Node *PARSE_var()
{
//
// No arrays for now!
//
PARSE_Node *ans;
LEX_Token lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_VARIABLE)
{
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_VAR_ADDRESS;
ans->variable = PARSE_add_string(lt.variable);
return ans;
}
//
// ERROR!
//
PARSE_throw();
return NULL;
}
PARSE_Node *PARSE_struct()
{
//
// No arrays for now!
//
PARSE_Node *ans;
LEX_Token lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_VARIABLE)
{
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_FIELD;
{
//
// Build the field name (insert a '.' at the beginning)
//
CBYTE field[LEX_MAX_STRING_LENGTH + 32];
sprintf(field, ".%s", lt.variable);
ans->field = PARSE_add_string(field);
}
return ans;
}
//
// ERROR!
//
PARSE_throw();
return NULL;
}
PARSE_Node *PARSE_lvalue()
{
LEX_Token lt;
PARSE_Node *lhs;
PARSE_Node *rhs;
PARSE_Node *ans;
lhs = PARSE_var();
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_DOT:
LEX_pop();
rhs = PARSE_struct();
//
// Build a little tree...
//
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_PUSH_FIELD_ADDRESS;
ans->child1 = lhs;
ans->child2 = rhs;
//
// Now carry on...
//
lhs = ans;
break;
case LEX_TOKEN_TYPE_OSQUARE:
LEX_pop();
//
// Get a list of numbers...
//
rhs = PARSE_expression_list();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_PUSH_ARRAY_ADDRESS;
ans->dimensions = PARSE_expression_list_depth(rhs);
ans->child1 = lhs;
ans->child2 = rhs;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CSQUARE)
{
//
// ERROR!
//
PARSE_throw("Missing close square bracket in array access");
}
LEX_pop();
//
// Now carry on...
//
lhs = ans;
break;
default:
return lhs;
}
}
}
PARSE_Node *PARSE_argument_definition()
{
LEX_Token lt;
//
// Initailise our answer.
//
PARSE_Node *ans = NULL;
PARSE_Node *arg = NULL;
PARSE_Node *last = NULL;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// No arguements?
//
return NULL;
}
LEX_pop();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
LEX_pop();
//
// An empty argument call...
//
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_NOP;
return ans;
}
while(1)
{
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_VARIABLE:
LEX_pop();
//
// Create another argument.
//
arg = PARSE_get_node();
arg->type = PARSE_NODE_TYPE_ARGUMENT;
arg->variable = PARSE_add_string(lt.variable);
arg->child1 = NULL;
//
// Is this the first argument?
//
if (ans == NULL)
{
ans = arg;
}
//
// Do we have to put this onto the end of existing tree
// of arguments?
//
if (last)
{
last->child1 = arg;
}
last = arg;
//
// End of the list or is there another one?
//
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_COMMA)
{
//
// Another variable...
//
LEX_pop();
}
else
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
//
// End of the list.
//
LEX_pop();
return ans;
}
else
{
//
// ERROR!
//
PARSE_throw("Expected a comma or close bracket in argument list");
}
break;
default:
//
// ERROR!
//
PARSE_throw("Something odd in the argument list - only variables allowed!");
}
}
}
PARSE_Node *PARSE_statement()
{
PARSE_Node *ans;
LEX_Token lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_PRINT:
LEX_pop();
ans = PARSE_get_node();
if (LEX_get().type == LEX_TOKEN_TYPE_NEWLINE ||
LEX_get().type == LEX_TOKEN_TYPE_COLON)
{
ans->type = PARSE_NODE_TYPE_NEWLINE;
}
else
{
ans->type = PARSE_NODE_TYPE_PRINT;
ans->child1 = PARSE_expression();
}
return ans;
case LEX_TOKEN_TYPE_GOTO:
case LEX_TOKEN_TYPE_GOSUB:
LEX_pop();
ans = PARSE_get_node();
switch(lt.type)
{
case LEX_TOKEN_TYPE_GOTO: ans->type = PARSE_NODE_TYPE_GOTO; break;
case LEX_TOKEN_TYPE_GOSUB: ans->type = PARSE_NODE_TYPE_GOSUB; break;
default:
ASSERT(0);
break;
}
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_SLUMBER)
{
LEX_pop();
CBYTE label[32];
CBYTE *ch;
itoa(lt.slumber, label, 10);
for (ch = label; *ch; ch++);
ch[0] = ':';
ch[1] = '\000';
ans->label = PARSE_add_string(label);
}
else
if (lt.type == LEX_TOKEN_TYPE_VARIABLE)
{
LEX_pop();
strcat(lt.variable, ":");
ans->label = PARSE_add_string(lt.variable);
}
else
{
//
// ERROR!
//
PARSE_throw("You can only GOTO labels or line numbers");
}
return ans;
case LEX_TOKEN_TYPE_VARIABLE:
{
CBYTE *varname = PARSE_add_string(lt.variable);
//
// This could be a function call.
//
LEX_pop();
LEX_Token lookahead = LEX_get();
if (lookahead.type == LEX_TOKEN_TYPE_OPEN)
{
//
// This is a function call!
//
LEX_push(lt);
ans = PARSE_function_call();
return ans;
}
//
// The 'variable' part could have been
// overwritten by the LEX_get()...
//
lt.variable = varname;
LEX_push(lt);
//
// Variable assignement...
//
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_ASSIGN;
ans->child1 = PARSE_lvalue();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_EQUALS)
{
//
// ERROR!
//
PARSE_throw("Missing equals after a variable (assuming you wanted to assign a value!)");
}
LEX_pop();
ans->child2 = PARSE_expression();
return ans;
}
case LEX_TOKEN_TYPE_IF:
//
// IF THEN (ELSE) statement.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_IF;
ans->child1 = PARSE_expression();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_NEWLINE)
{
//
// This must be a multi-line if.
//
ans->type = PARSE_NODE_TYPE_MIF;
ans->child2 = NULL;
ans->child3 = NULL;
return ans;
}
if (lt.type != LEX_TOKEN_TYPE_THEN)
{
//
// ERROR!
//
PARSE_throw("Missing THEN after an IF statement");
}
LEX_pop();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_NEWLINE)
{
//
// This must be a multi-line if.
//
ans->type = PARSE_NODE_TYPE_MIF;
ans->child2 = NULL;
ans->child3 = NULL;
return ans;
}
ans->child2 = PARSE_statement_list();
//
// Tell the THEN statements they are part of a conditional.
//
PARSE_traverse(ans->child2, PARSE_set_conditional_flag);
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_ELSE)
{
//
// This IF statement has an ELSE bit!
//
LEX_pop();
ans->child3 = PARSE_statement_list();
//
// Tell the ELSE statements they are part of a conditional.
//
PARSE_traverse(ans->child3, PARSE_set_conditional_flag);
}
else
{
//
// IF and THEN without an ELSE.
//
ans->child3 = NULL;
}
return ans;
case LEX_TOKEN_TYPE_NEWLINE:
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_NOP;
return ans;
case LEX_TOKEN_TYPE_EXIT:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_EXIT;
return ans;
case LEX_TOKEN_TYPE_FOR:
{
LEX_pop();
PARSE_forcode++;
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_FOR;
ans->forcode = PARSE_forcode;
//
// The initialisation.
//
ans->child1 = PARSE_statement_list();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_TO)
{
//
// ERROR!
//
PARSE_throw("Missing TO in the FOR statement");
}
LEX_pop();
//
// Find the variable by which the FOR loop is recognised.
//
{
PARSE_Node *init;
if (ans->child1->type == PARSE_NODE_TYPE_STATEMENT_LIST)
{
init = ans->child1->child1;
}
else
{
init = ans->child1;
}
if (init->type == PARSE_NODE_TYPE_ASSIGN)
{
//
// Store the lvalue the FOR loop is initialising.
//
ans->lvalue = init->child1;
}
else
{
//
// This is an anonymous FOR loop.
//
ans->lvalue = NULL;
}
}
//
// Now parse the end condition.
//
ans->child2 = PARSE_expression();
if (PARSE_expression_is_boolean(ans->child2))
{
//
// We don't have to create a boolean expression.
//
}
else
{
//
// Replace the expression with the condition that
// this FOR loop's lvalue > the expression.
//
if (ans->lvalue == NULL)
{
//
// ERROR!
//
PARSE_throw("Cannot establish a condition for terminating the FOR loop. Try making the TO part of the FOR loop a BOOLEAN expression");
}
PARSE_Node *cond = PARSE_get_node();
cond->type = PARSE_NODE_TYPE_GT;
cond->child1 = PARSE_convert_lvalue_to_rvalue(ans->lvalue);
cond->child2 = ans->child2;
ans->child2 = cond;
}
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_STEP)
{
if (ans->lvalue == NULL)
{
//
// ERROR! Can't build a default STEP for an anonymous FOR loop.
//
PARSE_throw( "Cannot create a default STEP for the FOR loop. You must specify a STEP instruction");
}
//
// Build a parse tree to increment this variable.
//
PARSE_Node *assign;
PARSE_Node *varaddress;
PARSE_Node *varvalue;
PARSE_Node *one;
PARSE_Node *add;
varaddress = PARSE_copy_tree(ans->lvalue);
varvalue = PARSE_convert_lvalue_to_rvalue(ans->lvalue);
one = PARSE_get_node();
one->type = PARSE_NODE_TYPE_SLUMBER;
one->slumber = 1;
add = PARSE_get_node();
add->type = PARSE_NODE_TYPE_PLUS;
add->child1 = varvalue;
add->child2 = one;
assign = PARSE_get_node();
assign->type = PARSE_NODE_TYPE_ASSIGN;
assign->child1 = varaddress;
assign->child2 = add;
//
// And make this our STEP instruction.
//
ans->child3 = assign;
}
else
{
LEX_pop();
//
// We have a STEP statement!
//
ans->child3 = PARSE_statement_list();
}
}
return ans;
case LEX_TOKEN_TYPE_NEXT:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_NEXT;
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_VARIABLE)
{
//
// The NEXT statement that knows which FOR loop it belongs to.
//
ans->lvalue = PARSE_lvalue();
}
else
{
//
// An anonymous NEXT.
//
ans->lvalue = NULL;
}
return ans;
case LEX_TOKEN_TYPE_SWAP:
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_SWAP;
ans->child1 = PARSE_lvalue();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_AND)
{
//
// ERROR!
//
PARSE_throw("The two things to swap must be separated by an AND with a SWAP statment. e.g. SWAP x AND w");
}
LEX_pop();
ans->child2 = PARSE_lvalue();
return ans;
case LEX_TOKEN_TYPE_ELSE:
//
// This must be the ELSE instruction of a multi-line IF.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_MELSE;
return ans;
case LEX_TOKEN_TYPE_ENDIF:
//
// This must be the ELSE instruction of a multi-line IF.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_MENDIF;
return ans;
case LEX_TOKEN_TYPE_WHILE:
//
// The start of a WHILE loop.
//
LEX_pop();
PARSE_whilecode += 1;
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_WHILE;
ans->whilecode = PARSE_whilecode;
ans->child1 = PARSE_expression();
return ans;
case LEX_TOKEN_TYPE_LOOP:
//
// The end of a while loop.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_LOOP;
return ans;
case LEX_TOKEN_TYPE_FUNCTION:
//
// A function definition!
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_FUNCTION;
//
// Name of the function...
//
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_VARIABLE)
{
//
// ERROR!
//
PARSE_throw("Expected the name of the function after the FUNCTION keyword");
}
LEX_pop();
{
//
// Put "()" onto the front of the function name so
// we know that it's a function.
//
CBYTE name[LEX_MAX_STRING_LENGTH + 32];
sprintf(name, "()%s", lt.variable);
ans->variable = PARSE_add_string(name);
}
//
// Argument declaration.
//
ans->child1 = PARSE_argument_definition();
return ans;
case LEX_TOKEN_TYPE_RETURN:
//
// Return from a function or subroutine.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_RETURN;
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_ELSE ||
lt.type == LEX_TOKEN_TYPE_ENDIF ||
lt.type == LEX_TOKEN_TYPE_COLON ||
lt.type == LEX_TOKEN_TYPE_NEWLINE)
{
//
// No argument to return...
//
ans->child1 = NULL;
}
else
{
//
// The expression to be returned
//
ans->child1 = PARSE_expression();
}
return ans;
case LEX_TOKEN_TYPE_ENDFUNC:
//
// The end of a function declaration...
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_ENDFUNC;
return ans;
case LEX_TOKEN_TYPE_CALL:
LEX_pop();
ans = PARSE_function_call();
return ans;
case LEX_TOKEN_TYPE_DRAW:
//
// Drawing something!
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_DRAW;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// ERROR!
//
PARSE_throw("Missing open bracket after DRAW");
}
LEX_pop();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
//
// No arguments
//
LEX_pop();
ans->child1 = NULL;
ans->args = 0;
}
else
{
//
// An argument list.
//
ans->child1 = PARSE_expression_list();
ans->args = PARSE_expression_list_depth(ans->child1);
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
PARSE_throw("Missing close bracket after DRAW");
}
LEX_pop();
}
return ans;
case LEX_TOKEN_TYPE_CLS:
//
// Clear the screen.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_CLS;
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OPEN)
{
//
// A CLS without an argument list.
//
ans->child1 = NULL;
ans->args = 0;
}
else
{
LEX_pop();
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_CLOSE)
{
//
// No arguments
//
LEX_pop();
ans->child1 = NULL;
ans->args = 0;
}
else
{
//
// An argument list.
//
ans->child1 = PARSE_expression_list();
ans->args = PARSE_expression_list_depth(ans->child1);
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CLOSE)
{
//
// ERROR!
//
PARSE_throw("Missing close bracket after CLS");
}
LEX_pop();
}
}
return ans;
case LEX_TOKEN_TYPE_FLIP:
//
// A FLIP statement.
//
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_FLIP;
return ans;
case LEX_TOKEN_TYPE_KEY:
//
// Assiging to the KEY array.
//
LEX_pop();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_OSQUARE)
{
//
// ERROR!
//
PARSE_throw("Expected an open square bracket after KEY");
}
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_KEY_ASSIGN;
ans->child1 = PARSE_expression();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_CSQUARE)
{
//
// ERROR!
//
PARSE_throw("Expected a close square bracket after KEY");
}
LEX_pop();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_EQUALS)
{
//
// ERROR!
//
PARSE_throw("Expected an equals after KEY[...]");
}
LEX_pop();
ans->child2 = PARSE_expression();
return ans;
case LEX_TOKEN_TYPE_INKEY:
//
// Assigning to INKEY
//
LEX_pop();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_EQUALS)
{
//
// ERROR!
//
PARSE_throw("Expected an equals after INKEY");
}
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_INKEY_ASSIGN;
ans->child1 = PARSE_expression();
return ans;
case LEX_TOKEN_TYPE_EXPORT:
//
// Exporting a global or function.
//
LEX_pop();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_VARIABLE)
{
//
// ERROR!
//
PARSE_throw("Expected a variable list after EXPORT");
}
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_EXPORT;
ans->variable = PARSE_add_string(lt.variable);
{
PARSE_Node *higher = ans;
PARSE_Node *lower;
//
// We can have a whole long list of exports.
//
while(1)
{
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_COMMA)
{
break;
}
LEX_pop();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_VARIABLE)
{
//
// ERROR!
//
PARSE_throw("Expected a variable after the comma in the EXPORT statement");
}
LEX_pop();
//
// Create a new node.
//
lower = PARSE_get_node();
lower->type = PARSE_NODE_TYPE_EXPORT;
lower->variable = PARSE_add_string(lt.variable);
higher->child1 = lower;
higher = lower;
}
}
return ans;
case LEX_TOKEN_TYPE_LOCAL:
//
// Declaring a variable to be a local.
//
LEX_pop();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_VARIABLE)
{
//
// ERROR!
//
PARSE_throw("Expected a variable list after LOCAL");
}
LEX_pop();
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_LOCAL;
ans->variable = PARSE_add_string(lt.variable);
{
PARSE_Node *higher = ans;
PARSE_Node *lower;
//
// We can have a whole long list of locals.
//
while(1)
{
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_COMMA)
{
break;
}
LEX_pop();
lt = LEX_get();
if (lt.type != LEX_TOKEN_TYPE_VARIABLE)
{
//
// ERROR!
//
PARSE_throw("Expected a variable after the comma in the LOCAL statement");
}
LEX_pop();
//
// Create a new node.
//
lower = PARSE_get_node();
lower->type = PARSE_NODE_TYPE_LOCAL;
lower->variable = PARSE_add_string(lt.variable);
higher->child1 = lower;
higher = lower;
}
}
return ans;
default:
//
// ERROR!
//
PARSE_throw();
return NULL; // To stop the compiler complaining that not all control paths return a value.
}
}
PARSE_Node *PARSE_statement_list()
{
LEX_Token lt;
PARSE_Node *statement;
statement = PARSE_statement();
//
// Should we continue to PARSE another statement?
//
SLONG another_statement = FALSE;
lt = LEX_get();
switch(lt.type)
{
case LEX_TOKEN_TYPE_COLON:
//
// We've found a colon separator.
//
LEX_pop();
another_statement = TRUE;
break;
case LEX_TOKEN_TYPE_ENDIF:
//
// We don't need a colon before an ENDIF instruction...
//
another_statement = TRUE;
break;
case LEX_TOKEN_TYPE_NEWLINE:
break;
default:
if (statement->type == PARSE_NODE_TYPE_MELSE)
{
//
// We dont need a COLON before the next statement.
//
another_statement = TRUE;
}
break;
}
if (another_statement)
{
PARSE_Node *ans;
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_STATEMENT_LIST;
ans->child1 = statement;
ans->child2 = PARSE_statement_list();
return ans;
}
return statement;
}
PARSE_Node *PARSE_labelled_statement_list()
{
PARSE_Node *ans;
LEX_Token lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_FLUMBER)
{
//
// ERROR! Labelling a line with a floating point number?
//
PARSE_throw("You can't label a line with a floating point number");
}
if (lt.type == LEX_TOKEN_TYPE_SLUMBER)
{
LEX_pop();
//
// This line is labelled with a number.
//
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_LABEL;
CBYTE label[32];
CBYTE *ch;
itoa(lt.slumber, label, 10);
for (ch = label; *ch; ch++);
ch[0] = ':';
ch[1] = '\000';
ans->label = PARSE_add_string(label);
ans->child1 = PARSE_statement();
}
else if (lt.type == LEX_TOKEN_TYPE_LABEL)
{
LEX_pop();
//
// This line is labelled with a proper label.
//
ans = PARSE_get_node();
ans->type = PARSE_NODE_TYPE_LABEL;
ans->label = PARSE_add_string(lt.label);
ans->child1 = PARSE_statement();
}
else
{
//
// No label...
//
ans = PARSE_statement_list();
}
lt = LEX_pop();
if (lt.type != LEX_TOKEN_TYPE_NEWLINE)
{
//
// ERROR!
//
PARSE_throw("Expected the end of the line but there was extra stuff");
}
return ans;
}
//
// The maximum length of a source file in bytes. This is pants!
//
CBYTE *PARSE_program;
SLONG PARSE_program_upto;
SLONG PARSE_program_max;
void PARSE_do(CBYTE *fname)
{
SLONG want_to_read;
SLONG bytes_read;
LEX_Token lt;
PARSE_Node *nop;
//
// Initialise...
//
PARSE_node_upto = 0;
PARSE_string_table_upto = 0;
PARSE_line_upto = 0;
PARSE_error_upto = 0;
PARSE_errbuf_upto = 0;
memset(PARSE_node, 0, sizeof(PARSE_node));
if (PARSE_program_max == 0)
{
//
// Allocate memory for the file.
//
PARSE_program_max = 16;
PARSE_program = (CBYTE *) malloc(sizeof(CBYTE) * PARSE_program_max);
memset(PARSE_program, 0, sizeof(CBYTE) * PARSE_program_max);
}
//
// Load the file.
//
FILE *handle = fopen(fname, "rb");
if (!handle)
{
PARSE_add_error("Could not open source file \"%s\"", fname);
return;
}
PARSE_program_upto = 0;
while(1)
{
//
// How many bytes till the end of our buffer?
//
want_to_read = PARSE_program_max - PARSE_program_upto;
//
// Try and load in that many bytes.
//
bytes_read = fread(
PARSE_program + PARSE_program_upto,
sizeof(CBYTE),
want_to_read,
handle);
PARSE_program_upto += bytes_read;
if (bytes_read == want_to_read)
{
//
// More data to read, so allocate a bigger buffer.
//
PARSE_program_max *= 2;
PARSE_program = (CBYTE *) realloc(PARSE_program, sizeof(CBYTE) * PARSE_program_max);
//
// Zero out newly allocated memory.
//
memset(PARSE_program + (PARSE_program_max / 2), 0, sizeof(CBYTE) * PARSE_program_max / 2);
}
else
{
//
// Finished reading program.
//
break;
}
}
//
// Give the input to the lexical analyser.
//
LEX_start(PARSE_program);
//
// This is where we pop back to when we get an error!
//
if (setjmp(PARSE_error_jmp) == 0)
{
//
// This is the first call...
//
ASSERT(PARSE_line_upto == 0);
}
else
{
//
// An error has occurred.
//
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_ERROR)
{
//
// Override the parse error with the lexical error.
//
PARSE_error_type = lt.error;
}
if (!PARSE_add_error("ERROR in %s line %d : %s", fname, PARSE_line_upto, PARSE_error_type))
{
//
// Too many errors! ABORT!
//
return;
}
//
// Try and recover. Go onto the next line.
//
LEX_next_line();
//
// Mark this line as blank.
//
nop = PARSE_get_node();
nop->type = PARSE_NODE_TYPE_NOP;
PARSE_line[PARSE_line_upto++] = nop;
}
while(1)
{
lt = LEX_get();
if (lt.type == LEX_TOKEN_TYPE_EOF)
{
//
// Parsing complete!
//
break;
}
ASSERT(WITHIN(PARSE_line_upto, 0, PARSE_MAX_LINES - 1));
PARSE_line[PARSE_line_upto++] = PARSE_labelled_statement_list();
}
}
void PARSE_traverse_do(PARSE_Node *pn, SLONG (*user_function)(PARSE_Node *pn))
{
switch(pn->type)
{
case PARSE_NODE_TYPE_NOP:
case PARSE_NODE_TYPE_SLUMBER:
case PARSE_NODE_TYPE_FLUMBER:
case PARSE_NODE_TYPE_STRING:
case PARSE_NODE_TYPE_VAR_VALUE:
case PARSE_NODE_TYPE_VAR_ADDRESS:
case PARSE_NODE_TYPE_GOTO:
case PARSE_NODE_TYPE_BOOLEAN:
case PARSE_NODE_TYPE_NEWLINE:
case PARSE_NODE_TYPE_FIELD:
case PARSE_NODE_TYPE_INPUT:
case PARSE_NODE_TYPE_UNDEFINED:
case PARSE_NODE_TYPE_EXIT:
case PARSE_NODE_TYPE_GOSUB:
case PARSE_NODE_TYPE_NEXT:
case PARSE_NODE_TYPE_RANDOM:
case PARSE_NODE_TYPE_MELSE:
case PARSE_NODE_TYPE_MENDIF:
case PARSE_NODE_TYPE_LOOP:
case PARSE_NODE_TYPE_ENDFUNC:
case PARSE_NODE_TYPE_FLIP:
case PARSE_NODE_TYPE_INKEY_VALUE:
case PARSE_NODE_TYPE_TIMER:
case PARSE_NODE_TYPE_EXPORT:
//
// No children.
//
user_function(pn);
break;
case PARSE_NODE_TYPE_LABEL:
case PARSE_NODE_TYPE_UMINUS:
case PARSE_NODE_TYPE_NOT:
case PARSE_NODE_TYPE_PRINT:
case PARSE_NODE_TYPE_SQRT:
case PARSE_NODE_TYPE_ABS:
case PARSE_NODE_TYPE_MIF:
case PARSE_NODE_TYPE_INKEY_ASSIGN:
case PARSE_NODE_TYPE_KEY_VALUE:
case PARSE_NODE_TYPE_SIN:
case PARSE_NODE_TYPE_COS:
case PARSE_NODE_TYPE_TAN:
case PARSE_NODE_TYPE_ASIN:
case PARSE_NODE_TYPE_ACOS:
case PARSE_NODE_TYPE_ATAN:
case PARSE_NODE_TYPE_LEN:
//
// One child.
//
PARSE_traverse_do(pn->child1, user_function);
user_function(pn);
break;
case PARSE_NODE_TYPE_EQUALS:
case PARSE_NODE_TYPE_PLUS:
case PARSE_NODE_TYPE_MINUS:
case PARSE_NODE_TYPE_TIMES:
case PARSE_NODE_TYPE_DIVIDE:
case PARSE_NODE_TYPE_GT:
case PARSE_NODE_TYPE_LT:
case PARSE_NODE_TYPE_GTEQ:
case PARSE_NODE_TYPE_LTEQ:
case PARSE_NODE_TYPE_AND:
case PARSE_NODE_TYPE_OR:
case PARSE_NODE_TYPE_XOR:
case PARSE_NODE_TYPE_DOT:
case PARSE_NODE_TYPE_MOD:
case PARSE_NODE_TYPE_EXP_LIST:
case PARSE_NODE_TYPE_PUSH_ARRAY_ADDRESS:
case PARSE_NODE_TYPE_PUSH_ARRAY_VALUE:
case PARSE_NODE_TYPE_STATEMENT_LIST:
case PARSE_NODE_TYPE_NOTEQUAL:
case PARSE_NODE_TYPE_SWAP:
case PARSE_NODE_TYPE_ATAN2:
//
// Two children.
//
PARSE_traverse_do(pn->child1, user_function);
PARSE_traverse_do(pn->child2, user_function);
user_function(pn);
break;
case PARSE_NODE_TYPE_PUSH_FIELD_ADDRESS:
case PARSE_NODE_TYPE_PUSH_FIELD_VALUE:
//
// Two children- strange order of traversal!
//
PARSE_traverse_do(pn->child1, user_function);
user_function(pn);
PARSE_traverse_do(pn->child2, user_function);
break;
case PARSE_NODE_TYPE_IF:
{
//
// Special case order of traversal including
// putting in fake nodes to help out the CG module!
//
PARSE_Node fake_then;
PARSE_Node fake_else;
PARSE_Node fake_end_else;
memset(&fake_then, 0, sizeof(fake_then ));
memset(&fake_else, 0, sizeof(fake_else ));
memset(&fake_end_else, 0, sizeof(fake_end_else));
pn->ifcode = PARSE_ifcode;
fake_then.type = PARSE_NODE_TYPE_FAKE_THEN;
fake_then.ifcode = PARSE_ifcode;
fake_else.type = PARSE_NODE_TYPE_FAKE_ELSE;
fake_else.ifcode = PARSE_ifcode;
fake_end_else.type = PARSE_NODE_TYPE_FAKE_END_ELSE;
fake_end_else.ifcode = PARSE_ifcode;
PARSE_ifcode += 1;
PARSE_traverse_do(pn->child1, user_function); // Condition
user_function(pn); // IF node ...
PARSE_traverse_do(pn->child2, user_function); // 'THEN' statement
user_function(&fake_then); // The fake THEN node.
if (pn->child3)
{
//
// This IF statement has an ELSE.
//
user_function(&fake_else); // The fake ELSE node.
PARSE_traverse_do(pn->child3, user_function); // 'ELSE' statement
user_function(&fake_end_else); // The fake END_ELSE node.
}
}
break;
case PARSE_NODE_TYPE_ASSIGN:
case PARSE_NODE_TYPE_KEY_ASSIGN:
//
// Special case order of traversal.
//
PARSE_traverse_do(pn->child2, user_function);
PARSE_traverse_do(pn->child1, user_function);
user_function(pn);
break;
case PARSE_NODE_TYPE_FOR:
{
PARSE_Node fake_end_for;
PARSE_Node fake_for_cond;
memset(&fake_end_for, 0, sizeof(fake_end_for ));
memset(&fake_for_cond, 0, sizeof(fake_for_cond));
fake_end_for.type = PARSE_NODE_TYPE_FAKE_END_FOR;
fake_end_for.forcode = pn->forcode;
fake_for_cond.type = PARSE_NODE_TYPE_FAKE_FOR_COND;
fake_for_cond.forcode = pn->forcode;
//
// Initialisation.
//
PARSE_traverse_do(pn->child1, user_function);
//
// The FOR...
//
user_function(pn);
//
// The STEP code.
//
PARSE_traverse_do(pn->child3, user_function);
//
// The END_FOR code...
//
user_function(&fake_end_for);
//
// The condition.
//
PARSE_traverse_do(pn->child2, user_function);
//
// THE FOR_COND node...
//
user_function(&fake_for_cond);
}
break;
case PARSE_NODE_TYPE_WHILE:
{
//
// Build a FAKE_WHILE_COND node.
//
PARSE_Node fake_while_cond;
memset(&fake_while_cond, 0, sizeof(fake_while_cond));
fake_while_cond.type = PARSE_NODE_TYPE_FAKE_WHILE_COND;
fake_while_cond.whilecode = pn->whilecode;
//
// Mark the beginning of the while loop.
//
user_function(pn);
//
// The condition.
//
PARSE_traverse_do(pn->child1, user_function);
//
// Mark the end of the condition.
//
user_function(&fake_while_cond);
}
break;
case PARSE_NODE_TYPE_FUNCTION:
case PARSE_NODE_TYPE_ARGUMENT:
case PARSE_NODE_TYPE_LOCAL:
user_function(pn);
if (pn->child1)
{
PARSE_traverse_do(pn->child1, user_function);
}
break;
case PARSE_NODE_TYPE_CALL:
case PARSE_NODE_TYPE_TEXTURE:
case PARSE_NODE_TYPE_BUFFER:
case PARSE_NODE_TYPE_DRAW:
case PARSE_NODE_TYPE_CLS:
case PARSE_NODE_TYPE_RETURN:
case PARSE_NODE_TYPE_MATRIX:
case PARSE_NODE_TYPE_VECTOR:
//
// Possibly one child.
//
if (pn->child1)
{
PARSE_traverse_do(pn->child1, user_function);
}
user_function(pn);
break;
case PARSE_NODE_TYPE_LEFT:
case PARSE_NODE_TYPE_RIGHT:
//
// One or two kids...
//
PARSE_traverse_do(pn->child1, user_function);
if (pn->child2)
{
PARSE_traverse_do(pn->child2, user_function);
}
user_function(pn);
break;
case PARSE_NODE_TYPE_MID:
//
// Two or three kids...
//
PARSE_traverse_do(pn->child1, user_function);
PARSE_traverse_do(pn->child2, user_function);
if (pn->child3)
{
PARSE_traverse_do(pn->child3, user_function);
}
user_function(pn);
break;
default:
ASSERT(0);
break;
}
return;
}
void PARSE_traverse(PARSE_Node *pn, SLONG (*user_function)(PARSE_Node *pn))
{
//
// Store stack info...
//
PARSE_traverse_do(pn, user_function);
}