QFCC -- the QuakeForge Code Compiler -- an autoconfiscated qcc.

It sucks, but it works, and will form the basis for something that Does
Not Suck.
This commit is contained in:
Jeff Teunissen 2001-02-24 06:58:54 +00:00
parent df916ef2c7
commit 2dd03876af
18 changed files with 4350 additions and 0 deletions

6
tools/qfcc/AUTHORS Normal file
View file

@ -0,0 +1,6 @@
The original "qcc" program was written by Id Software, Inc.
This version was created by Jeff Teunissen <deek@dusknet.dhs.org> and is
maintained by The QuakeForge Project <quake-devel@lists.sourceforge.net>
Please do not contact Id Software for technical support on this program -- they
do not care about it any more. :)

53
tools/qfcc/Makefile.am Normal file
View file

@ -0,0 +1,53 @@
# Makefile.am
#
# Automake-using build system for QuakeForge
#
# Copyright (C) 2000 Jeff Teunissen <deek@quakeforge.net>
#
# This Makefile 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:
#
# Free Software Foundation, Inc.
# 59 Temple Place - Suite 330
# Boston, MA 02111-1307, USA
#
# $Id$
#
AUTOMAKE_OPTIONS= foreign
SUBDIRS= include source
# uncomment the following if qfcc requires the math library
#qfcc_LDADD=-lm
EXTRA_DIST=qfcc.lsm.in qfcc.spec.in
dist-zip: distdir
-chmod -R a+r $(distdir)
ZIP="-r9q" zip $(distdir).zip $(NOCONV_DIST)
ZIP="-r9ql" zip $(distdir).zip $(distdir) -x $(NOCONV_DIST)
-rm -rf $(distdir)
dist-bz2: distdir
-chmod -R a+r $(distdir)
BZIP2="-9" $(TAR) Ichof $(distdir).tar.bz2 $(distdir)
-rm -rf $(distdir)
dist-all-local: distdir
-chmod -R a+r $(distdir)
GZIP=$(GZIP_ENV) $(TAR) chozf $(distdir).tar.gz $(distdir)
BZIP2="-9" $(TAR) Ichof $(distdir).tar.bz2 $(distdir)
ZIP="-r9q" zip $(distdir).zip $(NOCONV_DIST)
ZIP="-r9ql" zip $(distdir).zip $(distdir) -x $(NOCONV_DIST)
-rm -rf $(distdir)

8
tools/qfcc/acconfig.h Normal file
View file

@ -0,0 +1,8 @@
/*
Compiler/Machine-Specific Configuration
*/
#ifndef __config_h_
#define __config_h_
@TOP@
@BOTTOM@
#endif // __config_h_

2
tools/qfcc/bootstrap Executable file
View file

@ -0,0 +1,2 @@
#!/bin/sh
aclocal && autoheader && automake --add-missing && autoconf

55
tools/qfcc/configure.in Normal file
View file

@ -0,0 +1,55 @@
dnl Process this file with autoconf to produce a configure script.
AC_PREREQ(2.13)
AC_INIT(source/qfcc.c)
AC_REVISION($Revision$) dnl
AM_CONFIG_HEADER(include/config.h)
AC_CANONICAL_SYSTEM
dnl Every other copy of the package version number gets its value from here
AM_INIT_AUTOMAKE(qfcc, 0.1.0)
AC_SUBST(VERSION)
ISODATE=$(date +%Y-%m-%d)
AC_SUBST(ISODATE)
AC_LANG_C
dnl Checks for programs.
AC_PROG_INSTALL
AC_PROG_CC
AC_PROG_CPP
set $CC
if test "$1" = gcc; then
shift
args="$*"
AC_MSG_CHECKING(for broken gcc)
if test $(gcc --version) = 2.96; then
AC_MSG_RESULT(yes. You poor sod, hope you have egcs)
CC="egcs $args"
set $CPP
shift
CPP="egcs $*"
else
AC_MSG_RESULT(no. good.)
fi
fi
dnl Checks for libraries.
dnl Checks for header files.
AC_HEADER_STDC
AC_CHECK_HEADERS(unistd.h)
dnl Checks for typedefs, structures, and compiler characteristics.
dnl Checks for library functions.
AC_OUTPUT(
include/Makefile
source/Makefile
Makefile
qfcc.lsm
)

55
tools/qfcc/doc/man/qfcc.1 Normal file
View file

@ -0,0 +1,55 @@
.\" hey, Emacs: -*- nroff -*-
.\" qfcc 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; see the file COPYING. If not, write to
.\" the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
.\"
.TH QFCC 1 "February 21, 2001" QuakeForge QuakeForge\ Developer\'s\ Manual
.\" Please update the above date whenever this man page is modified.
.\"
.\" Some roff macros, for reference:
.\" .nh disable hyphenation
.\" .hy enable hyphenation
.\" .ad l left justify
.\" .ad b justify to both left and right margins (default)
.\" .nf disable filling
.\" .fi enable filling
.\" .br insert line break
.\" .sp <n> insert n+1 empty lines
.\" for manpage-specific macros, see man(7)
.SH NAME
qfcc \- The QuakeForge Code Compiler
.SH SYNOPSIS
.B qfcc
.RI [ options ]
.SH DESCRIPTION
\fBqfcc\fP compiles GameC source into a form that the QuakeForge server can
understand.
.PP
.SH OPTIONS
\fBqfcc\fP accepts the following options:
.TP
.B \-s, \-\-source <dir>
Set source directory for "progs.src".
.TP
.B \-h, \-\-help
Show summary of options.
.TP
.B \-V, \-\-version
Show version of program.
.SH "SEE ALSO"
.BR quakeforge (1)
.SH AUTHORS
The original \fBqcc\fP program was written by Id Software, Inc. The members of
the QuakeForge Project have modified it to work with extensions to the QuakeC
language. To minimize confusion, the name of the language compiled by \fBqfcc\fP
is called GameC.

View file

@ -0,0 +1,3 @@
AUTOMAKE_OPTIONS= foreign
EXTRA_DIST= config.h.in pr_comp.h qfcc.h

116
tools/qfcc/include/cmdlib.h Normal file
View file

@ -0,0 +1,116 @@
/* 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.
*/
// cmdlib.h
#ifndef __CMDLIB__
#define __CMDLIB__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <ctype.h>
#include <time.h>
#include <stdarg.h>
#ifndef __BYTEBOOL__
#define __BYTEBOOL__
typedef enum {false, true} qboolean;
typedef unsigned char byte;
#endif
// the dec offsetof macro doesn't work very well...
#define myoffsetof(type,identifier) ((size_t)&((type *)0)->identifier)
// set these before calling CheckParm
extern int myargc;
extern char **myargv;
char *strupr (char *in);
char *strlower (char *in);
int Q_strncasecmp (char *s1, char *s2, int n);
int Q_strcasecmp (char *s1, char *s2);
void Q_getwd (char *out);
int filelength (FILE *f);
int FileTime (char *path);
void Q_mkdir (char *path);
extern char qdir[1024];
extern char gamedir[1024];
void SetQdirFromPath (char *path);
char *ExpandPath (char *path);
char *ExpandPathAndArchive (char *path);
double I_FloatTime (void);
void Error (char *error, ...);
int CheckParm (char *check);
FILE *SafeOpenWrite (char *filename);
FILE *SafeOpenRead (char *filename);
void SafeRead (FILE *f, void *buffer, int count);
void SafeWrite (FILE *f, void *buffer, int count);
int LoadFile (char *filename, void **bufferptr);
void SaveFile (char *filename, void *buffer, int count);
void DefaultExtension (char *path, char *extension);
void DefaultPath (char *path, char *basepath);
void StripFilename (char *path);
void StripExtension (char *path);
void ExtractFilePath (char *path, char *dest);
void ExtractFileBase (char *path, char *dest);
void ExtractFileExtension (char *path, char *dest);
int ParseNum (char *str);
short BigShort (short l);
short LittleShort (short l);
int BigLong (int l);
int LittleLong (int l);
float BigFloat (float l);
float LittleFloat (float l);
char *COM_Parse (char *data);
extern char com_token[1024];
extern qboolean com_eof;
char *copystring(char *s);
void CRC_Init(unsigned short *crcvalue);
void CRC_ProcessByte(unsigned short *crcvalue, byte data);
unsigned short CRC_Value(unsigned short crcvalue);
void CreatePath (char *path);
void CopyFile (char *from, char *to);
extern qboolean archive;
extern char archivedir[1024];
#endif

View file

@ -0,0 +1,20 @@
/* include/config.h.in. Generated automatically from configure.in by autoheader. */
/*
Compiler/Machine-Specific Configuration
*/
#ifndef __config_h_
#define __config_h_
/* Define if you have the ANSI C header files. */
#undef STDC_HEADERS
/* Define if you have the <unistd.h> header file. */
#undef HAVE_UNISTD_H
/* Name of package */
#undef PACKAGE
/* Version number of package */
#undef VERSION
#endif // __config_h_

View file

@ -0,0 +1,179 @@
/* 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.
*/
// this file is shared by QuakeForge and qfcc
typedef int func_t;
typedef int string_t;
typedef enum {ev_void, ev_string, ev_float, ev_vector, ev_entity, ev_field, ev_function, ev_pointer} etype_t;
#define OFS_NULL 0
#define OFS_RETURN 1
#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors
#define OFS_PARM1 7
#define OFS_PARM2 10
#define OFS_PARM3 13
#define OFS_PARM4 16
#define OFS_PARM5 19
#define OFS_PARM6 22
#define OFS_PARM7 25
#define RESERVED_OFS 28
enum {
OP_DONE,
OP_MUL_F,
OP_MUL_V,
OP_MUL_FV,
OP_MUL_VF,
OP_DIV_F,
OP_ADD_F,
OP_ADD_V,
OP_SUB_F,
OP_SUB_V,
OP_EQ_F,
OP_EQ_V,
OP_EQ_S,
OP_EQ_E,
OP_EQ_FNC,
OP_NE_F,
OP_NE_V,
OP_NE_S,
OP_NE_E,
OP_NE_FNC,
OP_LE,
OP_GE,
OP_LT,
OP_GT,
OP_LOAD_F,
OP_LOAD_V,
OP_LOAD_S,
OP_LOAD_ENT,
OP_LOAD_FLD,
OP_LOAD_FNC,
OP_ADDRESS,
OP_STORE_F,
OP_STORE_V,
OP_STORE_S,
OP_STORE_ENT,
OP_STORE_FLD,
OP_STORE_FNC,
OP_STOREP_F,
OP_STOREP_V,
OP_STOREP_S,
OP_STOREP_ENT,
OP_STOREP_FLD,
OP_STOREP_FNC,
OP_RETURN,
OP_NOT_F,
OP_NOT_V,
OP_NOT_S,
OP_NOT_ENT,
OP_NOT_FNC,
OP_IF,
OP_IFNOT,
OP_CALL0,
OP_CALL1,
OP_CALL2,
OP_CALL3,
OP_CALL4,
OP_CALL5,
OP_CALL6,
OP_CALL7,
OP_CALL8,
OP_STATE,
OP_GOTO,
OP_AND,
OP_OR,
OP_BITAND,
OP_BITOR
};
typedef struct statement_s
{
unsigned short op;
short a,b,c;
} dstatement_t;
typedef struct
{
unsigned short type; // if DEF_SAVEGLOBGAL bit is set
// the variable needs to be saved in savegames
unsigned short ofs;
int s_name;
} ddef_t;
#define DEF_SAVEGLOBGAL (1<<15)
#define MAX_PARMS 8
typedef struct
{
int first_statement; // negative numbers are builtins
int parm_start;
int locals; // total ints of parms + locals
int profile; // runtime
int s_name;
int s_file; // source file defined in
int numparms;
byte parm_size[MAX_PARMS];
} dfunction_t;
#define PROG_VERSION 6
typedef struct
{
int version;
int crc; // check of header file
int ofs_statements;
int numstatements; // statement 0 is an error
int ofs_globaldefs;
int numglobaldefs;
int ofs_fielddefs;
int numfielddefs;
int ofs_functions;
int numfunctions; // function 0 is an empty
int ofs_strings;
int numstrings; // first string is a null string
int ofs_globals;
int numglobals;
int entityfields;
} dprograms_t;

455
tools/qfcc/include/qfcc.h Normal file
View file

@ -0,0 +1,455 @@
/* 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 "cmdlib.h"
#include <stdio.h>
#include <setjmp.h>
#include "pr_comp.h"
/*
TODO:
"stopped at 10 errors"
other pointer types for models and clients?
compact string heap?
allways initialize all variables to something safe
the def->type->type arrangement is really silly.
return type checking
parm count type checking
immediate overflow checking
pass the first two parms in call->b and call->c
*/
/*
comments
--------
// comments discard text until the end of line
/ * * / comments discard all enclosed text (spaced out on this line because this documentation is in a regular C comment block, and typing them in normally causes a parse error)
code structure
--------------
A definition is:
<type> <name> [ = <immediate>] {, <name> [ = <immediate>] };
types
-----
simple types: void, float, vector, string, or entity
float width, height;
string name;
entity self, other;
vector types:
vector org; // also creates org_x, org_y, and org_z float defs
A function type is specified as: simpletype ( type name {,type name} )
The names are ignored except when the function is initialized.
void() think;
entity() FindTarget;
void(vector destination, float speed, void() callback) SUB_CalcMove;
void(...) dprint; // variable argument builtin
A field type is specified as: .type
.vector origin;
.string netname;
.void() think, touch, use;
names
-----
Names are a maximum of 64 characters, must begin with A-Z,a-z, or _, and can continue with those characters or 0-9.
There are two levels of scoping: global, and function. The parameter list of a function and any vars declared inside a function with the "local" statement are only visible within that function,
immediates
----------
Float immediates must begin with 0-9 or minus sign. .5 is illegal.
A parsing ambiguity is present with negative constants. "a-5" will be parsed as "a", then "-5", causing an error. Seperate the - from the digits with a space "a - 5" to get the proper behavior.
12
1.6
0.5
-100
Vector immediates are three float immediates enclosed in single quotes.
'0 0 0'
'20.5 -10 0.00001'
String immediates are characters enclosed in double quotes. The string cannot contain explicit newlines, but the escape character \n can embed one. The \" escape can be used to include a quote in the string.
"maps/jrwiz1.bsp"
"sound/nin/pain.wav"
"ouch!\n"
Code immediates are statements enclosed in {} braces.
statement:
{ <multiple statements> }
<expression>;
local <type> <name> [ = <immediate>] {, <name> [ = <immediate>] };
return <expression>;
if ( <expression> ) <statement> [ else <statement> ];
while ( <expression> ) <statement>;
do <statement> while ( <expression> );
<function name> ( <function parms> );
expression:
combiations of names and these operators with standard C precedence:
"&&", "||", "<=", ">=","==", "!=", "!", "*", "/", "-", "+", "=", ".", "<", ">", "&", "|"
Parenthesis can be used to alter order of operation.
The & and | operations perform integral bit ops on floats
A built in function immediate is a number sign followed by an integer.
#1
#12
compilation
-----------
Source files are processed sequentially without dumping any state, so if a defs file is the first one processed, the definitions will be available to all other files.
The language is strongly typed and there are no casts.
Anything that is initialized is assumed to be constant, and will have immediates folded into it. If you change the value, your program will malfunction. All uninitialized globals will be saved to savegame files.
Functions cannot have more than eight parameters.
Error recovery during compilation is minimal. It will skip to the next global definition, so you will never see more than one error at a time in a given function. All compilation aborts after ten error messages.
Names can be defined multiple times until they are defined with an initialization, allowing functions to be prototyped before their definition.
void() MyFunction; // the prototype
void() MyFunction = // the initialization
{
dprint ("we're here\n");
};
entities and fields
-------------------
execution
---------
Code execution is initiated by C code in quake from two main places: the timed think routines for periodic control, and the touch function when two objects impact each other.
There are three global variables that are set before beginning code execution:
entity world; // the server's world object, which holds all global
// state for the server, like the deathmatch flags
// and the body ques.
entity self; // the entity the function is executing for
entity other; // the other object in an impact, not used for thinks
float time; // the current game time. Note that because the
// entities in the world are simulated sequentially,
// time is NOT strictly increasing. An impact late
// in one entity's time slice may set time higher
// than the think function of the next entity.
// The difference is limited to 0.1 seconds.
Execution is also caused by a few uncommon events, like the addition of a new client to an existing server.
There is a runnaway counter that stops a program if 100000 statements are executed, assuming it is in an infinite loop.
It is acceptable to change the system set global variables. This is usually done to pose as another entity by changing self and calling a function.
The interpretation is fairly efficient, but it is still over an order of magnitude slower than compiled C code. All time consuming operations should be made into built in functions.
A profile counter is kept for each function, and incremented for each interpreted instruction inside that function. The "profile" console command in Quake will dump out the top 10 functions, then clear all the counters. The "profile all" command will dump sorted stats for every function that has been executed.
afunc ( 4, bfunc(1,2,3));
will fail because there is a shared parameter marshaling area, which will cause the 1 from bfunc to overwrite the 4 allready placed in parm0. When a function is called, it copies the parms from the globals into it's privately scoped variables, so there is no collision when calling another function.
total = factorial(3) + factorial(4);
Will fail because the return value from functions is held in a single global area. If this really gets on your nerves, tell me and I can work around it at a slight performance and space penalty by allocating a new register for the function call and copying it out.
built in functions
------------------
void(string text) dprint;
Prints the string to the server console.
void(entity client, string text) cprint;
Prints a message to a specific client.
void(string text) bprint;
Broadcast prints a message to all clients on the current server.
entity() spawn;
Returns a totally empty entity. You can manually set everything up, or just set the origin and call one of the existing entity setup functions.
entity(entity start, .string field, string match) find;
Searches the server entity list beginning at start, looking for an entity that has entity.field = match. To start at the beginning of the list, pass world. World is returned when the end of the list is reached.
<FIXME: define all the other functions...>
gotchas
-------
The && and || operators DO NOT EARLY OUT like C!
Don't confuse single quoted vectors with double quoted strings
The function declaration syntax takes a little getting used to.
Don't forget the ; after the trailing brace of a function initialization.
Don't forget the "local" before defining local variables.
There are no ++ / -- operators, or operate/assign operators.
*/
//=============================================================================
// offsets are allways multiplied by 4 before using
typedef int gofs_t; // offset in global data block
typedef struct function_s function_t;
#define MAX_PARMS 8
typedef struct type_s
{
etype_t type;
struct def_s *def; // a def that points to this type
struct type_s *next;
// function types are more complex
struct type_s *aux_type; // return type or field type
int num_parms; // -1 = variable args
struct type_s *parm_types[MAX_PARMS]; // only [num_parms] allocated
} type_t;
typedef struct def_s
{
type_t *type;
char *name;
struct def_s *next;
struct def_s *search_next; // for finding faster
gofs_t ofs;
struct def_s *scope; // function the var was defined in, or NULL
int initialized; // 1 when a declaration included "= immediate"
} def_t;
//============================================================================
// pr_loc.h -- program local defs
#define MAX_ERRORS 10
#define MAX_NAME 64 // chars long
#define MAX_REGS 16384
//=============================================================================
typedef union eval_s
{
string_t string;
float _float;
float vector[3];
func_t function;
int _int;
union eval_s *ptr;
} eval_t;
extern int type_size[8];
extern def_t *def_for_type[8];
extern type_t type_void, type_string, type_float, type_vector, type_entity, type_field, type_function, type_pointer, type_floatfield;
extern def_t def_void, def_string, def_float, def_vector, def_entity, def_field, def_function, def_pointer;
struct function_s
{
int builtin; // if non 0, call an internal function
int code; // first statement
char *file; // source file with definition
int file_line;
struct def_s *def;
int parm_ofs[MAX_PARMS]; // allways contiguous, right?
};
//
// output generated by prog parsing
//
typedef struct
{
char *memory;
int max_memory;
int current_memory;
type_t *types;
def_t def_head; // unused head of linked list
def_t *def_tail; // add new defs after this and move it
def_t *search; // search chain through defs
int size_fields;
} pr_info_t;
extern pr_info_t pr;
typedef struct
{
char *name;
char *opname;
float priority;
qboolean right_associative;
def_t *type_a, *type_b, *type_c;
} opcode_t;
//============================================================================
extern opcode_t pr_opcodes[99]; // sized by initialization
extern qboolean pr_dumpasm;
extern def_t *pr_global_defs[MAX_REGS]; // to find def for a global variable
typedef enum {
tt_eof, // end of file reached
tt_name, // an alphanumeric name token
tt_punct, // code punctuation
tt_immediate, // string, float, vector
} token_type_t;
extern char pr_token[2048];
extern token_type_t pr_token_type;
extern type_t *pr_immediate_type;
extern eval_t pr_immediate;
void PR_PrintStatement (dstatement_t *s);
void PR_Lex (void);
// reads the next token into pr_token and classifies its type
type_t *PR_ParseType (void);
char *PR_ParseName (void);
qboolean PR_Check (char *string);
void PR_Expect (char *string);
void PR_ParseError (char *error, ...);
extern jmp_buf pr_parse_abort; // longjump with this on parse error
extern int pr_source_line;
extern char *pr_file_p;
void *PR_Malloc (int size);
#define OFS_NULL 0
#define OFS_RETURN 1
#define OFS_PARM0 4 // leave 3 ofs for each parm to hold vectors
#define OFS_PARM1 7
#define OFS_PARM2 10
#define OFS_PARM3 13
#define OFS_PARM4 16
#define RESERVED_OFS 28
extern def_t *pr_scope;
extern int pr_error_count;
void PR_NewLine (void);
def_t *PR_GetDef (type_t *type, char *name, def_t *scope, qboolean allocate);
void PR_PrintDefs (void);
void PR_SkipToSemicolon (void);
extern char pr_parm_names[MAX_PARMS][MAX_NAME];
extern qboolean pr_trace;
#define G_FLOAT(o) (pr_globals[o])
#define G_INT(o) (*(int *)&pr_globals[o])
#define G_VECTOR(o) (&pr_globals[o])
#define G_STRING(o) (strings + *(string_t *)&pr_globals[o])
#define G_FUNCTION(o) (*(func_t *)&pr_globals[o])
char *PR_ValueString (etype_t type, void *val);
void PR_ClearGrabMacros (void);
qboolean PR_CompileFile (char *string, char *filename);
extern qboolean pr_dumpasm;
extern string_t s_file; // filename for function definition
extern def_t def_ret, def_parms[MAX_PARMS];
//=============================================================================
#define MAX_STRINGS 500000
#define MAX_GLOBALS 16384
#define MAX_FIELDS 1024
#define MAX_STATEMENTS 65536
#define MAX_FUNCTIONS 8192
#define MAX_SOUNDS 1024
#define MAX_MODELS 1024
#define MAX_FILES 1024
#define MAX_DATA_PATH 64
extern char strings[MAX_STRINGS];
extern int strofs;
extern dstatement_t statements[MAX_STATEMENTS];
extern int numstatements;
extern int statement_linenums[MAX_STATEMENTS];
extern dfunction_t functions[MAX_FUNCTIONS];
extern int numfunctions;
extern float pr_globals[MAX_REGS];
extern int numpr_globals;
extern char pr_immediate_string[2048];
extern char precache_sounds[MAX_SOUNDS][MAX_DATA_PATH];
extern int precache_sounds_block[MAX_SOUNDS];
extern int numsounds;
extern char precache_models[MAX_MODELS][MAX_DATA_PATH];
extern int precache_models_block[MAX_SOUNDS];
extern int nummodels;
extern char precache_files[MAX_FILES][MAX_DATA_PATH];
extern int precache_files_block[MAX_SOUNDS];
extern int numfiles;
int CopyString (char *str);

19
tools/qfcc/qfcc.lsm.in Normal file
View file

@ -0,0 +1,19 @@
Begin3
Title: qfcc
Version: @VERSION@
Entered-date: @ISODATE@
Description: The QuakeForge Code Compiler
@configure_input@
qfcc is a compiler for the GameC (formerly QuakeC) language.
It is intended to replace the original QuakeC compiler.
Keywords: Quake, QuakeC, QuakeForge, GameC, compiler
Author: quake-devel@lists.sourceforge.net (The QuakeForge Project)
Maintained-by: quake-devel@lists.sourceforge.net (The QuakeForge Project)
Primary-site: http://www.quakeforge.net/
31k qfcc-@VERSION@.tar.gz
500 qfcc.lsm
Alternate-site:
Original-site:
Platforms:
Copying-policy: GNU copyleft
End

25
tools/qfcc/source/.indent.pro vendored Normal file
View file

@ -0,0 +1,25 @@
-bad -bap
-c41 -cd41 -ncdb -fca -nfc1 -d0
-br -ce -cli4 -pcs -nss -cs -bs
-di12 -nbc -psl -brs
-i4 -ci4 -lp -ip0 -lps
-l80 -bbo -hnl
-ts4
-T byte
-T def_s
-T def_t
-T etype_t
-T eval_s
-T eval_t
-T func_t
-T function_s
-T function_t
-T gofs_t
-T qboolean
-T statement_s
-T statement_t
-T string_t
-T type_s
-T type_t

View file

@ -0,0 +1,35 @@
## Process this file with automake to produce Makefile.in
#
# Makefile.am
#
# Automake-using build system for QuakeForge
#
# Copyright (C) 2000 Jeff Teunissen <deek@quakeforge.net>
#
# This Makefile 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:
#
# Free Software Foundation, Inc.
# 59 Temple Place - Suite 330
# Boston, MA 02111-1307, USA
#
# $Id$
#
AUTOMAKE_OPTIONS= foreign
INCLUDES= -I$(top_srcdir)/include
bin_PROGRAMS= qfcc
qfcc_SOURCES= cmdlib.c pr_comp.c pr_lex.c qfcc.c

891
tools/qfcc/source/cmdlib.c Normal file
View file

@ -0,0 +1,891 @@
/*
cmdlib.c
Command library
Copyright (C) 1996-1997 id Software, Inc.
Copyright (C) 2001 Jeff Teunissen <deek@dusknet.dhs.org>
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "cmdlib.h"
#include <sys/types.h>
#include <sys/stat.h>
#ifdef WIN32
#include <direct.h>
#endif
#ifdef NeXT
#include <libc.h>
#endif
#define PATHSEPERATOR '/'
// set these before calling CheckParm
int myargc;
char **myargv;
char com_token[1024];
qboolean com_eof;
qboolean archive;
char archivedir[1024];
/*
Error
For abnormal program terminations
*/
void
Error (char *error, ...)
{
va_list argptr;
printf ("************ ERROR ************\n");
va_start (argptr, error);
vprintf (error, argptr);
va_end (argptr);
printf ("\n");
exit (1);
}
/*
qdir will hold the path up to the quake directory, including the slash
f:\quake\
/raid/quake/
gamedir will hold qdir + the game directory (id1, id2, etc)
*/
char qdir[1024];
char gamedir[1024];
void
SetQdirFromPath (char *path)
{
char temp[1024];
char *c;
if (!(path[0] == '/' || path[0] == '\\' || path[1] == ':')) { // partial path
Q_getwd (temp);
strcat (temp, path);
path = temp;
}
// search for "quake" in path
for (c = path; *c; c++) {
if (!Q_strncasecmp (c, "quake", 5)) {
strncpy (qdir, path, c + 6 - path);
printf ("qdir: %s\n", qdir);
c += 6;
while (*c) {
if (*c == '/' || *c == '\\') {
strncpy (gamedir, path, c + 1 - path);
printf ("gamedir: %s\n", gamedir);
return;
}
c++;
}
Error ("No gamedir in %s", path);
return;
}
}
Error ("SeetQdirFromPath: no 'quake' in %s", path);
}
char *
ExpandPath (char *path)
{
static char full[1024];
if (!qdir)
Error ("ExpandPath called without qdir set");
if (path[0] == '/' || path[0] == '\\' || path[1] == ':')
return path;
sprintf (full, "%s%s", qdir, path);
return full;
}
char *
ExpandPathAndArchive (char *path)
{
char *expanded;
char archivename[1024];
expanded = ExpandPath (path);
if (archive) {
sprintf (archivename, "%s/%s", archivedir, path);
CopyFile (expanded, archivename);
}
return expanded;
}
char *
copystring (char *s)
{
char *b;
b = malloc (strlen (s) + 1);
strcpy (b, s);
return b;
}
/*
I_FloatTime
*/
double
I_FloatTime (void)
{
time_t t;
time (&t);
return t;
#if 0
// more precise, less portable
struct timeval tp;
struct timezone tzp;
static int secbase;
gettimeofday (&tp, &tzp);
if (!secbase) {
secbase = tp.tv_sec;
return tp.tv_usec / 1000000.0;
}
return (tp.tv_sec - secbase) + tp.tv_usec / 1000000.0;
#endif
}
void
Q_getwd (char *out)
{
#ifdef WIN32
_getcwd (out, 256);
strcat (out, "\\");
#else
getcwd (out, 256);
#endif
}
void
Q_mkdir (char *path)
{
#ifdef WIN32
if (_mkdir (path) != -1)
return;
#else
if (mkdir (path, 0777) != -1)
return;
#endif
if (errno != EEXIST)
Error ("mkdir %s: %s", path, strerror (errno));
}
/*
FileTime
returns -1 if not present
*/
int
FileTime (char *path)
{
struct stat buf;
if (stat (path, &buf) == -1)
return -1;
return buf.st_mtime;
}
/*
COM_Parse
Parse a token out of a string
*/
char *
COM_Parse (char *data)
{
int c;
int len = 0;
com_token[0] = 0;
if (!data)
return NULL;
// skip whitespace
skipwhite:
while ((c = *data) <= ' ') {
if (c == 0) {
com_eof = true;
return NULL; // end of file;
}
data++;
}
// skip // comments
if (c == '/' && data[1] == '/') {
while (*data && *data != '\n')
data++;
goto skipwhite;
}
// handle quoted strings specially
if (c == '\"') {
data++;
do {
c = *data++;
if (c == '\"') {
com_token[len] = 0;
return data;
}
com_token[len] = c;
len++;
} while (1);
}
// parse single characters
if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\'' || c == ':') {
com_token[len] = c;
len++;
com_token[len] = 0;
return data + 1;
}
// parse a regular word
do {
com_token[len] = c;
data++;
len++;
c = *data;
if (c == '{' || c == '}' || c == ')' || c == '(' || c == '\''
|| c == ':')
break;
} while (c > 32);
com_token[len] = 0;
return data;
}
int
Q_strncasecmp (char *s1, char *s2, int n)
{
int c1, c2;
while (1) {
c1 = *s1++;
c2 = *s2++;
if (!n--)
return 0; // strings are equal until end point
if (c1 != c2) {
if (c1 >= 'a' && c1 <= 'z')
c1 -= ('a' - 'A');
if (c2 >= 'a' && c2 <= 'z')
c2 -= ('a' - 'A');
if (c1 != c2)
return -1; // strings not equal
}
if (!c1)
return 0; // strings are equal
}
return -1;
}
int
Q_strcasecmp (char *s1, char *s2)
{
return Q_strncasecmp (s1, s2, 99999);
}
char *
strupr (char *start)
{
char *in;
in = start;
while (*in) {
*in = toupper (*in);
in++;
}
return start;
}
char *
strlower (char *start)
{
char *in;
in = start;
while (*in) {
*in = tolower (*in);
in++;
}
return start;
}
/*
=============================================================================
MISC FUNCTIONS
=============================================================================
*/
/*
CheckParm
Checks for the given parameter in the program's command line arguments
Returns the argument number (1 to argc-1) or 0 if not present
*/
int
CheckParm (char *check)
{
int i;
for (i = 1; i < myargc; i++) {
if (!Q_strcasecmp (check, myargv[i]))
return i;
}
return 0;
}
/*
filelength
*/
int
filelength (FILE *f)
{
int pos;
int end;
pos = ftell (f);
fseek (f, 0, SEEK_END);
end = ftell (f);
fseek (f, pos, SEEK_SET);
return end;
}
FILE *
SafeOpenWrite (char *filename)
{
FILE *f;
f = fopen (filename, "wb");
if (!f)
Error ("Error opening %s: %s", filename, strerror (errno));
return f;
}
FILE *
SafeOpenRead (char *filename)
{
FILE *f;
f = fopen (filename, "rb");
if (!f)
Error ("Error opening %s: %s", filename, strerror (errno));
return f;
}
void
SafeRead (FILE *f, void *buffer, int count)
{
if (fread (buffer, 1, count, f) != (size_t) count)
Error ("File read failure");
}
void
SafeWrite (FILE *f, void *buffer, int count)
{
if (fwrite (buffer, 1, count, f) != (size_t) count)
Error ("File read failure");
}
/*
LoadFile
*/
int
LoadFile (char *filename, void **bufferptr)
{
FILE *f;
int length;
void *buffer;
f = SafeOpenRead (filename);
length = filelength (f);
buffer = malloc (length + 1);
((char *) buffer)[length] = 0;
SafeRead (f, buffer, length);
fclose (f);
*bufferptr = buffer;
return length;
}
/*
SaveFile
*/
void
SaveFile (char *filename, void *buffer, int count)
{
FILE *f;
f = SafeOpenWrite (filename);
SafeWrite (f, buffer, count);
fclose (f);
}
/*
DefaultExtension
Append an extension if the path doesn't have one.
The extension passed should include . at the beginning.
*/
void
DefaultExtension (char *path, char *extension)
{
char *src;
src = path + strlen (path) - 1;
while (*src != PATHSEPERATOR && src != path) {
if (*src == '.')
return; // it has an extension
src--;
}
strcat (path, extension);
}
void
DefaultPath (char *path, char *basepath)
{
char temp[128];
if (path[0] == PATHSEPERATOR)
return; // absolute path location
strcpy (temp, path);
strcpy (path, basepath);
strcat (path, temp);
}
void
StripFilename (char *path)
{
int length;
length = strlen (path) - 1;
while (length > 0 && path[length] != PATHSEPERATOR)
length--;
path[length] = 0;
}
void
StripExtension (char *path)
{
int length;
length = strlen (path) - 1;
while (length > 0 && path[length] != '.') {
length--;
if (path[length] == PATHSEPERATOR) // no extension
return;
}
if (length)
path[length] = 0;
}
/*
====================
Extract file parts
====================
*/
void
ExtractFilePath (char *path, char *dest)
{
char *src;
src = path + strlen (path) - 1;
// back up until a / or the start
while (src != path && *(src - 1) != PATHSEPERATOR)
src--;
memcpy (dest, path, src - path);
dest[src - path] = 0;
}
void
ExtractFileBase (char *path, char *dest)
{
char *src;
src = path + strlen (path) - 1;
// back up until a / or the start
while (src != path && *(src - 1) != PATHSEPERATOR)
src--;
while (*src && *src != '.') {
*dest++ = *src++;
}
*dest = 0;
}
void
ExtractFileExtension (char *path, char *dest)
{
char *src;
src = path + strlen (path) - 1;
// back up until a . or the start
while (src != path && *(src - 1) != '.')
src--;
if (src == path) {
*dest = 0; // no extension
return;
}
strcpy (dest, src);
}
/*
ParseNum / ParseHex
*/
int
ParseHex (char *hex)
{
char *str;
int num;
num = 0;
str = hex;
while (*str) {
num <<= 4;
if (*str >= '0' && *str <= '9')
num += *str - '0';
else if (*str >= 'a' && *str <= 'f')
num += 10 + *str - 'a';
else if (*str >= 'A' && *str <= 'F')
num += 10 + *str - 'A';
else
Error ("Bad hex number: %s", hex);
str++;
}
return num;
}
int
ParseNum (char *str)
{
if (str[0] == '$')
return ParseHex (str + 1);
if (str[0] == '0' && str[1] == 'x')
return ParseHex (str + 2);
return atol (str);
}
/*
============================================================================
BYTE ORDER FUNCTIONS
============================================================================
*/
#ifdef WORDS_BIGENDIAN
short
LittleShort (short l)
{
byte b1, b2;
b1 = l & 255;
b2 = (l >> 8) & 255;
return (b1 << 8) + b2;
}
short
BigShort (short l)
{
return l;
}
int
LittleLong (int l)
{
byte b1, b2, b3, b4;
b1 = l & 255;
b2 = (l >> 8) & 255;
b3 = (l >> 16) & 255;
b4 = (l >> 24) & 255;
return ((int) b1 << 24) + ((int) b2 << 16) + ((int) b3 << 8) + b4;
}
int
BigLong (int l)
{
return l;
}
float
LittleFloat (float l)
{
union {
byte b[4];
float f;
} in, out;
in.f = l;
out.b[0] = in.b[3];
out.b[1] = in.b[2];
out.b[2] = in.b[1];
out.b[3] = in.b[0];
return out.f;
}
float
BigFloat (float l)
{
return l;
}
#else
short
BigShort (short l)
{
byte b1, b2;
b1 = l & 255;
b2 = (l >> 8) & 255;
return (b1 << 8) + b2;
}
short
LittleShort (short l)
{
return l;
}
int
BigLong (int l)
{
byte b1, b2, b3, b4;
b1 = l & 255;
b2 = (l >> 8) & 255;
b3 = (l >> 16) & 255;
b4 = (l >> 24) & 255;
return ((int) b1 << 24) + ((int) b2 << 16) + ((int) b3 << 8) + b4;
}
int
LittleLong (int l)
{
return l;
}
float
BigFloat (float l)
{
union {
byte b[4];
float f;
} in, out;
in.f = l;
out.b[0] = in.b[3];
out.b[1] = in.b[2];
out.b[2] = in.b[1];
out.b[3] = in.b[0];
return out.f;
}
float
LittleFloat (float l)
{
return l;
}
#endif
//=======================================================
// FIXME: byte swap?
// this is a 16 bit, non-reflected CRC using the polynomial 0x1021
// and the initial and final xor values shown below... in other words, the
// CCITT standard CRC used by XMODEM
#define CRC_INIT_VALUE 0xffff
#define CRC_XOR_VALUE 0x0000
static unsigned short crctable[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, 0x8108,
0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, 0x1231, 0x0210,
0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, 0x9339, 0x8318, 0xb37b,
0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, 0x2462, 0x3443, 0x0420, 0x1401,
0x64e6, 0x74c7, 0x44a4, 0x5485, 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee,
0xf5cf, 0xc5ac, 0xd58d, 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6,
0x5695, 0x46b4, 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d,
0xc7bc, 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823,
0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, 0x5af5,
0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, 0xdbfd, 0xcbdc,
0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, 0x6ca6, 0x7c87, 0x4ce4,
0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, 0xedae, 0xfd8f, 0xcdec, 0xddcd,
0xad2a, 0xbd0b, 0x8d68, 0x9d49, 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13,
0x2e32, 0x1e51, 0x0e70, 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a,
0x9f59, 0x8f78, 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e,
0xe16f, 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, 0x02b1,
0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, 0xb5ea, 0xa5cb,
0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, 0x34e2, 0x24c3, 0x14a0,
0x0481, 0x7466, 0x6447, 0x5424, 0x4405, 0xa7db, 0xb7fa, 0x8799, 0x97b8,
0xe75f, 0xf77e, 0xc71d, 0xd73c, 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657,
0x7676, 0x4615, 0x5634, 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9,
0xb98a, 0xa9ab, 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882,
0x28a3, 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a,
0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, 0xfd2e,
0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, 0x7c26, 0x6c07,
0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, 0xef1f, 0xff3e, 0xcf5d,
0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, 0x6e17, 0x7e36, 0x4e55, 0x5e74,
0x2e93, 0x3eb2, 0x0ed1, 0x1ef0
};
void
CRC_Init (unsigned short *crcvalue)
{
*crcvalue = CRC_INIT_VALUE;
}
void
CRC_ProcessByte (unsigned short *crcvalue, byte data)
{
*crcvalue = (*crcvalue << 8) ^ crctable[(*crcvalue >> 8) ^ data];
}
unsigned short
CRC_Value (unsigned short crcvalue)
{
return crcvalue ^ CRC_XOR_VALUE;
}
//=============================================================================
/*
============
CreatePath
============
*/
void
CreatePath (char *path)
{
char *ofs, c;
for (ofs = path + 1; *ofs; ofs++) {
c = *ofs;
if (c == '/' || c == '\\') { // create the directory
*ofs = 0;
Q_mkdir (path);
*ofs = c;
}
}
}
/*
CopyFile
Used to archive source files
*/
void
CopyFile (char *from, char *to)
{
void *buffer;
int length;
length = LoadFile (from, &buffer);
CreatePath (to);
SaveFile (to, buffer, length);
free (buffer);
}

939
tools/qfcc/source/pr_comp.c Normal file
View file

@ -0,0 +1,939 @@
/* 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.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "qfcc.h"
pr_info_t pr;
def_t *pr_global_defs[MAX_REGS]; // to find def for a global variable
int pr_edict_size;
//========================================
def_t *pr_scope; // the function being parsed, or NULL
qboolean pr_dumpasm;
string_t s_file; // filename for function definition
int locals_end; // for tracking local variables vs temps
jmp_buf pr_parse_abort; // longjump with this on parse error
void PR_ParseDefs (void);
//========================================
opcode_t pr_opcodes[] = {
{"<DONE>", "DONE", -1, false, &def_entity, &def_field, &def_void},
{"*", "MUL_F", 2, false, &def_float, &def_float, &def_float},
{"*", "MUL_V", 2, false, &def_vector, &def_vector, &def_float},
{"*", "MUL_FV", 2, false, &def_float, &def_vector, &def_vector},
{"*", "MUL_VF", 2, false, &def_vector, &def_float, &def_vector},
{"/", "DIV", 2, false, &def_float, &def_float, &def_float},
{"+", "ADD_F", 3, false, &def_float, &def_float, &def_float},
{"+", "ADD_V", 3, false, &def_vector, &def_vector, &def_vector},
{"-", "SUB_F", 3, false, &def_float, &def_float, &def_float},
{"-", "SUB_V", 3, false, &def_vector, &def_vector, &def_vector},
{"==", "EQ_F", 4, false, &def_float, &def_float, &def_float},
{"==", "EQ_V", 4, false, &def_vector, &def_vector, &def_float},
{"==", "EQ_S", 4, false, &def_string, &def_string, &def_float},
{"==", "EQ_E", 4, false, &def_entity, &def_entity, &def_float},
{"==", "EQ_FNC", 4, false, &def_function, &def_function, &def_float},
{"!=", "NE_F", 4, false, &def_float, &def_float, &def_float},
{"!=", "NE_V", 4, false, &def_vector, &def_vector, &def_float},
{"!=", "NE_S", 4, false, &def_string, &def_string, &def_float},
{"!=", "NE_E", 4, false, &def_entity, &def_entity, &def_float},
{"!=", "NE_FNC", 4, false, &def_function, &def_function, &def_float},
{"<=", "LE", 4, false, &def_float, &def_float, &def_float},
{">=", "GE", 4, false, &def_float, &def_float, &def_float},
{"<", "LT", 4, false, &def_float, &def_float, &def_float},
{">", "GT", 4, false, &def_float, &def_float, &def_float},
{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_float},
{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_vector},
{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_string},
{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_entity},
{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_field},
{".", "INDIRECT", 1, false, &def_entity, &def_field, &def_function},
{".", "ADDRESS", 1, false, &def_entity, &def_field, &def_pointer},
{"=", "STORE_F", 5, true, &def_float, &def_float, &def_float},
{"=", "STORE_V", 5, true, &def_vector, &def_vector, &def_vector},
{"=", "STORE_S", 5, true, &def_string, &def_string, &def_string},
{"=", "STORE_ENT", 5, true, &def_entity, &def_entity, &def_entity},
{"=", "STORE_FLD", 5, true, &def_field, &def_field, &def_field},
{"=", "STORE_FNC", 5, true, &def_function, &def_function, &def_function},
{"=", "STOREP_F", 5, true, &def_pointer, &def_float, &def_float},
{"=", "STOREP_V", 5, true, &def_pointer, &def_vector, &def_vector},
{"=", "STOREP_S", 5, true, &def_pointer, &def_string, &def_string},
{"=", "STOREP_ENT", 5, true, &def_pointer, &def_entity, &def_entity},
{"=", "STOREP_FLD", 5, true, &def_pointer, &def_field, &def_field},
{"=", "STOREP_FNC", 5, true, &def_pointer, &def_function, &def_function},
{"<RETURN>", "RETURN", -1, false, &def_void, &def_void, &def_void},
{"!", "NOT_F", -1, false, &def_float, &def_void, &def_float},
{"!", "NOT_V", -1, false, &def_vector, &def_void, &def_float},
{"!", "NOT_S", -1, false, &def_vector, &def_void, &def_float},
{"!", "NOT_ENT", -1, false, &def_entity, &def_void, &def_float},
{"!", "NOT_FNC", -1, false, &def_function, &def_void, &def_float},
{"<IF>", "IF", -1, false, &def_float, &def_float, &def_void},
{"<IFNOT>", "IFNOT", -1, false, &def_float, &def_float, &def_void},
// calls returns REG_RETURN
{"<CALL0>", "CALL0", -1, false, &def_function, &def_void, &def_void},
{"<CALL1>", "CALL1", -1, false, &def_function, &def_void, &def_void},
{"<CALL2>", "CALL2", -1, false, &def_function, &def_void, &def_void},
{"<CALL3>", "CALL3", -1, false, &def_function, &def_void, &def_void},
{"<CALL4>", "CALL4", -1, false, &def_function, &def_void, &def_void},
{"<CALL5>", "CALL5", -1, false, &def_function, &def_void, &def_void},
{"<CALL6>", "CALL6", -1, false, &def_function, &def_void, &def_void},
{"<CALL7>", "CALL7", -1, false, &def_function, &def_void, &def_void},
{"<CALL8>", "CALL8", -1, false, &def_function, &def_void, &def_void},
{"<STATE>", "STATE", -1, false, &def_float, &def_float, &def_void},
{"<GOTO>", "GOTO", -1, false, &def_float, &def_void, &def_void},
{"&&", "AND", 6, false, &def_float, &def_float, &def_float},
{"||", "OR", 6, false, &def_float, &def_float, &def_float},
{"&", "BITAND", 2, false, &def_float, &def_float, &def_float},
{"|", "BITOR", 2, false, &def_float, &def_float, &def_float},
{NULL}
};
#define TOP_PRIORITY 6
#define NOT_PRIORITY 4
def_t junkdef;
def_t *PR_Expression (int priority);
//===========================================================================
/*
PR_Statement
Emits a primitive statement, returning the var it places it's value in
*/
def_t *
PR_Statement (opcode_t * op, def_t * var_a, def_t * var_b)
{
dstatement_t *statement;
def_t *var_c;
statement = &statements[numstatements];
numstatements++;
statement_linenums[statement - statements] = pr_source_line;
statement->op = op - pr_opcodes;
statement->a = var_a ? var_a->ofs : 0;
statement->b = var_b ? var_b->ofs : 0;
if (op->type_c == &def_void || op->right_associative) {
// ifs, gotos, and assignments don't need vars allocated
var_c = NULL;
statement->c = 0;
} else { // allocate result space
var_c = malloc (sizeof (def_t));
memset (var_c, 0, sizeof (def_t));
var_c->ofs = numpr_globals;
var_c->type = op->type_c->type;
statement->c = numpr_globals;
numpr_globals += type_size[op->type_c->type->type];
}
if (op->right_associative)
return var_a;
return var_c;
}
/*
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;
}
void
PrecacheSound (def_t *e, int ch)
{
char *n;
int i;
if (!e->ofs)
return;
n = G_STRING (e->ofs);
for (i = 0; i < numsounds; i++) {
if (!strcmp (n, precache_sounds[i])) {
return;
}
}
if (numsounds == MAX_SOUNDS)
Error ("PrecacheSound: numsounds == MAX_SOUNDS");
strcpy (precache_sounds[i], n);
if (ch >= '1' && ch <= '9')
precache_sounds_block[i] = ch - '0';
else
precache_sounds_block[i] = 1;
numsounds++;
}
void
PrecacheModel (def_t *e, int ch)
{
char *n;
int i;
if (!e->ofs)
return;
n = G_STRING (e->ofs);
for (i = 0; i < nummodels; i++) {
if (!strcmp (n, precache_models[i])) {
return;
}
}
if (numsounds == MAX_SOUNDS)
Error ("PrecacheModels: numsounds == MAX_SOUNDS");
strcpy (precache_models[i], n);
if (ch >= '1' && ch <= '9')
precache_models_block[i] = ch - '0';
else
precache_models_block[i] = 1;
nummodels++;
}
void
PrecacheFile (def_t *e, int ch)
{
char *n;
int i;
if (!e->ofs)
return;
n = G_STRING (e->ofs);
for (i = 0; i < numfiles; i++) {
if (!strcmp (n, precache_files[i])) {
return;
}
}
if (numfiles == MAX_FILES)
Error ("PrecacheFile: numfiles == MAX_FILES");
strcpy (precache_files[i], n);
if (ch >= '1' && ch <= '9')
precache_files_block[i] = ch - '0';
else
precache_files_block[i] = 1;
numfiles++;
}
/*
PR_ParseFunctionCall
*/
def_t *
PR_ParseFunctionCall (def_t *func)
{
def_t *e;
int arg;
type_t *t;
t = func->type;
if (t->type != ev_function)
PR_ParseError ("not a function");
// copy the arguments to the global parameter variables
arg = 0;
if (!PR_Check (")")) {
do {
if (t->num_parms != -1 && arg >= t->num_parms)
PR_ParseError ("too many parameters");
e = PR_Expression (TOP_PRIORITY);
if (arg == 0 && func->name) {
// save information for model and sound caching
if (!strncmp (func->name, "precache_sound", 14))
PrecacheSound (e, func->name[14]);
else if (!strncmp (func->name, "precache_model", 14))
PrecacheModel (e, func->name[14]);
else if (!strncmp (func->name, "precache_file", 13))
PrecacheFile (e, func->name[13]);
}
if (t->num_parms != -1 && (e->type != t->parm_types[arg]))
PR_ParseError ("type mismatch on parm %i", arg);
// a vector copy will copy everything
def_parms[arg].type = t->parm_types[arg];
PR_Statement (&pr_opcodes[OP_STORE_V], e, &def_parms[arg]);
arg++;
} while (PR_Check (","));
if (t->num_parms != -1 && arg != t->num_parms)
PR_ParseError ("too few parameters");
PR_Expect (")");
}
if (arg > 8)
PR_ParseError ("More than eight parameters");
PR_Statement (&pr_opcodes[OP_CALL0 + arg], func, 0);
def_ret.type = t->aux_type;
return &def_ret;
}
/*
PR_ParseValue
Return the global offset for the current token
*/
def_t *
PR_ParseValue (void)
{
def_t *d;
char *name;
// if the token is an immediate, allocate a constant for it
if (pr_token_type == tt_immediate)
return PR_ParseImmediate ();
name = PR_ParseName ();
// look through the defs
if (!(d = PR_GetDef (NULL, name, pr_scope, false)))
PR_ParseError ("Unknown value \"%s\"", name);
return d;
}
/*
PR_Term
*/
def_t *
PR_Term (void)
{
def_t *e;
etype_t t;
if (PR_Check ("!")) {
e = PR_Expression (NOT_PRIORITY);
t = e->type->type;
switch (t) {
case ev_float:
return PR_Statement (&pr_opcodes[OP_NOT_F], e, 0);
case ev_string:
return PR_Statement (&pr_opcodes[OP_NOT_S], e, 0);
case ev_entity:
return PR_Statement (&pr_opcodes[OP_NOT_ENT], e, 0);
case ev_vector:
return PR_Statement (&pr_opcodes[OP_NOT_V], e, 0);
case ev_function:
return PR_Statement (&pr_opcodes[OP_NOT_FNC], e, 0);
default:
PR_ParseError ("Type mismatch for !");
return NULL;
}
}
if (PR_Check ("(")) {
e = PR_Expression (TOP_PRIORITY);
PR_Expect (")");
return e;
}
return PR_ParseValue ();
}
/*
PR_Expression
*/
def_t *
PR_Expression (int priority)
{
opcode_t *op, *oldop;
def_t *e, *e2;
etype_t type_a, type_b, type_c;
if (!priority)
return PR_Term ();
e = PR_Expression (priority - 1);
while (1) {
if (priority == 1 && PR_Check ("("))
return PR_ParseFunctionCall (e);
for (op = pr_opcodes; op->name; op++) {
if (op->priority != priority)
continue;
if (!PR_Check (op->name))
continue;
if (op->right_associative) {
// if last statement is an indirect, change it to an address of
if ((unsigned) (statements[numstatements - 1].op - OP_LOAD_F) < 6) {
statements[numstatements - 1].op = OP_ADDRESS;
def_pointer.type->aux_type = e->type;
e->type = def_pointer.type;
}
e2 = PR_Expression (priority);
} else {
e2 = PR_Expression (priority - 1);
}
// type check
type_a = e->type->type;
type_b = e2->type->type;
if (op->name[0] == '.') { // field access gets type from field
if (e2->type->aux_type)
type_c = e2->type->aux_type->type;
else
type_c = -1; // not a field
} else {
type_c = ev_void;
}
oldop = op;
while (type_a != op->type_a->type->type
|| type_b != op->type_b->type->type
|| (type_c != ev_void && type_c != op->type_c->type->type)) {
op++;
if (!op->name || strcmp (op->name, oldop->name))
PR_ParseError ("type mismatch for %s", oldop->name);
}
if (type_a == ev_pointer && type_b != e->type->aux_type->type)
PR_ParseError ("type mismatch for %s", op->name);
if (op->right_associative)
e = PR_Statement (op, e2, e);
else
e = PR_Statement (op, e, e2);
if (type_c != ev_void) // field access gets type from field
e->type = e2->type->aux_type;
break;
}
if (!op->name) // next token isn't at this priority level
break;
}
return e;
}
/*
PR_ParseStatement
*/
void
PR_ParseStatement (void)
{
def_t *e;
dstatement_t *patch1, *patch2;
if (PR_Check ("{")) {
do {
PR_ParseStatement ();
} while (!PR_Check ("}"));
return;
}
if (PR_Check ("return")) {
if (PR_Check (";")) {
PR_Statement (&pr_opcodes[OP_RETURN], 0, 0);
return;
}
e = PR_Expression (TOP_PRIORITY);
PR_Expect (";");
PR_Statement (&pr_opcodes[OP_RETURN], e, 0);
return;
}
if (PR_Check ("while")) {
PR_Expect ("(");
patch2 = &statements[numstatements];
e = PR_Expression (TOP_PRIORITY);
PR_Expect (")");
patch1 = &statements[numstatements];
PR_Statement (&pr_opcodes[OP_IFNOT], e, 0);
PR_ParseStatement ();
junkdef.ofs = patch2 - &statements[numstatements];
PR_Statement (&pr_opcodes[OP_GOTO], &junkdef, 0);
patch1->b = &statements[numstatements] - patch1;
return;
}
if (PR_Check ("do")) {
patch1 = &statements[numstatements];
PR_ParseStatement ();
PR_Expect ("while");
PR_Expect ("(");
e = PR_Expression (TOP_PRIORITY);
PR_Expect (")");
PR_Expect (";");
junkdef.ofs = patch1 - &statements[numstatements];
PR_Statement (&pr_opcodes[OP_IF], e, &junkdef);
return;
}
if (PR_Check ("local")) {
PR_ParseDefs ();
locals_end = numpr_globals;
return;
}
if (PR_Check ("if")) {
PR_Expect ("(");
e = PR_Expression (TOP_PRIORITY);
PR_Expect (")");
patch1 = &statements[numstatements];
PR_Statement (&pr_opcodes[OP_IFNOT], e, 0);
PR_ParseStatement ();
if (PR_Check ("else")) {
patch2 = &statements[numstatements];
PR_Statement (&pr_opcodes[OP_GOTO], 0, 0);
patch1->b = &statements[numstatements] - patch1;
PR_ParseStatement ();
patch2->a = &statements[numstatements] - patch2;
} else {
patch1->b = &statements[numstatements] - patch1;
}
return;
}
PR_Expression (TOP_PRIORITY);
PR_Expect (";");
}
/*
PR_ParseState
States are special functions made for convenience. They automatically
set frame, nextthink (implicitly), and think (allowing forward definitions).
void() name = [framenum, nextthink] {code}
expands to:
function void name ()
{
self.frame=framenum;
self.nextthink = time + 0.1;
self.think = nextthink
<code>
};
Weird, huh? :)
*/
void
PR_ParseState (void)
{
char *name;
def_t *s1, *def;
if (pr_token_type != tt_immediate || pr_immediate_type != &type_float)
PR_ParseError ("state frame must be a number");
s1 = PR_ParseImmediate ();
PR_Expect (",");
name = PR_ParseName ();
def = PR_GetDef (&type_function, name, 0, true);
PR_Expect ("]");
PR_Statement (&pr_opcodes[OP_STATE], s1, def);
}
/*
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;
}
/*
PR_GetDef
If type is NULL, it will match any type
If allocate is true, a new def will be allocated if it can't be found
*/
def_t *
PR_GetDef (type_t *type, char *name, def_t *scope, qboolean allocate)
{
def_t *def, **old;
char element[MAX_NAME];
// see if the name is already in use
old = &pr.search;
for (def = *old; def; old = &def->search_next, def = *old)
if (!strcmp (def->name, name)) {
if (def->scope && def->scope != scope) // in a different function
continue;
if (type && def->type != type)
PR_ParseError ("Type mismatch on redeclaration of %s", name);
// move to head of list to find fast next time
*old = def->search_next;
def->search_next = pr.search;
pr.search = def;
return def;
}
if (!allocate)
return NULL;
// allocate a new def
def = malloc (sizeof (def_t));
memset (def, 0, sizeof (*def));
def->next = NULL;
pr.def_tail->next = def;
pr.def_tail = def;
def->search_next = pr.search;
pr.search = def;
def->name = malloc (strlen (name) + 1);
strcpy (def->name, name);
def->type = type;
def->scope = scope;
def->ofs = numpr_globals;
pr_global_defs[numpr_globals] = def;
/*
make automatic defs for the vectors elements
.origin can be accessed as .origin_x, .origin_y, and .origin_z
*/
if (type->type == ev_vector) {
sprintf (element, "%s_x", name);
PR_GetDef (&type_float, element, scope, true);
sprintf (element, "%s_y", name);
PR_GetDef (&type_float, element, scope, true);
sprintf (element, "%s_z", name);
PR_GetDef (&type_float, element, scope, true);
} else {
numpr_globals += type_size[type->type];
}
if (type->type == ev_field) {
*(int *) &pr_globals[def->ofs] = pr.size_fields;
if (type->aux_type->type == ev_vector) {
sprintf (element, "%s_x", name);
PR_GetDef (&type_floatfield, element, scope, true);
sprintf (element, "%s_y", name);
PR_GetDef (&type_floatfield, element, scope, true);
sprintf (element, "%s_z", name);
PR_GetDef (&type_floatfield, element, scope, true);
} else {
pr.size_fields += type_size[type->aux_type->type];
}
}
// if (pr_dumpasm)
// PR_PrintOfs (def->ofs);
return def;
}
/*
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_CompileFile
Compile the null-terminated text, adding definitions 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);
}

673
tools/qfcc/source/pr_lex.c Normal file
View file

@ -0,0 +1,673 @@
/*
pr_lex.c
Lexical parser for GameC
Copyright (C) 1996-1997 Id Software, Inc.
Copyright (C) 2001 Jeff Teunissen <deek@dusknet.dhs.org>
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "qfcc.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 first to avoid partial matches
"&&", "||", "<=", ">=", "==", "!=", ";", ",", "!", "*", "/", "(", ")", "-",
"+", "=", "[", "]", "{", "}", "...", ".", "<", ">", "#", "&", "|", 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)
{
qboolean 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
Parse 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
Parse a number
*/
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
Parse 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
Skip whitespace and comments
*/
void
PR_LexWhitespace (void)
{
int c;
while (1) {
// skip whitespace
while ((c = *pr_file_p) <= ' ') {
if (c == '\n')
PR_NewLine ();
if (!c)
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);
}
/*
PR_SimpleGetToken
Just parses text, returning false if an eol is reached
*/
qboolean
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;
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 ();
} else if (!strcmp (pr_token, "cd") // ignore other known $commands
|| !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 ();
} else { // look for a frame name macro
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;
}
if (c == '\"') { // handle quoted strings as a unit
PR_LexString ();
return;
}
if (c == '\'') { // handle quoted vectors as a unit
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
Gets the next token. Will issue an error if the current token isn't equal
to string
*/
void
PR_Expect (char *string)
{
if (strcmp (string, pr_token))
PR_ParseError ("expected %s, found %s", string, pr_token);
PR_Lex ();
}
/*
PR_Check
If current token equals string, get next token and return true.
Otherwise, return false and do nothing else.
*/
qboolean
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
}
char pr_parm_names[MAX_PARMS][MAX_NAME];
/*
PR_ParseType
Parses a variable type, including field and functions types
*/
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);
}

816
tools/qfcc/source/qfcc.c Normal file
View file

@ -0,0 +1,816 @@
/*
qfcc.c
QuakeForge Code Compiler (main program)
Copyright (C) 1996-1997 id Software, Inc.
Copyright (C) 2001 Jeff Teunissen <deek@dusknet.dhs.org>
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:
Free Software Foundation, Inc.
59 Temple Place - Suite 330
Boston, MA 02111-1307, USA
$Id$
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "qfcc.h"
char sourcedir[1024];
char destfile[1024];
float pr_globals[MAX_REGS];
int numpr_globals;
char strings[MAX_STRINGS];
int strofs;
dstatement_t statements[MAX_STATEMENTS];
int numstatements;
int statement_linenums[MAX_STATEMENTS];
dfunction_t functions[MAX_FUNCTIONS];
int numfunctions;
ddef_t globals[MAX_GLOBALS];
int numglobaldefs;
ddef_t fields[MAX_FIELDS];
int numfielddefs;
char precache_sounds[MAX_SOUNDS][MAX_DATA_PATH];
int precache_sounds_block[MAX_SOUNDS];
int numsounds;
char precache_models[MAX_MODELS][MAX_DATA_PATH];
int precache_models_block[MAX_SOUNDS];
int nummodels;
char precache_files[MAX_FILES][MAX_DATA_PATH];
int precache_files_block[MAX_SOUNDS];
int numfiles;
/*
WriteFiles
Generates files.dat, which contains all of the data files actually used by
the game, to be processed by qfiles
*/
void
WriteFiles (void)
{
FILE *f;
int i;
char filename[1024];
sprintf (filename, "%sfiles.dat", sourcedir);
f = fopen (filename, "w");
if (!f)
Error ("Couldn't open %s", filename);
fprintf (f, "%i\n", numsounds);
for (i = 0; i < numsounds; i++)
fprintf (f, "%i %s\n", precache_sounds_block[i], precache_sounds[i]);
fprintf (f, "%i\n", nummodels);
for (i = 0; i < nummodels; i++)
fprintf (f, "%i %s\n", precache_models_block[i], precache_models[i]);
fprintf (f, "%i\n", numfiles);
for (i = 0; i < numfiles; i++)
fprintf (f, "%i %s\n", precache_files_block[i], precache_files[i]);
fclose (f);
}
/*
CopyString
Return an offset from the string heap
*/
int
CopyString (char *str)
{
int old;
old = strofs;
strcpy (strings + strofs, str);
strofs += strlen (str) + 1;
return old;
}
void
PrintStrings (void)
{
int i, l, j;
for (i = 0; i < strofs; i += l) {
l = strlen (strings + i) + 1;
printf ("%5i : ", i);
for (j = 0; j < l; j++) {
if (strings[i + j] == '\n') {
putchar ('\\');
putchar ('n');
} else {
putchar (strings[i + j]);
}
}
printf ("\n");
}
}
void
PrintFunctions (void)
{
int i, j;
dfunction_t *d;
for (i = 0; i < numfunctions; i++) {
d = &functions[i];
printf ("%s : %s : %i %i (", strings + d->s_file, strings + d->s_name,
d->first_statement, d->parm_start);
for (j = 0; j < d->numparms; j++)
printf ("%i ", d->parm_size[j]);
printf (")\n");
}
}
void
PrintFields (void)
{
int i;
ddef_t *d;
for (i = 0; i < numfielddefs; i++) {
d = &fields[i];
printf ("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
}
}
void
PrintGlobals (void)
{
int i;
ddef_t *d;
for (i = 0; i < numglobaldefs; i++) {
d = &globals[i];
printf ("%5i : (%i) %s\n", d->ofs, d->type, strings + d->s_name);
}
}
void
InitData (void)
{
int i;
numstatements = 1;
strofs = 1;
numfunctions = 1;
numglobaldefs = 1;
numfielddefs = 1;
def_ret.ofs = OFS_RETURN;
for (i = 0; i < MAX_PARMS; i++)
def_parms[i].ofs = OFS_PARM0 + 3 * i;
}
void
WriteData (int crc)
{
def_t *def;
ddef_t *dd;
dprograms_t progs;
FILE *h;
int i;
for (def = pr.def_head.next; def; def = def->next) {
if (def->type->type == ev_function) {
// df = &functions[numfunctions];
// numfunctions++;
} else if (def->type->type == ev_field) {
dd = &fields[numfielddefs];
numfielddefs++;
dd->type = def->type->aux_type->type;
dd->s_name = CopyString (def->name);
dd->ofs = G_INT (def->ofs);
}
dd = &globals[numglobaldefs];
numglobaldefs++;
dd->type = def->type->type;
if (!def->initialized
&& def->type->type != ev_function
&& def->type->type != ev_field && def->scope == NULL)
dd->type |= DEF_SAVEGLOBGAL;
dd->s_name = CopyString (def->name);
dd->ofs = def->ofs;
}
// PrintStrings ();
// PrintFunctions ();
// PrintFields ();
// PrintGlobals ();
strofs = (strofs + 3) & ~3;
printf ("%6i strofs\n", strofs);
printf ("%6i statements\n", numstatements);
printf ("%6i functions\n", numfunctions);
printf ("%6i globaldefs\n", numglobaldefs);
printf ("%6i fielddefs\n", numfielddefs);
printf ("%6i pr_globals\n", numpr_globals);
h = SafeOpenWrite (destfile);
SafeWrite (h, &progs, sizeof (progs));
progs.ofs_strings = ftell (h);
progs.numstrings = strofs;
SafeWrite (h, strings, strofs);
progs.ofs_statements = ftell (h);
progs.numstatements = numstatements;
for (i = 0; i < numstatements; i++) {
statements[i].op = LittleShort (statements[i].op);
statements[i].a = LittleShort (statements[i].a);
statements[i].b = LittleShort (statements[i].b);
statements[i].c = LittleShort (statements[i].c);
}
SafeWrite (h, statements, numstatements * sizeof (dstatement_t));
progs.ofs_functions = ftell (h);
progs.numfunctions = numfunctions;
for (i = 0; i < numfunctions; i++) {
functions[i].first_statement = LittleLong (functions[i].first_statement);
functions[i].parm_start = LittleLong (functions[i].parm_start);
functions[i].s_name = LittleLong (functions[i].s_name);
functions[i].s_file = LittleLong (functions[i].s_file);
functions[i].numparms = LittleLong (functions[i].numparms);
functions[i].locals = LittleLong (functions[i].locals);
}
SafeWrite (h, functions, numfunctions * sizeof (dfunction_t));
progs.ofs_globaldefs = ftell (h);
progs.numglobaldefs = numglobaldefs;
for (i = 0; i < numglobaldefs; i++) {
globals[i].type = LittleShort (globals[i].type);
globals[i].ofs = LittleShort (globals[i].ofs);
globals[i].s_name = LittleLong (globals[i].s_name);
}
SafeWrite (h, globals, numglobaldefs * sizeof (ddef_t));
progs.ofs_fielddefs = ftell (h);
progs.numfielddefs = numfielddefs;
for (i = 0; i < numfielddefs; i++) {
fields[i].type = LittleShort (fields[i].type);
fields[i].ofs = LittleShort (fields[i].ofs);
fields[i].s_name = LittleLong (fields[i].s_name);
}
SafeWrite (h, fields, numfielddefs * sizeof (ddef_t));
progs.ofs_globals = ftell (h);
progs.numglobals = numpr_globals;
for (i = 0; i < numpr_globals; i++)
((int *) pr_globals)[i] = LittleLong (((int *) pr_globals)[i]);
SafeWrite (h, pr_globals, numpr_globals * 4);
printf ("%6i TOTAL SIZE\n", (int) ftell (h));
progs.entityfields = pr.size_fields;
progs.version = PROG_VERSION;
progs.crc = crc;
// byte swap the header and write it out
for (i = 0; i < sizeof (progs) / 4; i++)
((int *) &progs)[i] = LittleLong (((int *) &progs)[i]);
fseek (h, 0, SEEK_SET);
SafeWrite (h, &progs, sizeof (progs));
fclose (h);
}
/*
PR_String
Returns a string suitable for printing (no newlines, max 60 chars length)
*/
char *
PR_String (char *string)
{
static char buf[80];
char *s;
s = buf;
*s++ = '"';
while (string && *string) {
if (s == buf + sizeof (buf) - 2)
break;
if (*string == '\n') {
*s++ = '\\';
*s++ = 'n';
} else if (*string == '"') {
*s++ = '\\';
*s++ = '"';
} else {
*s++ = *string;
}
string++;
if (s - buf > 60) {
*s++ = '.';
*s++ = '.';
*s++ = '.';
break;
}
}
*s++ = '"';
*s++ = 0;
return buf;
}
def_t *
PR_DefForFieldOfs (gofs_t ofs)
{
def_t *d;
for (d = pr.def_head.next; d; d = d->next) {
if (d->type->type != ev_field)
continue;
if (*((int *) &pr_globals[d->ofs]) == ofs)
return d;
}
Error ("PR_DefForFieldOfs: couldn't find %i", ofs);
return NULL;
}
/*
PR_ValueString
Return a string describing *data in a type-specific manner
*/
char *
PR_ValueString (etype_t type, void *val)
{
static char line[256];
def_t *def;
dfunction_t *f;
switch (type) {
case ev_string:
sprintf (line, "%s", PR_String (strings + *(int *) val));
break;
case ev_entity:
sprintf (line, "entity %i", *(int *) val);
break;
case ev_function:
if (!(f = functions + *(int *) val))
sprintf (line, "undefined function");
else
sprintf (line, "%s()", strings + f->s_name);
break;
case ev_field:
def = PR_DefForFieldOfs (*(int *) val);
sprintf (line, ".%s", def->name);
break;
case ev_void:
sprintf (line, "void");
break;
case ev_float:
sprintf (line, "%5.1f", *(float *) val);
break;
case ev_vector:
sprintf (line, "'%5.1f %5.1f %5.1f'", ((float *) val)[0],
((float *) val)[1], ((float *) val)[2]);
break;
case ev_pointer:
sprintf (line, "pointer");
break;
default:
sprintf (line, "bad type %i", type);
break;
}
return line;
}
/*
PR_GlobalString
Return a string with a description and the contents of a global, padded
to 20 field width
*/
char *
PR_GlobalStringNoContents (gofs_t ofs)
{
int i;
def_t *def;
void *val;
static char line[128];
val = (void *) &pr_globals[ofs];
def = pr_global_defs[ofs];
if (!def) {
// Error ("PR_GlobalString: no def for %i", ofs);
sprintf (line, "%i(???)", ofs);
} else {
sprintf (line, "%i(%s)", ofs, def->name);
}
for (i = strlen (line); i < 16; i++) {
strcat (line, " ");
}
strcat (line, " ");
return line;
}
char *
PR_GlobalString (gofs_t ofs)
{
char *s;
int i;
def_t *def;
void *val;
static char line[128];
val = (void *) &pr_globals[ofs];
if (!(def = pr_global_defs[ofs]))
return PR_GlobalStringNoContents (ofs);
if (def->initialized && def->type->type != ev_function) {
s = PR_ValueString (def->type->type, &pr_globals[ofs]);
sprintf (line, "%i(%s)", ofs, s);
} else {
sprintf (line, "%i(%s)", ofs, def->name);
}
for (i = strlen (line); i < 16; i++) {
strcat (line, " ");
}
strcat (line, " ");
return line;
}
/*
============
PR_PrintOfs
============
*/
void
PR_PrintOfs (gofs_t ofs)
{
printf ("%s\n", PR_GlobalString (ofs));
}
/*
=================
PR_PrintStatement
=================
*/
void
PR_PrintStatement (dstatement_t *s)
{
int i;
printf ("%4i : %4i : %s ", (int) (s - statements),
statement_linenums[s - statements], pr_opcodes[s->op].opname);
for (i = strlen (pr_opcodes[s->op].opname); i < 10; i++) {
printf (" ");
}
if (s->op == OP_IF || s->op == OP_IFNOT) {
printf ("%sbranch %i", PR_GlobalString (s->a), s->b);
} else if (s->op == OP_GOTO) {
printf ("branch %i", s->a);
} else if ((unsigned) (s->op - OP_STORE_F) < 6) {
printf ("%s", PR_GlobalString (s->a));
printf ("%s", PR_GlobalStringNoContents (s->b));
} else {
if (s->a)
printf ("%s", PR_GlobalString (s->a));
if (s->b)
printf ("%s", PR_GlobalString (s->b));
if (s->c)
printf ("%s", PR_GlobalStringNoContents (s->c));
}
printf ("\n");
}
/*
PR_PrintDefs
*/
void
PR_PrintDefs (void)
{
def_t *d;
for (d = pr.def_head.next; d; d = d->next)
PR_PrintOfs (d->ofs);
}
/*
PR_BeginCompilation
called before compiling a batch of files, clears the pr struct
*/
void
PR_BeginCompilation (void *memory, int memsize)
{
int i;
pr.memory = memory;
pr.max_memory = memsize;
numpr_globals = RESERVED_OFS;
pr.def_tail = &pr.def_head;
for (i = 0; i < RESERVED_OFS; i++)
pr_global_defs[i] = &def_void;
// link the function type in so state forward declarations match proper type
pr.types = &type_function;
type_function.next = NULL;
pr_error_count = 0;
}
/*
PR_FinishCompilation
called after all files are compiled to check for errors.
Returns false if errors were detected.
*/
qboolean
PR_FinishCompilation (void)
{
def_t *d;
qboolean errors = false;
// check to make sure all functions prototyped have code
for (d = pr.def_head.next; d; d = d->next) {
if (d->type->type == ev_function && !d->scope) { // function args ok
// f = G_FUNCTION(d->ofs);
// if (!f || (!f->code && !f->builtin))
if (!d->initialized) {
printf ("function %s was not defined\n", d->name);
errors = true;
}
}
}
return !errors;
}
//=============================================================================
/*
PR_WriteProgdefs
Writes the global and entity structures out.
Returns a crc of the header, to be stored in the progs file for comparison
at load time.
*/
int
PR_WriteProgdefs (char *filename)
{
def_t *d;
FILE *f;
unsigned short crc;
int c;
printf ("writing %s\n", filename);
f = fopen (filename, "w");
// print global vars until the first field is defined
fprintf (f, "\n/* file generated by qfcc, do not modify */\n\ntypedef struct {\nint\tpad[%i];\n", RESERVED_OFS);
for (d = pr.def_head.next; d; d = d->next) {
if (!strcmp (d->name, "end_sys_globals"))
break;
switch (d->type->type) {
case ev_float:
fprintf (f, "\tfloat\t%s;\n", d->name);
break;
case ev_vector:
fprintf (f, "\tvec3_t\t%s;\n", d->name);
d = d->next->next->next; // skip the elements
break;
case ev_string:
fprintf (f, "\tstring_t\t%s;\n", d->name);
break;
case ev_function:
fprintf (f, "\tfunc_t\t%s;\n", d->name);
break;
case ev_entity:
fprintf (f, "\tint\t%s;\n", d->name);
break;
default:
fprintf (f, "\tint\t%s;\n", d->name);
break;
}
}
fprintf (f, "} globalvars_t;\n\n");
// print all fields
fprintf (f, "typedef struct\n{\n");
for (d = pr.def_head.next; d; d = d->next) {
if (!strcmp (d->name, "end_sys_fields"))
break;
if (d->type->type != ev_field)
continue;
switch (d->type->aux_type->type) {
case ev_float:
fprintf (f, "\tfloat\t%s;\n", d->name);
break;
case ev_vector:
fprintf (f, "\tvec3_t\t%s;\n", d->name);
d = d->next->next->next; // skip the elements
break;
case ev_string:
fprintf (f, "\tstring_t\t%s;\n", d->name);
break;
case ev_function:
fprintf (f, "\tfunc_t\t%s;\n", d->name);
break;
case ev_entity:
fprintf (f, "\tint\t%s;\n", d->name);
break;
default:
fprintf (f, "\tint\t%s;\n", d->name);
break;
}
}
fprintf (f, "} entvars_t;\n\n");
fclose (f);
// do a crc of the file
CRC_Init (&crc);
f = fopen (filename, "r+");
while ((c = fgetc (f)) != EOF)
CRC_ProcessByte (&crc, (byte) c);
fprintf (f, "#define PROGHEADER_CRC %i\n", crc);
fclose (f);
return crc;
}
void
PrintFunction (char *name)
{
int i;
dstatement_t *ds;
dfunction_t *df;
for (i = 0; i < numfunctions; i++)
if (!strcmp (name, strings + functions[i].s_name))
break;
if (i == numfunctions)
Error ("No function names \"%s\"", name);
df = functions + i;
printf ("Statements for %s:\n", name);
ds = statements + df->first_statement;
while (1) {
PR_PrintStatement (ds);
if (!ds->op)
break;
ds++;
}
}
//============================================================================
/*
main
The nerve center of our little operation
*/
int
main (int argc, char **argv)
{
char *src;
char *src2;
char filename[1024];
int p, crc;
double start, stop;
start = I_FloatTime ();
myargc = argc;
myargv = argv;
if (CheckParm ("-h") || CheckParm ("--help")) {
printf ("%s - A compiler for the QuakeC language\n", argv[0]);
printf ("Usage: %s [options]\n", argv[0]);
printf ("\
Options: \n\
-s, --source <dir> look for progs.src in directory <dir>\n\
-h, --help display this help and exit\n\
-V, --version output version information and exit\n\
");
return;
}
if (CheckParm ("-V") || CheckParm ("--version")) {
printf ("%s version %s\n", PACKAGE, VERSION);
return;
}
if ((p = CheckParm ("--source")) && p < argc - 1) {
strcpy (sourcedir, argv[p + 1]);
} else {
if ((p = CheckParm ("-s")) && p < argc - 1) {
strcpy (sourcedir, argv[p + 1]);
} else {
strcpy (sourcedir, ".");
}
}
if (strcmp (sourcedir, ".")) {
printf ("Source directory: %s\n", sourcedir);
}
InitData ();
sprintf (filename, "%s/progs.src", sourcedir);
LoadFile (filename, (void *) &src);
if (!(src = COM_Parse (src)))
Error ("No destination filename. qfcc --help for info.\n");
strcpy (destfile, com_token);
printf ("outputfile: %s\n", destfile);
pr_dumpasm = false;
PR_BeginCompilation (malloc (0x100000), 0x100000);
// compile all the files
while ((src = COM_Parse (src))) {
sprintf (filename, "%s/%s", sourcedir, com_token);
printf ("compiling %s\n", filename);
LoadFile (filename, (void *) &src2);
if (!PR_CompileFile (src2, filename))
exit (1);
}
if (!PR_FinishCompilation ())
Error ("compilation errors");
// write progdefs.h
crc = PR_WriteProgdefs ("progdefs.h");
// write data file
WriteData (crc);
// write files.dat
WriteFiles ();
stop = I_FloatTime ();
printf ("Compilation time: %i seconds.\n", (int) (stop - start));
}