distro/arch/*
!distro/arch/git/PKGBUILD
!distro/arch/release/PKGBUILD
+!distro/arch/bsd-release/PKGBUILD
+!distro/arch/bsd-git/PKGBUILD
+!distro/arch/this/Makefile
-GMQCC brought to you by:
- Dale Weiler
- Wolfgang Bumiller
+Authors:
+ Dale Weiler - Charismatic Visionary / Programmer
+ Wolfgang Bumiller - Main Programmer
+
+Thanks to:
+ Forest `LordHavoc` Hale - Technical support and assistance
+ Rudolf `divVerent` Polzer - Technical support and assistance
+ Matthias `matthiaskrgr` Krüger - Miscellaneous assistance
+ Samual `Samual` Lenks - Preprocessor assistance
- Various optimizations and progs-size reductions.
- A new spell-checking algorithm tries to hint you at existing
variables on error.
+ - Some problems with VM related vector-instructions issues
+ have been solved in both DP and our own executor. A new
+ compatbility option (enabled by default) has been added for
+ now: -flegacy-vector-maths
* qcvm:
- Improved commandline argument handling.
- More builtins: sqrt(), normalize()
MINGW = $(findstring MINGW32, $(UNAME))
CC ?= clang
-CFLAGS += -Wall -Wextra -I. -fno-strict-aliasing -fsigned-char
+CFLAGS += -Wall -Wextra -I. -fno-strict-aliasing -fsigned-char -Wno-overlength-strings
ifneq ($(shell git describe --always 2>/dev/null),)
CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\""
endif
#turn on tons of warnings if clang is present
# but also turn off the STUPID ONES
ifeq ($(CC), clang)
- CFLAGS += \
- -Weverything \
- -Wno-padded \
- -Wno-format-nonliteral \
- -Wno-disabled-macro-expansion \
- -Wno-conversion \
- -Wno-missing-prototypes \
- -Wno-float-equal \
- -Wno-cast-align
+ CFLAGS += \
+ -Weverything \
+ -Wno-padded \
+ -Wno-format-nonliteral \
+ -Wno-disabled-macro-expansion \
+ -Wno-conversion \
+ -Wno-missing-prototypes \
+ -Wno-float-equal \
+ -Wno-cast-align \
+ -Wno-missing-variable-declarations \
+ -Wno-unknown-warning-option
else
#Tiny C Compiler doesn't know what -pedantic-errors is
# and instead of ignoring .. just errors.
install -m755 $(QCVM) $(DESTDIR)$(BINDIR)/qcvm
install-doc:
install -d -m755 $(DESTDIR)$(MANDIR)/man1
- install -m755 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/
- install -m755 doc/qcvm.1 $(DESTDIR)$(MANDIR)/man1/
+ install -m644 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/
+ install -m644 doc/qcvm.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
# DO NOT DELETE
self->getter = NULL;
self->desc = NULL;
- self->argcounter = NULL;
+ self->argcounter = NULL;
return self;
}
vtype->hasvalue = true;
vtype->constval.vfunc = self;
- self->varargs = NULL;
- self->argc = NULL;
+ self->varargs = NULL;
+ self->argc = NULL;
+ self->fixedparams = NULL;
return self;
}
ast_delete(self->varargs);
if (self->argc)
ast_delete(self->argc);
+ if (self->fixedparams)
+ ast_unref(self->fixedparams);
mem_d(self);
}
size_t len;
char *from;
- if (!opts.dump && !opts.dumpfin && !opts.debug)
+ if (!OPTS_OPTION_BOOL(OPTION_DUMP) &&
+ !OPTS_OPTION_BOOL(OPTION_DUMPFIN) &&
+ !OPTS_OPTION_BOOL(OPTION_DEBUG))
+ {
return NULL;
+ }
id = (self->labelcount++);
len = strlen(prefix);
}
/* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
- if (!array->expression.count || array->expression.count > opts.max_array_size)
+ 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;
int vtype = elemtype->vtype;
/* same as with field arrays */
- if (!self->expression.count || self->expression.count > opts.max_array_size)
+ if (!self->expression.count || self->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE))
compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
v = ir_builder_create_global(ir, self->name, vtype);
}
/* we are lame now - considering the way QC works we won't tolerate arrays > 1024 elements */
- if (!self->expression.count || self->expression.count > opts.max_array_size) {
+ if (!self->expression.count || self->expression.count > OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE)) {
compile_error(ast_ctx(self), "Invalid array of size %lu", (unsigned long)self->expression.count);
}
if (self->argc) {
ir_value *va_count;
+ ir_value *fixed;
+ ir_value *sub;
if (!ast_local_codegen(self->argc, self->ir_func, true))
return false;
cgen = self->argc->expression.codegen;
if (!(*cgen)((ast_expression*)(self->argc), self, false, &va_count))
return false;
+ cgen = self->fixedparams->expression.codegen;
+ if (!(*cgen)((ast_expression*)(self->fixedparams), self, false, &fixed))
+ return false;
+ sub = ir_block_create_binop(self->curblock, ast_ctx(self),
+ ast_function_label(self, "va_count"), INSTR_SUB_F,
+ ir_builder_get_va_count(ir), fixed);
+ if (!sub)
+ return false;
if (!ir_block_create_store_op(self->curblock, ast_ctx(self), INSTR_STORE_F,
- va_count, ir_builder_get_va_count(ir)))
+ va_count, sub))
{
return false;
}
for (i = 0; i < vec_size(self->locals); ++i)
{
if (!ast_local_codegen(self->locals[i], func->ir_func, false)) {
- if (opts.debug)
+ if (OPTS_OPTION_BOOL(OPTION_DEBUG))
compile_error(ast_ctx(self), "failed to generate local `%s`", self->locals[i]->name);
return false;
}
return true;
cmpinstr = type_eq_instr[irop->vtype];
- if (cmpinstr >= AINSTR_END) {
+ if (cmpinstr >= VINSTR_END) {
ast_type_to_string(self->operand, typestr, sizeof(typestr));
compile_error(ast_ctx(self), "invalid type to perform a switch on: %s", typestr);
return false;
ast_value *varargs;
ast_value *argc;
+ ast_value *fixedparams;
};
ast_function* ast_function_new(lex_ctx ctx, const char *name, ast_value *vtype);
/* This will NOT delete the underlying ast_value */
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.forcecrc)
- code_header.crc16 = opts.forced_crc;
+ if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
+ code_header.crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
else
code_header.crc16 = code_crc;
code_header.entfield = code_entfields;
* edit distance the smaller the error. This usually causes some un-
* expected problems. e.g reciet->recite yields recipt. For QuakeC
* this could become a problem when lots of identifiers are involved.
- *
- * Our control mechanisim could use a limit, i.e limit the number of
- * sets of edits for distance X. This would also increase execution
- * speed considerably.
*/
return t;
}
-void correct_trie_del_sub(correct_trie_t *t) {
+static GMQCC_INLINE void correct_trie_del_sub(correct_trie_t *t) {
size_t i;
if (!t->entries)
return;
mem_d(t->entries);
}
-void correct_trie_del(correct_trie_t *t) {
+static GMQCC_INLINE void correct_trie_del(correct_trie_t *t) {
size_t i;
if (t->entries) {
for (i = 0; i < sizeof(correct_alpha)-1; ++i)
mem_d(t);
}
-void* correct_trie_get(const correct_trie_t *t, const char *key) {
+static GMQCC_INLINE void* correct_trie_get(const correct_trie_t *t, const char *key) {
const unsigned char *data = (const unsigned char*)key;
while (*data) {
return t->value;
}
-void correct_trie_set(correct_trie_t *t, const char *key, void * const value) {
+static GMQCC_INLINE void correct_trie_set(correct_trie_t *t, const char *key, void * const value) {
const unsigned char *data = (const unsigned char*)key;
while (*data) {
if (!t->entries) {
* need to take a size_t ** to carry it along (would all the argument
* overhead be worth it?)
*/
-static size_t correct_deletion(const char *ident, char **array) {
+static GMQCC_INLINE size_t correct_deletion(const char *ident, char **array) {
size_t itr = 0;
const size_t len = strlen(ident);
return itr;
}
-static size_t correct_transposition(const char *ident, char **array) {
+static GMQCC_INLINE size_t correct_transposition(const char *ident, char **array) {
size_t itr = 0;
const size_t len = strlen(ident);
return itr;
}
-static size_t correct_alteration(const char *ident, char **array) {
+static GMQCC_INLINE size_t correct_alteration(const char *ident, char **array) {
size_t itr = 0;
size_t jtr = 0;
size_t ktr = 0;
return ktr;
}
-static size_t correct_insertion(const char *ident, char **array) {
+static GMQCC_INLINE size_t correct_insertion(const char *ident, char **array) {
size_t itr = 0;
size_t jtr = 0;
const size_t len = strlen(ident);
return (len) + (len - 1) + (len * (sizeof(correct_alpha)-1)) + ((len + 1) * (sizeof(correct_alpha)-1));
}
-static char **correct_edit(const char *ident) {
+static GMQCC_INLINE char **correct_edit(const char *ident, size_t **lens) {
size_t next;
- char **find = (char**)correct_pool_alloc(correct_size(ident) * sizeof(char*));
+ size_t size = correct_size(ident);
+ char **find = (char**)correct_pool_alloc(size * sizeof(char*));
- if (!find)
+ if (!find || !(*lens = (size_t*)correct_pool_alloc(size * sizeof(size_t))))
return NULL;
next = correct_deletion (ident, find);
next += correct_alteration (ident, find+next);
/*****/ correct_insertion (ident, find+next);
+ /* precompute lengths */
+ for (next = 0; next < size; next++)
+ (*lens)[next] = strlen(find[next]);
+
return find;
}
-/*
- * We could use a hashtable but the space complexity isn't worth it
- * since we're only going to determine the "did you mean?" identifier
- * on error.
- */
-static int correct_exist(char **array, size_t rows, char *ident) {
+static GMQCC_INLINE int correct_exist(char **array, register size_t *sizes, size_t rows, char *ident, register size_t len) {
size_t itr;
- /*
- * As an experiment I tried the following assembly for memcmp here:
- *
- * correct_cmp_loop:
- * incl %eax ; eax = LHS
- * incl %edx ; edx = LRS
- * cmpl %eax, %ebx ; ebx = &LHS[END_POS]
- *
- * jbe correct_cmp_eq
- * movb (%edx), %cl ; micro-optimized even on atoms :-)
- * cmpb %cl, (%eax) ; ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- * jg correct_cmp_gt
- * jge correct_cmp_loop
- * ...
- *
- * Despite how much optimization went in to this, the speed was
- * being conflicted by the strlen(ident) used for &LHS[END_POS]
- * If we could eliminate the strlen with what I suggested on line
- * 311 ... we can accelerate this whole damn thing quite a bit.
- *
- * However there is still something we can do here that does give
- * us a little more speed. Although one more branch, we know for
- * sure there is at least one byte to compare, if that one byte
- * simply isn't the same we can skip the full check. Which means
- * we skip a whole strlen call.
- */
for (itr = 0; itr < rows; itr++) {
- if (!memcmp(array[itr], ident, strlen(ident)))
+ /*
+ * We can save tons of calls to memcmp if we simply ignore comparisions
+ * that we know cannot contain the same length.
+ */
+ if (sizes[itr] == len && !memcmp(array[itr], ident, len))
return 1;
}
if (size < oldallocated)
return res;
- out = correct_pool_alloc(sizeof(*res) * oldallocated + 32);
+ out = (char**)correct_pool_alloc(sizeof(*res) * oldallocated + 32);
memcpy(out, res, sizeof(*res) * oldallocated);
*allocated += 32;
size_t len = 0;
size_t row = 0;
size_t nxt = 8;
- char **res = correct_pool_alloc(sizeof(char *) * nxt);
- char **end = NULL;
+ char **res = (char**)correct_pool_alloc(sizeof(char *) * nxt);
+ char **end = NULL;
+ size_t *bit = NULL;
for (; itr < rows; itr++) {
if (!array[itr][0])
continue;
- if (vec_size(corr->edits) > itr+1)
+ if (vec_size(corr->edits) > itr+1) {
end = corr->edits[itr+1];
- else {
- end = correct_edit(array[itr]);
+ bit = corr->lens [itr+1];
+ } else {
+ end = correct_edit(array[itr], &bit);
vec_push(corr->edits, end);
+ vec_push(corr->lens, bit);
}
row = correct_size(array[itr]);
for (jtr = 0; jtr < row; jtr++) {
- if (correct_find(table, end[jtr]) && !correct_exist(res, len, end[jtr])) {
+ if (correct_find(table, end[jtr]) && !correct_exist(res, bit, len, end[jtr], bit[jtr])) {
res = correct_known_resize(res, &nxt, len+1);
res[len++] = end[jtr];
}
return res;
}
-static char *correct_maximum(correct_trie_t* table, char **array, size_t rows) {
+static GMQCC_INLINE char *correct_maximum(correct_trie_t* table, char **array, size_t rows) {
char *str = NULL;
size_t *itm = NULL;
size_t itr = 0;
{
correct_pool_new();
c->edits = NULL;
+ c->lens = NULL;
}
void correct_free(correction_t *c)
{
vec_free(c->edits);
+ vec_free(c->lens);
correct_pool_delete();
}
char *e2ident = NULL;
size_t e1rows = 0;
size_t e2rows = 0;
+ size_t *bits = NULL;
/* needs to be allocated for free later */
if (correct_find(table, ident))
if (vec_size(corr->edits) > 0)
e1 = corr->edits[0];
else {
- e1 = correct_edit(ident);
+ e1 = correct_edit(ident, &bits);
vec_push(corr->edits, e1);
+ vec_push(corr->lens, bits);
}
if ((e1ident = correct_maximum(table, e1, e1rows)))
+++ /dev/null
-# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
-
-pkgname=gmqcc-git
-pkgver=20130110
-pkgrel=1
-pkgdesc="An Improved Quake C Compiler"
-arch=('i686' 'x86_64')
-depends=('glibc')
-conflicts=('gmqcc')
-provides=('gmqcc=0.2.4')
-makedepends=('git')
-url="https://github.com/graphitemaster/gmqcc.git"
-license=('MIT')
-
-_gitroot="git://github.com/graphitemaster/gmqcc.git"
-_gitname="gmqcc"
-
-build() {
- cd $srcdir
- msg "Connecting to the GIT server..."
- if [[ -d $srcdir/$_gitname ]] ; then
- cd $_gitname
- msg "Removing build files..."
- git clean -dfx
- msg "Updating..."
- git pull --no-tags
- msg "The local files are updated."
- else
- msg "Cloning..."
- git clone $_gitroot $_gitname --depth 1
- msg "Clone done."
- fi
-
- msg "Starting compilation..."
- cd "$srcdir"/"$_gitname"
-
- msg "Compiling..."
- make
-}
-
-check() {
- cd "$srcdir"/"$_gitname"
- make check
-}
-
-package() {
- cd "$srcdir"/"$_gitname"
- msg "Compiling and installing to pkgdir this time..."
- make install DESTDIR=$pkgdir PREFIX=/usr
- msg "Compiling done."
-
- install -D LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
-}
+++ /dev/null
-# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
-
-pkgname=gmqcc
-pkgver=0.2.2
-pkgrel=1
-pkgdesc="An Improved Quake C Compiler"
-arch=('i686' 'x86_64')
-depends=('glibc')
-url="https://github.com/graphitemaster/gmqcc.git"
-license=('MIT')
-source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
-sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353')
-
-_gitname=graphitemaster-gmqcc-de24486/
-
-build() {
- msg "Starting compilation..."
- cd "$srcdir"/"$_gitname"
-
- msg "Compiling..."
- make
-}
-
-check() {
- cd "$srcdir"/"$_gitname"
- make check
-}
-
-package() {
- cd "$srcdir"/"$_gitname"
- msg "Compiling and installing to pkgdir this time..."
- make install DESTDIR=$pkgdir PREFIX=/usr
- msg "Compiling done."
-
- install -D LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
-}
--- /dev/null
+# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
+# Contributor: Wolfgang Bumiller <blub@speed.at>
+
+pkgname=gmqcc-git
+pkgver=20130127
+pkgrel=1
+pkgdesc="An Improved Quake C Compiler"
+arch=('i686' 'x86_64')
+depends=()
+conflicts=('gmqcc')
+provides=('gmqcc=0.2.4')
+makedepends=('git')
+url="https://github.com/graphitemaster/gmqcc.git"
+license=('MIT')
+
+_gitroot="git://github.com/graphitemaster/gmqcc.git"
+_gitname="gmqcc"
+
+build() {
+ cd $srcdir
+ msg "Connecting to the GIT server..."
+ if [[ -d $srcdir/$_gitname ]] ; then
+ cd $_gitname
+ msg "Removing build files..."
+ git clean -dfx
+ msg "Updating..."
+ git pull --no-tags
+ msg "The local files are updated."
+ else
+ msg "Cloning..."
+ git clone $_gitroot $_gitname --depth 1
+ msg "Clone done."
+ fi
+
+ msg "Starting compilation..."
+ cd "$srcdir"/"$_gitname"
+
+ msg "Compiling..."
+ gmake
+}
+
+check() {
+ cd "$srcdir"/"$_gitname"
+ gmake check
+}
+
+package() {
+ cd "$srcdir"/"$_gitname"
+ msg "Compiling and installing to pkgdir this time..."
+ gmake install DESTDIR=$pkgdir PREFIX=/usr/local MANDIR=/usr/local/man
+ msg "Compiling done."
+
+ install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc
+ install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE
+}
--- /dev/null
+# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
+# Contributor: Wolfgang Bumiller <blub@speed.at>
+
+pkgname=gmqcc
+pkgver=0.2.2
+pkgrel=1
+pkgdesc="An Improved Quake C Compiler"
+arch=('i686' 'x86_64')
+depends=()
+url="https://github.com/graphitemaster/gmqcc.git"
+license=('MIT')
+source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
+sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353')
+
+_gitname=graphitemaster-gmqcc-de24486/
+
+build() {
+ msg "Starting compilation..."
+ cd "$srcdir"/"$_gitname"
+
+ msg "Compiling..."
+ gmake
+}
+
+check() {
+ cd "$srcdir"/"$_gitname"
+ gmake check
+}
+
+package() {
+ cd "$srcdir"/"$_gitname"
+ msg "Compiling and installing to pkgdir this time..."
+ gmake install DESTDIR=$pkgdir PREFIX=/usr/local MANDIR=/usr/local/man
+ msg "Compiling done."
+
+ install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc
+ install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE
+}
--- /dev/null
+# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
+
+pkgname=gmqcc-git
+pkgver=20130127
+pkgrel=1
+pkgdesc="An Improved Quake C Compiler"
+arch=('i686' 'x86_64')
+depends=('glibc')
+conflicts=('gmqcc')
+provides=('gmqcc=0.2.4')
+makedepends=('git')
+url="https://github.com/graphitemaster/gmqcc.git"
+license=('MIT')
+
+_gitroot="git://github.com/graphitemaster/gmqcc.git"
+_gitname="gmqcc"
+
+build() {
+ cd $srcdir
+ msg "Connecting to the GIT server..."
+ if [[ -d $srcdir/$_gitname ]] ; then
+ cd $_gitname
+ msg "Removing build files..."
+ git clean -dfx
+ msg "Updating..."
+ git pull --no-tags
+ msg "The local files are updated."
+ else
+ msg "Cloning..."
+ git clone $_gitroot $_gitname --depth 1
+ msg "Clone done."
+ fi
+
+ msg "Starting compilation..."
+ cd "$srcdir"/"$_gitname"
+
+ msg "Compiling..."
+ make
+}
+
+check() {
+ cd "$srcdir"/"$_gitname"
+ make check
+}
+
+package() {
+ cd "$srcdir"/"$_gitname"
+ msg "Compiling and installing to pkgdir this time..."
+ make install DESTDIR=$pkgdir PREFIX=/usr
+ msg "Compiling done."
+
+ install -D LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
+}
--- /dev/null
+# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
+
+pkgname=gmqcc
+pkgver=0.2.2
+pkgrel=1
+pkgdesc="An Improved Quake C Compiler"
+arch=('i686' 'x86_64')
+depends=('glibc')
+url="https://github.com/graphitemaster/gmqcc.git"
+license=('MIT')
+source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
+sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353')
+
+_gitname=graphitemaster-gmqcc-de24486/
+
+build() {
+ msg "Starting compilation..."
+ cd "$srcdir"/"$_gitname"
+
+ msg "Compiling..."
+ make
+}
+
+check() {
+ cd "$srcdir"/"$_gitname"
+ make check
+}
+
+package() {
+ cd "$srcdir"/"$_gitname"
+ msg "Compiling and installing to pkgdir this time..."
+ make install DESTDIR=$pkgdir PREFIX=/usr
+ msg "Compiling done."
+
+ install -D LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
+}
--- /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))
+PKGREL := 1
+CARCH := $(shell uname -m)
+PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH)
+PKG := $(PKGDIR).pkg.tar.xz
+PKGINFO := $(PKGDIR)/.PKGINFO
+
+base: clean
+ $(MAKE) -C $(BASEDIR) DESTDIR=distro/archlinux/this/$(PKGDIR) PREFIX=$(PREFIX) install
+ @echo "pkgname = gmqcc" > $(PKGINFO)
+ @echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO)
+ @echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO)
+ @echo "url = https://github.com/graphitemaster/gmqcc.git" >> $(PKGINFO)
+ @echo "builddate = `date -u \"+%s\"`" >> $(PKGINFO)
+ @echo "packager = Unknown Packager" >> $(PKGINFO)
+ @echo "size = `du -sk $(PKGDIR) | awk '{print $$1 * 1024}'`" >> $(PKGINFO)
+ @echo "arch = $(CARCH)" >> $(PKGINFO)
+ @echo "license = MIT" >> $(PKGINFO)
+ @echo "conflict = gmqcc" >> $(PKGINFO)
+ @echo "depend = glibc" >> $(PKGINFO)
+ @echo "makepkgopt = strip" >> $(PKGINFO)
+ @echo "makepkgopt = docs" >> $(PKGINFO)
+ @echo "makepkgopt = libtool" >> $(PKGINFO)
+ @echo "makepkgopt = emptydirs" >> $(PKGINFO)
+ @echo "makepkgopt = zipman" >> $(PKGINFO)
+ @echo "makepkgopt = purge" >> $(PKGINFO)
+ @echo "makepkgopt = !upx" >> $(PKGINFO)
+ @tar -cJvf $(PKG) -C $(PKGDIR)/ .PKGINFO usr/
+ @rm -rf $(PKGDIR)
+
+clean:
+ @rm -f $(PKG)
+
+
+all: base
--- /dev/null
+BASEDIR := ../..
+PREFIX := /usr
+HEADER := $(BASEDIR)/gmqcc.h
+MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)`
+MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)`
+PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)`
+DEBDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)
+DEB := $(DEBDIR).deb
+
+base:
+ $(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
+ @install -d -m755 $(DEBDIR)/DEBIAN
+ @cp control $(DEBDIR)/DEBIAN/control
+ @tar czf data.tar.gz -C $(DEBDIR)/ . --exclude=DEBIAN
+ @tar czf control.tar.gz -C $(DEBDIR)/DEBIAN/ .
+ @echo 2.0 > debian-binary
+ @ar r $(DEB) debian-binary control.tar.gz data.tar.gz
+ @rm -rf debian-binary control.tar.gz data.tar.gz $(DEBDIR)
+clean:
+ @rm -f $(DEB)
+
+
+all: base
--- /dev/null
+Package: gmqcc
+Version: 0.3.0
+Section: user/hidden
+Priority: optional
+Architecture: i386
+Installed-Size: `du -ks usr|cut -f 1`
+Maintainer: Dale Weiler <killfieldengine@gmail.com>
+Description: An improved Quake C Compiler
+ For an enduring period of time the options for a decent compiler for the Quake C programming language
+ were confined to a specific compiler known as QCC. Attempts were made to extend and improve upon the
+ design of QCC, but many foreseen the consequences of building on a broken foundation. The solution
+ was obvious, a new compiler; one born from the NIH realm of sarcastic wit. We welcome you. You won't
+ find a better Quake C compiler.
-.\" Process with groff -man -Tascii file.3
-.TH GMQCC 1 2012-07-12 "" "gmqcc Manual"
-.SH NAME
-gmqcc \- A Quake C compiler built from the NIH realm of sarcastic wit
-.SH SYNOPSIS
-.B gmqcc
-[\fIOPTIONS\fR] [\fIfiles...\fR]
-.SH DESCRIPTION
-Traditionally, a QC compiler reads the file \fIprogs.src\fR which
-in its first line contains the output filename, and the rest is a
+.\"mdoc
+.Dd January 24, 2013
+.Dt GMQCC 1 PRM
+.Os
+.Sh NAME
+.Nm gmqcc
+.Nd A Quake C compiler built from the NIH realm of sarcastic wit
+.Sh SYNOPSIS
+.Nm gmqcc
+.Op Cm options
+.Op Ar files...
+.Sh DESCRIPTION
+Traditionally, a QC compiler reads the file
+.Pa progs.src
+which in its first line contains the output filename, and the rest is a
list of QC source files that are to be compiled in order.
-\fBgmqcc\fR optionally takes options to specify the output and
+.Nm gmqcc
+optionally takes options to specify the output and
input files on the commandline, and also accepts assembly files.
-.SH OPTIONS
-\fBgmqcc\fR mostly tries to mimic gcc's commandline handling, though
+.Sh OPTIONS
+.Nm gmqcc
+mostly tries to mimic gcc's commandline handling, though
there are also traditional long-options available.
-.TP
-.B "-h, --help"
+.Bl -tag -width Ds
+.It Fl h , Fl -help
Show a usage message and exit.
-.TP
-.B "-debug"
+.It Fl "debug"
Turn on some compiler debugging mechanisms.
-.TP
-.B "-memchk"
+.It Fl memchk
Turn on compiler mem-check. (Shows allocations and checks for leaks.)
-.TP
-.BI "-o, --output=" filename
+.It Fl o , Fl -output= Ns Ar filename
Specify the output filename. Defaults to progs.dat. This will overwrite
-the output file listed in a \fIprogs.src\fR file in case such a file is used.
-.TP
-.BI "-O" number
+the output file listed in a
+.Pa progs.src
+file in case such a file is used.
+.Bl -tag -width indent
+.It Fl O Ns Ar number
Specify the optimization level
-.RS
-.IP 3
+.It Ar 3
Highest optimization level
-.IP 2
+.It Ar 2
Default optimization level
-.IP 1
+.It Ar 1
Minimal optimization level
-.IP 0
+.It Ar 0
Disable optimization entirely
-.RE
-.TP
-.BI "-O" name "\fR, " "" -Ono- name
+.El
+.Pp
+.It Fl O Ns Ar name Fl Ono- Ns Ar name
Enable or disable a specific optimization. Note that these options
must be used after setting the optimization level, otherwise they'll
be overwritten.
-.TP
-.B -Ohelp
+.It Fl O Ns Cm help
List all possible optimizations and the optimization level they're
activated at.
-.TP
-.BR -q ", " --quiet
+.It Fl q , Fl -quiet
Be less verbose. In particular removes the messages about which files
are being processed, and which compilation mode is being used, and
some others. Warnings and errors will of course still be displayed.
-.TP
-.B "-E"
-Run only the preprocessor as if -fftepp was used and print the
-preprocessed code to stdout.
-.TP
-.BI -W warning "\fR, " "" -Wno- warning
+.It Fl E
+Run only the preprocessor as if
+.Fl f Ns Cm ftepp
+was used and print the preprocessed code to stdout.
+.It Fl W Ns Ar warning , Fl Wno- Ns Ar warning
Enable or disable a warning.
-.TP
-.B -Wall
-Enable almost all warnings. Overrides preceding -W parameters.
-.sp
-The following warnings will \fBnot\fR be anbled:
-.in +4
-.nf
--Wuninitialized-global
-.fi
-.in
-.TP
-.BR -Werror ", " -Wno-error
+.It Fl W Ns Cm all
+Enable almost all warnings. Overrides preceding
+.Fl W
+parameters.
+.Pp
+The following warnings will
+.Em not
+be enabled:
+.Bl -tag -width indent -offset indent
+.It Fl W Ns Cm uninitialized-global
+.El
+.It Fl W Ns Cm error , Fl Wno- Ns Cm error
Controls whether or not all warnings should be treated as errors.
-.TP
-.BI -Werror- warning "\fR, " "" -Wno-error- warning
+.It Fl Werror- Ns Ar warning , Fl Wno-error- Ns Ar warning
Controls whether a specific warning should be an error.
-.TP
-.B -Whelp
+.It Fl W Ns Cm help
List all possible warn flags.
-.TP
-.BI -f flag "\fR, " "" -fno- flag
+.It Fl f Ns Ar flag , Fl fno- Ns Ar flag
Enable or disable a specific compile flag. See the list of flags
below.
-.TP
-.B -fhelp
+.It Fl f Ns Cm help
List all possible compile flags.
-.TP
-.B -nocolor
+.It Fl nocolor
Disables colored output
-.TP
-.BI -config= file
-Use an ini file to read all the -O, -W and -f flag from. See the
-CONFIG section about the file format.
-.TP
-.BI "-redirout=" file
-Redirects standard output to a \fIfile\fR
-.TP
-.BI "-redirerr=" file
-Redirects standard error to a \fIfile\fR
-.TP
-.BI "-std=" standard
+.It Fl config= Ns Ar file
+Use an ini file to read all the
+.Fl O , Fl W
+and
+.Fl f
+flag from. See the
+.Sx CONFIG
+section about the file format.
+.It Fl redirout= Ns Ar file
+Redirects standard output to a
+.Ar file
+.It Fl redirerr= Ns Ar file
+Redirects standard error to a
+.Ar file
+.It Fl std= Ns Ar standard
Use the specified standard for parsing QC code. The following standards
are available:
-.IR gmqcc , qcc , fteqcc
-Selecting a standard also implies some -f options and behaves as if
-those options have been written right after the -std option, meaning
-if you changed them before the -std option, you're now overwriting
-them.
-.sp
-.BR -std=gmqcc " includes:"
-.in +4
-.nf
--fadjust-vector-fields
--fcorrect-logic
--ftrue-empty-strings
--floop-labels
--finitialized-nonconstants
--ftranslatable-strings
--f\fIno-\fRfalse-empty-strings
--Winvalid-parameter-count
--Wmissing-returnvalues
--fcorrect-ternary (cannot be turned off)
-.fi
-.in
-.sp
-.BR -std=qcc " includes:"
-.in +4
-.nf
--fassign-function-types
--f\fIno-\fRadjust-vector-fields
-.fi
-.in
-.sp
-.BR -std=fteqcc " includes:"
-.in +4
-.nf
--fftepp
--ftranslatable-strings
--fassign-function-types
--Wternary-precedence
--f\fIno-\fRadjust-vector-fields
--f\fIno-\fRcorrect-ternary
-.fi
-.in
-.TP
-.B "--add-info"
+.Ar gmqcc , Ar qcc , Ar fteqcc
+Selecting a standard also implies some
+.Fl f
+options and behaves as if
+those options have been written right after the
+.Fl std
+option, meaning
+if you changed them before the
+.Fl -std
+option, you're now overwriting them.
+.Pp
+.Fl std= Ns Cm gmqcc No includes:
+.Bl -tag -width indent -compact -offset Ds
+.It Fl f Ns Cm adjust-vector-fields
+.It Fl f Ns Cm correct-logic
+.It Fl f Ns Cm true-empty-strings
+.It Fl f Ns Cm loop-labels
+.It Fl f Ns Cm initialized-nonconstants
+.It Fl f Ns Cm translatable-strings
+.It Fl fno- Ns Cm false-empty-strings
+.It Fl W Ns Cm invalid-parameter-count
+.It Fl W Ns Cm missing-returnvalues
+.It Fl f Ns Cm correct-ternary Li (cannot be turned off)
+.El
+.Pp
+.Fl std= Ns Cm qcc No includes:
+.Bl -tag -width indent -compact -offset Ds
+.It Fl f Ns Cm assign-function-types
+.It Fl fIno- Ns Cm adjust-vector-fields
+.El
+.Pp
+.Fl std= Ns Cm fteqcc No includes:
+.Bl -tag -width indent -compact -offset Ds
+.It Fl f Ns Cm ftepp
+.It Fl f Ns Cm translatable-strings
+.It Fl f Ns Cm assign-function-types
+.It Fl W Ns Cm ternary-precedence
+.It Fl fno- Ns Cm adjust-vector-fields
+.It Fl fno- Ns Cm correct-ternary
+.El
+.It Fl -add-info
Adds compiler information to the generated binary file. Currently
this includes the following globals:
-.RS
-.IP "reserved:version"
+.Bl -tag -width indent -compact
+.It Li reserved:version
String containing the compiler version as printed by the --version
parameter.
-.RE
-.TP
-.BR "--correct" ", " "--no-correct"
+.El
+.It Fl -correct , Fl -no-correct
When enabled, errors about undefined values try to suggest an existing
value via spell checking.
-.TP
-.B "-dump"
+.It Fl dump
DEBUG OPTION. Print the code's intermediate representation before the
optimization and finalization passes to stdout before generating the
binary.
-.TP
-.B "-dumpfin"
+.It Fl dumpfin
DEBUG OPTION. Print the code's intermediate representation after the
optimization and finalization passes to stdout before generating the
binary. The instructions will be enumerated, and values will contain a
list of liferanges.
-.SH COMPILE WARNINGS
-.TP
-.B -Wunused-variable
+.El
+.Sh COMPILE WARNINGS
+.Bl -tag -width Ds
+.It Fl W Ns Cm unused-variable
Generate a warning about variables which are declared but never used.
-This can be avoided by adding the \fInoref\fR keyword in front of the
+This can be avoided by adding the
+.Ql noref
+keyword in front of the
variable declaration. Additionally a complete section of unreferenced
-variables can be opened using \fI#pragma noref 1\fR, and closed via
-\fI#pragma noref 0\fR.
-.TP
-.B -Wused-uninitialized
+variables can be opened using
+.Ql #pragma noref 1
+and closed via
+.Ql #pragma noref 0 Ns .
+.It Fl W Ns Cm used-uninitialized
Generate a warning if it is possible that a variable can be used
without prior initialization. Note that this warning is not
necessarily reliable if the initialization happens only under certain
-conditions. The other way is \fInot\fR possible: that the warning is
-\fInot\fR generated when uninitialized use \fIis possible\fR.
-.TP
-.B -Wunknown-control-sequence
+conditions. The other way is
+.Em not
+possible: that the warning is
+.Em not
+generated when uninitialized use
+.Em is
+possible.
+.It Fl W Ns Cm unknown-control-sequence
Generate an error when an unrecognized control sequence in a string is
used. Meaning: when there's a character after a backslash in a string
which has no known meaning.
-.TP
-.B -Wextensions
+.It Fl W Ns Cm extensions
Warn when using special extensions which are not part of the selected
standard.
-.TP
-.B -Wfield-redeclared
+.It Fl W Ns Cm field-redeclared
Generally QC compilers ignore redeclaration of fields. Here you can
optionally enable a warning.
-.TP
-.B -Wmissing-return-values
-Functions which aren't of type \fIvoid\fR will warn if it possible to
+.It Fl W Ns Cm missing-return-values
+Functions which aren't of type
+.Ft void
+will warn if it possible to
reach the end without returning an actual value.
-.TP
-.B -Winvalid-parameter-count
+.It Fl W Ns Cm invalid-parameter-count
Warn about a function call with an invalid number of parameters.
-.TP
-.B -Wlocal-shadows
+.It Fl W Ns Cm local-shadows
Warn when a locally declared variable shadows variable.
-.TP
-.B -Wlocal-constants
+.It Fl W Ns Cm local-constants
Warn when the initialization of a local variable turns the variable
into a constant. This is default behaviour unless
-\fI-finitialized-nonconstants\fR is used.
-.TP
-.B -Wvoid-variables
-There are only 2 known global variables of type void: end_sys_globals
-and end_sys_fields. Any other void-variable will warn.
-.TP
-.B -Wimplicit-function-pointer
-A global function which is not declared with the \fIvar\fR keyword is
+.Fl f Ns Cm initialized-nonconstants
+is used.
+.It Fl W Ns Cm void-variables
+There are only 2 known global variables of type void:
+.Ql end_sys_globals
+and
+.Ql end_sys_fields Ns .
+Any other void-variable will warn.
+.It Fl W Ns Cm implicit-function-pointer
+A global function which is not declared with the
+.Ql var
+keyword is
expected to have an implementing body, or be a builtin. If neither is
the case, it implicitly becomes a function pointer, and a warning is
generated.
-.TP
-.B -Wvariadic-function
+.It Fl W Ns Cm variadic-function
Currently there's no way for an in QC implemented function to access
variadic parameters. If a function with variadic parameters has an
implementing body, a warning will be generated.
-.TP
-.B -Wframe-macros
-Generate warnings about \fI$frame\fR commands, for instance about
+.It Fl W Ns Cm frame-macros
+Generate warnings about
+.Ql $frame
+commands, for instance about
duplicate frame definitions.
-.TP
-.B -Weffectless-statement
+.It Fl W Ns Cm effectless-statement
Warn about statements which have no effect. Any expression which does
not call a function or assigns a variable.
-.TP
-.B -Wend-sys-fields
-The \fIend_sys_fields\fR variable is supposed to be a global variable
-of type \fIvoid\fR. It is also recognized as a \fIfield\fR but this
+.It Fl W Ns Cm end-sys-fields
+The
+.Ql end_sys_fields
+variable is supposed to be a global variable
+of type
+.Ft void Ns .
+It is also recognized as a \fIfield\fR but this
will generate a warning.
-.TP
-.B -Wassign-function-types
+.It Fl W Ns Cm assign-function-types
Warn when assigning to a function pointer with an unmatching
signature. This usually happens in cases like assigning the null
function to an entity's .think function pointer.
-.TP
-.B -Wpreprocessor
-Enable warnings coming from the preprocessor. Like duplicate macro
-declarations. This warning triggers when there's a problem with the
-way the preprocessor has been used, it will \fBnot\fR affect warnings
-generated with the '#warning' directive. See -Wcpp.
-.TP
-.B -Wcpp
+.It Fl W Ns Cm cpp
Show warnings created using the preprocessor's '#warning' directive.
-.TP
-.B -Wmultifile-if
+.It Fl W Ns Cm multifile-if
Warn if there's a preprocessor \fI#if\fR spanning across several
files.
-.TP
-.B -Wdouble-declaration
+.It Fl W Ns Cm double-declaration
Warn about multiple declarations of globals. This seems pretty common
in QC code so you probably do not want this unless you want to clean
up your code.
-.TP
-.B -Wconst-var
+.It Fl W Ns Cm const-var
The combination of \fIconst\fR and \fIvar\fR is not illegal, however
different compilers may handle them differently. We were told, the
intention is to create a function-pointer which is not assignable.
This is exactly how we interpret it. However for this interpretation
-the \fIvar\fR keyword is considered superfluous (and philosophically
+the
+.Ql var
+keyword is considered superfluous (and philosophically
wrong), so it is possible to generate a warning about this.
-.TP
-.B -Wmultibyte-character
+.It Fl W Ns Cm multibyte-character
Warn about multibyte character constants, they do not work right now.
-.TP
-.B -Wternary-precedence
+.It Fl W Ns Cm ternary-precedence
Warn if a ternary expression which contains a comma operator is used
without enclosing parenthesis, since this is most likely not what you
-actually want. We recommend the \fI-fcorrect-ternary\fR option.
-.TP
-.B -Wunknown-pragmas
-Warn when encountering an unrecognized \fI#pragma\fR line.
-.TP
-.B -Wunreachable-code
+actually want. We recommend the
+.Fl f Ns Cm correct-ternary
+option.
+.It Fl W Ns Cm unknown-pragmas
+Warn when encountering an unrecognized
+.Ql #pragma
+line.
+.It Fl W Ns Cm unreachable-code
Warn about unreachable code. That is: code after a return statement,
or code after a call to a function marked as 'noreturn'.
-.TP
-.B -Wdebug
+.It Fl W Ns Cm debug
Enable some warnings added in order to help debugging in the compiler.
You won't need this.
-.B -Wunknown-attribute
+.It Fl W Ns Cm unknown-attribute
Warn on an unknown attribute. The warning will inlclude only the first
token inside the enclosing attribute-brackets. This may change when
the actual attribute syntax is better defined.
-.TP
-.B -Wreserved-names
-Warn when using reserved names such as 'nil'.
-.TP
-.B -Wuninitialized-constant
-Warn about global constants (using the 'const' keyword) with no
+.It Fl W Ns Cm reserved-names
+Warn when using reserved names such as
+.Ql nil Ns .
+.It Fl W Ns Cm uninitialized-constant
+Warn about global constants (using the
+.Ql const
+keyword) with no
assigned value.
-.TP
-.B -Wuninitialized-global
+.It Fl W Ns Cm uninitialized-global
Warn about global variables with no initializing value. This is off by
default, and is added mostly to help find null-values which are
supposed to be replaced by the untyped 'nil' constant.
-.TP
-.B -Wdifferent-qualifiers
+.It Fl W Ns Cm different-qualifiers
Warn when a variables is redeclared with a different qualifier. For
example when redeclaring a variable as \'var\' which was previously
marked \'const\'.
-.TP
-.B -Wdifferent-attributes
-Similar to the above but for attributes like "[[noreturn]]".
-.TP
-.B -Wdeprecated
+.It Fl W Ns Cm different-attributes
+Similar to the above but for attributes like
+.Ql [[noreturn]] Ns .
+.It Fl W Ns Cm deprecated
Warn when a function is marked with the attribute
"[[deprecated]]". This flag enables a warning on calls to functions
marked as such.
-.TP
-.B -Wparenthesis
+.It Fl W Ns Cm parenthesis
Warn about possible mistakes caused by missing or wrong parenthesis,
like an assignment in an 'if' condition when there's no additional set
of parens around the assignment.
-.SH COMPILE FLAGS
-.TP
-.B -fdarkplaces-string-table-bug
+.El
+.Sh COMPILE FLAGS
+.Bl -tag -width Ds
+.It Fl f Ns Cm darkplaces-string-table-bug
Add some additional characters to the string table in order to
compensate for a wrong boundcheck in some specific version of the
darkplaces engine.
-.TP
-.B -fadjust-vector-fields
+.It Fl f Ns Cm adjust-vector-fields
When assigning to field pointers of type \fI.vector\fR the common
behaviour in compilers like \fIfteqcc\fR is to only assign the
x-component of the pointer. This means that you can use the vector as
such, but you cannot use its y and z components directly. This flag
fixes this behaviour. Before using it make sure your code does not
depend on the buggy behaviour.
-.TP
-.B -fftepp
+.It Fl f Ns Cm ftepp
Enable a partially fteqcc-compatible preprocessor. It supports all the
features used in the Xonotic codebase. If you need more, write a
ticket.
-.TP
-.B -fftepp-predefs
+.It Fl f Ns Cm ftepp-predefs
Enable some predefined macros. This only works in combination with
\'-fftepp' and is currently not included by '-std=fteqcc'. The
following macros will be added:
-.in +4
-.nf
+.Bd -literal -offset indent
__LINE__
__FILE__
__COUNTER__
__RANDOM_LAST__
__DATE__
__TIME__
-.fi
-.in
-Note that fteqcc also defines __NULL__ which is not implemented yet.
-(See -funtyped-nil about gmqcc's alternative to __NULL__).
-.TP
-.B -frelaxed-switch
+.Ed
+.Pp
+Note that fteqcc also defines
+.Li __NULL__
+which is not implemented yet.
+(See
+.Fl f Ns Cm untyped-nil
+about gmqcc's alternative to
+.Li __NULL__ Ns ).
+.It Fl f Ns Cm relaxed-switch
Allow switch cases to use non constant variables.
-.TP
-.B -fshort-logic
+.It Fl f Ns Cm short-logic
Perform early out in logical AND and OR expressions. The final result
will be either a 0 or a 1, see the next flag for more possibilities.
-.TP
-.B -fperl-logic
+.It Fl f Ns Cm perl-logic
In many languages, logical expressions perform early out in a special
way: If the left operand of an AND yeilds true, or the one of an OR
yields false, the complete expression evaluates to the right side.
-Thus \fItrue && 5\fI evaluates to 5 rather than 1.
-.TP
-.B -ftranslatable-strings
-Enable the underscore intrinsic: Using \fI_("A string constant")\fR
+Thus
+.Ql true && 5
+evaluates to 5 rather than 1.
+.It Fl f Ns Cm translatable-strings
+Enable the underscore intrinsic: Using
+.Ql _("A string constant")
will cause the string immediate to get a name with a "dotranslate_"
prefix. The darkplaces engine recognizes these and translates them in
a way similar to how gettext works.
-.TP
-.B -finitialized-nonconstants
+.It Fl f Ns Cm initialized-nonconstants
Don't implicitly convert initialized variables to constants. With this
flag, the \fIconst\fR keyword is required to make a constant.
-.TP
-.B -fassign-function-types
+.It Fl f Ns Cm assign-function-types
If this flag is not set, (and it is set by default in the qcc and
fteqcc standards), assigning function pointers of mismatching
signatures will result in an error rather than a warning.
-.TP
-.B -flno
+.It Fl f Ns Cm lno
Produce a linenumber file along with the output .dat file.
-.TP
-.B -fcorrect-ternary
+.It Fl f Ns Cm correct-ternary
Use C's operator precedence for ternary expressions. Unless your code
depends on fteqcc-compatible behaviour, you'll want to use thi
soption.
-.TP
-.B -fsingle-vector-defs
+.It Fl f Ns Cm single-vector-defs
Normally vectors generate 4 defs, once for the vector, and once for
its components with _x, _y, _z suffixes. This option
prevents components from being listed.
-.TP
-.B -fcorrect-logic
-Most QC compilers translate if(a_vector) directly as an IF on the
-vector, which means only the x-component is checked. This causes
+.It Fl f Ns Cm correct-logic
+Most QC compilers translate
+.Ql if(a_vector)
+directly as an IF on the
+vector, which means only the x-component is checked. This option causes
vectors to be cast to actual booleans via a NOT_V and, if necessary, a
NOT_F chained to it.
-.in +4
-.nf
+.Bd -literal -offset indent
if (a_vector) // becomes
if not(!a_vector)
// likewise
a = a_vector && a_float // becomes
a = !!a_vector && a_float
-.fi
-.in
-.TP
-.B -ftrue-empty-strings
+.Ed
+.It Fl f Ns Cm true-empty-strings
An empty string is considered to be true everywhere. The NOT_S
instruction usually considers an empty string to be false, this option
effectively causes the unary not in strings to use NOT_F instead.
-.TP
-.B -ffalse-empty-strings
+.It Fl f Ns Cm false-empty-strings
An empty string is considered to be false everywhere. This means loops
and if statements which depend on a string will perform a NOT_S
instruction on the string before using it.
-.TP
-.B -futf8
+.It Fl f Ns Cm utf8
Enable utf8 characters. This allows utf-8 encoded character constants,
and escape sequence codepoints in the valid utf-8 range. Effectively
enabling escape sequences like '\\{x2211}'.
-.TP
-.B -fbail-on-werror
+.It Fl f Ns Cm bail-on-werror
When a warning is treated as an error, and this option is set (which
it is by default), it is like any other error and will cause
compilation to stop. When disabling this flag by using
\-fno-bail-on-werror, compilation will continue until the end, but no
output is generated. Instead the first such error message's context is
shown.
-.TP
-.B -floop-labels
+.It Fl f Ns Cm loop-labels
Allow loops to be labeled, and allow 'break' and 'continue' to take an
optional label to decide which loop to actually jump out of or
continue.
-.sp
-.in +4
-.nf
+.Bd -literal -offset indent
for :outer (i = 0; i < n; ++i) {
while (inner) {
...;
continue outer;
}
}
-.fi
-.in
-.TP
-.B -funtyped-nil
+.Ed
+.It Fl f Ns Cm untyped-nil
Adds a global named 'nil' which is of no type and can be assigned to
anything. No typechecking will be performed on assignments. Assigning
to it is forbidden, using it in any other kind of expression is also
In that gmqcc the nil global is an actual global filled with zeroes,
and can be assigned to anything including fields, vectors or function
pointers, and they end up becoming zeroed.
-.TP
-.B -fpermissive
+.It Fl f Ns Cm permissive
Various effects, usually to weaken some conditions.
-.RS
-.IP "with -funtyped-nil"
-Allow local variables named 'nil'. (This will not allow declaring a
-global of that name.)
-.RE
-.TP
-.B -fvariadic-args
+.Bl -tag -width indent -offset indent
+.It with Fl f Ns Cm untyped-nil
+Allow local variables named
+.Ql nil Ns .
+(This will not allow declaring a global of that name.)
+.El
+.It Fl f Ns Cm variadic-args
Allow variadic parameters to be accessed by QC code. This can be
achieved via the '...' function, which takes a parameter index and a
typename.
-
+.Pp
Example:
-.sp
-.in +4
-.nf
+.Bd -literal -offset indent
void vafunc(string...count) {
float i;
for (i = 0; i < count; ++i)
print(...(i, string), "\\n");
}
-.fi
-.in
-.TP -flegacy-vector-maths
+.Ed
+.It Fl f Ns Cm legacy-vector-maths
Most Quake VMs, including the one from FTEQW or up till recently
Darkplaces, do not cope well with vector instructions with overlapping
input and output. This option will avoid producing such code.
-.SH OPTIMIZATIONS
-.TP
-.B -Opeephole
+.El
+.Sh OPTIMIZATIONS
+.Bl -tag -width Ds
+.It Fl O Ns Cm peephole
Some general peephole optimizations. For instance the code `a = b + c`
typically generates 2 instructions, an ADD and a STORE. This
optimization removes the STORE and lets the ADD write directly into A.
-.TP
-.B -Otail-recursion
+.It Fl O Ns Cm tail-recursion
Tail recursive function calls will be turned into loops to avoid the
overhead of the CALL and RETURN instructions.
-.TP
-.B -Ooverlap-locals
+.It Fl O Ns Cm overlap-locals
Make all functions which use neither local arrays nor have locals
which are seen as possibly uninitialized use the same local section.
This should be pretty safe compared to other compilers which do not
it's hard to know whether or not an array is actually fully
initialized, especially when initializing it via a loop, we assume
functions with arrays to be too dangerous for this optimization.
-.TP
-.B -Olocal-temps
+.It Fl O Ns Cm local-temps
This promotes locally declared variables to "temps". Meaning when a
temporary result of an operation has to be stored somewhere, a local
variable which is not 'alive' at that point can be used to keep the
result. This can reduce the size of the global section.
This will not have declared variables overlap, even if it was
possible.
-.TP
-.B -Oglobal-temps
+.It Fl O Ns Cm global-temps
Causes temporary values which do not need to be backed up on a CALL to
not be stored in the function's locals-area. With this, a CALL to a
function may need to back up fewer values and thus execute faster.
-.TP
-.B -Ostrip-constant-names
+.It Fl O Ns Cm strip-constant-names
Don't generate defs for immediate values or even declared constants.
Meaning variables which are implicitly constant or qualified as such
using the 'const' keyword.
-.TP
-.B -Ooverlap-strings
+.It Fl O Ns Cm overlap-strings
Aggressively reuse strings in the string section. When a string should
be added which is the trailing substring of an already existing
string, the existing string's tail will be returned instead of the new
string being added.
-
+.Pp
For example the following code will only generate 1 string:
-
-.in +4
-.nf
+.Bd -literal -offset indent
print("Hell you!\\n");
print("you!\\n"); // trailing substring of "Hello you!\\n"
-.fi
-.in
+.Ed
+.Pp
There's however one limitation. Strings are still processed in order,
so if the above print statements were reversed, this optimization
would not happen.
-.TP
-.B -Ocall-stores
+.It Fl O Ns Cm call-stores
By default, all parameters of a CALL are copied into the
parameter-globals right before the CALL instructions. This is the
easiest and safest way to translate calls, but also adds a lot of
optimization makes operations which are used as a parameter evaluate
directly into the parameter-global if that is possible, which is when
there's no other CALL instruction in between.
-.TP
-.B -Ovoid-return
+.It Fl O Ns Cm void-return
Usually an empty RETURN instruction is added to the end of a void
typed function. However, additionally after every function a DONE
instruction is added for several reasons. (For example the qcvm's
disassemble switch uses it to know when the function ends.). This
optimization replaces that last RETURN with DONE rather than adding
the DONE additionally.
-.TP
-.B -Ovector-components
+.It Fl O Ns Cm vector-components
Because traditional QC code doesn't allow you to access individual
vector components of a computed vector without storing it in a local
-first, sometimes people multiply it by a constant like '0 1 0' to get,
+first, sometimes people multiply it by a constant like
+.Ql '0 1 0'
+to get,
in this case, the y component of a vector. This optimization will turn
such a multiplication into a direct component access. If the factor is
anything other than 1, a float-multiplication will be added, which is
still faster than a vector multiplication.
-.SH CONFIG
+.El
+.Sh CONFIG
The configuration file is similar to regular .ini files. Comments
start with hashtags or semicolons, sections are written in square
brackets and in each section there can be arbitrary many key-value
pairs.
-.sp
+.Pp
There are 3 sections currently:
-.IR flags ", " warnings ", and " optimizations .
-They contain a list of boolean values of the form `VARNAME = true` or
-`VARNAME = false`. The variable names are the same as for the
-corresponding -W, -f or -O flag written with only capital letters and
+.Ql flags Ns ,
+.Ql warnings Ns ,
+.Ql optimizations Ns .
+They contain a list of boolean values of the form
+.Ql VARNAME = true
+or
+.Ql VARNAME = false Ns .
+The variable names are the same as for the
+corresponding
+.Fl W , Fl f
+or
+.Fl O
+flag written with only capital letters and
dashes replaced by underscores.
-.sp
+.Pp
Here's an example:
-.in +4
-.nf
+.Bd -literal -offset indent
# a GMQCC configuration file
[flags]
FTEPP = true
[optimizations]
PEEPHOLE = true
TAIL_RECURSION = true
-.fi
-.in
-.SH BUGS
+.Ed
+.Sh FILES
+.Bl -tag -width Ds
+.It gmqcc.ini.example
+A documented example for a gmqcc.ini file.
+.El
+.Sh SEE ALSO
+.Xr qcvm 1
+.Sh AUTHOR
+See <http://graphitemaster.github.com/gmqcc>.
+.Sh BUGS
Currently the '-fftepp-predefs' flag is not included by '-std=fteqcc',
partially because it is not entirely conformant to fteqcc.
-.sp
-
+.Pp
Please report bugs on <http://github.com/graphitemaster/gmqcc/issues>,
or see <http://graphitemaster.github.com/gmqcc> on how to contact us.
-.SH FILES
-.TP 20
-.B gmqcc.ini.example
-A documented example for a gmqcc.ini file.
-.SH SEE ALSO
-.IR qcvm (1)
-.SH AUTHOR
-See <http://graphitemaster.github.com/gmqcc>.
-.\" Process with groff -man -Tascii file.3
-.TH QCVM 1 2012-18-12 "" "gmqcc Manual"
-.SH NAME
-qcvm \- A standalone QuakeC VM binary executor.
-.SH SYNOPSIS
-.B qcvm
-[\fIOPTIONS\fR] [\fIPARAMS\fR] [\fIfile\fR]
-.SH DESCRIPTION
-qcvm is an executor for QuakeC VM binary files created using a QC
+.\" qcvm mdoc manpage
+.Dd January 31, 2013
+.Dt QCVM 1 PRM
+.Os
+.Sh NAME
+.Nm qcvm
+.Nd A standalone QuakeC VM binary executor
+.Sh SYNOPSIS
+.Nm qcvm
+.Op Cm options
+.Op Cm parameters
+.Ar program-file
+.Sh DESCRIPTION
+.Nm qcvm
+is an executor for QuakeC VM binary files created using a QC
compiler such as gmqcc(1) or fteqcc. It provides a small set of
-builtin functions, and by default executes the \fImain\fR function if
-there is one. Some options useful for debugging are available as well.
-.SH OPTIONS
+builtin functions, and by default executes the
+.Fn main
+function if there is one. Some options useful for debugging are
+available as well.
+.Sh OPTIONS
There are 2 types of options. Options for the executor, and parameter
options used to add parameters which are passed to the main function
on execution.
-.TP
-.B "-h, --help"
+.Bl -tag -width Ds
+.It Fl h , Fl -help
Show a usage message and exit.
-.TP
-.B "-trace"
+.It Fl trace
Trace the execution. Each instruction will be printed to stdout before
executing it.
-.TP
-.B "-profile"
+.It Fl profile
Perform some profiling. This is currently not really implemented, the
option is available nonetheless.
-.TP
-.B "-info"
+.It Fl info
Print information from the program's header instead of executing.
-.TP
-.B "-disasm"
+.It Fl disasm
Disassemble the program by function instead of executing.
-.TP
-.BI "-disasm-func" function
+.It Fl disasm-func Ar function
Search for and disassemble the given function.
-.TP
-.B "-printdefs"
+.It Fl printdefs
List all entries from the program's defs-section. Effectively
listing all the global variables of the program.
This option disables execution.
-.TP
-.B "-printfields"
+.It Fl printfields
List all entries from the program's fields-section. Listing all
entity-fields declared in the program.
This option disables execution.
-.TP
-.B "-printfuns"
+.It Fl printfuns
List functions and some information about their parameters.
This option disables execution. With a verbosity level of 1, builtin
numbers are printed. With a verbosity of 2, the function's sizes are
printed as well. This takes a little longer since the size is found by
-searching for a DONE instruction in the code.
-.TP
-.B "-v"
+searching for a
+.Ql DONE
+instruction in the code.
+.It Fl v
Increase verbosity level, can be used multiple times.
-.TP
-.BI "-vector """ "x y z" """"
-Append a vector parameter to be passed to \fImain\fR.
-.TP
-.BI "-float " number
-Append a float parameter to be passed to \fImain\fR.
-.TP
-.BI "-string """ "text" """"
-Append a string parameter to be passed to \fImain\fR.
-.SH BUILTINS
+.It Fl vector Ar 'x y z'
+Append a vector parameter to be passed to
+.Fn main Ns .
+.It Fl float Ar number
+Append a float parameter to be passed to
+.Fn main Ns .
+.It Fl string Ar 'text'
+Append a string parameter to be passed to
+.Fn main Ns .
+.El
+.Sh BUILTINS
The following builtin functions are available:
-.fi
-
-.RI "1) " void " print(" string... ") = " "#1" ;
-.in +8
-Print the passed strings to stdout. At most 8 strings are allowed.
-.in
-
-.RI "2) " string " ftos(" float ") = " "#2" ;
-.in +8
-Convert a float to a string.
-.in
-
-.RI "3) " entity " spawn() = " "#3" ;
-.in +8
-Spawn an entity.
-.in
-
-.RI "4) " void " remove(" entity ") = " "#4" ;
-.in +8
-Remove an entity.
-.in
-
-.RI "5) " string " vtos(" vector ") = " "#5" ;
-.in +8
-Convert a vector to a string.
-.in
-
-.RI "6) " void " error(" string... ") = " "#6" ;
-.in +8
-Print at most 8 strings to stdout and then exit with an error.
-.in
-
-.RI "7) " float " vlen(" vector ") = " "#7" ;
-.in +8
-Get the length of a vector.
-.in
-
-.RI "8) " string " etos(" entity ") = " "#8" ;
-.in +8
-Get the entity ID as string.
-.in
-
-.RI "9) " float " stof(" string ") = " "#9" ;
-.in +8
-Convert a string to a float.
-.in
-
-.RI "10) " string " strcat(" string ", " string ") = " "#10" ;
-.in +8
-Concatenate two strings, returning a tempstring.
-.in
-
-.RI "11) " float " strcmp (" string ", " string ") = " "#11" ;
-.fi
-.RI " " float " strncmp(" string ", " string ", " float ") = " "#11" ;
-.in +8
-Compare two strings. Returns the same as the corresponding C functions.
-.in
-
-.RI "12) " vector " normalize (" vector ") = " "#12" ;
-.in +8
-Normalize a vector so its length is 1.
-.in
-
-.RI "13) " float " sqrt (" float ") = " "#13" ;
-.in +8
-Get a value's square root.
-.in
-.SH BUGS
+.Bl -ohang
+.It Li 1) void print(string...) = #1;
+.D1 Print the passed strings to stdout. At most 8 strings are allowed.
+.It Li 2) string ftos(float) = #2;
+.D1 Convert a float to a string.
+.It Li 3) entity spawn() = #3;
+.D1 Spawn an entity.
+.It Li 4) void remove(entity) = #4;
+.D1 Remove an entity.
+.It Li 5) string vtos(vector) = #5;
+.D1 Convert a vector to a string.
+.It Li 6) void error(string...) = #6;
+.D1 Print at most 8 strings to stdout and then exit with an error.
+.It Li 7) float vlen(vector) = #7;
+.D1 Get the length of a vector.
+.It Li 8) string etos(entity) = #8;
+.D1 Get the entity ID as string.
+.It Li 9) float stof(string) = #9;
+.D1 Convert a string to a float.
+.It Li 10) string strcat(string, string) = #10;
+.D1 Concatenate two strings, returning a tempstring.
+.It Li 11) float strcmp(string, string) = #11;
+.Li 12) float strncmp(string, string, float) = #11;
+.D1 Compare two strings. Returns the same as the corresponding C functions.
+.It Li 12) vector normalize(vector) = #12;
+.D1 Normalize a vector so its length is 1.
+.It Li 13) float sqrt(float) = #13;
+.D1 Get a value's square root.
+.El
+.Sh SEE ALSO
+.Xr gmqcc 1
+.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.
-.SH SEE ALSO
-.IR gmqcc (1)
-.SH AUTHOR
-See <http://graphitemaster.github.com/gmqcc>.
putchar('\n');
}
-qc_program* prog_load(const char *filename)
+qc_program* prog_load(const char *filename, bool skipversion)
{
qc_program *prog;
prog_header header;
return NULL;
}
- if (header.version != 6) {
+ if (!skipversion && header.version != 6) {
loaderror("header says this is a version %i progs, we need version 6\n", header.version);
file_close(file);
return NULL;
prog = (qc_program*)mem_a(sizeof(qc_program));
if (!prog) {
file_close(file);
- printf("failed to allocate program data\n");
+ fprintf(stderr, "failed to allocate program data\n");
return NULL;
}
memset(prog, 0, sizeof(*prog));
{
if (e >= (qcint)vec_size(prog->entitypool)) {
prog->vmerror++;
- printf("Accessing out of bounds edict %i\n", (int)e);
+ fprintf(stderr, "Accessing out of bounds edict %i\n", (int)e);
e = 0;
}
return (qcany*)(prog->entitydata + (prog->entityfields * e));
{
if (!e) {
prog->vmerror++;
- printf("Trying to free world entity\n");
+ fprintf(stderr, "Trying to free world entity\n");
return;
}
if (e >= (qcint)vec_size(prog->entitypool)) {
prog->vmerror++;
- printf("Trying to free out of bounds entity\n");
+ fprintf(stderr, "Trying to free out of bounds entity\n");
return;
}
if (!prog->entitypool[e]) {
prog->vmerror++;
- printf("Double free on entity\n");
+ fprintf(stderr, "Double free on entity\n");
return;
}
prog->entitypool[e] = false;
#define CheckArgs(num) do { \
if (prog->argc != (num)) { \
prog->vmerror++; \
- printf("ERROR: invalid number of arguments for %s: %i, expected %i\n", \
+ fprintf(stderr, "ERROR: invalid number of arguments for %s: %i, expected %i\n", \
__FUNCTION__, prog->argc, (num)); \
return -1; \
} \
static int qc_error(qc_program *prog)
{
- printf("*** VM raised an error:\n");
+ fprintf(stderr, "*** VM raised an error:\n");
qc_print(prog);
prog->vmerror++;
return -1;
qcany out;
if (prog->argc != 2 && prog->argc != 3) {
- printf("ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n",
+ fprintf(stderr, "ERROR: invalid number of arguments for strcmp/strncmp: %i, expected 2 or 3\n",
prog->argc);
return -1;
}
arg->string = prog_tempstring(prog, main_params[i].value);
break;
default:
- printf("error: unhandled parameter type: %i\n", main_params[i].vtype);
+ fprintf(stderr, "error: unhandled parameter type: %i\n", main_params[i].vtype);
break;
}
}
}
else if (argv[1][0] != '-') {
if (progsfile) {
- printf("only 1 program file may be specified\n");
+ fprintf(stderr, "only 1 program file may be specified\n");
usage();
exit(1);
}
}
else
{
- printf("unknown parameter: %s\n", argv[1]);
+ fprintf(stderr, "unknown parameter: %s\n", argv[1]);
usage();
exit(1);
}
}
if (!progsfile) {
- printf("must specify a program to execute\n");
+ fprintf(stderr, "must specify a program to execute\n");
usage();
exit(1);
}
- prog = prog_load(progsfile);
+ prog = prog_load(progsfile, noexec);
if (!prog) {
- printf("failed to load program '%s'\n", progsfile);
+ fprintf(stderr, "failed to load program '%s'\n", progsfile);
exit(1);
}
}
if (opts_printdefs) {
for (i = 0; i < vec_size(prog->defs); ++i) {
- printf("Global: %8s %-16s at %u%s\n",
+ printf("Global: %8s %-16s at %u%s",
type_name[prog->defs[i].type & DEF_TYPEMASK],
prog_getstring(prog, prog->defs[i].name),
(unsigned int)prog->defs[i].offset,
((prog->defs[i].type & DEF_SAVEGLOBAL) ? " [SAVE]" : ""));
+ if (opts_v) {
+ switch (prog->defs[i].type & DEF_TYPEMASK) {
+ case TYPE_FLOAT:
+ printf(" [init: %g]", ((qcany*)(prog->globals + prog->defs[i].offset))->_float);
+ break;
+ case TYPE_INTEGER:
+ printf(" [init: %i]", (int)( ((qcany*)(prog->globals + prog->defs[i].offset))->_int ));
+ break;
+ case TYPE_ENTITY:
+ case TYPE_FUNCTION:
+ case TYPE_FIELD:
+ case TYPE_POINTER:
+ 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 ));
+ break;
+ default:
+ break;
+ }
+ }
+ printf("\n");
}
}
if (opts_printfields) {
prog_exec(prog, &prog->functions[fnmain], xflags, VM_JUMPS_DEFAULT);
}
else
- printf("No main function found\n");
+ fprintf(stderr, "No main function found\n");
}
prog_delete(prog);
char *ftepp_predef_date(lex_file *context) {
struct tm *itime;
time_t rtime;
- char *value = mem_a(82);
+ char *value = (char*)mem_a(82);
/* 82 is enough for strftime but we also have " " in our string */
(void)context;
char *ftepp_predef_time(lex_file *context) {
struct tm *itime;
time_t rtime;
- char *value = mem_a(82);
+ char *value = (char*)mem_a(82);
/* 82 is enough for strftime but we also have " " in our string */
(void)context;
{
pptoken *ptok;
while (ftepp->token != TOKEN_EOL && ftepp->token < TOKEN_EOF) {
- if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__"))
- ftepp->token = TOKEN_VA_ARGS;
- ptok = pptoken_make(ftepp);
- vec_push(macro->output, ptok);
- ftepp_next(ftepp);
+ bool subscript = false;
+ size_t index = 0;
+ if (macro->variadic && !strcmp(ftepp_tokval(ftepp), "__VA_ARGS__")) {
+ subscript = !!(ftepp_next(ftepp) == '#');
+
+ if (subscript && ftepp_next(ftepp) != '#') {
+ ftepp_error(ftepp, "expected `##` in __VA_ARGS__ for subscripting");
+ return false;
+ } else if (subscript) {
+ if (ftepp_next(ftepp) == '[') {
+ if (ftepp_next(ftepp) != TOKEN_INTCONST) {
+ ftepp_error(ftepp, "expected index for __VA_ARGS__ subscript");
+ return false;
+ }
+
+ index = atoi(ftepp_tokval(ftepp));
+
+ if (ftepp_next(ftepp) != ']') {
+ ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
+ return false;
+ }
+
+ /*
+ * mark it as an array to be handled later as such and not
+ * as traditional __VA_ARGS__
+ */
+ ftepp->token = TOKEN_VA_ARGS_ARRAY;
+ ptok = pptoken_make(ftepp);
+ ptok->constval.i = index;
+ vec_push(macro->output, ptok);
+ ftepp_next(ftepp);
+ } else {
+ ftepp_error(ftepp, "expected `[` for subscripting of __VA_ARGS__");
+ return false;
+ }
+ } else {
+ int old = ftepp->token;
+ ftepp->token = TOKEN_VA_ARGS;
+ ptok = pptoken_make(ftepp);
+ vec_push(macro->output, ptok);
+ ftepp->token = old;
+ }
+ }
+ else
+ {
+ ptok = pptoken_make(ftepp);
+ vec_push(macro->output, ptok);
+ ftepp_next(ftepp);
+ }
}
/* recursive expansion can cause EOFs here */
if (ftepp->token != TOKEN_EOL && ftepp->token != TOKEN_EOF) {
case TOKEN_KEYWORD:
macro = ftepp_macro_find(ftepp, ftepp_tokval(ftepp));
if (macro && ftepp->output_on) {
- if (ftepp_warn(ftepp, WARN_PREPROCESSOR, "redefining `%s`", ftepp_tokval(ftepp)))
+ if (ftepp_warn(ftepp, WARN_CPP, "redefining `%s`", ftepp_tokval(ftepp)))
return false;
ftepp_macro_delete(ftepp, ftepp_tokval(ftepp));
}
}
if (!varargs)
break;
+
pi = 0;
ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
for (++pi; pi < varargs; ++pi) {
ftepp_param_out(ftepp, ¶ms[pi + vararg_start]);
}
break;
+
+ case TOKEN_VA_ARGS_ARRAY:
+ if ((size_t)out->constval.i >= varargs) {
+ ftepp_error(ftepp, "subscript of `[%u]` is out of bounds for `__VA_ARGS__`", out->constval.i);
+ vec_free(old_string);
+ return false;
+ }
+
+ ftepp_param_out(ftepp, ¶ms[out->constval.i + vararg_start]);
+ break;
+
case TOKEN_IDENT:
case TOKEN_TYPENAME:
case TOKEN_KEYWORD:
default:
ftepp_error(ftepp, "junk in #if: `%s` ...", ftepp_tokval(ftepp));
- if (opts.debug)
+ if (OPTS_OPTION_BOOL(OPTION_DEBUG))
ftepp_error(ftepp, "internal: token %i\n", ftepp->token);
return false;
}
ftepp_error(ftepp, "#error %s", ftepp_tokval(ftepp));
}
+static void ftepp_directive_message(ftepp_t *ftepp) {
+ char *message = NULL;
+
+ if (!ftepp_skipspace(ftepp))
+ return;
+
+ /* handle the odd non string constant case so it works like C */
+ if (ftepp->token != TOKEN_STRINGCONST) {
+ vec_upload(message, "#message", 8);
+ ftepp_next(ftepp);
+ while (ftepp->token != TOKEN_EOL) {
+ vec_upload(message, ftepp_tokval(ftepp), strlen(ftepp_tokval(ftepp)));
+ ftepp_next(ftepp);
+ }
+ vec_push(message, '\0');
+ con_cprintmsg(&ftepp->lex->tok.ctx, LVL_MSG, "message", message);
+ vec_free(message);
+ return;
+ }
+
+ unescape (ftepp_tokval(ftepp), ftepp_tokval(ftepp));
+ con_cprintmsg(&ftepp->lex->tok.ctx, LVL_MSG, "message", ftepp_tokval(ftepp));
+}
+
/**
* Include a file.
* FIXME: do we need/want a -I option?
ftepp_directive_error(ftepp);
break;
}
+ else if (!strcmp(ftepp_tokval(ftepp), "message")) {
+ ftepp_directive_message(ftepp);
+ break;
+ }
else {
if (ftepp->output_on) {
ftepp_error(ftepp, "unrecognized preprocessor directive: `%s`", ftepp_tokval(ftepp));
/* set the right macro based on the selected standard */
ftepp_add_define(NULL, "GMQCC");
- if (opts.standard == COMPILER_FTEQCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
ftepp_add_define(NULL, "__STD_FTEQCC__");
/* 1.00 */
major[0] = '"';
minor[0] = '"';
minor[1] = '0';
minor[2] = '"';
- } else if (opts.standard == COMPILER_GMQCC) {
+ } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
ftepp_add_define(NULL, "__STD_GMQCC__");
sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
- } else if (opts.standard == COMPILER_QCCX) {
+ } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
ftepp_add_define(NULL, "__STD_QCCX__");
sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR);
sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR);
- } else if (opts.standard == COMPILER_QCC) {
+ } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
ftepp_add_define(NULL, "__STD_QCC__");
/* 1.0 */
major[0] = '"';
*/
#if defined(__GNUC__) || defined(__CLANG__)
# define GMQCC_WARN __attribute__((warn_unused_result))
+# define GMQCC_USED __attribute__((used))
#else
# define GMQCC_WARN
+# define GMQCC_USED
#endif
/*
* This is a hack to silent clang regarding empty
/*=========================== correct.c =============================*/
/*===================================================================*/
typedef struct {
- char ***edits;
+ char ***edits;
+ size_t **lens;
} correction_t;
void correct_del (correct_trie_t*, size_t **);
INSTR_BITAND,
INSTR_BITOR,
- /*
- * Virtual instructions used by the assembler
- * keep at the end but before virtual instructions
- * for the IR below.
- */
- AINSTR_END,
-
/*
* Virtual instructions used by the IR
* Keep at the end!
*/
+ VINSTR_END,
VINSTR_PHI,
VINSTR_JUMP,
VINSTR_COND,
int argc; /* current arg count for debugging */
} qc_program;
-qc_program* prog_load(const char *filename);
+qc_program* prog_load(const char *filename, bool ignoreversion);
void prog_delete(qc_program *prog);
bool prog_exec(qc_program *prog, prog_section_function *func, size_t flags, long maxjumps);
typedef struct {
const char *name;
longbit bit;
+ const char *description;
} opts_flag_def;
bool opts_setflag (const char *, bool);
enum {
# define GMQCC_TYPE_FLAGS
-# define GMQCC_DEFINE_FLAG(X) X,
+# define GMQCC_DEFINE_FLAG(X, Y) X,
# include "opts.def"
COUNT_FLAGS
};
static const opts_flag_def opts_flag_list[] = {
# define GMQCC_TYPE_FLAGS
-# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) },
+# define GMQCC_DEFINE_FLAG(X, Y) { #X, LONGBIT(X), Y},
# include "opts.def"
- { NULL, LONGBIT(0) }
+ { NULL, LONGBIT(0), "" }
};
enum {
# define GMQCC_TYPE_WARNS
-# define GMQCC_DEFINE_FLAG(X) WARN_##X,
+# define GMQCC_DEFINE_FLAG(X, Y) WARN_##X,
# include "opts.def"
COUNT_WARNINGS
};
static const opts_flag_def opts_warn_list[] = {
# define GMQCC_TYPE_WARNS
-# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(WARN_##X) },
+# define GMQCC_DEFINE_FLAG(X, Y) { #X, LONGBIT(WARN_##X), Y },
# include "opts.def"
- { NULL, LONGBIT(0) }
+ { NULL, LONGBIT(0), "" }
};
enum {
# define GMQCC_TYPE_OPTIMIZATIONS
-# define GMQCC_DEFINE_FLAG(NAME, MIN_O) OPTIM_##NAME,
+# define GMQCC_DEFINE_FLAG(NAME, MIN_O, Y) OPTIM_##NAME,
# include "opts.def"
COUNT_OPTIMIZATIONS
};
static const opts_flag_def opts_opt_list[] = {
# define GMQCC_TYPE_OPTIMIZATIONS
-# define GMQCC_DEFINE_FLAG(NAME, MIN_O) { #NAME, LONGBIT(OPTIM_##NAME) },
+# define GMQCC_DEFINE_FLAG(NAME, MIN_O, Y) { #NAME, LONGBIT(OPTIM_##NAME), Y},
# include "opts.def"
- { NULL, LONGBIT(0) }
+ { NULL, LONGBIT(0), "" }
};
static const unsigned int opts_opt_oflag[] = {
# define GMQCC_TYPE_OPTIMIZATIONS
-# define GMQCC_DEFINE_FLAG(NAME, MIN_O) MIN_O,
+# define GMQCC_DEFINE_FLAG(NAME, MIN_O, Y) MIN_O,
# include "opts.def"
0
};
+
+enum {
+# define GMQCC_TYPE_OPTIONS
+# define GMQCC_DEFINE_FLAG(X, Y) OPTION_##X,
+# include "opts.def"
+ OPTION_COUNT
+};
+
+
+GMQCC_USED static const char *opts_options_descriptions[] = {
+# define GMQCC_TYPE_OPTIONS
+# define GMQCC_DEFINE_FLAG(X, Y) Y,
+# include "opts.def"
+ ""
+};
+
extern unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
/* other options: */
COMPILER_GMQCC /* this QuakeC */
} opts_std_t;
-/* TODO: cleanup this */
+typedef union {
+ bool B;
+ uint16_t U16;
+ uint32_t U32;
+ char *STR;
+} opt_value_t;
+
+
typedef struct {
- uint32_t O; /* -Ox */
- const char *output; /* -o file */
- bool quiet; /* -q --quiet */
- bool g; /* -g */
- opts_std_t standard; /* -std= */
- bool debug; /* -debug */
- bool memchk; /* -memchk */
- bool dumpfin; /* -dumpfin */
- bool dump; /* -dump */
- bool forcecrc; /* --force-crc= */
- uint16_t forced_crc; /* --force-crc= */
- bool pp_only; /* -E */
- size_t max_array_size; /* --max-array= */
- bool add_info; /* --add-info */
- bool correction; /* --correct */
-
- uint32_t flags [1 + (COUNT_FLAGS / 32)];
- uint32_t warn [1 + (COUNT_WARNINGS / 32)];
- uint32_t werror [1 + (COUNT_WARNINGS / 32)];
- uint32_t warn_backup [1 + (COUNT_WARNINGS / 32)];
- uint32_t werror_backup[1 + (COUNT_WARNINGS / 32)];
- uint32_t optimization [1 + (COUNT_OPTIMIZATIONS / 32)];
+ opt_value_t options [OPTION_COUNT];
+ uint32_t flags [1 + (COUNT_FLAGS / 32)];
+ uint32_t warn [1 + (COUNT_WARNINGS / 32)];
+ uint32_t werror [1 + (COUNT_WARNINGS / 32)];
+ uint32_t warn_backup [1 + (COUNT_WARNINGS / 32)];
+ uint32_t werror_backup[1 + (COUNT_WARNINGS / 32)];
+ uint32_t optimization [1 + (COUNT_OPTIMIZATIONS / 32)];
} opts_cmd_t;
extern opts_cmd_t opts;
#define OPTS_WARN(i) OPTS_GENERIC(opts.warn, (i))
#define OPTS_WERROR(i) OPTS_GENERIC(opts.werror, (i))
#define OPTS_OPTIMIZATION(i) OPTS_GENERIC(opts.optimization, (i))
+#define OPTS_OPTION_BOOL(X) (opts.options[X].B)
+#define OPTS_OPTION_U16(X) (opts.options[X].U16)
+#define OPTS_OPTION_U32(X) (opts.options[X].U32)
+#define OPTS_OPTION_STR(X) (opts.options[X].STR)
#endif
INSTR_STORE_V, /* variant, should never be accessed */
- AINSTR_END, /* struct */
- AINSTR_END, /* union */
- AINSTR_END, /* array */
- AINSTR_END, /* nil */
- AINSTR_END, /* noexpr */
+ VINSTR_END, /* struct */
+ VINSTR_END, /* union */
+ VINSTR_END, /* array */
+ VINSTR_END, /* nil */
+ VINSTR_END, /* noexpr */
};
uint16_t field_store_instr[TYPE_COUNT] = {
INSTR_STORE_V, /* variant, should never be accessed */
- AINSTR_END, /* struct */
- AINSTR_END, /* union */
- AINSTR_END, /* array */
- AINSTR_END, /* nil */
- AINSTR_END, /* noexpr */
+ VINSTR_END, /* struct */
+ VINSTR_END, /* union */
+ VINSTR_END, /* array */
+ VINSTR_END, /* nil */
+ VINSTR_END, /* noexpr */
};
uint16_t type_storep_instr[TYPE_COUNT] = {
INSTR_STOREP_V, /* variant, should never be accessed */
- AINSTR_END, /* struct */
- AINSTR_END, /* union */
- AINSTR_END, /* array */
- AINSTR_END, /* nil */
- AINSTR_END, /* noexpr */
+ VINSTR_END, /* struct */
+ VINSTR_END, /* union */
+ VINSTR_END, /* array */
+ VINSTR_END, /* nil */
+ VINSTR_END, /* noexpr */
};
uint16_t type_eq_instr[TYPE_COUNT] = {
INSTR_EQ_V, /* variant, should never be accessed */
- AINSTR_END, /* struct */
- AINSTR_END, /* union */
- AINSTR_END, /* array */
- AINSTR_END, /* nil */
- AINSTR_END, /* noexpr */
+ VINSTR_END, /* struct */
+ VINSTR_END, /* union */
+ VINSTR_END, /* array */
+ VINSTR_END, /* nil */
+ VINSTR_END, /* noexpr */
};
uint16_t type_ne_instr[TYPE_COUNT] = {
INSTR_NE_V, /* variant, should never be accessed */
- AINSTR_END, /* struct */
- AINSTR_END, /* union */
- AINSTR_END, /* array */
- AINSTR_END, /* nil */
- AINSTR_END, /* noexpr */
+ VINSTR_END, /* struct */
+ VINSTR_END, /* union */
+ VINSTR_END, /* array */
+ VINSTR_END, /* nil */
+ VINSTR_END, /* noexpr */
};
uint16_t type_not_instr[TYPE_COUNT] = {
INSTR_NOT_V, /* variant, should never be accessed */
- AINSTR_END, /* struct */
- AINSTR_END, /* union */
- AINSTR_END, /* array */
- AINSTR_END, /* nil */
- AINSTR_END, /* noexpr */
+ VINSTR_END, /* struct */
+ VINSTR_END, /* union */
+ VINSTR_END, /* array */
+ VINSTR_END, /* nil */
+ VINSTR_END, /* noexpr */
};
/* protos */
irerror(call->context, "internal error: unlocked parameter %s not found", v->name);
goto error;
}
-
++opts_optimizationcount[OPTIM_CALL_STORES];
v->callparam = true;
if (param < 8)
ir_value_code_setaddr(v, OFS_PARM0 + 3*param);
else {
+ size_t nprotos = vec_size(self->owner->extparam_protos);
ir_value *ep;
param -= 8;
- if (vec_size(self->owner->extparam_protos) <= param)
- ep = ir_gen_extparam_proto(self->owner);
- else
+ if (nprotos > param)
ep = self->owner->extparam_protos[param];
+ else
+ {
+ ep = ir_gen_extparam_proto(self->owner);
+ while (++nprotos <= param)
+ ep = ir_gen_extparam_proto(self->owner);
+ }
ir_instr_op(v->writes[0], 0, ep, true);
call->params[param+8] = ep;
}
ir_value *global;
char name[128];
- snprintf(name, sizeof(name), "EXTPARM#%i", (int)(vec_size(ir->extparam_protos)+8));
+ snprintf(name, sizeof(name), "EXTPARM#%i", (int)(vec_size(ir->extparam_protos)));
global = ir_value_var(name, store_global, TYPE_VECTOR);
vec_push(ir->extparam_protos, global);
irfun = global->constval.vfunc;
def = code_functions + irfun->code_function_def;
- if (opts.g || !OPTS_OPTIMIZATION(OPTIM_OVERLAP_LOCALS) || (irfun->flags & IR_FLAG_MASK_NO_OVERLAP))
+ if (OPTS_OPTION_BOOL(OPTION_G) ||
+ !OPTS_OPTIMIZATION(OPTIM_OVERLAP_LOCALS) ||
+ (irfun->flags & IR_FLAG_MASK_NO_OVERLAP))
+ {
firstlocal = def->firstlocal = vec_size(code_globals);
- else {
+ } else {
firstlocal = def->firstlocal = ir->first_common_local;
++opts_optimizationcount[OPTIM_OVERLAP_LOCALS];
}
def.type = global->vtype;
def.offset = vec_size(code_globals);
def.name = 0;
- if (opts.g || !islocal)
+ if (OPTS_OPTION_BOOL(OPTION_G) || !islocal)
{
pushdef = true;
def.offset = (uint16_t)vec_size(code_globals);
/* create a global named the same as the field */
- if (opts.standard == COMPILER_GMQCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
/* in our standard, the global gets a dot prefix */
size_t len = strlen(field->name);
char name[1024];
code_push_statement(&stmt, vec_last(code_linenums));
}
- if (opts.pp_only)
+ if (OPTS_OPTION_BOOL(OPTION_PP_ONLY))
return true;
if (vec_size(code_statements) != vec_size(code_linenums)) {
memcpy(vec_add(lnofile, 5), ".lno", 5);
}
- if (!opts.quiet) {
+ if (!OPTS_OPTION_BOOL(OPTION_QUIET)) {
if (lnofile)
con_out("writing '%s' and '%s'...\n", filename, lnofile);
else
}
if (ch == '+' || ch == '-' || /* ++, --, +=, -= and -> as well! */
- ch == '>' || ch == '<' || /* <<, >>, <=, >= */
- ch == '=' || ch == '!' || /* ==, != */
- ch == '&' || ch == '|') /* &&, ||, &=, |= */
- {
+ ch == '>' || ch == '<' || /* <<, >>, <=, >= */
+ ch == '=' || ch == '!' || /* ==, != */
+ ch == '&' || ch == '|' || /* &&, ||, &=, |= */
+ ch == '~' /* ~=, ~ */
+ ) {
lex_tokench(lex, ch);
nextch = lex_getch(lex);
if (!strcmp(v, keywords_qc[kw]))
return (lex->tok.ttype = TOKEN_KEYWORD);
}
- if (opts.standard != COMPILER_QCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) {
for (kw = 0; kw < num_keywords_fg; ++kw) {
if (!strcmp(v, keywords_fg[kw]))
return (lex->tok.ttype = TOKEN_KEYWORD);
return (lex->tok.ttype = ch);
}
- lexerror(lex, "unknown token");
+ lexerror(lex, "unknown token: `%s`", lex->tok.value);
return (lex->tok.ttype = TOKEN_ERROR);
}
TOKEN_ATTRIBUTE_CLOSE, /* ]] */
TOKEN_VA_ARGS, /* for the ftepp only */
+ TOKEN_VA_ARGS_ARRAY, /* for the ftepp only */
TOKEN_STRINGCONST, /* not the typename but an actual "string" */
TOKEN_CHARCONST,
opts_set(opts.flags, INITIALIZED_NONCONSTANTS, true);
opts_set(opts.werror, WARN_INVALID_PARAMETER_COUNT, true);
opts_set(opts.werror, WARN_MISSING_RETURN_VALUES, true);
- opts.standard = COMPILER_GMQCC;
+
+
+ OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC;
} else if (!strcmp(argarg, "qcc")) {
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
- opts.standard = COMPILER_QCC;
+
+ OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCC;
} else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
opts_set(opts.flags, CORRECT_TERNARY, false);
opts_set(opts.warn, WARN_TERNARY_PRECEDENCE, true);
- opts.standard = COMPILER_FTEQCC;
+
+ OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_FTEQCC;
} else if (!strcmp(argarg, "qccx")) {
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
- opts.standard = COMPILER_QCCX;
+ OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCCX;
} else {
con_out("Unknown standard: %s\n", argarg);
continue;
}
if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
- opts.forcecrc = true;
- opts.forced_crc = strtol(argarg, NULL, 0);
+
+ OPTS_OPTION_BOOL(OPTION_FORCECRC) = true;
+ OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, NULL, 0);
continue;
}
if (options_long_gcc("redirout", &argc, &argv, &redirout)) {
}
if (!strcmp(argv[0]+1, "debug")) {
- opts.debug = true;
+ OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
continue;
}
if (!strcmp(argv[0]+1, "dump")) {
- opts.dump = true;
+ OPTS_OPTION_BOOL(OPTION_DUMP) = true;
continue;
}
if (!strcmp(argv[0]+1, "dumpfin")) {
- opts.dumpfin = true;
+ OPTS_OPTION_BOOL(OPTION_DUMPFIN) = true;
continue;
}
if (!strcmp(argv[0]+1, "memchk")) {
- opts.memchk = true;
+ OPTS_OPTION_BOOL(OPTION_MEMCHK) = true;
continue;
}
if (!strcmp(argv[0]+1, "nocolor")) {
exit(0);
case 'E':
- opts.pp_only = true;
+ OPTS_OPTION_BOOL(OPTION_PP_ONLY) = true;
opts_set(opts.flags, FTEPP_PREDEFS, true); /* predefs on for -E */
break;
/* debug turns on -flno */
case 'g':
opts_setflag("LNO", true);
- opts.g = true;
+ OPTS_OPTION_BOOL(OPTION_G) = true;
break;
case 'q':
- opts.quiet = true;
+ OPTS_OPTION_BOOL(OPTION_QUIET) = true;
break;
case 'D':
case 'f':
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
- con_out("Possible flags:\n");
+ con_out("Possible flags:\n\n");
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
- con_out(" -f%s\n", buffer);
+ con_out(" -f%s:\n%s\n\n", buffer, opts_flag_list[itr].description);
}
exit(0);
}
con_out("Possible warnings:\n");
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
- con_out(" -W%s\n", buffer);
+ con_out(" -W%s:\n%s\n\n\n", buffer, opts_warn_list[itr].description);
}
exit(0);
}
return false;
}
if (isdigit(argarg[0])) {
- opts.O = atoi(argarg);
- opts_setoptimlevel(opts.O);
+ uint32_t val = atoi(argarg);
+ OPTS_OPTION_U32(OPTION_O) = val;
+ opts_setoptimlevel(val);
} else {
util_strtocmd(argarg, argarg, strlen(argarg)+1);
if (!strcmp(argarg, "HELP")) {
con_out("Possible optimizations:\n");
for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
util_strtononcmd(opts_opt_list[itr].name, buffer, sizeof(buffer));
- con_out(" -O%-20s (-O%u)\n", buffer, opts_opt_oflag[itr]);
+ con_out(" -O%-20s (-O%u):\n%s\n\n", buffer, opts_opt_oflag[itr], opts_opt_list[itr].description);
}
exit(0);
}
else if (!strcmp(argarg, "ALL"))
- opts_setoptimlevel(opts.O = 9999);
+ opts_setoptimlevel(OPTS_OPTION_U32(OPTION_O) = 9999);
else if (!strncmp(argarg, "NO_", 3)) {
if (!opts_setoptim(argarg+3, false)) {
con_out("unknown optimization: %s\n", argarg+3);
con_out("option -o requires an argument: the output file name\n");
return false;
}
- opts.output = argarg;
+ OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
opts_output_wasset = true;
break;
}
/* All long options without arguments */
else if (!strcmp(argv[0]+2, "help")) {
- usage();
+ /* TODO .. map name back .. prittery print of
+ * options and their associations.
+ */
+ for (itr = 0; itr < OPTION_COUNT; itr++) {
+ con_out("%s\n\n", opts_options_descriptions[itr]);
+ }
+
exit(0);
}
else if (!strcmp(argv[0]+2, "version")) {
exit(0);
}
else if (!strcmp(argv[0]+2, "quiet")) {
- opts.quiet = true;
+ OPTS_OPTION_BOOL(OPTION_QUIET) = true;
break;
}
else if (!strcmp(argv[0]+2, "correct")) {
- opts.correction = true;
+ OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
break;
}
else if (!strcmp(argv[0]+2, "no-correct")) {
- opts.correction = false;
+ OPTS_OPTION_BOOL(OPTION_CORRECTION) = false;
break;
}
else if (!strcmp(argv[0]+2, "add-info")) {
- opts.add_info = true;
+ OPTS_OPTION_BOOL(OPTION_ADD_INFO) = true;
break;
}
else {
/* All long options with arguments */
if (options_long_witharg("output", &argc, &argv, &argarg)) {
- opts.output = argarg;
+ OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
opts_output_wasset = true;
} else {
con_out("Unknown parameter: %s\n", argv[0]);
}
/* the standard decides which set of operators to use */
- if (opts.standard == COMPILER_GMQCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
operators = c_operators;
operator_count = c_operator_count;
- } else if (opts.standard == COMPILER_FTEQCC) {
+ } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
operators = fte_operators;
operator_count = fte_operator_count;
} else {
}
}
- if (opts.dump) {
+ if (OPTS_OPTION_BOOL(OPTION_DUMP)) {
for (itr = 0; itr < COUNT_FLAGS; ++itr)
con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
for (itr = 0; itr < COUNT_WARNINGS; ++itr)
con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
- con_out("output = %s\n", opts.output);
- con_out("optimization level = %d\n", opts.O);
- con_out("standard = %i\n", opts.standard);
+ con_out("output = %s\n", OPTS_OPTION_STR(OPTION_OUTPUT));
+ con_out("optimization level = %u\n", OPTS_OPTION_U32(OPTION_O));
+ con_out("standard = %u\n", OPTS_OPTION_U32(OPTION_STANDARD));
}
- if (opts.pp_only) {
+ if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (opts_output_wasset) {
- outfile = file_open(opts.output, "wb");
+ outfile = file_open(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
if (!outfile) {
- con_err("failed to open `%s` for writing\n", opts.output);
+ con_err("failed to open `%s` for writing\n", OPTS_OPTION_STR(OPTION_OUTPUT));
retval = 1;
goto cleanup;
}
}
}
- if (!opts.pp_only) {
+ if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (!parser_init()) {
con_err("failed to initialize parser\n");
retval = 1;
}
}
- if (opts.pp_only || OPTS_FLAG(FTEPP)) {
+ if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
if (!ftepp_init()) {
con_err("failed to initialize parser\n");
retval = 1;
util_debug("COM", "starting ...\n");
/* add macros */
- if (opts.pp_only || OPTS_FLAG(FTEPP)) {
+ if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
for (itr = 0; itr < vec_size(ppems); itr++) {
ftepp_add_macro(ppems[itr].name, ppems[itr].value);
mem_d(ppems[itr].name);
}
if (!opts_output_wasset) {
- opts.output = util_strdup(line);
+ OPTS_OPTION_STR(OPTION_OUTPUT) = util_strdup(line);
opts_output_free = true;
}
goto cleanup;
if (vec_size(items)) {
- if (!opts.quiet && !opts.pp_only) {
+ if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
+ !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
+ {
con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
}
for (itr = 0; itr < vec_size(items); ++itr) {
- if (!opts.quiet && !opts.pp_only) {
+ if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
+ !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
+ {
con_out(" item: %s (%s)\n",
items[itr].filename,
( (items[itr].type == TYPE_QC ? "qc" :
("unknown"))))));
}
- if (opts.pp_only) {
+ if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
const char *out;
if (!ftepp_preprocess_file(items[itr].filename)) {
retval = 1;
}
ftepp_finish();
- if (!opts.pp_only) {
- if (!parser_finish(opts.output)) {
+ if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
+ if (!parser_finish(OPTS_OPTION_STR(OPTION_OUTPUT))) {
retval = 1;
goto cleanup;
}
}
/* stuff */
- if (!opts.quiet && !opts.pp_only) {
+ if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
+ !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
+ {
for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
if (opts_optimizationcount[itr]) {
con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)opts_optimizationcount[itr]);
vec_free(items);
vec_free(ppems);
- if (!opts.pp_only)
+ if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
parser_cleanup();
if (opts_output_free)
- mem_d((char*)opts.output);
+ mem_d(OPTS_OPTION_STR(OPTION_OUTPUT));
if (operators_free)
mem_d((void*)operators);
static void opts_setdefault() {
memset(&opts, 0, sizeof(opts_cmd_t));
- opts.correction = true;
+ OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
/* warnings */
opts_set(opts.warn, WARN_UNUSED_VARIABLE, true);
opts_set(opts.warn, WARN_FIELD_REDECLARED, true);
opts_set(opts.warn, WARN_MISSING_RETURN_VALUES, true);
opts_set(opts.warn, WARN_INVALID_PARAMETER_COUNT, true);
- opts_set(opts.warn, WARN_LOCAL_SHADOWS, false);
opts_set(opts.warn, WARN_LOCAL_CONSTANTS, true);
opts_set(opts.warn, WARN_VOID_VARIABLES, true);
opts_set(opts.warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
opts_set(opts.warn, WARN_EFFECTLESS_STATEMENT, true);
opts_set(opts.warn, WARN_END_SYS_FIELDS, true);
opts_set(opts.warn, WARN_ASSIGN_FUNCTION_TYPES, true);
- opts_set(opts.warn, WARN_PREPROCESSOR, true);
+ opts_set(opts.warn, WARN_CPP, true);
opts_set(opts.warn, WARN_MULTIFILE_IF, true);
opts_set(opts.warn, WARN_DOUBLE_DECLARATION, true);
opts_set(opts.warn, WARN_CONST_VAR, true);
opts_set(opts.warn, WARN_MULTIBYTE_CHARACTER, true);
opts_set(opts.warn, WARN_UNKNOWN_PRAGMAS, true);
opts_set(opts.warn, WARN_UNREACHABLE_CODE, true);
- opts_set(opts.warn, WARN_CPP, true);
opts_set(opts.warn, WARN_UNKNOWN_ATTRIBUTE, true);
opts_set(opts.warn, WARN_RESERVED_NAMES, true);
opts_set(opts.warn, WARN_UNINITIALIZED_CONSTANT, true);
- opts_set(opts.warn, WARN_UNINITIALIZED_GLOBAL, false);
opts_set(opts.warn, WARN_DEPRECATED, true);
opts_set(opts.warn, WARN_PARENTHESIS, true);
+
/* flags */
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
- opts_set(opts.flags, FTEPP, false);
- opts_set(opts.flags, FTEPP_PREDEFS, false);
opts_set(opts.flags, CORRECT_TERNARY, true);
opts_set(opts.flags, BAIL_ON_WERROR, true);
opts_set(opts.flags, LEGACY_VECTOR_MATHS, true);
void opts_init(const char *output, int standard, size_t arraysize) {
opts_setdefault();
- opts.output = output;
- opts.standard = (opts_std_t)standard; /* C++ ... y u no like me? */
- opts.max_array_size = arraysize;
+ OPTS_OPTION_STR(OPTION_OUTPUT) = (char*)output;
+ OPTS_OPTION_U32(OPTION_STANDARD) = standard;
+ OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
}
static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
for (i = 0; i < listsize; ++i) {
if (!strcmp(name, list[i].name)) {
longbit lb = list[i].bit;
-#if 1
+
if (on)
flags[lb.idx] |= (1<<(lb.bit));
else
flags[lb.idx] &= ~(1<<(lb.bit));
-#else
- if (on)
- flags[0] |= (1<<lb);
- else
- flags[0] &= ~(1<<(lb));
-#endif
+
return true;
}
}
void opts_set(uint32_t *flags, size_t idx, bool on) {
longbit lb;
LONGBIT_SET(lb, idx);
-#if 1
+
if (on)
flags[lb.idx] |= (1<<(lb.bit));
else
flags[lb.idx] &= ~(1<<(lb.bit));
-#else
- if (on)
- flags[0] |= (1<<(lb));
- else
- flags[0] &= ~(1<<(lb));
-#endif
}
void opts_setoptimlevel(unsigned int level) {
/* flags */
#define GMQCC_TYPE_FLAGS
- #define GMQCC_DEFINE_FLAG(X) \
+ #define GMQCC_DEFINE_FLAG(X, Y) \
if (!strcmp(section, "flags") && !strcmp(name, #X)) { \
opts_set(opts.flags, X, opts_ini_bool(value)); \
found = true; \
/* warnings */
#define GMQCC_TYPE_WARNS
- #define GMQCC_DEFINE_FLAG(X) \
+ #define GMQCC_DEFINE_FLAG(X, Y) \
if (!strcmp(section, "warnings") && !strcmp(name, #X)) { \
opts_set(opts.warn, WARN_##X, opts_ini_bool(value)); \
found = true; \
/* Werror-individuals */
#define GMQCC_TYPE_WARNS
- #define GMQCC_DEFINE_FLAG(X) \
+ #define GMQCC_DEFINE_FLAG(X, Y) \
if (!strcmp(section, "errors") && !strcmp(name, #X)) { \
opts_set(opts.werror, WARN_##X, opts_ini_bool(value)); \
found = true; \
/* optimizations */
#define GMQCC_TYPE_OPTIMIZATIONS
- #define GMQCC_DEFINE_FLAG(X,Y) \
+ #define GMQCC_DEFINE_FLAG(X,Y,Z) \
if (!strcmp(section, "optimizations") && !strcmp(name, #X)) { \
opts_set(opts.optimization, OPTIM_##X, opts_ini_bool(value)); \
found = true; \
* SOFTWARE.
*/
#ifndef GMQCC_DEFINE_FLAG
-# define GMQCC_DEFINE_FLAG(x)
+# ifdef GMQCC_TYPE_OPTIMIZATIONS
+# define GMQCC_DEFINE_FLAG(X, Y, Z)
+# else
+# define GMQCC_DEFINE_FLAG(X, Y)
+# endif /* !GMQCC_TYPE_OPTIMIZATIONS */
#endif
/* codegen flags */
#ifdef GMQCC_TYPE_FLAGS
- GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
- GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
- GMQCC_DEFINE_FLAG(FTEPP)
- GMQCC_DEFINE_FLAG(FTEPP_PREDEFS)
- GMQCC_DEFINE_FLAG(RELAXED_SWITCH)
- GMQCC_DEFINE_FLAG(SHORT_LOGIC)
- GMQCC_DEFINE_FLAG(PERL_LOGIC)
- GMQCC_DEFINE_FLAG(TRANSLATABLE_STRINGS)
- GMQCC_DEFINE_FLAG(INITIALIZED_NONCONSTANTS)
- GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES)
- GMQCC_DEFINE_FLAG(LNO)
- GMQCC_DEFINE_FLAG(CORRECT_TERNARY)
- GMQCC_DEFINE_FLAG(SINGLE_VECTOR_DEFS)
- GMQCC_DEFINE_FLAG(CORRECT_LOGIC)
- GMQCC_DEFINE_FLAG(TRUE_EMPTY_STRINGS)
- GMQCC_DEFINE_FLAG(FALSE_EMPTY_STRINGS)
- GMQCC_DEFINE_FLAG(UTF8)
- GMQCC_DEFINE_FLAG(BAIL_ON_WERROR)
- GMQCC_DEFINE_FLAG(LOOP_LABELS)
- GMQCC_DEFINE_FLAG(UNTYPED_NIL)
- GMQCC_DEFINE_FLAG(PERMISSIVE)
- GMQCC_DEFINE_FLAG(VARIADIC_ARGS)
- GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS)
+ GMQCC_DEFINE_FLAG (
+ DARKPLACES_STRING_TABLE_BUG,
+
+ "Add some additional characters to the string table in order to\n"
+ "compensate for a wrong boundcheck in some specific version of the\n"
+ "darkplaces engine."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ ADJUST_VECTOR_FIELDS,
+
+ "When assigning to field pointers of type .vector the common be\n"
+ "haviour in compilers like fteqcc is to only assign the x-compo-\n"
+ "nent of the pointer. This means that you can use the vector as\n"
+ "such, but you cannot use its y and z components directly. This\n"
+ "flag fixes this behaviour. Before using it make sure your code\n"
+ "does not depend on the buggy behaviour."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ FTEPP,
+
+ "Enable a partially fteqcc-compatible preprocessor. It supports\n"
+ "all the features used in the Xonotic codebase. If you need more,\n"
+ "write a ticket."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ FTEPP_PREDEFS,
+
+ "Enable some predefined macros. This only works in combination\n"
+ "with '-fftepp' and is currently not included by '-std=fteqcc'.\n"
+ "The following macros will be added:\n\n"
+ " __LINE__\n"
+ " __FILE__\n"
+ " __COUNTER__\n"
+ " __COUNTER_LAST__\n"
+ " __RANDOM__\n"
+ " __RANDOM_LAST__\n"
+ " __DATE__\n"
+ " __TIME__\n\n"
+ "Note that fteqcc also defines __NULL__ which is not implemented\n"
+ "yet. (See -funtyped-nil about gmqcc's alternative to __NULL__)."
+ )
+
+
+ GMQCC_DEFINE_FLAG (
+ RELAXED_SWITCH,
+
+ "Allow switch cases to use non constant variables."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ SHORT_LOGIC,
+
+ "Perform early out in logical AND and OR expressions. The final\n"
+ "result will be either a 0 or a 1, see the next flag for more pos-\n"
+ "sibilities."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ PERL_LOGIC,
+
+ "In many languages, logical expressions perform early out in a\n"
+ "special way: If the left operand of an AND yeilds true, or the\n"
+ "one of an OR yields false, the complete expression evaluates to\n"
+ "the right side. Thus ‘true && 5’ evaluates to 5 rather than 1."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ TRANSLATABLE_STRINGS,
+
+ "Enable the underscore intrinsic: Using ‘_(\"A string constant\")’\n"
+ "will cause the string immediate to get a name with a \"dotrans-\n"
+ "late_\" prefix. The darkplaces engine recognizes these and trans-\n"
+ "lates them in a way similar to how gettext works."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ INITIALIZED_NONCONSTANTS,
+
+ "Don't implicitly convert initialized variables to constants. With\n"
+ "this flag, the const keyword is required to make a constant."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ ASSIGN_FUNCTION_TYPES,
+
+ "If this flag is not set, (and it is set by default in the qcc and\n"
+ "fteqcc standards), assigning function pointers of mismatching\n"
+ "signatures will result in an error rather than a warning."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ LNO,
+
+ "Produce a linenumber file along with the output .dat file."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ CORRECT_TERNARY,
+
+ "Use C's operator precedence for ternary expressions. Unless\n"
+ "code depends on fteqcc-compatible behaviour, you'll want to use\n"
+ "this option."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ SINGLE_VECTOR_DEFS,
+
+ "Normally vectors generate 4 defs, once for the vector, and once\n"
+ "for its components with _x, _y, _z suffixes. This option prevents\n"
+ "components from being listed."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ CORRECT_LOGIC,
+
+ "Most QC compilers translate ‘if(a_vector)’ directly as an IF on\n"
+ "the vector, which means only the x-component is checked. This\n"
+ "option causes vectors to be cast to actual booleans via a NOT_V\n"
+ "and, if necessary, a NOT_F chained to it."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ TRUE_EMPTY_STRINGS,
+
+ "An empty string is considered to be true everywhere. The NOT_S\n"
+ "instruction usually considers an empty string to be false, this\n"
+ "option effectively causes the unary not in strings to use NOT_F\n"
+ "instead."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ FALSE_EMPTY_STRINGS,
+
+ "An empty string is considered to be false everywhere. This means\n"
+ "loops and if statements which depend on a string will perform a\n"
+ "NOT_S instruction on the string before using it."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ UTF8,
+
+ "Enable utf8 characters. This allows utf-8 encoded character con-\n"
+ "stants, and escape sequence codepoints in the valid utf-8 range.\n"
+ "Effectively enabling escape sequences like '\\{x2211}'."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ BAIL_ON_WERROR,
+
+ "When a warning is treated as an error, and this option is set\n"
+ "(which it is by default), it is like any other error and will\n"
+ "cause compilation to stop. When disabling this flag by using\n"
+ "-fno-bail-on-werror, compilation will continue until the end, but\n"
+ "no output is generated. Instead the first such error message's\n"
+ "context is shown."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ LOOP_LABELS,
+
+ "Allow loops to be labeled, and allow 'break' and 'continue' to\n"
+ "take an optional label to decide which loop to actually jump out\n"
+ "of or continue.\n\n"
+ " for :outer (i = 0; i < n; ++i) {\n"
+ " while (inner) {\n"
+ " ...;\n"
+ " if (something)\n"
+ " continue outer;\n"
+ " }\n"
+ " }"
+ )
+
+ GMQCC_DEFINE_FLAG (
+ UNTYPED_NIL,
+
+ "Adds a global named 'nil' which is of no type and can be assigned\n"
+ "to anything. No typechecking will be performed on assignments.\n"
+ "Assigning to it is forbidden, using it in any other kind of\n"
+ "expression is also not allowed.\n\n"
+ "Note that this is different from fteqcc's __NULL__: In fteqcc,\n"
+ "__NULL__ maps to the integer written as '0i'. It's can be\n"
+ "assigned to function pointers and integers, but it'll error about\n"
+ "invalid instructions when assigning it to floats without enabling\n"
+ "the FTE instruction set. There's also a bug which allows it to be\n"
+ "assigned to vectors, for which the source will be the global at\n"
+ "offset 0, meaning the vector's y and z components will contain\n"
+ "the OFS_RETURN x and y components.\n\n"
+ "In that gmqcc the nil global is an actual global filled with\n"
+ "zeroes, and can be assigned to anything including fields, vectors\n"
+ "or function pointers, and they end up becoming zeroed."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ PERMISSIVE,
+
+ "Various effects, usually to weaken some conditions.\n\n"
+ " with -funtyped-nil\n"
+ " Allow local variables named ‘nil’. (This will not\n"
+ " allow declaring a global of that name.)"
+ )
+
+ GMQCC_DEFINE_FLAG (
+ VARIADIC_ARGS,
+
+ "Allow variadic parameters to be accessed by QC code. This can be\n"
+ "achieved via the '...' function, which takes a parameter index\n"
+ "and a typename.\n\n"
+ "Example:\n"
+ " void vafunc(string...count) {\n"
+ " float i;\n"
+ " for (i = 0; i < count; ++i)\n"
+ " print(...(i, string), \"\\n\");\n"
+ " }"
+ )
+
+ GMQCC_DEFINE_FLAG (
+ LEGACY_VECTOR_MATHS,
+
+ "Most Quake VMs, including the one from FTEQW or up till recently\n"
+ "Darkplaces, do not cope well with vector instructions with over‐\n"
+ "lapping input and output. This option will avoid producing such\n"
+ "code."
+ )
#endif
/* warning flags */
#ifdef GMQCC_TYPE_WARNS
- GMQCC_DEFINE_FLAG(UNINITIALIZED_GLOBAL)
- GMQCC_DEFINE_FLAG(DEBUG)
- GMQCC_DEFINE_FLAG(UNUSED_VARIABLE)
- GMQCC_DEFINE_FLAG(USED_UNINITIALIZED)
- GMQCC_DEFINE_FLAG(UNKNOWN_CONTROL_SEQUENCE)
- GMQCC_DEFINE_FLAG(EXTENSIONS)
- GMQCC_DEFINE_FLAG(FIELD_REDECLARED)
- GMQCC_DEFINE_FLAG(MISSING_RETURN_VALUES)
- GMQCC_DEFINE_FLAG(INVALID_PARAMETER_COUNT)
- GMQCC_DEFINE_FLAG(LOCAL_SHADOWS)
- GMQCC_DEFINE_FLAG(LOCAL_CONSTANTS)
- GMQCC_DEFINE_FLAG(VOID_VARIABLES)
- GMQCC_DEFINE_FLAG(IMPLICIT_FUNCTION_POINTER)
- GMQCC_DEFINE_FLAG(VARIADIC_FUNCTION)
- GMQCC_DEFINE_FLAG(FRAME_MACROS)
- GMQCC_DEFINE_FLAG(EFFECTLESS_STATEMENT)
- GMQCC_DEFINE_FLAG(END_SYS_FIELDS)
- GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES)
- GMQCC_DEFINE_FLAG(PREPROCESSOR)
- GMQCC_DEFINE_FLAG(MULTIFILE_IF)
- GMQCC_DEFINE_FLAG(DOUBLE_DECLARATION)
- GMQCC_DEFINE_FLAG(CONST_VAR)
- GMQCC_DEFINE_FLAG(MULTIBYTE_CHARACTER)
- GMQCC_DEFINE_FLAG(TERNARY_PRECEDENCE)
- GMQCC_DEFINE_FLAG(UNKNOWN_PRAGMAS)
- GMQCC_DEFINE_FLAG(UNREACHABLE_CODE)
- GMQCC_DEFINE_FLAG(CPP)
- GMQCC_DEFINE_FLAG(UNKNOWN_ATTRIBUTE)
- GMQCC_DEFINE_FLAG(RESERVED_NAMES)
- GMQCC_DEFINE_FLAG(UNINITIALIZED_CONSTANT)
- GMQCC_DEFINE_FLAG(DIFFERENT_QUALIFIERS)
- GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES)
- GMQCC_DEFINE_FLAG(DEPRECATED)
- GMQCC_DEFINE_FLAG(PARENTHESIS)
+ GMQCC_DEFINE_FLAG (
+ UNUSED_VARIABLE,
+
+ "Generate a warning about variables which are declared but never"
+ "used. This can be avoided by adding the ‘noref’ keyword in front"
+ "of the variable declaration. Additionally a complete section of"
+ "unreferenced variables can be opened using ‘#pragma noref 1’ and"
+ "closed via ‘#pragma noref 0’."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ USED_UNINITIALIZED,
+
+ "Generate a warning if it is possible that a variable can be used"
+ "without prior initialization. Note that this warning is not nec"
+ "essarily reliable if the initialization happens only under cer"
+ "tain conditions. The other way is not possible: that the warning"
+ "is not generated when uninitialized use is possible."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ UNKNOWN_CONTROL_SEQUENCE,
+
+ "Generate an error when an unrecognized control sequence in a"
+ "string is used. Meaning: when there's a character after a back-"
+ "slash in a string which has no known meaning."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ EXTENSIONS,
+
+ "Warn when using special extensions which are not part of the"
+ "selected standard."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ FIELD_REDECLARED,
+
+ "Generally QC compilers ignore redeclaration of fields. Here you"
+ "can optionally enable a warning."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ MISSING_RETURN_VALUES,
+
+ "Functions which aren't of type void will warn if it possible to"
+ "reach the end without returning an actual value."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ INVALID_PARAMETER_COUNT,
+
+ "Warn about a function call with an invalid number of parameters."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ LOCAL_SHADOWS,
+
+ "Warn when a locally declared variable shadows variable."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ LOCAL_CONSTANTS,
+
+ " Warn when the initialization of a local variable turns the vari"
+ "able into a constant. This is default behaviour unless"
+ "-finitialized-nonconstants is used."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ VOID_VARIABLES,
+
+ "There are only 2 known global variables of type void:"
+ "‘end_sys_globals’ and ‘end_sys_fields’. Any other void-variable"
+ "will warn."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ IMPLICIT_FUNCTION_POINTER,
+
+ "A global function which is not declared with the ‘var’ keyword is"
+ "expected to have an implementing body, or be a builtin. If nei"
+ "ther is the case, it implicitly becomes a function pointer, and a"
+ "warning is generated."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ VARIADIC_FUNCTION,
+
+ "Currently there's no way for an in QC implemented function to"
+ "access variadic parameters. If a function with variadic parame"
+ "ters has an implementing body, a warning will be generated."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ FRAME_MACROS,
+
+ "Generate warnings about ‘$frame’ commands, for instance about"
+ "duplicate frame definitions."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ EFFECTLESS_STATEMENT,
+
+ "Warn about statements which have no effect. Any expression which"
+ "does not call a function or assigns a variable."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ END_SYS_FIELDS,
+
+ "The ‘end_sys_fields’ variable is supposed to be a global variable"
+ "of type void. It is also recognized as a field but this will"
+ "generate a warning."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ ASSIGN_FUNCTION_TYPES,
+
+ "Warn when assigning to a function pointer with an unmatching sig"
+ "nature. This usually happens in cases like assigning the null"
+ "function to an entity's .think function pointer."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ CPP,
+
+ "Enable warnings coming from the preprocessor. Like duplicate"
+ "macro declarations. This warning triggers when there's a problem"
+ "with the way the preprocessor has been used."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ MULTIFILE_IF,
+
+ "Warn if there's a preprocessor #if spanning across several files."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ DOUBLE_DECLARATION,
+
+ "Warn about multiple declarations of globals. This seems pretty"
+ "common in QC code so you probably do not want this unless you"
+ "want to clean up your code."
+ )
+ GMQCC_DEFINE_FLAG (
+ CONST_VAR,
+
+ "The combination of const and var is not illegal, however differ"
+ "ent compilers may handle them differently. We were told, the"
+ "intention is to create a function-pointer which is not assigna"
+ "ble. This is exactly how we interpret it. However for this"
+ "interpretation the ‘var’ keyword is considered superfluous (and"
+ "philosophically wrong), so it is possible to generate a warning"
+ "about this."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ MULTIBYTE_CHARACTER,
+
+ "Warn about multibyte character constants, they do not work right"
+ "now."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ TERNARY_PRECEDENCE,
+
+ "Warn if a ternary expression which contains a comma operator is"
+ "used without enclosing parenthesis, since this is most likely not"
+ "what you actually want. We recommend the -fcorrect-ternary"
+ "option."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ UNKNOWN_PRAGMAS,
+
+ "Warn when encountering an unrecognized ‘#pragma’ line."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ UNREACHABLE_CODE,
+
+ "Warn about unreachable code. That is: code after a return state"
+ "ment, or code after a call to a function marked as 'noreturn'."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ DEBUG,
+
+ "Enable some warnings added in order to help debugging in the com"
+ "piler. You won't need this."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ UNKNOWN_ATTRIBUTE,
+
+ "Warn on an unknown attribute. The warning will inlclude only the"
+ "first token inside the enclosing attribute-brackets. This may"
+ "change when the actual attribute syntax is better defined."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ RESERVED_NAMES,
+
+ "Warn when using reserved names such as ‘nil’."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ UNINITIALIZED_CONSTANT,
+
+ "Warn about global constants (using the ‘const’ keyword) with no"
+ "assigned value."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ UNINITIALIZED_GLOBAL,
+
+ "Warn about global variables with no initializing value. This is"
+ "off by default, and is added mostly to help find null-values"
+ "which are supposed to be replaced by the untyped 'nil' constant."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ DIFFERENT_QUALIFIERS,
+
+ "Warn when a variables is redeclared with a different qualifier."
+ "For example when redeclaring a variable as 'var' which was previ"
+ "ously marked 'const'."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ DIFFERENT_ATTRIBUTES,
+
+ "Similar to qualifiers but for attributes like ‘[[noreturn]]’."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ DEPRECATED,
+
+ "Warn when a function is marked with the attribute \"[[depre"
+ "cated]]\". This flag enables a warning on calls to functions"
+ "marked as such."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ PARENTHESIS,
+
+ "Warn about possible mistakes caused by missing or wrong parenthe"
+ "sis, like an assignment in an 'if' condition when there's no"
+ "additional set of parens around the assignment."
+ )
#endif
#ifdef GMQCC_TYPE_OPTIMIZATIONS
- GMQCC_DEFINE_FLAG(PEEPHOLE, 1)
- GMQCC_DEFINE_FLAG(LOCAL_TEMPS, 3)
- GMQCC_DEFINE_FLAG(GLOBAL_TEMPS, 3)
- GMQCC_DEFINE_FLAG(TAIL_RECURSION, 1)
- GMQCC_DEFINE_FLAG(TAIL_CALLS, 2)
- GMQCC_DEFINE_FLAG(OVERLAP_LOCALS, 3)
- GMQCC_DEFINE_FLAG(STRIP_CONSTANT_NAMES, 1)
- GMQCC_DEFINE_FLAG(OVERLAP_STRINGS, 2)
- GMQCC_DEFINE_FLAG(CALL_STORES, 4)
- GMQCC_DEFINE_FLAG(VOID_RETURN, 1)
- GMQCC_DEFINE_FLAG(VECTOR_COMPONENTS, 1)
+ GMQCC_DEFINE_FLAG (
+ PEEPHOLE, 1,
+
+ "Some general peephole optimizations. For instance the code `a = b\n"
+ "+ c` typically generates 2 instructions, an ADD and a STORE. This\n"
+ "optimization removes the STORE and lets the ADD write directly\n"
+ "into A."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ TAIL_RECURSION, 1,
+
+ "Tail recursive function calls will be turned into loops to avoid\n"
+ "the overhead of the CALL and RETURN instructions."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ OVERLAP_LOCALS, 3,
+
+ "Make all functions which use neither local arrays nor have locals\n"
+ "which are seen as possibly uninitialized use the same local sec‐\n"
+ "tion. This should be pretty safe compared to other compilers\n"
+ "which do not check for uninitialized values properly. The problem\n"
+ "is that there's QC code out there which really doesn't initialize\n"
+ "some values. This is fine as long as this kind of optimization\n"
+ "isn't used, but also, only as long as the functions cannot be\n"
+ "called in a recursive manner. Since it's hard to know whether or\n"
+ "not an array is actually fully initialized, especially when ini‐\n"
+ "tializing it via a loop, we assume functions with arrays to be\n"
+ "too dangerous for this optimization."
+ )
+
+
+ GMQCC_DEFINE_FLAG (
+ LOCAL_TEMPS, 3,
+
+ "This promotes locally declared variables to \"temps\". Meaning when\n"
+ "a temporary result of an operation has to be stored somewhere, a\n"
+ "local variable which is not 'alive' at that point can be used to\n"
+ "keep the result. This can reduce the size of the global section.\n"
+ "This will not have declared variables overlap, even if it was\n"
+ "possible."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ GLOBAL_TEMPS, 3,
+
+ "Causes temporary values which do not need to be backed up on a\n"
+ "CALL to not be stored in the function's locals-area. With this, a\n"
+ "CALL to a function may need to back up fewer values and thus exe‐\n"
+ "cute faster."
+ )
+
+
+ GMQCC_DEFINE_FLAG (
+ STRIP_CONSTANT_NAMES, 1,
+
+ "Don't generate defs for immediate values or even declared con‐\n"
+ "stants. Meaning variables which are implicitly constant or qual‐\n"
+ "ified as such using the 'const' keyword."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ OVERLAP_STRINGS, 2,
+
+ "Aggressively reuse strings in the string section. When a string\n"
+ "should be added which is the trailing substring of an already\n"
+ "existing string, the existing string's tail will be returned\n"
+ "instead of the new string being added.\n\n"
+ "For example the following code will only generate 1 string:\n\n"
+ " print(\"Hell you!\\n\");\n"
+ " print(\"you!\n\"); // trailing substring of \"Hello you!\\n\"\n\n"
+ "There's however one limitation. Strings are still processed in\n"
+ "order, so if the above print statements were reversed, this opti‐\n"
+ "mization would not happen."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ CALL_STORES, 3,
+
+ "By default, all parameters of a CALL are copied into the parame‐\n"
+ "ter-globals right before the CALL instructions. This is the easi‐\n"
+ "est and safest way to translate calls, but also adds a lot of\n"
+ "unnecessary copying and unnecessary temporary values. This opti‐\n"
+ "mization makes operations which are used as a parameter evaluate\n"
+ "directly into the parameter-global if that is possible, which is\n"
+ "when there's no other CALL instruction in between."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ VOID_RETURN, 1,
+
+ "Usually an empty RETURN instruction is added to the end of a void\n"
+ "typed function. However, additionally after every function a DONE\n"
+ "instruction is added for several reasons. (For example the qcvm's\n"
+ "disassemble switch uses it to know when the function ends.). This\n"
+ "optimization replaces that last RETURN with DONE rather than\n"
+ "adding the DONE additionally."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ VECTOR_COMPONENTS, 1,
+
+ "Because traditional QC code doesn't allow you to access individ‐\n"
+ "ual vector components of a computed vector without storing it in\n"
+ "a local first, sometimes people multiply it by a constant like\n"
+ "‘'0 1 0'’ to get, in this case, the y component of a vector. This\n"
+ "optimization will turn such a multiplication into a direct compo‐\n"
+ "nent access. If the factor is anything other than 1, a float-mul‐\n"
+ "tiplication will be added, which is still faster than a vector"
+ "multiplication."
+ )
+#endif
+
+#ifdef GMQCC_TYPE_OPTIONS
+ GMQCC_DEFINE_FLAG (
+ O,
+
+ "Specify the optimization level\n"
+ "3 Highest optimization level\n"
+ "2 Default optimization level\n"
+ "1 Minimal optimization level\n"
+ "0 Disable optimization entirely"
+ )
+ GMQCC_DEFINE_FLAG (
+ OUTPUT,
+
+ "Specify the output file name. Defaults to progs.dat. This will\n"
+ "overwrite the output file listed in a progs.src file in case such\n"
+ "a file is used."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ QUIET,
+
+ "Be less verbose. In particular removes the messages about which\n"
+ "files are being processed, and which compilation mode is being\n"
+ "used, and some others. Warnings and errors will of course still\n"
+ "be displayed."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ G,
+
+ ""
+ )
+
+ GMQCC_DEFINE_FLAG (
+ STANDARD,
+
+ "Use the specified standard for parsing QC code. The following\n"
+ "standards are available: gmqcc, qcc, fteqcc Selecting a standard\n"
+ "also implies some -f options and behaves as if those options have\n"
+ "been written right after the -std option, meaning if you changed\n"
+ "them before the --std option, you're now overwriting them.\n\n"
+ "-std=gmqcc includes:\n"
+ " -fadjust-vector-fields\n"
+ " -fcorrect-logic\n"
+ " -ftrue-empty-strings\n"
+ " -floop-labels\n"
+ " -finitialized-nonconstants\n"
+ " -ftranslatable-strings\n"
+ " -fno-false-empty-strings\n"
+ " -Winvalid-parameter-count\n"
+ " -Wmissing-returnvalues\n"
+ " -fcorrect-ternary (cannot be turned off)\n\n"
+ "-std=qcc includes:\n"
+ " -fassign-function-types\n"
+ " -fIno-adjust-vector-fields\n\n"
+ "-std=fteqcc includes:\n"
+ " -fftepp\n"
+ " -ftranslatable-strings\n"
+ " -fassign-function-types\n"
+ " -Wternary-precedence\n"
+ " -fno-adjust-vector-fields\n"
+ " -fno-correct-ternary"
+ )
+
+ GMQCC_DEFINE_FLAG (
+ DEBUG,
+
+ "Turn on some compiler debuggin mechanisms"
+ )
+
+ GMQCC_DEFINE_FLAG (
+ MEMCHK,
+
+ "Turn on compiler mem-chk. (Shows allocations and checks for\n"
+ "leaks.)"
+ )
+
+ GMQCC_DEFINE_FLAG (
+ DUMPFIN,
+
+ "DEBUG OPTION. Print the code's intermediate representation after\n"
+ "the optimization and finalization passes to stdout before gener‐\n"
+ "ating the binary. The instructions will be enumerated, and values\n"
+ "will contain a list of liferanges."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ DUMP,
+
+ "DEBUG OPTION. Print the code's intermediate representation before\n"
+ "the optimization and finalization passes to stdout before gener‐\n"
+ "ating the binary."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ FORCECRC,
+
+ "When enabled allows forcing a specific CRC16 for the generated\n"
+ "progs.dat"
+ )
+
+ GMQCC_DEFINE_FLAG (
+ FORCED_CRC,
+
+ "Value which represents the CRC to force into the progs.dat"
+ )
+
+ GMQCC_DEFINE_FLAG (
+ PP_ONLY,
+
+ "Run only the preprocessor as if -fftepp was used and print the\n"
+ "preprocessed code to stdout."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ MAX_ARRAY_SIZE,
+
+ "Maximum allowed one-dimensional array size. Arrays are expensive\n"
+ "since binary search trees are generated for each set/get of an array\n"
+ "the more elements in an array (the larger it is), the longer it will\n"
+ "take to set/get elements."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ ADD_INFO,
+
+ "Adds compiler information to the generated binary file. Currently\n"
+ "this includes the following globals:\n"
+ " reserved:version\n"
+ " String containing the compiler version as printed by the\n"
+ " --version parameter."
+ )
+
+ GMQCC_DEFINE_FLAG (
+ CORRECTION,
+
+ "When enabled, errors about undefined values try to suggest an\n"
+ "existing value via spell checking."
+ )
#endif
/* some cleanup so we don't have to */
#undef GMQCC_TYPE_FLAGS
#undef GMQCC_TYPE_WARNS
+#undef GMQCC_TYPE_OPTIONS
#undef GMQCC_TYPE_OPTIMIZATIONS
#undef GMQCC_DEFINE_FLAG
#define PARSER_HT_SIZE 128
#define TYPEDEF_HT_SIZE 16
-enum parser_pot { POT_PAREN, POT_TERNARY1, POT_TERNARY2 };
typedef struct {
lex_file *lex;
int tok;
ast_value *imm_float_zero;
ast_value *imm_float_one;
+ ast_value *imm_float_neg_one;
+
ast_value *imm_vector_zero;
+
ast_value *nil;
ast_value *reserved_version;
/* we store the '=' operator info */
const oper_info *assign_op;
- /* Keep track of our ternary vs parenthesis nesting state.
- * If we reach a 'comma' operator in a ternary without a paren,
- * we shall trigger -Wternary-precedence.
- */
- enum parser_pot *pot;
-
/* magic values */
ast_value *const_vec[3];
return r;
}
-static bool GMQCC_WARN genwarning(lex_ctx ctx, int warntype, const char *fmt, ...)
-{
- bool r;
- va_list ap;
- va_start(ap, fmt);
- r = vcompile_warning(ctx, warntype, fmt, ap);
- va_end(ap);
- return r;
-}
-
/**********************************************************************
* some maths used for constant folding
*/
return parser->imm_float_zero;
}
+static ast_value* parser_const_float_neg1(parser_t *parser) {
+ if (!parser->imm_float_neg_one)
+ parser->imm_float_neg_one = parser_const_float(parser, -1);
+ return parser->imm_float_neg_one;
+}
+
static ast_value* parser_const_float_1(parser_t *parser)
{
if (!parser->imm_float_one)
typedef struct
{
size_t etype; /* 0 = expression, others are operators */
- int paren;
+ bool isparen;
size_t off;
ast_expression *out;
ast_block *block; /* for commas and function calls */
lex_ctx ctx;
} sy_elem;
+
+enum {
+ PAREN_EXPR,
+ PAREN_FUNC,
+ PAREN_INDEX,
+ PAREN_TERNARY1,
+ PAREN_TERNARY2
+};
typedef struct
{
- sy_elem *out;
- sy_elem *ops;
+ sy_elem *out;
+ sy_elem *ops;
+ size_t *argc;
+ unsigned int *paren;
} shunt;
-#define SY_PAREN_EXPR '('
-#define SY_PAREN_FUNC 'f'
-#define SY_PAREN_INDEX '['
-#define SY_PAREN_TERNARY '?'
-
static sy_elem syexp(lex_ctx ctx, ast_expression *v) {
sy_elem e;
e.etype = 0;
e.out = v;
e.block = NULL;
e.ctx = ctx;
- e.paren = 0;
+ e.isparen = false;
return e;
}
e.out = (ast_expression*)v;
e.block = v;
e.ctx = ctx;
- e.paren = 0;
+ e.isparen = false;
return e;
}
e.out = NULL;
e.block = NULL;
e.ctx = ctx;
- e.paren = 0;
+ e.isparen = false;
return e;
}
-static sy_elem syparen(lex_ctx ctx, int p, size_t off) {
+static sy_elem syparen(lex_ctx ctx, size_t off) {
sy_elem e;
e.etype = 0;
e.off = off;
e.out = NULL;
e.block = NULL;
e.ctx = ctx;
- e.paren = p;
+ e.isparen = true;
return e;
}
-#ifdef DEBUGSHUNT
-# define DEBUGSHUNTDO(x) x
-#else
-# define DEBUGSHUNTDO(x)
-#endif
-
/* With regular precedence rules, ent.foo[n] is the same as (ent.foo)[n],
* so we need to rotate it to become ent.(foo[n]).
*/
return false;
}
- if (vec_last(sy->ops).paren) {
+ if (vec_last(sy->ops).isparen) {
parseerror(parser, "unmatched parenthesis");
return false;
}
op = &operators[vec_last(sy->ops).etype - 1];
ctx = vec_last(sy->ops).ctx;
- DEBUGSHUNTDO(con_out("apply %s\n", op->op));
-
if (vec_size(sy->out) < op->operands) {
compile_error(ctx, "internal error: not enough operands: %i (operator %s (%i))", vec_size(sy->out),
op->op, (int)op->id);
{
#if 0
/* This is not broken in fteqcc anymore */
- if (opts.standard != COMPILER_GMQCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
/* this error doesn't need to make us bail out */
(void)!parsewarning(parser, WARN_EXTENSIONS,
"accessing array-field members of an entity without parenthesis\n"
break;
case opid1(','):
+ if (vec_size(sy->paren) && vec_last(sy->paren) == PAREN_FUNC) {
+ vec_push(sy->out, syexp(ctx, exprs[0]));
+ vec_push(sy->out, syexp(ctx, exprs[1]));
+ vec_last(sy->argc)++;
+ return true;
+ }
if (blocks[0]) {
if (!ast_block_add_expr(blocks[0], exprs[1]))
return false;
break;
case opid2('?',':'):
- if (vec_last(parser->pot) != POT_TERNARY2) {
+ if (vec_last(sy->paren) != PAREN_TERNARY2) {
compile_error(ctx, "mismatched parenthesis/ternary");
return false;
}
- vec_pop(parser->pot);
+ vec_pop(sy->paren);
if (!ast_compare_type(exprs[1], exprs[2])) {
ast_type_to_string(exprs[1], ty1, sizeof(ty1));
ast_type_to_string(exprs[2], ty2, sizeof(ty2));
}
else
assignop = type_storep_instr[exprs[0]->expression.vtype];
- if (assignop == AINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
+ if (assignop == VINSTR_END || !ast_compare_type(field->expression.next, exprs[1]))
{
ast_type_to_string(field->expression.next, ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
assignop = type_store_instr[exprs[0]->expression.vtype];
}
- if (assignop == AINSTR_END) {
+ if (assignop == VINSTR_END) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
ast_type_to_string(exprs[1], ty2, sizeof(ty2));
compile_error(ctx, "invalid types in assignment: cannot assign %s to %s", ty2, ty1);
asbinstore->keep_dest = true;
out = (ast_expression*)asbinstore;
break;
+
+ case opid2('~', 'P'):
+ if (exprs[0]->expression.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(CanConstFold1(exprs[0]))
+ out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0));
+ else
+ out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
+ break;
+
}
#undef NotSameType
if (!out) {
- compile_error(ctx, "failed to apply operand %s", op->op);
+ compile_error(ctx, "failed to apply operator %s", op->op);
return false;
}
- DEBUGSHUNTDO(con_out("applied %s\n", op->op));
vec_push(sy->out, syexp(ctx, out));
return true;
}
ast_call *call;
size_t fid;
- size_t paramcount;
+ size_t paramcount, i;
+ fid = vec_last(sy->ops).off;
vec_shrinkby(sy->ops, 1);
- fid = sy->ops[vec_size(sy->ops)].off;
/* out[fid] is the function
* everything above is parameters...
- * 0 params = nothing
- * 1 params = ast_expression
- * more = ast_block
*/
+ if (!vec_size(sy->argc)) {
+ parseerror(parser, "internal error: no argument counter available");
+ return false;
+ }
+
+ paramcount = vec_last(sy->argc);
+ vec_pop(sy->argc);
- if (vec_size(sy->out) < 1 || vec_size(sy->out) <= fid) {
- parseerror(parser, "internal error: function call needs function and parameter list...");
+ if (vec_size(sy->out) < fid) {
+ parseerror(parser, "internal error: broken function call%lu < %lu+%lu\n",
+ (unsigned long)vec_size(sy->out),
+ (unsigned long)fid,
+ (unsigned long)paramcount);
return false;
}
if (!call)
return false;
- if (fid+1 == vec_size(sy->out)) {
- /* no arguments */
- paramcount = 0;
- } else if (fid+2 == vec_size(sy->out)) {
- ast_block *params;
- vec_shrinkby(sy->out, 1);
- params = sy->out[vec_size(sy->out)].block;
- if (!params) {
- /* 1 param */
- paramcount = 1;
- vec_push(call->params, sy->out[vec_size(sy->out)].out);
- } else {
- paramcount = vec_size(params->exprs);
- call->params = params->exprs;
- params->exprs = NULL;
- ast_delete(params);
- }
- if (parser->max_param_count < paramcount)
- parser->max_param_count = paramcount;
- (void)!ast_call_check_types(call);
- } else {
- parseerror(parser, "invalid function call");
+ if (fid+1 < vec_size(sy->out))
+ ++paramcount;
+
+ if (fid+1 + paramcount != vec_size(sy->out)) {
+ parseerror(parser, "internal error: parameter count mismatch: (%lu+1+%lu), %lu",
+ (unsigned long)fid, (unsigned long)paramcount, (unsigned long)vec_size(sy->out));
return false;
}
+ for (i = 0; i < paramcount; ++i)
+ vec_push(call->params, sy->out[fid+1 + i].out);
+ vec_shrinkby(sy->out, paramcount);
+ (void)!ast_call_check_types(call);
+ if (parser->max_param_count < paramcount)
+ parser->max_param_count = paramcount;
+
if (ast_istype(fun, ast_value)) {
funval = (ast_value*)fun;
if ((fun->expression.flags & AST_FLAG_VARIADIC) &&
!(/*funval->cvq == CV_CONST && */ funval->hasvalue && funval->constval.vfunc->builtin))
{
- size_t va_count;
- if (paramcount < vec_size(fun->expression.params))
- va_count = 0;
- else
- va_count = paramcount - vec_size(fun->expression.params);
- call->va_count = (ast_expression*)parser_const_float(parser, (double)va_count);
+ call->va_count = (ast_expression*)parser_const_float(parser, (double)paramcount);
}
}
return true;
}
-static bool parser_close_paren(parser_t *parser, shunt *sy, bool functions_only)
+static bool parser_close_paren(parser_t *parser, shunt *sy)
{
if (!vec_size(sy->ops)) {
parseerror(parser, "unmatched closing paren");
return false;
}
- /* this would for bit a + (x) because there are no operators inside (x)
- if (sy->ops[vec_size(sy->ops)-1].paren == 1) {
- parseerror(parser, "empty parenthesis expression");
- return false;
- }
- */
+
while (vec_size(sy->ops)) {
- if (vec_last(sy->ops).paren == SY_PAREN_FUNC) {
- if (!parser_close_call(parser, sy))
- return false;
- break;
- }
- if (vec_last(sy->ops).paren == SY_PAREN_EXPR) {
- if (!vec_size(sy->out)) {
- compile_error(vec_last(sy->ops).ctx, "empty paren expression");
+ if (vec_last(sy->ops).isparen) {
+ if (vec_last(sy->paren) == PAREN_FUNC) {
+ vec_pop(sy->paren);
+ if (!parser_close_call(parser, sy))
+ return false;
+ break;
+ }
+ if (vec_last(sy->paren) == PAREN_EXPR) {
+ vec_pop(sy->paren);
+ if (!vec_size(sy->out)) {
+ compile_error(vec_last(sy->ops).ctx, "empty paren expression");
+ vec_shrinkby(sy->ops, 1);
+ return false;
+ }
vec_shrinkby(sy->ops, 1);
- return false;
+ break;
}
- vec_shrinkby(sy->ops, 1);
- return !functions_only;
- }
- if (vec_last(sy->ops).paren == SY_PAREN_INDEX) {
- if (functions_only)
- return false;
- /* pop off the parenthesis */
- vec_shrinkby(sy->ops, 1);
- /* then apply the index operator */
- if (!parser_sy_apply_operator(parser, sy))
- return false;
- return true;
- }
- if (vec_last(sy->ops).paren == SY_PAREN_TERNARY) {
- if (functions_only)
- return false;
- if (vec_last(parser->pot) != POT_TERNARY1) {
- parseerror(parser, "mismatched colon in ternary expression (missing closing paren?)");
- return false;
+ if (vec_last(sy->paren) == PAREN_INDEX) {
+ vec_pop(sy->paren);
+ /* pop off the parenthesis */
+ vec_shrinkby(sy->ops, 1);
+ /* then apply the index operator */
+ if (!parser_sy_apply_operator(parser, sy))
+ return false;
+ break;
}
- vec_last(parser->pot) = POT_TERNARY2;
- /* pop off the parenthesis */
- vec_shrinkby(sy->ops, 1);
- return true;
+ if (vec_last(sy->paren) == PAREN_TERNARY1) {
+ vec_last(sy->paren) = PAREN_TERNARY2;
+ /* pop off the parenthesis */
+ vec_shrinkby(sy->ops, 1);
+ break;
+ }
+ compile_error(vec_last(sy->ops).ctx, "invalid parenthesis");
+ return false;
}
if (!parser_sy_apply_operator(parser, sy))
return false;
static ast_expression* parse_vararg(parser_t *parser)
{
- bool old_noops = parser->lex->flags.noops;
- enum parser_pot *old_pot = parser->pot;
+ bool old_noops = parser->lex->flags.noops;
ast_expression *out;
- parser->pot = NULL;
parser->lex->flags.noops = true;
out = parse_vararg_do(parser);
- parser->pot = old_pot;
parser->lex->flags.noops = old_noops;
return out;
}
-static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels)
+static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
{
- ast_expression *expr = NULL;
- shunt sy;
- size_t i;
- bool wantop = false;
- /* only warn once about an assignment in a truth value because the current code
- * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
- */
- bool warn_truthvalue = true;
-
- /* count the parens because an if starts with one, so the
- * end of a condition is an unmatched closing paren
- */
- int parens = 0;
- int ternaries = 0;
-
- sy.out = NULL;
- sy.ops = NULL;
-
- parser->lex->flags.noops = false;
-
- parser_reclassify_token(parser);
-
- while (true)
+ if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
+ parser->tok == TOKEN_IDENT &&
+ !strcmp(parser_tokval(parser), "_"))
{
- if (OPTS_FLAG(TRANSLATABLE_STRINGS) &&
- parser->tok == TOKEN_IDENT && !strcmp(parser_tokval(parser), "_"))
- {
- /* a translatable string */
- ast_value *val;
-
- if (wantop) {
- parseerror(parser, "expected operator or end of statement, got constant");
- goto onerr;
- }
+ /* a translatable string */
+ ast_value *val;
- parser->lex->flags.noops = true;
- if (!parser_next(parser) || parser->tok != '(') {
- parseerror(parser, "use _(\"string\") to create a translatable string constant");
- goto onerr;
- }
- parser->lex->flags.noops = false;
- if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) {
- parseerror(parser, "expected a constant string in translatable-string extension");
- goto onerr;
- }
- val = parser_const_string(parser, parser_tokval(parser), true);
- wantop = true;
- if (!val)
- return NULL;
- vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
- DEBUGSHUNTDO(con_out("push string\n"));
+ parser->lex->flags.noops = true;
+ if (!parser_next(parser) || parser->tok != '(') {
+ parseerror(parser, "use _(\"string\") to create a translatable string constant");
+ return false;
+ }
+ parser->lex->flags.noops = false;
+ if (!parser_next(parser) || parser->tok != TOKEN_STRINGCONST) {
+ parseerror(parser, "expected a constant string in translatable-string extension");
+ return false;
+ }
+ val = parser_const_string(parser, parser_tokval(parser), true);
+ if (!val)
+ return false;
+ vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
- if (!parser_next(parser) || parser->tok != ')') {
- parseerror(parser, "expected closing paren after translatable string");
- goto onerr;
- }
+ if (!parser_next(parser) || parser->tok != ')') {
+ parseerror(parser, "expected closing paren after translatable string");
+ return false;
}
- else if (parser->tok == TOKEN_DOTS)
+ return true;
+ }
+ else if (parser->tok == TOKEN_DOTS)
+ {
+ ast_expression *va;
+ if (!OPTS_FLAG(VARIADIC_ARGS)) {
+ parseerror(parser, "cannot access varargs (try -fvariadic-args)");
+ return false;
+ }
+ va = parse_vararg(parser);
+ if (!va)
+ return false;
+ vec_push(sy->out, syexp(parser_ctx(parser), va));
+ return true;
+ }
+ else if (parser->tok == TOKEN_FLOATCONST) {
+ ast_value *val;
+ val = parser_const_float(parser, (parser_token(parser)->constval.f));
+ if (!val)
+ return false;
+ vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+ return true;
+ }
+ else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
+ ast_value *val;
+ val = parser_const_float(parser, (double)(parser_token(parser)->constval.i));
+ if (!val)
+ return false;
+ vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+ return true;
+ }
+ else if (parser->tok == TOKEN_STRINGCONST) {
+ ast_value *val;
+ val = parser_const_string(parser, parser_tokval(parser), false);
+ if (!val)
+ return false;
+ vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+ return true;
+ }
+ else if (parser->tok == TOKEN_VECTORCONST) {
+ ast_value *val;
+ val = parser_const_vector(parser, parser_token(parser)->constval.v);
+ if (!val)
+ return false;
+ vec_push(sy->out, syexp(parser_ctx(parser), (ast_expression*)val));
+ return true;
+ }
+ else if (parser->tok == TOKEN_IDENT)
+ {
+ const char *ctoken = parser_tokval(parser);
+ ast_expression *prev = vec_size(sy->out) ? vec_last(sy->out).out : NULL;
+ ast_expression *var;
+ /* a_vector.{x,y,z} */
+ if (!vec_size(sy->ops) ||
+ !vec_last(sy->ops).etype ||
+ operators[vec_last(sy->ops).etype-1].id != opid1('.') ||
+ (prev >= intrinsic_debug_typestring &&
+ prev <= intrinsic_debug_typestring))
{
- ast_expression *va;
- if (!OPTS_FLAG(VARIADIC_ARGS)) {
- parseerror(parser, "cannot access varargs (try -fvariadic-args)");
- goto onerr;
- }
- if (wantop) {
- parseerror(parser, "expected operator or end of statement");
- goto onerr;
- }
- wantop = true;
- va = parse_vararg(parser);
- if (!va)
- goto onerr;
- vec_push(sy.out, syexp(parser_ctx(parser), va));
- DEBUGSHUNTDO(con_out("push `...`\n"));
+ /* When adding more intrinsics, fix the above condition */
+ prev = NULL;
}
- else if (parser->tok == TOKEN_IDENT)
+ if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
{
- const char *ctoken = parser_tokval(parser);
- ast_expression *prev = vec_size(sy.out) ? vec_last(sy.out).out : NULL;
- ast_expression *var;
- if (wantop) {
- parseerror(parser, "expected operator or end of statement");
- goto onerr;
+ var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
+ } else {
+ var = parser_find_var(parser, parser_tokval(parser));
+ if (!var)
+ var = parser_find_field(parser, parser_tokval(parser));
+ }
+ if (!var && with_labels) {
+ var = (ast_expression*)parser_find_label(parser, parser_tokval(parser));
+ if (!with_labels) {
+ ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true);
+ var = (ast_expression*)lbl;
+ vec_push(parser->labels, lbl);
}
- wantop = true;
- /* a_vector.{x,y,z} */
- if (!vec_size(sy.ops) ||
- !vec_last(sy.ops).etype ||
- operators[vec_last(sy.ops).etype-1].id != opid1('.') ||
- (prev >= intrinsic_debug_typestring &&
- prev <= intrinsic_debug_typestring))
- {
- /* When adding more intrinsics, fix the above condition */
- prev = NULL;
+ }
+ if (!var) {
+ /* intrinsics */
+ if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
+ var = (ast_expression*)intrinsic_debug_typestring;
}
- if (prev && prev->expression.vtype == TYPE_VECTOR && ctoken[0] >= 'x' && ctoken[0] <= 'z' && !ctoken[1])
+ else
{
- var = (ast_expression*)parser->const_vec[ctoken[0]-'x'];
- } else {
- var = parser_find_var(parser, parser_tokval(parser));
- if (!var)
- var = parser_find_field(parser, parser_tokval(parser));
- }
- if (!var && with_labels) {
- var = (ast_expression*)parser_find_label(parser, parser_tokval(parser));
- if (!with_labels) {
- ast_label *lbl = ast_label_new(parser_ctx(parser), parser_tokval(parser), true);
- var = (ast_expression*)lbl;
- vec_push(parser->labels, lbl);
- }
- }
- if (!var) {
- /* intrinsics */
- if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
- var = (ast_expression*)intrinsic_debug_typestring;
- }
- else
- {
- char *correct = NULL;
-
- /*
- * sometimes people use preprocessing predefs without enabling them
- * i've done this thousands of times already myself. Lets check for
- * it in the predef table. And diagnose it better :)
- */
- if (!OPTS_FLAG(FTEPP_PREDEFS)) {
- for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) {
- if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) {
- parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
- goto onerr;
- }
+ char *correct = NULL;
+ size_t i;
+
+ /*
+ * sometimes people use preprocessing predefs without enabling them
+ * i've done this thousands of times already myself. Lets check for
+ * it in the predef table. And diagnose it better :)
+ */
+ if (!OPTS_FLAG(FTEPP_PREDEFS)) {
+ for (i = 0; i < sizeof(ftepp_predefs)/sizeof(*ftepp_predefs); i++) {
+ if (!strcmp(ftepp_predefs[i].name, parser_tokval(parser))) {
+ parseerror(parser, "unexpected ident: %s (use -fftepp-predef to enable pre-defined macros)", parser_tokval(parser));
+ return false;
}
}
+ }
- /*
- * TODO: determine the best score for the identifier: be it
- * a variable, a field.
- *
- * We should also consider adding correction tables for
- * other things as well.
- */
- if (opts.correction) {
- correction_t corr;
- correct_init(&corr);
-
- for (i = 0; i < vec_size(parser->correct_variables); i++) {
- correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser));
- if (strcmp(correct, parser_tokval(parser))) {
- break;
- } else if (correct) {
- mem_d(correct);
- correct = NULL;
- }
- }
- correct_free(&corr);
-
- if (correct) {
- parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+ /*
+ * TODO: determine the best score for the identifier: be it
+ * a variable, a field.
+ *
+ * We should also consider adding correction tables for
+ * other things as well.
+ */
+ if (OPTS_OPTION_BOOL(OPTION_CORRECTION)) {
+ correction_t corr;
+ correct_init(&corr);
+
+ for (i = 0; i < vec_size(parser->correct_variables); i++) {
+ correct = correct_str(&corr, parser->correct_variables[i], parser_tokval(parser));
+ if (strcmp(correct, parser_tokval(parser))) {
+ break;
+ } else if (correct) {
mem_d(correct);
- goto onerr;
+ correct = NULL;
}
}
- parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
- goto onerr;
- }
- }
- else
- {
- if (ast_istype(var, ast_value)) {
- ((ast_value*)var)->uses++;
- }
- else if (ast_istype(var, ast_member)) {
- ast_member *mem = (ast_member*)var;
- if (ast_istype(mem->owner, ast_value))
- ((ast_value*)(mem->owner))->uses++;
+ correct_free(&corr);
+
+ if (correct) {
+ parseerror(parser, "unexpected ident: %s (did you mean %s?)", parser_tokval(parser), correct);
+ mem_d(correct);
+ return false;
+ }
}
+ parseerror(parser, "unexpected ident: %s", parser_tokval(parser));
+ return false;
}
- vec_push(sy.out, syexp(parser_ctx(parser), var));
- DEBUGSHUNTDO(con_out("push %s\n", parser_tokval(parser)));
- }
- else if (parser->tok == TOKEN_FLOATCONST) {
- ast_value *val;
- if (wantop) {
- parseerror(parser, "expected operator or end of statement, got constant");
- goto onerr;
- }
- wantop = true;
- val = parser_const_float(parser, (parser_token(parser)->constval.f));
- if (!val)
- return NULL;
- vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
- DEBUGSHUNTDO(con_out("push %g\n", parser_token(parser)->constval.f));
- }
- else if (parser->tok == TOKEN_INTCONST || parser->tok == TOKEN_CHARCONST) {
- ast_value *val;
- if (wantop) {
- parseerror(parser, "expected operator or end of statement, got constant");
- goto onerr;
- }
- wantop = true;
- val = parser_const_float(parser, (double)(parser_token(parser)->constval.i));
- if (!val)
- return NULL;
- vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
- DEBUGSHUNTDO(con_out("push %i\n", parser_token(parser)->constval.i));
- }
- else if (parser->tok == TOKEN_STRINGCONST) {
- ast_value *val;
- if (wantop) {
- parseerror(parser, "expected operator or end of statement, got constant");
- goto onerr;
- }
- wantop = true;
- val = parser_const_string(parser, parser_tokval(parser), false);
- if (!val)
- return NULL;
- vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
- DEBUGSHUNTDO(con_out("push string\n"));
- }
- else if (parser->tok == TOKEN_VECTORCONST) {
- ast_value *val;
- if (wantop) {
- parseerror(parser, "expected operator or end of statement, got constant");
- goto onerr;
- }
- wantop = true;
- val = parser_const_vector(parser, parser_token(parser)->constval.v);
- if (!val)
- return NULL;
- vec_push(sy.out, syexp(parser_ctx(parser), (ast_expression*)val));
- DEBUGSHUNTDO(con_out("push '%g %g %g'\n",
- parser_token(parser)->constval.v.x,
- parser_token(parser)->constval.v.y,
- parser_token(parser)->constval.v.z));
- }
- else if (parser->tok == '(') {
- parseerror(parser, "internal error: '(' should be classified as operator");
- goto onerr;
}
- else if (parser->tok == '[') {
- parseerror(parser, "internal error: '[' should be classified as operator");
- goto onerr;
- }
- else if (parser->tok == ')') {
- if (wantop) {
- DEBUGSHUNTDO(con_out("do[op] )\n"));
- --parens;
- if (parens < 0)
- break;
- /* we do expect an operator next */
- /* closing an opening paren */
- if (!parser_close_paren(parser, &sy, false))
- goto onerr;
- if (vec_last(parser->pot) != POT_PAREN) {
- parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
- goto onerr;
- }
- vec_pop(parser->pot);
- } else {
- DEBUGSHUNTDO(con_out("do[nop] )\n"));
- --parens;
- if (parens < 0)
- break;
- /* allowed for function calls */
- if (!parser_close_paren(parser, &sy, true))
- goto onerr;
- if (vec_last(parser->pot) != POT_PAREN) {
- parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
- goto onerr;
- }
- vec_pop(parser->pot);
+ else
+ {
+ if (ast_istype(var, ast_value)) {
+ ((ast_value*)var)->uses++;
}
- wantop = true;
- }
- else if (parser->tok == ']') {
- if (!wantop)
- parseerror(parser, "operand expected");
- --parens;
- if (parens < 0)
- break;
- if (!parser_close_paren(parser, &sy, false))
- goto onerr;
- if (vec_last(parser->pot) != POT_PAREN) {
- parseerror(parser, "mismatched parentheses (closing paren during ternary expression?)");
- goto onerr;
+ else if (ast_istype(var, ast_member)) {
+ ast_member *mem = (ast_member*)var;
+ if (ast_istype(mem->owner, ast_value))
+ ((ast_value*)(mem->owner))->uses++;
}
- vec_pop(parser->pot);
- wantop = true;
}
- else if (parser->tok == TOKEN_TYPENAME) {
+ vec_push(sy->out, syexp(parser_ctx(parser), var));
+ return true;
+ }
+ parseerror(parser, "unexpected token `%s`", parser_tokval(parser));
+ return false;
+}
+
+static ast_expression* parse_expression_leave(parser_t *parser, bool stopatcomma, bool truthvalue, bool with_labels)
+{
+ ast_expression *expr = NULL;
+ shunt sy;
+ size_t i;
+ bool wantop = false;
+ /* only warn once about an assignment in a truth value because the current code
+ * would trigger twice on: if(a = b && ...), once for the if-truth-value, once for the && part
+ */
+ bool warn_truthvalue = true;
+
+ /* count the parens because an if starts with one, so the
+ * end of a condition is an unmatched closing paren
+ */
+ int ternaries = 0;
+
+ memset(&sy, 0, sizeof(sy));
+
+ parser->lex->flags.noops = false;
+
+ parser_reclassify_token(parser);
+
+ while (true)
+ {
+ if (parser->tok == TOKEN_TYPENAME) {
parseerror(parser, "unexpected typename");
goto onerr;
}
- else if (parser->tok != TOKEN_OPERATOR) {
- if (wantop) {
- parseerror(parser, "expected operator or end of statement");
- goto onerr;
- }
- break;
- }
- else
+
+ if (parser->tok == TOKEN_OPERATOR)
{
/* classify the operator */
const oper_info *op;
op = &operators[o];
/* when declaring variables, a comma starts a new variable */
- if (op->id == opid1(',') && !parens && stopatcomma) {
+ if (op->id == opid1(',') && !vec_size(sy.paren) && stopatcomma) {
/* fixup the token */
parser->tok = ',';
break;
}
if (op->id == opid1(',')) {
- if (vec_size(parser->pot) && vec_last(parser->pot) == POT_TERNARY2) {
+ if (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) {
(void)!parsewarning(parser, WARN_TERNARY_PRECEDENCE, "suggesting parenthesis around ternary expression");
}
}
- if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
+ if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
olast = &operators[vec_last(sy.ops).etype-1];
#define IsAssignOp(x) (\
if (warn_truthvalue) {
if ( (olast && IsAssignOp(olast->id) && (op->id == opid2('&','&') || op->id == opid2('|','|'))) ||
(olast && IsAssignOp(op->id) && (olast->id == opid2('&','&') || olast->id == opid2('|','|'))) ||
- (truthvalue && !vec_size(parser->pot) && IsAssignOp(op->id))
+ (truthvalue && !vec_size(sy.paren) && IsAssignOp(op->id))
)
{
(void)!parsewarning(parser, WARN_PARENTHESIS, "suggesting parenthesis around assignment used as truth value");
{
if (!parser_sy_apply_operator(parser, &sy))
goto onerr;
- if (vec_size(sy.ops) && !vec_last(sy.ops).paren)
+ if (vec_size(sy.ops) && !vec_last(sy.ops).isparen)
olast = &operators[vec_last(sy.ops).etype-1];
else
olast = NULL;
if (op->id == opid1('(')) {
if (wantop) {
size_t sycount = vec_size(sy.out);
- DEBUGSHUNTDO(con_out("push [op] (\n"));
- ++parens; vec_push(parser->pot, POT_PAREN);
/* we expected an operator, this is the function-call operator */
- vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_FUNC, sycount-1));
+ vec_push(sy.paren, PAREN_FUNC);
+ vec_push(sy.ops, syparen(parser_ctx(parser), sycount-1));
+ vec_push(sy.argc, 0);
} else {
- ++parens; vec_push(parser->pot, POT_PAREN);
- vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_EXPR, 0));
- DEBUGSHUNTDO(con_out("push [nop] (\n"));
+ vec_push(sy.paren, PAREN_EXPR);
+ vec_push(sy.ops, syparen(parser_ctx(parser), 0));
}
wantop = false;
} else if (op->id == opid1('[')) {
parseerror(parser, "unexpected array subscript");
goto onerr;
}
- ++parens; vec_push(parser->pot, POT_PAREN);
+ vec_push(sy.paren, PAREN_INDEX);
/* push both the operator and the paren, this makes life easier */
vec_push(sy.ops, syop(parser_ctx(parser), op));
- vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_INDEX, 0));
+ vec_push(sy.ops, syparen(parser_ctx(parser), 0));
wantop = false;
} else if (op->id == opid2('?',':')) {
vec_push(sy.ops, syop(parser_ctx(parser), op));
- vec_push(sy.ops, syparen(parser_ctx(parser), SY_PAREN_TERNARY, 0));
+ vec_push(sy.ops, syparen(parser_ctx(parser), 0));
wantop = false;
++ternaries;
- vec_push(parser->pot, POT_TERNARY1);
+ vec_push(sy.paren, PAREN_TERNARY1);
} else if (op->id == opid2(':','?')) {
- if (!vec_size(parser->pot)) {
+ if (!vec_size(sy.paren)) {
parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)");
goto onerr;
}
- if (vec_last(parser->pot) != POT_TERNARY1) {
+ if (vec_last(sy.paren) != PAREN_TERNARY1) {
parseerror(parser, "unexpected colon outside ternary expression (missing parenthesis?)");
goto onerr;
}
- if (!parser_close_paren(parser, &sy, false))
+ if (!parser_close_paren(parser, &sy))
goto onerr;
vec_push(sy.ops, syop(parser_ctx(parser), op));
wantop = false;
--ternaries;
} else {
- DEBUGSHUNTDO(con_out("push operator %s\n", op->op));
vec_push(sy.ops, syop(parser_ctx(parser), op));
wantop = !!(op->flags & OP_SUFFIX);
}
}
+ else if (parser->tok == ')') {
+ while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) {
+ if (!parser_sy_apply_operator(parser, &sy))
+ goto onerr;
+ }
+ if (!vec_size(sy.paren))
+ break;
+ if (wantop) {
+ if (vec_last(sy.paren) == PAREN_TERNARY1) {
+ parseerror(parser, "mismatched parentheses (closing paren in ternary expression?)");
+ goto onerr;
+ }
+ if (!parser_close_paren(parser, &sy))
+ goto onerr;
+ } else {
+ /* must be a function call without parameters */
+ if (vec_last(sy.paren) != PAREN_FUNC) {
+ parseerror(parser, "closing paren in invalid position");
+ goto onerr;
+ }
+ if (!parser_close_paren(parser, &sy))
+ goto onerr;
+ }
+ wantop = true;
+ }
+ else if (parser->tok == '(') {
+ parseerror(parser, "internal error: '(' should be classified as operator");
+ goto onerr;
+ }
+ else if (parser->tok == '[') {
+ parseerror(parser, "internal error: '[' should be classified as operator");
+ goto onerr;
+ }
+ else if (parser->tok == ']') {
+ while (vec_size(sy.paren) && vec_last(sy.paren) == PAREN_TERNARY2) {
+ if (!parser_sy_apply_operator(parser, &sy))
+ goto onerr;
+ }
+ if (!vec_size(sy.paren))
+ break;
+ if (vec_last(sy.paren) != PAREN_INDEX) {
+ parseerror(parser, "mismatched parentheses, unexpected ']'");
+ goto onerr;
+ }
+ if (!parser_close_paren(parser, &sy))
+ goto onerr;
+ wantop = true;
+ }
+ else if (!wantop) {
+ if (!parse_sya_operand(parser, &sy, with_labels))
+ goto onerr;
+#if 0
+ if (vec_size(sy.paren) && vec_last(sy.ops).isparen && vec_last(sy.paren) == PAREN_FUNC)
+ vec_last(sy.argc)++;
+#endif
+ wantop = true;
+ }
+ else {
+ parseerror(parser, "expected operator or end of statement");
+ goto onerr;
+ }
+
if (!parser_next(parser)) {
goto onerr;
}
if (parser->tok == ';' ||
- (!parens && (parser->tok == ']' || parser->tok == ')' || parser->tok == '}')))
+ ((!vec_size(sy.paren) || (vec_size(sy.paren) == 1 && vec_last(sy.paren) == PAREN_TERNARY2)) &&
+ (parser->tok == ']' || parser->tok == ')' || parser->tok == '}')))
{
break;
}
expr = sy.out[0].out;
vec_free(sy.out);
vec_free(sy.ops);
- DEBUGSHUNTDO(con_out("shunt done\n"));
- if (vec_size(parser->pot)) {
- parseerror(parser, "internal error: vec_size(parser->pot) = %lu", (unsigned long)vec_size(parser->pot));
+ if (vec_size(sy.paren)) {
+ parseerror(parser, "internal error: vec_size(sy.paren) = %lu", (unsigned long)vec_size(sy.paren));
return NULL;
}
- vec_free(parser->pot);
+ vec_free(sy.paren);
+ vec_free(sy.argc);
return expr;
onerr:
}
vec_free(sy.out);
vec_free(sy.ops);
+ vec_free(sy.paren);
+ vec_free(sy.argc);
return NULL;
}
ast_unary *unary;
ast_expression *prev;
+ if (cond->expression.vtype == TYPE_VOID || cond->expression.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)
{
prev = cond;
if (typevar || parser->tok == TOKEN_TYPENAME) {
#if 0
- if (opts.standard != COMPILER_GMQCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
if (parsewarning(parser, WARN_EXTENSIONS,
"current standard does not allow variable declarations in for-loop initializers"))
goto onerr;
/* parse the incrementor */
if (parser->tok != ')') {
+ lex_ctx condctx = parser_ctx(parser);
increment = parse_expression_leave(parser, false, false, false);
if (!increment)
goto onerr;
if (!ast_side_effects(increment)) {
- if (genwarning(ast_ctx(increment), WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
+ if (compile_warning(condctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
goto onerr;
}
}
parseerror(parser, "cannot declare a variable from here");
return false;
}
- if (opts.standard == COMPILER_QCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
if (parsewarning(parser, WARN_EXTENSIONS, "missing 'local' keyword when declaring a local variable"))
return false;
}
}
else if (!strcmp(parser_tokval(parser), "for"))
{
- if (opts.standard == COMPILER_QCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
if (parsewarning(parser, WARN_EXTENSIONS, "for loops are not recognized in the original Quake C standard, to enable try an alternate standard --std=?"))
return false;
}
}
else
{
+ lex_ctx ctx = parser_ctx(parser);
ast_expression *exp = parse_expression(parser, false, false);
if (!exp)
return false;
*out = exp;
if (!ast_side_effects(exp)) {
- if (genwarning(ast_ctx(exp), WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
+ if (compile_warning(ctx, WARN_EFFECTLESS_STATEMENT, "statement has no effect"))
return false;
}
return true;
goto enderrfn;
}
func->varargs = varargs;
+
+ func->fixedparams = parser_const_float(parser, vec_size(var->expression.params));
}
parser->function = func;
if (parser->tok == ';')
return parser_next(parser);
- else if (opts.standard == COMPILER_QCC)
+ else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
parseerror(parser, "missing semicolon after function body (mandatory with -std=qcc)");
return retval;
vec_free(params);
/* sanity check */
- if (vec_size(params) > 8 && opts.standard == COMPILER_QCC)
+ if (vec_size(params) > 8 && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
(void)!parsewarning(parser, WARN_EXTENSIONS, "more than 8 parameters are not supported by this standard");
/* parse-out */
}
/* now there may be function parens again */
- if (parser->tok == '(' && opts.standard == COMPILER_QCC)
+ if (parser->tok == '(' && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
if (parser->tok == '(' && wasarray)
parseerror(parser, "arrays as part of a return type is not supported");
/* Part 0: finish the type */
if (parser->tok == '(') {
- if (opts.standard == COMPILER_QCC)
+ if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
var = parse_parameter_list(parser, var);
if (!var) {
}
/* for functions returning functions */
while (parser->tok == '(') {
- if (opts.standard == COMPILER_QCC)
+ if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC)
parseerror(parser, "C-style function syntax is not allowed in -std=qcc");
var = parse_parameter_list(parser, var);
if (!var) {
goto cleanup;
*/
}
- if ((opts.standard == COMPILER_QCC || opts.standard == COMPILER_FTEQCC) &&
+ if ((OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC || OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) &&
(old = parser_find_global(parser, var->name)))
{
parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
ast_delete(var);
var = proto;
}
- if (opts.standard == COMPILER_QCC &&
+ if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC &&
(old = parser_find_field(parser, var->name)))
{
parseerror(parser, "cannot declare a field and a global of the same name with -std=qcc");
retval = false;
goto cleanup;
}
- if (opts.standard != COMPILER_GMQCC) {
+ if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_GMQCC) {
ast_delete(var);
var = NULL;
goto skipvar;
break;
}
- if (localblock && opts.standard == COMPILER_QCC) {
+ if (localblock && OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
if (parsewarning(parser, WARN_LOCAL_CONSTANTS,
"initializing expression turns variable `%s` into a constant in this standard",
var->name) )
break;
}
}
- else if (opts.standard == COMPILER_QCC) {
+ else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
parseerror(parser, "expected '=' before function body in this standard");
}
}
} else {
int cvq;
- shunt sy = { NULL, NULL };
+ shunt sy = { NULL, NULL, NULL, NULL };
cvq = var->cvq;
var->cvq = CV_NONE;
vec_push(sy.out, syexp(ast_ctx(var), (ast_expression*)var));
}
else
{
- parseerror(parser, "unexpected token: %s", parser->lex->tok.value);
+ parseerror(parser, "unexpected token: `%s`", parser->lex->tok.value);
return false;
}
return true;
parser->const_vec[1] = ast_value_new(empty_ctx, "<vector.y>", TYPE_NOEXPR);
parser->const_vec[2] = ast_value_new(empty_ctx, "<vector.z>", TYPE_NOEXPR);
- if (opts.add_info) {
+ if (OPTS_OPTION_BOOL(OPTION_ADD_INFO)) {
parser->reserved_version = ast_value_new(empty_ctx, "reserved:version", TYPE_STRING);
parser->reserved_version->cvq = CV_CONST;
parser->reserved_version->hasvalue = true;
continue;
asvalue = (ast_value*)(parser->globals[i]);
if (!asvalue->uses && !asvalue->hasvalue && asvalue->expression.vtype != TYPE_FUNCTION) {
- retval = retval && !genwarning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
- "unused global: `%s`", asvalue->name);
+ retval = retval && !compile_warning(ast_ctx(asvalue), WARN_UNUSED_VARIABLE,
+ "unused global: `%s`", asvalue->name);
}
if (!ast_global_codegen(asvalue, ir, false)) {
con_out("failed to generate global %s\n", asvalue->name);
return false;
}
}
- if (opts.dump)
+ if (OPTS_OPTION_BOOL(OPTION_DUMP))
ir_builder_dump(ir, con_out);
for (i = 0; i < vec_size(parser->functions); ++i) {
if (!ir_function_finalize(parser->functions[i]->ir_func)) {
}
if (retval) {
- if (opts.dumpfin)
+ if (OPTS_OPTION_BOOL(OPTION_DUMPFIN))
ir_builder_dump(ir, con_out);
generate_checksum(parser);
* Used to set a description of the current test, this must be
* provided, this tag is NOT optional.
*
- * F:
- * Used to set a failure message, this message will be displayed
- * if the test fails, this tag is optional
- *
- * S:
- * Used to set a success message, this message will be displayed
- * if the test succeeds, this tag is optional.
- *
* T:
* Used to set the procedure for the given task, there are two
* options for this:
* Used to set the compilation flags for the given task, this
* must be provided, this tag is NOT optional.
*
+ * F: Used to set some test suite flags, currently the only option
+ * is -no-defs (to including of defs.qh)
+ *
* E:
* Used to set the execution flags for the given task. This tag
* must be provided if T == -execute, otherwise it's erroneous
*/
typedef struct {
char *description;
- char *failuremessage;
- char *successmessage;
char *compileflags;
char *executeflags;
char *proceduretype;
char *sourcefile;
char *tempfilename;
char **comparematch;
+ char *rulesfile;
+ char *testflags;
} task_template_t;
/*
* 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 *template, char tag, const char *file, size_t line, const char *value) {
+bool task_template_generate(task_template_t *template, char tag, const char *file, size_t line, const char *value, size_t *pad) {
+ size_t desclen = 0;
char **destval = NULL;
if (!template)
switch(tag) {
case 'D': destval = &template->description; break;
- case 'F': destval = &template->failuremessage; break;
- case 'S': destval = &template->successmessage; break;
case 'T': destval = &template->proceduretype; break;
case 'C': destval = &template->compileflags; break;
case 'E': destval = &template->executeflags; break;
case 'I': destval = &template->sourcefile; break;
+ case 'F': destval = &template->testflags; break;
default:
con_printmsg(LVL_ERROR, __FILE__, __LINE__, "internal error",
"invalid tag `%c:` during code generation\n",
*/
*destval = util_strdup(value);
+
+ if (*destval == template->description) {
+ /*
+ * Create some padding for the description to align the
+ * printing of the rules file.
+ */
+ if ((desclen = strlen(template->description)) > pad[0])
+ pad[0] = desclen;
+ }
+
return true;
}
-bool task_template_parse(const char *file, task_template_t *template, FILE *fp) {
+bool task_template_parse(const char *file, task_template_t *template, FILE *fp, size_t *pad) {
char *data = NULL;
char *back = NULL;
size_t size = 0;
* it to.
*/
case 'D':
- case 'F':
- case 'S':
case 'T':
case 'C':
case 'E':
case 'I':
+ case 'F':
if (data[1] != ':') {
con_printmsg(LVL_ERROR, file, line, "template parse error",
"expected `:` after `%c`",
);
goto failure;
}
- if (!task_template_generate(template, *data, file, line, &data[3])) {
+ if (!task_template_generate(template, *data, file, line, &data[3], pad)) {
con_printmsg(LVL_ERROR, file, line, "template compile error",
"failed to generate for given task\n"
);
return;
template->description = NULL;
- template->failuremessage = NULL;
- template->successmessage = NULL;
template->proceduretype = NULL;
template->compileflags = NULL;
template->executeflags = NULL;
template->comparematch = NULL;
template->sourcefile = NULL;
template->tempfilename = NULL;
+ template->rulesfile = NULL;
+ template->testflags = NULL;
}
-task_template_t *task_template_compile(const char *file, const char *dir) {
+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;
FILE *tempfile = NULL;
task_template_t *template = NULL;
memset (fullfile, 0, sizeof(fullfile));
snprintf(fullfile, sizeof(fullfile), "%s/%s", dir, file);
- tempfile = file_open(fullfile, "r");
- template = mem_a(sizeof(task_template_t));
+ tempfile = file_open(fullfile, "r");
+ template = mem_a(sizeof(task_template_t));
task_template_nullify(template);
+ /*
+ * Create some padding for the printing to align the
+ * printing of the rules file to the console.
+ */
+ if ((filepadd = strlen(fullfile)) > pad[1])
+ pad[1] = filepadd;
+
+ template->rulesfile = util_strdup(fullfile);
+
/*
* Esnure the file even exists for the task, this is pretty useless
* to even do.
goto failure;
}
- if (!task_template_parse(file, template, tempfile)) {
+ if (!task_template_parse(file, template, tempfile, pad)) {
con_err("template parse error: error during parsing\n");
goto failure;
}
return;
if ((*template)->description) mem_d((*template)->description);
- if ((*template)->failuremessage) mem_d((*template)->failuremessage);
- if ((*template)->successmessage) mem_d((*template)->successmessage);
if ((*template)->proceduretype) mem_d((*template)->proceduretype);
if ((*template)->compileflags) mem_d((*template)->compileflags);
if ((*template)->executeflags) mem_d((*template)->executeflags);
if ((*template)->sourcefile) mem_d((*template)->sourcefile);
+ if ((*template)->rulesfile) mem_d((*template)->rulesfile);
+ if ((*template)->testflags) mem_d((*template)->testflags);
/*
* Delete all allocated string for task template then destroy the
* 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) {
+bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
bool success = true;
DIR *dir;
struct dirent *files;
* actually a directory, so it must be a file :)
*/
if (strcmp(files->d_name + strlen(files->d_name) - 5, ".tmpl") == 0) {
- task_template_t *template = task_template_compile(files->d_name, curdir);
+ task_template_t *template = task_template_compile(files->d_name, curdir, pad);
char buf[4096]; /* one page should be enough */
char *qcflags = NULL;
task_t task;
*/
memset (buf,0,sizeof(buf));
if (qcflags) {
- snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
- task_bins[TASK_COMPILE],
- curdir,
- template->sourcefile,
- qcflags,
- template->compileflags,
- template->tempfilename
- );
+ if (template->testflags && !strcmp(template->testflags, "-no-defs")) {
+ snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
+ task_bins[TASK_COMPILE],
+ curdir,
+ template->sourcefile,
+ qcflags,
+ template->compileflags,
+ template->tempfilename
+ );
+ } else {
+ snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s",
+ task_bins[TASK_COMPILE],
+ curdir,
+ defs,
+ curdir,
+ template->sourcefile,
+ qcflags,
+ template->compileflags,
+ template->tempfilename
+ );
+ }
} else {
- snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
- task_bins[TASK_COMPILE],
- curdir,
- template->sourcefile,
- template->compileflags,
- template->tempfilename
- );
+ if (template->testflags && !strcmp(template->testflags, "-no-defs")) {
+ snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
+ task_bins[TASK_COMPILE],
+ curdir,
+ template->sourcefile,
+ template->compileflags,
+ template->tempfilename
+ );
+ } else {
+ snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s",
+ task_bins[TASK_COMPILE],
+ curdir,
+ defs,
+ curdir,
+ template->sourcefile,
+ template->compileflags,
+ template->tempfilename
+ );
+ }
}
/*
size_t compare = 0;
while (file_getline(&data, &size, execute) != EOF) {
if (!strcmp(data, "No main function found\n")) {
- con_err("test failure: `%s` [%s] (No main function found)\n",
+ con_err("test failure: `%s` (No main function found) [%s]\n",
template->description,
- (template->failuremessage) ?
- template->failuremessage : "unknown"
+ template->rulesfile
);
pclose(execute);
return false;
* execution this takes more work since a task needs to be generated
* from thin air and executed INLINE.
*/
-void task_schedualize() {
+void task_schedualize(size_t *pad) {
bool execute = false;
char *data = NULL;
char **match = NULL;
}
if (!task_tasks[i].compiled && strcmp(task_tasks[i].template->proceduretype, "-fail")) {
- con_err("test failure: `%s` [%s] (failed to compile) see %s.stdout and %s.stderr\n",
+ con_err("test failure: `%s` (failed to compile) see %s.stdout and %s.stderr [%s]\n",
task_tasks[i].template->description,
- (task_tasks[i].template->failuremessage) ?
- task_tasks[i].template->failuremessage : "unknown",
task_tasks[i].template->tempfilename,
- task_tasks[i].template->tempfilename
+ task_tasks[i].template->tempfilename,
+ task_tasks[i].template->rulesfile
);
continue;
}
if (!execute) {
- con_out("test succeeded: `%s` [%s]\n",
- task_tasks[i].template->description,
- (task_tasks[i].template->successmessage) ?
- task_tasks[i].template->successmessage : "unknown"
+ con_out("test succeeded: `%s` %*s\n",
+ task_tasks[i].template->description,
+ (pad[0] + pad[1] - strlen(task_tasks[i].template->description)) +
+ (strlen(task_tasks[i].template->rulesfile) - pad[1]),
+ task_tasks[i].template->rulesfile
+
);
continue;
}
if (!task_execute(task_tasks[i].template, &match)) {
size_t d = 0;
- con_err("test failure: `%s` [%s] (invalid results from execution)\n",
- task_tasks[i].template->description,
- (task_tasks[i].template->failuremessage) ?
- task_tasks[i].template->failuremessage : "unknown"
+ con_err("test failure: `%s` (invalid results from execution) [%s]\n",
+ task_tasks[i].template->description,
+ task_tasks[i].template->rulesfile
);
/*
mem_d(match[j]);
vec_free(match);
- con_out("test succeeded: `%s` [%s]\n",
- task_tasks[i].template->description,
- (task_tasks[i].template->successmessage) ?
- task_tasks[i].template->successmessage : "unknown"
+ con_out("test succeeded: `%s` %*s\n",
+ task_tasks[i].template->description,
+ (pad[0] + pad[1] - strlen(task_tasks[i].template->description)) +
+ (strlen(task_tasks[i].template->rulesfile) - pad[1]),
+ task_tasks[i].template->rulesfile
+
);
}
mem_d(data);
*
* It expects con_init() was called before hand.
*/
-bool test_perform(const char *curdir) {
+GMQCC_WARN bool test_perform(const char *curdir, const char *defs) {
+ static const char *default_defs = "defs.qh";
+
+ size_t pad[] = {
+ 0, 0
+ };
+
+ /*
+ * If the default definition file isn't set to anything. We will
+ * use the default_defs here, which is "defs.qc"
+ */
+ if (!defs) {
+ defs = default_defs;
+ }
+
+
task_precleanup(curdir);
- if (!task_propagate(curdir)) {
+ if (!task_propagate(curdir, pad, defs)) {
con_err("error: failed to propagate tasks\n");
task_destroy();
return false;
* it's designed to prevent lock contention, and possible syncronization
* issues.
*/
- task_schedualize();
+ task_schedualize(pad);
task_destroy();
return true;
}
int main(int argc, char **argv) {
+ bool succeed = false;
char *redirout = (char*)stdout;
char *redirerr = (char*)stderr;
+ char *defs = NULL;
con_init();
continue;
if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false))
continue;
+ if (parsecmd("defs", &argc, &argv, &defs, 1, false))
+ continue;
con_change(redirout, redirerr);
if (!strcmp(argv[0]+1, "debug")) {
- opts.debug = true;
+ OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
continue;
}
if (!strcmp(argv[0]+1, "memchk")) {
- opts.memchk = true;
+ OPTS_OPTION_BOOL(OPTION_MEMCHK) = true;
continue;
}
if (!strcmp(argv[0]+1, "nocolor")) {
}
}
con_change(redirout, redirerr);
- test_perform("tests");
+ succeed = test_perform("tests", defs);
util_meminfo();
- return 0;
+
+
+ return (succeed) ? EXIT_SUCCESS : EXIT_FAILURE;
}
-void print(...) = #1;
-string ftos (float) = #2;
-entity() spawn = #3;
-
float glob[7];
.float above;
--- /dev/null
+void main() {
+ float a; a = 1;
+ float b; b = 1;
+ float c; c = 1;
+ float d; d = 1;
+
+ a &~= 1; // 0
+ b &= ~1; // 0
+ c &= ~d; // 0
+
+ print("a: ", ftos(a), "\nb: ",
+ ftos(b), "\nc: ",
+ ftos(c), "\n");
+}
--- /dev/null
+# used to test the builtins
+I: bitnot.qc
+D: test bitwise not operators
+T: -execute
+C: -std=gmqcc
+E: $null
+M: a: 0
+M: b: 0
+M: c: 0
-void print(...) = #1;
-string ftos (float) = #2;
-
void test(float brkat, float contat) {
float i;
-void(string) print = #1;
-
void() main = {
print("hello world");
}
T: -execute
C: -std=gmqcc
E: $null
-F: builtins failed
-S: builtins worked
M: hello world
-void(string, ...) print = #1;
-string(float) ftos = #2;
-
float(float x, float y, float z) sum = {
return x + y + z;
};
T: -execute
C: -std=gmqcc
E: -float 100 -float 200 -float 300
-F: calls failed
-S: calls worked
M: 4600
-void print(...) = #1;
-string ftos (float) = #2;
-
float test_s_not (vector s) { return !s; }
float test_s_and (vector s, vector t) { return s && t; }
float test_s_or (vector s, vector t) { return s || t; }
-void print(...) = #1;
-string ftos (float) = #2;
-
void test(vector a, vector b) {
print(ftos((a && b) + (a && b)), " ");
print(ftos((a || b) + (a || b)), " ");
--- /dev/null
+// builtins for the standalone qcvm included with gmqcc
+// in exec.c These should be updated to reflect the new
+// builtins. I no event shall you even consider adding
+// these individually per test.
+
+void (string, ...) print = #1;
+string (float) ftos = #2;
+entity () spawn = #3;
+void (entity) kill = #4;
+string (vector) vtos = #5;
+void (string) error = #6;
+float (vector) vlen = #7;
+string (entity) etos = #8;
+float (string) stof = #9;
+string (...) strcat = #10;
+float (string, string) strcmp = #11;
+vector (vector) normalize = #12;
+float (float) sqrt = #13;
N
};
-void (string, ...) print = #1;
-string (float) ftos = #2;
void main() {
print(ftos(A), "\n");
print(ftos(B), "\n");
-void(string, ...) print = #1;
-string(float) ftos = #2;
-
void(float a, float b) main = {
if (a == b) print("eq,");
if (a != b) print("ne,");
T: -execute
C: -std=gmqcc
E: -float 100 -float 200
-F: equality failed
-S: equality worked
M: ne,lt,le,
-void(string, string) print = #1;
-entity() spawn = #3;
-
.string a;
.string b;
..string ps;
T: -execute
C: -std=qcc
E: $null
-F: field paramaters fail
-S: field paramaters work
M: bar
M: foo
-void(string, string) print = #1;
-
string() getter = {
return "correct";
};
T: -execute
C: -std=gmqcc
E: $null
-F: functions as paramaters failed
-S: functions as paramaters passed
M: correct
-void(string, ...) print = #1;
-
// correct execution order:
// label_3
// label_2
D: test goto (both normal and computed)
T: -execute
C: -std=gmqcc
-F: goto failed
-S: goto worked
M: label_3
M: label_2
M: label_4
-void(string, ...) print = #1;
-
void(float c) main = {
if (c == 1)
print("One\n");
T: -execute
C: -std=gmqcc
E: -float 2
-F: if statement failed
-S: if statement passed
M: Two
-void print(...) = #1;
-string vtos(vector) = #5;
-
// getter to work around future -O
vector get(vector v) {
return v;
-void(...) print = %:1;
-
void() main = ??<
print("??=??'??(??)??!??<??>??-??/??/%>|");
print("#^[]|{}~\\%>\n");
T: -execute
C: -std=gmqcc
E: $null
-F: digraphs and trigraphs failed
-S: digraphs and trigraphs passed
M: #^[]|{}~\%>|#^[]|{}~\%>
#define NORETURN [[noreturn]]
#endif
-void print(...) = #1;
-string ftos(float) = #2;
+void (...) print = #1;
+string (float) ftos = #2;
+
+
NORETURN void error(...) = #6;
#if TEST == 1
D: noreturn keyword - should work
T: -compile
C: -std=fteqcc -Wall -Werror -DTEST=1 -DNORETURN=[[noreturn]]
+F: -no-defs
D: noreturn keyword - should fail
T: -compile
C: -std=fteqcc -Wall -Werror -DTEST=2 -DNORETURN=[[noreturn]]
+F: -no-defs
D: noreturn keyword - should work
T: -fail
C: -std=fteqcc -Wall -Werror -DTEST=1 -DNORETURN
+F: -no-defs
D: noreturn keyword - should fail
T: -fail
C: -std=fteqcc -Wall -Werror -DTEST=2 -DNORETURN
+F: -no-defs
-void print(...) = #1;
-string ftos (float) = #2;
-string vtos (vector) = #5;
-entity spawn() = #3;
-
.float mem;
void main() {
-void print(...) = #1;
-string ftos(float) = #2;
-
void p10(float a, float b, float c, float d, float e, float f, float g, float h,
float e1, float e2)
{
D: test extended parameters
T: -execute
C: -std=fteqcc
-F: extended parameters failed
-S: extended parameters worked
M: 10 20 30 40 50 60 70 80 90 100
M: 1 1 1 1 1 1 1 1 1 1
M: 10 20 30 40 50 60 70 80 90 100
--- /dev/null
+float arr[2];
+
+string gets() { return "S\n"; }
+void main(float x) {
+ string s;
+
+ s = gets(); // 0 params
+ print(s); // 1 param
+ print("A ", "B\n"); // 2 params
+ print("A ", "B ", "C\n"); // more params
+ print(gets()); // 0-param call in call
+ print(gets(), "next\n"); // 0-param call and another
+ print("-> ", gets()); // param + 0-param call
+ print(ftos(x), "\n"); // param-call + another
+ print(x ? "xA\n" : "xB\n"); // ternary in PAREN_FUNC
+ print(!x ? "xA\n" : "xB\n"); // ternary in PAREN_FUNC
+ // PAREN_INDEX
+ arr[0] = 10;
+ arr[1] = 11;
+ // PAREN_TERNARY + PAREN_INDEX
+ arr[x ? 0 : 1] += 100;
+ print(ftos(arr[0]), "\n");
+ print(ftos(arr[1]), "\n");
+ print(ftos(arr[x ? 0 : 1]), "\n");
+ print(ftos(arr[!x ? 0 : 1]), "\n");
+
+ // loops with comma operators
+ float i, j;
+ for (i = 0, j = 0; i < x; ++i)
+ print("-");
+ print("\n");
+
+ // if + PAREN_TERNARY2
+ if (x ? 1 : 0)
+ print("OK\n");
+ if (x ? 0 : 1)
+ print("NO\n");
+
+ // PAREN_FUNC in PAREN_EXPR
+ print(("Is this wrong ", "now?\n"));
+}
--- /dev/null
+I: parens.qc
+D: parentheses, SYA stuff
+T: -execute
+C: -std=fteqcc
+E: -float 4
+M: S
+M: A B
+M: A B C
+M: S
+M: S
+M: next
+M: -> S
+M: 4
+M: xA
+M: xB
+M: 110
+M: 11
+M: 110
+M: 11
+M: ----
+M: OK
+M: now?
-void print(...) = #1;
-
void main() {
vector va, vb;
string sa, sb;
# define ABC ALPHA(a)##ALPHA(b)##ALPHA(c)
- void(string, ...) print = #1;
void() main = {
if (ABC == "abc")
print("ABC\n");
D: test preprocessor
T: -execute
C: -std=fteqcc
-F: preprocessor failed
-S: preprocessor works
M: ABC
M: 123
-void print(...) = #1;
-
var float foo = 0;
void funcall() {}
-void print(...) = #1;
+// method 0
+#define METHOD__(...) __VA_ARGS__
+#define METHOD_0(F,A) F METHOD__(A)
-#define NOPARENS(...) __VA_ARGS__
-#define callem(func, args) func(NOPARENS args)
+// method 1
+#define METHOD_1(F,A) F(METHOD__ A)
+
+// method 2
+#define METHOD_2(F,...) F __VA_ARGS__##[0]
+
+// method 3
+#define METHOD_3(F,...) F __VA_ARGS__
+
+// selector
+#define METHOD(I, F, ...) METHOD_##I (F, __VA_ARGS__)
void main() {
- print(NOPARENS("hello ", "world\n"));
- callem(print, ("Yay", ", there\n"));
+ METHOD(0, print, ("Method", " <zero>\n"));
+ METHOD(1, print, ("Method", " <one>\n"));
+ METHOD(2, print, ("Method", " <two>\n"));
+ METHOD(3, print, ("Method", " <three>\n"));
}
D: __VA_ARGS__
T: -execute
C: -std=fteqcc
-M: hello world
-M: Yay, there
+M: Method <zero>
+M: Method <one>
+M: Method <two>
+M: Method <three>
-void print(...) = #1;
-string ftos(float) = #2;
-
float glob1;
float glob2;
float glob3;
-void print(...) = #1;
-string ftos(float) = #2;
-
void test(float param, float p2) {
float i;
float c80 = 80;
-void print(...) = #1;
-string ftos (float) = #2;
-
void test(float cond, float v1, float v2, float a) {
print(ftos(cond ? v1 : v2), " ");
print( (cond ? v1 : v2) ? ( (a == 1) ? "a=1"
-void print(...) = #1;
-string ftos (float) = #2;
-
float test_s_not (string s) { return !s; }
float test_s_and (string s, string t) { return s && t; }
float test_s_or (string s, string t) { return s || t; }
-void print(...) = #1;
-string ftos (float) = #2;
-
void test(string s) {
print(ftos(!s));
if (s) print(" on");
-typedef void(string, ...) ptype;
+typedef void(...) ptype;
typedef string(float) funcsf;
ptype print = #1;
T: -execute
C: -std=fteqcc
M: A typedeffed function, 0=0
+F: -no-defs
-void print(...) = #1;
-string ftos (float) = #2;
-string vtos (vector) = #5;
-
vector main(float a, vector vin) {
vector v;
-void print(...) = #1;
-string ftos (float) = #2;
-
void main() {
print("Sum: \{x2211} ");
print("\{8721} ");
-void(string...) print = #1;
-string(float) ftos = #2;
-
void nbva(float a, string...count) {
print("You gave me ", ftos(count), " additional parameters\n");
print("First: ", ...(0, string), "\n");
print("Vararg ", ftos(a), " = ", ...(a, string), "\n");
}
+var void unstable(...);
+void stability(float a, float b, ...count)
+{
+ print("Got: ", ftos(count), "\n");
+}
+
void main() {
nbva(1, "Hello", "You", "There");
+ stability(1, 2, 3, 4, 5);
+ unstable = stability;
+ unstable(1, 2, 3, 4, 5);
}
M: Vararg 0 = Hello
M: Vararg 1 = You
M: Vararg 2 = There
+M: Got: 3
+M: Got: 3
-void(...) print = #1;
-
void() main = {
print("hello", " world");
}
T: -execute
C: -std=gmqcc
E: $null
-F: variadic arguments failed
-S: variadic arguments worked
M: hello world
-void print(string...) = #1;
-string vtos(vector) = #5;
-
void main(vector v) {
print(vtos(v), "\n");
v /= 2;
-void print(...) = #1;
-string ftos (float) = #2;
-string vtos (vector) = #5;
-
void main() {
vector v;
void util_meminfo() {
struct memblock_t *info;
- if (!opts.memchk)
+ if (!OPTS_OPTION_BOOL(OPTION_MEMCHK))
return;
for (info = mem_start; info; info = info->next) {
void util_debug(const char *area, const char *ms, ...) {
va_list va;
- if (!opts.debug)
+ if (!OPTS_OPTION_BOOL(OPTION_DEBUG))
return;
- if (!strcmp(area, "MEM") && !opts.memchk)
+ if (!strcmp(area, "MEM") && !OPTS_OPTION_BOOL(OPTION_MEMCHK))
return;
va_start(va, ms);
set->bits ++;
set->capacity = (size_t)(1 << set->bits);
set->mask = set->capacity - 1;
- set->items = mem_a(set->capacity * sizeof(size_t));
+ set->items = (size_t*)mem_a(set->capacity * sizeof(size_t));
set->total = 0;
/*assert(set->items);*/
hash_set_t *util_hsnew(void) {
hash_set_t *set;
- if (!(set = mem_a(sizeof(hash_set_t))))
+ if (!(set = (hash_set_t*)mem_a(sizeof(hash_set_t))))
return NULL;
set->bits = 3;
set->total = 0;
set->capacity = (size_t)(1 << set->bits);
set->mask = set->capacity - 1;
- set->items = mem_a(set->capacity * sizeof(size_t));
+ set->items = (size_t*)mem_a(set->capacity * sizeof(size_t));
if (!set->items) {
util_hsdel(set);
}
/* not large enough ... */
- tmp = mem_a(len + 1);
+ tmp = (char*)mem_a(len + 1);
if ((ret = vsnprintf(tmp, len + 1, fmt, args)) != len) {
mem_d(tmp);
*dat = NULL;