Permalink
Cannot retrieve contributors at this time
Fetching contributors…

/* Copyright (C) 1996-1997 Id Software, Inc. | |
This program is free software; you can redistribute it and/or modify | |
it under the terms of the GNU General Public License as published by | |
the Free Software Foundation; either version 2 of the License, or | |
(at your option) any later version. | |
This program is distributed in the hope that it will be useful, | |
but WITHOUT ANY WARRANTY; without even the implied warranty of | |
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
GNU General Public License for more details. | |
You should have received a copy of the GNU General Public License | |
along with this program; if not, write to the Free Software | |
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
See file, 'COPYING', for details. | |
*/ | |
#include "qcc.h" | |
int pr_source_line; | |
char *pr_file_p; | |
char *pr_line_start; // start of current source line | |
int pr_bracelevel; | |
char pr_token[2048]; | |
token_type_t pr_token_type; | |
type_t *pr_immediate_type; | |
eval_t pr_immediate; | |
char pr_immediate_string[2048]; | |
int pr_error_count; | |
char *pr_punctuation[] = | |
// longer symbols must be before a shorter partial match | |
{"&&", "||", "<=", ">=","==", "!=", ";", ",", "!", "*", "/", "(", ")", "-", "+", "=", "[", "]", "{", "}", "...", ".", "<", ">" , "#" , "&" , "|" , NULL}; | |
// simple types. function types are dynamically allocated | |
type_t type_void = {ev_void, &def_void}; | |
type_t type_string = {ev_string, &def_string}; | |
type_t type_float = {ev_float, &def_float}; | |
type_t type_vector = {ev_vector, &def_vector}; | |
type_t type_entity = {ev_entity, &def_entity}; | |
type_t type_field = {ev_field, &def_field}; | |
type_t type_function = {ev_function, &def_function,NULL,&type_void}; | |
// type_function is a void() function used for state defs | |
type_t type_pointer = {ev_pointer, &def_pointer}; | |
type_t type_floatfield = {ev_field, &def_field, NULL, &type_float}; | |
int type_size[8] = {1,1,1,3,1,1,1,1}; | |
def_t def_void = {&type_void, "temp"}; | |
def_t def_string = {&type_string, "temp"}; | |
def_t def_float = {&type_float, "temp"}; | |
def_t def_vector = {&type_vector, "temp"}; | |
def_t def_entity = {&type_entity, "temp"}; | |
def_t def_field = {&type_field, "temp"}; | |
def_t def_function = {&type_function, "temp"}; | |
def_t def_pointer = {&type_pointer, "temp"}; | |
def_t def_ret, def_parms[MAX_PARMS]; | |
def_t *def_for_type[8] = {&def_void, &def_string, &def_float, &def_vector, &def_entity, &def_field, &def_function, &def_pointer}; | |
void PR_LexWhitespace (void); | |
/* | |
============== | |
PR_PrintNextLine | |
============== | |
*/ | |
void PR_PrintNextLine (void) | |
{ | |
char *t; | |
printf ("%3i:",pr_source_line); | |
for (t=pr_line_start ; *t && *t != '\n' ; t++) | |
printf ("%c",*t); | |
printf ("\n"); | |
} | |
/* | |
============== | |
PR_NewLine | |
Call at start of file and when *pr_file_p == '\n' | |
============== | |
*/ | |
void PR_NewLine (void) | |
{ | |
boolean m; | |
if (*pr_file_p == '\n') | |
{ | |
pr_file_p++; | |
m = true; | |
} | |
else | |
m = false; | |
pr_source_line++; | |
pr_line_start = pr_file_p; | |
// if (pr_dumpasm) | |
// PR_PrintNextLine (); | |
if (m) | |
pr_file_p--; | |
} | |
/* | |
============== | |
PR_LexString | |
Parses a quoted string | |
============== | |
*/ | |
void PR_LexString (void) | |
{ | |
int c; | |
int len; | |
len = 0; | |
pr_file_p++; | |
do | |
{ | |
c = *pr_file_p++; | |
if (!c) | |
PR_ParseError ("EOF inside quote"); | |
if (c=='\n') | |
PR_ParseError ("newline inside quote"); | |
if (c=='\\') | |
{ // escape char | |
c = *pr_file_p++; | |
if (!c) | |
PR_ParseError ("EOF inside quote"); | |
if (c == 'n') | |
c = '\n'; | |
else if (c == '"') | |
c = '"'; | |
else | |
PR_ParseError ("Unknown escape char"); | |
} | |
else if (c=='\"') | |
{ | |
pr_token[len] = 0; | |
pr_token_type = tt_immediate; | |
pr_immediate_type = &type_string; | |
strcpy (pr_immediate_string, pr_token); | |
return; | |
} | |
pr_token[len] = c; | |
len++; | |
} while (1); | |
} | |
/* | |
============== | |
PR_LexNumber | |
============== | |
*/ | |
float PR_LexNumber (void) | |
{ | |
int c; | |
int len; | |
len = 0; | |
c = *pr_file_p; | |
do | |
{ | |
pr_token[len] = c; | |
len++; | |
pr_file_p++; | |
c = *pr_file_p; | |
} while ((c >= '0' && c<= '9') || c == '.'); | |
pr_token[len] = 0; | |
return atof (pr_token); | |
} | |
/* | |
============== | |
PR_LexVector | |
Parses a single quoted vector | |
============== | |
*/ | |
void PR_LexVector (void) | |
{ | |
int i; | |
pr_file_p++; | |
pr_token_type = tt_immediate; | |
pr_immediate_type = &type_vector; | |
for (i=0 ; i<3 ; i++) | |
{ | |
pr_immediate.vector[i] = PR_LexNumber (); | |
PR_LexWhitespace (); | |
} | |
if (*pr_file_p != '\'') | |
PR_ParseError ("Bad vector"); | |
pr_file_p++; | |
} | |
/* | |
============== | |
PR_LexName | |
Parses an identifier | |
============== | |
*/ | |
void PR_LexName (void) | |
{ | |
int c; | |
int len; | |
len = 0; | |
c = *pr_file_p; | |
do | |
{ | |
pr_token[len] = c; | |
len++; | |
pr_file_p++; | |
c = *pr_file_p; | |
} while ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' | |
|| (c >= '0' && c <= '9')); | |
pr_token[len] = 0; | |
pr_token_type = tt_name; | |
} | |
/* | |
============== | |
PR_LexPunctuation | |
============== | |
*/ | |
void PR_LexPunctuation (void) | |
{ | |
int i; | |
int len; | |
char *p; | |
pr_token_type = tt_punct; | |
for (i=0 ; (p = pr_punctuation[i]) != NULL ; i++) | |
{ | |
len = strlen(p); | |
if (!strncmp(p, pr_file_p, len) ) | |
{ | |
strcpy (pr_token, p); | |
if (p[0] == '{') | |
pr_bracelevel++; | |
else if (p[0] == '}') | |
pr_bracelevel--; | |
pr_file_p += len; | |
return; | |
} | |
} | |
PR_ParseError ("Unknown punctuation"); | |
} | |
/* | |
============== | |
PR_LexWhitespace | |
============== | |
*/ | |
void PR_LexWhitespace (void) | |
{ | |
int c; | |
while (1) | |
{ | |
// skip whitespace | |
while ( (c = *pr_file_p) <= ' ') | |
{ | |
if (c=='\n') | |
PR_NewLine (); | |
if (c == 0) | |
return; // end of file | |
pr_file_p++; | |
} | |
// skip // comments | |
if (c=='/' && pr_file_p[1] == '/') | |
{ | |
while (*pr_file_p && *pr_file_p != '\n') | |
pr_file_p++; | |
PR_NewLine(); | |
pr_file_p++; | |
continue; | |
} | |
// skip /* */ comments | |
if (c=='/' && pr_file_p[1] == '*') | |
{ | |
do | |
{ | |
pr_file_p++; | |
if (pr_file_p[0]=='\n') | |
PR_NewLine(); | |
if (pr_file_p[1] == 0) | |
return; | |
} while (pr_file_p[-1] != '*' || pr_file_p[0] != '/'); | |
pr_file_p++; | |
continue; | |
} | |
break; // a real character has been found | |
} | |
} | |
//============================================================================ | |
#define MAX_FRAMES 256 | |
char pr_framemacros[MAX_FRAMES][16]; | |
int pr_nummacros; | |
void PR_ClearGrabMacros (void) | |
{ | |
pr_nummacros = 0; | |
} | |
void PR_FindMacro (void) | |
{ | |
int i; | |
for (i=0 ; i<pr_nummacros ; i++) | |
if (!strcmp (pr_token, pr_framemacros[i])) | |
{ | |
sprintf (pr_token,"%d", i); | |
pr_token_type = tt_immediate; | |
pr_immediate_type = &type_float; | |
pr_immediate._float = i; | |
return; | |
} | |
PR_ParseError ("Unknown frame macro $%s", pr_token); | |
} | |
// just parses text, returning false if an eol is reached | |
boolean PR_SimpleGetToken (void) | |
{ | |
int c; | |
int i; | |
// skip whitespace | |
while ( (c = *pr_file_p) <= ' ') | |
{ | |
if (c=='\n' || c == 0) | |
return false; | |
pr_file_p++; | |
} | |
i = 0; | |
while ( (c = *pr_file_p) > ' ' && c != ',' && c != ';') | |
{ | |
pr_token[i] = c; | |
i++; | |
pr_file_p++; | |
} | |
pr_token[i] = 0; | |
return true; | |
} | |
void PR_ParseFrame (void) | |
{ | |
while (PR_SimpleGetToken ()) | |
{ | |
strcpy (pr_framemacros[pr_nummacros], pr_token); | |
pr_nummacros++; | |
} | |
} | |
/* | |
============== | |
PR_LexGrab | |
Deals with counting sequence numbers and replacing frame macros | |
============== | |
*/ | |
void PR_LexGrab (void) | |
{ | |
pr_file_p++; // skip the $ | |
if (!PR_SimpleGetToken ()) | |
PR_ParseError ("hanging $"); | |
// check for $frame | |
if (!strcmp (pr_token, "frame")) | |
{ | |
PR_ParseFrame (); | |
PR_Lex (); | |
} | |
// ignore other known $commands | |
else if (!strcmp (pr_token, "cd") | |
|| !strcmp (pr_token, "origin") | |
|| !strcmp (pr_token, "base") | |
|| !strcmp (pr_token, "flags") | |
|| !strcmp (pr_token, "scale") | |
|| !strcmp (pr_token, "skin") ) | |
{ // skip to end of line | |
while (PR_SimpleGetToken ()) | |
; | |
PR_Lex (); | |
} | |
// look for a frame name macro | |
else | |
PR_FindMacro (); | |
} | |
//============================================================================ | |
/* | |
============== | |
PR_Lex | |
Sets pr_token, pr_token_type, and possibly pr_immediate and pr_immediate_type | |
============== | |
*/ | |
void PR_Lex (void) | |
{ | |
int c; | |
pr_token[0] = 0; | |
if (!pr_file_p) | |
{ | |
pr_token_type = tt_eof; | |
return; | |
} | |
PR_LexWhitespace (); | |
c = *pr_file_p; | |
if (!c) | |
{ | |
pr_token_type = tt_eof; | |
return; | |
} | |
// handle quoted strings as a unit | |
if (c == '\"') | |
{ | |
PR_LexString (); | |
return; | |
} | |
// handle quoted vectors as a unit | |
if (c == '\'') | |
{ | |
PR_LexVector (); | |
return; | |
} | |
// if the first character is a valid identifier, parse until a non-id | |
// character is reached | |
if ( (c >= '0' && c <= '9') || ( c=='-' && pr_file_p[1]>='0' && pr_file_p[1] <='9') ) | |
{ | |
pr_token_type = tt_immediate; | |
pr_immediate_type = &type_float; | |
pr_immediate._float = PR_LexNumber (); | |
return; | |
} | |
if ( (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' ) | |
{ | |
PR_LexName (); | |
return; | |
} | |
if (c == '$') | |
{ | |
PR_LexGrab (); | |
return; | |
} | |
// parse symbol strings until a non-symbol is found | |
PR_LexPunctuation (); | |
} | |
//============================================================================= | |
/* | |
============ | |
PR_ParseError | |
Aborts the current file load | |
============ | |
*/ | |
void PR_ParseError (char *error, ...) | |
{ | |
va_list argptr; | |
char string[1024]; | |
va_start (argptr,error); | |
vsprintf (string,error,argptr); | |
va_end (argptr); | |
printf ("%s:%i:%s\n", strings + s_file, pr_source_line, string); | |
longjmp (pr_parse_abort, 1); | |
} | |
/* | |
============= | |
PR_Expect | |
Issues an error if the current token isn't equal to string | |
Gets the next token | |
============= | |
*/ | |
void PR_Expect (char *string) | |
{ | |
if (strcmp (string, pr_token)) | |
PR_ParseError ("expected %s, found %s",string, pr_token); | |
PR_Lex (); | |
} | |
/* | |
============= | |
PR_Check | |
Returns true and gets the next token if the current token equals string | |
Returns false and does nothing otherwise | |
============= | |
*/ | |
boolean PR_Check (char *string) | |
{ | |
if (strcmp (string, pr_token)) | |
return false; | |
PR_Lex (); | |
return true; | |
} | |
/* | |
============ | |
PR_ParseName | |
Checks to see if the current token is a valid name | |
============ | |
*/ | |
char *PR_ParseName (void) | |
{ | |
static char ident[MAX_NAME]; | |
if (pr_token_type != tt_name) | |
PR_ParseError ("not a name"); | |
if (strlen(pr_token) >= MAX_NAME-1) | |
PR_ParseError ("name too long"); | |
strcpy (ident, pr_token); | |
PR_Lex (); | |
return ident; | |
} | |
/* | |
============ | |
PR_FindType | |
Returns a preexisting complex type that matches the parm, or allocates | |
a new one and copies it out. | |
============ | |
*/ | |
type_t *PR_FindType (type_t *type) | |
{ | |
def_t *def; | |
type_t *check; | |
int i; | |
for (check = pr.types ; check ; check = check->next) | |
{ | |
if (check->type != type->type | |
|| check->aux_type != type->aux_type | |
|| check->num_parms != type->num_parms) | |
continue; | |
for (i=0 ; i< type->num_parms ; i++) | |
if (check->parm_types[i] != type->parm_types[i]) | |
break; | |
if (i == type->num_parms) | |
return check; | |
} | |
// allocate a new one | |
check = malloc (sizeof (*check)); | |
*check = *type; | |
check->next = pr.types; | |
pr.types = check; | |
// allocate a generic def for the type, so fields can reference it | |
def = malloc (sizeof(def_t)); | |
def->name = "COMPLEX TYPE"; | |
def->type = check; | |
check->def = def; | |
return check; | |
} | |
/* | |
============ | |
PR_SkipToSemicolon | |
For error recovery, also pops out of nested braces | |
============ | |
*/ | |
void PR_SkipToSemicolon (void) | |
{ | |
do | |
{ | |
if (!pr_bracelevel && PR_Check (";")) | |
return; | |
PR_Lex (); | |
} while (pr_token[0]); // eof will return a null token | |
} | |
/* | |
============ | |
PR_ParseType | |
Parses a variable type, including field and functions types | |
============ | |
*/ | |
char pr_parm_names[MAX_PARMS][MAX_NAME]; | |
type_t *PR_ParseType (void) | |
{ | |
type_t new; | |
type_t *type; | |
char *name; | |
if (PR_Check (".")) | |
{ | |
memset (&new, 0, sizeof(new)); | |
new.type = ev_field; | |
new.aux_type = PR_ParseType (); | |
return PR_FindType (&new); | |
} | |
if (!strcmp (pr_token, "float") ) | |
type = &type_float; | |
else if (!strcmp (pr_token, "vector") ) | |
type = &type_vector; | |
else if (!strcmp (pr_token, "float") ) | |
type = &type_float; | |
else if (!strcmp (pr_token, "entity") ) | |
type = &type_entity; | |
else if (!strcmp (pr_token, "string") ) | |
type = &type_string; | |
else if (!strcmp (pr_token, "void") ) | |
type = &type_void; | |
else | |
{ | |
PR_ParseError ("\"%s\" is not a type", pr_token); | |
type = &type_float; // shut up compiler warning | |
} | |
PR_Lex (); | |
if (!PR_Check ("(")) | |
return type; | |
// function type | |
memset (&new, 0, sizeof(new)); | |
new.type = ev_function; | |
new.aux_type = type; // return type | |
new.num_parms = 0; | |
if (!PR_Check (")")) | |
{ | |
if (PR_Check ("...")) | |
new.num_parms = -1; // variable args | |
else | |
do | |
{ | |
type = PR_ParseType (); | |
name = PR_ParseName (); | |
strcpy (pr_parm_names[new.num_parms], name); | |
new.parm_types[new.num_parms] = type; | |
new.num_parms++; | |
} while (PR_Check (",")); | |
PR_Expect (")"); | |
} | |
return PR_FindType (&new); | |
} | |