gmqpak
qcvm
testsuite
-pak
Authors:
- Dale Weiler - Charismatic Visionary / Programmer
- Wolfgang Bumiller - Main Programmer
+ Dale `graphitemaster` Weiler - Charismatic Visionary / Programmer
+ Wolfgang `Blub\w` Bumiller - Main Programmer
Thanks to:
Forest `LordHavoc` Hale - Technical support and assistance
Release v0.3.0
- * to fill
+ * Language:
+ - Return assignments, the ability to assign to the return keyword
+ as if it were a local variable.
+ * Compilation:
+ - Optimized memory usage (now uses on average %3 less memory for
+ compilation).
+ - Fixed dotranslate (translatable strings)
+ * QCVM:
+ - Escape strings for -printdefs
+ * Commandline:
+ - Added statistic dumps, gives information about the number of used
+ hashtables, vectors, and number of unique sizes of vectors and
+ hashtables. The amount of memory used for vectors. As well as the
+ number of strdups used in total for compilation.
+ * Testsuite:
+ - Fixed a floating point exception rasied by modulo operation in
+ -memchk.
+ * Build:
+ - Added gentoo ebuilds.
+ - Added win32 Makefile for building win32 packages.
+ - Added slackware pkg build files
2012-04-27 v0.2.9
* Preprocessor:
- Added defs.qh (auto included) for qcvm definitions
* Syntax Highlighting:
- Added various syntax highlighting description files for
- various text editors / integrated development envirorments,
- including support for: geany, kate, kwrite, kdevelop, QtCreator,
- gtksourceview, gedit, sany, nano, jedit
+ various text editors / integrated development envirorments,
+ including support for: geany, kate, kwrite, kdevelop, QtCreator,
+ gtksourceview, gedit, sany, nano, jedit
* Build:
- Build scripts for building debian, archlinux and archbsd
- packages for x86, and x86_64.
+ packages for x86, and x86_64.
- Makefile targets for gource visualization, and render of
gource visualization.
-Wno-conversion \
-Wno-missing-prototypes \
-Wno-float-equal \
- -Wno-unknown-warning-option
+ -Wno-unknown-warning-option \
+ -Wstrict-prototypes
else
#Tiny C Compiler doesn't know what -pedantic-errors is
# and instead of ignoring .. just errors.
ifneq ($(CC), tcc)
- CFLAGS += -pedantic-errors
+ CFLAGS += -Wstrict-prototypes -pedantic-errors
else
CFLAGS += -Wno-pointer-sign -fno-common
endif
CFLAGS += -DNOTRACK
endif
-OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o
-OBJ_P = util.o fs.o conout.o opts.o pak.o
-OBJ_T = test.o util.o conout.o fs.o
-OBJ_C = main.o lexer.o parser.o fs.o
-OBJ_X = exec-standalone.o util.o conout.o fs.o
+OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o stat.o
+OBJ_P = util.o fs.o conout.o opts.o pak.o stat.o
+OBJ_T = test.o util.o conout.o fs.o stat.o
+OBJ_C = main.o lexer.o parser.o fs.o stat.o
+OBJ_X = exec-standalone.o util.o conout.o fs.o stat.o
+
+#we have duplicate object files when dealing with creating a simple list
+#for dependinces. To combat this we use some clever recrusive-make to
+#filter the list and remove duplicates which we use for make depend
+RMDUP = $(if $1,$(firstword $1) $(call RMDUP,$(filter-out $(firstword $1),$1)))
+DEPS := $(call RMDUP, $(OBJ_D) $(OBJ_P) $(OBJ_T) $(OBJ_C) $(OBJ_X))
ifneq ("$(CYGWIN)", "")
#nullify the common variables that
QCVM = qcvm.exe
GMQCC = gmqcc.exe
TESTSUITE = testsuite.exe
- PAK = pak.exe
+ PAK = gmqpak.exe
else
QCVM = qcvm
GMQCC = gmqcc
TESTSUITE = testsuite
- PAK = pak
+ PAK = gmqpak
endif
endif
-onlytrans \
-predboolint \
-boolops \
- -exportlocal \
-incondefs \
-macroredef \
-retvalint \
-temptrans \
-usereleased \
-warnposix \
- -shiftimplementation \
+charindex \
-kepttrans \
-unqualifiedtrans \
-mayaliasunique \
-realcompare \
-observertrans \
- -shiftnegative \
- -freshtrans \
-abstract \
-statictrans \
-castfcnptr
depend:
@makedepend -Y -w 65536 2> /dev/null \
- $(subst .o,.c,$(OBJ_D))
- @makedepend -a -Y -w 65536 2> /dev/null \
- $(subst .o,.c,$(OBJ_T))
- @makedepend -a -Y -w 65536 2> /dev/null \
- $(subst .o,.c,$(OBJ_C))
- @makedepend -a -Y -w 65536 2> /dev/null \
- $(subst .o,.c,$(OBJ_X))
- @makedepend -a -Y -w 65536 2> /dev/null \
- $(subst .o,.c,$(OBJ_P))
+ $(subst .o,.c,$(DEPS))
#install rules
-install: install-gmqcc install-qcvm install-doc
+install: install-gmqcc install-qcvm install-gmqpak install-doc
install-gmqcc: $(GMQCC)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(GMQCC) $(DESTDIR)$(BINDIR)/$(GMQCC)
install-qcvm: $(QCVM)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(QCVM) $(DESTDIR)$(BINDIR)/$(QCVM)
+install-gmqpak: $(PAK)
+ install -d -m755 $(DESTDIR)$(BINDIR)
+ install -m755 $(PAK) $(DESTDIR)$(BINDIR)/$(PAK)
install-doc:
install -d -m755 $(DESTDIR)$(MANDIR)/man1
install -m644 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/
install -m644 doc/qcvm.1 $(DESTDIR)$(MANDIR)/man1/
+ install -m644 doc/gmqpak.1 $(DESTDIR)$(MANDIR)/man1/
uninstall:
- rm $(DESTDIR)$(BINDIR)/gmqcc
- rm $(DESTDIR)$(BINDIR)/qcvm
- rm $(DESTDIR)$(MANDIR)/man1/doc/gmqcc.1
- rm $(DESTDIR)$(MANDIR)/man1/doc/qcvm.1
+ rm -f $(DESTDIR)$(BINDIR)/gmqcc
+ rm -f $(DESTDIR)$(BINDIR)/qcvm
+ rm -f $(DESTDIR)$(BINDIR)/gmqpak
+ rm -f $(DESTDIR)$(MANDIR)/man1/doc/gmqcc.1
+ rm -f $(DESTDIR)$(MANDIR)/man1/doc/qcvm.1
+ rm -f $(DESTDIR)$(MANDIR)/man1/doc/gmqpak.1
# DO NOT DELETE
fs.o: gmqcc.h opts.def
utf8.o: gmqcc.h opts.def
correct.o: gmqcc.h opts.def
-
+stat.o: gmqcc.h opts.def
+pak.o: gmqcc.h opts.def
test.o: gmqcc.h opts.def
-util.o: gmqcc.h opts.def
-conout.o: gmqcc.h opts.def
-fs.o: gmqcc.h opts.def
-
main.o: gmqcc.h opts.def lexer.h
lexer.o: gmqcc.h opts.def lexer.h
parser.o: gmqcc.h opts.def lexer.h ast.h ir.h intrin.h
-fs.o: gmqcc.h opts.def
-
-util.o: gmqcc.h opts.def
-conout.o: gmqcc.h opts.def
-fs.o: gmqcc.h opts.def
-
-util.o: gmqcc.h opts.def
-fs.o: gmqcc.h opts.def
-conout.o: gmqcc.h opts.def
-opts.o: gmqcc.h opts.def
-pak.o: gmqcc.h opts.def
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include <stdio.h>
#include <stdlib.h>
#include <string.h>
return NULL; \
} \
ast_node_init((ast_node*)self, ctx, TYPE_##T); \
- ( (ast_node*)self )->node.destroy = (ast_node_delete*)destroyfn
+ ( (ast_node*)self )->destroy = (ast_node_delete*)destroyfn
+/*
+ * forward declarations, these need not be in ast.h for obvious
+ * static reasons.
+ */
+static bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**);
+static void ast_array_index_delete(ast_array_index*);
+static bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_value**);
+static void ast_store_delete(ast_store*);
+static bool ast_store_codegen(ast_store*, ast_function*, bool lvalue, ir_value**);
+static void ast_ifthen_delete(ast_ifthen*);
+static bool ast_ifthen_codegen(ast_ifthen*, ast_function*, bool lvalue, ir_value**);
+static void ast_ternary_delete(ast_ternary*);
+static bool ast_ternary_codegen(ast_ternary*, ast_function*, bool lvalue, ir_value**);
+static void ast_loop_delete(ast_loop*);
+static bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
+static void ast_breakcont_delete(ast_breakcont*);
+static bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
+static void ast_switch_delete(ast_switch*);
+static bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**);
+static void ast_label_delete(ast_label*);
+static void ast_label_register_goto(ast_label*, ast_goto*);
+static bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**);
+static bool ast_goto_codegen(ast_goto*, ast_function*, bool lvalue, ir_value**);
+static void ast_goto_delete(ast_goto*);
+static void ast_call_delete(ast_call*);
+static bool ast_call_codegen(ast_call*, ast_function*, bool lvalue, ir_value**);
+static bool ast_block_codegen(ast_block*, ast_function*, bool lvalue, ir_value**);
+static void ast_unary_delete(ast_unary*);
+static bool ast_unary_codegen(ast_unary*, ast_function*, bool lvalue, ir_value**);
+static void ast_entfield_delete(ast_entfield*);
+static bool ast_entfield_codegen(ast_entfield*, ast_function*, bool lvalue, ir_value**);
+static void ast_return_delete(ast_return*);
+static bool ast_return_codegen(ast_return*, ast_function*, bool lvalue, ir_value**);
+static void ast_binstore_delete(ast_binstore*);
+static bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**);
+static void ast_binary_delete(ast_binary*);
+static bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
/* It must not be possible to get here. */
static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
/* Initialize main ast node aprts */
static void ast_node_init(ast_node *self, lex_ctx ctx, int nodetype)
{
- self->node.context = ctx;
- self->node.destroy = &_ast_node_destroy;
- self->node.keep = false;
- self->node.nodetype = nodetype;
- self->node.side_effects = false;
+ self->context = ctx;
+ self->destroy = &_ast_node_destroy;
+ self->keep = false;
+ self->nodetype = nodetype;
+ self->side_effects = false;
}
/* weight and side effects */
static void ast_expression_init(ast_expression *self,
ast_expression_codegen *codegen)
{
- self->expression.codegen = codegen;
- self->expression.vtype = TYPE_VOID;
- self->expression.next = NULL;
- self->expression.outl = NULL;
- self->expression.outr = NULL;
- self->expression.params = NULL;
- self->expression.count = 0;
- self->expression.flags = 0;
- self->expression.varparam = NULL;
+ self->codegen = codegen;
+ self->vtype = TYPE_VOID;
+ self->next = NULL;
+ self->outl = NULL;
+ self->outr = NULL;
+ self->params = NULL;
+ self->count = 0;
+ self->flags = 0;
+ self->varparam = NULL;
}
static void ast_expression_delete(ast_expression *self)
{
size_t i;
- if (self->expression.next)
- ast_delete(self->expression.next);
- for (i = 0; i < vec_size(self->expression.params); ++i) {
- ast_delete(self->expression.params[i]);
+ if (self->next)
+ ast_delete(self->next);
+ for (i = 0; i < vec_size(self->params); ++i) {
+ ast_delete(self->params[i]);
}
- vec_free(self->expression.params);
- if (self->expression.varparam)
- ast_delete(self->expression.varparam);
+ vec_free(self->params);
+ if (self->varparam)
+ ast_delete(self->varparam);
}
static void ast_expression_delete_full(ast_expression *self)
ast_value* ast_value_copy(const ast_value *self)
{
size_t i;
- const ast_expression_common *fromex;
- ast_expression_common *selfex;
+ const ast_expression *fromex;
+ ast_expression *selfex;
ast_value *cp = ast_value_new(self->expression.node.context, self->name, self->expression.vtype);
if (self->expression.next) {
cp->expression.next = ast_type_copy(self->expression.node.context, self->expression.next);
void ast_type_adopt_impl(ast_expression *self, const ast_expression *other)
{
size_t i;
- const ast_expression_common *fromex;
- ast_expression_common *selfex;
- self->expression.vtype = other->expression.vtype;
- if (other->expression.next) {
- self->expression.next = (ast_expression*)ast_type_copy(ast_ctx(self), other->expression.next);
- }
- fromex = &other->expression;
- selfex = &self->expression;
+ const ast_expression *fromex;
+ ast_expression *selfex;
+ self->vtype = other->vtype;
+ if (other->next) {
+ self->next = (ast_expression*)ast_type_copy(ast_ctx(self), other->next);
+ }
+ fromex = other;
+ selfex = self;
selfex->count = fromex->count;
selfex->flags = fromex->flags;
for (i = 0; i < vec_size(fromex->params); ++i) {
{
ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
ast_expression_init(self, NULL);
- self->expression.codegen = NULL;
- self->expression.next = NULL;
- self->expression.vtype = vtype;
+ self->codegen = NULL;
+ self->next = NULL;
+ self->vtype = vtype;
return self;
}
ast_expression* ast_type_copy(lex_ctx ctx, const ast_expression *ex)
{
size_t i;
- const ast_expression_common *fromex;
- ast_expression_common *selfex;
+ const ast_expression *fromex;
+ ast_expression *selfex;
if (!ex)
return NULL;
ast_instantiate(ast_expression, ctx, ast_expression_delete_full);
ast_expression_init(self, NULL);
- fromex = &ex->expression;
- selfex = &self->expression;
+ fromex = ex;
+ selfex = self;
/* This may never be codegen()d */
selfex->codegen = NULL;
bool ast_compare_type(ast_expression *a, ast_expression *b)
{
- if (a->expression.vtype == TYPE_NIL ||
- b->expression.vtype == TYPE_NIL)
+ if (a->vtype == TYPE_NIL ||
+ b->vtype == TYPE_NIL)
return true;
- if (a->expression.vtype != b->expression.vtype)
+ if (a->vtype != b->vtype)
return false;
- if (!a->expression.next != !b->expression.next)
+ if (!a->next != !b->next)
return false;
- if (vec_size(a->expression.params) != vec_size(b->expression.params))
+ if (vec_size(a->params) != vec_size(b->params))
return false;
- if ((a->expression.flags & AST_FLAG_TYPE_MASK) !=
- (b->expression.flags & AST_FLAG_TYPE_MASK) )
+ if ((a->flags & AST_FLAG_TYPE_MASK) !=
+ (b->flags & AST_FLAG_TYPE_MASK) )
{
return false;
}
- if (vec_size(a->expression.params)) {
+ if (vec_size(a->params)) {
size_t i;
- for (i = 0; i < vec_size(a->expression.params); ++i) {
- if (!ast_compare_type((ast_expression*)a->expression.params[i],
- (ast_expression*)b->expression.params[i]))
+ for (i = 0; i < vec_size(a->params); ++i) {
+ if (!ast_compare_type((ast_expression*)a->params[i],
+ (ast_expression*)b->params[i]))
return false;
}
}
- if (a->expression.next)
- return ast_compare_type(a->expression.next, b->expression.next);
+ if (a->next)
+ return ast_compare_type(a->next, b->next);
return true;
}
if (pos + 1 >= bufsize)
goto full;
- switch (e->expression.vtype) {
+ switch (e->vtype) {
case TYPE_VARIANT:
util_strncpy(buf + pos, "(variant)", 9);
return pos + 9;
case TYPE_FIELD:
buf[pos++] = '.';
- return ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
+ return ast_type_to_string_impl(e->next, buf, bufsize, pos);
case TYPE_POINTER:
if (pos + 3 >= bufsize)
goto full;
buf[pos++] = '*';
buf[pos++] = '(';
- pos = ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
+ pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
if (pos + 1 >= bufsize)
goto full;
buf[pos++] = ')';
return pos;
case TYPE_FUNCTION:
- pos = ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
+ pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
if (pos + 2 >= bufsize)
goto full;
- if (!vec_size(e->expression.params)) {
+ if (!vec_size(e->params)) {
buf[pos++] = '(';
buf[pos++] = ')';
return pos;
}
buf[pos++] = '(';
- pos = ast_type_to_string_impl((ast_expression*)(e->expression.params[0]), buf, bufsize, pos);
- for (i = 1; i < vec_size(e->expression.params); ++i) {
+ pos = ast_type_to_string_impl((ast_expression*)(e->params[0]), buf, bufsize, pos);
+ for (i = 1; i < vec_size(e->params); ++i) {
if (pos + 2 >= bufsize)
goto full;
buf[pos++] = ',';
buf[pos++] = ' ';
- pos = ast_type_to_string_impl((ast_expression*)(e->expression.params[i]), buf, bufsize, pos);
+ pos = ast_type_to_string_impl((ast_expression*)(e->params[i]), buf, bufsize, pos);
}
if (pos + 1 >= bufsize)
goto full;
return pos;
case TYPE_ARRAY:
- pos = ast_type_to_string_impl(e->expression.next, buf, bufsize, pos);
+ pos = ast_type_to_string_impl(e->next, buf, bufsize, pos);
if (pos + 1 >= bufsize)
goto full;
buf[pos++] = '[';
- pos += util_snprintf(buf + pos, bufsize - pos - 1, "%i", (int)e->expression.count);
+ pos += util_snprintf(buf + pos, bufsize - pos - 1, "%i", (int)e->count);
if (pos + 1 >= bufsize)
goto full;
buf[pos++] = ']';
return pos;
default:
- typestr = type_name[e->expression.vtype];
+ typestr = type_name[e->vtype];
typelen = strlen(typestr);
if (pos + typelen >= bufsize)
goto full;
buf[pos] = 0;
}
+static bool ast_value_codegen(ast_value *self, ast_function *func, bool lvalue, ir_value **out);
ast_value* ast_value_new(lex_ctx ctx, const char *name, int t)
{
ast_instantiate(ast_value, ctx, ast_value_delete);
self->cvq = CV_NONE;
self->hasvalue = false;
self->isimm = false;
- self->uses = 0;
+ self->uses = 0;
memset(&self->constval, 0, sizeof(self->constval));
+ self->initlist = NULL;
self->ir_v = NULL;
self->ir_values = NULL;
if (self->desc)
mem_d(self->desc);
+ if (self->initlist) {
+ if (self->expression.next->vtype == TYPE_STRING) {
+ /* strings are allocated, free them */
+ size_t i, len = vec_size(self->initlist);
+ /* in theory, len should be expression.count
+ * but let's not take any chances */
+ for (i = 0; i < len; ++i) {
+ if (self->initlist[i].vstring)
+ mem_d(self->initlist[i].vstring);
+ }
+ }
+ vec_free(self->initlist);
+ }
+
ast_expression_delete((ast_expression*)self);
mem_d(self);
}
else if (op == INSTR_MUL_V)
self->expression.vtype = TYPE_FLOAT;
else
- self->expression.vtype = left->expression.vtype;
+ self->expression.vtype = left->vtype;
/* references all */
self->refs = AST_REF_ALL;
ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field)
{
- if (field->expression.vtype != TYPE_FIELD) {
+ if (field->vtype != TYPE_FIELD) {
compile_error(ctx, "ast_entfield_new with expression not of type field");
return NULL;
}
- return ast_entfield_new_force(ctx, entity, field, field->expression.next);
+ return ast_entfield_new_force(ctx, entity, field, field->next);
}
ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype)
return NULL;
}
- if (owner->expression.vtype != TYPE_VECTOR &&
- owner->expression.vtype != TYPE_FIELD) {
- compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->expression.vtype]);
+ if (owner->vtype != TYPE_VECTOR &&
+ owner->vtype != TYPE_FIELD) {
+ compile_error(ctx, "member-access on an invalid owner of type %s", type_name[owner->vtype]);
mem_d(self);
return NULL;
}
ast_expression_init((ast_expression*)self, (ast_expression_codegen*)&ast_member_codegen);
self->expression.node.keep = true; /* keep */
- if (owner->expression.vtype == TYPE_VECTOR) {
+ if (owner->vtype == TYPE_VECTOR) {
self->expression.vtype = TYPE_FLOAT;
self->expression.next = NULL;
} else {
ast_expression *outtype;
ast_instantiate(ast_array_index, ctx, ast_array_index_delete);
- outtype = array->expression.next;
+ outtype = array->next;
if (!outtype) {
mem_d(self);
/* Error: field has no type... */
ast_propagate_effects(self, index);
ast_type_adopt(self, outtype);
- if (array->expression.vtype == TYPE_FIELD && outtype->expression.vtype == TYPE_ARRAY) {
+ if (array->vtype == TYPE_FIELD && outtype->vtype == TYPE_ARRAY) {
if (self->expression.vtype != TYPE_ARRAY) {
compile_error(ast_ctx(self), "array_index node on type");
ast_array_index_delete(self);
ast_propagate_effects(self, ontrue);
ast_propagate_effects(self, onfalse);
- if (ontrue->expression.vtype == TYPE_NIL)
+ if (ontrue->vtype == TYPE_NIL)
exprtype = onfalse;
ast_type_adopt(self, exprtype);
mem_d(self);
}
-void ast_label_register_goto(ast_label *self, ast_goto *g)
+static void ast_label_register_goto(ast_label *self, ast_goto *g)
{
vec_push(self->gotos, g);
}
ast_expression *funcexpr)
{
ast_instantiate(ast_call, ctx, ast_call_delete);
- if (!funcexpr->expression.next) {
+ if (!funcexpr->next) {
compile_error(ctx, "not a function");
mem_d(self);
return NULL;
self->func = funcexpr;
self->va_count = NULL;
- ast_type_adopt(self, funcexpr->expression.next);
+ ast_type_adopt(self, funcexpr->next);
return self;
}
bool retval = true;
const ast_expression *func = self->func;
size_t count = vec_size(self->params);
- if (count > vec_size(func->expression.params))
- count = vec_size(func->expression.params);
+ if (count > vec_size(func->params))
+ count = vec_size(func->params);
for (i = 0; i < count; ++i) {
- if (!ast_compare_type(self->params[i], (ast_expression*)(func->expression.params[i])))
+ if (!ast_compare_type(self->params[i], (ast_expression*)(func->params[i])))
{
ast_type_to_string(self->params[i], tgot, sizeof(tgot));
- ast_type_to_string((ast_expression*)func->expression.params[i], texp, sizeof(texp));
+ ast_type_to_string((ast_expression*)func->params[i], texp, sizeof(texp));
compile_error(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s",
(unsigned int)(i+1), texp, tgot);
/* we don't immediately return */
}
}
count = vec_size(self->params);
- if (count > vec_size(func->expression.params) && func->expression.varparam) {
+ if (count > vec_size(func->params) && func->varparam) {
for (; i < count; ++i) {
- if (!ast_compare_type(self->params[i], func->expression.varparam))
+ if (!ast_compare_type(self->params[i], func->varparam))
{
ast_type_to_string(self->params[i], tgot, sizeof(tgot));
- ast_type_to_string(func->expression.varparam, texp, sizeof(texp));
+ ast_type_to_string(func->varparam, texp, sizeof(texp));
compile_error(ast_ctx(self), "invalid type for parameter %u in function call: expected %s, got %s",
(unsigned int)(i+1), texp, tgot);
/* we don't immediately return */
void ast_block_collect(ast_block *self, ast_expression *expr)
{
vec_push(self->collect, expr);
- expr->expression.node.keep = true;
+ expr->node.keep = true;
}
void ast_block_delete(ast_block *self)
vtype->hasvalue = true;
vtype->constval.vfunc = self;
- self->varargs = NULL;
- self->argc = NULL;
- self->fixedparams = NULL;
+ self->varargs = NULL;
+ self->argc = NULL;
+ self->fixedparams = NULL;
+ self->return_value = NULL;
return self;
}
ast_delete(self->argc);
if (self->fixedparams)
ast_unref(self->fixedparams);
+ if (self->return_value)
+ ast_unref(self->return_value);
mem_d(self);
}
-const char* ast_function_label(ast_function *self, const char *prefix)
+static const char* ast_function_label(ast_function *self, const char *prefix)
{
size_t id;
size_t len;
* But I can't imagine a pituation where the output is truly unnecessary.
*/
-void _ast_codegen_output_type(ast_expression_common *self, ir_value *out)
+static void _ast_codegen_output_type(ast_expression *self, ir_value *out)
{
if (out->vtype == TYPE_FIELD)
- out->fieldtype = self->next->expression.vtype;
+ out->fieldtype = self->next->vtype;
if (out->vtype == TYPE_FUNCTION)
- out->outtype = self->next->expression.vtype;
+ out->outtype = self->next->vtype;
}
#define codegen_output_type(a,o) (_ast_codegen_output_type(&((a)->expression),(o)))
if (self->hasvalue && self->expression.vtype == TYPE_FUNCTION)
{
- ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->expression.vtype);
+ ir_function *func = ir_builder_create_function(ir, self->name, self->expression.next->vtype);
if (!func)
return false;
func->context = ast_ctx(self);
goto error;
}
- if (fieldtype->expression.vtype == TYPE_ARRAY) {
+ if (fieldtype->vtype == TYPE_ARRAY) {
size_t ai;
char *name;
size_t namelen;
- ast_expression_common *elemtype;
- int vtype;
- ast_value *array = (ast_value*)fieldtype;
+ ast_expression *elemtype;
+ int vtype;
+ ast_value *array = (ast_value*)fieldtype;
if (!ast_istype(fieldtype, ast_value)) {
compile_error(ast_ctx(self), "internal error: ast_value required");
if (!array->expression.count || array->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE))
compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)array->expression.count);
- elemtype = &array->expression.next->expression;
+ elemtype = array->expression.next;
vtype = elemtype->vtype;
v = ir_builder_create_field(ir, self->name, vtype);
}
else
{
- v = ir_builder_create_field(ir, self->name, self->expression.next->expression.vtype);
+ v = ir_builder_create_field(ir, self->name, self->expression.next->vtype);
if (!v)
return false;
v->context = ast_ctx(self);
char *name;
size_t namelen;
- ast_expression_common *elemtype = &self->expression.next->expression;
+ ast_expression *elemtype = self->expression.next;
int vtype = elemtype->vtype;
/* same as with field arrays */
return false;
}
-bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
+static bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
{
ir_value *v = NULL;
char *name;
size_t namelen;
- ast_expression_common *elemtype = &self->expression.next->expression;
+ ast_expression *elemtype = self->expression.next;
int vtype = elemtype->vtype;
func->flags |= IR_FLAG_HAS_ARRAYS;
{
ir_function *irf;
ir_value *dummy;
- ast_expression_common *ec;
+ ast_expression *ec;
ast_expression_codegen *cgen;
size_t i;
for (i = 0; i < vec_size(ec->params); ++i)
{
if (ec->params[i]->expression.vtype == TYPE_FIELD)
- vec_push(irf->params, ec->params[i]->expression.next->expression.vtype);
+ vec_push(irf->params, ec->params[i]->expression.next->vtype);
else
vec_push(irf->params, ec->params[i]->expression.vtype);
if (!self->builtin) {
return true;
}
+ /* have a local return value variable? */
+ if (self->return_value) {
+ if (!ast_local_codegen(self->return_value, self->ir_func, false))
+ return false;
+ }
+
if (!vec_size(self->blocks)) {
compile_error(ast_ctx(self), "function `%s` has no body", self->name);
return false;
if (!self->curblock->final)
{
if (!self->vtype->expression.next ||
- self->vtype->expression.next->expression.vtype == TYPE_VOID)
+ self->vtype->expression.next->vtype == TYPE_VOID)
{
return ir_block_create_return(self->curblock, ast_ctx(self), NULL);
}
else if (vec_size(self->curblock->entries) || self->curblock == irf->first)
{
- /* error("missing return"); */
- if (compile_warning(ast_ctx(self), WARN_MISSING_RETURN_VALUES,
+ if (self->return_value) {
+ cgen = self->return_value->expression.codegen;
+ if (!(*cgen)((ast_expression*)(self->return_value), self, false, &dummy))
+ return false;
+ return ir_block_create_return(self->curblock, ast_ctx(self), dummy);
+ }
+ else if (compile_warning(ast_ctx(self), WARN_MISSING_RETURN_VALUES,
"control reaches end of non-void function (`%s`) via %s",
self->name, self->curblock->label))
{
return false;
continue;
}
- gen = self->exprs[i]->expression.codegen;
+ gen = self->exprs[i]->codegen;
if (!(*gen)(self->exprs[i], func, false, out))
return false;
}
if (!(*cgen)((ast_expression*)(arr->setter), func, true, &funval))
return false;
- cgen = self->source->expression.codegen;
+ cgen = self->source->codegen;
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
return false;
{
/* regular code */
- cgen = self->dest->expression.codegen;
+ cgen = self->dest->codegen;
/* lvalue! */
if (!(*cgen)((ast_expression*)(self->dest), func, true, &left))
return false;
self->expression.outl = left;
- cgen = self->source->expression.codegen;
+ cgen = self->source->codegen;
/* rvalue! */
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
return false;
merge = ir_function_create_block(ast_ctx(self), func->ir_func, ast_function_label(func, "sce_merge"));
/* generate the left expression */
- cgen = self->left->expression.codegen;
+ cgen = self->left->codegen;
if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
return false;
/* remember the block */
/* enter the right-expression's block */
func->curblock = other;
/* generate */
- cgen = self->right->expression.codegen;
+ cgen = self->right->codegen;
if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
return false;
/* remember block */
return true;
}
- cgen = self->left->expression.codegen;
+ cgen = self->left->codegen;
if (!(*cgen)((ast_expression*)(self->left), func, false, &left))
return false;
- cgen = self->right->expression.codegen;
+ cgen = self->right->codegen;
if (!(*cgen)((ast_expression*)(self->right), func, false, &right))
return false;
if (!(*cgen)((ast_expression*)(idx), func, false, &iridx))
return false;
}
- cgen = self->dest->expression.codegen;
+ cgen = self->dest->codegen;
if (!(*cgen)((ast_expression*)(self->dest), func, false, &leftr))
return false;
/* source as rvalue only */
- cgen = self->source->expression.codegen;
+ cgen = self->source->codegen;
if (!(*cgen)((ast_expression*)(self->source), func, false, &right))
return false;
self->expression.outr = bin;
} else {
/* now store them */
- cgen = self->dest->expression.codegen;
+ cgen = self->dest->codegen;
/* lvalue of destination */
if (!(*cgen)((ast_expression*)(self->dest), func, true, &leftl))
return false;
return true;
}
- cgen = self->operand->expression.codegen;
+ cgen = self->operand->codegen;
/* lvalue! */
if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
return false;
self->expression.outr = (ir_value*)1;
if (self->operand) {
- cgen = self->operand->expression.codegen;
+ cgen = self->operand->codegen;
/* lvalue! */
if (!(*cgen)((ast_expression*)(self->operand), func, false, &operand))
return false;
return true;
}
- cgen = self->entity->expression.codegen;
+ cgen = self->entity->codegen;
if (!(*cgen)((ast_expression*)(self->entity), func, false, &ent))
return false;
- cgen = self->field->expression.codegen;
+ cgen = self->field->codegen;
if (!(*cgen)((ast_expression*)(self->field), func, false, &field))
return false;
return true;
}
- cgen = self->owner->expression.codegen;
+ cgen = self->owner->codegen;
if (!(*cgen)((ast_expression*)(self->owner), func, false, &vec))
return false;
if (vec->vtype != TYPE_VECTOR &&
- !(vec->vtype == TYPE_FIELD && self->owner->expression.next->expression.vtype == TYPE_VECTOR))
+ !(vec->vtype == TYPE_FIELD && self->owner->next->vtype == TYPE_VECTOR))
{
return false;
}
return false;
}
- cgen = self->index->expression.codegen;
+ cgen = self->index->codegen;
if (!(*cgen)((ast_expression*)(self->index), func, false, &iridx))
return false;
if (idx->expression.vtype == TYPE_FLOAT) {
unsigned int arridx = idx->constval.vfloat;
- if (arridx >= self->array->expression.count)
+ if (arridx >= self->array->count)
{
compile_error(ast_ctx(self), "array index out of bounds: %i", arridx);
return false;
}
else if (idx->expression.vtype == TYPE_INTEGER) {
unsigned int arridx = idx->constval.vint;
- if (arridx >= self->array->expression.count)
+ if (arridx >= self->array->count)
{
compile_error(ast_ctx(self), "array index out of bounds: %i", arridx);
return false;
self->expression.outr = (ir_value*)1;
/* generate the condition */
- cgen = self->cond->expression.codegen;
+ cgen = self->cond->codegen;
if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
return false;
/* update the block which will get the jump - because short-logic or ternaries may have changed this */
func->curblock = ontrue;
/* generate */
- cgen = self->on_true->expression.codegen;
+ cgen = self->on_true->codegen;
if (!(*cgen)((ast_expression*)(self->on_true), func, false, &dummy))
return false;
func->curblock = onfalse;
/* generate */
- cgen = self->on_false->expression.codegen;
+ cgen = self->on_false->codegen;
if (!(*cgen)((ast_expression*)(self->on_false), func, false, &dummy))
return false;
/* generate the condition */
func->curblock = cond;
- cgen = self->cond->expression.codegen;
+ cgen = self->cond->codegen;
if (!(*cgen)((ast_expression*)(self->cond), func, false, &condval))
return false;
cond_out = func->curblock;
func->curblock = ontrue;
/* generate */
- cgen = self->on_true->expression.codegen;
+ cgen = self->on_true->codegen;
if (!(*cgen)((ast_expression*)(self->on_true), func, false, &trueval))
return false;
func->curblock = onfalse;
/* generate */
- cgen = self->on_false->expression.codegen;
+ cgen = self->on_false->codegen;
if (!(*cgen)((ast_expression*)(self->on_false), func, false, &falseval))
return false;
*/
if (self->initexpr)
{
- cgen = self->initexpr->expression.codegen;
+ cgen = self->initexpr->codegen;
if (!(*cgen)((ast_expression*)(self->initexpr), func, false, &dummy))
return false;
}
func->curblock = bprecond;
/* generate */
- cgen = self->precond->expression.codegen;
+ cgen = self->precond->codegen;
if (!(*cgen)((ast_expression*)(self->precond), func, false, &precond))
return false;
/* generate */
if (self->body) {
- cgen = self->body->expression.codegen;
+ cgen = self->body->codegen;
if (!(*cgen)((ast_expression*)(self->body), func, false, &dummy))
return false;
}
func->curblock = bpostcond;
/* generate */
- cgen = self->postcond->expression.codegen;
+ cgen = self->postcond->codegen;
if (!(*cgen)((ast_expression*)(self->postcond), func, false, &postcond))
return false;
func->curblock = bincrement;
/* generate */
- cgen = self->increment->expression.codegen;
+ cgen = self->increment->codegen;
if (!(*cgen)((ast_expression*)(self->increment), func, false, &dummy))
return false;
(void)lvalue;
(void)out;
- cgen = self->operand->expression.codegen;
+ cgen = self->operand->codegen;
if (!(*cgen)((ast_expression*)(self->operand), func, false, &irop))
return false;
if (swcase->value) {
/* A regular case */
/* generate the condition operand */
- cgen = swcase->value->expression.codegen;
+ cgen = swcase->value->codegen;
if (!(*cgen)((ast_expression*)(swcase->value), func, false, &val))
return false;
/* generate the condition */
/* enter the case */
func->curblock = bcase;
- cgen = swcase->code->expression.codegen;
+ cgen = swcase->code->codegen;
if (!(*cgen)((ast_expression*)swcase->code, func, false, &dummy))
return false;
}
/* Now generate the default code */
- cgen = def_case->code->expression.codegen;
+ cgen = def_case->code->codegen;
if (!(*cgen)((ast_expression*)def_case->code, func, false, &dummy))
return false;
return true;
}
- cgen = self->func->expression.codegen;
+ cgen = self->func->codegen;
if (!(*cgen)((ast_expression*)(self->func), func, false, &funval))
return false;
if (!funval)
ir_value *param;
ast_expression *expr = self->params[i];
- cgen = expr->expression.codegen;
+ cgen = expr->codegen;
if (!(*cgen)(expr, func, false, ¶m))
goto error;
if (!param)
if (self->va_count) {
ir_value *va_count;
ir_builder *builder = func->curblock->owner->owner;
- cgen = self->va_count->expression.codegen;
+ cgen = self->va_count->codegen;
if (!(*cgen)((ast_expression*)(self->va_count), func, false, &va_count))
return false;
if (!ir_block_create_store_op(func->curblock, ast_ctx(self), INSTR_STORE_F,
callinstr = ir_block_create_call(func->curblock, ast_ctx(self),
ast_function_label(func, "call"),
- funval, !!(self->func->expression.flags & AST_FLAG_NORETURN));
+ funval, !!(self->func->flags & AST_FLAG_NORETURN));
if (!callinstr)
goto error;
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
+ * Dale Weiler
*
* 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
* "main" ast node types for now.
*/
-typedef union ast_node_u ast_node;
-typedef union ast_expression_u ast_expression;
+typedef struct ast_node_common ast_node;
+typedef struct ast_expression_common ast_expression;
typedef struct ast_value_s ast_value;
typedef struct ast_function_s ast_function;
TYPE_ast_goto /* 20 */
};
-#define ast_istype(x, t) ( ((ast_node_common*)x)->nodetype == (TYPE_##t) )
-#define ast_ctx(node) (((ast_node_common*)(node))->context)
-#define ast_side_effects(node) (((ast_node_common*)(node))->side_effects)
+#define ast_istype(x, t) ( ((ast_node*)x)->nodetype == (TYPE_##t) )
+#define ast_ctx(node) (((ast_node*)(node))->context)
+#define ast_side_effects(node) (((ast_node*)(node))->side_effects)
/* Node interface with common components
*/
typedef void ast_node_delete(ast_node*);
-typedef struct
+struct ast_node_common
{
lex_ctx context;
/* I don't feel comfortable using keywords like 'delete' as names... */
*/
bool keep;
bool side_effects;
-} ast_node_common;
-
-#define ast_delete(x) (*( ((ast_node*)(x))->node.destroy ))((ast_node*)(x))
-#define ast_unref(x) do \
-{ \
- if (! (((ast_node*)(x))->node.keep) ) { \
- ast_delete(x); \
- } \
+};
+
+#define ast_delete(x) (*( ((ast_node*)(x))->destroy ))((ast_node*)(x))
+#define ast_unref(x) do \
+{ \
+ if (! (((ast_node*)(x))->keep) ) { \
+ ast_delete(x); \
+ } \
} while(0)
/* Expression interface
* type `expression`, so the ast_ident's codegen would search for
* variables through the environment (or functions, constants...).
*/
-typedef struct
+struct ast_expression_common
{
- ast_node_common node;
+ ast_node node;
ast_expression_codegen *codegen;
int vtype;
ast_expression *next;
*/
ir_value *outl;
ir_value *outr;
-} ast_expression_common;
+};
#define AST_FLAG_VARIADIC (1<<0)
#define AST_FLAG_NORETURN (1<<1)
#define AST_FLAG_INLINE (1<<2)
* typedef float foo;
* is like creating a 'float foo', foo serving as the type's name.
*/
+typedef union {
+ double vfloat;
+ int vint;
+ vector vvec;
+ const char *vstring;
+ int ventity;
+ ast_function *vfunc;
+ ast_value *vfield;
+} basic_value_t;
struct ast_value_s
{
- ast_expression_common expression;
+ ast_expression expression;
const char *name;
const char *desc;
bool isfield; /* this declares a field */
bool isimm; /* an immediate, not just const */
bool hasvalue;
- union {
- double vfloat;
- int vint;
- vector vvec;
- const char *vstring;
- int ventity;
- ast_function *vfunc;
- ast_value *vfield;
- } constval;
+ basic_value_t constval;
+ /* for TYPE_ARRAY we have an optional vector
+ * of constants when an initializer list
+ * was provided.
+ */
+ basic_value_t *initlist;
/* usecount for the parser */
size_t uses;
bool ast_value_set_name(ast_value*, const char *name);
+/*
bool ast_value_codegen(ast_value*, ast_function*, bool lvalue, ir_value**);
bool ast_local_codegen(ast_value *self, ir_function *func, bool isparam);
+*/
+
bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield);
void ast_value_params_add(ast_value*, ast_value*);
*/
struct ast_binary_s
{
- ast_expression_common expression;
+ ast_expression expression;
int op;
ast_expression *left;
int op,
ast_expression *left,
ast_expression *right);
-void ast_binary_delete(ast_binary*);
-
-bool ast_binary_codegen(ast_binary*, ast_function*, bool lvalue, ir_value**);
/* Binstore
*
*/
struct ast_binstore_s
{
- ast_expression_common expression;
+ ast_expression expression;
int opstore;
int opbin;
int op,
ast_expression *left,
ast_expression *right);
-void ast_binstore_delete(ast_binstore*);
-
-bool ast_binstore_codegen(ast_binstore*, ast_function*, bool lvalue, ir_value**);
/* Unary
*
*/
struct ast_unary_s
{
- ast_expression_common expression;
+ ast_expression expression;
int op;
ast_expression *operand;
ast_unary* ast_unary_new(lex_ctx ctx,
int op,
ast_expression *expr);
-void ast_unary_delete(ast_unary*);
-
-bool ast_unary_codegen(ast_unary*, ast_function*, bool lvalue, ir_value**);
/* Return
*
*/
struct ast_return_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_expression *operand;
};
ast_return* ast_return_new(lex_ctx ctx,
ast_expression *expr);
-void ast_return_delete(ast_return*);
-
-bool ast_return_codegen(ast_return*, ast_function*, bool lvalue, ir_value**);
/* Entity-field
*
*/
struct ast_entfield_s
{
- ast_expression_common expression;
+ ast_expression expression;
/* The entity can come from an expression of course. */
ast_expression *entity;
/* As can the field, it just must result in a value of TYPE_FIELD */
};
ast_entfield* ast_entfield_new(lex_ctx ctx, ast_expression *entity, ast_expression *field);
ast_entfield* ast_entfield_new_force(lex_ctx ctx, ast_expression *entity, ast_expression *field, const ast_expression *outtype);
-void ast_entfield_delete(ast_entfield*);
-
-bool ast_entfield_codegen(ast_entfield*, ast_function*, bool lvalue, ir_value**);
/* Member access:
*
*/
struct ast_member_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_expression *owner;
unsigned int field;
const char *name;
void ast_member_delete(ast_member*);
bool ast_member_set_name(ast_member*, const char *name);
-bool ast_member_codegen(ast_member*, ast_function*, bool lvalue, ir_value**);
/* Array index access:
*
*/
struct ast_array_index_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_expression *array;
ast_expression *index;
};
ast_array_index* ast_array_index_new(lex_ctx ctx, ast_expression *array, ast_expression *index);
-void ast_array_index_delete(ast_array_index*);
-
-bool ast_array_index_codegen(ast_array_index*, ast_function*, bool lvalue, ir_value**);
/* Store
*
*/
struct ast_store_s
{
- ast_expression_common expression;
+ ast_expression expression;
int op;
ast_expression *dest;
ast_expression *source;
};
ast_store* ast_store_new(lex_ctx ctx, int op,
ast_expression *d, ast_expression *s);
-void ast_store_delete(ast_store*);
-
-bool ast_store_codegen(ast_store*, ast_function*, bool lvalue, ir_value**);
/* If
*
*/
struct ast_ifthen_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_expression *cond;
/* It's all just 'expressions', since an ast_block is one too. */
ast_expression *on_true;
ast_expression *on_false;
};
ast_ifthen* ast_ifthen_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
-void ast_ifthen_delete(ast_ifthen*);
-
-bool ast_ifthen_codegen(ast_ifthen*, ast_function*, bool lvalue, ir_value**);
/* Ternary expressions...
*
*/
struct ast_ternary_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_expression *cond;
/* It's all just 'expressions', since an ast_block is one too. */
ast_expression *on_true;
ast_expression *on_false;
};
ast_ternary* ast_ternary_new(lex_ctx ctx, ast_expression *cond, ast_expression *ontrue, ast_expression *onfalse);
-void ast_ternary_delete(ast_ternary*);
-
-bool ast_ternary_codegen(ast_ternary*, ast_function*, bool lvalue, ir_value**);
/* A general loop node
*
*/
struct ast_loop_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_expression *initexpr;
ast_expression *precond;
ast_expression *postcond;
ast_expression *postcond, bool post_not,
ast_expression *increment,
ast_expression *body);
-void ast_loop_delete(ast_loop*);
-
-bool ast_loop_codegen(ast_loop*, ast_function*, bool lvalue, ir_value**);
/* Break/Continue
*/
struct ast_breakcont_s
{
- ast_expression_common expression;
+ ast_expression expression;
bool is_continue;
unsigned int levels;
};
ast_breakcont* ast_breakcont_new(lex_ctx ctx, bool iscont, unsigned int levels);
-void ast_breakcont_delete(ast_breakcont*);
-
-bool ast_breakcont_codegen(ast_breakcont*, ast_function*, bool lvalue, ir_value**);
/* Switch Statements
*
} ast_switch_case;
struct ast_switch_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_expression *operand;
ast_switch_case *cases;
};
ast_switch* ast_switch_new(lex_ctx ctx, ast_expression *op);
-void ast_switch_delete(ast_switch*);
-
-bool ast_switch_codegen(ast_switch*, ast_function*, bool lvalue, ir_value**);
/* Label nodes
*
*/
struct ast_label_s
{
- ast_expression_common expression;
+ ast_expression expression;
const char *name;
ir_block *irblock;
ast_goto **gotos;
};
ast_label* ast_label_new(lex_ctx ctx, const char *name, bool undefined);
-void ast_label_delete(ast_label*);
-void ast_label_register_goto(ast_label*, ast_goto*);
-
-bool ast_label_codegen(ast_label*, ast_function*, bool lvalue, ir_value**);
/* GOTO nodes
*
*/
struct ast_goto_s
{
- ast_expression_common expression;
+ ast_expression expression;
const char *name;
ast_label *target;
ir_block *irblock_from;
};
ast_goto* ast_goto_new(lex_ctx ctx, const char *name);
-void ast_goto_delete(ast_goto*);
void ast_goto_set_label(ast_goto*, ast_label*);
-bool ast_goto_codegen(ast_goto*, ast_function*, bool lvalue, ir_value**);
-
/* CALL node
*
* Contains an ast_expression as target, rather than an ast_function/value.
*/
struct ast_call_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_expression *func;
ast_expression* *params;
ast_expression *va_count;
};
ast_call* ast_call_new(lex_ctx ctx,
ast_expression *funcexpr);
-void ast_call_delete(ast_call*);
-bool ast_call_codegen(ast_call*, ast_function*, bool lvalue, ir_value**);
bool ast_call_check_types(ast_call*);
/* Blocks
*/
struct ast_block_s
{
- ast_expression_common expression;
+ ast_expression expression;
ast_value* *locals;
ast_expression* *exprs;
ast_block* ast_block_new(lex_ctx ctx);
void ast_block_delete(ast_block*);
void ast_block_set_type(ast_block*, ast_expression *from);
-
-bool ast_block_codegen(ast_block*, ast_function*, bool lvalue, ir_value**);
void ast_block_collect(ast_block*, ast_expression*);
bool GMQCC_WARN ast_block_add_expr(ast_block*, ast_expression*);
*/
struct ast_function_s
{
- ast_node_common node;
+ ast_node node;
ast_value *vtype;
const char *name;
ast_value *varargs;
ast_value *argc;
ast_value *fixedparams;
+ ast_value *return_value;
};
ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype);
/* This will NOT delete the underlying ast_value */
/* For "optimized" builds this can just keep returning "foo"...
* or whatever...
*/
-const char* ast_function_label(ast_function*, const char *prefix);
+/*const char* ast_function_label(ast_function*, const char *prefix);*/
bool ast_function_codegen(ast_function *self, ir_builder *builder);
bool ast_generate_accessors(ast_value *asvalue, ir_builder *ir);
-/* Expression union
- */
-union ast_expression_u
-{
- ast_expression_common expression;
-
- ast_value value;
- ast_binary binary;
- ast_block block;
- ast_ternary ternary;
- ast_ifthen ifthen;
- ast_store store;
- ast_entfield entfield;
-};
-
-/* Node union
- */
-union ast_node_u
-{
- ast_node_common node;
- ast_expression expression;
-};
-
#endif
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+#include <string.h>
#include "gmqcc.h"
-/* This is outrageous! */
-#define QCINT_ENTRY void*
-#define QCINT_TO_HASH_ENTRY(q) ((void*)(uintptr_t)(q))
-#define HASH_ENTRY_TO_QCINT(h) ((qcint)(uintptr_t)(h))
+/*
+ * We could use the old method of casting to uintptr_t then to void*
+ * or qcint; however, it's incredibly unsafe for two reasons.
+ * 1) The compilers aliasing optimization can legally make it unstable
+ * (it's undefined behaviour).
+ *
+ * 2) The cast itself depends on fresh storage (newly allocated in which
+ * ever function is using the cast macros), the contents of which are
+ * transferred in a way that the obligation to release storage is not
+ * propagated.
+ */
+typedef union {
+ void *enter;
+ qcint leave;
+} code_hash_entry_t;
+
+/* Some sanity macros */
+#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
+#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
void code_push_statement(code_t *code, prog_section_statement *stmt, int linenum)
{
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
-uint32_t code_genstring(code_t *code, const char *str)
-{
- uint32_t off;
- size_t hash;
- QCINT_ENTRY existing;
+uint32_t code_genstring(code_t *code, const char *str) {
+ size_t hash;
+ code_hash_entry_t existing;
if (!str)
return 0;
}
if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
- hash = ((unsigned char*)str)[strlen(str)-1];
- existing = code_util_str_htgeth(code->string_cache, str, hash);
+ hash = ((unsigned char*)str)[strlen(str)-1];
+ CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash);
} else {
- hash = util_hthash(code->string_cache, str);
- existing = util_htgeth(code->string_cache, str, hash);
+ hash = util_hthash(code->string_cache, str);
+ CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash);
}
- if (existing)
- return HASH_ENTRY_TO_QCINT(existing);
+ if (CODE_HASH_ENTER(existing))
+ return CODE_HASH_LEAVE(existing);
- off = vec_size(code->chars);
+ CODE_HASH_LEAVE(existing) = vec_size(code->chars);
vec_upload(code->chars, str, strlen(str)+1);
- util_htseth(code->string_cache, str, hash, QCINT_TO_HASH_ENTRY(off));
- return off;
+ util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing));
+ return CODE_HASH_LEAVE(existing);
}
qcint code_alloc_field (code_t *code, size_t qcsize)
return pos;
}
-bool code_write(code_t *code, const char *filename, const char *lnofile) {
- prog_header code_header;
- FILE *fp = NULL;
- size_t it = 2;
-
- code_header.statements.offset = sizeof(prog_header);
- code_header.statements.length = vec_size(code->statements);
- code_header.defs.offset = code_header.statements.offset + (sizeof(prog_section_statement) * vec_size(code->statements));
- code_header.defs.length = vec_size(code->defs);
- code_header.fields.offset = code_header.defs.offset + (sizeof(prog_section_def) * vec_size(code->defs));
- code_header.fields.length = vec_size(code->fields);
- code_header.functions.offset = code_header.fields.offset + (sizeof(prog_section_field) * vec_size(code->fields));
- code_header.functions.length = vec_size(code->functions);
- code_header.globals.offset = code_header.functions.offset + (sizeof(prog_section_function) * vec_size(code->functions));
- code_header.globals.length = vec_size(code->globals);
- code_header.strings.offset = code_header.globals.offset + (sizeof(int32_t) * vec_size(code->globals));
- code_header.strings.length = vec_size(code->chars);
- code_header.version = 6;
+static void code_create_header(code_t *code, prog_header *code_header) {
+ code_header->statements.offset = sizeof(prog_header);
+ code_header->statements.length = vec_size(code->statements);
+ code_header->defs.offset = code_header->statements.offset + (sizeof(prog_section_statement) * vec_size(code->statements));
+ code_header->defs.length = vec_size(code->defs);
+ code_header->fields.offset = code_header->defs.offset + (sizeof(prog_section_def) * vec_size(code->defs));
+ code_header->fields.length = vec_size(code->fields);
+ code_header->functions.offset = code_header->fields.offset + (sizeof(prog_section_field) * vec_size(code->fields));
+ code_header->functions.length = vec_size(code->functions);
+ code_header->globals.offset = code_header->functions.offset + (sizeof(prog_section_function) * vec_size(code->functions));
+ code_header->globals.length = vec_size(code->globals);
+ code_header->strings.offset = code_header->globals.offset + (sizeof(int32_t) * vec_size(code->globals));
+ code_header->strings.length = vec_size(code->chars);
+ code_header->version = 6;
if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
- code_header.crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
+ code_header->crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
else
- code_header.crc16 = code->crc;
- code_header.entfield = code->entfields;
+ code_header->crc16 = code->crc;
+ code_header->entfield = code->entfields;
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n");
}
/* ensure all data is in LE format */
- util_endianswap(&code_header.version, 1, sizeof(code_header.version));
- util_endianswap(&code_header.crc16, 1, sizeof(code_header.crc16));
- util_endianswap(&code_header.statements, 2, sizeof(code_header.statements.offset));
- util_endianswap(&code_header.defs, 2, sizeof(code_header.statements.offset));
- util_endianswap(&code_header.fields, 2, sizeof(code_header.statements.offset));
- util_endianswap(&code_header.functions, 2, sizeof(code_header.statements.offset));
- util_endianswap(&code_header.strings, 2, sizeof(code_header.statements.offset));
- util_endianswap(&code_header.globals, 2, sizeof(code_header.statements.offset));
- util_endianswap(&code_header.entfield, 1, sizeof(code_header.entfield));
+ util_endianswap(&code_header->version, 1, sizeof(code_header->version));
+ util_endianswap(&code_header->crc16, 1, sizeof(code_header->crc16));
+ util_endianswap(&code_header->statements, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->defs, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->fields, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->functions, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->strings, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->globals, 2, sizeof(code_header->statements.offset));
+ util_endianswap(&code_header->entfield, 1, sizeof(code_header->entfield));
+
+ /*
+ * These are not part of the header but we ensure LE format here to save on duplicated
+ * code.
+ */
util_endianswap(code->statements, vec_size(code->statements), sizeof(prog_section_statement));
util_endianswap(code->defs, vec_size(code->defs), sizeof(prog_section_def));
util_endianswap(code->fields, vec_size(code->fields), sizeof(prog_section_field));
util_endianswap(code->functions, vec_size(code->functions), sizeof(prog_section_function));
util_endianswap(code->globals, vec_size(code->globals), sizeof(int32_t));
+}
+
+/*
+ * Same principle except this one allocates memory and writes the lno(optional) and the dat file
+ * directly out to allocated memory. Which is actually very useful for the future library support
+ * we're going to add.
+ */
+bool code_write_memory(code_t *code, uint8_t **datmem, size_t *sizedat, uint8_t **lnomem, size_t *sizelno) {
+ prog_header code_header;
+ uint32_t offset = 0;
+
+ if (!datmem)
+ return false;
+
+ code_create_header(code, &code_header);
+
+ #define WRITE_CHUNK(C,X,S) \
+ do { \
+ memcpy((void*)(&(*C)[offset]), (const void*)(X), (S)); \
+ offset += (S); \
+ } while (0)
+
+ /* Calculate size required to store entire file out to memory */
+ if (lnomem) {
+ uint32_t version = 1;
+
+ *sizelno += 4; /* LNOF */
+ *sizelno += sizeof(version);
+ *sizelno += sizeof(code_header.defs.length);
+ *sizelno += sizeof(code_header.globals.length);
+ *sizelno += sizeof(code_header.fields.length);
+ *sizelno += sizeof(code_header.statements.length);
+ *sizelno += sizeof(code->linenums[0]) * vec_size(code->linenums);
+
+ *lnomem = (uint8_t*)mem_a(*sizelno);
+
+ WRITE_CHUNK(lnomem, "LNOF", 4);
+ WRITE_CHUNK(lnomem, &version, sizeof(version));
+ WRITE_CHUNK(lnomem, &code_header.defs.length, sizeof(code_header.defs.length));
+ WRITE_CHUNK(lnomem, &code_header.globals.length, sizeof(code_header.globals.length));
+ WRITE_CHUNK(lnomem, &code_header.fields.length, sizeof(code_header.fields.length));
+ WRITE_CHUNK(lnomem, &code_header.statements.length, sizeof(code_header.statements.length));
+
+ /* something went terribly wrong */
+ if (offset != *sizelno) {
+ mem_d(*lnomem);
+ *sizelno = 0;
+ return false;
+ }
+ offset = 0;
+ }
+
+ /* Write out the dat */
+ *sizedat += sizeof(prog_header);
+ *sizedat += sizeof(prog_section_statement) * vec_size(code->statements);
+ *sizedat += sizeof(prog_section_def) * vec_size(code->defs);
+ *sizedat += sizeof(prog_section_field) * vec_size(code->fields);
+ *sizedat += sizeof(prog_section_function) * vec_size(code->functions);
+ *sizedat += sizeof(int32_t) * vec_size(code->globals);
+ *sizedat += 1 * vec_size(code->chars);
+
+ *datmem = (uint8_t*)mem_a(*sizedat);
+
+ WRITE_CHUNK(datmem, &code_header, sizeof(prog_header));
+ WRITE_CHUNK(datmem, code->statements, sizeof(prog_section_statement) * vec_size(code->statements));
+ WRITE_CHUNK(datmem, code->defs, sizeof(prog_section_def) * vec_size(code->defs));
+ WRITE_CHUNK(datmem, code->fields, sizeof(prog_section_field) * vec_size(code->fields));
+ WRITE_CHUNK(datmem, code->functions, sizeof(prog_section_function) * vec_size(code->functions));
+ WRITE_CHUNK(datmem, code->globals, sizeof(int32_t) * vec_size(code->globals));
+ WRITE_CHUNK(datmem, code->chars, 1 * vec_size(code->chars));
+
+ #undef WRITE_CHUNK
+
+ vec_free(code->statements);
+ vec_free(code->linenums);
+ vec_free(code->defs);
+ vec_free(code->fields);
+ vec_free(code->functions);
+ vec_free(code->globals);
+ vec_free(code->chars);
+
+ util_htdel(code->string_cache);
+ mem_d(code);
+
+ return true;
+}
+
+bool code_write(code_t *code, const char *filename, const char *lnofile) {
+ prog_header code_header;
+ FILE *fp = NULL;
+ size_t it = 2;
+
+ code_create_header(code, &code_header);
if (lnofile) {
uint32_t version = 1;
util_endianswap(code->linenums, vec_size(code->linenums), sizeof(code->linenums[0]));
- if (fs_file_write("LNOF", 4, 1, fp) != 1 ||
- fs_file_write(&version, sizeof(version), 1, fp) != 1 ||
+ if (fs_file_write("LNOF", 4, 1, fp) != 1 ||
+ fs_file_write(&version, sizeof(version), 1, fp) != 1 ||
fs_file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
fs_file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
fs_file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
fs_file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
- fs_file_write(code->linenums, sizeof(code->linenums[0]), vec_size(code->linenums), fp) != vec_size(code->linenums))
+ fs_file_write(code->linenums, sizeof(code->linenums[0]), vec_size(code->linenums), fp) != vec_size(code->linenums))
{
con_err("failed to write lno file\n");
}
if (!fp)
return false;
- if (1 != fs_file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
+ if (1 != fs_file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
vec_size(code->statements) != fs_file_write(code->statements, sizeof(prog_section_statement), vec_size(code->statements), fp) ||
vec_size(code->defs) != fs_file_write(code->defs, sizeof(prog_section_def) , vec_size(code->defs) , fp) ||
vec_size(code->fields) != fs_file_write(code->fields, sizeof(prog_section_field) , vec_size(code->fields) , fp) ||
}
}
+ fs_file_close(fp);
+ return true;
+}
+
+void code_cleanup(code_t *code) {
vec_free(code->statements);
vec_free(code->linenums);
vec_free(code->defs);
util_htdel(code->string_cache);
- fs_file_close(fp);
mem_d(code);
- return true;
}
* SOFTWARE.
*/
#include "gmqcc.h"
-#include <stdio.h>
/*
* isatty/STDERR_FILENO/STDOUT_FILNO
* NOTE: This prevents colored output to piped stdout/err via isatty
* checks.
*/
-static void con_enablecolor() {
+static void con_enablecolor(void) {
if (console.handle_err == stderr || console.handle_err == stdout)
console.color_err = !!(isatty(STDERR_FILENO));
if (console.handle_out == stderr || console.handle_out == stdout)
* for reporting of file:line based on lexer context, These are used
* heavily in the parser/ir/ast.
*/
-void con_vprintmsg_c(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap, const char *condname) {
+static void con_vprintmsg_c(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap, const char *condname) {
/* color selection table */
static int sel[] = {
CON_WHITE,
int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout;
if (color)
- print("\033[0;%dm%s:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, sel[level], msgtype);
+ print("\033[0;%dm%s:%d:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, (int)column, sel[level], msgtype);
else
- print("%s:%d: %s: ", name, (int)line, msgtype);
+ print("%s:%d:%d: %s: ", name, (int)line, (int)column, msgtype);
vprint(msg, ap);
if (condname)
print("\n");
}
-void con_vprintmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap) {
- con_vprintmsg_c(level, name, line, msgtype, msg, ap, NULL);
+void con_vprintmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap) {
+ con_vprintmsg_c(level, name, line, column, msgtype, msg, ap, NULL);
}
-void con_printmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, ...) {
+void con_printmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
- con_vprintmsg(level, name, line, msgtype, msg, va);
+ con_vprintmsg(level, name, line, column, msgtype, msg, va);
va_end (va);
}
void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
- con_vprintmsg(lvl, ((lex_ctx*)ctx)->file, ((lex_ctx*)ctx)->line, msgtype, msg, ap);
+ con_vprintmsg(lvl, ((lex_ctx*)ctx)->file, ((lex_ctx*)ctx)->line, ((lex_ctx*)ctx)->column, msgtype, msg, ap);
}
void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...) {
lvl = LVL_ERROR;
}
- con_vprintmsg_c(lvl, ctx.file, ctx.line, msgtype, fmt, ap, warn_name);
+ con_vprintmsg_c(lvl, ctx.file, ctx.line, ctx.column, msgtype, fmt, ap, warn_name);
return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR);
}
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+#include <string.h>
#include "gmqcc.h"
/*
CONTROL := $(DEBDIR)/DEBIAN/control
ifneq (, $(findstring i686, $(CARCH)))
- CFLAGS := -m32
+ CFLAGS += -m32
+ LDFLAGS += -m32
endif
base:
$(MAKE) -C $(BASEDIR) clean
- $(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
+ CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
+ $(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
@install -d -m755 $(DEBDIR)/DEBIAN
@echo "Package: gmqcc" > $(CONTROL)
@echo "Version: $(MAJOR).$(MINOR).$(PATCH)" >> $(CONTROL)
--- /dev/null
+To use the ebuilds provided in this gentoo directory you first must
+create a directory in your overlay tree.
+
+If you don't already have your own directory for custom ebuilds, you can
+create one. If you already have one, and that directory is set in your
+/etc/make.conf for PORTDIR_OVERLAY, this step can be skiped. Otherwise
+if you don't already, you can create one as such.
+
+# mkdir -p /usr/local/portage
+# vim /etc/make.conf
+ Set PORTDIR_OVERLAY=/usr/local/portage
+ Then save and exit
+
+Once that is completed, or you skiped that step, you need to create a
+directory in your overlay tree for gmqcc, this can be done as such:
+(subsitute [[PORTDIR_OVERLAY]] with the one set in /etc/make.conf)
+
+# mkdir -p [[PORTDIR_OVERLAY]]/gmqcc
+
+After the directory is created you need to move the correct version ebuild
+into that directory depending on which version of GMQCC you want. For
+instance, if you want gmqcc 0.3.0, you move gmqcc-0.3.0.ebuild into that
+directory.
+
+# mv gmqcc-{version}.ebuild [[PORTDIR_OVERLAY]]/gmqcc/
+
+After the file is moved into your newly created portage overlay tree, you'll
+need to build a digest for it with ebuild. A digest is simply a Manifest and
+digital signature for the source files used.
+
+# ebuild gmqcc-0.3.0.ebuild digest
+
+After the digest is built, you can emerge gmqcc as usual.
+
+# emerge gmqcc
--- /dev/null
+EAPI=5
+
+DESCRIPTION="An Improved Quake C Compiler"
+HOMEPAGE="http://graphitemaster.github.com/gmqcc/"
+SRC_URI="https://github.com/graphitemaster/${PN}/archive/${PV}.tar.gz -> ${P}.tar.gz"
+
+LICENSE="MIT"
+
+SLOT="0"
+IUSE=""
+KEYWORDS="~amd64 ~x86"
+
+src_prepare() {
+ sed -i -e "s:-Werror ::" Makefile || die
+}
+
+src_install() {
+ emake install PREFIX="${D}/usr"
+ dodoc README
+}
--- /dev/null
+BASEDIR := ../../../
+PREFIX := /usr
+HEADER := $(BASEDIR)/gmqcc.h
+MAJOR := $(shell sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER))
+MINOR := $(shell sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER))
+PATCH := $(shell sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER))
+CARCH := $(shell uname -m)
+PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(CARCH)-git
+PKG := $(PKGDIR).txz
+PKGINFO := $(PKGDIR)/.PKGINFO
+DESTDIR := distro/slackware/this/$(PKGDIR)
+CFLAGS :=
+
+
+ifneq (, $(findstring i686, $(CARCH)))
+ CFLAGS += -m32
+ LDFLAGS += -m32
+endif
+
+base:
+ $(MAKE) -C $(BASEDIR) clean
+ CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
+ $(MAKE) -C $(BASEDIR) "DESTDIR=$(DESTDIR)" "PREFIX=$(PREFIX)" install
+ gzip -9 $(PKGDIR)/usr/share/man/man?/*.?
+ strip -s $(PKGDIR)/usr/bin/*
+ mkdir $(PKGDIR)/install
+ cp slack-desc $(PKGDIR)/install
+ @tar -cJvf $(PKG) -C $(PKGDIR)/ install/ usr/
+ @rm -rf $(PKGDIR)
+
+clean:
+ $(MAKE) -C $(BASEDIR) clean
+ @rm -f *.txz
+
+all: base
--- /dev/null
+ |-----handy-ruler------------------------------------------------------|
+gmqcc: gmqcc (Quake C compiler)
+gmqcc:
+gmqcc: A modern written-from-scratch compiler for the QuakeC language with
+gmqcc: support for many common features found in other QC compilers.
+gmqcc: Additionally contains a standalone QCVM executor, and a tool to deal
+gmqcc: with .pak archive files.
+gmqcc:
+gmqcc:
+gmqcc: github page:
+gmqcc: http://github.com/graphitemaster/gmqcc
+gmqcc:
void printA() = #1; // the usual way
void printB() = #2-1; // with a constant expression
.Ed
+.It Fl f Ns Cm return-assignments
+Enabiling this option will allow assigning values or expressions to the
+return keyword as if it were a local variable of the same type as the
+function's signature's return type.
+.Pp
+Example:
+.Bd -literal -offset indent
+float bar() { return 1024; }
+float fun() {
+ return = bar();
+ return; // returns value of bar
+}
.El
.Sh OPTIMIZATIONS
.Bl -tag -width Ds
--- /dev/null
+.\" gmqpak mdoc manpage
+.Dd April 27, 2013
+.Dt GMQPAK 2 PRM
+.Os
+.Sh NAME
+.Nm gmqpak
+.Nd A standalone Quake PAK utility
+.Sh SYNOPSIS
+.Nm gmqpak
+.Op Cm options
+.Op Cm files
+.Sh DESCRIPTION
+.Nm gmqpak
+Is a standalone Quake PAK file utility supporting the extraction of files,
+directories, or whole PAKs, as well as the opposite (creation of PAK files).
+.Sh OPTIONS
+.Bl -tag
+.It Fl -file Ar file
+Specify the PAK file to create or extract
+.It Fl -e
+Used to denote the extraction operation on a PAK file.
+.It Fl -c
+Used to denote the creation operation on a PAK file.
+.El
+.Sh EXAMPLES
+Here's some examples of how to use the utility to manipulate PAK files.
+.Bl -ohang
+.It Li gmqpak -file id1.pak -e
+.D1 extracts a PAK to ./
+.It Li gmqpak -file new.pak -c file1 dir/file2
+.D1 creates a PAK with the files specified
+.It Li gmqpak -file new1.pak -c directory.
+.D1 creates a PAK from files within the directory, including subdirectories and files.
+.Sh AUTHOR
+See <http://graphitemaster.github.com/gmqcc>.
+.Sh BUGS
+Please report bugs on <http://github.com/graphitemaster/gmqcc/issues>,
+or see <http://graphitemaster.github.com/gmqcc> on how to contact us.
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
+#include <stdlib.h>
#include "gmqcc.h"
* VM code
*/
-char* prog_getstring(qc_program *prog, qcint str)
-{
+const char* prog_getstring(qc_program *prog, qcint str) {
/* cast for return required for C++ */
if (str < 0 || str >= (qcint)vec_size(prog->strings))
- return (char*)"<<<invalid string>>>";
+ return "<<<invalid string>>>";
+
return prog->strings + str;
}
-prog_section_def* prog_entfield(qc_program *prog, qcint off)
-{
+prog_section_def* prog_entfield(qc_program *prog, qcint off) {
size_t i;
for (i = 0; i < vec_size(prog->fields); ++i) {
if (prog->fields[i].offset == off)
return NULL;
}
-qcany* prog_getedict(qc_program *prog, qcint e)
-{
+qcany* prog_getedict(qc_program *prog, qcint e) {
if (e >= (qcint)vec_size(prog->entitypool)) {
prog->vmerror++;
fprintf(stderr, "Accessing out of bounds edict %i\n", (int)e);
return (qcany*)(prog->entitydata + (prog->entityfields * e));
}
-qcint prog_spawn_entity(qc_program *prog)
-{
+qcint prog_spawn_entity(qc_program *prog) {
char *data;
qcint e;
for (e = 0; e < (qcint)vec_size(prog->entitypool); ++e) {
return e;
}
-void prog_free_entity(qc_program *prog, qcint e)
-{
+void prog_free_entity(qc_program *prog, qcint e) {
if (!e) {
prog->vmerror++;
fprintf(stderr, "Trying to free world entity\n");
prog->entitypool[e] = false;
}
-qcint prog_tempstring(qc_program *prog, const char *str)
-{
+qcint prog_tempstring(qc_program *prog, const char *str) {
size_t len = strlen(str);
size_t at = prog->tempstring_at;
return at;
}
-static size_t print_escaped_string(const char *str, size_t maxlen)
-{
+static size_t print_escaped_string(const char *str, size_t maxlen) {
size_t len = 2;
putchar('"');
--maxlen; /* because we're lazy and have escape sequences */
return len;
}
-static void trace_print_global(qc_program *prog, unsigned int glob, int vtype)
-{
+static void trace_print_global(qc_program *prog, unsigned int glob, int vtype) {
static char spaces[28+1] = " ";
prog_section_def *def;
qcany *value;
}
}
-static void prog_print_statement(qc_program *prog, prog_section_statement *st)
-{
+static void prog_print_statement(qc_program *prog, prog_section_statement *st) {
if (st->opcode >= (sizeof(asm_instr)/sizeof(asm_instr[0]))) {
printf("<illegal instruction %d>\n", st->opcode);
return;
}
}
-static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
-{
+static qcint prog_enterfunction(qc_program *prog, prog_section_function *func) {
qc_exec_stack st;
size_t parampos;
int32_t p;
return func->entry;
}
-static qcint prog_leavefunction(qc_program *prog)
-{
+static qcint prog_leavefunction(qc_program *prog) {
prog_section_function *prev = NULL;
size_t oldsp;
return st.stmt - 1; /* offset the ++st */
}
-bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps)
-{
+bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps) {
long jumpcount = 0;
size_t oldxflags = prog->xflags;
prog_section_statement *st;
#define GetArg(num) GetGlobal(OFS_PARM0 + 3*(num))
#define Return(any) *(GetGlobal(OFS_RETURN)) = (any)
-static int qc_print(qc_program *prog)
-{
+static int qc_print(qc_program *prog) {
size_t i;
const char *laststr = NULL;
for (i = 0; i < (size_t)prog->argc; ++i) {
return 0;
}
-static int qc_error(qc_program *prog)
-{
+static int qc_error(qc_program *prog) {
fprintf(stderr, "*** VM raised an error:\n");
qc_print(prog);
prog->vmerror++;
return -1;
}
-static int qc_ftos(qc_program *prog)
-{
+static int qc_ftos(qc_program *prog) {
char buffer[512];
qcany *num;
qcany str;
return 0;
}
-static int qc_stof(qc_program *prog)
-{
+static int qc_stof(qc_program *prog) {
qcany *str;
qcany num;
CheckArgs(1);
return 0;
}
-static int qc_vtos(qc_program *prog)
-{
+static int qc_vtos(qc_program *prog) {
char buffer[512];
qcany *num;
qcany str;
return 0;
}
-static int qc_etos(qc_program *prog)
-{
+static int qc_etos(qc_program *prog) {
char buffer[512];
qcany *num;
qcany str;
return 0;
}
-static int qc_spawn(qc_program *prog)
-{
+static int qc_spawn(qc_program *prog) {
qcany ent;
CheckArgs(0);
ent.edict = prog_spawn_entity(prog);
return (ent.edict ? 0 : -1);
}
-static int qc_kill(qc_program *prog)
-{
+static int qc_kill(qc_program *prog) {
qcany *ent;
CheckArgs(1);
ent = GetArg(0);
return 0;
}
-static int qc_sqrt(qc_program *prog)
-{
+static int qc_sqrt(qc_program *prog) {
qcany *num, out;
CheckArgs(1);
num = GetArg(0);
return 0;
}
-static int qc_vlen(qc_program *prog)
-{
+static int qc_vlen(qc_program *prog) {
qcany *vec, len;
CheckArgs(1);
vec = GetArg(0);
return 0;
}
-static int qc_normalize(qc_program *prog)
-{
+static int qc_normalize(qc_program *prog) {
double len;
qcany *vec;
qcany out;
return 0;
}
-static int qc_strcat(qc_program *prog)
-{
+static int qc_strcat(qc_program *prog) {
char *buffer;
size_t len1, len2;
- char *cstr1, *cstr2;
qcany *str1, *str2;
qcany out;
+
+ const char *cstr1;
+ const char *cstr2;
CheckArgs(2);
str1 = GetArg(0);
return 0;
}
-static int qc_strcmp(qc_program *prog)
-{
- char *cstr1, *cstr2;
+static int qc_strcmp(qc_program *prog) {
qcany *str1, *str2;
qcany out;
+ const char *cstr1;
+ const char *cstr2;
+
if (prog->argc != 2 && prog->argc != 3) {
fprintf(stderr, "ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n",
prog->argc);
return 0;
}
-static int qc_floor(qc_program *prog)
-{
+static int qc_floor(qc_program *prog) {
qcany *num, out;
CheckArgs(1);
num = GetArg(0);
static const char *arg0 = NULL;
-static void version() {
+static void version(void) {
printf("GMQCC-QCVM %d.%d.%d Built %s %s\n",
GMQCC_VERSION_MAJOR,
GMQCC_VERSION_MINOR,
);
}
-static void usage()
-{
+static void usage(void) {
printf("usage: %s [options] [parameters] file\n", arg0);
printf("options:\n");
printf(" -h, --help print this message\n"
" -string <s> pass a string parameter to main() \n");
}
-static void prog_main_setparams(qc_program *prog)
-{
+static void prog_main_setparams(qc_program *prog) {
size_t i;
qcany *arg;
}
}
+void escapestring(char* dest, const char* src) {
+ char c;
+ while ((c = *(src++))) {
+ switch(c) {
+ case '\t':
+ *(dest++) = '\\', *(dest++) = 't';
+ break;
+ case '\n':
+ *(dest++) = '\\', *(dest++) = 'n';
+ break;
+ case '\r':
+ *(dest++) = '\\', *(dest++) = 'r';
+ break;
+ case '\\':
+ *(dest++) = '\\', *(dest++) = '\\';
+ break;
+ case '\"':
+ *(dest++) = '\\', *(dest++) = '\"';
+ break;
+ default:
+ *(dest++) = c;
+ }
+ }
+ *dest = '\0';
+}
+
void prog_disasm_function(qc_program *prog, size_t id);
-int main(int argc, char **argv)
-{
+
+int main(int argc, char **argv) {
size_t i;
qcint fnmain = -1;
qc_program *prog;
return 0;
}
if (opts_printdefs) {
+ char *escape = NULL;
+ const char *getstring = NULL;
+
for (i = 0; i < vec_size(prog->defs); ++i) {
printf("Global: %8s %-16s at %u%s",
type_name[prog->defs[i].type & DEF_TYPEMASK],
printf(" [init: %u]", (unsigned)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int ));
break;
case TYPE_STRING:
- printf(" [init: `%s`]", prog_getstring(prog, ((qcany*)(prog->globals + prog->defs[i].offset))->string ));
+ getstring = prog_getstring(prog, ((qcany*)(prog->globals + prog->defs[i].offset))->string);
+ escape = (char*)mem_a(strlen(getstring) * 2 + 1); /* will be enough */
+ escapestring(escape, getstring);
+ printf(" [init: `%s`]", escape);
+
+ mem_d(escape); /* free */
break;
default:
break;
return 0;
}
-void prog_disasm_function(qc_program *prog, size_t id)
-{
+void prog_disasm_function(qc_program *prog, size_t id) {
prog_section_function *fdef = prog->functions + id;
prog_section_statement *st;
* SOFTWARE.
*/
#include <time.h>
+#include <string.h>
+#include <stdlib.h>
#include <sys/stat.h>
+
#include "gmqcc.h"
#include "lexer.h"
static uint32_t ftepp_predef_randval = 0;
/* __DATE__ */
-char *ftepp_predef_date(lex_file *context) {
+static char *ftepp_predef_date(lex_file *context) {
struct tm *itime = NULL;
time_t rtime;
char *value = (char*)mem_a(82);
}
/* __TIME__ */
-char *ftepp_predef_time(lex_file *context) {
+static char *ftepp_predef_time(lex_file *context) {
struct tm *itime = NULL;
time_t rtime;
char *value = (char*)mem_a(82);
}
/* __LINE__ */
-char *ftepp_predef_line(lex_file *context) {
+static char *ftepp_predef_line(lex_file *context) {
char *value;
util_asprintf(&value, "%d", (int)context->line);
return value;
}
/* __FILE__ */
-char *ftepp_predef_file(lex_file *context) {
+static char *ftepp_predef_file(lex_file *context) {
size_t length = strlen(context->name) + 3; /* two quotes and a terminator */
char *value = (char*)mem_a(length);
util_snprintf(value, length, "\"%s\"", context->name);
return value;
}
/* __COUNTER_LAST__ */
-char *ftepp_predef_counterlast(lex_file *context) {
+static char *ftepp_predef_counterlast(lex_file *context) {
char *value;
util_asprintf(&value, "%u", ftepp_predef_countval);
return value;
}
/* __COUNTER__ */
-char *ftepp_predef_counter(lex_file *context) {
+static char *ftepp_predef_counter(lex_file *context) {
char *value;
ftepp_predef_countval ++;
util_asprintf(&value, "%u", ftepp_predef_countval);
return value;
}
/* __RANDOM__ */
-char *ftepp_predef_random(lex_file *context) {
+static char *ftepp_predef_random(lex_file *context) {
char *value;
ftepp_predef_randval = (util_rand() % 0xFF) + 1;
util_asprintf(&value, "%u", ftepp_predef_randval);
return value;
}
/* __RANDOM_LAST__ */
-char *ftepp_predef_randomlast(lex_file *context) {
+static char *ftepp_predef_randomlast(lex_file *context) {
char *value;
util_asprintf(&value, "%u", ftepp_predef_randval);
return value;
}
/* __TIMESTAMP__ */
-char *ftepp_predef_timestamp(lex_file *context) {
+static char *ftepp_predef_timestamp(lex_file *context) {
struct stat finfo;
char *find;
char *value;
mem_d(self);
}
-static ftepp_t* ftepp_new()
+static ftepp_t* ftepp_new(void)
{
ftepp_t *ftepp;
static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
{
- util_htrm(ftepp->macros, name, NULL);
+ util_htrm(ftepp->macros, name, (void (*)(void*))&ppmacro_delete);
}
static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
return false;
}
-#if 0
- if (ftepp->output_on)
- vec_push(ftepp->macros, macro);
-#endif
if (ftepp->output_on)
util_htset(ftepp->macros, macro->name, (void*)macro);
else {
void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
{
ppmacro *macro;
- lex_ctx ctx = { "__builtin__", 0 };
+ lex_ctx ctx = { "__builtin__", 0, 0 };
ctx.file = source;
macro = ppmacro_new(ctx, name);
/*vec_push(ftepp->macros, macro);*/
*/
#ifndef GMQCC_HDR
#define GMQCC_HDR
-#include <limits.h>
-#include <stdlib.h>
-#include <string.h>
#include <stdarg.h>
-#include <stdio.h>
-#include <ctype.h>
+#include <stdio.h> /* TODO: remove this */
/*
* Disable some over protective warnings in visual studio because fixing them is a waste
# include <dirent.h>
#endif /*! _WIN32 && !defined(__MINGW32__) */
+/*===================================================================*/
+/*=========================== stat.c ================================*/
+/*===================================================================*/
+void stat_info (void);
+char *stat_mem_strdup (const char *, size_t, const char *, bool);
+void *stat_mem_reallocate(void *, size_t, size_t, const char *);
+void stat_mem_deallocate(void *);
+void *stat_mem_allocate (size_t, size_t, const char *);
+
+#define mem_a(SIZE) stat_mem_allocate ((SIZE), __LINE__, __FILE__)
+#define mem_d(PTRN) stat_mem_deallocate((void*)(PTRN))
+#define mem_r(PTRN, SIZE) stat_mem_reallocate((void*)(PTRN), (SIZE), __LINE__, __FILE__)
+#define mem_af(SIZE, FILE, LINE) stat_mem_allocate ((SIZE), (LINE), (FILE))
+
+/* TODO: rename to mem variations */
+#define util_strdup(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, false)
+#define util_strdupe(SRC) stat_mem_strdup((char*)(SRC), __LINE__, __FILE__, true)
/*===================================================================*/
/*=========================== util.c ================================*/
/*===================================================================*/
-void *util_memory_a (size_t, /*****/ unsigned int, const char *);
-void *util_memory_r (void *, size_t, unsigned int, const char *);
-void util_memory_d (void *);
-void util_meminfo ();
-
bool util_filexists (const char *);
bool util_strupper (const char *);
bool util_strdigit (const char *);
-char *_util_Estrdup (const char *, const char *, size_t);
-char *_util_Estrdup_empty(const char *, const char *, size_t);
void util_debug (const char *, const char *, ...);
void util_endianswap (void *, size_t, unsigned int);
uint16_t util_crc16(uint16_t crc, const char *data, size_t len);
void util_seed(uint32_t);
-uint32_t util_rand();
+uint32_t util_rand(void);
/*
* String functions (formatting, copying, concatenating, errors). These are wrapped
char *util_strncpy (char *dest, const char *src, size_t num);
const char *util_strerror (int num);
-
-#ifdef NOTRACK
-# define mem_a(x) malloc (x)
-# define mem_d(x) free ((void*)x)
-# define mem_r(x, n) realloc((void*)x, n)
-# define mem_af(x,f,l) malloc (x)
-#else
-# define mem_a(x) util_memory_a((x), __LINE__, __FILE__)
-# define mem_d(x) util_memory_d((void*)(x))
-# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__)
-# define mem_af(x,f,l) util_memory_a((x), __LINE__, __FILE__)
-#endif /*! NOTRACK */
-
-#define util_strdup(X) _util_Estrdup((X), __FILE__, __LINE__)
-#define util_strdupe(X) _util_Estrdup_empty((X), __FILE__, __LINE__)
-
/*
* A flexible vector implementation: all vector pointers contain some
* data about themselfs exactly - sizeof(vector_t) behind the pointer
struct trie_s *entries;
} correct_trie_t;
-correct_trie_t* correct_trie_new();
+correct_trie_t* correct_trie_new(void);
typedef struct hash_table_t {
size_t size;
* code_pop_statement -- keeps statements and linenumbers together
*/
bool code_write (code_t *, const char *filename, const char *lno);
+GMQCC_WARN
code_t *code_init (void);
+void code_cleanup (code_t *);
uint32_t code_genstring (code_t *, const char *string);
qcint code_alloc_field (code_t *, size_t qcsize);
void code_push_statement(code_t *, prog_section_statement *stmt, int linenum);
typedef struct {
const char *file;
size_t line;
+ size_t column;
} lex_ctx;
/*===================================================================*/
LVL_ERROR
};
-FILE *con_default_out();
-FILE *con_default_err();
+FILE *con_default_out(void);
+FILE *con_default_err(void);
-void con_vprintmsg (int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap);
-void con_printmsg (int level, const char *name, size_t line, const char *msgtype, const char *msg, ...);
+void con_vprintmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap);
+void con_printmsg (int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...);
void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap);
void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...);
-void con_close ();
-void con_init ();
-void con_reset ();
+void con_close (void);
+void con_init (void);
+void con_reset (void);
void con_color (int);
int con_change(const char *, const char *);
int con_verr (const char *, va_list);
void /********/ vcompile_error (lex_ctx ctx, /*LVL_ERROR*/ const char *msg, va_list ap);
bool GMQCC_WARN compile_warning (lex_ctx ctx, int warntype, const char *fmt, ...);
bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap);
-void compile_show_werrors();
+void compile_show_werrors(void);
/*===================================================================*/
/*========================= assembler.c =============================*/
bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps);
-char* prog_getstring (qc_program *prog, qcint str);
+const char* prog_getstring (qc_program *prog, qcint str);
prog_section_def* prog_entfield (qc_program *prog, qcint off);
prog_section_def* prog_getdef (qc_program *prog, qcint off);
qcany* prog_getedict (qc_program *prog, qcint e);
/*===================== parser.c commandline ========================*/
/*===================================================================*/
struct parser_s;
-
-struct parser_s *parser_create ();
+struct parser_s *parser_create (void);
bool parser_compile_file (struct parser_s *parser, const char *);
bool parser_compile_string(struct parser_s *parser, const char *, const char *, size_t);
bool parser_finish (struct parser_s *parser, const char *);
/*====================== ftepp.c commandline ========================*/
/*===================================================================*/
struct ftepp_s;
-struct ftepp_s *ftepp_create ();
+struct ftepp_s *ftepp_create (void);
bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename);
bool ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str);
void ftepp_finish (struct ftepp_s *ftepp);
void opts_ini_init (const char *);
/* Saner flag handling */
-void opts_backup_non_Wall();
-void opts_restore_non_Wall();
-void opts_backup_non_Werror_all();
-void opts_restore_non_Werror_all();
+void opts_backup_non_Wall(void);
+void opts_restore_non_Wall(void);
+void opts_backup_non_Werror_all(void);
+void opts_restore_non_Werror_all(void);
enum {
# define GMQCC_TYPE_FLAGS
extern opts_cmd_t opts;
-#define OPTS_GENERIC(f,i) (!! (((f)[(i)/32]) & (1<< ((i)%32))))
+#define OPTS_GENERIC(f,i) (!! (((f)[(i)/32]) & (1<< (unsigned)((i)%32))))
#define OPTS_FLAG(i) OPTS_GENERIC(opts.flags, (i))
#define OPTS_WARN(i) OPTS_GENERIC(opts.warn, (i))
#define OPTS_WERROR(i) OPTS_GENERIC(opts.werror, (i))
const char *alias;
} intrin_t;
-ht intrin_intrinsics() {
+static ht intrin_intrinsics(void) {
static ht intrinsics = NULL;
if (!intrinsics)
intrinsics = util_htnew(PARSER_HT_SIZE);
vec_push(parser->globals, (ast_expression*)(VALUE)); \
} while (0)
-
-ast_expression *intrin_func (parser_t *parser, const char *name);
-
#define QC_M_E 2.71828182845905
-ast_expression *intrin_pow(parser_t *parser) {
+static ast_expression *intrin_func(parser_t *parser, const char *name);
+static ast_expression *intrin_pow (parser_t *parser) {
/*
* float pow(float x, float y) {
* float local = 1.0f;
return (ast_expression*)value;
}
-ast_expression *intrin_mod(parser_t *parser) {
+static ast_expression *intrin_mod(parser_t *parser) {
/*
* float mod(float x, float y) {
* return x - y * floor(x / y);
return (ast_expression*)value;
}
-ast_expression *intrin_exp(parser_t *parser) {
+static ast_expression *intrin_exp(parser_t *parser) {
/*
* float exp(float x) {
* return pow(QC_M_E, x);
return (ast_expression*)value;
}
-ast_expression *intrin_isnan(parser_t *parser) {
+static ast_expression *intrin_isnan(parser_t *parser) {
/*
* float isnan(float x) {
* float local;
}
-ast_expression *intrin_func(parser_t *parser, const char *name) {
+static ast_expression *intrin_func(parser_t *parser, const char *name) {
static bool init = false;
size_t i = 0;
void *find;
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
+ * Dale Weiler
*
* 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
*/
#include <stdlib.h>
#include <string.h>
+
#include "gmqcc.h"
#include "ir.h"
};
/* protos */
-static ir_value* ir_gen_extparam_proto(ir_builder *ir);
-static void ir_gen_extparam (code_t *, ir_builder *ir);
-
+static ir_value* ir_value_var(const char *name, int st, int vtype);
+static bool ir_value_set_name(ir_value*, const char *name);
+static void ir_value_dump(ir_value*, int (*oprintf)(const char*,...));
+
+static ir_value* ir_gen_extparam_proto(ir_builder *ir);
+static void ir_gen_extparam (code_t *, ir_builder *ir);
+
+static bool ir_builder_set_name(ir_builder *self, const char *name);
+
+static ir_function* ir_function_new(struct ir_builder_s *owner, int returntype);
+static bool ir_function_set_name(ir_function*, const char *name);
+static void ir_function_delete(ir_function*);
+static void ir_function_dump(ir_function*, char *ind, int (*oprintf)(const char*,...));
+
+static ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx, const char *label,
+ int op, ir_value *a, ir_value *b, int outype);
+static void ir_block_delete(ir_block*);
+static ir_block* ir_block_new(struct ir_function_s *owner, const char *label);
+static bool GMQCC_WARN ir_block_create_store(ir_block*, lex_ctx, ir_value *target, ir_value *what);
+static bool ir_block_set_label(ir_block*, const char *label);
+static void ir_block_dump(ir_block*, char *ind, int (*oprintf)(const char*,...));
+
+static bool ir_instr_op(ir_instr*, int op, ir_value *value, bool writing);
+static void ir_instr_delete(ir_instr*);
+static void ir_instr_dump(ir_instr* in, char *ind, int (*oprintf)(const char*,...));
/* error functions */
static void irerror(lex_ctx ctx, const char *msg, ...)
* Vector utility functions
*/
-bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx)
+static bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx)
{
size_t i;
size_t len = vec_size(vec);
return false;
}
-bool GMQCC_WARN vec_ir_block_find(ir_block **vec, ir_block *what, size_t *idx)
+static bool GMQCC_WARN vec_ir_block_find(ir_block **vec, ir_block *what, size_t *idx)
{
size_t i;
size_t len = vec_size(vec);
return false;
}
-bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx)
+static bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx)
{
size_t i;
size_t len = vec_size(vec);
ir_value_delete(self->extparams[i]);
}
vec_free(self->extparams);
+ vec_free(self->extparam_protos);
for (i = 0; i != vec_size(self->globals); ++i) {
ir_value_delete(self->globals[i]);
}
return !!self->name;
}
-ir_function* ir_builder_get_function(ir_builder *self, const char *name)
+static ir_function* ir_builder_get_function(ir_builder *self, const char *name)
{
return (ir_function*)util_htget(self->htfunctions, name);
}
return fn;
}
-ir_value* ir_builder_get_global(ir_builder *self, const char *name)
+static ir_value* ir_builder_get_global(ir_builder *self, const char *name)
{
return (ir_value*)util_htget(self->htglobals, name);
}
return (self->reserved_va_count = ir_builder_create_global(self, "reserved:va_count", TYPE_FLOAT));
}
-ir_value* ir_builder_get_field(ir_builder *self, const char *name)
+static ir_value* ir_builder_get_field(ir_builder *self, const char *name)
{
return (ir_value*)util_htget(self->htfields, name);
}
*IR Function
*/
-bool ir_function_naive_phi(ir_function*);
-void ir_function_enumerate(ir_function*);
-bool ir_function_calculate_liferanges(ir_function*);
-bool ir_function_allocate_locals(ir_function*);
+static bool ir_function_naive_phi(ir_function*);
+static void ir_function_enumerate(ir_function*);
+static bool ir_function_calculate_liferanges(ir_function*);
+static bool ir_function_allocate_locals(ir_function*);
ir_function* ir_function_new(ir_builder* owner, int outtype)
{
mem_d(self);
}
-void ir_function_collect_value(ir_function *self, ir_value *v)
+static void ir_function_collect_value(ir_function *self, ir_value *v)
{
vec_push(self->values, v);
}
(op >= INSTR_CALL0 && op <= INSTR_CALL8) );
}
-bool ir_function_pass_peephole(ir_function *self)
+static bool ir_function_pass_peephole(ir_function *self)
{
size_t b;
return true;
}
-bool ir_function_pass_tailrecursion(ir_function *self)
+static bool ir_function_pass_tailrecursion(ir_function *self)
{
size_t b, p;
*IR Instructions
*/
-ir_instr* ir_instr_new(lex_ctx ctx, ir_block* owner, int op)
+static ir_instr* ir_instr_new(lex_ctx ctx, ir_block* owner, int op)
{
ir_instr *self;
self = (ir_instr*)mem_a(sizeof(*self));
mem_d(self);
}
-void ir_instr_delete(ir_instr *self)
+static void ir_instr_delete(ir_instr *self)
{
size_t i;
/* The following calls can only delete from
mem_d(self);
}
-bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing)
+static bool ir_instr_op(ir_instr *self, int op, ir_value *v, bool writing)
{
if (self->_ops[op]) {
size_t idx;
*IR Value
*/
-void ir_value_code_setaddr(ir_value *self, int32_t gaddr)
+static void ir_value_code_setaddr(ir_value *self, int32_t gaddr)
{
self->code.globaladdr = gaddr;
if (self->members[0]) self->members[0]->code.globaladdr = gaddr;
if (self->members[2]) self->members[2]->code.globaladdr = gaddr;
}
-int32_t ir_value_code_addr(const ir_value *self)
+static int32_t ir_value_code_addr(const ir_value *self)
{
if (self->store == store_return)
return OFS_RETURN + self->code.addroffset;
return type_sizeof_[self->vtype];
}
-ir_value* ir_value_out(ir_function *owner, const char *name, int storetype, int vtype)
+static ir_value* ir_value_out(ir_function *owner, const char *name, int storetype, int vtype)
{
ir_value *v = ir_value_var(name, storetype, vtype);
if (!v)
return false;
}
-bool ir_value_life_insert(ir_value *self, size_t idx, ir_life_entry_t e)
+static bool ir_value_life_insert(ir_value *self, size_t idx, ir_life_entry_t e)
{
size_t k;
vec_push(self->life, e);
return true;
}
-bool ir_value_life_merge(ir_value *self, size_t s)
+static bool ir_value_life_merge(ir_value *self, size_t s)
{
size_t i;
const size_t vs = vec_size(self->life);
return ir_value_life_insert(self, i, new_entry);
}
-bool ir_value_life_merge_into(ir_value *self, const ir_value *other)
+static bool ir_value_life_merge_into(ir_value *self, const ir_value *other)
{
size_t i, myi;
return true;
}
-bool ir_values_overlap(const ir_value *a, const ir_value *b)
+static bool ir_values_overlap(const ir_value *a, const ir_value *b)
{
/* For any life entry in A see if it overlaps with
* any life entry in B.
return true;
}
-bool ir_block_create_store(ir_block *self, lex_ctx ctx, ir_value *target, ir_value *what)
+static bool ir_block_create_store(ir_block *self, lex_ctx ctx, ir_value *target, ir_value *what)
{
int op = 0;
int vtype;
return ir_block_create_general_instr(self, ctx, label, opcode, operand, NULL, ot);
}
-ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx ctx, const char *label,
+static ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx ctx, const char *label,
int op, ir_value *a, ir_value *b, int outype)
{
ir_instr *instr;
{
ir_value_code_setaddr(global, vec_size(code->globals));
if (global->hasvalue) {
- vec_push(code->globals, code_genstring(code, global->constval.vstring));
+ uint32_t load = code_genstring(code, global->constval.vstring);
+ vec_push(code->globals, load);
} else {
vec_push(code->globals, 0);
}
size_t i;
char *lnofile = NULL;
- code_init();
-
for (i = 0; i < vec_size(self->fields); ++i)
{
ir_builder_prepare_field(code, self->fields[i]);
# define strncat(dst, src, sz) strncat_s(dst, sz, src, _TRUNCATE)
#endif
-const char *qc_opname(int op)
+static const char *qc_opname(int op)
{
if (op < 0) return "<INVALID>";
if (op < (int)( sizeof(asm_instr) / sizeof(asm_instr[0]) ))
ind[strlen(ind)-1] = 0;
}
-void dump_phi(ir_instr *in, int (*oprintf)(const char*, ...))
+static void dump_phi(ir_instr *in, int (*oprintf)(const char*, ...))
{
size_t i;
oprintf("%s <- phi ", in->_ops[0]->name);
ind[strlen(ind)-1] = 0;
}
-void ir_value_dump_string(const char *str, int (*oprintf)(const char*, ...))
+static void ir_value_dump_string(const char *str, int (*oprintf)(const char*, ...))
{
oprintf("\"");
for (; *str; ++str) {
#ifndef GMQCC_IR_HDR
#define GMQCC_IR_HDR
#include "gmqcc.h"
-/* ir_value */
typedef struct
{
ir_life_entry_t *life;
} ir_value;
-int32_t ir_value_code_addr(const ir_value*);
-
/* ir_value can be a variable, or created by an operation */
-ir_value* ir_value_var(const char *name, int st, int vtype);
/* if a result of an operation: the function should store
* it to remember to delete it / garbage collect it
*/
-ir_value* ir_value_out(struct ir_function_s *owner, const char *name, int st, int vtype);
-void ir_value_delete(ir_value*);
-bool ir_value_set_name(ir_value*, const char *name);
-ir_value* ir_value_vector_member(ir_value*, unsigned int member);
-
-bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx);
-
+void ir_value_delete(ir_value*);
+ir_value* ir_value_vector_member(ir_value*, unsigned int member);
bool GMQCC_WARN ir_value_set_float(ir_value*, float f);
bool GMQCC_WARN ir_value_set_func(ir_value*, int f);
-#if 0
-bool GMQCC_WARN ir_value_set_int(ir_value*, int i);
-#endif
bool GMQCC_WARN ir_value_set_string(ir_value*, const char *s);
bool GMQCC_WARN ir_value_set_vector(ir_value*, vector v);
bool GMQCC_WARN ir_value_set_field(ir_value*, ir_value *fld);
-/*bool ir_value_set_pointer_v(ir_value*, ir_value* p); */
-/*bool ir_value_set_pointer_i(ir_value*, int i); */
-
-/* merge an instruction into the life-range */
-/* returns false if the lifepoint was already known */
-bool ir_value_life_merge(ir_value*, size_t);
-bool ir_value_life_merge_into(ir_value*, const ir_value*);
-/* check if a value lives at a specific point */
-bool ir_value_lives(ir_value*, size_t);
-/* check if the life-range of 2 values overlaps */
-bool ir_values_overlap(const ir_value*, const ir_value*);
-
-void ir_value_dump(ir_value*, int (*oprintf)(const char*,...));
-void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...));
+bool ir_value_lives(ir_value*, size_t);
+void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...));
/* PHI data */
typedef struct ir_phi_entry_s
struct ir_block_s *owner;
} ir_instr;
-ir_instr* ir_instr_new(lex_ctx ctx, struct ir_block_s *owner, int opcode);
-void ir_instr_delete(ir_instr*);
-
-bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx);
-
-bool ir_instr_op(ir_instr*, int op, ir_value *value, bool writing);
-
-void ir_instr_dump(ir_instr* in, char *ind, int (*oprintf)(const char*,...));
-
/* block */
typedef struct ir_block_s
{
size_t code_start;
} ir_block;
-ir_block* ir_block_new(struct ir_function_s *owner, const char *label);
-void ir_block_delete(ir_block*);
-
-bool ir_block_set_label(ir_block*, const char *label);
-
-ir_value* ir_block_create_binop(ir_block*, lex_ctx, const char *label, int op,
- ir_value *left, ir_value *right);
-ir_value* ir_block_create_unary(ir_block*, lex_ctx, const char *label, int op,
- ir_value *operand);
+ir_value* ir_block_create_binop(ir_block*, lex_ctx, const char *label, int op, ir_value *left, ir_value *right);
+ir_value* ir_block_create_unary(ir_block*, lex_ctx, const char *label, int op, ir_value *operand);
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx, int op, ir_value *target, ir_value *what);
-bool GMQCC_WARN ir_block_create_store(ir_block*, lex_ctx, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx, ir_value *target, ir_value *what);
-
-/* field must be of TYPE_FIELD */
-ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx, const char *label, ir_value *ent, ir_value *field, int outype);
-
-ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx, const char *label, ir_value *entity, ir_value *field);
+ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx, const char *label, ir_value *ent, ir_value *field, int outype);
+ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx, const char *label, ir_value *entity, ir_value *field);
/* This is to create an instruction of the form
* <outtype>%label := opcode a, b
*/
-ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx, const char *label,
- int op, ir_value *a, ir_value *b, int outype);
-
ir_instr* ir_block_create_phi(ir_block*, lex_ctx, const char *label, int vtype);
ir_value* ir_phi_value(ir_instr*);
void ir_phi_add(ir_instr*, ir_block *b, ir_value *v);
bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx, ir_block *to);
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx, ir_block *to);
-void ir_block_dump(ir_block*, char *ind, int (*oprintf)(const char*,...));
-
/* function */
-
typedef struct ir_function_s
{
char *name;
/* vararg support: */
size_t max_varargs;
} ir_function;
+
#define IR_FLAG_HAS_ARRAYS (1<<1)
#define IR_FLAG_HAS_UNINITIALIZED (1<<2)
#define IR_FLAG_HAS_GOTO (1<<3)
#define IR_FLAG_MASK_NO_OVERLAP (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
#define IR_FLAG_MASK_NO_LOCAL_TEMPS (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
-ir_function* ir_function_new(struct ir_builder_s *owner, int returntype);
-void ir_function_delete(ir_function*);
-
-void ir_function_collect_value(ir_function*, ir_value *value);
-
-bool ir_function_set_name(ir_function*, const char *name);
-
-ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param);
-
+ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param);
bool GMQCC_WARN ir_function_finalize(ir_function*);
-/*
-bool ir_function_naive_phi(ir_function*);
-bool ir_function_enumerate(ir_function*);
-bool ir_function_calculate_liferanges(ir_function*);
-*/
-
-ir_block* ir_function_create_block(lex_ctx ctx, ir_function*, const char *label);
-
-void ir_function_dump(ir_function*, char *ind, int (*oprintf)(const char*,...));
+ir_block* ir_function_create_block(lex_ctx ctx, ir_function*, const char *label);
/* builder */
#define IR_HT_SIZE 1024
ir_value *reserved_va_count;
} ir_builder;
-ir_builder* ir_builder_new(const char *modulename);
-void ir_builder_delete(ir_builder*);
-
-bool ir_builder_set_name(ir_builder *self, const char *name);
-
-ir_function* ir_builder_get_function(ir_builder*, const char *fun);
+ir_builder* ir_builder_new(const char *modulename);
+void ir_builder_delete(ir_builder*);
ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype);
-
-ir_value* ir_builder_get_global(ir_builder*, const char *fun);
-ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
-ir_value* ir_builder_get_field(ir_builder*, const char *fun);
-ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype);
-
-ir_value* ir_builder_get_va_count(ir_builder*);
-
-bool ir_builder_generate(code_t *, ir_builder *self, const char *filename);
-
-void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
-
+ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
+ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype);
+ir_value* ir_builder_get_va_count(ir_builder*);
+bool ir_builder_generate(code_t *, ir_builder *self, const char *filename);
+void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
/*
* This code assumes 32 bit floats while generating binary
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include <stdio.h>
-#include <stdlib.h>
+#include <ctype.h>
#include <string.h>
-#include <stdarg.h>
+#include <stdlib.h>
#include "gmqcc.h"
#include "lexer.h"
-
/*
* List of Keywords
*/
static char* *lex_filenames;
-void lexerror(lex_file *lex, const char *fmt, ...)
+static void lexerror(lex_file *lex, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (lex)
- con_vprintmsg(LVL_ERROR, lex->name, lex->sline, "parse error", fmt, ap);
+ con_vprintmsg(LVL_ERROR, lex->name, lex->sline, lex->column, "parse error", fmt, ap);
else
- con_vprintmsg(LVL_ERROR, "", 0, "parse error", fmt, ap);
+ con_vprintmsg(LVL_ERROR, "", 0, 0, "parse error", fmt, ap);
va_end(ap);
}
-bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
+static bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
{
bool r;
lex_ctx ctx;
#else
if (lex->tok.value)
vec_shrinkto(lex->tok.value, 0);
+
lex->tok.constval.t = 0;
- lex->tok.ctx.line = lex->sline;
- lex->tok.ctx.file = lex->name;
+ lex->tok.ctx.line = lex->sline;
+ lex->tok.ctx.file = lex->name;
+ lex->tok.ctx.column = lex->column;
#endif
}
#endif
memset(lex, 0, sizeof(*lex));
- lex->file = in;
- lex->name = util_strdup(file);
- lex->line = 1; /* we start counting at 1 */
-
+ lex->file = in;
+ lex->name = util_strdup(file);
+ lex->line = 1; /* we start counting at 1 */
+ lex->column = 0;
lex->peekpos = 0;
- lex->eof = false;
+ lex->eof = false;
vec_push(lex_filenames, lex->name);
return lex;
lex->open_string_length = len;
lex->open_string_pos = 0;
- lex->name = util_strdup(name ? name : "<string-source>");
- lex->line = 1; /* we start counting at 1 */
-
+ lex->name = util_strdup(name ? name : "<string-source>");
+ lex->line = 1; /* we start counting at 1 */
lex->peekpos = 0;
- lex->eof = false;
+ lex->eof = false;
+ lex->column = 0;
vec_push(lex_filenames, lex->name);
static int lex_fgetc(lex_file *lex)
{
- if (lex->file)
+ if (lex->file) {
+ lex->column++;
return fs_file_getc(lex->file);
+ }
if (lex->open_string) {
if (lex->open_string_pos >= lex->open_string_length)
return EOF;
+ lex->column++;
return lex->open_string[lex->open_string_pos++];
}
return EOF;
{
int c2, c3;
c2 = lex_fgetc(lex);
- if (!lex->push_line && c2 == '\n')
+ if (!lex->push_line && c2 == '\n') {
lex->line++;
+ lex->column = 0;
+ }
+
if (c2 != '?') {
lex_ungetch(lex, c2);
return old;
}
c3 = lex_fgetc(lex);
- if (!lex->push_line && c3 == '\n')
+ if (!lex->push_line && c3 == '\n') {
lex->line++;
+ lex->column = 0;
+ }
+
switch (c3) {
case '=': return '#';
case '/': return '\\';
static void lex_ungetch(lex_file *lex, int ch)
{
lex->peek[lex->peekpos++] = ch;
- if (!lex->push_line && ch == '\n')
+ lex->column--;
+ if (!lex->push_line && ch == '\n') {
lex->line--;
+ lex->column = 0;
+ }
}
/* classify characters
ch = 0;
else {
--u8len;
+ lex->column += u8len;
for (uc = 0; uc < u8len; ++uc)
lex_tokench(lex, u8buf[uc]);
/* the last character will be inserted with the tokench() call
*/
#ifndef GMQCC_LEXER_HDR
#define GMQCC_LEXER_HDR
-
typedef struct token_s token;
struct token_s {
char *name;
size_t line;
size_t sline; /* line at the start of a token */
+ size_t column;
int peek[256];
size_t peekpos;
unsigned int flags;
} oper_info;
-#define opid1(a) (a)
-#define opid2(a,b) ((a<<8)|b)
-#define opid3(a,b,c) ((a<<16)|(b<<8)|c)
+/*
+ * Explicit uint8_t casts since the left operand of shift operator cannot
+ * be negative, even though it won't happen, this supresses the future
+ * possibility.
+ */
+#define opid1(a) ((uint8_t)a)
+#define opid2(a,b) (((uint8_t)a<<8) |(uint8_t)b)
+#define opid3(a,b,c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c)
static const oper_info c_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
extern const oper_info *operators;
extern size_t operator_count;
-void lexerror(lex_file*, const char *fmt, ...);
+/*void lexerror(lex_file*, const char *fmt, ...);*/
#endif
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+
#include "gmqcc.h"
#include "lexer.h"
-#include <time.h>
/* TODO: cleanup this whole file .. it's a fuckign mess */
#define TYPE_ASM 1
#define TYPE_SRC 2
+
static const char *app_name;
-static void version() {
+static void version(void) {
con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
GMQCC_VERSION_MAJOR,
GMQCC_VERSION_MINOR,
);
}
-static int usage() {
+static int usage(void) {
con_out("usage: %s [options] [files...]", app_name);
con_out("options:\n"
" -h, --help show this help message\n"
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC;
+ OPTS_OPTION_BOOL(OPTION_STATISTICS) = true;
} else if (!strcmp(argarg, "qcc")) {
mem_d((void*)operators);
lex_cleanup();
- util_meminfo();
+ stat_info();
+
return retval;
}
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
#include "gmqcc.h"
+
unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
opts_cmd_t opts; /* command lien options */
-static void opts_setdefault() {
+static void opts_setdefault(void) {
memset(&opts, 0, sizeof(opts_cmd_t));
OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
if ((line = opts_ini_parse(ini, &opts_ini_load, &error)) != 0) {
/* there was a parse error with the ini file */
- con_printmsg(LVL_ERROR, file, line, "error", error);
+ con_printmsg(LVL_ERROR, file, line, 0 /*TODO: column for ini error*/, "error", error);
vec_free(error);
}
GMQCC_DEFINE_FLAG(VARIADIC_ARGS)
GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS)
GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
+ GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
#endif
/* warning flags */
GMQCC_DEFINE_FLAG(MAX_ARRAY_SIZE)
GMQCC_DEFINE_FLAG(ADD_INFO)
GMQCC_DEFINE_FLAG(CORRECTION)
+ GMQCC_DEFINE_FLAG(STATISTICS)
#endif
/* some cleanup so we don't have to */
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
+#include <string.h>
+#include <stdlib.h>
+
#include "gmqcc.h"
/*
* The PAK format uses a FOURCC concept for storing the magic ident within
* the header as a uint32_t.
*/
-#define PAK_FOURCC ((uint32_t)(('P' | ('A' << 8) | ('C' << 16) | ('K' << 24))))
+#define PAK_FOURCC ((uint32_t)(((uint8_t)'P'|((uint8_t)'A'<<8)|((uint8_t)'C'<<16)|((uint8_t)'K'<<24))))
typedef struct {
uint32_t magic; /* "PACK" */
return pak;
}
-pak_file_t *pak_open(const char *file, const char *mode) {
+static pak_file_t *pak_open(const char *file, const char *mode) {
if (!file || !mode)
return NULL;
return NULL;
}
-bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
+static bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
size_t itr;
if (!pak || !file)
/*
* Extraction abilities. These work as you expect them to.
*/
-bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
+static bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
pak_directory_t *dir = NULL;
unsigned char *dat = NULL;
char *local = NULL;
return true;
}
-bool pak_extract_all(pak_file_t *pak, const char *dir) {
+static bool pak_extract_all(pak_file_t *pak, const char *dir) {
size_t itr;
if (!fs_dir_make(dir))
* Insertion functions (the opposite of extraction). Yes for generating
* PAKs.
*/
-bool pak_insert_one(pak_file_t *pak, const char *file) {
+static bool pak_insert_one(pak_file_t *pak, const char *file) {
pak_directory_t dir;
unsigned char *dat;
FILE *fp;
return true;
}
-bool pak_close(pak_file_t *pak) {
+static bool pak_close(pak_file_t *pak) {
size_t itr;
if (!pak)
/* not possible */
pak_close(pak);
vec_free(files);
- util_meminfo();
+ stat_info();
+
return EXIT_SUCCESS;
}
pak_close(pak);
vec_free(files);
-
- util_meminfo();
+ stat_info();
return EXIT_SUCCESS;
}
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include <stdio.h>
-#include <stdarg.h>
+#include <string.h>
#include <math.h>
#include "gmqcc.h"
lex_file *lex;
int tok;
+ bool ast_cleaned;
+
ast_expression **globals;
ast_expression **fields;
ast_function **functions;
* parsing
*/
-bool parser_next(parser_t *parser)
+static bool parser_next(parser_t *parser)
{
/* lex_do kills the previous token */
parser->tok = lex_do(parser->lex);
blocks[i] = sy->out[vec_size(sy->out)+i].block;
asvalue[i] = (ast_value*)exprs[i];
- if (exprs[i]->expression.vtype == TYPE_NOEXPR &&
+ if (exprs[i]->vtype == TYPE_NOEXPR &&
!(i != 0 && op->id == opid2('?',':')) &&
!(i == 1 && op->id == opid1('.')))
{
}
#define NotSameType(T) \
- (exprs[0]->expression.vtype != exprs[1]->expression.vtype || \
- exprs[0]->expression.vtype != T)
+ (exprs[0]->vtype != exprs[1]->vtype || \
+ exprs[0]->vtype != T)
#define CanConstFold1(A) \
(ast_istype((A), ast_value) && ((ast_value*)(A))->hasvalue && (((ast_value*)(A))->cvq == CV_CONST) &&\
- (A)->expression.vtype != TYPE_FUNCTION)
+ (A)->vtype != TYPE_FUNCTION)
#define CanConstFold(A, B) \
(CanConstFold1(A) && CanConstFold1(B))
#define ConstV(i) (asvalue[(i)]->constval.vvec)
return false;
case opid1('.'):
- if (exprs[0]->expression.vtype == TYPE_VECTOR &&
- exprs[1]->expression.vtype == TYPE_NOEXPR)
+ if (exprs[0]->vtype == TYPE_VECTOR &&
+ exprs[1]->vtype == TYPE_NOEXPR)
{
if (exprs[1] == (ast_expression*)parser->const_vec[0])
out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
return false;
}
}
- else if (exprs[0]->expression.vtype == TYPE_ENTITY) {
- if (exprs[1]->expression.vtype != TYPE_FIELD) {
+ else if (exprs[0]->vtype == TYPE_ENTITY) {
+ if (exprs[1]->vtype != TYPE_FIELD) {
compile_error(ast_ctx(exprs[1]), "type error: right hand of member-operand should be an entity-field");
return false;
}
out = (ast_expression*)ast_entfield_new(ctx, exprs[0], exprs[1]);
}
- else if (exprs[0]->expression.vtype == TYPE_VECTOR) {
+ else if (exprs[0]->vtype == TYPE_VECTOR) {
compile_error(ast_ctx(exprs[1]), "vectors cannot be accessed this way");
return false;
}
break;
case opid1('['):
- if (exprs[0]->expression.vtype != TYPE_ARRAY &&
- !(exprs[0]->expression.vtype == TYPE_FIELD &&
- exprs[0]->expression.next->expression.vtype == TYPE_ARRAY))
+ if (exprs[0]->vtype != TYPE_ARRAY &&
+ !(exprs[0]->vtype == TYPE_FIELD &&
+ exprs[0]->next->vtype == TYPE_ARRAY))
{
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
compile_error(ast_ctx(exprs[0]), "cannot index value of type %s", ty1);
return false;
}
- if (exprs[1]->expression.vtype != TYPE_FLOAT) {
+ if (exprs[1]->vtype != TYPE_FLOAT) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
compile_error(ast_ctx(exprs[1]), "index must be of type float, not %s", ty1);
return false;
out = exprs[0];
break;
case opid2('-','P'):
- switch (exprs[0]->expression.vtype) {
+ switch (exprs[0]->vtype) {
case TYPE_FLOAT:
if (CanConstFold1(exprs[0]))
out = (ast_expression*)parser_const_float(parser, -ConstF(0));
break;
default:
compile_error(ctx, "invalid types used in expression: cannot negate type %s",
- type_name[exprs[0]->expression.vtype]);
+ type_name[exprs[0]->vtype]);
return false;
}
break;
case opid2('!','P'):
- switch (exprs[0]->expression.vtype) {
+ switch (exprs[0]->vtype) {
case TYPE_FLOAT:
if (CanConstFold1(exprs[0]))
out = (ast_expression*)parser_const_float(parser, !ConstF(0));
break;
default:
compile_error(ctx, "invalid types used in expression: cannot logically negate type %s",
- type_name[exprs[0]->expression.vtype]);
+ type_name[exprs[0]->vtype]);
return false;
}
break;
case opid1('+'):
- if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
- (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+ if (exprs[0]->vtype != exprs[1]->vtype ||
+ (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
{
compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
}
- switch (exprs[0]->expression.vtype) {
+ switch (exprs[0]->vtype) {
case TYPE_FLOAT:
if (CanConstFold(exprs[0], exprs[1]))
{
break;
default:
compile_error(ctx, "invalid types used in expression: cannot add type %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
};
break;
case opid1('-'):
- if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
- (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+ if (exprs[0]->vtype != exprs[1]->vtype ||
+ (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
{
compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
- type_name[exprs[1]->expression.vtype],
- type_name[exprs[0]->expression.vtype]);
+ type_name[exprs[1]->vtype],
+ type_name[exprs[0]->vtype]);
return false;
}
- switch (exprs[0]->expression.vtype) {
+ switch (exprs[0]->vtype) {
case TYPE_FLOAT:
if (CanConstFold(exprs[0], exprs[1]))
out = (ast_expression*)parser_const_float(parser, ConstF(0) - ConstF(1));
break;
default:
compile_error(ctx, "invalid types used in expression: cannot subtract type %s from %s",
- type_name[exprs[1]->expression.vtype],
- type_name[exprs[0]->expression.vtype]);
+ type_name[exprs[1]->vtype],
+ type_name[exprs[0]->vtype]);
return false;
};
break;
case opid1('*'):
- if (exprs[0]->expression.vtype != exprs[1]->expression.vtype &&
- !(exprs[0]->expression.vtype == TYPE_VECTOR &&
- exprs[1]->expression.vtype == TYPE_FLOAT) &&
- !(exprs[1]->expression.vtype == TYPE_VECTOR &&
- exprs[0]->expression.vtype == TYPE_FLOAT)
+ if (exprs[0]->vtype != exprs[1]->vtype &&
+ !(exprs[0]->vtype == TYPE_VECTOR &&
+ exprs[1]->vtype == TYPE_FLOAT) &&
+ !(exprs[1]->vtype == TYPE_VECTOR &&
+ exprs[0]->vtype == TYPE_FLOAT)
)
{
compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
- type_name[exprs[1]->expression.vtype],
- type_name[exprs[0]->expression.vtype]);
+ type_name[exprs[1]->vtype],
+ type_name[exprs[0]->vtype]);
return false;
}
- switch (exprs[0]->expression.vtype) {
+ switch (exprs[0]->vtype) {
case TYPE_FLOAT:
- if (exprs[1]->expression.vtype == TYPE_VECTOR)
+ if (exprs[1]->vtype == TYPE_VECTOR)
{
if (CanConstFold(exprs[0], exprs[1]))
out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(1), ConstF(0)));
}
break;
case TYPE_VECTOR:
- if (exprs[1]->expression.vtype == TYPE_FLOAT)
+ if (exprs[1]->vtype == TYPE_FLOAT)
{
if (CanConstFold(exprs[0], exprs[1]))
out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), ConstF(1)));
if (!vec.y && !vec.z) { /* 'n 0 0' * v */
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
out = (ast_expression*)ast_member_new(ctx, exprs[1], 0, NULL);
- out->expression.node.keep = false;
+ out->node.keep = false;
((ast_member*)out)->rvalue = true;
if (vec.x != 1)
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.x), out);
else if (!vec.x && !vec.z) { /* '0 n 0' * v */
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
out = (ast_expression*)ast_member_new(ctx, exprs[1], 1, NULL);
- out->expression.node.keep = false;
+ out->node.keep = false;
((ast_member*)out)->rvalue = true;
if (vec.y != 1)
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.y), out);
else if (!vec.x && !vec.y) { /* '0 n 0' * v */
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
out = (ast_expression*)ast_member_new(ctx, exprs[1], 2, NULL);
- out->expression.node.keep = false;
+ out->node.keep = false;
((ast_member*)out)->rvalue = true;
if (vec.z != 1)
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, (ast_expression*)parser_const_float(parser, vec.z), out);
if (!vec.y && !vec.z) { /* v * 'n 0 0' */
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
out = (ast_expression*)ast_member_new(ctx, exprs[0], 0, NULL);
- out->expression.node.keep = false;
+ out->node.keep = false;
((ast_member*)out)->rvalue = true;
if (vec.x != 1)
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.x));
else if (!vec.x && !vec.z) { /* v * '0 n 0' */
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
out = (ast_expression*)ast_member_new(ctx, exprs[0], 1, NULL);
- out->expression.node.keep = false;
+ out->node.keep = false;
((ast_member*)out)->rvalue = true;
if (vec.y != 1)
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.y));
else if (!vec.x && !vec.y) { /* v * '0 n 0' */
++opts_optimizationcount[OPTIM_VECTOR_COMPONENTS];
out = (ast_expression*)ast_member_new(ctx, exprs[0], 2, NULL);
- out->expression.node.keep = false;
+ out->node.keep = false;
((ast_member*)out)->rvalue = true;
if (vec.z != 1)
out = (ast_expression*)ast_binary_new(ctx, INSTR_MUL_F, out, (ast_expression*)parser_const_float(parser, vec.z));
break;
default:
compile_error(ctx, "invalid types used in expression: cannot multiply types %s and %s",
- type_name[exprs[1]->expression.vtype],
- type_name[exprs[0]->expression.vtype]);
+ type_name[exprs[1]->vtype],
+ type_name[exprs[0]->vtype]);
return false;
};
break;
case opid1('/'):
- if (exprs[1]->expression.vtype != TYPE_FLOAT) {
+ if (exprs[1]->vtype != TYPE_FLOAT) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
compile_error(ctx, "invalid types used in expression: cannot divide tyeps %s and %s", ty1, ty2);
return false;
}
- if (exprs[0]->expression.vtype == TYPE_FLOAT) {
+ if (exprs[0]->vtype == TYPE_FLOAT) {
if (CanConstFold(exprs[0], exprs[1]))
out = (ast_expression*)parser_const_float(parser, ConstF(0) / ConstF(1));
else
out = (ast_expression*)ast_binary_new(ctx, INSTR_DIV_F, exprs[0], exprs[1]);
}
- else if (exprs[0]->expression.vtype == TYPE_VECTOR) {
+ else if (exprs[0]->vtype == TYPE_VECTOR) {
if (CanConstFold(exprs[0], exprs[1]))
out = (ast_expression*)parser_const_vector(parser, vec3_mulvf(ConstV(0), 1.0/ConstF(1)));
else {
case opid1('%'):
if (NotSameType(TYPE_FLOAT)) {
compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
}
if (CanConstFold(exprs[0], exprs[1])) {
case opid1('&'):
if (NotSameType(TYPE_FLOAT)) {
compile_error(ctx, "invalid types used in expression: cannot perform bit operations between types %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
}
if (CanConstFold(exprs[0], exprs[1]))
case opid2('>','>'):
if (CanConstFold(exprs[0], exprs[1]) && ! NotSameType(TYPE_FLOAT)) {
if (op->id == opid2('<','<'))
- out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) << (int)(ConstF(1))));
+ out = (ast_expression*)parser_const_float(parser, (double)((unsigned int)(ConstF(0)) << (unsigned int)(ConstF(1))));
else
- out = (ast_expression*)parser_const_float(parser, (double)((int)(ConstF(0)) >> (int)(ConstF(1))));
+ out = (ast_expression*)parser_const_float(parser, (double)((unsigned int)(ConstF(0)) >> (unsigned int)(ConstF(1))));
break;
}
case opid3('<','<','='):
return false;
}
for (i = 0; i < 2; ++i) {
- if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->expression.vtype == TYPE_VECTOR) {
+ if (OPTS_FLAG(CORRECT_LOGIC) && exprs[i]->vtype == TYPE_VECTOR) {
out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_V, exprs[i]);
if (!out) break;
out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
break;
}
}
- else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->expression.vtype == TYPE_STRING) {
+ else if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && exprs[i]->vtype == TYPE_STRING) {
out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_S, exprs[i]);
if (!out) break;
out = (ast_expression*)ast_unary_new(ctx, INSTR_NOT_F, out);
generated_op += INSTR_LE;
if (NotSameType(TYPE_FLOAT)) {
compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
}
out = (ast_expression*)ast_binary_new(ctx, generated_op, exprs[0], exprs[1]);
break;
case opid2('!', '='):
- if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) {
+ if (exprs[0]->vtype != exprs[1]->vtype) {
compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
}
- out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]);
+ out = (ast_expression*)ast_binary_new(ctx, type_ne_instr[exprs[0]->vtype], exprs[0], exprs[1]);
break;
case opid2('=', '='):
- if (exprs[0]->expression.vtype != exprs[1]->expression.vtype) {
+ if (exprs[0]->vtype != exprs[1]->vtype) {
compile_error(ctx, "invalid types used in expression: cannot perform comparison between types %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
}
- out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->expression.vtype], exprs[0], exprs[1]);
+ out = (ast_expression*)ast_binary_new(ctx, type_eq_instr[exprs[0]->vtype], exprs[0], exprs[1]);
break;
case opid1('='):
if (ast_istype(exprs[0], ast_entfield)) {
ast_expression *field = ((ast_entfield*)exprs[0])->field;
if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
- exprs[0]->expression.vtype == TYPE_FIELD &&
- exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+ exprs[0]->vtype == TYPE_FIELD &&
+ exprs[0]->next->vtype == TYPE_VECTOR)
{
assignop = type_storep_instr[TYPE_VECTOR];
}
else
- assignop = type_storep_instr[exprs[0]->expression.vtype];
- if (assignop == VINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
+ assignop = type_storep_instr[exprs[0]->vtype];
+ if (assignop == VINSTR_END || !ast_compare_type(field->next, exprs[1]))
{
- ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
+ ast_type_to_string(field->next, ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) &&
- field->expression.next->expression.vtype == TYPE_FUNCTION &&
- exprs[1]->expression.vtype == TYPE_FUNCTION)
+ field->next->vtype == TYPE_FUNCTION &&
+ exprs[1]->vtype == TYPE_FUNCTION)
{
(void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES,
"invalid types in assignment: cannot assign %s to %s", ty2, ty1);
else
{
if (OPTS_FLAG(ADJUST_VECTOR_FIELDS) &&
- exprs[0]->expression.vtype == TYPE_FIELD &&
- exprs[0]->expression.next->expression.vtype == TYPE_VECTOR)
+ exprs[0]->vtype == TYPE_FIELD &&
+ exprs[0]->next->vtype == TYPE_VECTOR)
{
assignop = type_store_instr[TYPE_VECTOR];
}
else {
- assignop = type_store_instr[exprs[0]->expression.vtype];
+ assignop = type_store_instr[exprs[0]->vtype];
}
if (assignop == VINSTR_END) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
if (OPTS_FLAG(ASSIGN_FUNCTION_TYPES) &&
- exprs[0]->expression.vtype == TYPE_FUNCTION &&
- exprs[1]->expression.vtype == TYPE_FUNCTION)
+ exprs[0]->vtype == TYPE_FUNCTION &&
+ exprs[1]->vtype == TYPE_FUNCTION)
{
(void)!compile_warning(ctx, WARN_ASSIGN_FUNCTION_TYPES,
"invalid types in assignment: cannot assign %s to %s", ty2, ty1);
case opid3('+','+','P'):
case opid3('-','-','P'):
/* prefix ++ */
- if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+ if (exprs[0]->vtype != TYPE_FLOAT) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
compile_error(ast_ctx(exprs[0]), "invalid type for prefix increment: %s", ty1);
return false;
case opid3('S','+','+'):
case opid3('S','-','-'):
/* prefix ++ */
- if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+ if (exprs[0]->vtype != TYPE_FLOAT) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
compile_error(ast_ctx(exprs[0]), "invalid type for suffix increment: %s", ty1);
return false;
break;
case opid2('+','='):
case opid2('-','='):
- if (exprs[0]->expression.vtype != exprs[1]->expression.vtype ||
- (exprs[0]->expression.vtype != TYPE_VECTOR && exprs[0]->expression.vtype != TYPE_FLOAT) )
+ if (exprs[0]->vtype != exprs[1]->vtype ||
+ (exprs[0]->vtype != TYPE_VECTOR && exprs[0]->vtype != TYPE_FLOAT) )
{
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
}
if (ast_istype(exprs[0], ast_entfield))
- assignop = type_storep_instr[exprs[0]->expression.vtype];
+ assignop = type_storep_instr[exprs[0]->vtype];
else
- assignop = type_store_instr[exprs[0]->expression.vtype];
- switch (exprs[0]->expression.vtype) {
+ assignop = type_store_instr[exprs[0]->vtype];
+ switch (exprs[0]->vtype) {
case TYPE_FLOAT:
out = (ast_expression*)ast_binstore_new(ctx, assignop,
(op->id == opid2('+','=') ? INSTR_ADD_F : INSTR_SUB_F),
break;
default:
compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
};
break;
case opid2('*','='):
case opid2('/','='):
- if (exprs[1]->expression.vtype != TYPE_FLOAT ||
- !(exprs[0]->expression.vtype == TYPE_FLOAT ||
- exprs[0]->expression.vtype == TYPE_VECTOR))
+ if (exprs[1]->vtype != TYPE_FLOAT ||
+ !(exprs[0]->vtype == TYPE_FLOAT ||
+ exprs[0]->vtype == TYPE_VECTOR))
{
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
}
if (ast_istype(exprs[0], ast_entfield))
- assignop = type_storep_instr[exprs[0]->expression.vtype];
+ assignop = type_storep_instr[exprs[0]->vtype];
else
- assignop = type_store_instr[exprs[0]->expression.vtype];
- switch (exprs[0]->expression.vtype) {
+ assignop = type_store_instr[exprs[0]->vtype];
+ switch (exprs[0]->vtype) {
case TYPE_FLOAT:
out = (ast_expression*)ast_binstore_new(ctx, assignop,
(op->id == opid2('*','=') ? INSTR_MUL_F : INSTR_DIV_F),
break;
default:
compile_error(ctx, "invalid types used in expression: cannot add or subtract type %s and %s",
- type_name[exprs[0]->expression.vtype],
- type_name[exprs[1]->expression.vtype]);
+ type_name[exprs[0]->vtype],
+ type_name[exprs[1]->vtype]);
return false;
};
break;
compile_error(ctx, "assignment to constant `%s`", asvalue[0]->name);
}
if (ast_istype(exprs[0], ast_entfield))
- assignop = type_storep_instr[exprs[0]->expression.vtype];
+ assignop = type_storep_instr[exprs[0]->vtype];
else
- assignop = type_store_instr[exprs[0]->expression.vtype];
+ assignop = type_store_instr[exprs[0]->vtype];
out = (ast_expression*)ast_binstore_new(ctx, assignop,
(op->id == opid2('&','=') ? INSTR_BITAND : INSTR_BITOR),
exprs[0], exprs[1]);
return false;
}
if (ast_istype(exprs[0], ast_entfield))
- assignop = type_storep_instr[exprs[0]->expression.vtype];
+ assignop = type_storep_instr[exprs[0]->vtype];
else
- assignop = type_store_instr[exprs[0]->expression.vtype];
+ assignop = type_store_instr[exprs[0]->vtype];
out = (ast_expression*)ast_binary_new(ctx, INSTR_BITAND, exprs[0], exprs[1]);
if (!out)
return false;
break;
case opid2('~', 'P'):
- if (exprs[0]->expression.vtype != TYPE_FLOAT) {
+ if (exprs[0]->vtype != TYPE_FLOAT) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
compile_error(ast_ctx(exprs[0]), "invalid type for bit not: %s", ty1);
return false;
if (ast_istype(fun, ast_value)) {
funval = (ast_value*)fun;
- if ((fun->expression.flags & AST_FLAG_VARIADIC) &&
+ if ((fun->flags & AST_FLAG_VARIADIC) &&
!(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
{
call->va_count = (ast_expression*)parser_const_float(parser, (double)paramcount);
/* overwrite fid, the function, with a call */
sy->out[fid] = syexp(call->expression.node.context, (ast_expression*)call);
- if (fun->expression.vtype != TYPE_FUNCTION) {
- parseerror(parser, "not a function (%s)", type_name[fun->expression.vtype]);
+ if (fun->vtype != TYPE_FUNCTION) {
+ parseerror(parser, "not a function (%s)", type_name[fun->vtype]);
return false;
}
- if (!fun->expression.next) {
+ if (!fun->next) {
parseerror(parser, "could not determine function return type");
return false;
} else {
ast_value *fval = (ast_istype(fun, ast_value) ? ((ast_value*)fun) : NULL);
- if (fun->expression.flags & AST_FLAG_DEPRECATED) {
+ if (fun->flags & AST_FLAG_DEPRECATED) {
if (!fval) {
return !parsewarning(parser, WARN_DEPRECATED,
"call to function (which is marked deprecated)\n",
ast_ctx(fun).line);
}
- if (vec_size(fun->expression.params) != paramcount &&
- !((fun->expression.flags & AST_FLAG_VARIADIC) &&
- vec_size(fun->expression.params) < paramcount))
+ if (vec_size(fun->params) != paramcount &&
+ !((fun->flags & AST_FLAG_VARIADIC) &&
+ vec_size(fun->params) < paramcount))
{
- const char *fewmany = (vec_size(fun->expression.params) > paramcount) ? "few" : "many";
+ const char *fewmany = (vec_size(fun->params) > paramcount) ? "few" : "many";
if (fval)
return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
"too %s parameters for call to %s: expected %i, got %i\n"
" -> `%s` has been declared here: %s:%i",
- fewmany, fval->name, (int)vec_size(fun->expression.params), (int)paramcount,
+ fewmany, fval->name, (int)vec_size(fun->params), (int)paramcount,
fval->name, ast_ctx(fun).file, (int)ast_ctx(fun).line);
else
return !parsewarning(parser, WARN_INVALID_PARAMETER_COUNT,
"too %s parameters for function call: expected %i, got %i\n"
" -> it has been declared here: %s:%i",
- fewmany, (int)vec_size(fun->expression.params), (int)paramcount,
+ fewmany, (int)vec_size(fun->params), (int)paramcount,
ast_ctx(fun).file, (int)ast_ctx(fun).line);
}
}
/* When adding more intrinsics, fix the above condition */
prev = NULL;
}
- if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
+ if (prev && prev->vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
{
var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
} else {
}
parser->lex->flags.noops = true;
- if (!vec_size(sy.out)) {
- parseerror(parser, "empty expression");
+ if (vec_size(sy.out) != 1) {
+ parseerror(parser, "expression with not 1 but %lu output values...", (unsigned long) vec_size(sy.out));
expr = NULL;
} else
expr = sy.out[0].out;
ast_unary *unary;
ast_expression *prev;
- if (cond->expression.vtype == TYPE_VOID || cond->expression.vtype >= TYPE_VARIANT) {
+ if (cond->vtype == TYPE_VOID || cond->vtype >= TYPE_VARIANT) {
char ty[1024];
ast_type_to_string(cond, ty, sizeof(ty));
compile_error(ast_ctx(cond), "invalid type for if() condition: %s", ty);
}
- if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->expression.vtype == TYPE_STRING)
+ if (OPTS_FLAG(FALSE_EMPTY_STRINGS) && cond->vtype == TYPE_STRING)
{
prev = cond;
cond = (ast_expression*)ast_unary_new(ast_ctx(cond), INSTR_NOT_S, cond);
}
ifnot = !ifnot;
}
- else if (OPTS_FLAG(CORRECT_LOGIC) && cond->expression.vtype == TYPE_VECTOR)
+ else if (OPTS_FLAG(CORRECT_LOGIC) && cond->vtype == TYPE_VECTOR)
{
/* vector types need to be cast to true booleans */
ast_binary *bin = (ast_binary*)cond;
/* closing paren */
if (parser->tok != ')') {
parseerror(parser, "expected closing paren after 'if' condition");
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
/* parse into the 'then' branch */
if (!parser_next(parser)) {
parseerror(parser, "expected statement for on-true branch of 'if'");
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
if (!parse_statement_or_block(parser, &ontrue)) {
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
if (!ontrue)
if (!parser_next(parser)) {
parseerror(parser, "expected on-false branch after 'else'");
ast_delete(ontrue);
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
if (!parse_statement_or_block(parser, &onfalse)) {
ast_delete(ontrue);
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
}
/* closing paren */
if (parser->tok != ')') {
parseerror(parser, "expected closing paren after 'while' condition");
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
/* parse into the 'then' branch */
if (!parser_next(parser)) {
parseerror(parser, "expected while-loop body");
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
if (!parse_statement_or_block(parser, &ontrue)) {
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
cond = process_condition(parser, cond, &ifnot);
if (!cond) {
- ast_delete(ontrue);
+ ast_unref(ontrue);
return false;
}
aloop = ast_loop_new(ctx, NULL, cond, ifnot, NULL, false, NULL, ontrue);
if (parser->tok != ')') {
parseerror(parser, "expected closing paren after 'while' condition");
ast_delete(ontrue);
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
/* parse on */
if (!parser_next(parser) || parser->tok != ';') {
parseerror(parser, "expected semicolon after condition");
ast_delete(ontrue);
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
if (!parser_next(parser)) {
parseerror(parser, "parse error");
ast_delete(ontrue);
- ast_delete(cond);
+ ast_unref(cond);
return false;
}
ast_expression *initexpr, *cond, *increment, *ontrue;
ast_value *typevar;
- bool retval = true;
bool ifnot = false;
lex_ctx ctx = parser_ctx(parser);
aloop = ast_loop_new(ctx, initexpr, cond, ifnot, NULL, false, increment, ontrue);
*out = (ast_expression*)aloop;
- if (!parser_leaveblock(parser))
- retval = false;
- return retval;
+ if (!parser_leaveblock(parser)) {
+ ast_delete(aloop);
+ return false;
+ }
+ return true;
onerr:
- if (initexpr) ast_delete(initexpr);
- if (cond) ast_delete(cond);
- if (increment) ast_delete(increment);
+ if (initexpr) ast_unref(initexpr);
+ if (cond) ast_unref(cond);
+ if (increment) ast_unref(increment);
(void)!parser_leaveblock(parser);
return false;
}
static bool parse_return(parser_t *parser, ast_block *block, ast_expression **out)
{
- ast_expression *exp = NULL;
- ast_return *ret = NULL;
+ ast_expression *exp = NULL;
+ ast_expression *var = NULL;
+ ast_return *ret = NULL;
+ ast_value *retval = parser->function->return_value;
ast_value *expected = parser->function->vtype;
lex_ctx ctx = parser_ctx(parser);
return false;
}
+ /* return assignments */
+ if (parser->tok == '=') {
+ if (!OPTS_FLAG(RETURN_ASSIGNMENTS)) {
+ parseerror(parser, "return assignments not activated, try using -freturn-assigments");
+ return false;
+ }
+
+ if (type_store_instr[expected->expression.next->vtype] == VINSTR_END) {
+ char ty1[1024];
+ ast_type_to_string(expected->expression.next, ty1, sizeof(ty1));
+ parseerror(parser, "invalid return type: `%s'", ty1);
+ return false;
+ }
+
+ if (!parser_next(parser)) {
+ parseerror(parser, "expected return assignment expression");
+ return false;
+ }
+
+ if (!(exp = parse_expression_leave(parser, false, false, false)))
+ return false;
+
+ /* prepare the return value */
+ if (!retval) {
+ retval = ast_value_new(ctx, "#LOCAL_RETURN", TYPE_VOID);
+ ast_type_adopt(retval, expected->expression.next);
+ parser->function->return_value = retval;
+ }
+
+ if (!ast_compare_type(exp, (ast_expression*)retval)) {
+ char ty1[1024], ty2[1024];
+ ast_type_to_string(exp, ty1, sizeof(ty1));
+ ast_type_to_string(&retval->expression, ty2, sizeof(ty2));
+ parseerror(parser, "invalid type for return value: `%s', expected `%s'", ty1, ty2);
+ }
+
+ /* store to 'return' local variable */
+ var = (ast_expression*)ast_store_new(
+ ctx,
+ type_store_instr[expected->expression.next->vtype],
+ (ast_expression*)retval, exp);
+
+ if (!var) {
+ ast_unref(exp);
+ return false;
+ }
+
+ if (parser->tok != ';')
+ parseerror(parser, "missing semicolon after return assignment");
+ else if (!parser_next(parser))
+ parseerror(parser, "parse error after return assignment");
+
+ *out = var;
+ return true;
+ }
+
if (parser->tok != ';') {
exp = parse_expression(parser, false, false);
if (!exp)
return false;
- if (exp->expression.vtype != TYPE_NIL &&
- exp->expression.vtype != expected->expression.next->expression.vtype)
+ if (exp->vtype != TYPE_NIL &&
+ exp->vtype != ((ast_expression*)expected)->next->vtype)
{
parseerror(parser, "return with invalid expression");
}
ret = ast_return_new(ctx, exp);
if (!ret) {
- ast_delete(exp);
+ ast_unref(exp);
return false;
}
} else {
if (!parser_next(parser))
parseerror(parser, "parse error");
- if (expected->expression.next->expression.vtype != TYPE_VOID) {
+
+ if (!retval && expected->expression.next->vtype != TYPE_VOID)
+ {
(void)!parsewarning(parser, WARN_MISSING_RETURN_VALUES, "return without value");
}
- ret = ast_return_new(ctx, NULL);
+ ret = ast_return_new(ctx, (ast_expression*)retval);
}
*out = (ast_expression*)ret;
return true;
}
return parse_typedef(parser);
}
- parseerror(parser, "Unexpected keyword");
+ parseerror(parser, "Unexpected keyword: `%s'", parser_tokval(parser));
return false;
}
else if (parser->tok == '{')
/* qc allows the use of not-yet-declared functions here
* - this automatically creates a prototype */
ast_value *thinkfunc;
- ast_expression *functype = fld_think->expression.next;
+ ast_expression *functype = fld_think->next;
- thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->expression.vtype);
+ thinkfunc = ast_value_new(parser_ctx(parser), parser_tokval(parser), functype->vtype);
if (!thinkfunc) { /* || !ast_type_adopt(thinkfunc, functype)*/
ast_unref(framenum);
parseerror(parser, "failed to create implicit prototype for `%s`", parser_tokval(parser));
if (param->expression.vtype != TYPE_VECTOR &&
(param->expression.vtype != TYPE_FIELD ||
- param->expression.next->expression.vtype != TYPE_VECTOR))
+ param->expression.next->vtype != TYPE_VECTOR))
{
continue;
}
}
vec_push(func->blocks, block);
+
parser->function = old;
if (!parser_leaveblock(parser))
ast_store *st;
int assignop = type_store_instr[value->expression.vtype];
- if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+ if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
assignop = INSTR_STORE_V;
subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
ast_store *st;
int assignop = type_storep_instr[value->expression.vtype];
- if (value->expression.vtype == TYPE_FIELD && value->expression.next->expression.vtype == TYPE_VECTOR)
+ if (value->expression.vtype == TYPE_FIELD && value->expression.next->vtype == TYPE_VECTOR)
assignop = INSTR_STOREP_V;
subscript = ast_array_index_new(ctx, (ast_expression*)array, (ast_expression*)parser_const_float(parser, from));
/* for the sake of less code we parse-in in this function */
if (!parser_next(parser)) {
+ ast_delete(var);
parseerror(parser, "expected parameter list");
return NULL;
}
/* parse on */
if (!parser_next(parser)) {
ast_delete(var);
+ mem_d(name);
parseerror(parser, "error after variable or field declaration");
return NULL;
}
if (parser->tok == '[') {
wasarray = true;
var = parse_arraysize(parser, var);
- if (!var)
+ if (!var) {
+ if (name) mem_d(name);
return NULL;
+ }
}
/* This is the point where we can turn it into a field */
while (parser->tok == '(') {
var = parse_parameter_list(parser, var);
if (!var) {
- if (name)
- mem_d((void*)name);
+ if (name) mem_d(name);
return NULL;
}
}
if (name) {
if (!ast_value_set_name(var, name)) {
ast_delete(var);
+ mem_d(name);
parseerror(parser, "internal error: failed to set name");
return NULL;
}
/* free the name, ast_value_set_name duplicates */
- mem_d((void*)name);
+ mem_d(name);
}
return var;
{
/* deal with other globals */
old = parser_find_global(parser, var->name);
- if (old && var->expression.vtype == TYPE_FUNCTION && old->expression.vtype == TYPE_FUNCTION)
+ if (old && var->expression.vtype == TYPE_FUNCTION && old->vtype == TYPE_FUNCTION)
{
/* This is a function which had a prototype */
if (!ast_istype(old, ast_value)) {
if (var->expression.vtype == TYPE_VECTOR)
isvector = true;
else if (var->expression.vtype == TYPE_FIELD &&
- var->expression.next->expression.vtype == TYPE_VECTOR)
+ var->expression.next->vtype == TYPE_VECTOR)
isvector = true;
if (isvector) {
return false;
}
- if (var->expression.vtype != find->expression.vtype) {
+ if (var->expression.vtype != find->vtype) {
char ty1[1024];
char ty2[1024];
}
else if (!localblock && !nofields &&
var->expression.vtype == TYPE_FIELD &&
- var->expression.next->expression.vtype == TYPE_ARRAY)
+ var->expression.next->vtype == TYPE_ARRAY)
{
char name[1024];
ast_expression *telem;
}
}
- if (parser->tok != '{') {
+ if (parser->tok != '{' || var->expression.vtype != TYPE_FUNCTION) {
if (parser->tok != '=') {
parseerror(parser, "missing semicolon or initializer, got: `%s`", parser_tokval(parser));
break;
break;
}
}
- else if (parser->tok == '{' || parser->tok == '[')
+ else if (var->expression.vtype == TYPE_ARRAY && parser->tok == '{')
+ {
+ if (localblock) {
+ /* Note that fteqcc and most others don't even *have*
+ * local arrays, so this is not a high priority.
+ */
+ parseerror(parser, "TODO: initializers for local arrays");
+ break;
+ }
+ /*
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels);
+*/
+ parseerror(parser, "TODO: initializing global arrays is not supported yet!");
+ break;
+ }
+ else if (var->expression.vtype == TYPE_FUNCTION && (parser->tok == '{' || parser->tok == '['))
{
if (localblock) {
parseerror(parser, "cannot declare functions within functions");
}
vec_free(sy.out);
vec_free(sy.ops);
+ vec_free(sy.argc);
var->cvq = cvq;
}
}
if (!ast_istype(parser->fields[i], ast_value))
continue;
value = (ast_value*)(parser->fields[i]);
- switch (value->expression.next->expression.vtype) {
+ switch (value->expression.next->vtype) {
case TYPE_FLOAT: crc = progdefs_crc_both(crc, "\tfloat\t"); break;
case TYPE_VECTOR: crc = progdefs_crc_both(crc, "\tvec3_t\t"); break;
case TYPE_STRING: crc = progdefs_crc_both(crc, "\tstring_t\t"); break;
mem_d(parser);
return NULL;
}
-
for (i = 0; i < operator_count; ++i) {
if (operators[i].id == opid1('=')) {
return parser;
}
-bool parser_compile(parser_t *parser)
+static bool parser_compile(parser_t *parser)
{
/* initial lexer/parser state */
parser->lex->flags.noops = true;
return parser_compile(parser);
}
-void parser_cleanup(parser_t *parser)
+static void parser_remove_ast(parser_t *parser)
{
size_t i;
+ if (parser->ast_cleaned)
+ return;
+ parser->ast_cleaned = true;
for (i = 0; i < vec_size(parser->accessors); ++i) {
ast_delete(parser->accessors[i]->constval.vfunc);
parser->accessors[i]->constval.vfunc = NULL;
ast_value_delete(parser->const_vec[2]);
util_htdel(parser->aliases);
-
intrin_intrinsics_destroy(parser);
+}
+
+void parser_cleanup(parser_t *parser)
+{
+ parser_remove_ast(parser);
+ code_cleanup(parser->code);
mem_d(parser);
}
ast_expression *subtype;
field->hasvalue = true;
subtype = field->expression.next;
- ifld = ir_builder_create_field(ir, field->name, subtype->expression.vtype);
- if (subtype->expression.vtype == TYPE_FIELD)
- ifld->fieldtype = subtype->expression.next->expression.vtype;
- else if (subtype->expression.vtype == TYPE_FUNCTION)
- ifld->outtype = subtype->expression.next->expression.vtype;
+ ifld = ir_builder_create_field(ir, field->name, subtype->vtype);
+ if (subtype->vtype == TYPE_FIELD)
+ ifld->fieldtype = subtype->next->vtype;
+ else if (subtype->vtype == TYPE_FUNCTION)
+ ifld->outtype = subtype->next->vtype;
(void)!ir_value_set_field(field->ir_v, ifld);
}
}
}
for (i = 0; i < vec_size(parser->fields); ++i) {
ast_value *asvalue;
- asvalue = (ast_value*)(parser->fields[i]->expression.next);
+ asvalue = (ast_value*)(parser->fields[i]->next);
if (!ast_istype((ast_expression*)asvalue, ast_value))
continue;
return false;
}
}
+
+ generate_checksum(parser);
if (OPTS_OPTION_BOOL(OPTION_DUMP))
ir_builder_dump(ir, con_out);
for (i = 0; i < vec_size(parser->functions); ++i) {
return false;
}
}
+ parser_remove_ast(parser);
if (compile_Werrors) {
con_out("*** there were warnings treated as errors\n");
if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
ir_builder_dump(ir, con_out);
- generate_checksum(parser);
-
if (!ir_builder_generate(parser->code, ir, output)) {
con_out("*** failed to generate output file\n");
ir_builder_delete(ir);
return false;
}
}
-
ir_builder_delete(ir);
return retval;
}
--- /dev/null
+/*
+ * Copyright (C) 2012, 2013
+ * 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.
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "gmqcc.h"
+
+/*
+ * GMQCC performs tons of allocations, constructions, and crazyness
+ * all around. When trying to optimizes systems, or just get fancy
+ * statistics out of the compiler, it's often printf mess. This file
+ * implements the statistics system of the compiler. I.E the allocator
+ * we use to track allocations, and other systems of interest.
+ */
+#define ST_SIZE 1024
+
+typedef struct stat_mem_block_s {
+ const char *file;
+ size_t line;
+ size_t size;
+ struct stat_mem_block_s *next;
+ struct stat_mem_block_s *prev;
+} stat_mem_block_t;
+
+typedef struct {
+ size_t key;
+ size_t value;
+} stat_size_entry_t, **stat_size_table_t;
+
+static uint64_t stat_mem_allocated = 0;
+static uint64_t stat_mem_deallocated = 0;
+static uint64_t stat_mem_allocated_total = 0;
+static uint64_t stat_mem_deallocated_total = 0;
+static uint64_t stat_mem_high = 0;
+static uint64_t stat_mem_peak = 0;
+static uint64_t stat_used_strdups = 0;
+static uint64_t stat_used_vectors = 0;
+static uint64_t stat_used_hashtables = 0;
+static uint64_t stat_type_vectors = 0;
+static uint64_t stat_type_hashtables = 0;
+static stat_size_table_t stat_size_vectors = NULL;
+static stat_size_table_t stat_size_hashtables = NULL;
+static stat_mem_block_t *stat_mem_block_root = NULL;
+
+/*
+ * A tiny size_t key-value hashtbale for tracking vector and hashtable
+ * sizes. We can use it for other things too, if we need to. This is
+ * very TIGHT, and efficent in terms of space though.
+ */
+static stat_size_table_t stat_size_new(void) {
+ return (stat_size_table_t)memset(
+ mem_a(sizeof(stat_size_entry_t*) * ST_SIZE),
+ 0, ST_SIZE * sizeof(stat_size_entry_t*)
+ );
+}
+
+static void stat_size_del(stat_size_table_t table) {
+ size_t i = 0;
+ for (; i < ST_SIZE; i++) if(table[i]) mem_d(table[i]);
+ mem_d(table);
+}
+
+static stat_size_entry_t *stat_size_get(stat_size_table_t table, size_t key) {
+ size_t hash = (key % ST_SIZE);
+ while (table[hash] && table[hash]->key != key)
+ hash = (hash + 1) % ST_SIZE;
+ return table[hash];
+}
+static void stat_size_put(stat_size_table_t table, size_t key, size_t value) {
+ size_t hash = (key % ST_SIZE);
+ while (table[hash] && table[hash]->key != key)
+ hash = (hash + 1) % ST_SIZE;
+ table[hash] = (stat_size_entry_t*)mem_a(sizeof(stat_size_entry_t));
+ table[hash]->key = key;
+ table[hash]->value = value;
+}
+
+/*
+ * A basic header of information wrapper allocator. Simply stores
+ * information as a header, returns the memory + 1 past it, can be
+ * retrieved again with - 1. Where type is stat_mem_block_t*.
+ */
+void *stat_mem_allocate(size_t size, size_t line, const char *file) {
+ stat_mem_block_t *info = (stat_mem_block_t*)malloc(sizeof(stat_mem_block_t) + size);
+ void *data = (void*)(info + 1);
+
+ if(!info)
+ return NULL;
+
+ info->line = line;
+ info->size = size;
+ info->file = file;
+ info->prev = NULL;
+ info->next = stat_mem_block_root;
+
+ if (stat_mem_block_root)
+ stat_mem_block_root->prev = info;
+
+ stat_mem_block_root = info;
+ stat_mem_allocated += size;
+ stat_mem_high += size;
+ stat_mem_allocated_total ++;
+
+ if (stat_mem_high > stat_mem_peak)
+ stat_mem_peak = stat_mem_high;
+
+ return data;
+}
+
+void stat_mem_deallocate(void *ptr) {
+ stat_mem_block_t *info = NULL;
+
+ if (!ptr)
+ return;
+
+ info = ((stat_mem_block_t*)ptr - 1);
+
+ stat_mem_deallocated += info->size;
+ stat_mem_high -= info->size;
+ stat_mem_deallocated_total ++;
+
+ if (info->prev) info->prev->next = info->next;
+ if (info->next) info->next->prev = info->prev;
+
+ /* move ahead */
+ if (info == stat_mem_block_root)
+ stat_mem_block_root = info->next;
+
+ free(info);
+}
+
+void *stat_mem_reallocate(void *ptr, size_t size, size_t line, const char *file) {
+ stat_mem_block_t *oldinfo = NULL;
+ stat_mem_block_t *newinfo;
+
+ if (!ptr)
+ return stat_mem_allocate(size, line, file);
+
+ /* stay consistent with glic */
+ if (!size) {
+ stat_mem_deallocate(ptr);
+ return NULL;
+ }
+
+ oldinfo = ((stat_mem_block_t*)ptr - 1);
+ newinfo = ((stat_mem_block_t*)malloc(sizeof(stat_mem_block_t) + size));
+
+ if (!newinfo) {
+ stat_mem_deallocate(ptr);
+ return NULL;
+ }
+
+ memcpy(newinfo+1, oldinfo+1, oldinfo->size);
+
+ if (oldinfo->prev) oldinfo->prev->next = oldinfo->next;
+ if (oldinfo->next) oldinfo->next->prev = oldinfo->prev;
+
+ /* move ahead */
+ if (oldinfo == stat_mem_block_root)
+ stat_mem_block_root = oldinfo->next;
+
+ newinfo->line = line;
+ newinfo->size = size;
+ newinfo->file = file;
+ newinfo->prev = NULL;
+ newinfo->next = stat_mem_block_root;
+
+ if (stat_mem_block_root)
+ stat_mem_block_root->prev = newinfo;
+
+ stat_mem_block_root = newinfo;
+ stat_mem_allocated -= oldinfo->size;
+ stat_mem_high -= oldinfo->size;
+ stat_mem_allocated += newinfo->size;
+ stat_mem_high += newinfo->size;
+
+ if (stat_mem_high > stat_mem_peak)
+ stat_mem_peak = stat_mem_high;
+
+ free(oldinfo);
+
+ return newinfo + 1;
+}
+
+/*
+ * strdup does it's own malloc, we need to track malloc. We don't want
+ * to overwrite malloc though, infact, we can't really hook it at all
+ * without library specific assumptions. So we re implement strdup.
+ */
+char *stat_mem_strdup(const char *src, size_t line, const char *file, bool empty) {
+ size_t len = 0;
+ char *ptr = NULL;
+
+ if (!src)
+ return NULL;
+
+ len = strlen(src);
+ if (((!empty) ? len : true) && (ptr = (char*)stat_mem_allocate(len + 1, line, file))) {
+ memcpy(ptr, src, len);
+ ptr[len] = '\0';
+ }
+
+ stat_used_strdups ++;
+ return ptr;
+}
+
+/*
+ * The reallocate function for resizing vectors.
+ */
+void _util_vec_grow(void **a, size_t i, size_t s) {
+ vector_t *d = vec_meta(*a);
+ size_t m = 0;
+ stat_size_entry_t *e = NULL;
+ void *p = NULL;
+
+ if (*a) {
+ m = 2 * d->allocated + i;
+ p = mem_r(d, s * m + sizeof(vector_t));
+ } else {
+ m = i + 1;
+ p = mem_a(s * m + sizeof(vector_t));
+ ((vector_t*)p)->used = 0;
+ stat_used_vectors++;
+ }
+
+ if (!stat_size_vectors)
+ stat_size_vectors = stat_size_new();
+
+ if ((e = stat_size_get(stat_size_vectors, s))) {
+ e->value ++;
+ } else {
+ stat_size_put(stat_size_vectors, s, 1); /* start off with 1 */
+ stat_type_vectors++;
+ }
+
+ *a = (vector_t*)p + 1;
+ vec_meta(*a)->allocated = m;
+}
+
+/*
+ * Hash table for generic data, based on dynamic memory allocations
+ * all around. This is the internal interface, please look for
+ * EXPOSED INTERFACE comment below
+ */
+typedef struct hash_node_t {
+ char *key; /* the key for this node in table */
+ void *value; /* pointer to the data as void* */
+ struct hash_node_t *next; /* next node (linked list) */
+} hash_node_t;
+
+GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) {
+ const uint32_t mix = 0x5BD1E995;
+ const uint32_t rot = 24;
+ size_t size = strlen(key);
+ uint32_t hash = 0x1EF0 /* LICRC TAB */ ^ size;
+ uint32_t alias = 0;
+ const unsigned char *data = (const unsigned char*)key;
+
+ while (size >= 4) {
+ alias = (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
+ alias *= mix;
+ alias ^= alias >> rot;
+ alias *= mix;
+
+ hash *= mix;
+ hash ^= alias;
+
+ data += 4;
+ size -= 4;
+ }
+
+ switch (size) {
+ case 3: hash ^= data[2] << 16;
+ case 2: hash ^= data[1] << 8;
+ case 1: hash ^= data[0];
+ hash *= mix;
+ }
+
+ hash ^= hash >> 13;
+ hash *= mix;
+ hash ^= hash >> 15;
+
+ return (size_t) (hash % ht->size);
+}
+
+static hash_node_t *_util_htnewpair(const char *key, void *value) {
+ hash_node_t *node;
+ if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
+ return NULL;
+
+ if (!(node->key = util_strdupe(key))) {
+ mem_d(node);
+ return NULL;
+ }
+
+ node->value = value;
+ node->next = NULL;
+
+ return node;
+}
+
+/*
+ * EXPOSED INTERFACE for the hashtable implementation
+ * util_htnew(size) -- to make a new hashtable
+ * util_htset(table, key, value, sizeof(value)) -- to set something in the table
+ * util_htget(table, key) -- to get something from the table
+ * util_htdel(table) -- to delete the table
+ */
+hash_table_t *util_htnew(size_t size) {
+ hash_table_t *hashtable = NULL;
+ stat_size_entry_t *find = NULL;
+
+ if (size < 1)
+ return NULL;
+
+ if (!stat_size_hashtables)
+ stat_size_hashtables = stat_size_new();
+
+ if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t))))
+ return NULL;
+
+ if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) {
+ mem_d(hashtable);
+ return NULL;
+ }
+
+ if ((find = stat_size_get(stat_size_hashtables, size)))
+ find->value++;
+ else {
+ stat_type_hashtables++;
+ stat_size_put(stat_size_hashtables, size, 1);
+ }
+
+ hashtable->size = size;
+ memset(hashtable->table, 0, sizeof(hash_node_t*) * size);
+
+ stat_used_hashtables++;
+ return hashtable;
+}
+
+void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) {
+ hash_node_t *newnode = NULL;
+ hash_node_t *next = NULL;
+ hash_node_t *last = NULL;
+
+ next = ht->table[bin];
+
+ while (next && next->key && strcmp(key, next->key) > 0)
+ last = next, next = next->next;
+
+ /* already in table, do a replace */
+ if (next && next->key && strcmp(key, next->key) == 0) {
+ next->value = value;
+ } else {
+ /* not found, grow a pair man :P */
+ newnode = _util_htnewpair(key, value);
+ if (next == ht->table[bin]) {
+ newnode->next = next;
+ ht->table[bin] = newnode;
+ } else if (!next) {
+ last->next = newnode;
+ } else {
+ newnode->next = next;
+ last->next = newnode;
+ }
+ }
+}
+
+void util_htset(hash_table_t *ht, const char *key, void *value) {
+ util_htseth(ht, key, util_hthash(ht, key), value);
+}
+
+void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) {
+ hash_node_t *pair = ht->table[bin];
+
+ while (pair && pair->key && strcmp(key, pair->key) > 0)
+ pair = pair->next;
+
+ if (!pair || !pair->key || strcmp(key, pair->key) != 0)
+ return NULL;
+
+ return pair->value;
+}
+
+void *util_htget(hash_table_t *ht, const char *key) {
+ return util_htgeth(ht, key, util_hthash(ht, key));
+}
+
+void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
+ hash_node_t *pair;
+ size_t len, keylen;
+ int cmp;
+
+ keylen = strlen(key);
+
+ pair = ht->table[bin];
+ while (pair && pair->key) {
+ len = strlen(pair->key);
+ if (len < keylen) {
+ pair = pair->next;
+ continue;
+ }
+ if (keylen == len) {
+ cmp = strcmp(key, pair->key);
+ if (cmp == 0)
+ return pair->value;
+ if (cmp < 0)
+ return NULL;
+ pair = pair->next;
+ continue;
+ }
+ cmp = strcmp(key, pair->key + len - keylen);
+ if (cmp == 0) {
+ uintptr_t up = (uintptr_t)pair->value;
+ up += len - keylen;
+ return (void*)up;
+ }
+ pair = pair->next;
+ }
+ return NULL;
+}
+
+/*
+ * Free all allocated data in a hashtable, this is quite the amount
+ * of work.
+ */
+void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
+ size_t i = 0;
+ for (; i < ht->size; i++) {
+ hash_node_t *n = ht->table[i];
+ hash_node_t *p;
+
+ /* free in list */
+ while (n) {
+ if (n->key)
+ mem_d(n->key);
+ if (callback)
+ callback(n->value);
+ p = n;
+ n = n->next;
+ mem_d(p);
+ }
+
+ }
+ /* free table */
+ mem_d(ht->table);
+ mem_d(ht);
+}
+
+void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
+ hash_node_t **pair = &ht->table[bin];
+ hash_node_t *tmp;
+
+ while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
+ pair = &(*pair)->next;
+
+ tmp = *pair;
+ if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
+ return;
+
+ if (cb)
+ (*cb)(tmp->value);
+
+ *pair = tmp->next;
+ mem_d(tmp->key);
+ mem_d(tmp);
+}
+
+void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
+ util_htrmh(ht, key, util_hthash(ht, key), cb);
+}
+
+void util_htdel(hash_table_t *ht) {
+ util_htrem(ht, NULL);
+}
+
+/*
+ * The following functions below implement printing / dumping of statistical
+ * information.
+ */
+static void stat_dump_mem_contents(stat_mem_block_t *memory, uint16_t cols) {
+ uint32_t i, j;
+ for (i = 0; i < memory->size + ((memory->size % cols) ? (cols - memory->size % cols) : 0); i++) {
+ if (i % cols == 0) con_out(" 0x%06X: ", i);
+ if (i < memory->size) con_out("%02X " , 0xFF & ((unsigned char*)(memory + 1))[i]);
+ else con_out(" ");
+
+ if ((uint16_t)(i % cols) == (cols - 1)) {
+ for (j = i - (cols - 1); j <= i; j++) {
+ con_out("%c",
+ (j >= memory->size)
+ ? ' '
+ : (isprint(((unsigned char*)(memory + 1))[j]))
+ ? 0xFF & ((unsigned char*)(memory + 1)) [j]
+ : '.'
+ );
+ }
+ con_out("\n");
+ }
+ }
+}
+
+static void stat_dump_mem_leaks(void) {
+ stat_mem_block_t *info;
+ for (info = stat_mem_block_root; info; info = info->next) {
+ con_out("lost: %u (bytes) at %s:%u\n",
+ info->size,
+ info->file,
+ info->line
+ );
+
+ stat_dump_mem_contents(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS));
+ }
+}
+
+static void stat_dump_mem_info(void) {
+ con_out("Memory information:\n\
+ Total allocations: %llu\n\
+ Total deallocations: %llu\n\
+ Total allocated: %f (MB)\n\
+ Total deallocated: %f (MB)\n\
+ Total peak memory: %f (MB)\n\
+ Total leaked memory: %f (MB) in %llu allocations\n",
+ stat_mem_allocated_total,
+ stat_mem_deallocated_total,
+ (float)(stat_mem_allocated) / 1048576.0f,
+ (float)(stat_mem_deallocated) / 1048576.0f,
+ (float)(stat_mem_peak) / 1048576.0f,
+ (float)(stat_mem_allocated - stat_mem_deallocated) / 1048576.0f,
+ stat_mem_allocated_total - stat_mem_deallocated_total
+ );
+}
+
+static void stat_dump_stats_table(stat_size_table_t table, const char *string, uint64_t *size) {
+ size_t i,j;
+
+ if (!table)
+ return;
+
+ for (i = 0, j = 1; i < ST_SIZE; i++) {
+ stat_size_entry_t *entry;
+
+ if (!(entry = table[i]))
+ continue;
+
+ con_out(string, (unsigned)j, (unsigned)entry->key, (unsigned)entry->value);
+ j++;
+
+ if (size)
+ *size += entry->key * entry->value;
+ }
+}
+
+void stat_info() {
+ if (OPTS_OPTION_BOOL(OPTION_DEBUG))
+ stat_dump_mem_leaks();
+
+ if (OPTS_OPTION_BOOL(OPTION_DEBUG) ||
+ OPTS_OPTION_BOOL(OPTION_MEMCHK))
+ stat_dump_mem_info();
+
+ if (OPTS_OPTION_BOOL(OPTION_MEMCHK) ||
+ OPTS_OPTION_BOOL(OPTION_STATISTICS)) {
+ uint64_t mem = 0;
+
+ con_out("\nAdditional Statistics:\n\
+ Total vectors allocated: %llu\n\
+ Total string duplicates: %llu\n\
+ Total hashtables allocated: %llu\n\
+ Total unique vector sizes: %llu\n",
+ stat_used_vectors,
+ stat_used_strdups,
+ stat_used_hashtables,
+ stat_type_vectors
+ );
+
+ stat_dump_stats_table (
+ stat_size_vectors,
+ " %2u| # of %4u byte vectors: %u\n",
+ &mem
+ );
+
+ con_out (
+ " Total unique hashtable sizes: %llu\n",
+ stat_type_hashtables
+ );
+
+ stat_dump_stats_table (
+ stat_size_hashtables,
+ " %2u| # of %4u element hashtables: %u\n",
+ NULL
+ );
+
+ con_out (
+ " Total vector memory: %f (MB)\n",
+ (float)(mem) / 1048576.0f
+ );
+ }
+
+ if (stat_size_vectors)
+ stat_size_del(stat_size_vectors);
+ if (stat_size_hashtables)
+ stat_size_del(stat_size_hashtables);
+}
+#undef ST_SIZE
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include "gmqcc.h"
+#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
+#include "gmqcc.h"
+
opts_cmd_t opts;
static const char *task_bins[] = {
int pid;
} popen_t;
-FILE ** task_popen(const char *command, const char *mode) {
+static FILE ** task_popen(const char *command, const char *mode) {
int inhandle [2];
int outhandle [2];
int errhandle [2];
return NULL;
}
-int task_pclose(FILE **handles) {
+static int task_pclose(FILE **handles) {
popen_t *data = (popen_t*)handles;
int status = 0;
char name_out[L_tmpnam];
} popen_t;
- FILE **task_popen(const char *command, const char *mode) {
+ static FILE **task_popen(const char *command, const char *mode) {
char *cmd = NULL;
popen_t *open = (popen_t*)mem_a(sizeof(popen_t));
return open->handles;
}
- void task_pclose(FILE **files) {
+ static void task_pclose(FILE **files) {
popen_t *open = ((popen_t*)files);
fs_file_close(files[1]);
fs_file_close(files[2]);
* This is very much like a compiler code generator :-). This generates
* a value from some data observed from the compiler.
*/
-bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) {
+static bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) {
size_t desclen = 0;
size_t filelen = 0;
char **destval = NULL;
case 'I': destval = &tmpl->sourcefile; break;
case 'F': destval = &tmpl->testflags; break;
default:
- con_printmsg(LVL_ERROR, __FILE__, __LINE__, "internal error",
+ con_printmsg(LVL_ERROR, __FILE__, __LINE__, 0, "internal error",
"invalid tag `%c:` during code generation\n",
tag
);
* assigned value.
*/
if (*destval) {
- con_printmsg(LVL_ERROR, file, line, "compile error",
+ con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "compile error",
"tag `%c:` already assigned value: %s\n",
tag, *destval
);
return true;
}
-bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size_t *pad) {
+static bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size_t *pad) {
char *data = NULL;
char *back = NULL;
size_t size = 0;
*/
case '/':
if (data[1] != '/') {
- con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
+ con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
"invalid character `/`, perhaps you meant `//` ?");
mem_d(back);
case 'I':
case 'F':
if (data[1] != ':') {
- con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
+ con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
"expected `:` after `%c`",
*data
);
goto failure;
}
if (!task_template_generate(tmpl, *data, file, line, &data[3], pad)) {
- con_printmsg(LVL_ERROR, file, line, "tmpl compile error",
+ con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl compile error",
"failed to generate for given task\n"
);
goto failure;
{
char *value = &data[3];
if (data[1] != ':') {
- con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
+ con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
"expected `:` after `%c`",
*data
);
}
default:
- con_printmsg(LVL_ERROR, file, line, "tmpl parse error",
+ con_printmsg(LVL_ERROR, file, line, 0, /*TODO: column for match*/ "tmpl parse error",
"invalid tag `%c`", *data
);
goto failure;
* Nullifies the template data: used during initialization of a new
* template and free.
*/
-void task_template_nullify(task_template_t *tmpl) {
+static void task_template_nullify(task_template_t *tmpl) {
if (!tmpl)
return;
tmpl->testflags = NULL;
}
-task_template_t *task_template_compile(const char *file, const char *dir, size_t *pad) {
+static task_template_t *task_template_compile(const char *file, const char *dir, size_t *pad) {
/* a page should be enough */
char fullfile[4096];
size_t filepadd = 0;
return NULL;
}
-void task_template_destroy(task_template_t **tmpl) {
+static void task_template_destroy(task_template_t **tmpl) {
if (!tmpl)
return;
* Read a directory and searches for all template files in it
* which is later used to run all tests.
*/
-bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
+static bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
bool success = true;
DIR *dir;
struct dirent *files;
* Task precleanup removes any existing temporary files or log files
* left behind from a previous invoke of the test-suite.
*/
-void task_precleanup(const char *curdir) {
+static void task_precleanup(const char *curdir) {
DIR *dir;
struct dirent *files;
char buffer[4096];
fs_dir_close(dir);
}
-void task_destroy(void) {
+static void task_destroy(void) {
/*
* Free all the data in the task list and finally the list itself
* then proceed to cleanup anything else outside the program like
* messages IF the procedure type is -execute, otherwise it matches
* the preprocessor output.
*/
-bool task_trymatch(task_template_t *tmpl, char ***line) {
+static bool task_trymatch(task_template_t *tmpl, char ***line) {
bool success = true;
bool preprocessing = false;
FILE *execute;
return success;
}
-const char *task_type(task_template_t *tmpl) {
+static const char *task_type(task_template_t *tmpl) {
if (!strcmp(tmpl->proceduretype, "-pp"))
return "type: preprocessor";
if (!strcmp(tmpl->proceduretype, "-execute"))
* from thin air and executed INLINE.
*/
#include <math.h>
-void task_schedualize(size_t *pad) {
+static void task_schedualize(size_t *pad) {
char space[2][64];
bool execute = false;
char *data = NULL;
*
* It expects con_init() was called before hand.
*/
-GMQCC_WARN bool test_perform(const char *curdir, const char *defs) {
+static GMQCC_WARN bool test_perform(const char *curdir, const char *defs) {
static const char *default_defs = "defs.qh";
size_t pad[] = {
char *defs = NULL;
con_init();
+ OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = 16;
/*
* Command line option parsing commences now We only need to support
}
con_change(redirout, redirerr);
succeed = test_perform("tests", defs);
- util_meminfo();
+ stat_info();
return (succeed) ? EXIT_SUCCESS : EXIT_FAILURE;
--- /dev/null
+float f_float() {
+ return = 100.0f;
+ return = 200.0f;
+ return;
+}
+
+vector f_vector() {
+ vector foo;
+ foo.x = f_float();
+ foo.y = f_float();
+ foo.z = f_float();
+
+ return = foo;
+ return;
+}
+
+string f_string() {
+#ifndef FAIL_TEST
+ return = "hello";
+ return = "world";
+#endif
+ return;
+}
+
+float factorial(float n) {
+ if (n == 0) return = 1;
+ else return = n * factorial(n - 1);
+}
+
+void main() {
+ print(ftos(f_float()), "\n"); // 200.0f
+ print(vtos(f_vector()), "\n"); // '1 2 3'
+ print(f_string(), "\n"); // world
+ print(ftos(factorial(4)), "\n"); // 24
+}
--- /dev/null
+I: rassign.qc
+D: test return assignments
+T: -execute
+C: -fftepp -freturn-assignments
+M: 200
+M: '200 200 200'
+M: world
+M: 24
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-#include <stdarg.h>
-#include <errno.h>
-#include "gmqcc.h"
-
-/* TODO: remove globals ... */
-static uint64_t mem_ab = 0;
-static uint64_t mem_db = 0;
-static uint64_t mem_at = 0;
-static uint64_t mem_dt = 0;
-static uint64_t mem_pk = 0;
-static uint64_t mem_hw = 0;
-
-struct memblock_t {
- const char *file;
- unsigned int line;
- size_t byte;
- struct memblock_t *next;
- struct memblock_t *prev;
-};
-
-#define PEAK_MEM \
- do { \
- if (mem_hw > mem_pk) \
- mem_pk = mem_hw; \
- } while (0)
-
-static struct memblock_t *mem_start = NULL;
-
-void *util_memory_a(size_t byte, unsigned int line, const char *file) {
- struct memblock_t *info = (struct memblock_t*)malloc(sizeof(struct memblock_t) + byte);
- void *data = (void*)(info+1);
- if (!info) return NULL;
- info->line = line;
- info->byte = byte;
- info->file = file;
- info->prev = NULL;
- info->next = mem_start;
- if (mem_start)
- mem_start->prev = info;
- mem_start = info;
-
- mem_at++;
- mem_ab += info->byte;
- mem_hw += info->byte;
-
- PEAK_MEM;
-
- return data;
-}
-
-void util_memory_d(void *ptrn) {
- struct memblock_t *info = NULL;
-
- if (!ptrn) return;
- info = ((struct memblock_t*)ptrn - 1);
-
- mem_db += info->byte;
- mem_hw -= info->byte;
- mem_dt++;
-
- if (info->prev)
- info->prev->next = info->next;
- if (info->next)
- info->next->prev = info->prev;
- if (info == mem_start)
- mem_start = info->next;
-
- free(info);
-}
-
-void *util_memory_r(void *ptrn, size_t byte, unsigned int line, const char *file) {
- struct memblock_t *oldinfo = NULL;
-
- struct memblock_t *newinfo;
-
- if (!ptrn)
- return util_memory_a(byte, line, file);
- if (!byte) {
- util_memory_d(ptrn);
- return NULL;
- }
+#include <string.h>
+#include <ctype.h>
- oldinfo = ((struct memblock_t*)ptrn - 1);
- newinfo = ((struct memblock_t*)malloc(sizeof(struct memblock_t) + byte));
-
- /* new data */
- if (!newinfo) {
- util_memory_d(oldinfo+1);
- return NULL;
- }
-
- /* copy old */
- memcpy(newinfo+1, oldinfo+1, oldinfo->byte);
-
- /* free old */
- if (oldinfo->prev)
- oldinfo->prev->next = oldinfo->next;
- if (oldinfo->next)
- oldinfo->next->prev = oldinfo->prev;
- if (oldinfo == mem_start)
- mem_start = oldinfo->next;
-
- /* fill info */
- newinfo->line = line;
- newinfo->byte = byte;
- newinfo->file = file;
- newinfo->prev = NULL;
- newinfo->next = mem_start;
- if (mem_start)
- mem_start->prev = newinfo;
- mem_start = newinfo;
-
- mem_ab -= oldinfo->byte;
- mem_hw -= oldinfo->byte;
- mem_ab += newinfo->byte;
- mem_hw += newinfo->byte;
-
- PEAK_MEM;
-
- free(oldinfo);
-
- return newinfo+1;
-}
-
-static void util_dumpmem(struct memblock_t *memory, uint16_t cols) {
- uint32_t i, j;
- for (i = 0; i < memory->byte + ((memory->byte % cols) ? (cols - memory->byte % cols) : 0); i++) {
- if (i % cols == 0) con_out(" 0x%06X: ", i);
- if (i < memory->byte) con_out("%02X " , 0xFF & ((char*)(memory + 1))[i]);
- else con_out(" ");
-
- if ((uint16_t)(i % cols) == (cols - 1)) {
- for (j = i - (cols - 1); j <= i; j++) {
- con_out("%c",
- (j >= memory->byte)
- ? ' '
- : (isprint(((char*)(memory + 1))[j]))
- ? 0xFF & ((char*)(memory + 1)) [j]
- : '.'
- );
- }
- con_out("\n");
- }
- }
-}
-
-void util_meminfo() {
- struct memblock_t *info;
-
-
- if (OPTS_OPTION_BOOL(OPTION_DEBUG)) {
- for (info = mem_start; info; info = info->next) {
- con_out("lost: %u (bytes) at %s:%u\n",
- info->byte,
- info->file,
- info->line);
-
- util_dumpmem(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS));
- }
- }
-
- if (OPTS_OPTION_BOOL(OPTION_DEBUG) ||
- OPTS_OPTION_BOOL(OPTION_MEMCHK)) {
- con_out("Memory information:\n\
- Total allocations: %llu\n\
- Total deallocations: %llu\n\
- Total allocated: %f (MB)\n\
- Total deallocated: %f (MB)\n\
- Total peak memory: %f (MB)\n\
- Total leaked memory: %f (MB) in %llu allocations\n",
- mem_at,
- mem_dt,
- (float)(mem_ab) / 1048576.0f,
- (float)(mem_db) / 1048576.0f,
- (float)(mem_pk) / 1048576.0f,
- (float)(mem_ab - mem_db) / 1048576.0f,
-
- /* could be more clever */
- (mem_at - mem_dt)
- );
- }
-}
-
-/*
- * Some string utility functions, because strdup uses malloc, and we want
- * to track all memory (without replacing malloc).
- */
-char *_util_Estrdup(const char *s, const char *file, size_t line) {
- size_t len = 0;
- char *ptr = NULL;
-
- /* in case of -DNOTRACK */
- (void)file;
- (void)line;
-
- if (!s)
- return NULL;
-
- if ((len = strlen(s)) && (ptr = (char*)mem_af(len+1, line, file))) {
- memcpy(ptr, s, len);
- ptr[len] = '\0';
- }
- return ptr;
-}
-
-char *_util_Estrdup_empty(const char *s, const char *file, size_t line) {
- size_t len = 0;
- char *ptr = NULL;
-
- /* in case of -DNOTRACK */
- (void)file;
- (void)line;
-
- if (!s)
- return NULL;
-
- len = strlen(s);
- if ((ptr = (char*)mem_af(len+1, line, file))) {
- memcpy(ptr, s, len);
- ptr[len] = '\0';
- }
- return ptr;
-}
+#include "gmqcc.h"
void util_debug(const char *area, const char *ms, ...) {
va_list va;
return sz-1;
}
-/* TODO: rewrite ... when I redo the ve cleanup */
-void _util_vec_grow(void **a, size_t i, size_t s) {
- vector_t *d = vec_meta(*a);
- size_t m = *a ? 2 * d->allocated +i : i+1;
- void *p = mem_r((*a ? d : NULL), s * m + sizeof(vector_t));
-
- if (!*a)
- ((vector_t*)p)->used = 0;
- *a = (vector_t*)p + 1;
-
- vec_meta(*a)->allocated = m;
-}
-
-/*
- * Hash table for generic data, based on dynamic memory allocations
- * all around. This is the internal interface, please look for
- * EXPOSED INTERFACE comment below
- */
-typedef struct hash_node_t {
- char *key; /* the key for this node in table */
- void *value; /* pointer to the data as void* */
- struct hash_node_t *next; /* next node (linked list) */
-} hash_node_t;
-
-GMQCC_INLINE size_t util_hthash(hash_table_t *ht, const char *key) {
- const uint32_t mix = 0x5BD1E995;
- const uint32_t rot = 24;
- size_t size = strlen(key);
- uint32_t hash = 0x1EF0 /* LICRC TAB */ ^ size;
- uint32_t alias = 0;
- const unsigned char *data = (const unsigned char*)key;
-
- while (size >= 4) {
- alias = (data[0] | (data[1] << 8) | (data[2] << 16) | (data[3] << 24));
- alias *= mix;
- alias ^= alias >> rot;
- alias *= mix;
-
- hash *= mix;
- hash ^= alias;
-
- data += 4;
- size -= 4;
- }
-
- switch (size) {
- case 3: hash ^= data[2] << 16;
- case 2: hash ^= data[1] << 8;
- case 1: hash ^= data[0];
- hash *= mix;
- }
-
- hash ^= hash >> 13;
- hash *= mix;
- hash ^= hash >> 15;
-
- return (size_t) (hash % ht->size);
-}
-
-hash_node_t *_util_htnewpair(const char *key, void *value) {
- hash_node_t *node;
- if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
- return NULL;
-
- if (!(node->key = util_strdupe(key))) {
- mem_d(node);
- return NULL;
- }
-
- node->value = value;
- node->next = NULL;
-
- return node;
-}
-
-/*
- * EXPOSED INTERFACE for the hashtable implementation
- * util_htnew(size) -- to make a new hashtable
- * util_htset(table, key, value, sizeof(value)) -- to set something in the table
- * util_htget(table, key) -- to get something from the table
- * util_htdel(table) -- to delete the table
- */
-hash_table_t *util_htnew(size_t size) {
- hash_table_t *hashtable = NULL;
- if (size < 1)
- return NULL;
-
- if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t))))
- return NULL;
-
- if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) {
- mem_d(hashtable);
- return NULL;
- }
-
- hashtable->size = size;
- memset(hashtable->table, 0, sizeof(hash_node_t*) * size);
-
- return hashtable;
-}
-
-void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) {
- hash_node_t *newnode = NULL;
- hash_node_t *next = NULL;
- hash_node_t *last = NULL;
-
- next = ht->table[bin];
-
- while (next && next->key && strcmp(key, next->key) > 0)
- last = next, next = next->next;
-
- /* already in table, do a replace */
- if (next && next->key && strcmp(key, next->key) == 0) {
- next->value = value;
- } else {
- /* not found, grow a pair man :P */
- newnode = _util_htnewpair(key, value);
- if (next == ht->table[bin]) {
- newnode->next = next;
- ht->table[bin] = newnode;
- } else if (!next) {
- last->next = newnode;
- } else {
- newnode->next = next;
- last->next = newnode;
- }
- }
-}
-
-void util_htset(hash_table_t *ht, const char *key, void *value) {
- util_htseth(ht, key, util_hthash(ht, key), value);
-}
-
-void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) {
- hash_node_t *pair = ht->table[bin];
-
- while (pair && pair->key && strcmp(key, pair->key) > 0)
- pair = pair->next;
-
- if (!pair || !pair->key || strcmp(key, pair->key) != 0)
- return NULL;
-
- return pair->value;
-}
-
-void *util_htget(hash_table_t *ht, const char *key) {
- return util_htgeth(ht, key, util_hthash(ht, key));
-}
-
-void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
- hash_node_t *pair;
- size_t len, keylen;
- int cmp;
-
- keylen = strlen(key);
-
- pair = ht->table[bin];
- while (pair && pair->key) {
- len = strlen(pair->key);
- if (len < keylen) {
- pair = pair->next;
- continue;
- }
- if (keylen == len) {
- cmp = strcmp(key, pair->key);
- if (cmp == 0)
- return pair->value;
- if (cmp < 0)
- return NULL;
- pair = pair->next;
- continue;
- }
- cmp = strcmp(key, pair->key + len - keylen);
- if (cmp == 0) {
- uintptr_t up = (uintptr_t)pair->value;
- up += len - keylen;
- return (void*)up;
- }
- pair = pair->next;
- }
- return NULL;
-}
-
-/*
- * Free all allocated data in a hashtable, this is quite the amount
- * of work.
- */
-void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
- size_t i = 0;
- for (; i < ht->size; i++) {
- hash_node_t *n = ht->table[i];
- hash_node_t *p;
-
- /* free in list */
- while (n) {
- if (n->key)
- mem_d(n->key);
- if (callback)
- callback(n->value);
- p = n;
- n = n->next;
- mem_d(p);
- }
-
- }
- /* free table */
- mem_d(ht->table);
- mem_d(ht);
-}
-
-void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
- hash_node_t **pair = &ht->table[bin];
- hash_node_t *tmp;
-
- while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
- pair = &(*pair)->next;
-
- tmp = *pair;
- if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
- return;
-
- if (cb)
- (*cb)(tmp->value);
-
- *pair = tmp->next;
- mem_d(tmp->key);
- mem_d(tmp);
-}
-
-void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
- util_htrmh(ht, key, util_hthash(ht, key), cb);
-}
-
-void util_htdel(hash_table_t *ht) {
- util_htrem(ht, NULL);
-}
-
/*
* Portable implementation of vasprintf/asprintf. Assumes vsnprintf
* exists, otherwise compiler error.
static uint32_t mt_state[MT_SIZE];
static size_t mt_index = 0;
-static GMQCC_INLINE void mt_generate() {
+static GMQCC_INLINE void mt_generate(void) {
/*
* The loop has been unrolled here: the original paper and implemenation
* Called for the following code: