mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-01-18 14:21:36 +00:00
Merge branch 'master' into test-suite
Conflicts: Makefile ir.c
This commit is contained in:
commit
61fa54318c
14 changed files with 1801 additions and 114 deletions
39
INSTALL
Normal file
39
INSTALL
Normal file
|
@ -0,0 +1,39 @@
|
|||
Installing gmqcc
|
||||
|
||||
1. Prerequisites
|
||||
- A C-Compiler such as gcc or clang
|
||||
- GNU Make. This document will assume GNU-Make to be executed via
|
||||
`make'. On BSD systems you probably have to use `gmake' instead.
|
||||
|
||||
2. Compilation
|
||||
Run the GNU make program `make' or `gmake'.
|
||||
|
||||
make
|
||||
|
||||
If no error appears, the following binary files will have been
|
||||
created:
|
||||
- gmqcc
|
||||
- qcvm
|
||||
|
||||
3. Installation
|
||||
The `install' target will install the 2 binaries to /usr/local/bin
|
||||
by default.
|
||||
The Makefile honors the following variables:
|
||||
|
||||
- DESTDIR: The installation directory root.
|
||||
- PREFIX: The installation prefix, default: /usr/local
|
||||
- BINDIR: Directory for binary executables,
|
||||
deafult: $PREFIX/bin
|
||||
|
||||
To install to /usr/local run:
|
||||
|
||||
make install
|
||||
|
||||
To install to /usr run:
|
||||
|
||||
make PREFIX=/usr install
|
||||
|
||||
To install to a package-staging directory such as $pkgdir when
|
||||
writing an ArchLinux PKGBUILD file:
|
||||
|
||||
make DESTDIR=$pkgdir install
|
19
LICENSE
Normal file
19
LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (C) 2012 Dale Weiler, Wolfgang Bumiller
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
5
Makefile
5
Makefile
|
@ -28,8 +28,9 @@ OBJ = \
|
|||
code.o \
|
||||
ast.o \
|
||||
ir.o \
|
||||
con.o
|
||||
|
||||
con.o \
|
||||
ftepp.o
|
||||
|
||||
OBJ_T = test.o util.o con.o
|
||||
OBJ_C = main.o lexer.o parser.o
|
||||
OBJ_X = exec-standalone.o util.o con.o
|
||||
|
|
5
README
Normal file
5
README
Normal file
|
@ -0,0 +1,5 @@
|
|||
The gmqcc Quake C Compiler
|
||||
|
||||
For licensing, see the LICENSE file.
|
||||
|
||||
For installation notes, see the INSTALL file.
|
7
exec.c
7
exec.c
|
@ -334,16 +334,15 @@ static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
|
|||
def = prog_getdef(prog, glob);
|
||||
value = (qcany*)(&prog->globals[glob]);
|
||||
|
||||
len = printf("[@%u] ", glob);
|
||||
if (def) {
|
||||
const char *name = prog_getstring(prog, def->name);
|
||||
if (name[0] == '#')
|
||||
len = printf("$");
|
||||
len += printf("$");
|
||||
else
|
||||
len = printf("%s ", name);
|
||||
len += printf("%s ", name);
|
||||
vtype = def->type & DEF_TYPEMASK;
|
||||
}
|
||||
else
|
||||
len = printf("[@%u] ", glob);
|
||||
|
||||
switch (vtype) {
|
||||
case TYPE_VOID:
|
||||
|
|
12
gmqcc.h
12
gmqcc.h
|
@ -752,6 +752,18 @@ bool parser_compile_file (const char *filename);
|
|||
bool parser_compile_string(const char *name, const char *str);
|
||||
bool parser_finish (const char *output);
|
||||
void parser_cleanup ();
|
||||
/* There's really no need to strlen() preprocessed files */
|
||||
bool parser_compile_string_len(const char *name, const char *str, size_t len);
|
||||
|
||||
/*===================================================================*/
|
||||
/*====================== ftepp.c commandline ========================*/
|
||||
/*===================================================================*/
|
||||
bool ftepp_init ();
|
||||
bool ftepp_preprocess_file (const char *filename);
|
||||
bool ftepp_preprocess_string(const char *name, const char *str);
|
||||
void ftepp_finish ();
|
||||
const char *ftepp_get ();
|
||||
void ftepp_flush ();
|
||||
|
||||
/*===================================================================*/
|
||||
/*======================= main.c commandline ========================*/
|
||||
|
|
105
ir.c
105
ir.c
|
@ -259,6 +259,7 @@ ir_builder* ir_builder_new(const char *modulename)
|
|||
self->functions = NULL;
|
||||
self->globals = NULL;
|
||||
self->fields = NULL;
|
||||
self->extparams = NULL;
|
||||
self->filenames = NULL;
|
||||
self->filestrings = NULL;
|
||||
|
||||
|
@ -280,6 +281,10 @@ void ir_builder_delete(ir_builder* self)
|
|||
ir_function_delete_quick(self->functions[i]);
|
||||
}
|
||||
vec_free(self->functions);
|
||||
for (i = 0; i != vec_size(self->extparams); ++i) {
|
||||
ir_value_delete(self->extparams[i]);
|
||||
}
|
||||
vec_free(self->extparams);
|
||||
for (i = 0; i != vec_size(self->globals); ++i) {
|
||||
ir_value_delete(self->globals[i]);
|
||||
}
|
||||
|
@ -2500,10 +2505,13 @@ tailcall:
|
|||
* generation already. This would even include later
|
||||
* reuse.... probably... :)
|
||||
*/
|
||||
size_t p;
|
||||
size_t p, first;
|
||||
ir_value *retvalue;
|
||||
|
||||
for (p = 0; p < vec_size(instr->params); ++p)
|
||||
first = vec_size(instr->params);
|
||||
if (first > 8)
|
||||
first = 8;
|
||||
for (p = 0; p < first; ++p)
|
||||
{
|
||||
ir_value *param = instr->params[p];
|
||||
|
||||
|
@ -2518,6 +2526,33 @@ tailcall:
|
|||
stmt.o2.u1 = OFS_PARM0 + 3 * p;
|
||||
vec_push(code_statements, stmt);
|
||||
}
|
||||
/* No whandle extparams */
|
||||
first = vec_size(instr->params);
|
||||
for (; p < first; ++p)
|
||||
{
|
||||
ir_builder *ir = func->owner;
|
||||
ir_value *param = instr->params[p];
|
||||
ir_value *target;
|
||||
|
||||
if (p-8 >= vec_size(ir->extparams)) {
|
||||
irerror(instr->context, "Not enough extparam-globals have been created");
|
||||
return false;
|
||||
}
|
||||
|
||||
target = ir->extparams[p-8];
|
||||
|
||||
stmt.opcode = INSTR_STORE_F;
|
||||
stmt.o3.u1 = 0;
|
||||
|
||||
if (param->vtype == TYPE_FIELD)
|
||||
stmt.opcode = field_store_instr[param->fieldtype];
|
||||
else
|
||||
stmt.opcode = type_store_instr[param->vtype];
|
||||
stmt.o1.u1 = ir_value_code_addr(param);
|
||||
stmt.o2.u1 = ir_value_code_addr(target);
|
||||
vec_push(code_statements, stmt);
|
||||
}
|
||||
|
||||
stmt.opcode = INSTR_CALL0 + vec_size(instr->params);
|
||||
if (stmt.opcode > INSTR_CALL8)
|
||||
stmt.opcode = INSTR_CALL8;
|
||||
|
@ -2652,6 +2687,8 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
|
|||
fun.file = ir_builder_filestring(ir, global->context.file);
|
||||
fun.profile = 0; /* always 0 */
|
||||
fun.nargs = vec_size(irfun->params);
|
||||
if (fun.nargs > 8)
|
||||
fun.nargs = 8;
|
||||
|
||||
for (i = 0;i < 8; ++i) {
|
||||
if (i >= fun.nargs)
|
||||
|
@ -2698,6 +2735,63 @@ static bool gen_global_function(ir_builder *ir, ir_value *global)
|
|||
return true;
|
||||
}
|
||||
|
||||
static void ir_gen_extparam(ir_builder *ir)
|
||||
{
|
||||
prog_section_def def;
|
||||
ir_value *global;
|
||||
char name[128];
|
||||
|
||||
snprintf(name, sizeof(name), "EXTPARM#%i", (int)(vec_size(ir->extparams)+8));
|
||||
global = ir_value_var(name, store_global, TYPE_VECTOR);
|
||||
|
||||
def.name = code_genstring(name);
|
||||
def.type = TYPE_VECTOR;
|
||||
def.offset = vec_size(code_globals);
|
||||
|
||||
vec_push(code_defs, def);
|
||||
ir_value_code_setaddr(global, def.offset);
|
||||
vec_push(code_globals, 0);
|
||||
vec_push(code_globals, 0);
|
||||
vec_push(code_globals, 0);
|
||||
|
||||
vec_push(ir->extparams, global);
|
||||
}
|
||||
|
||||
static bool gen_function_extparam_copy(ir_function *self)
|
||||
{
|
||||
size_t i, ext, numparams;
|
||||
|
||||
ir_builder *ir = self->owner;
|
||||
ir_value *ep;
|
||||
prog_section_statement stmt;
|
||||
|
||||
numparams = vec_size(self->params);
|
||||
if (!numparams)
|
||||
return true;
|
||||
|
||||
stmt.opcode = INSTR_STORE_F;
|
||||
stmt.o3.s1 = 0;
|
||||
for (i = 8; i < numparams; ++i) {
|
||||
ext = i - 8;
|
||||
if (ext >= vec_size(ir->extparams))
|
||||
ir_gen_extparam(ir);
|
||||
|
||||
ep = ir->extparams[ext];
|
||||
|
||||
stmt.opcode = type_store_instr[self->locals[i]->vtype];
|
||||
if (self->locals[i]->vtype == TYPE_FIELD &&
|
||||
self->locals[i]->fieldtype == TYPE_VECTOR)
|
||||
{
|
||||
stmt.opcode = INSTR_STORE_V;
|
||||
}
|
||||
stmt.o1.u1 = ir_value_code_addr(ep);
|
||||
stmt.o2.u1 = ir_value_code_addr(self->locals[i]);
|
||||
vec_push(code_statements, stmt);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool gen_global_function_code(ir_builder *ir, ir_value *global)
|
||||
{
|
||||
prog_section_function *fundef;
|
||||
|
@ -2721,6 +2815,10 @@ static bool gen_global_function_code(ir_builder *ir, ir_value *global)
|
|||
fundef = &code_functions[irfun->code_function_def];
|
||||
|
||||
fundef->entry = vec_size(code_statements);
|
||||
if (!gen_function_extparam_copy(irfun)) {
|
||||
irerror(irfun->context, "Failed to generate extparam-copy code for function %s", irfun->name);
|
||||
return false;
|
||||
}
|
||||
if (!gen_function_code(irfun)) {
|
||||
irerror(irfun->context, "Failed to generate code for function %s", irfun->name);
|
||||
return false;
|
||||
|
@ -2972,7 +3070,8 @@ bool ir_builder_generate(ir_builder *self, const char *filename)
|
|||
stmt.o3.u1 = 0;
|
||||
vec_push(code_statements, stmt);
|
||||
|
||||
con_out("writing '%s'...\n", filename);
|
||||
if (!opts_pp_only)
|
||||
con_out("writing '%s'...\n", filename);
|
||||
return code_write(filename);
|
||||
}
|
||||
|
||||
|
|
2
ir.h
2
ir.h
|
@ -290,6 +290,8 @@ typedef struct ir_builder_s
|
|||
ir_value **globals;
|
||||
ir_value **fields;
|
||||
|
||||
ir_value **extparams;
|
||||
|
||||
const char **filenames;
|
||||
qcint *filestrings;
|
||||
/* we cache the #IMMEDIATE string here */
|
||||
|
|
186
lexer.c
186
lexer.c
|
@ -10,32 +10,32 @@ char* *lex_filenames;
|
|||
|
||||
void lexerror(lex_file *lex, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_list ap;
|
||||
|
||||
va_start(ap, fmt);
|
||||
if (lex)
|
||||
va_start(ap, fmt);
|
||||
if (lex)
|
||||
con_vprintmsg(LVL_ERROR, lex->name, lex->sline, "parse error", fmt, ap);
|
||||
else
|
||||
con_vprintmsg(LVL_ERROR, "", 0, "parse error", fmt, ap);
|
||||
va_end(ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
int lvl = LVL_WARNING;
|
||||
va_list ap;
|
||||
int lvl = LVL_WARNING;
|
||||
|
||||
if (!OPTS_WARN(warntype))
|
||||
return false;
|
||||
|
||||
if (opts_werror)
|
||||
lvl = LVL_ERROR;
|
||||
lvl = LVL_ERROR;
|
||||
|
||||
va_start(ap, fmt);
|
||||
va_start(ap, fmt);
|
||||
con_vprintmsg(lvl, lex->name, lex->sline, "warning", fmt, ap);
|
||||
va_end(ap);
|
||||
va_end(ap);
|
||||
|
||||
return opts_werror;
|
||||
return opts_werror;
|
||||
}
|
||||
|
||||
|
||||
|
@ -289,13 +289,13 @@ static int lex_getch(lex_file *lex)
|
|||
|
||||
if (lex->peekpos) {
|
||||
lex->peekpos--;
|
||||
if (lex->peek[lex->peekpos] == '\n')
|
||||
if (!lex->push_line && lex->peek[lex->peekpos] == '\n')
|
||||
lex->line++;
|
||||
return lex->peek[lex->peekpos];
|
||||
}
|
||||
|
||||
ch = lex_fgetc(lex);
|
||||
if (ch == '\n')
|
||||
if (!lex->push_line && ch == '\n')
|
||||
lex->line++;
|
||||
else if (ch == '?')
|
||||
return lex_try_trigraph(lex, ch);
|
||||
|
@ -307,7 +307,7 @@ static int lex_getch(lex_file *lex)
|
|||
static void lex_ungetch(lex_file *lex, int ch)
|
||||
{
|
||||
lex->peek[lex->peekpos++] = ch;
|
||||
if (ch == '\n')
|
||||
if (!lex->push_line && ch == '\n')
|
||||
lex->line--;
|
||||
}
|
||||
|
||||
|
@ -347,6 +347,115 @@ static void lex_endtoken(lex_file *lex)
|
|||
vec_shrinkby(lex->tok.value, 1);
|
||||
}
|
||||
|
||||
static bool lex_try_pragma(lex_file *lex)
|
||||
{
|
||||
int ch;
|
||||
char *pragma = NULL;
|
||||
char *command = NULL;
|
||||
char *param = NULL;
|
||||
size_t line;
|
||||
|
||||
if (lex->flags.preprocessing)
|
||||
return false;
|
||||
|
||||
line = lex->line;
|
||||
|
||||
ch = lex_getch(lex);
|
||||
if (ch != '#') {
|
||||
lex_ungetch(lex, ch);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (ch = lex_getch(lex); vec_size(pragma) < 8 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex))
|
||||
vec_push(pragma, ch);
|
||||
vec_push(pragma, 0);
|
||||
|
||||
if (ch != ' ' || strcmp(pragma, "pragma")) {
|
||||
lex_ungetch(lex, ch);
|
||||
goto unroll;
|
||||
}
|
||||
|
||||
for (ch = lex_getch(lex); vec_size(command) < 32 && ch >= 'a' && ch <= 'z'; ch = lex_getch(lex))
|
||||
vec_push(command, ch);
|
||||
vec_push(command, 0);
|
||||
|
||||
if (ch != '(') {
|
||||
lex_ungetch(lex, ch);
|
||||
goto unroll;
|
||||
}
|
||||
|
||||
for (ch = lex_getch(lex); vec_size(param) < 32 && ch != ')' && ch != '\n'; ch = lex_getch(lex))
|
||||
vec_push(param, ch);
|
||||
vec_push(param, 0);
|
||||
|
||||
if (ch != ')') {
|
||||
lex_ungetch(lex, ch);
|
||||
goto unroll;
|
||||
}
|
||||
|
||||
if (!strcmp(command, "push")) {
|
||||
if (!strcmp(param, "line")) {
|
||||
lex->push_line++;
|
||||
--line;
|
||||
}
|
||||
else
|
||||
goto unroll;
|
||||
}
|
||||
else if (!strcmp(command, "pop")) {
|
||||
if (!strcmp(param, "line")) {
|
||||
if (lex->push_line)
|
||||
lex->push_line--;
|
||||
--line;
|
||||
}
|
||||
else
|
||||
goto unroll;
|
||||
}
|
||||
else if (!strcmp(command, "file")) {
|
||||
lex->name = util_strdup(param);
|
||||
vec_push(lex_filenames, lex->name);
|
||||
}
|
||||
else if (!strcmp(command, "line")) {
|
||||
line = strtol(param, NULL, 0)-1;
|
||||
}
|
||||
else
|
||||
goto unroll;
|
||||
|
||||
lex->line = line;
|
||||
while (ch != '\n' && ch != EOF)
|
||||
ch = lex_getch(lex);
|
||||
return true;
|
||||
|
||||
unroll:
|
||||
if (command) {
|
||||
vec_pop(command);
|
||||
while (vec_size(command)) {
|
||||
lex_ungetch(lex, vec_last(command));
|
||||
vec_pop(command);
|
||||
}
|
||||
vec_free(command);
|
||||
}
|
||||
if (command) {
|
||||
vec_pop(command);
|
||||
while (vec_size(command)) {
|
||||
lex_ungetch(lex, vec_last(command));
|
||||
vec_pop(command);
|
||||
}
|
||||
vec_free(command);
|
||||
}
|
||||
if (pragma) {
|
||||
vec_pop(pragma);
|
||||
while (vec_size(pragma)) {
|
||||
lex_ungetch(lex, vec_last(pragma));
|
||||
vec_pop(pragma);
|
||||
}
|
||||
vec_free(pragma);
|
||||
}
|
||||
lex_ungetch(lex, '#');
|
||||
|
||||
lex->line = line;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Skip whitespace and comments and return the first
|
||||
* non-white character.
|
||||
* As this makes use of the above getch() ungetch() functions,
|
||||
|
@ -388,6 +497,10 @@ static int lex_skipwhite(lex_file *lex)
|
|||
{
|
||||
ch = lex_getch(lex);
|
||||
while (ch != EOF && isspace(ch)) {
|
||||
if (ch == '\n') {
|
||||
if (lex_try_pragma(lex))
|
||||
continue;
|
||||
}
|
||||
if (lex->flags.preprocessing) {
|
||||
if (ch == '\n') {
|
||||
/* end-of-line */
|
||||
|
@ -415,13 +528,17 @@ static int lex_skipwhite(lex_file *lex)
|
|||
|
||||
if (lex->flags.preprocessing) {
|
||||
haswhite = true;
|
||||
/*
|
||||
lex_tokench(lex, '/');
|
||||
lex_tokench(lex, '/');
|
||||
*/
|
||||
lex_tokench(lex, ' ');
|
||||
lex_tokench(lex, ' ');
|
||||
}
|
||||
|
||||
while (ch != EOF && ch != '\n') {
|
||||
if (lex->flags.preprocessing)
|
||||
lex_tokench(lex, ch);
|
||||
lex_tokench(lex, ' '); /* ch); */
|
||||
ch = lex_getch(lex);
|
||||
}
|
||||
if (lex->flags.preprocessing) {
|
||||
|
@ -436,8 +553,12 @@ static int lex_skipwhite(lex_file *lex)
|
|||
/* multiline comment */
|
||||
if (lex->flags.preprocessing) {
|
||||
haswhite = true;
|
||||
/*
|
||||
lex_tokench(lex, '/');
|
||||
lex_tokench(lex, '*');
|
||||
*/
|
||||
lex_tokench(lex, ' ');
|
||||
lex_tokench(lex, ' ');
|
||||
}
|
||||
|
||||
while (ch != EOF)
|
||||
|
@ -447,14 +568,18 @@ static int lex_skipwhite(lex_file *lex)
|
|||
ch = lex_getch(lex);
|
||||
if (ch == '/') {
|
||||
if (lex->flags.preprocessing) {
|
||||
/*
|
||||
lex_tokench(lex, '*');
|
||||
lex_tokench(lex, '/');
|
||||
*/
|
||||
lex_tokench(lex, ' ');
|
||||
lex_tokench(lex, ' ');
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (lex->flags.preprocessing) {
|
||||
lex_tokench(lex, ch);
|
||||
lex_tokench(lex, ' '); /* ch); */
|
||||
}
|
||||
}
|
||||
ch = ' '; /* cause TRUE in the isspace check */
|
||||
|
@ -561,7 +686,17 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
if (ch == quote)
|
||||
return TOKEN_STRINGCONST;
|
||||
|
||||
if (!lex->flags.preprocessing && ch == '\\') {
|
||||
if (lex->flags.preprocessing && ch == '\\') {
|
||||
lex_tokench(lex, ch);
|
||||
ch = lex_getch(lex);
|
||||
if (ch == EOF) {
|
||||
lexerror(lex, "unexpected end of file");
|
||||
lex_ungetch(lex, EOF); /* next token to be TOKEN_EOF */
|
||||
return (lex->tok.ttype = TOKEN_ERROR);
|
||||
}
|
||||
lex_tokench(lex, ch);
|
||||
}
|
||||
else if (ch == '\\') {
|
||||
ch = lex_getch(lex);
|
||||
if (ch == EOF) {
|
||||
lexerror(lex, "unexpected end of file");
|
||||
|
@ -571,6 +706,8 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
|
|||
|
||||
switch (ch) {
|
||||
case '\\': break;
|
||||
case '\'': break;
|
||||
case '"': break;
|
||||
case 'a': ch = '\a'; break;
|
||||
case 'b': ch = '\b'; break;
|
||||
case 'r': ch = '\r'; break;
|
||||
|
@ -678,7 +815,21 @@ int lex_do(lex_file *lex)
|
|||
return TOKEN_FATAL;
|
||||
#endif
|
||||
|
||||
ch = lex_skipwhite(lex);
|
||||
while (true) {
|
||||
ch = lex_skipwhite(lex);
|
||||
if (!lex->flags.mergelines || ch != '\\')
|
||||
break;
|
||||
ch = lex_getch(lex);
|
||||
if (ch != '\n') {
|
||||
lex_ungetch(lex, ch);
|
||||
ch = '\\';
|
||||
break;
|
||||
}
|
||||
/* we reached a linemerge */
|
||||
lex_tokench(lex, '\n');
|
||||
continue;
|
||||
}
|
||||
|
||||
lex->sline = lex->line;
|
||||
lex->tok.ctx.line = lex->sline;
|
||||
lex->tok.ctx.file = lex->name;
|
||||
|
@ -851,6 +1002,7 @@ int lex_do(lex_file *lex)
|
|||
return (lex->tok.ttype = TOKEN_OPERATOR);
|
||||
case ')':
|
||||
case ';':
|
||||
case ':':
|
||||
case '{':
|
||||
case '}':
|
||||
case ']':
|
||||
|
|
3
lexer.h
3
lexer.h
|
@ -119,11 +119,14 @@ typedef struct {
|
|||
bool noops;
|
||||
bool nodigraphs; /* used when lexing string constants */
|
||||
bool preprocessing; /* whitespace and EOLs become actual tokens */
|
||||
bool mergelines; /* backslash at the end of a line escapes the newline */
|
||||
} flags;
|
||||
|
||||
int framevalue;
|
||||
frame_macro *frames;
|
||||
char *modelname;
|
||||
|
||||
size_t push_line;
|
||||
} lex_file;
|
||||
|
||||
lex_file* lex_open (const char *file);
|
||||
|
|
177
main.c
177
main.c
|
@ -1,6 +1,7 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* Dale Weiler
|
||||
* Wolfgang Bumiller
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
* this software and associated documentation files (the "Software"), to deal in
|
||||
|
@ -57,28 +58,28 @@ static const char *app_name;
|
|||
static int usage() {
|
||||
con_out("usage: %s [options] [files...]", app_name);
|
||||
con_out("options:\n"
|
||||
" -h, --help show this help message\n"
|
||||
" -debug turns on compiler debug messages\n"
|
||||
" -memchk turns on compiler memory leak check\n");
|
||||
" -h, --help show this help message\n"
|
||||
" -debug turns on compiler debug messages\n"
|
||||
" -memchk turns on compiler memory leak check\n");
|
||||
con_out(" -o, --output=file output file, defaults to progs.dat\n"
|
||||
" -a filename add an asm file to be assembled\n"
|
||||
" -s filename add a progs.src file to be used\n");
|
||||
" -a filename add an asm file to be assembled\n"
|
||||
" -s filename add a progs.src file to be used\n");
|
||||
con_out(" -E stop after preprocessing\n");
|
||||
con_out(" -f<flag> enable a flag\n"
|
||||
" -fno-<flag> disable a flag\n"
|
||||
" -std standard select one of the following standards\n"
|
||||
" -std=qcc original QuakeC\n"
|
||||
" -std=fteqcc fteqcc QuakeC\n"
|
||||
" -std=gmqcc this compiler (default)\n");
|
||||
" -fno-<flag> disable a flag\n"
|
||||
" -std standard select one of the following standards\n"
|
||||
" -std=qcc original QuakeC\n"
|
||||
" -std=fteqcc fteqcc QuakeC\n"
|
||||
" -std=gmqcc this compiler (default)\n");
|
||||
con_out(" -W<warning> enable a warning\n"
|
||||
" -Wno-<warning> disable a warning\n"
|
||||
" -Wall enable all warnings\n"
|
||||
" -Werror treat warnings as errors\n");
|
||||
" -Wno-<warning> disable a warning\n"
|
||||
" -Wall enable all warnings\n"
|
||||
" -Werror treat warnings as errors\n");
|
||||
con_out(" -force-crc=num force a specific checksum into the header\n");
|
||||
con_out("\n");
|
||||
con_out("flags:\n"
|
||||
" -fadjust-vector-fields\n"
|
||||
" when assigning a vector field, its _y and _z fields also get assigned\n"
|
||||
" -fadjust-vector-fields\n"
|
||||
" when assigning a vector field, its _y and _z fields also get assigned\n"
|
||||
);
|
||||
return -1;
|
||||
}
|
||||
|
@ -202,6 +203,7 @@ static bool options_parse(int argc, char **argv) {
|
|||
options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
|
||||
opts_standard = COMPILER_QCC;
|
||||
} else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
|
||||
options_set(opts_flags, FTEPP, true);
|
||||
options_set(opts_flags, ADJUST_VECTOR_FIELDS, false);
|
||||
opts_standard = COMPILER_FTEQCC;
|
||||
} else if (!strcmp(argarg, "qccx")) {
|
||||
|
@ -416,6 +418,8 @@ int main(int argc, char **argv) {
|
|||
size_t itr;
|
||||
int retval = 0;
|
||||
bool opts_output_free = false;
|
||||
bool progs_src = false;
|
||||
FILE *outfile = NULL;
|
||||
|
||||
app_name = argv[0];
|
||||
con_init();
|
||||
|
@ -436,8 +440,11 @@ int main(int argc, char **argv) {
|
|||
options_set(opts_warn, WARN_EFFECTLESS_STATEMENT, true);
|
||||
options_set(opts_warn, WARN_END_SYS_FIELDS, true);
|
||||
options_set(opts_warn, WARN_ASSIGN_FUNCTION_TYPES, true);
|
||||
options_set(opts_warn, WARN_PREPROCESSOR, true);
|
||||
options_set(opts_warn, WARN_MULTIFILE_IF, true);
|
||||
|
||||
options_set(opts_flags, ADJUST_VECTOR_FIELDS, true);
|
||||
options_set(opts_flags, FTEPP, false);
|
||||
|
||||
if (!options_parse(argc, argv)) {
|
||||
return usage();
|
||||
|
@ -464,53 +471,53 @@ int main(int argc, char **argv) {
|
|||
con_out("standard = %i\n", opts_standard);
|
||||
}
|
||||
|
||||
if (!parser_init()) {
|
||||
con_out("failed to initialize parser\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
if (opts_pp_only) {
|
||||
if (opts_output_wasset) {
|
||||
outfile = util_fopen(opts_output, "wb");
|
||||
if (!outfile) {
|
||||
con_err("failed to open `%s` for writing\n", opts_output);
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
else
|
||||
outfile = stdout;
|
||||
}
|
||||
|
||||
if (!opts_pp_only) {
|
||||
if (!parser_init()) {
|
||||
con_err("failed to initialize parser\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
if (opts_pp_only || OPTS_FLAG(FTEPP)) {
|
||||
if (!ftepp_init()) {
|
||||
con_err("failed to initialize parser\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
util_debug("COM", "starting ...\n");
|
||||
|
||||
if (vec_size(items)) {
|
||||
con_out("Mode: manual\n");
|
||||
con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
|
||||
for (itr = 0; itr < vec_size(items); ++itr) {
|
||||
con_out(" item: %s (%s)\n",
|
||||
items[itr].filename,
|
||||
( (items[itr].type == TYPE_QC ? "qc" :
|
||||
(items[itr].type == TYPE_ASM ? "asm" :
|
||||
(items[itr].type == TYPE_SRC ? "progs.src" :
|
||||
("unknown"))))));
|
||||
|
||||
if (!parser_compile_file(items[itr].filename))
|
||||
{
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (!parser_finish(opts_output)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (!vec_size(items)) {
|
||||
FILE *src;
|
||||
char *line;
|
||||
size_t linelen = 0;
|
||||
|
||||
con_out("Mode: progs.src\n");
|
||||
progs_src = true;
|
||||
|
||||
src = util_fopen("progs.src", "rb");
|
||||
if (!src) {
|
||||
con_out("failed to open `progs.src` for reading\n");
|
||||
con_err("failed to open `progs.src` for reading\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
line = NULL;
|
||||
if (!progs_nextline(&line, &linelen, src) || !line[0]) {
|
||||
con_out("illformatted progs.src file: expected output filename in first line\n");
|
||||
con_err("illformatted progs.src file: expected output filename in first line\n");
|
||||
retval = 1;
|
||||
goto srcdone;
|
||||
}
|
||||
|
@ -521,30 +528,92 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
|
||||
while (progs_nextline(&line, &linelen, src)) {
|
||||
argitem item;
|
||||
if (!line[0] || (line[0] == '/' && line[1] == '/'))
|
||||
continue;
|
||||
con_out(" src: %s\n", line);
|
||||
if (!parser_compile_file(line)) {
|
||||
retval = 1;
|
||||
goto srcdone;
|
||||
}
|
||||
item.filename = util_strdup(line);
|
||||
item.type = TYPE_QC;
|
||||
vec_push(items, item);
|
||||
}
|
||||
|
||||
parser_finish(opts_output);
|
||||
|
||||
srcdone:
|
||||
fclose(src);
|
||||
mem_d(line);
|
||||
}
|
||||
|
||||
if (retval)
|
||||
goto cleanup;
|
||||
|
||||
if (vec_size(items)) {
|
||||
if (!opts_pp_only) {
|
||||
con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
|
||||
con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
|
||||
}
|
||||
for (itr = 0; itr < vec_size(items); ++itr) {
|
||||
if (!opts_pp_only) {
|
||||
con_out(" item: %s (%s)\n",
|
||||
items[itr].filename,
|
||||
( (items[itr].type == TYPE_QC ? "qc" :
|
||||
(items[itr].type == TYPE_ASM ? "asm" :
|
||||
(items[itr].type == TYPE_SRC ? "progs.src" :
|
||||
("unknown"))))));
|
||||
}
|
||||
|
||||
if (opts_pp_only) {
|
||||
if (!ftepp_preprocess_file(items[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
fprintf(outfile, "%s", ftepp_get());
|
||||
ftepp_flush();
|
||||
}
|
||||
else {
|
||||
if (OPTS_FLAG(FTEPP)) {
|
||||
const char *data;
|
||||
if (!ftepp_preprocess_file(items[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
data = ftepp_get();
|
||||
if (!parser_compile_string_len(items[itr].filename, data, vec_size(data)-1)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
ftepp_flush();
|
||||
}
|
||||
else {
|
||||
if (!parser_compile_file(items[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (progs_src) {
|
||||
mem_d(items[itr].filename);
|
||||
items[itr].filename = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ftepp_finish();
|
||||
if (!opts_pp_only) {
|
||||
if (!parser_finish(opts_output)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stuff */
|
||||
|
||||
cleanup:
|
||||
util_debug("COM", "cleaning ...\n");
|
||||
ftepp_finish();
|
||||
con_close();
|
||||
vec_free(items);
|
||||
|
||||
parser_cleanup();
|
||||
if (!opts_pp_only)
|
||||
parser_cleanup();
|
||||
if (opts_output_free)
|
||||
mem_d((char*)opts_output);
|
||||
|
||||
|
|
3
opts.def
3
opts.def
|
@ -31,6 +31,7 @@
|
|||
GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
|
||||
GMQCC_DEFINE_FLAG(OMIT_NULL_BYTES)
|
||||
GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
|
||||
GMQCC_DEFINE_FLAG(FTEPP)
|
||||
#endif
|
||||
|
||||
/* warning flags */
|
||||
|
@ -52,6 +53,8 @@
|
|||
GMQCC_DEFINE_FLAG(EFFECTLESS_STATEMENT)
|
||||
GMQCC_DEFINE_FLAG(END_SYS_FIELDS)
|
||||
GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES)
|
||||
GMQCC_DEFINE_FLAG(PREPROCESSOR)
|
||||
GMQCC_DEFINE_FLAG(MULTIFILE_IF)
|
||||
#endif
|
||||
|
||||
/* some cleanup so we don't have to */
|
||||
|
|
72
parser.c
72
parser.c
|
@ -20,6 +20,9 @@ typedef struct {
|
|||
ast_value **imm_string;
|
||||
ast_value **imm_vector;
|
||||
|
||||
/* must be deleted first, they reference immediates and values */
|
||||
ast_value **accessors;
|
||||
|
||||
ast_value *imm_float_zero;
|
||||
ast_value *imm_vector_zero;
|
||||
|
||||
|
@ -2396,6 +2399,7 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con
|
|||
{
|
||||
ast_function *func = NULL;
|
||||
ast_value *fval = NULL;
|
||||
ast_block *body = NULL;
|
||||
|
||||
fval = ast_value_new(ast_ctx(array), funcname, TYPE_FUNCTION);
|
||||
if (!fval) {
|
||||
|
@ -2410,15 +2414,25 @@ static bool parser_create_array_accessor(parser_t *parser, ast_value *array, con
|
|||
return false;
|
||||
}
|
||||
|
||||
body = ast_block_new(ast_ctx(array));
|
||||
if (!body) {
|
||||
parseerror(parser, "failed to create block for array accessor");
|
||||
ast_delete(fval);
|
||||
ast_delete(func);
|
||||
return false;
|
||||
}
|
||||
|
||||
vec_push(func->blocks, body);
|
||||
*out = fval;
|
||||
|
||||
vec_push(parser->accessors, fval);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool parser_create_array_setter(parser_t *parser, ast_value *array, const char *funcname)
|
||||
{
|
||||
ast_expression *root = NULL;
|
||||
ast_block *body = NULL;
|
||||
ast_value *index = NULL;
|
||||
ast_value *value = NULL;
|
||||
ast_function *func;
|
||||
|
@ -2434,12 +2448,6 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
|
|||
func = fval->constval.vfunc;
|
||||
fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
|
||||
|
||||
body = ast_block_new(ast_ctx(array));
|
||||
if (!body) {
|
||||
parseerror(parser, "failed to create block for array accessor");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
|
||||
value = ast_value_copy((ast_value*)array->expression.next);
|
||||
|
||||
|
@ -2457,12 +2465,10 @@ static bool parser_create_array_setter(parser_t *parser, ast_value *array, const
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
vec_push(body->exprs, root);
|
||||
vec_push(func->blocks, body);
|
||||
vec_push(func->blocks[0]->exprs, root);
|
||||
array->setter = fval;
|
||||
return true;
|
||||
cleanup:
|
||||
if (body) ast_delete(body);
|
||||
if (index) ast_delete(index);
|
||||
if (value) ast_delete(value);
|
||||
if (root) ast_delete(root);
|
||||
|
@ -2474,7 +2480,6 @@ cleanup:
|
|||
static bool parser_create_array_field_setter(parser_t *parser, ast_value *array, const char *funcname)
|
||||
{
|
||||
ast_expression *root = NULL;
|
||||
ast_block *body = NULL;
|
||||
ast_value *entity = NULL;
|
||||
ast_value *index = NULL;
|
||||
ast_value *value = NULL;
|
||||
|
@ -2491,12 +2496,6 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array,
|
|||
func = fval->constval.vfunc;
|
||||
fval->expression.next = (ast_expression*)ast_value_new(ast_ctx(array), "<void>", TYPE_VOID);
|
||||
|
||||
body = ast_block_new(ast_ctx(array));
|
||||
if (!body) {
|
||||
parseerror(parser, "failed to create block for array accessor");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
entity = ast_value_new(ast_ctx(array), "entity", TYPE_ENTITY);
|
||||
index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
|
||||
value = ast_value_copy((ast_value*)array->expression.next);
|
||||
|
@ -2515,12 +2514,10 @@ static bool parser_create_array_field_setter(parser_t *parser, ast_value *array,
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
vec_push(body->exprs, root);
|
||||
vec_push(func->blocks, body);
|
||||
vec_push(func->blocks[0]->exprs, root);
|
||||
array->setter = fval;
|
||||
return true;
|
||||
cleanup:
|
||||
if (body) ast_delete(body);
|
||||
if (entity) ast_delete(entity);
|
||||
if (index) ast_delete(index);
|
||||
if (value) ast_delete(value);
|
||||
|
@ -2533,7 +2530,6 @@ cleanup:
|
|||
static bool parser_create_array_getter(parser_t *parser, ast_value *array, const ast_expression *elemtype, const char *funcname)
|
||||
{
|
||||
ast_expression *root = NULL;
|
||||
ast_block *body = NULL;
|
||||
ast_value *index = NULL;
|
||||
ast_value *fval;
|
||||
ast_function *func;
|
||||
|
@ -2551,12 +2547,6 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
|
|||
func = fval->constval.vfunc;
|
||||
fval->expression.next = ast_type_copy(ast_ctx(array), elemtype);
|
||||
|
||||
body = ast_block_new(ast_ctx(array));
|
||||
if (!body) {
|
||||
parseerror(parser, "failed to create block for array accessor");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
index = ast_value_new(ast_ctx(array), "index", TYPE_FLOAT);
|
||||
|
||||
if (!index) {
|
||||
|
@ -2571,12 +2561,10 @@ static bool parser_create_array_getter(parser_t *parser, ast_value *array, const
|
|||
goto cleanup;
|
||||
}
|
||||
|
||||
vec_push(body->exprs, root);
|
||||
vec_push(func->blocks, body);
|
||||
vec_push(func->blocks[0]->exprs, root);
|
||||
array->getter = fval;
|
||||
return true;
|
||||
cleanup:
|
||||
if (body) ast_delete(body);
|
||||
if (index) ast_delete(index);
|
||||
if (root) ast_delete(root);
|
||||
ast_delete(func);
|
||||
|
@ -2649,8 +2637,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
|
|||
}
|
||||
|
||||
/* sanity check */
|
||||
if (vec_size(params) > 8)
|
||||
parseerror(parser, "more than 8 parameters are currently not supported");
|
||||
if (vec_size(params) > 8 && opts_standard == COMPILER_QCC)
|
||||
(void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard");
|
||||
|
||||
/* parse-out */
|
||||
if (!parser_next(parser)) {
|
||||
|
@ -3431,7 +3419,17 @@ bool parser_compile_file(const char *filename)
|
|||
{
|
||||
parser->lex = lex_open(filename);
|
||||
if (!parser->lex) {
|
||||
con_out("failed to open file \"%s\"\n", filename);
|
||||
con_err("failed to open file \"%s\"\n", filename);
|
||||
return false;
|
||||
}
|
||||
return parser_compile();
|
||||
}
|
||||
|
||||
bool parser_compile_string_len(const char *name, const char *str, size_t len)
|
||||
{
|
||||
parser->lex = lex_open_string(str, len, name);
|
||||
if (!parser->lex) {
|
||||
con_err("failed to create lexer for string \"%s\"\n", name);
|
||||
return false;
|
||||
}
|
||||
return parser_compile();
|
||||
|
@ -3441,7 +3439,7 @@ bool parser_compile_string(const char *name, const char *str)
|
|||
{
|
||||
parser->lex = lex_open_string(str, strlen(str), name);
|
||||
if (!parser->lex) {
|
||||
con_out("failed to create lexer for string \"%s\"\n", name);
|
||||
con_err("failed to create lexer for string \"%s\"\n", name);
|
||||
return false;
|
||||
}
|
||||
return parser_compile();
|
||||
|
@ -3450,6 +3448,11 @@ bool parser_compile_string(const char *name, const char *str)
|
|||
void parser_cleanup()
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < vec_size(parser->accessors); ++i) {
|
||||
ast_delete(parser->accessors[i]->constval.vfunc);
|
||||
parser->accessors[i]->constval.vfunc = NULL;
|
||||
ast_delete(parser->accessors[i]);
|
||||
}
|
||||
for (i = 0; i < vec_size(parser->functions); ++i) {
|
||||
ast_delete(parser->functions[i]);
|
||||
}
|
||||
|
@ -3470,6 +3473,7 @@ void parser_cleanup()
|
|||
ast_delete(parser->globals[i].var);
|
||||
mem_d(parser->globals[i].name);
|
||||
}
|
||||
vec_free(parser->accessors);
|
||||
vec_free(parser->functions);
|
||||
vec_free(parser->imm_vector);
|
||||
vec_free(parser->imm_string);
|
||||
|
|
Loading…
Reference in a new issue