/* ============== 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_CompileFile compiles the 0 terminated text, adding defintions to the pr structure ============ */ qboolean PR_CompileFile (char *string, char *filename) { if (!pr.memory) Error ("PR_CompileFile: Didn't clear"); PR_ClearGrabMacros (); // clear the frame macros pr_file_p = string; s_file = CopyString (filename); pr_source_line = 0; PR_NewLine (); PR_Lex (); // read first token while (pr_token_type != tt_eof) { if (setjmp(pr_parse_abort)) { if (++pr_error_count > MAX_ERRORS) return false; PR_SkipToSemicolon (); if (pr_token_type == tt_eof) return false; } pr_scope = NULL; // outside all functions PR_ParseDefs (); } return (pr_error_count == 0); }
void PR_Expect(char *string) { if (DefCmp(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 ============= */ qboolean PR_Check (char *string) { if (strcmp (string, pr_token)) return false; PR_Lex (); return true; }
/* ============ 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 }
void PR_Expect (char *string, token_type_t type) { if (STRCMP(string, pr_token)) PR_ParseError (550, "Expected %s, found %s",string, pr_token); if (type) if (pr_token_type != type) PR_ParseError (550, "Expected %s, found %s",string, pr_token); PR_Lex (); }
/* ============ PR_ParseImmediateStatements Parse a function body ============ */ function_t *PR_ParseImmediateStatements (type_t *type) { int i; function_t *f; def_t *defs[MAX_PARMS]; f = malloc (sizeof(function_t)); // // check for builtin function definition #1, #2, etc // if (PR_Check ("#")) { if (pr_token_type != tt_immediate || pr_immediate_type != &type_float || pr_immediate._float != (int)pr_immediate._float) PR_ParseError ("Bad builtin immediate"); f->builtin = (int)pr_immediate._float; PR_Lex (); return f; } f->builtin = 0; // // define the parms // for (i=0 ; i<type->num_parms ; i++) { defs[i] = PR_GetDef (type->parm_types[i], pr_parm_names[i], pr_scope, true); f->parm_ofs[i] = defs[i]->ofs; if (i > 0 && f->parm_ofs[i] < f->parm_ofs[i-1]) Error ("bad parm order"); } f->code = numstatements; // // check for a state opcode // if (PR_Check ("[")) PR_ParseState (); // // parse regular statements // PR_Expect ("{"); while (!PR_Check("}")) PR_ParseStatement (); // emit an end of statements opcode PR_Statement (pr_opcodes, 0,0); return f; }
boolean PR_Check (char *string, token_type_t type) { if (STRCMP(string, pr_token)) return false; if (type) if (pr_token_type != type) return false; PR_Lex (); return true; }
/* ============ PR_ParseImmediate Allocates the immediate if needed and gets next token ============ */ def_t *PR_ParseImmediate (void) { assert (pr_immediate_type == &type_const_float || pr_immediate_type == &type_const_string || pr_immediate_type == &type_const_vector); def_t *def = PR_GetImmediate (pr_immediate_type, pr_immediate, pr_immediate_string); PR_Lex (); return def; }
/* ============ 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_ParseType Parses a variable type, including field and functions types ============ */ type_t *PR_ParseType (void) { if (PR_Check (".")) { type_t newtype; memset (&newtype, 0, sizeof(newtype)); newtype.type = ev_field; newtype.aux_type = PR_ParseType (); return PR_GetType (&newtype); } type_t *type; bool constant = PR_Check ("const"); if (!strcmp (pr_token, "float") ) type = constant ? &type_const_float : &type_float; else if (!strcmp (pr_token, "vector") ) type = constant ? &type_const_vector : &type_vector; else if (!strcmp (pr_token, "entity") ) type = &type_entity; else if (!strcmp (pr_token, "string") ) type = constant ? &type_const_string : &type_string; else if (!strcmp (pr_token, "void") ) type = &type_void; else { PR_ParseError ("\"%s\" is not a type", pr_token); type = &type_void; // shut up compiler warning } PR_Lex (); if (PR_Check("(")) { // function type // go back to non-const types // FIXME: don't bother? Or force const types instead? if (type == &type_const_float) type = &type_float; else if (type == &type_const_vector) type = &type_vector; else if (type == &type_const_string) type = &type_string; return PR_ParseFunctionType(type); } return type; }
/* ================ PR_ParseInitialization "<type> <name> = " was parsed, parse the rest ================ */ void PR_ParseInitialization (type_t *type, char *name, def_t *def) { if (def->initialized) PR_ParseError ("%s redeclared", name); if (pr_token_type != tt_immediate && pr_token_type != tt_name) PR_ParseError ("syntax error : '%s'", pr_token); if (pr_token_type == tt_name) { PR_ParseError ("initializer is not a constant"); } if (!CompareType(pr_immediate_type, type)) PR_ParseError ("wrong immediate type for %s", name); def->initialized = 1; if (type == &type_const_string || type == &type_string) pr_immediate.string = CopyString (pr_immediate_string); memcpy (pr_globals + def->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); PR_Lex (); }
/* ================ PR_ParseDefs Called at the outer layer and when a local statement is hit ================ */ void PR_ParseDefs (void) { char *name; type_t *type; def_t *def; function_t *f; dfunction_t *df; int i; int locals_start; type = PR_ParseType (); if (pr_scope && (type->type == ev_field || type->type == ev_function) ) PR_ParseError ("Fields and functions must be global"); do { name = PR_ParseName (); def = PR_GetDef (type, name, pr_scope, true); // check for an initialization if ( PR_Check ("=") ) { if (def->initialized) PR_ParseError ("%s redeclared", name); if (type->type == ev_function) { locals_start = locals_end = numpr_globals; pr_scope = def; f = PR_ParseImmediateStatements (type); pr_scope = NULL; def->initialized = 1; G_FUNCTION(def->ofs) = numfunctions; f->def = def; // if (pr_dumpasm) // PR_PrintFunction (def); // fill in the dfunction df = &functions[numfunctions]; numfunctions++; if (f->builtin) df->first_statement = -f->builtin; else df->first_statement = f->code; df->s_name = CopyString (f->def->name); df->s_file = s_file; df->numparms = f->def->type->num_parms; df->locals = locals_end - locals_start; df->parm_start = locals_start; for (i=0 ; i<df->numparms ; i++) df->parm_size[i] = type_size[f->def->type->parm_types[i]->type]; continue; } else if (pr_immediate_type != type) PR_ParseError ("wrong immediate type for %s", name); def->initialized = 1; memcpy (pr_globals + def->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); PR_Lex (); } } while (PR_Check (",")); PR_Expect (";"); }
/* ============ PR_ParseImmediate Looks for a preexisting constant ============ */ def_t *PR_ParseImmediate (void) { def_t *cn; // check for a constant with the same value for (cn=pr.def_head.next ; cn ; cn=cn->next) { if (!cn->initialized) continue; if (cn->type != pr_immediate_type) continue; if (pr_immediate_type == &type_string) { if (!strcmp(G_STRING(cn->ofs), pr_immediate_string) ) { PR_Lex (); return cn; } } else if (pr_immediate_type == &type_float) { if ( G_FLOAT(cn->ofs) == pr_immediate._float ) { PR_Lex (); return cn; } } else if (pr_immediate_type == &type_vector) { if ( ( G_FLOAT(cn->ofs) == pr_immediate.vector[0] ) && ( G_FLOAT(cn->ofs+1) == pr_immediate.vector[1] ) && ( G_FLOAT(cn->ofs+2) == pr_immediate.vector[2] ) ) { PR_Lex (); return cn; } } else PR_ParseError ("weird immediate type"); } // allocate a new one cn = malloc (sizeof(def_t)); cn->next = NULL; pr.def_tail->next = cn; pr.def_tail = cn; cn->search_next = pr.search; pr.search = cn; cn->type = pr_immediate_type; cn->name = "IMMEDIATE"; cn->initialized = 1; cn->scope = NULL; // always share immediates // copy the immediate to the global area cn->ofs = numpr_globals; pr_global_defs[cn->ofs] = cn; numpr_globals += type_size[pr_immediate_type->type]; if (pr_immediate_type == &type_string) pr_immediate.string = CopyString (pr_immediate_string); memcpy (pr_globals + cn->ofs, &pr_immediate, 4*type_size[pr_immediate_type->type]); PR_Lex (); return cn; }
/* ======================== PR_LexPrecomp parses and executes directives with a leading '#': #define #undef #ifdef #ifndef #else #endif #error #message #pragma message ======================== */ void PR_LexPrecomp (void) { // yeah it isn't quite Precompiler is it? pr_file_p++; // skip the hash if (!PR_SimpleGetToken ()) PR_ParseError ("Q534: Invalid preprocessor command"); // that's not possible if (!strcmp(pr_token, "ifdef")) { if (ifdefdepth > ignoredepth) { // inside another ignored "ifdef"/"ifndef" // -> ignore statements ifdefdepth++; return; } ifdefdepth++; ignoredepth = ifdefdepth; PR_Lex(); if (!PR_FindDefine(pr_token, true)) { // not defined // -> ignore statements until endif or else ignoredepth--; while(ifdefdepth > ignoredepth) PR_Lex(); return; } // defined // -> parse statements PR_Lex(); return; } else if (!strcmp(pr_token, "ifndef")) { if (ifdefdepth > ignoredepth) { // inside another ignored ifdef // -> ignore statements ifdefdepth++; return; } ifdefdepth++; ignoredepth = ifdefdepth; PR_Lex(); if (PR_FindDefine(pr_token, true)) { // defined // -> ignore statements ignoredepth--; while(ifdefdepth > ignoredepth) PR_Lex(); return; } PR_Lex(); return; } else if (!strcmp(pr_token, "endif")) { ifdefdepth--; if (ifdefdepth < 0) PR_ParseError ("Q119: Too many #endifs"); PR_Lex(); return; } else if (!strcmp(pr_token, "else")) { if (ifdefdepth == (ignoredepth + 1)) { // the "ifdef" or "ifndef" part has not been entered // -> parse the statements inside "else" //print("parsing statment %s in else on %s(%ld)", pr_token, s_file + strings, pr_source_line); ignoredepth = ifdefdepth; pr_token_type = tt_name; PR_Lex(); return; } // "ifdef" or "ifndef" part has already been entered // -> ignore statements in "else" part ignoredepth--; while (ifdefdepth > ignoredepth) PR_Lex(); return; } else if (ifdefdepth > ignoredepth) { //print("ignored %s on %s(%ld)", pr_token, s_file + strings, pr_source_line); return; } else if (PR_Check("error")) { if (pr_immediate_type != &type_string && pr_immediate_type != &type_const_string) PR_ParseError ("Q541: Error must be a string"); PR_ParseError ("User Error on %s(%ld): %s", s_file + strings, pr_source_line, pr_immediate_string); PR_Lex(); return; } else if (PR_Check("message")) { if (pr_immediate_type != &type_string && pr_immediate_type != &type_const_string) PR_ParseError ("Q541: Message must be a string"); printf ("Message on %s(%ld): %s\n", s_file + strings, (long)pr_source_line, pr_immediate_string); PR_Lex(); return; } else if (PR_Check("pragma")) { if (PR_Check("message")) { if (pr_immediate_type != &type_string && pr_immediate_type != &type_const_string) PR_ParseError ("Q541: Message must be a string"); printf ("Message on %s(%ld): %s\n", s_file + strings, (long)pr_source_line, pr_immediate_string); PR_Lex(); return; } // unknown pragma directive printf ("Warning on %s(%ld): unknown #pragma \"%s\" (will be ignored)", s_file + strings, (long)pr_source_line, pr_token); // skip to the end of the line while (PR_SimpleGetToken ()) ; PR_Lex(); return; } else if (PR_Check("define")) { char define_name[2048]; if (pr_token_type != tt_name) PR_ParseError ("Q543: #define: Invalid name"); // predefine it: strlcpy (define_name, pr_token, sizeof(define_name)); if (PR_AddDefine (define_name, &type_const_float, NULL, false) <= 0) PR_ParseError ("Q544: #define \"%s\": creation failed", define_name); // get the value of the define PR_Lex(); if (pr_token_type == tt_immediate) { if (pr_immediate_type != &type_float && pr_immediate_type != &type_const_float) PR_ParseError ("Q545: #define \"%s\": Invalid type of value", define_name); // finally fix the define (with given value) if (PR_AddDefine (define_name, pr_immediate_type, &pr_immediate, false) <= 0) PR_ParseError ("Q544: #define \"%s\": creation failed", define_name); PR_Lex(); } else { eval_t value; value._float = 1; // finally fix the define (with default-value) if (PR_AddDefine (define_name, &type_const_float, &value, false) <= 0) PR_ParseError ("Q544: #define \"%s\": creation failed", define_name); } return; } else if (PR_Check("undef")) { if (pr_token_type != tt_name) PR_ParseError ("Q544: #undef: Invalid name"); PR_DelDefine (pr_token, false); PR_Lex(); return; } } // END_FUNC PR_LexPrecomp