testsuite
qcvm
gmqcc
+pak
-distro/arch/*
+distro/archlinux/*
+distro/archbsd/*
!distro/archlinux/git/PKGBUILD
!distro/archlinux/release/PKGBUILD
!distro/archbsd/release/PKGBUILD
!distro/archbsd/git/PKGBUILD
-!distro/arch/this/Makefile
+!distro/archlinux/this/Makefile
-Release v0.2.4
+Release v0.2.9
* Preprocessor:
- __VA_ARGS__ support
+ _ __VA_ARGS__ indexing
- Predefined macros like __DATE__, __TIME__, ...
(check the manpage for a full list)
- Signed numbers as single token in the
- Fixes some issues with #if operations on macros.
+ - Speed improvements
* Language:
- Untyped `nil` keyword.
- Removed the `noreturn` keyword.
- Type restricted variadict parameters:
ie: void print(string...);
- Accessing varargs from QC via: ...(index, type)
+ - New operators: ** (exponentiation), % (modulo), etc
+ - Enumeration attributes: flag, reverse
* Compilation:
- Various optimizations and progs-size reductions.
- A new spell-checking algorithm tries to hint you at existing
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:
+ - Compiler intrinsics: __builtin_floor, __builtin_mod,
+ __builtin_exp, __builtin_isnan
+ - Improved memory tracing
+ - Speed improvements
+ * QCVM:
- Improved commandline argument handling.
- More builtins: sqrt(), normalize()
* Commandline:
+ - Nicer memory dumps
- Support for making individual warnings an error
- via -Werror-<warning>
- added --add-info
* Testsuite:
- Support for QCFLAGS to run tests with several additional
flags.
+ - Added support for preprocessor tests
+ - Added preprocessor tests
+ - Added defs.qh (auto included) for qcvm definitions
+ * Syntax Highlighting:
+ - Added various syntax highlighting description files for
+ various text editors / integrated development envirorments,
+ including support for: geany, kate, kwrite, kdevelop, QtCreator,
+ gtksourceview, gedit, sany, nano, jedit
+ * Build:
+ - Build scripts for building debian, archlinux and archbsd
+ packages for x86, and x86_64.
+ - Makefile targets for gource visualization, and render of
+ gource visualization.
+
2012-12-27 Hotfix v0.2.2
* Liferanges
DESTDIR :=
+OPTIONAL:=
PREFIX := /usr/local
BINDIR := $(PREFIX)/bin
DATADIR := $(PREFIX)/share
MINGW = $(findstring MINGW32, $(UNAME))
CC ?= clang
-CFLAGS += -Wall -Wextra -Werror -I. -fno-strict-aliasing -fsigned-char
+CFLAGS += -Wall -Wextra -Werror -I. -fno-strict-aliasing -fsigned-char $(OPTIONAL)
ifneq ($(shell git describe --always 2>/dev/null),)
CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\""
endif
# 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 \
- -Wno-missing-variable-declarations \
- -Wno-unknown-warning-option
+ -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.
ifneq ($(CC), tcc)
- CFLAGS +=-pedantic-errors
+ CFLAGS +=-pedantic-errors -ffunction-sections -fdata-sections -Wl,-gc-sections
else
CFLAGS += -Wno-pointer-sign -fno-common
endif
endif
ifeq ($(track), no)
- CFLAGS += -DNOTRACK
+ CFLAGS += -DNOTRACK
endif
OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o
endif
endif
+#gource flags
+GOURCEFLAGS= \
+ --date-format "%d %B, %Y" \
+ --seconds-per-day 0.01 \
+ --auto-skip-seconds 1 \
+ --title "GMQCC" \
+ --key \
+ --camera-mode overview \
+ --highlight-all-users \
+ --file-idle-time 0 \
+ --hide progress,mouse \
+ --stop-at-end \
+ --max-files 99999999999 \
+ --max-file-lag 0.000001 \
+ --bloom-multiplier 1.3 \
+ -1280x720
+
+#ffmpeg flags for gource
+FFMPEGFLAGS= \
+ -y \
+ -r 60 \
+ -f image2pipe \
+ -vcodec ppm \
+ -i - \
+ -vcodec libx264 \
+ -preset ultrafast \
+ -crf 1 \
+ -threads 0 \
+ -bf 0
+
#splint flags
SPLINTFLAGS = \
-redef \
-kepttrans \
-unqualifiedtrans \
+matchanyintegral \
- -bufferoverflowhigh \
+voidabstract \
-nullassign \
-unrecog \
$(CC) -o $@ $^ $(CFLAGS) -lm
$(GMQCC): $(OBJ_C) $(OBJ_D)
- $(CC) -o $@ $^ $(CFLAGS)
+ $(CC) -o $@ $^ $(CFLAGS) -lm
$(TESTSUITE): $(OBJ_T)
$(CC) -o $@ $^ $(CFLAGS) -lm
@ ./$(TESTSUITE)
clean:
- rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat
+ rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4
splint:
@ splint $(SPLINTFLAGS) *.c *.h
+gource:
+ @ gource $(GOURCEFLAGS)
+
+gource-record:
+ @ gource $(GOURCEFLAGS) -o - | ffmpeg $(FFMPEGFLAGS) gource.mp4
+
depend:
@makedepend -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_D))
main.o: gmqcc.h opts.def lexer.h
lexer.o: gmqcc.h opts.def lexer.h
-parser.o: gmqcc.h opts.def lexer.h ast.h ir.h
+parser.o: gmqcc.h opts.def lexer.h ast.h ir.h intrin.h
fs.o: gmqcc.h opts.def
util.o: gmqcc.h opts.def
conout.o: gmqcc.h opts.def
fs.o: gmqcc.h opts.def
+
+util.o: gmqcc.h opts.def
+fs.o: gmqcc.h opts.def
+conout.o: gmqcc.h opts.def
+opts.o: gmqcc.h opts.def
+pak.o: gmqcc.h opts.def
GMQCC: An improved Quake C compiler
-For licensing: see the LICENSE file.
-For installation notes: see the INSTALL file.
-For a list of authors: see the AUTHORS file.
-For a list of changes: see the CHANGES file.
+For licensing: see the LICENSE file.
+For installation notes: see the INSTALL file.
+For a list of authors: see the AUTHORS file.
+For a list of changes: see the CHANGES file.
For documentation:
See the manpages, or visit the documentation online at
http://graphitemaster.github.com/gmqcc/doc.html
+
+For syntax highlighting description files, or information
+regarding how to install them:
+ See the README in syntax directory
{
(void)self;
con_err("ast node missing destroy()\n");
- abort();
+ exit(EXIT_FAILURE);
}
/* Initialize main ast node aprts */
ast_delete(self->expression.params[i]);
}
vec_free(self->expression.params);
+ if (self->expression.varparam)
+ ast_delete(self->expression.varparam);
}
static void ast_expression_delete_full(ast_expression *self)
if (!e) {
if (pos + 6 >= bufsize)
goto full;
- strcpy(buf + pos, "(null)");
+ strncpy(buf + pos, "(null)", 6);
return pos + 6;
}
switch (e->expression.vtype) {
case TYPE_VARIANT:
- strcpy(buf + pos, "(variant)");
+ strncpy(buf + pos, "(variant)", 9);
return pos + 9;
case TYPE_FIELD:
typelen = strlen(typestr);
if (pos + typelen >= bufsize)
goto full;
- strcpy(buf + pos, typestr);
+ strncpy(buf + pos, typestr, typelen);
return pos + typelen;
}
* purpose that is not garbage-collected.
*/
ast_expression_delete((ast_expression*)self);
+ mem_d(self->name);
mem_d(self);
}
namelen = strlen(self->name);
name = (char*)mem_a(namelen + 16);
- strcpy(name, self->name);
+ strncpy(name, self->name, namelen);
array->ir_values = (ir_value**)mem_a(sizeof(array->ir_values[0]) * array->expression.count);
array->ir_values[0] = v;
namelen = strlen(self->name);
name = (char*)mem_a(namelen + 16);
- strcpy(name, self->name);
+ strncpy(name, self->name, namelen);
self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
self->ir_values[0] = v;
v = ir_function_create_local(func, self->name, vtype, param);
if (!v) {
- compile_error(ast_ctx(self), "ir_function_create_local failed");
+ compile_error(ast_ctx(self), "internal error: ir_function_create_local failed");
return false;
}
v->context = ast_ctx(self);
namelen = strlen(self->name);
name = (char*)mem_a(namelen + 16);
- strcpy(name, self->name);
+ strncpy(name, self->name, namelen);
self->ir_values[0] = v;
for (ai = 1; ai < self->expression.count; ++ai) {
snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
self->ir_values[ai] = ir_function_create_local(func, name, vtype, param);
if (!self->ir_values[ai]) {
- compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name);
+ compile_error(ast_ctx(self), "internal_error: ir_builder_create_global failed on `%s`", name);
return false;
}
self->ir_values[ai]->context = ast_ctx(self);
self->ir_values[ai]->unique_life = true;
self->ir_values[ai]->locked = true;
}
+ mem_d(name);
}
else
{
irf = self->ir_func;
if (!irf) {
- compile_error(ast_ctx(self), "ast_function's related ast_value was not generated yet");
+ compile_error(ast_ctx(self), "internal error: ast_function's related ast_value was not generated yet");
return false;
}
state = -1;
}
} else {
- fs_file_putc(h, *str);
+ fs_file_write(str, 1, 1, stdout);
length ++;
}
str++;
con_enablecolor();
} else if (!(console.handle_err = fs_file_open(err, "w"))) return 0;
- /* no buffering */
- setvbuf(console.handle_out, NULL, _IONBF, 0);
- setvbuf(console.handle_err, NULL, _IONBF, 0);
-
return 1;
}
--- /dev/null
+DROPBOX := dropbox_uploader.sh
+UNAME := $(shell uname -m)
+ifneq ($(shell uname -m), x86_64)
+ $(error Cannot build packages without an x86_64 capable CPU)
+endif
+
+.NOTPARALLEL: base
+.NOTPARALLEL: upload
+
+base:
+ $(MAKE) -C deb/
+ $(MAKE) -C deb/ CARCH=i686
+ $(MAKE) -C archlinux/this/
+ $(MAKE) -C archlinux/this/ CARCH=i686
+ @mv deb/*.deb ./
+ @mv archlinux/this/*pkg.tar.xz ./
+
+upload:
+ @echo "APPKEY:76vh3q42hnvmzm3" > dropbox_config
+ @echo "APPSECRET:tmeecht2cmh72xa" >> dropbox_config
+ @echo "ACCESS_LEVEL:sandbox" >> dropbox_config
+ @echo "OAUTH_ACCESS_TOKEN:w0bxzf0dft8edfq" >> dropbox_config
+ @echo "OAUTH_ACCESS_TOKEN_SECRET:9vosx7x8gy4kgjk" >> dropbox_config
+ @wget -q "http://raw.github.com/andreafabrizi/Dropbox-Uploader/master/dropbox_uploader.sh"
+ @chmod +x dropbox_uploader.sh
+ @sed -i -e "s/~\/.dropbox_uploader/.\/dropbox_config/g" $$(basename $(DROPBOX))
+ @find . -type f -regex ".*/.*\.\(xz\|deb\)" -exec ./$$(basename $(DROPBOX)) upload {} \;
+ @rm dropbox_config dropbox_uploader.sh
+
+clean:
+ @rm -f *.deb
+ @rm -f *.pkg.tar.xz
+
+all: base upload
package() {
cd "$srcdir"/"$_gitname"
msg "Compiling and installing to pkgdir this time..."
- gmake install DESTDIR=$pkgdir PREFIX=/usr/local MANDIR=/usr/local/man
+ gmake install DESTDIR=$pkgdir PREFIX=/usr
msg "Compiling done."
- install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc
- install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE
+ install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
+ install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
}
package() {
cd "$srcdir"/"$_gitname"
msg "Compiling and installing to pkgdir this time..."
- gmake install DESTDIR=$pkgdir PREFIX=/usr/local MANDIR=/usr/local/man
+ gmake install DESTDIR=$pkgdir PREFIX=/usr
msg "Compiling done."
- install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc
- install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE
+ install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
+ install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
}
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
pkgname=gmqcc-git
-pkgver=20130127
+pkgver=0.2.524.gc6bd5e6
+pkgver(){
+ cd gmqcc
+ git describe --tags | sed -e 's/^gmqcc\-//' -e 's/-/./g'
+}
pkgrel=1
pkgdesc="An Improved Quake C Compiler"
arch=('i686' 'x86_64')
makedepends=('git')
url="https://github.com/graphitemaster/gmqcc.git"
license=('MIT')
+source=('gmqcc::git://github.com/graphitemaster/gmqcc.git')
+sha1sums=('SKIP')
-_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"
+ cd "$srcdir"/"gmqcc"
msg "Compiling..."
make
}
check() {
- cd "$srcdir"/"$_gitname"
+ cd "$srcdir"/"gmqcc"
make check
}
package() {
- cd "$srcdir"/"$_gitname"
+ cd "$srcdir"/"gmqcc"
msg "Compiling and installing to pkgdir this time..."
make install DESTDIR=$pkgdir PREFIX=/usr
msg "Compiling done."
url="https://github.com/graphitemaster/gmqcc.git"
license=('MIT')
source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
-sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353')
+sha1sums=('e0fe99af9a55d36cd9e0909a96d1b14f2db8b757')
_gitname=graphitemaster-gmqcc-de24486/
PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH)
PKG := $(PKGDIR).pkg.tar.xz
PKGINFO := $(PKGDIR)/.PKGINFO
+DESTDIR := distro/archlinux/this/$(PKGDIR)
+CFLAGS :=
-base: clean
- $(MAKE) -C $(BASEDIR) DESTDIR=distro/archlinux/this/$(PKGDIR) PREFIX=$(PREFIX) install
+
+ifneq (, $(findstring i686, $(CARCH)))
+ CFLAGS := -m32
+endif
+
+base:
+ $(MAKE) -C $(BASEDIR) clean
+ $(MAKE) -C $(BASEDIR) OPTIONAL=$(CFLAGS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX) install
@echo "pkgname = gmqcc" > $(PKGINFO)
@echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO)
@echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO)
@rm -rf $(PKGDIR)
clean:
- @rm -f $(PKG)
-
+ $(MAKE) -C $(BASEDIR) clean
+ @rm -f *.pkg.tar.xz
all: base
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
+CARCH := $(shell uname -m)
+DEB := $(DEBDIR)-$(CARCH).deb
+CONTROL := $(DEBDIR)/DEBIAN/control
+
+ifneq (, $(findstring i686, $(CARCH)))
+ CFLAGS := -m32
+endif
base:
+ $(MAKE) -C $(BASEDIR) clean
$(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
@install -d -m755 $(DEBDIR)/DEBIAN
- @cp control $(DEBDIR)/DEBIAN/control
+ @echo "Package: gmqcc" > $(CONTROL)
+ @echo "Version: $(MAJOR).$(MINOR).$(PATCH)" >> $(CONTROL)
+ @echo "Section: user/hidden" >> $(CONTROL)
+ @echo "Priority: optional" >> $(CONTROL)
+ @echo "Architecture: $(CARCH)" >> $(CONTROL)
+ @echo "Installed-Size: `du -ks $($(DEBDIR)/usr) | cut -f 1`" >> $(CONTROL)
+ @echo "Maintainer: Dale Weiler <killfieldengine@gmail.com>" >> $(CONTROL)
+ @echo "Description: An improved Quake C Compiler" >> $(CONTROL)
+ @echo " For an enduring period of time the options for a decent compiler for the Quake C programming language" >> $(CONTROL)
+ @echo " were confined to a specific compiler known as QCC. Attempts were made to extend and improve upon the" >> $(CONTROL)
+ @echo " design of QCC, but many foreseen the consequences of building on a broken foundation. The solution" >> $(CONTROL)
+ @echo " was obvious, a new compiler; one born from the NIH realm of sarcastic wit. We welcome you. You won't" >> $(CONTROL)
+ @echo " find a better Quake C compiler." >> $(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)
+clean:
+ $(MAKE) -C $(BASEDIR) 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.
else printf("(none)");
printf("\n");
}
- fflush(stdout);
}
static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
return 0;
}
+static int qc_floor(qc_program *prog)
+{
+ qcany *num, out;
+ CheckArgs(1);
+ num = GetArg(0);
+ out._float = floor(num->_float);
+ Return(out);
+ return 0;
+}
+
static prog_builtin qc_builtins[] = {
NULL,
&qc_print, /* 1 */
&qc_strcat, /* 10 */
&qc_strcmp, /* 11 */
&qc_normalize, /* 12 */
- &qc_sqrt /* 13 */
+ &qc_sqrt, /* 13 */
+ &qc_floor /* 14 */
};
static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]);
) {
wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression);
wprintf(L"Aborting ...\n");
- abort();
+ exit(EXIT_FAILURE);
}
static void file_init() {
return fseek(fp, off, whence);
}
-int fs_file_putc(FILE *fp, int ch) {
- /* Invokes file_exception on windows if fp is null */
- return fputc(ch, fp);
-}
-
-int fs_file_flush(FILE *fp) {
- /* Invokes file_exception on windows if fp is null */
- return fflush(fp);
-}
-
long int fs_file_tell(FILE *fp) {
/* Invokes file_exception on windows if fp is null */
return ftell(fp);
if (!dir)
return NULL;
- strcpy(dir->dd_name, name);
+ strncpy(dir->dd_name, name, strlen(name));
return dir;
}
if (*dir->dd_name) {
size_t n = strlen(dir->dd_name);
if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) {
- strcpy(dirname, dir->dd_name);
- strcpy(dirname + n, "\\*.*"); /* 4 + 1 */
+ strncpy(dirname, dir->dd_name, n);
+ strncpy(dirname + n, "\\*.*", 4); /* 4 + 1 */
}
} else {
if (!(dirname = util_strdup("\\*.*")))
#else
# if !defined(__MINGW32__)
# include <sys/stat.h> /* mkdir */
-# include <unistd.h> /* chdir */
int fs_dir_make(const char *path) {
return mkdir(path, 0700);
return readdir(dir);
}
-int fs_dir_change(const char *path) {
- return chdir(path);
-}
-
#endif /*! defined(_WIN32) && !defined(__MINGW32__) */
#include "gmqcc.h"
#include "lexer.h"
+#define HT_MACROS 1024
typedef struct {
bool on;
bool was_on;
pptoken **output;
} ppmacro;
-typedef struct {
+typedef struct ftepp_s {
lex_file *lex;
int token;
unsigned int errors;
bool output_on;
ppcondition *conditions;
- ppmacro **macros;
+ /*ppmacro **macros;*/
+ ht macros; /* hashtable<string, ppmacro*> */
char *output_string;
char *ftepp_predef_file(lex_file *context) {
size_t length = strlen(context->name) + 3; /* two quotes and a terminator */
char *value = (char*)mem_a(length);
- sprintf(value, "\"%s\"", context->name);
+ snprintf(value, length, "\"%s\"", context->name);
return value;
}
return token;
}
-static void pptoken_delete(pptoken *self)
+static GMQCC_INLINE void pptoken_delete(pptoken *self)
{
mem_d(self->value);
mem_d(self);
ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
memset(ftepp, 0, sizeof(*ftepp));
+ ftepp->macros = util_htnew(HT_MACROS);
ftepp->output_on = true;
return ftepp;
}
-static void ftepp_flush_do(ftepp_t *self)
+static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self)
{
vec_free(self->output_string);
}
static void ftepp_delete(ftepp_t *self)
{
- size_t i;
ftepp_flush_do(self);
if (self->itemname)
mem_d(self->itemname);
if (self->includename)
vec_free(self->includename);
- for (i = 0; i < vec_size(self->macros); ++i)
- ppmacro_delete(self->macros[i]);
- vec_free(self->macros);
+
+ util_htrem(self->macros, (void (*)(void*))&ppmacro_delete);
+
vec_free(self->conditions);
if (self->lex)
lex_close(self->lex);
}
}
-static void ftepp_update_output_condition(ftepp_t *ftepp)
+static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp)
{
size_t i;
ftepp->output_on = true;
ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on;
}
-static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
+static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name)
{
- size_t i;
- for (i = 0; i < vec_size(ftepp->macros); ++i) {
- if (!strcmp(name, ftepp->macros[i]->name))
- return ftepp->macros[i];
- }
- return NULL;
+ return util_htget(ftepp->macros, name);
}
-static void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
+static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name)
{
- size_t i;
- for (i = 0; i < vec_size(ftepp->macros); ++i) {
- if (!strcmp(name, ftepp->macros[i]->name)) {
- vec_remove(ftepp->macros, i, 1);
- return;
- }
- }
+ util_htrm(ftepp->macros, name, NULL);
}
static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp)
return false;
}
} while (ftepp->token == ',');
+
if (ftepp->token != ')') {
ftepp_error(ftepp, "expected closing paren after macro parameter list");
return false;
return false;
}
- index = atoi(ftepp_tokval(ftepp));
+ index = (int)strtol(ftepp_tokval(ftepp), NULL, 10);
if (ftepp_next(ftepp) != ']') {
ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript");
static bool ftepp_define(ftepp_t *ftepp)
{
- ppmacro *macro;
+ ppmacro *macro = NULL;
size_t l = ftepp_ctx(ftepp).line;
(void)ftepp_next(ftepp);
if (ftepp->token == '(') {
macro->has_params = true;
- if (!ftepp_define_params(ftepp, macro))
+ if (!ftepp_define_params(ftepp, macro)) {
+ ppmacro_delete(macro);
return false;
+ }
}
- if (!ftepp_skipspace(ftepp))
+ if (!ftepp_skipspace(ftepp)) {
+ ppmacro_delete(macro);
return false;
+ }
- if (!ftepp_define_body(ftepp, macro))
+ if (!ftepp_define_body(ftepp, macro)) {
+ ppmacro_delete(macro);
return false;
+ }
+#if 0
if (ftepp->output_on)
vec_push(ftepp->macros, macro);
+#endif
+ if (ftepp->output_on)
+ util_htset(ftepp->macros, macro->name, (void*)macro);
else {
ppmacro_delete(macro);
}
if (resetline && !ftepp->in_macro) {
char lineno[128];
- sprintf(lineno, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
+ snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline));
ftepp_out(ftepp, lineno, false);
}
/* Like in parser.c - files keep the previous state so we have one global
* preprocessor. Except here we will want to warn about dangling #ifs.
*/
-static ftepp_t *ftepp;
-
-static bool ftepp_preprocess_done()
+static bool ftepp_preprocess_done(ftepp_t *ftepp)
{
bool retval = true;
if (vec_size(ftepp->conditions)) {
return retval;
}
-bool ftepp_preprocess_file(const char *filename)
+bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename)
{
ftepp->lex = lex_open(filename);
ftepp->itemname = util_strdup(filename);
}
if (!ftepp_preprocess(ftepp))
return false;
- return ftepp_preprocess_done();
+ return ftepp_preprocess_done(ftepp);
}
-bool ftepp_preprocess_string(const char *name, const char *str)
+bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str)
{
ftepp->lex = lex_open_string(str, strlen(str), name);
ftepp->itemname = util_strdup(name);
}
if (!ftepp_preprocess(ftepp))
return false;
- return ftepp_preprocess_done();
+ return ftepp_preprocess_done(ftepp);
}
-void ftepp_add_macro(const char *name, const char *value) {
+void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) {
char *create = NULL;
/* use saner path for empty macros */
if (!value) {
- ftepp_add_define("__builtin__", name);
+ ftepp_add_define(ftepp, "__builtin__", name);
return;
}
vec_upload(create, value, strlen(value));
vec_push (create, 0);
- ftepp_preprocess_string("__builtin__", create);
+ ftepp_preprocess_string(ftepp, "__builtin__", create);
vec_free (create);
}
-bool ftepp_init()
+ftepp_t *ftepp_create()
{
+ ftepp_t *ftepp;
char minor[32];
char major[32];
ftepp = ftepp_new();
if (!ftepp)
- return false;
+ return NULL;
memset(minor, 0, sizeof(minor));
memset(major, 0, sizeof(major));
/* set the right macro based on the selected standard */
- ftepp_add_define(NULL, "GMQCC");
+ ftepp_add_define(ftepp, NULL, "GMQCC");
if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
- ftepp_add_define(NULL, "__STD_FTEQCC__");
+ ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__");
/* 1.00 */
major[0] = '"';
major[1] = '1';
minor[1] = '0';
minor[2] = '"';
} 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);
+ ftepp_add_define(ftepp, NULL, "__STD_GMQCC__");
+ snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
+ snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
} 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);
+ ftepp_add_define(ftepp, NULL, "__STD_QCCX__");
+ snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
+ snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
- ftepp_add_define(NULL, "__STD_QCC__");
+ ftepp_add_define(ftepp, NULL, "__STD_QCC__");
/* 1.0 */
major[0] = '"';
major[1] = '1';
minor[2] = '"';
}
- ftepp_add_macro("__STD_VERSION_MINOR__", minor);
- ftepp_add_macro("__STD_VERSION_MAJOR__", major);
+ ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
+ ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major);
- return true;
+ return ftepp;
}
-void ftepp_add_define(const char *source, const char *name)
+void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name)
{
ppmacro *macro;
lex_ctx ctx = { "__builtin__", 0 };
ctx.file = source;
macro = ppmacro_new(ctx, name);
- vec_push(ftepp->macros, macro);
+ /*vec_push(ftepp->macros, macro);*/
+ util_htset(ftepp->macros, name, macro);
}
-const char *ftepp_get()
+const char *ftepp_get(ftepp_t *ftepp)
{
return ftepp->output_string;
}
-void ftepp_flush()
+void ftepp_flush(ftepp_t *ftepp)
{
ftepp_flush_do(ftepp);
}
-void ftepp_finish()
+void ftepp_finish(ftepp_t *ftepp)
{
if (!ftepp)
return;
ftepp_delete(ftepp);
- ftepp = NULL;
}
bool util_filexists (const char *);
bool util_strupper (const char *);
bool util_strdigit (const char *);
-char *util_strdup (const char *);
+char *_util_Estrdup (const char *, const char *, size_t);
+char *_util_Estrdup_empty(const char *, const char *, size_t);
void util_debug (const char *, const char *, ...);
void util_endianswap (void *, size_t, unsigned int);
#ifdef NOTRACK
-# define mem_a(x) malloc (x)
-# define mem_d(x) free ((void*)x)
-# define mem_r(x, n) realloc((void*)x, n)
+# define mem_a(x) malloc (x)
+# define mem_d(x) free ((void*)x)
+# define mem_r(x, n) realloc((void*)x, n)
+# define mem_af(x,f,l) malloc (x)
#else
-# define mem_a(x) util_memory_a((x), __LINE__, __FILE__)
-# define mem_d(x) util_memory_d((void*)(x))
-# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__)
+# define mem_a(x) util_memory_a((x), __LINE__, __FILE__)
+# define mem_d(x) util_memory_d((void*)(x))
+# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__)
+# define mem_af(x,f,l) util_memory_a((x), __LINE__, __FILE__)
#endif /*! NOTRACK */
+#define util_strdup(X) _util_Estrdup((X), __FILE__, __LINE__)
+#define util_strdupe(X) _util_Estrdup_empty((X), __FILE__, __LINE__)
+
/*
* A flexible vector implementation: all vector pointers contain some
* data about themselfs exactly - sizeof(vector_t) behind the pointer
* this data is represented in the structure below. Doing this allows
* us to use the array [] to access individual elements from the vector
* opposed to using set/get methods.
- */
+ */
typedef struct {
size_t allocated;
size_t used;
struct hash_node_t **table;
} hash_table_t, *ht;
-typedef struct hash_set_t {
- size_t bits;
- size_t mask;
- size_t capacity;
- size_t *items;
- size_t total;
-} hash_set_t, *hs;
-
/*
* hashtable implementation:
*
* util_htdel(foo);
*/
hash_table_t *util_htnew (size_t size);
+void util_htrem (hash_table_t *ht, void (*callback)(void *data));
void util_htset (hash_table_t *ht, const char *key, void *value);
void util_htdel (hash_table_t *ht);
size_t util_hthash(hash_table_t *ht, const char *key);
void util_htseth(hash_table_t *ht, const char *key, size_t hash, void *value);
+void util_htrmh (hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*));
+void util_htrm (hash_table_t *ht, const char *key, void (*cb)(void*));
void *util_htget (hash_table_t *ht, const char *key);
void *util_htgeth(hash_table_t *ht, const char *key, size_t hash);
-
-/*
- * hashset implementation:
- * This was designed for pointers: you manage the life of the object yourself
- * if you do use this for non-pointers please be warned that the object may not
- * be valid if the duration of it exceeds (i.e on stack). So you need to allocate
- * yourself, or put those in global scope to ensure duration is for the whole
- * runtime.
- *
- * util_hsnew() -- to make a new hashset
- * util_hsadd(set, key) -- to add something in the set
- * util_hshas(set, key) -- to check if something is in the set
- * util_hsrem(set, key) -- to remove something in the set
- * util_hsdel(set) -- to delete the set
- *
- * example of use:
- *
- * hs foo = util_hsnew();
- * char *bar = "hello blub\n";
- * char *baz = "hello dale\n";
- *
- * util_hsadd(foo, bar);
- * util_hsadd(foo, baz);
- * util_hsrem(foo, baz);
- *
- * printf("bar %d | baz %d\n",
- * util_hshas(foo, bar),
- * util_hshad(foo, baz)
- * );
- *
- * util_hsdel(foo);
- */
-
-hash_set_t *util_hsnew(void);
-int util_hsadd(hash_set_t *, void *);
-int util_hshas(hash_set_t *, void *);
-int util_hsrem(hash_set_t *, void *);
-void util_hsdel(hash_set_t *);
/*===================================================================*/
/*============================ file.c ===============================*/
void fs_file_close (FILE *);
int fs_file_error (FILE *);
int fs_file_getc (FILE *);
-int fs_file_flush (FILE *);
int fs_file_printf (FILE *, const char *, ...);
int fs_file_puts (FILE *, const char *);
-int fs_file_putc (FILE *, int);
int fs_file_seek (FILE *, long int, int);
long int fs_file_tell (FILE *);
int fs_file_getline(char **, size_t *, FILE *);
/* directory handling */
+int fs_dir_make (const char *);
DIR *fs_dir_open (const char *);
int fs_dir_close (DIR *);
struct dirent *fs_dir_read (DIR *);
-int fs_dir_make (const char *);
-int fs_dir_change (const char *);
/*===================================================================*/
/*===================================================================*/
/*===================== parser.c commandline ========================*/
/*===================================================================*/
+struct parser_s;
-bool parser_init ();
-bool parser_compile_file (const char *);
-bool parser_compile_string(const char *, const char *, size_t);
-bool parser_finish (const char *);
-void parser_cleanup ();
+struct parser_s *parser_create ();
+bool parser_compile_file (struct parser_s *parser, const char *);
+bool parser_compile_string(struct parser_s *parser, const char *, const char *, size_t);
+bool parser_finish (struct parser_s *parser, const char *);
+void parser_cleanup (struct parser_s *parser);
/*===================================================================*/
/*====================== ftepp.c commandline ========================*/
/*===================================================================*/
struct lex_file_s;
+struct ftepp_s;
+
typedef struct {
const char *name;
char *(*func)(struct lex_file_s *);
*/
#define FTEPP_PREDEF_COUNT 8
-bool ftepp_init ();
-bool ftepp_preprocess_file (const char *filename);
-bool ftepp_preprocess_string(const char *name, const char *str);
-void ftepp_finish ();
-const char *ftepp_get ();
-void ftepp_flush ();
-void ftepp_add_define (const char *source, const char *name);
-void ftepp_add_macro (const char *name, const char *value);
+struct ftepp_s *ftepp_create ();
+bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename);
+bool ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str);
+void ftepp_finish (struct ftepp_s *ftepp);
+const char *ftepp_get (struct ftepp_s *ftepp);
+void ftepp_flush (struct ftepp_s *ftepp);
+void ftepp_add_define (struct ftepp_s *ftepp, const char *source, const char *name);
+void ftepp_add_macro (struct ftepp_s *ftepp, const char *name, const char *value);
extern const ftepp_predef_t ftepp_predefs[FTEPP_PREDEF_COUNT];
--- /dev/null
+/*
+ * Copyright (C) 2012, 2013
+ * Dale Weiler
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of
+ * this software and associated documentation files (the "Software"), to deal in
+ * the Software without restriction, including without limitation the rights to
+ * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+ * of the Software, and to permit persons to whom the Software is furnished to do
+ * so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+/*
+ * Provides all the "intrinsics" / "builtins" for GMQCC. These can do
+ * a few things, they can provide fall back implementations for math
+ * functions if the definitions don't exist for some given engine. Or
+ * then can determine definitions for existing builtins, and simply
+ * wrap back to them instead. This is like a "portable" intrface that
+ * is entered when -fintrin is used (causing all existing builtins to
+ * be ignored by the compiler and instead interface through here.
+ */
+typedef struct {
+ ast_expression *(*intrin)(parser_t *);
+ const char *name;
+ const char *alias;
+} intrin_t;
+
+ht intrin_intrinsics() {
+ static ht intrinsics = NULL;
+ if (!intrinsics)
+ intrinsics = util_htnew(PARSER_HT_SIZE);
+
+ return intrinsics;
+}
+
+#define INTRIN_VAL(VALUE, NAME, FUNC, STYPE, VTYPE) \
+ do { \
+ (VALUE) = ast_value_new ( \
+ parser_ctx(parser), \
+ "__builtin_" NAME, \
+ TYPE_FUNCTION \
+ ); \
+ (VALUE)->expression.next = (ast_expression*)ast_value_new ( \
+ parser_ctx(parser), \
+ STYPE, \
+ VTYPE \
+ ); \
+ (FUNC) = ast_function_new ( \
+ parser_ctx(parser), \
+ "__builtin_" NAME, \
+ (VALUE) \
+ ); \
+ } while (0)
+
+#define INTRIN_REG(FUNC, VALUE) \
+ do { \
+ vec_push(parser->functions, (FUNC)); \
+ vec_push(parser->globals, (ast_expression*)(VALUE)); \
+ } while (0)
+
+
+ast_expression *intrin_func (parser_t *parser, const char *name);
+
+#define QC_M_E 2.71828182845905
+
+ast_expression *intrin_pow(parser_t *parser) {
+ /*
+ * float pow(float x, float y) {
+ * float local = 1.0f;
+ * while (y > 0) {
+ * while (!(y & 1)) {
+ * y >>= 2;
+ * x *= x;
+ * }
+ * y--;
+ * local *= x;
+ * }
+ * return local;
+ * }
+ */
+ static ast_value *value = NULL;
+
+ if (!value) {
+ ast_value *arg1 = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT);
+ ast_value *arg2 = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT);
+ ast_value *local = ast_value_new(parser_ctx(parser), "local", TYPE_FLOAT);
+ ast_block *body = ast_block_new(parser_ctx(parser));
+ ast_block *l1b = ast_block_new(parser_ctx(parser)); /* loop 1 body */
+ ast_block *l2b = ast_block_new(parser_ctx(parser)); /* looo 2 body */
+ ast_loop *loop1 = NULL;
+ ast_loop *loop2 = NULL;
+ ast_function *func = NULL;
+
+ INTRIN_VAL(value, "pow", func, "<float>", TYPE_FLOAT);
+
+ /* arguments */
+ vec_push(value->expression.params, arg1);
+ vec_push(value->expression.params, arg2);
+
+ /* local */
+ vec_push(body->locals, local);
+
+ /* assignment to local of value 1.0f */
+ vec_push(body->exprs,
+ (ast_expression*)ast_store_new (
+ parser_ctx(parser),
+ INSTR_STORE_F,
+ (ast_expression*)local,
+ (ast_expression*)parser_const_float_1(parser)
+ )
+ );
+
+ /* y >>= 2 */
+ vec_push(l2b->exprs,
+ (ast_expression*)ast_binstore_new (
+ parser_ctx(parser),
+ INSTR_STORE_F,
+ INSTR_MUL_F,
+ (ast_expression*)arg2,
+ (ast_expression*)parser_const_float(parser, 0.25f)
+ )
+ );
+
+ /* x *= x */
+ vec_push(l2b->exprs,
+ (ast_expression*)ast_binstore_new (
+ parser_ctx(parser),
+ INSTR_STORE_F,
+ INSTR_MUL_F,
+ (ast_expression*)arg1,
+ (ast_expression*)arg1
+ )
+ );
+
+ /* while (!(y&1)) */
+ loop2 = ast_loop_new (
+ parser_ctx(parser),
+ NULL,
+ (ast_expression*)ast_binary_new (
+ parser_ctx(parser),
+ INSTR_AND,
+ (ast_expression*)arg2,
+ (ast_expression*)parser_const_float_1(parser)
+ ),
+ true, /* ! not */
+ NULL,
+ false,
+ NULL,
+ (ast_expression*)l2b
+ );
+
+ /* push nested loop into loop expressions */
+ vec_push(l1b->exprs, (ast_expression*)loop2);
+
+ /* y-- */
+ vec_push(l1b->exprs,
+ (ast_expression*)ast_binstore_new (
+ parser_ctx(parser),
+ INSTR_STORE_F,
+ INSTR_SUB_F,
+ (ast_expression*)arg2,
+ (ast_expression*)parser_const_float_1(parser)
+ )
+ );
+ /* local *= x */
+ vec_push(l1b->exprs,
+ (ast_expression*)ast_binstore_new (
+ parser_ctx(parser),
+ INSTR_STORE_F,
+ INSTR_MUL_F,
+ (ast_expression*)local,
+ (ast_expression*)arg1
+ )
+ );
+
+ /* while (y > 0) */
+ loop1 = ast_loop_new (
+ parser_ctx(parser),
+ NULL,
+ (ast_expression*)ast_binary_new (
+ parser_ctx(parser),
+ INSTR_GT,
+ (ast_expression*)arg2,
+ (ast_expression*)parser_const_float_0(parser)
+ ),
+ false,
+ NULL,
+ false,
+ NULL,
+ (ast_expression*)l1b
+ );
+
+ /* push the loop1 into the body for the function */
+ vec_push(body->exprs, (ast_expression*)loop1);
+
+ /* return local; */
+ vec_push(body->exprs,
+ (ast_expression*)ast_return_new (
+ parser_ctx(parser),
+ (ast_expression*)local
+ )
+ );
+
+ /* push block and register intrin for codegen */
+ vec_push(func->blocks, body);
+
+ INTRIN_REG(func, value);
+ }
+
+ return (ast_expression*)value;
+}
+
+ast_expression *intrin_mod(parser_t *parser) {
+ /*
+ * float mod(float x, float y) {
+ * return x - y * floor(x / y);
+ * }
+ */
+ static ast_value *value = NULL;
+
+ if (!value) {
+ ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "floor"));
+ ast_value *arg1 = ast_value_new(parser_ctx(parser), "x", TYPE_FLOAT);
+ ast_value *arg2 = ast_value_new(parser_ctx(parser), "y", TYPE_FLOAT);
+ ast_block *body = ast_block_new(parser_ctx(parser));
+ ast_function *func = NULL;
+
+ INTRIN_VAL(value, "mod", func, "<float>", TYPE_FLOAT);
+
+ /* floor(x/y) */
+ vec_push(call->params,
+ (ast_expression*)ast_binary_new (
+ parser_ctx(parser),
+ INSTR_DIV_F,
+ (ast_expression*)arg1,
+ (ast_expression*)arg2
+ )
+ );
+
+ vec_push(body->exprs,
+ (ast_expression*)ast_return_new(
+ parser_ctx(parser),
+ (ast_expression*)ast_binary_new(
+ parser_ctx(parser),
+ INSTR_SUB_F,
+ (ast_expression*)arg1,
+ (ast_expression*)ast_binary_new(
+ parser_ctx(parser),
+ INSTR_MUL_F,
+ (ast_expression*)arg2,
+ (ast_expression*)call
+ )
+ )
+ )
+ );
+
+ vec_push(value->expression.params, arg1); /* float x (for param) */
+ vec_push(value->expression.params, arg2); /* float y (for param) */
+
+ vec_push(func->blocks, body); /* {{{ body }}} */
+
+ INTRIN_REG(func, value);
+ }
+
+ return (ast_expression*)value;
+}
+
+ast_expression *intrin_exp(parser_t *parser) {
+ /*
+ * float exp(float x) {
+ * return pow(QC_M_E, x);
+ * }
+ */
+ static ast_value *value = NULL;
+
+ if (!value) {
+ ast_call *call = ast_call_new (parser_ctx(parser), intrin_func(parser, "pow"));
+ ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT);
+ ast_block *body = ast_block_new (parser_ctx(parser));
+ ast_function *func = NULL;
+
+ INTRIN_VAL(value, "exp", func, "<float>", TYPE_FLOAT);
+
+ /* push arguments for params to call */
+ vec_push(call->params, (ast_expression*)parser_const_float(parser, QC_M_E));
+ vec_push(call->params, (ast_expression*)arg1);
+
+ /* return pow(QC_M_E, x) */
+ vec_push(body->exprs,
+ (ast_expression*)ast_return_new(
+ parser_ctx(parser),
+ (ast_expression*)call
+ )
+ );
+
+ vec_push(value->expression.params, arg1); /* float x (for param) */
+
+ vec_push(func->blocks, body); /* {{{ body }}} */
+
+ INTRIN_REG(func, value);
+ }
+
+ return (ast_expression*)value;
+}
+
+ast_expression *intrin_isnan(parser_t *parser) {
+ /*
+ * float isnan(float x) {
+ * float local;
+ * local = x;
+ *
+ * return (x != local);
+ * }
+ */
+ static ast_value *value = NULL;
+
+ if (!value) {
+ ast_value *arg1 = ast_value_new (parser_ctx(parser), "x", TYPE_FLOAT);
+ ast_value *local = ast_value_new (parser_ctx(parser), "local", TYPE_FLOAT);
+ ast_block *body = ast_block_new (parser_ctx(parser));
+ ast_function *func = NULL;
+
+ INTRIN_VAL(value, "isnan", func, "<float>", TYPE_FLOAT);
+
+ vec_push(body->locals, local);
+ vec_push(body->exprs,
+ (ast_expression*)ast_store_new(
+ parser_ctx(parser),
+ INSTR_STORE_F,
+ (ast_expression*)local,
+ (ast_expression*)arg1
+ )
+ );
+
+ vec_push(body->exprs,
+ (ast_expression*)ast_return_new(
+ parser_ctx(parser),
+ (ast_expression*)ast_binary_new(
+ parser_ctx(parser),
+ INSTR_NE_F,
+ (ast_expression*)arg1,
+ (ast_expression*)local
+ )
+ )
+ );
+
+ vec_push(value->expression.params, arg1);
+
+ vec_push(func->blocks, body);
+
+ INTRIN_REG(func, value);
+ }
+
+ return (ast_expression*)value;
+}
+
+static intrin_t intrinsics[] = {
+ {&intrin_exp, "__builtin_exp", "exp"},
+ {&intrin_mod, "__builtin_mod", "mod"},
+ {&intrin_pow, "__builtin_pow", "pow"},
+ {&intrin_isnan, "__builtin_isnan", "isnan"}
+};
+
+void intrin_intrinsics_destroy(parser_t *parser) {
+ /*size_t i;*/
+ (void)parser;
+ util_htdel(intrin_intrinsics());
+#if 0
+ for (i = 0; i < sizeof(intrinsics)/sizeof(intrin_t); i++)
+ ast_value_delete( (ast_value*) intrinsics[i].intrin(parser));
+#endif
+}
+
+
+ast_expression *intrin_func(parser_t *parser, const char *name) {
+ static bool init = false;
+ size_t i = 0;
+ void *find;
+
+ /* register the intrinsics in the hashtable for O(1) lookup */
+ if (!init) {
+ for (i = 0; i < sizeof(intrinsics)/sizeof(*intrinsics); i++)
+ util_htset(intrin_intrinsics(), intrinsics[i].alias, &intrinsics[i]);
+
+ init = true; /* only once */
+ }
+
+ /*
+ * jesus fucking christ, Blub design something less fucking
+ * impossible to use, like a ast_is_builtin(ast_expression *), also
+ * use a hashtable :P
+ */
+ if ((find = (void*)parser_find_global(parser, name)) && ((ast_value*)find)->expression.vtype == TYPE_FUNCTION)
+ for (i = 0; i < vec_size(parser->functions); ++i)
+ if (((ast_value*)find)->name && !strcmp(parser->functions[i]->name, ((ast_value*)find)->name) && parser->functions[i]->builtin < 0)
+ return find;
+
+ if ((find = util_htget(intrin_intrinsics(), name))) {
+ /* intrinsic is in table. This will "generate the function" so
+ * to speak (if it's not already generated).
+ */
+ return ((intrin_t*)find)->intrin(parser);
+ }
+
+ parseerror(parser, "need function: `%s` compiler depends on it", name);
+ return NULL;
+}
return true;
}
-static char *ir_strdup(const char *str)
-{
- if (str && !*str) {
- /* actually dup empty strings */
- char *out = (char*)mem_a(1);
- *out = 0;
- return out;
- }
- return util_strdup(str);
-}
-
bool ir_value_set_string(ir_value *self, const char *str)
{
if (self->vtype != TYPE_STRING)
return false;
- self->constval.vstring = ir_strdup(str);
+ self->constval.vstring = util_strdupe(str);
self->hasvalue = true;
return true;
}
* is doing something wrong.
*/
irerror(self->context, "Invalid entry block for PHI");
- abort();
+ exit(EXIT_FAILURE);
}
pe.value = v;
def.offset++;
component[len-1]++;
}
+
+ mem_d(component);
}
static void gen_vector_fields(prog_section_field fld, const char *name)
fld.offset++;
component[len-1]++;
}
+
+ mem_d(component);
}
static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool islocal)
static int lex_fgetc(lex_file *lex)
{
if (lex->file)
- return fgetc(lex->file);
+ return fs_file_getc(lex->file);
if (lex->open_string) {
if (lex->open_string_pos >= lex->open_string_length)
return EOF;
lex->line = line;
while (ch != '\n' && ch != EOF)
ch = lex_getch(lex);
+ vec_free(command);
+ vec_free(param);
+ vec_free(pragma);
return true;
unroll:
vec_free(command);
lex_ungetch(lex, ' ');
}
- if (command) {
- vec_pop(command);
- while (vec_size(command)) {
- lex_ungetch(lex, (unsigned char)vec_last(command));
- vec_pop(command);
+ if (param) {
+ vec_pop(param);
+ while (vec_size(param)) {
+ lex_ungetch(lex, (unsigned char)vec_last(param));
+ vec_pop(param);
}
- vec_free(command);
+ vec_free(param);
lex_ungetch(lex, ' ');
}
if (pragma) {
/*
case '+':
case '-':
- */
+ */
case '*':
case '/':
case '<':
lex_tokench(lex, ch);
nextch = lex_getch(lex);
- if (nextch == '=') {
+ if (nextch == '=' || nextch == '*') {
lex_tokench(lex, nextch);
} else
lex_ungetch(lex, nextch);
return (lex->tok.ttype = TOKEN_OPERATOR);
}
+ if (ch == '%') {
+ lex_tokench(lex, ch);
+ lex_endtoken(lex);
+ return (lex->tok.ttype = TOKEN_OPERATOR);
+ }
+
if (isident_start(ch))
{
const char *v;
static const oper_info c_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
- { "++", 1, opid3('S','+','+'), ASSOC_LEFT, 15, OP_SUFFIX},
- { "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX},
- { ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
- { "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
- { "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */
+ { "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX},
+ { "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX},
+ { ".", 2, opid1('.'), ASSOC_LEFT, 17, 0 },
+ { "(", 0, opid1('('), ASSOC_LEFT, 17, 0 }, /* function call */
+ { "[", 2, opid1('['), ASSOC_LEFT, 17, 0 }, /* array subscript */
+
+ { "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX },
+ { "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX },
+
+ { "**", 2, opid2('*', '*'), ASSOC_RIGHT, 15, 0 },
{ "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "~", 1, opid2('~', 'P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
- { "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
- { "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX }, */
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
bool argend = false;
size_t itr;
char buffer[1024];
- char *redirout = NULL;
- char *redirerr = NULL;
- char *config = NULL;
+ char *redirout = NULL;
+ char *redirerr = NULL;
+ char *config = NULL;
+ char *memdumpcols = NULL;
while (!argend && argc > 1) {
char *argarg;
config = argarg;
continue;
}
+ if (options_long_gcc("memdumpcols", &argc, &argv, &memdumpcols)) {
+ OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = (uint16_t)strtol(memdumpcols, NULL, 10);
+ continue;
+ }
/* show defaults (like pathscale) */
if (!strcmp(argv[0]+1, "show-defaults")) {
return false;
}
if (isdigit(argarg[0])) {
- uint32_t val = atoi(argarg);
+ uint32_t val = (uint32_t)strtol(argarg, NULL, 10);
OPTS_OPTION_U32(OPTION_O) = val;
opts_setoptimlevel(val);
} else {
}
int main(int argc, char **argv) {
- size_t itr;
- int retval = 0;
- bool opts_output_free = false;
- bool operators_free = false;
- bool progs_src = false;
- FILE *outfile = NULL;
+ size_t itr;
+ int retval = 0;
+ bool opts_output_free = false;
+ bool operators_free = false;
+ bool progs_src = false;
+ FILE *outfile = NULL;
+ struct parser_s *parser = NULL;
+ struct ftepp_s *ftepp = NULL;
app_name = argv[0];
con_init ();
}
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
- if (!parser_init()) {
+ if (!(parser = parser_create())) {
con_err("failed to initialize parser\n");
retval = 1;
goto cleanup;
}
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
- if (!ftepp_init()) {
+ if (!(ftepp = ftepp_create())) {
con_err("failed to initialize parser\n");
retval = 1;
goto cleanup;
/* add macros */
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);
+ ftepp_add_macro(ftepp, ppems[itr].name, ppems[itr].value);
mem_d(ppems[itr].name);
/* can be null */
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_OPTION_BOOL(OPTION_QUIET) &&
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
const char *out;
- if (!ftepp_preprocess_file(items[itr].filename)) {
+ if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
retval = 1;
goto cleanup;
}
- out = ftepp_get();
+ out = ftepp_get(ftepp);
if (out)
fs_file_printf(outfile, "%s", out);
- ftepp_flush();
+ ftepp_flush(ftepp);
}
else {
if (OPTS_FLAG(FTEPP)) {
const char *data;
- if (!ftepp_preprocess_file(items[itr].filename)) {
+ if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
retval = 1;
goto cleanup;
}
- data = ftepp_get();
+ data = ftepp_get(ftepp);
if (vec_size(data)) {
- if (!parser_compile_string(items[itr].filename, data, vec_size(data))) {
+ if (!parser_compile_string(parser, items[itr].filename, data, vec_size(data))) {
retval = 1;
goto cleanup;
}
}
- ftepp_flush();
+ ftepp_flush(ftepp);
}
else {
- if (!parser_compile_file(items[itr].filename)) {
+ if (!parser_compile_file(parser, items[itr].filename)) {
retval = 1;
goto cleanup;
}
}
}
- ftepp_finish();
+ ftepp_finish(ftepp);
+ ftepp = NULL;
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
- if (!parser_finish(OPTS_OPTION_STR(OPTION_OUTPUT))) {
+ if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) {
retval = 1;
goto cleanup;
}
cleanup:
util_debug("COM", "cleaning ...\n");
- ftepp_finish();
+ if (ftepp)
+ ftepp_finish(ftepp);
con_close();
vec_free(items);
vec_free(ppems);
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
- parser_cleanup();
+ parser_cleanup(parser);
if (opts_output_free)
mem_d(OPTS_OPTION_STR(OPTION_OUTPUT));
if (operators_free)
opts_set(opts.flags, BAIL_ON_WERROR, true);
opts_set(opts.flags, LEGACY_VECTOR_MATHS, true);
opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true);
+
}
void opts_backup_non_Wall() {
OPTS_OPTION_STR(OPTION_OUTPUT) = (char*)output;
OPTS_OPTION_U32(OPTION_STANDARD) = standard;
OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
+ OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = 16;
}
static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) {
static bool opts_ini_bool(const char *value) {
if (!strcmp(value, "true")) return true;
if (!strcmp(value, "false")) return false;
- return !!atoi(value);
+ return !!strtol(value, NULL, 10);
}
static char *opts_ini_load(const char *section, const char *name, const char *value) {
GMQCC_DEFINE_FLAG(G)
GMQCC_DEFINE_FLAG(STANDARD)
GMQCC_DEFINE_FLAG(DEBUG)
+ GMQCC_DEFINE_FLAG(MEMDUMPCOLS)
GMQCC_DEFINE_FLAG(MEMCHK)
GMQCC_DEFINE_FLAG(DUMPFIN)
GMQCC_DEFINE_FLAG(DUMP)
/*
* Extraction abilities. These work as you expect them to.
*/
-bool pak_extract_one(pak_file_t *pak, const char *file) {
- pak_directory_t *dir = NULL;
- unsigned char *dat = NULL;
+bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
+ pak_directory_t *dir = NULL;
+ unsigned char *dat = NULL;
+ char *local = NULL;
FILE *out;
if (!pak_exists(pak, file, &dir)) {
*/
pak_tree_build(file);
+ /* TODO portable path seperators */
+ util_asprintf(&local, "%s/%s", outdir, file);
+
/*
* Now create the file, if this operation fails. Then abort
* It shouldn't fail though.
*/
- if (!(out = fs_file_open(file, "wb"))) {
+ if (!(out = fs_file_open(local, "wb"))) {
mem_d(dat);
return false;
}
+ /* free memory for directory string */
+ mem_d(local);
/* read */
fs_file_seek (pak->handle, dir->pos, SEEK_SET);
if (!fs_dir_make(dir))
return false;
- if (fs_dir_change(dir))
- return false;
-
for (itr = 0; itr < vec_size(pak->directories); itr++) {
- if (!pak_extract_one(pak, pak->directories[itr].name))
+ if (!pak_extract_one(pak, pak->directories[itr].name, dir))
return false;
}
return false;
}
- strcpy(dir.name, file);
+ strncpy(dir.name, file, strlen(file));
/*
* Allocate some memory for loading in the data that will be
bool extract = true;
char *redirout = (char*)stdout;
char *redirerr = (char*)stderr;
- char *directory = NULL;
char *file = NULL;
char **files = NULL;
pak_file_t *pak = NULL;
continue;
if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false))
continue;
- if (parsecmd("directory", &argc, &argv, &directory, 1, false))
- continue;
if (parsecmd("file", &argc, &argv, &file, 1, false))
continue;
return EXIT_FAILURE;
}
- if (!pak_extract_all(pak, (directory) ? directory : "./")) {
+ if (!pak_extract_all(pak, "./")) {
con_err("failed to extract PAK %s (files may be missing)\n", file);
pak_close(pak);
vec_free(files);
return EXIT_FAILURE;
}
- if (directory && !fs_dir_change(directory)) {
- con_err("failed to change directory %s\n", directory);
- pak_close(pak);
- vec_free(files);
- return EXIT_FAILURE;
- }
-
for (iter = 0; iter < vec_size(files); iter++) {
if (!(pak_insert_one(pak, files[iter]))) {
con_err("failed inserting %s for PAK %s\n", files[iter], file);
*/
#include <stdio.h>
#include <stdarg.h>
+#include <math.h>
#include "gmqcc.h"
#include "lexer.h"
#define PARSER_HT_SIZE 128
#define TYPEDEF_HT_SIZE 16
-typedef struct {
+typedef struct parser_s {
lex_file *lex;
int tok;
ast_value **imm_vector;
size_t translated;
+ ht ht_imm_string;
+
/* must be deleted first, they reference immediates and values */
ast_value **accessors;
static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate)
{
- size_t i;
+ size_t hash = util_hthash(parser->ht_imm_string, str);
ast_value *out;
+ if ( (out = util_htgeth(parser->ht_imm_string, str, hash)) ) {
+ if (dotranslate && out->name[0] == '#') {
+ char name[32];
+ snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
+ ast_value_set_name(out, name);
+ }
+ return out;
+ }
+ /*
for (i = 0; i < vec_size(parser->imm_string); ++i) {
if (!strcmp(parser->imm_string[i]->constval.vstring, str))
return parser->imm_string[i];
}
+ */
if (dotranslate) {
char name[32];
snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++));
out->hasvalue = true;
out->constval.vstring = parser_strdup(str);
vec_push(parser->imm_string, out);
+ util_htseth(parser->ht_imm_string, str, hash, out);
return out;
}
return NULL;
}
+/* include intrinsics */
+#include "intrin.h"
+
typedef struct
{
size_t etype; /* 0 = expression, others are operators */
return false;
}
break;
+
case opid1('%'):
+ if (NotSameType(TYPE_FLOAT)) {
+ compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s",
+ type_name[exprs[0]->expression.vtype],
+ type_name[exprs[1]->expression.vtype]);
+ return false;
+ }
+ if (CanConstFold(exprs[0], exprs[1])) {
+ out = (ast_expression*)parser_const_float(parser,
+ (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1))));
+ } else {
+ /* generate a call to __builtin_mod */
+ ast_expression *mod = intrin_func(parser, "mod");
+ ast_call *call = NULL;
+ if (!mod) return false; /* can return null for missing floor */
+
+ call = ast_call_new(parser_ctx(parser), mod);
+ vec_push(call->params, exprs[0]);
+ vec_push(call->params, exprs[1]);
+
+ out = (ast_expression*)call;
+ }
+ break;
+
case opid2('%','='):
- compile_error(ctx, "qc does not have a modulo operator");
+ compile_error(ctx, "%= is unimplemented");
return false;
+
case opid1('|'):
case opid1('&'):
if (NotSameType(TYPE_FLOAT)) {
out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
break;
+ case opid2('*', '*'):
+ if (NotSameType(TYPE_FLOAT)) {
+ ast_type_to_string(exprs[0], ty1, sizeof(ty1));
+ ast_type_to_string(exprs[1], ty2, sizeof(ty2));
+ compile_error(ctx, "invalid types used in exponentiation: %s and %s",
+ ty1, ty2);
+
+ return false;
+ }
+
+ if (CanConstFold(exprs[0], exprs[1])) {
+ out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1)));
+ } else {
+ ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow"));
+ vec_push(gencall->params, exprs[0]);
+ vec_push(gencall->params, exprs[1]);
+ out = (ast_expression*)gencall;
+ }
+ break;
+
case opid3('<','=','>'): /* -1, 0, or 1 */
if (NotSameType(TYPE_FLOAT)) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1));
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]);
+ out = (ast_expression*)
+ ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]);
break;
}
#undef NotSameType
if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
var = (ast_expression*)intrinsic_debug_typestring;
}
+ /* now we try for the real intrinsic hashtable. If the string
+ * begins with __builtin, we simply skip past it, otherwise we
+ * use the identifier as is.
+ */
+ else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) {
+ var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */);
+ } else {
+ var = intrin_func(parser, parser_tokval(parser));
+ }
if (!var) {
char *correct = NULL;
static bool parse_enum(parser_t *parser)
{
+ bool flag = false;
+ bool reverse = false;
qcfloat num = 0;
ast_value **values = NULL;
ast_value *var = NULL;
ast_expression *old;
- if (!parser_next(parser) || parser->tok != '{') {
- parseerror(parser, "expected `{` after `enum` keyword");
+ if (!parser_next(parser) || (parser->tok != '{' && parser->tok != ':')) {
+ parseerror(parser, "expected `{` or `:` after `enum` keyword");
return false;
}
+ /* enumeration attributes (can add more later) */
+ if (parser->tok == ':') {
+ if (!parser_next(parser) || parser->tok != TOKEN_IDENT){
+ parseerror(parser, "expected `flag` or `reverse` for enumeration attribute");
+ return false;
+ }
+
+ /* attributes? */
+ if (!strcmp(parser_tokval(parser), "flag")) {
+ num = 1;
+ flag = true;
+ }
+ else if (!strcmp(parser_tokval(parser), "reverse")) {
+ reverse = true;
+ }
+ else {
+ parseerror(parser, "invalid attribute `%s` for enumeration", parser_tokval(parser));
+ return false;
+ }
+
+ if (!parser_next(parser) || parser->tok != '{') {
+ parseerror(parser, "expected `{` after enum attribute ");
+ return false;
+ }
+ }
+
while (true) {
if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
if (parser->tok == '}') {
vec_push(values, var);
var->cvq = CV_CONST;
var->hasvalue = true;
- var->constval.vfloat = num++;
+ /* for flagged enumerations increment in POTs of TWO */
+ var->constval.vfloat = (flag) ? (num *= 2) : (num ++);
parser_addglobal(parser, var->name, (ast_expression*)var);
if (!parser_next(parser)) {
}
}
+ /* patch them all (for reversed attribute) */
+ if (reverse) {
+ size_t i;
+ for (i = 0; i < vec_size(values); i++)
+ values[i]->constval.vfloat = vec_size(values) - i - 1;
+ }
+
if (parser->tok != '}') {
parseerror(parser, "internal error: breaking without `}`");
goto onerror;
on_error:
if (argcounter)
mem_d(argcounter);
+ if (varparam)
+ ast_delete(varparam);
ast_delete(var);
for (i = 0; i < vec_size(params); ++i)
ast_delete(params[i]);
code_crc = crc;
}
-static parser_t *parser;
-
-bool parser_init()
+parser_t *parser_create()
{
+ parser_t *parser;
lex_ctx empty_ctx;
size_t i;
parser = (parser_t*)mem_a(sizeof(parser_t));
if (!parser)
- return false;
+ return NULL;
memset(parser, 0, sizeof(*parser));
if (!parser->assign_op) {
printf("internal error: initializing parser: failed to find assign operator\n");
mem_d(parser);
- return false;
+ return NULL;
}
vec_push(parser->variables, parser->htfields = util_htnew(PARSER_HT_SIZE));
parser->aliases = util_htnew(PARSER_HT_SIZE);
+ parser->ht_imm_string = util_htnew(512);
+
/* corrector */
vec_push(parser->correct_variables, correct_trie_new());
vec_push(parser->correct_variables_score, NULL);
} else {
parser->reserved_version = NULL;
}
- return true;
+
+ return parser;
}
-bool parser_compile()
+bool parser_compile(parser_t *parser)
{
/* initial lexer/parser state */
parser->lex->flags.noops = true;
return !compile_errors;
}
-bool parser_compile_file(const char *filename)
+bool parser_compile_file(parser_t *parser, const char *filename)
{
parser->lex = lex_open(filename);
if (!parser->lex) {
con_err("failed to open file \"%s\"\n", filename);
return false;
}
- return parser_compile();
+ return parser_compile(parser);
}
-bool parser_compile_string(const char *name, const char *str, size_t len)
+bool parser_compile_string(parser_t *parser, const char *name, const char *str, size_t len)
{
parser->lex = lex_open_string(str, len, name);
if (!parser->lex) {
con_err("failed to create lexer for string \"%s\"\n", name);
return false;
}
- return parser_compile();
+ return parser_compile(parser);
}
-void parser_cleanup()
+void parser_cleanup(parser_t *parser)
{
size_t i;
for (i = 0; i < vec_size(parser->accessors); ++i) {
vec_free(parser->functions);
vec_free(parser->imm_vector);
vec_free(parser->imm_string);
+ util_htdel(parser->ht_imm_string);
vec_free(parser->imm_float);
vec_free(parser->globals);
vec_free(parser->fields);
util_htdel(parser->aliases);
+ intrin_intrinsics_destroy(parser);
+
mem_d(parser);
}
-bool parser_finish(const char *output)
+bool parser_finish(parser_t *parser, const char *output)
{
size_t i;
ir_builder *ir;
--- /dev/null
+Here exists some syntax highlighting configuration files for various
+text editors. Inside each directory exists some documentaiton on how
+you can install the configuration file correctly.
+
+Currently the supported text editors:
+ geany
+ kate
+ kwrite - uses kate syntax highlighting
+ kdevelop - uses kate syntax highlighting
+ QtCreator - supports kate syntax highlighting
+ gtksourceview - main source viewer in GNOME
+ gedit - uses gtksourceview
+ sandy - uses gtksourceview
+ nano
+ jedit
+
+
+Other text editors we plan to provide syntax highlighting configuration
+files for (but never got around to figuring out)
+ vim
+ emacs
+
+If your text editor is not supported and you'd like to create syntax
+highlighting support for it, don't hesitate to share it with us.
--- /dev/null
+To use the geany syntax highlighting install filetypes.qc to the syntax
+directory for geany.
+
+# Can be installed globally to
+/usr/share/geany/
+
+# Can be installed locally to
+~/.config/geany/filedefs/
--- /dev/null
+[styling]
+default=default
+comment=comment
+commentline=comment_line
+commentdoc=comment_doc
+preprocessorcomment=comment
+number=number_1
+word=keyword_1
+word2=keyword_2
+string=string_1
+stringraw=string_2
+character=character
+uuid=other
+preprocessor=preprocessor
+operator=operator
+identifier=identifier_1
+stringeol=string_eol
+verbatim=string_2
+regex=regex
+commentlinedoc=comment_line_doc
+commentdockeyword=comment_doc_keyword
+commentdockeyworderror=comment_doc_keyword_error
+globalclass=class
+tripleverbatim=string_2
+hashquotedstring=string_2
+
+[keywords]
+primary=break case const continue string default do else enum float for goto if return switch typedef void while false nil true
+secondary=
+docComment=
+
+[lexer_properties]
+styling.within.preprocessor=1
+lexer.cpp.track.preprocessor=0
+preprocessor.symbol.$(file.patterns.cpp)=#
+preprocessor.start.$(file.patterns.cpp)=if ifdef ifndef
+preprocessor.middle.$(file.patterns.cpp)=else elif
+preprocessor.end.$(file.patterns.cpp)=endif
+
+[settings]
+extension=qc
+comment_single=//
+comment_open=/*
+comment_close=*/
+comment_use_indent=true
+context_action_cmd=
+
+[indentation]
+width=4
+type=0
+
+[build_settings]
+compiler=gmqcc -Wall "%f" -o "%e"
+linker=
+run_cmd=qcvm "./%e"
--- /dev/null
+To use the gtksourceview syntax highlighting install qc.lang to the syntax
+directory for gtksourceview
+
+# Can be installed globally to
+/usr/share/gtksourceview-[version]/language-specs/
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<language id="qc" _name="QuakeC" version="1.0" _section="Sources">
+ <metadata>
+ <property name="globs">*.qc</property>
+ <property name="line-comment-start">//</property>
+ <property name="block-comment-start">/*</property>
+ <property name="block-comment-end">*/</property>
+ </metadata>
+
+ <styles>
+ <style id="comment" _name="Comment" map-to="def:comment"/>
+ <style id="string" _name="String" map-to="def:string"/>
+ <style id="preprocessor" _name="Preprocessor" map-to="def:preprocessor"/>
+ <style id="common-defines" _name="Common Defines" map-to="def:special-constant"/>
+ <style id="included-file" _name="Included File" map-to="def:string"/>
+ <style id="keyword" _name="Keyword" map-to="def:keyword"/>
+ <style id="type" _name="Data Type" map-to="def:type"/>
+ <style id="escaped-character" _name="Escaped Character" map-to="def:special-char"/>
+ <style id="floating-point" _name="Floating point number" map-to="def:floating-point"/>
+ <style id="decimal" _name="Decimal number" map-to="def:decimal"/>
+ <style id="hexadecimal" _name="Hexadecimal number" map-to="def:base-n-integer"/>
+ <style id="boolean" _name="Boolean value" map-to="def:boolean"/>
+ </styles>
+
+ <definitions>
+
+ <!--regexs-->
+ <define-regex id="preproc-start">^\s*#\s*</define-regex>
+ <define-regex id="escaped-character" extended="true">
+ \\( # leading backslash
+ [\\\"\'nrbtfav\?] | # escaped character
+ [0-7]{1,3} | # one, two, or three octal digits
+ x[0-9A-Fa-f]+ # 'x' followed by hex digits
+ )
+ </define-regex>
+
+ <!-- Preprocessor -->
+ <context id="if0-comment" style-ref="comment">
+ <start>\%{preproc-start}if\b\s*0\b</start>
+ <end>\%{preproc-start}(endif|else|elif)\b</end>
+ <include>
+ <context id="if-in-if0">
+ <start>\%{preproc-start}if(n?def)?\b</start>
+ <end>\%{preproc-start}endif\b</end>
+ <include>
+ <context ref="if-in-if0"/>
+ <context ref="def:in-comment"/>
+ </include>
+ </context>
+ <context ref="def:in-comment"/>
+ </include>
+ </context>
+ <context id="include" style-ref="preprocessor">
+ <match extended="true">
+ \%{preproc-start}
+ (include|import)\s*
+ (".*?"|<.*>)
+ </match>
+ <include>
+ <context id="included-file" sub-pattern="2" style-ref="included-file"/>
+ </include>
+ </context>
+ <context id="preprocessor" style-ref="preprocessor" end-at-line-end="true">
+ <start extended="true">
+ \%{preproc-start}
+ (define|undef|error|pragma|ident|if(n?def)?|else|elif|endif|line|warning)
+ \b
+ </start>
+ <include>
+ <context ref="def:line-continue" ignore-style="true"/>
+ <context ref="string" ignore-style="true"/>
+ <context ref="def:qc-like-comment"/>
+ <context ref="def:qc-like-comment-multiline"/>
+ </include>
+ </context>
+
+ <context id="float" style-ref="floating-point">
+ <match extended="true">
+ (?<![\w\.])
+ ((\.[0-9]+ | [0-9]+\.[0-9]*) ([Ee][+-]?[0-9]*)? |
+ ([0-9]+[Ee][+-]?[0-9]*))
+ [fFlL]?
+ (?![\w\.])
+ </match>
+ </context>
+
+ <context id="hexadecimal" style-ref="hexadecimal">
+ <match extended="true">
+ (?<![\w\.])
+ 0[xX][a-fA-F0-9]+[uUlL]*
+ (?![\w\.])
+ </match>
+ </context>
+
+ <context id="invalid-hexadecimal" style-ref="error">
+ <match extended="true">
+ (?<![\w\.])
+ 0[xX][a-fA-F0-9]*[g-zG-Z][a-zA-Z0-9]*[uUlL]*
+ (?![\w\.])
+ </match>
+ </context>
+
+ <context id="decimal" style-ref="decimal">
+ <match extended="true">
+ (?<![\w\.])
+ (0|[1-9][0-9]*)[uUlL]*
+ (?![\w\.])
+ </match>
+ </context>
+
+ <context id="keywords" style-ref="keyword">
+ <keyword>break</keyword>
+ <keyword>case</keyword>
+ <keyword>continue</keyword>
+ <keyword>default</keyword>
+ <keyword>do</keyword>
+ <keyword>else</keyword>
+ <keyword>enum</keyword>
+ <keyword>for</keyword>
+ <keyword>goto</keyword>
+ <keyword>if</keyword>
+ <keyword>return</keyword>
+ <keyword>switch</keyword>
+ <keyword>typedef</keyword>
+ <keyword>while</keyword>
+ <keyword>nil</keyword>
+ </context>
+
+ <context id="types" style-ref="type">
+ <keyword>bool</keyword>
+ <keyword>string</keyword>
+ <keyword>vector</keyword>
+ <keyword>float</keyword>
+ <keyword>void</keyword>
+ </context>
+
+ <context id="boolean" style-ref="boolean">
+ <keyword>true</keyword>
+ <keyword>false</keyword>
+ </context>
+
+ <context id="common-defines" style-ref="common-defines">
+ <keyword>__LINE__</keyword>
+ <keyword>__FILE__</keyword>
+ <keyword>__TIME__</keyword>
+ <keyword>__RANDOM__</keyword>
+ <keyword>__RANDOM_LAST__</keyword>
+ <keyword>__COUNTER__</keyword>
+ <keyword>__COUNTER_LAST__</keyword>
+ <keyword>__DATE__</keyword>
+ </context>
+
+ <context id="qc" class="no-spell-check">
+ <include>
+ <context ref="def:qc-like-comment"/>
+ <context ref="def:qc-like-comment-multiline"/>
+ <context ref="def:qc-like-close-comment-outside-comment"/>
+ <context ref="if0-comment"/>
+ <context ref="include"/>
+ <context ref="preprocessor"/>
+ <context ref="string"/>
+ <context ref="float"/>
+ <context ref="hexadecimal"/>
+ <context ref="invalid-hexadecimal"/>
+ <context ref="decimal"/>
+ <context ref="keywords"/>
+ <context ref="types"/>
+ <context ref="boolean"/>
+ <context ref="common-defines"/>
+ </include>
+ </context>
+ </definitions>
+</language>
--- /dev/null
+To use the jedit syntax highlighting install qc.xml to the syntax
+directory for jedit
+
+# For Windows Users that directory is
+C:\Users\username\.jedit\modes
+
+# For Linux users that directory is
+/home/username/.jedit/modes
+
+# For Mac users that directory is
+/Users/username/Library/jEdit/modes
+
+After the file is installed, a mode line needs to be added to
+a file caled catalog in that same directory.
+
+Add the following line:
+<MODE NAME="QuakeC Code" FILE="qc.xml" FILE_NAME_GLOB="*.qc" />
+
+inside the <MODES> block before the end tag </MODES>. If the file
+does not exist, you can simply make one and use the following:
+
+<?xml version="1.0"?>
+<!DOCTYPE MODES SYSTEM "catalog.dtd">
+<MODES>
+ <MODE NAME="QuakeC Code" FILE="qc.xml" FILE_NAME_GLOB="*.qc" />
+</MODES>
--- /dev/null
+<?xml version="1.0"?>
+
+<!DOCTYPE MODE SYSTEM "xmode.dtd">
+
+<MODE>
+ <PROPS>
+ <PROPERTY NAME="commentStart" VALUE="/*" />
+ <PROPERTY NAME="commentEnd" VALUE="*/" />
+ <PROPERTY NAME="lineComment" VALUE="//" />
+ <PROPERTY NAME="wordBreakChars" VALUE=",+-=<>/?^&*" />
+
+ <!-- Auto indent -->
+ <PROPERTY NAME="indentOpenBrackets" VALUE="{" />
+ <PROPERTY NAME="indentCloseBrackets" VALUE="}" />
+ <PROPERTY NAME="unalignedOpenBrackets" VALUE="(" />
+ <PROPERTY NAME="unalignedCloseBrackets" VALUE=")" />
+ <PROPERTY NAME="indentNextLine"
+ VALUE="(?!^\s*(#|//)).*(\b(if|while|for)\s*\(.*\)|\b(else|do)\b)[^{;]*$" />
+ <PROPERTY NAME="unindentThisLine"
+ VALUE="^\s*((case\b.*|[\p{Alpha}_][\p{Alnum}_]*)\s*:(?!:)).*$" />
+ <PROPERTY NAME="electricKeys" VALUE=":" />
+ </PROPS>
+
+ <RULES
+ IGNORE_CASE="FALSE"
+ HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+ <EOL_SPAN TYPE="KEYWORD2" AT_WHITESPACE_END="TRUE" DELEGATE="CPP">#</EOL_SPAN>
+
+ <IMPORT DELEGATE="LEX"/>
+ <IMPORT DELEGATE="CORE"/>
+ </RULES>
+
+ <RULES SET="LEX" IGNORE_CASE="FALSE">
+ <IMPORT DELEGATE="COMMENTS" />
+ <IMPORT DELEGATE="C_LEXER" />
+ </RULES>
+
+ <!-- Comments, Trigraph, Alternate-Tokens -->
+ <RULES SET="C_LEXER"
+ IGNORE_CASE="FALSE"
+ HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+
+ <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
+ <BEGIN>L"</BEGIN>
+ <END>"</END>
+ </SPAN>
+ <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
+ <BEGIN>"</BEGIN>
+ <END>"</END>
+ </SPAN>
+ <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
+ <BEGIN>L'</BEGIN>
+ <END>'</END>
+ </SPAN>
+ <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE" ESCAPE="\">
+ <BEGIN>'</BEGIN>
+ <END>'</END>
+ </SPAN>
+
+ <!-- Trigraphs -->
+ <SEQ TYPE="LITERAL4">??(</SEQ>
+ <SEQ TYPE="LITERAL4">??/</SEQ>
+ <SEQ TYPE="LITERAL4">??)</SEQ>
+ <SEQ TYPE="LITERAL4">??'</SEQ>
+ <SEQ TYPE="LITERAL4">??<</SEQ>
+ <SEQ TYPE="LITERAL4">??!</SEQ>
+ <SEQ TYPE="LITERAL4">??></SEQ>
+ <SEQ TYPE="LITERAL4">??-</SEQ>
+ <SEQ TYPE="LITERAL4">??=</SEQ>
+
+ <!-- Alternate tokens -->
+ <SEQ TYPE="LITERAL4"><:</SEQ>
+ <SEQ TYPE="LITERAL4">:></SEQ>
+ <SEQ TYPE="LITERAL4"><%</SEQ>
+ <SEQ TYPE="LITERAL4">%></SEQ>
+ <SEQ TYPE="LITERAL4">%:</SEQ>
+
+ <!-- Labels.
+ This is a part of core language syntax, but must be here
+ because it can't work after SEQ for ':'. -->
+ <MARK_PREVIOUS AT_WHITESPACE_END="TRUE"
+ MATCH_TYPE="OPERATOR"
+ TYPE="LABEL">:</MARK_PREVIOUS>
+
+ <!-- Function-like macro or function calls.
+ This can't work after SEQ for '('. -->
+ <MARK_PREVIOUS
+ TYPE="FUNCTION"
+ MATCH_TYPE="OPERATOR">(</MARK_PREVIOUS>
+
+ <SEQ TYPE="OPERATOR">=</SEQ>
+ <SEQ TYPE="OPERATOR">!</SEQ>
+ <SEQ TYPE="OPERATOR">+</SEQ>
+ <SEQ TYPE="OPERATOR">-</SEQ>
+ <SEQ TYPE="OPERATOR">/</SEQ>
+ <SEQ TYPE="OPERATOR">*</SEQ>
+ <SEQ TYPE="OPERATOR">></SEQ>
+ <SEQ TYPE="OPERATOR"><</SEQ>
+ <SEQ TYPE="OPERATOR">%</SEQ>
+ <SEQ TYPE="OPERATOR">&</SEQ>
+ <SEQ TYPE="OPERATOR">|</SEQ>
+ <SEQ TYPE="OPERATOR">^</SEQ>
+ <SEQ TYPE="OPERATOR">~</SEQ>
+ <SEQ TYPE="OPERATOR">?</SEQ>
+ <SEQ TYPE="OPERATOR">:</SEQ>
+ <SEQ TYPE="OPERATOR">.</SEQ>
+ <SEQ TYPE="OPERATOR">,</SEQ>
+ <SEQ TYPE="OPERATOR">[</SEQ>
+ <SEQ TYPE="OPERATOR">]</SEQ>
+ <SEQ TYPE="OPERATOR">)</SEQ>
+ <SEQ TYPE="OPERATOR">}</SEQ>
+ <SEQ TYPE="OPERATOR">{</SEQ>
+ <SEQ TYPE="OPERATOR">;</SEQ>
+
+ <KEYWORDS>
+ <LITERAL2>__FILE__</LITERAL2>
+ <LITERAL2>__LINE__</LITERAL2>
+ <LITERAL2>__DATE__</LITERAL2>
+ <LITERAL2>__RANDOM__</LITERAL2>
+ <LITERAL2>__RANDOM_LAST</LITERAL2>
+ <LITERAL2>__COUNT__</LITERAL2>
+ <LITERAL2>__COUNT_LAST</LITERAL2>
+ </KEYWORDS>
+ </RULES>
+
+ <!-- Core language -->
+ <RULES SET="CORE"
+ IGNORE_CASE="FALSE"
+ HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+ <KEYWORDS>
+ <!-- Types -->
+ <KEYWORD3>float</KEYWORD3>
+ <KEYWORD3>vector</KEYWORD3>
+ <KEYWORD3>string</KEYWORD3>
+ <KEYWORD3>entity</KEYWORD3>
+ <KEYWORD3>enum</KEYWORD3>
+ <KEYWORD3>.float</KEYWORD3>
+ <KEYWORD3>.int</KEYWORD3>
+ <KEYWORD3>.vector</KEYWORD3>
+ <KEYWORD3>.string</KEYWORD3>
+ <KEYWORD3>.entity</KEYWORD3>
+ <KEYWORD3>.void</KEYWORD3>
+ <KEYWORD3>typedef</KEYWORD3>
+
+ <KEYWORD1>break</KEYWORD1>
+ <KEYWORD1>case</KEYWORD1>
+ <KEYWORD1>continue</KEYWORD1>
+ <KEYWORD1>default</KEYWORD1>
+ <KEYWORD1>do</KEYWORD1>
+ <KEYWORD1>else</KEYWORD1>
+ <KEYWORD1>for</KEYWORD1>
+ <KEYWORD1>goto</KEYWORD1>
+ <KEYWORD1>if</KEYWORD1>
+ <KEYWORD1>return</KEYWORD1>
+ <KEYWORD1>switch</KEYWORD1>
+ <KEYWORD1>void</KEYWORD1>
+ <KEYWORD1>while</KEYWORD1>
+ <KEYWORD1>nil</KEYWORD1>
+
+ <LITERAL2>FALSE</LITERAL2>
+ <LITERAL2>TRUE</LITERAL2>
+ <LITERAL2>...</LITERAL2>
+ </KEYWORDS>
+ </RULES>
+
+ <!-- Different comment styles. -->
+ <RULES SET="COMMENTS">
+ <!-- Doxygen comment, Javadoc style -->
+ <SEQ TYPE="COMMENT1">/**/</SEQ>
+ <SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
+ <BEGIN>/**<</BEGIN>
+ <END>*/</END>
+ </SPAN>
+ <SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
+ <BEGIN>/**</BEGIN>
+ <END>*/</END>
+ </SPAN>
+ <EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">///<</EOL_SPAN>
+ <EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">///</EOL_SPAN>
+
+ <!-- Doxygen comment, Qt style -->
+ <SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
+ <BEGIN>/*!<</BEGIN>
+ <END>*/</END>
+ </SPAN>
+ <SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
+ <BEGIN>/*!</BEGIN>
+ <END>*/</END>
+ </SPAN>
+ <EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">//!<</EOL_SPAN>
+ <EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">//!</EOL_SPAN>
+
+ <!-- C style comment -->
+ <SPAN TYPE="COMMENT1">
+ <BEGIN>/*</BEGIN>
+ <END>*/</END>
+ </SPAN>
+ <EOL_SPAN TYPE="COMMENT1">//</EOL_SPAN>
+ </RULES>
+
+ <!-- Preprocessor specific rules -->
+ <RULES SET="CPP"
+ IGNORE_CASE="FALSE"
+ HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+
+ <EOL_SPAN_REGEXP HASH_CHAR="include" TYPE="MARKUP" DELEGATE="INCLUDE">include\b</EOL_SPAN_REGEXP>
+ <EOL_SPAN_REGEXP HASH_CHAR="define" TYPE="MARKUP" DELEGATE="DEFINE">define\b</EOL_SPAN_REGEXP>
+ <EOL_SPAN_REGEXP HASH_CHAR="endif" TYPE="MARKUP" DELEGATE="LEX">endif\b</EOL_SPAN_REGEXP>
+ <EOL_SPAN_REGEXP HASH_CHAR="elif" TYPE="MARKUP" DELEGATE="CONDITION">elif\b</EOL_SPAN_REGEXP>
+ <EOL_SPAN_REGEXP HASH_CHAR="if" TYPE="MARKUP" DELEGATE="CONDITION">if\b</EOL_SPAN_REGEXP>
+
+ <IMPORT DELEGATE="LEX"/>
+
+ <!-- Directives -->
+ <KEYWORDS>
+ <MARKUP>undef</MARKUP>
+ <MARKUP>ifdef</MARKUP>
+ <MARKUP>ifndef</MARKUP>
+ <MARKUP>else</MARKUP>
+ <MARKUP>error</MARKUP>
+ <MARKUP>warning</MARKUP>
+ <MARKUP>pragma</MARKUP>
+ <MARKUP>$frame</MARKUP>
+ <MARKUP>$model</MARKUP>
+ </KEYWORDS>
+ </RULES>
+
+ <!-- After #include directive -->
+ <!-- "\"s are not escaped. -->
+ <RULES SET="INCLUDE"
+ IGNORE_CASE="FALSE"
+ HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+ <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE">
+ <BEGIN><</BEGIN>
+ <END>></END>
+ </SPAN>
+ <SPAN TYPE="LITERAL1" NO_LINE_BREAK="TRUE">
+ <BEGIN>"</BEGIN>
+ <END>"</END>
+ </SPAN>
+ <IMPORT DELEGATE="LEX"/>
+ </RULES>
+
+ <!-- After #define directive -->
+ <!-- Almost same as the normal code,
+ except two additional operators # and ##. -->
+ <RULES SET="DEFINE"
+ IGNORE_CASE="FALSE"
+ HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+ <SEQ TYPE="OPERATOR">#</SEQ>
+ <IMPORT DELEGATE="LEX"/>
+ <IMPORT DELEGATE="CORE"/>
+ </RULES>
+
+ <!-- After #if or #elif directive -->
+ <!-- All constant expressions and a special operator
+ 'defined' is available. But the core language elements
+ (such as operator 'sizeof', type casting, etc...) are not. -->
+ <RULES SET="CONDITION"
+ IGNORE_CASE="FALSE"
+ HIGHLIGHT_DIGITS="TRUE" DIGIT_RE="[0-9][0-9a-zA-Z]*">
+ <IMPORT DELEGATE="LEX"/>
+ <KEYWORDS>
+ <KEYWORD2>defined</KEYWORD2>
+ <KEYWORD2>TRUE</KEYWORD2>
+ <KEYWORD2>FALSE</KEYWORD2>
+ <KEYWORD2>true</KEYWORD2>
+ <KEYWORD2>false</KEYWORD2>
+ </KEYWORDS>
+ </RULES>
+</MODE>
--- /dev/null
+To use the Kate syntax highlighting install qc.xml to the syntax
+directory for kate.
+
+# Can be installed globally to
+$KDEDIR/share/apps/katepart/syntax
+
+if $KDEDIR is unset you can lookup the folder directory with
+kde4-config --prefix if that doesn't work chances are KDEDIR is
+/usr
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE language SYSTEM "language.dtd">
+<language name="QuakeC" section="Sources"
+ version="1.00" kateversion="2.4"
+ indenter="cstyle"
+ extensions="*.qc;*.QC;*.qh"
+ mimetype=""
+ priority="5"
+ author="Dale Weiler">
+ <highlighting>
+ <list name="keywords">
+ <item> break </item>
+ <item> case </item>
+ <item> continue </item>
+ <item> default </item>
+ <item> do </item>
+ <item> else </item>
+ <item> enum </item>
+ <item> for </item>
+ <item> goto </item>
+ <item> if </item>
+ <item> return </item>
+ <item> switch </item>
+ <item> typedef </item>
+ <item> while </item>
+ <item> nil </item>
+ </list>
+ <list name="types">
+ <item> const </item>
+ <item> vector </item>
+ <item> float </item>
+ <item> void </item>
+ <item> string </item>
+ </list>
+ <contexts>
+ <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
+ <DetectSpaces />
+ <RegExpr attribute="Preprocessor" context="Outscoped" String="#\s*if\s+0\s*$" beginRegion="PP" firstNonSpace="true" />
+ <DetectChar context="AfterHash" char="#" firstNonSpace="true" lookAhead="true" />
+ <StringDetect attribute="Region Marker" context="Region Marker" String="//BEGIN" beginRegion="Region1" firstNonSpace="true" />
+ <StringDetect attribute="Region Marker" context="Region Marker" String="//END" endRegion="Region1" firstNonSpace="true" />
+ <keyword attribute="Keyword" context="#stay" String="keywords"/>
+ <keyword attribute="Data Type" context="#stay" String="types"/>
+ <DetectIdentifier />
+ <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" />
+ <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" />
+ <Float attribute="Float" context="#stay">
+ <AnyChar String="fF" attribute="Float" context="#stay"/>
+ </Float>
+ <HlCHex attribute="Hex" context="#stay"/>
+ <Int attribute="Decimal" context="#stay" >
+ <StringDetect attribute="Decimal" context="#stay" String="ULL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LUL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LLU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="UL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LU" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="LL" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="U" insensitive="TRUE"/>
+ <StringDetect attribute="Decimal" context="#stay" String="L" insensitive="TRUE"/>
+ </Int>
+ <HlCChar attribute="Char" context="#stay"/>
+ <DetectChar attribute="String" context="String" char="""/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+ <AnyChar attribute="Symbol" context="#stay" String=":!%&()+,-/.*<=>?[]|~^;"/>
+ </context>
+
+ <context attribute="String" lineEndContext="#pop" name="String">
+ <LineContinue attribute="String" context="#stay"/>
+ <HlCStringChar attribute="String Char" context="#stay"/>
+ <DetectChar attribute="String" context="#pop" char="""/>
+ </context>
+
+ <context attribute="Region Marker" lineEndContext="#pop" name="Region Marker">
+ </context>
+
+ <context attribute="Comment" lineEndContext="#pop" name="Commentar 1">
+ <LineContinue attribute="Comment" context="#stay"/>
+ <IncludeRules context="##Alerts" />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar 2">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
+ <IncludeRules context="##Alerts" />
+ </context>
+
+ <context attribute="Error" lineEndContext="#pop" name="AfterHash">
+ <!-- define, elif, else, endif, error, if, ifdef, ifndef, include, include_next, line, pragma, undef, warning -->
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*if(?:def|ndef)?(?=\s+\S)" insensitive="true" beginRegion="PP" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*endif" insensitive="true" endRegion="PP" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="Define" String="#\s*define.*((?=\\))" insensitive="true" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s*(?:el(?:se|if)|include(?:_next)?|define|undef|line|error|warning|pragma)" insensitive="true" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="Preprocessor" String="#\s+[0-9]+" insensitive="true" firstNonSpace="true" />
+ </context>
+
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Preprocessor">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char=""" char1="""/>
+ <RangeDetect attribute="Prep. Lib" context="#stay" char="<" char1=">"/>
+ <IncludeRules context="##Doxygen" />
+ <Detect2Chars attribute="Comment" context="Commentar/Preprocessor" char="/" char1="*" beginRegion="Comment2" />
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/" />
+ </context>
+
+ <context attribute="Preprocessor" lineEndContext="#pop" name="Define">
+ <LineContinue attribute="Preprocessor" context="#stay"/>
+ </context>
+
+ <context attribute="Comment" lineEndContext="#stay" name="Commentar/Preprocessor">
+ <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment2" />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#stay" name="Outscoped" >
+ <DetectSpaces />
+ <DetectIdentifier />
+ <DetectChar attribute="String" context="String" char="""/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+ <RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="PP" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="#pop" String="#\s*el(?:se|if)" firstNonSpace="true" />
+ <RegExpr attribute="Preprocessor" context="#pop" String="#\s*endif" endRegion="PP" firstNonSpace="true" />
+ </context>
+
+ <context attribute="Comment" lineEndContext="#stay" name="Outscoped intern">
+ <DetectSpaces />
+ <DetectIdentifier />
+ <DetectChar attribute="String" context="String" char="""/>
+ <Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
+ <Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
+ <RegExpr attribute="Comment" context="Outscoped intern" String="#\s*if" beginRegion="PP" firstNonSpace="true" />
+ <RegExpr attribute="Comment" context="#pop" String="#\s*endif" endRegion="PP" firstNonSpace="true" />
+ </context>
+ </contexts>
+ <itemDatas>
+ <itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
+ <itemData name="Data Type" defStyleNum="dsDataType" spellChecking="false"/>
+ <itemData name="Decimal" defStyleNum="dsDecVal" spellChecking="false"/>
+ <itemData name="Hex" defStyleNum="dsBaseN" spellChecking="false"/>
+ <itemData name="Float" defStyleNum="dsFloat" spellChecking="false"/>
+ <itemData name="String" defStyleNum="dsString"/>
+ <itemData name="String Char" defStyleNum="dsChar"/>
+ <itemData name="Comment" defStyleNum="dsComment"/>
+ <itemData name="Symbol" defStyleNum="dsNormal" spellChecking="false"/>
+ <itemData name="Preprocessor" defStyleNum="dsOthers" spellChecking="false"/>
+ </itemDatas>
+ </highlighting>
+ <general>
+ <comments>
+ <comment name="singleLine" start="//" />
+ <comment name="multiLine" start="/*" end="*/" />
+ </comments>
+ <keywords casesensitive="1" additionalDeliminator="'"" />
+ </general>
+</language>
--- /dev/null
+To use the nano syntax highlighting install qc.nanorc somewhere and
+add:
+
+include /directory/qc.nanorc
+
+to your nanorc file located at ~/.nanorc. If the file doesn't exist
+create it.
+
+Optionally you can install it globally by installing qc.nanorc to
+/usr/share/nano
+
+However you still need to provide the include to your ~/.nanorc
--- /dev/null
+# Language: QuakeC
+# Maintainer: Dale Weiler
+
+syntax "qc" "\.(qc|QC)$" "\.(qh|QH)$"
+color brightred "\<[A-Z_][0-9A-Z_]+\>"
+color green "\<(float|string|enum|void|const|typedef|nil)\>"
+color brightyellow "\<(for|if|while|do|else|case|default|switch)\>"
+color magenta "\<(goto|continue|break|return)\>"
+color brightcyan "^[[:space:]]*#[[:space:]]*(define|include|(un|ifn?)def|endif|el(if|se)|if|warning|error|pragma)"
+color brightmagenta "'([^'\]|(\\["'abfnrtv\\]))'" "'\\(([0-3]?[0-7]{1,2}))'" "'\\x[0-9A-Fa-f]{1,2}'"
+
+color brightyellow "<[^= ]*>" ""(\\.|[^"])*""
+
+## This string is VERY resource intensive!
+color brightyellow start=""(\\.|[^"])*\\[[:space:]]*$" end="^(\\.|[^"])*""
+
+## Comment highlighting
+color brightblue "//.*"
+color brightblue start="/\*" end="\*/"
+
+## Trailing whitespace
+color ,green "[[:space:]]+$"
}
#endif /*! _WIN32 */
-#define TASK_COMPILE 0
-#define TASK_EXECUTE 1
+#define TASK_COMPILE 0
+#define TASK_EXECUTE 1
/*
* Task template system:
* templates are rules for a specific test, used to create a "task" that
*/
bool task_template_generate(task_template_t *tmpl, char tag, const char *file, size_t line, char *value, size_t *pad) {
size_t desclen = 0;
+ size_t filelen = 0;
char **destval = NULL;
if (!tmpl)
if (strchr(value, '\n'))
*strrchr(value, '\n')='\0';
else /* cppcheck: possible nullpointer dereference */
- abort();
+ exit(EXIT_FAILURE);
/*
* Now allocate and set the actual value for the specific tag. Which
pad[0] = desclen;
}
+ if ((filelen = strlen(file)) > pad[2])
+ pad[2] = filelen;
+
return true;
}
if (strrchr(value, '\n'))
*strrchr(value, '\n')='\0';
else /* cppcheck: possible null pointer dereference */
- abort();
+ exit(EXIT_FAILURE);
vec_push(tmpl->comparematch, util_strdup(value));
con_err("template compile warning: %s erroneous tag `E:` when only failing\n", file);
if (tmpl->comparematch)
con_err("template compile warning: %s erroneous tag `M:` when only failing\n", file);
- goto success;
+ } else if (!strcmp(tmpl->proceduretype, "-pp")) {
+ if (tmpl->executeflags)
+ con_err("template compile warning: %s erroneous tag `E:` when only preprocessing\n", file);
+ if (!tmpl->comparematch) {
+ con_err("template compile error: %s missing `M:` tag (use `$null` for exclude)\n", file);
+ goto failure;
+ }
} else {
con_err("template compile error: %s invalid procedure type: %s\n", file, tmpl->proceduretype);
goto failure;
char buffer[4096];
size_t found = 0;
- dir = opendir(curdir);
+ dir = fs_dir_open(curdir);
- while ((files = readdir(dir))) {
- snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name);
+ while ((files = fs_dir_read(dir))) {
+ snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name);
if (stat(buffer, &directory) == -1) {
con_err("internal error: stat failed, aborting\n");
* Generate a temportary file name for the output binary
* so we don't trample over an existing one.
*/
- tmpl->tempfilename = tempnam(curdir, "TMPDAT");
+ tmpl->tempfilename = NULL;
+ util_asprintf(&tmpl->tempfilename, "%s/TMPDAT.%s", curdir, files->d_name);
/*
* Additional QCFLAGS enviroment variable may be used
* which will be refered to with a handle in the task for
* reading the data from the pipe.
*/
- if (qcflags) {
- if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
- snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
- task_bins[TASK_COMPILE],
- curdir,
- tmpl->sourcefile,
- qcflags,
- tmpl->compileflags,
- tmpl->tempfilename
- );
+ if (strcmp(tmpl->proceduretype, "-pp")) {
+ if (qcflags) {
+ if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
+ snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
+ task_bins[TASK_COMPILE],
+ curdir,
+ tmpl->sourcefile,
+ qcflags,
+ tmpl->compileflags,
+ tmpl->tempfilename
+ );
+ } else {
+ snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s",
+ task_bins[TASK_COMPILE],
+ curdir,
+ defs,
+ curdir,
+ tmpl->sourcefile,
+ qcflags,
+ tmpl->compileflags,
+ tmpl->tempfilename
+ );
+ }
} else {
- snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s",
- task_bins[TASK_COMPILE],
- curdir,
- defs,
- curdir,
- tmpl->sourcefile,
- qcflags,
- tmpl->compileflags,
- tmpl->tempfilename
- );
+ if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
+ snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
+ task_bins[TASK_COMPILE],
+ curdir,
+ tmpl->sourcefile,
+ tmpl->compileflags,
+ tmpl->tempfilename
+ );
+ } else {
+ snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s",
+ task_bins[TASK_COMPILE],
+ curdir,
+ defs,
+ curdir,
+ tmpl->sourcefile,
+ tmpl->compileflags,
+ tmpl->tempfilename
+ );
+ }
}
} else {
+ /* Preprocessing (qcflags mean shit all here we don't allow them) */
if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
- snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
+ snprintf(buf, sizeof(buf), "%s -E %s/%s -o %s",
task_bins[TASK_COMPILE],
curdir,
tmpl->sourcefile,
- tmpl->compileflags,
tmpl->tempfilename
);
} else {
- snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s -o %s",
+ snprintf(buf, sizeof(buf), "%s -E %s/%s %s/%s -o %s",
task_bins[TASK_COMPILE],
curdir,
defs,
curdir,
tmpl->sourcefile,
- tmpl->compileflags,
tmpl->tempfilename
);
}
found
);
- closedir(dir);
+ fs_dir_close(dir);
return success;
}
struct dirent *files;
char buffer[4096];
- dir = opendir(curdir);
+ dir = fs_dir_open(curdir);
- while ((files = readdir(dir))) {
+ while ((files = fs_dir_read(dir))) {
if (strstr(files->d_name, "TMP") ||
strstr(files->d_name, ".stdout") ||
strstr(files->d_name, ".stderr"))
}
}
- closedir(dir);
+ fs_dir_close(dir);
}
void task_destroy(void) {
/*
* This executes the QCVM task for a specificly compiled progs.dat
* using the template passed into it for call-flags and user defined
- * messages.
+ * messages IF the procedure type is -execute, otherwise it matches
+ * the preprocessor output.
*/
-bool task_execute(task_template_t *tmpl, char ***line) {
+bool task_trymatch(task_template_t *tmpl, char ***line) {
bool success = true;
FILE *execute;
char buffer[4096];
memset (buffer,0,sizeof(buffer));
- /*
- * Drop the execution flags for the QCVM if none where
- * actually specified.
- */
- if (!strcmp(tmpl->executeflags, "$null")) {
- snprintf(buffer, sizeof(buffer), "%s %s",
- task_bins[TASK_EXECUTE],
- tmpl->tempfilename
+ if (strcmp(tmpl->proceduretype, "-pp")) {
+ /*
+ * Drop the execution flags for the QCVM if none where
+ * actually specified.
+ */
+ if (!strcmp(tmpl->executeflags, "$null")) {
+ snprintf(buffer, sizeof(buffer), "%s %s",
+ task_bins[TASK_EXECUTE],
+ tmpl->tempfilename
+ );
+ } else {
+ snprintf(buffer, sizeof(buffer), "%s %s %s",
+ task_bins[TASK_EXECUTE],
+ tmpl->executeflags,
+ tmpl->tempfilename
+ );
+ }
+
+ util_debug("TEST", "executing qcvm: `%s` [%s]\n",
+ tmpl->description,
+ buffer
);
+
+ execute = popen(buffer, "r");
+ if (!execute)
+ return false;
} else {
- snprintf(buffer, sizeof(buffer), "%s %s %s",
- task_bins[TASK_EXECUTE],
- tmpl->executeflags,
- tmpl->tempfilename
- );
+ /*
+ * we're preprocessing, which means we need to read int
+ * the produced file and do some really weird shit.
+ */
+ if (!(execute = fs_file_open(tmpl->tempfilename, "r")))
+ return false;
}
- util_debug("TEST", "executing qcvm: `%s` [%s]\n",
- tmpl->description,
- buffer
- );
-
- execute = popen(buffer, "r");
- if (!execute)
- return false;
-
/*
* Now lets read the lines and compare them to the matches we expect
* and handle accordingly.
if (strrchr(data, '\n'))
*strrchr(data, '\n') = '\0';
+ /*
+ * If data is just null now, that means the line was an empty
+ * one and for that, we just ignore it.
+ */
+ if (!*data)
+ continue;
+
if (vec_size(tmpl->comparematch) > compare) {
if (strcmp(data, tmpl->comparematch[compare++]))
success = false;
mem_d(data);
data = NULL;
}
- pclose(execute);
+
+ if (strcmp(tmpl->proceduretype, "-pp"))
+ pclose(execute);
+ else
+ fs_file_close(execute);
+
return success;
}
+const char *task_type(task_template_t *tmpl) {
+ if (!strcmp(tmpl->proceduretype, "-pp"))
+ return "type: preprocessor";
+ if (!strcmp(tmpl->proceduretype, "-execute"))
+ return "type: execution";
+ if (!strcmp(tmpl->proceduretype, "-compile"))
+ return "type: compile";
+ return "type: fail";
+}
+
/*
* This schedualizes all tasks and actually runs them individually
* this is generally easy for just -compile variants. For compile and
*/
#include <math.h>
void task_schedualize(size_t *pad) {
+ char space[2][64];
bool execute = false;
char *data = NULL;
char **match = NULL;
size_t i = 0;
size_t j = 0;
+ snprintf(space[0], sizeof(space[0]), "%d", (int)vec_size(task_tasks));
+
for (; i < vec_size(task_tasks); i++) {
- con_out("test #%u %*s", i + 1, (int)log10(vec_size(task_tasks)) - (int)(log10(i + 1)), "");
+ memset(space[1], 0, sizeof(space[1]));
+ snprintf(space[1], sizeof(space[1]), "%d", (int)(i + 1));
+
+ con_out("test #%u %*s", i + 1, strlen(space[0]) - strlen(space[1]), "");
util_debug("TEST", "executing task: %d: %s\n", i, task_tasks[i].tmpl->description);
/*
* Generate a task from thin air if it requires execution in
* the QCVM.
*/
- execute = !!(!strcmp(task_tasks[i].tmpl->proceduretype, "-execute"));
+ execute = !! (!strcmp(task_tasks[i].tmpl->proceduretype, "-execute")) ||
+ (!strcmp(task_tasks[i].tmpl->proceduretype, "-pp"));
/*
* We assume it compiled before we actually compiled :). On error
task_tasks[i].compiled = false;
execute = false;
}
-
- fs_file_flush(task_tasks[i].stdoutlog);
}
while (fs_file_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) {
/*
}
fs_file_puts (task_tasks[i].stderrlog, data);
- fs_file_flush(task_tasks[i].stdoutlog);
}
if (!task_tasks[i].compiled && strcmp(task_tasks[i].tmpl->proceduretype, "-fail")) {
- con_err("failure: `%s` (failed to compile) see %s.stdout and %s.stderr [%s]\n",
+ con_out("failure: `%s` %*s %*s\n",
task_tasks[i].tmpl->description,
- task_tasks[i].tmpl->tempfilename,
- task_tasks[i].tmpl->tempfilename,
- task_tasks[i].tmpl->rulesfile
+ (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+ task_tasks[i].tmpl->rulesfile,
+ (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen("(failed to compile)") - pad[2]),
+ "(failed to compile)"
);
continue;
}
if (!execute) {
- con_out("succeeded: `%s` %*s\n",
+ con_out("succeeded: `%s` %*s %*s\n",
task_tasks[i].tmpl->description,
- (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) +
- (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
- task_tasks[i].tmpl->rulesfile
+ (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+ task_tasks[i].tmpl->rulesfile,
+ (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen(task_type(task_tasks[i].tmpl)) - pad[2]),
+ task_type(task_tasks[i].tmpl)
);
continue;
/*
* If we made it here that concludes the task is to be executed
- * in the virtual machine.
+ * in the virtual machine (or the preprocessor output needs to
+ * be matched).
*/
- if (!task_execute(task_tasks[i].tmpl, &match)) {
+ if (!task_trymatch(task_tasks[i].tmpl, &match)) {
size_t d = 0;
- con_err("failure: `%s` (invalid results from execution) [%s]\n",
+ con_out("failure: `%s` %*s %*s\n",
task_tasks[i].tmpl->description,
- task_tasks[i].tmpl->rulesfile
+ (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+ task_tasks[i].tmpl->rulesfile,
+ (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen(
+ (strcmp(task_tasks[i].tmpl->proceduretype, "-pp"))
+ ? "(invalid results from execution)"
+ : "(invalid results from preprocessing)"
+ ) - pad[2]),
+ (strcmp(task_tasks[i].tmpl->proceduretype, "-pp"))
+ ? "(invalid results from execution)"
+ : "(invalid results from preprocessing)"
);
/*
* handler for the all the given matches in the template file and
* what was actually returned from executing.
*/
- con_err(" Expected From %u Matches: (got %u Matches)\n",
+ con_out(" Expected From %u Matches: (got %u Matches)\n",
vec_size(task_tasks[i].tmpl->comparematch),
vec_size(match)
);
char *select = task_tasks[i].tmpl->comparematch[d];
size_t length = 40 - strlen(select);
- con_err(" Expected: \"%s\"", select);
+ con_out(" Expected: \"%s\"", select);
while (length --)
- con_err(" ");
- con_err("| Got: \"%s\"\n", (d >= vec_size(match)) ? "<<nothing else to compare>>" : match[d]);
+ con_out(" ");
+ con_out("| Got: \"%s\"\n", (d >= vec_size(match)) ? "<<nothing else to compare>>" : match[d]);
}
/*
*/
if (vec_size(match) > vec_size(task_tasks[i].tmpl->comparematch)) {
for (d = 0; d < vec_size(match) - vec_size(task_tasks[i].tmpl->comparematch); d++) {
- con_err(" Expected: Nothing | Got: \"%s\"\n",
+ con_out(" Expected: Nothing | Got: \"%s\"\n",
match[d + vec_size(task_tasks[i].tmpl->comparematch)]
);
}
mem_d(match[j]);
vec_free(match);
- con_out("succeeded: `%s` %*s\n",
+ con_out("succeeded: `%s` %*s %*s\n",
task_tasks[i].tmpl->description,
- (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) +
- (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
- task_tasks[i].tmpl->rulesfile
+ (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
+ task_tasks[i].tmpl->rulesfile,
+ (pad[1] + pad[2] - strlen(task_tasks[i].tmpl->rulesfile)) + (strlen(task_type(task_tasks[i].tmpl))- pad[2]),
+ task_type(task_tasks[i].tmpl)
);
}
static const char *default_defs = "defs.qh";
size_t pad[] = {
- 0, 0
+ /* test ### [succeed/fail]: `description` [tests/template.tmpl] [type] */
+ 0, 0, 0
};
/*
float (string, string) strcmp = #11;
vector (vector) normalize = #12;
float (float) sqrt = #13;
+float (float) floor = #14;
-void(string, ...) print = #1;enum {
+enum {
// this behaviour is confusing, but I like that
// we support it.
__ = (__ - 1),
N
};
+enum : flag {
+ F1, /* = 1 << 1 */
+ F2, /* = 1 << 2 */
+ F3 /* = 1 << 3 */
+};
+
+/* reversed enumeration */
+enum : reverse {
+ R1, // 3
+ R2, // 2
+ R3, // 1
+ R4 // 0
+};
+
void main() {
print(ftos(A), "\n");
print(ftos(B), "\n");
print(ftos(L), "\n");
print(ftos(M), "\n");
print(ftos(N), "\n");
+
+ print(ftos(F1), "\n");
+ print(ftos(F2), "\n");
+ print(ftos(F3), "\n");
+
+ print(ftos(R1), "\n");
+ print(ftos(R2), "\n");
+ print(ftos(R3), "\n");
+ print(ftos(R4), "\n");
};
M: 11
M: 12
M: 13
+M: 2
+M: 4
+M: 8
+M: 3
+M: 2
+M: 1
+M: 0
--- /dev/null
+float pow(float x, float y) {
+ return __builtin_pow(x, y);
+}
+
+void main() {
+ float hundy = pow(10, 2); // 10^2 == 100
+ print(ftos(hundy), "\n"); // prints: 100
+
+ hundy -= 90; // 100-90 = 10
+ print(ftos(hundy ** 2), "\n"); // prints: 100
+
+ hundy = 10.0f;
+ print(ftos(__builtin_exp(hundy)), "\n"); // prints: 22026.5
+}
--- /dev/null
+# used to test the builtins
+I: exponentiation.qc
+D: test exponentiation operator and __builtin_pow
+T: -execute
+C: -std=gmqcc
+E: $null
+M: 100
+M: 100
+M: 22026.5
--- /dev/null
+#define CAT(X, Y) X##Y
+CAT(hello, world)
+
+#define REDIR(X, Y) CAT(X, Y)
+REDIR(CAT(hello, world), CAT(world, hello))
+
+#define SCONS(X, ...) REDIR(X, __VA_ARGS__)
+SCONS(hello, world)
+
+#define FOO(X) X##X
+#define BAR(X) FOO(X)##FOO(X)
+
+REDIR(BAR(hello), BAR(world))
--- /dev/null
+I: ppcat.qc
+D: test preprocessor concatenation
+T: -pp
+C: -std=gmqcc
+F: -no-defs
+M: helloworld
+M: helloworldworldhello
+M: helloworld
+M: hellohellohellohelloworldworldworldworld
+
I: uninit.qc
-D: catch another case of unused vector accesses - should fail
+D: catch another case of unused vector accesses
T: -fail
C: -std=fteqcc -Wall -Werror -DUNINIT
if (!u8_analyze(_s, &st, &ln, &ch, 0x10))
ch = 0;
- if (_end)
+ else if (_end)
*_end = _s + st + ln;
return ch;
}
if (!u8_analyze(_s, &st, &ln, &ch, _maxlen))
ch = 0;
- if (_end)
+ else if (_end)
*_end = _s + st + ln;
return ch;
}
uint64_t mem_db = 0;
uint64_t mem_at = 0;
uint64_t mem_dt = 0;
+uint64_t mem_pk = 0;
+uint64_t mem_hw = 0;
struct memblock_t {
const char *file;
struct memblock_t *prev;
};
+#define PEAK_MEM \
+ do { \
+ if (mem_hw > mem_pk) \
+ mem_pk = mem_hw; \
+ } while (0)
+
static struct memblock_t *mem_start = NULL;
void *util_memory_a(size_t byte, unsigned int line, const char *file) {
mem_at++;
mem_ab += info->byte;
+ mem_hw += info->byte;
+
+ PEAK_MEM;
return data;
}
info = ((struct memblock_t*)ptrn - 1);
mem_db += info->byte;
+ mem_hw -= info->byte;
mem_dt++;
if (info->prev)
mem_start = newinfo;
mem_ab -= oldinfo->byte;
+ mem_hw -= oldinfo->byte;
mem_ab += newinfo->byte;
+ mem_hw += newinfo->byte;
+
+ PEAK_MEM;
free(oldinfo);
return newinfo+1;
}
+static void util_dumpmem(struct memblock_t *memory, uint16_t cols) {
+ uint32_t i, j;
+ for (i = 0; i < memory->byte + ((memory->byte % cols) ? (cols - memory->byte % cols) : 0); i++) {
+ if (i % cols == 0) con_out(" 0x%06X: ", i);
+ if (i < memory->byte) con_out("%02X " , 0xFF & ((char*)(memory + 1))[i]);
+ else con_out(" ");
+
+ if ((uint16_t)(i % cols) == (cols - 1)) {
+ for (j = i - (cols - 1); j <= i; j++) {
+ con_out("%c",
+ (j >= memory->byte)
+ ? ' '
+ : (isprint(((char*)(memory + 1))[j]))
+ ? 0xFF & ((char*)(memory + 1)) [j]
+ : '.'
+ );
+ }
+ con_out("\n");
+ }
+ }
+}
+
void util_meminfo() {
struct memblock_t *info;
- if (!OPTS_OPTION_BOOL(OPTION_MEMCHK))
- return;
- for (info = mem_start; info; info = info->next) {
- util_debug("MEM", "lost: % 8u (bytes) at %s:%u\n",
- info->byte,
- info->file,
- info->line);
+ if (OPTS_OPTION_BOOL(OPTION_DEBUG)) {
+ for (info = mem_start; info; info = info->next) {
+ con_out("lost: %u (bytes) at %s:%u\n",
+ info->byte,
+ info->file,
+ info->line);
+
+ util_dumpmem(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS));
+ }
}
- util_debug("MEM", "Memory information:\n\
- Total allocations: %llu\n\
- Total deallocations: %llu\n\
- Total allocated: %llu (bytes)\n\
- Total deallocated: %llu (bytes)\n\
- Leaks found: lost %llu (bytes) in %d allocations\n",
- mem_at, mem_dt,
- mem_ab, mem_db,
- (mem_ab - mem_db),
- (mem_at - mem_dt)
- );
+ if (OPTS_OPTION_BOOL(OPTION_DEBUG) ||
+ OPTS_OPTION_BOOL(OPTION_MEMCHK)) {
+ con_out("Memory information:\n\
+ Total allocations: %llu\n\
+ Total deallocations: %llu\n\
+ Total allocated: %f (MB)\n\
+ Total deallocated: %f (MB)\n\
+ Total peak memory: %f (MB)\n\
+ Total leaked memory: %f (MB) in %llu allocations\n",
+ mem_at,
+ mem_dt,
+ (float)(mem_ab) / 1048576.0f,
+ (float)(mem_db) / 1048576.0f,
+ (float)(mem_pk) / 1048576.0f,
+ (float)(mem_ab - mem_db) / 1048576.0f,
+
+ /* could be more clever */
+ (mem_at - mem_dt)
+ );
+ }
}
/*
* Some string utility functions, because strdup uses malloc, and we want
* to track all memory (without replacing malloc).
*/
-char *util_strdup(const char *s) {
+char *_util_Estrdup(const char *s, const char *file, size_t line) {
size_t len = 0;
char *ptr = NULL;
+ /* in case of -DNOTRACK */
+ (void)file;
+ (void)line;
+
if (!s)
return NULL;
- if ((len = strlen(s)) && (ptr = (char*)mem_a(len+1))) {
+ if ((len = strlen(s)) && (ptr = (char*)mem_af(len+1, line, file))) {
+ memcpy(ptr, s, len);
+ ptr[len] = '\0';
+ }
+ return ptr;
+}
+
+char *_util_Estrdup_empty(const char *s, const char *file, size_t line) {
+ size_t len = 0;
+ char *ptr = NULL;
+
+ /* in case of -DNOTRACK */
+ (void)file;
+ (void)line;
+
+ if (!s)
+ return NULL;
+
+ len = strlen(s);
+ if ((ptr = (char*)mem_af(len+1, line, file))) {
memcpy(ptr, s, len);
ptr[len] = '\0';
}
util_swap64((uint32_t*)_data, length>>3);
return;
- default: abort(); /* please blow the fuck up! */
+ default: exit(EXIT_FAILURE); /* please blow the fuck up! */
}
# endif
#endif
if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
return NULL;
- if (!(node->key = util_strdup(key))) {
+ if (!(node->key = util_strdupe(key))) {
mem_d(node);
return NULL;
}
* Free all allocated data in a hashtable, this is quite the amount
* of work.
*/
-void util_htdel(hash_table_t *ht) {
+void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
size_t i = 0;
for (; i < ht->size; i++) {
hash_node_t *n = ht->table[i];
while (n) {
if (n->key)
mem_d(n->key);
+ if (callback)
+ callback(n->value);
p = n;
n = n->next;
mem_d(p);
mem_d(ht);
}
-/*
- * A basic implementation of a hash-set. Unlike a hashtable, a hash
- * set doesn't maintain key-value pairs. It simply maintains a key
- * that can be set, removed, and checked for.
- *
- * See EXPOSED interface comment below
- */
-#define GMQCC_HASHSET_PRIME0 0x0049
-#define GMQCC_HASHSET_PRIME1 0x1391
-
-static int util_hsput(hash_set_t *set, void *item) {
- size_t hash = (size_t)item; /* shouldn't drop the bits */
- size_t iter;
-
- /* a == 0 || a == 1 */
- if (hash >> 1)
- return -1;
-
- iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
-
- /* while (set->items[iter] != 0 && set->items[iter] != 1) */
- while (!(set->items[iter] >> 1)) {
- if (set->items[iter] == hash)
- return 0;
+void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
+ hash_node_t **pair = &ht->table[bin];
+ hash_node_t *tmp;
- iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
- }
-
- set->total ++;
- set->items[iter] = hash;
-
- return 1;
-}
+ while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
+ pair = &(*pair)->next;
-static void util_hsupdate(hash_set_t *set) {
- size_t *old;
- size_t end;
- size_t itr;
-
- /* time to rehash? */
- if ((float)set->total >= (size_t)((double)set->capacity * 0.85)) {
- old = set->items;
- end = set->capacity;
-
- set->bits ++;
- set->capacity = (size_t)(1 << set->bits);
- set->mask = set->capacity - 1;
- set->items = (size_t*)mem_a(set->capacity * sizeof(size_t));
- set->total = 0;
-
- /*assert(set->items);*/
-
- /*
- * this shouldn't be slow? if so unroll it a little perhaps
- * (shouldn't be though)
- */
- for (itr = 0; itr < end; itr++)
- util_hsput(set, (void*)old[itr]);
-
- mem_d(old);
- }
-}
-
-/*
- * EXPOSED interface: all of these functions are exposed to the outside
- * for use. The stuff above is static because it's the "internal" mechanics
- * for syncronizing the set for updating, and putting data into the set.
- */
-int util_hsadd(hash_set_t *set, void *item) {
- int run = util_hsput(set, item); /* inlined */
- util_hsupdate(set);
-
- return run;
-}
-
-/* remove item in set */
-int util_hsrem(hash_set_t *set, void *item) {
- size_t hash = (size_t)item;
- size_t iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
-
- while (set->items[iter]) {
- if (set->items[iter] == hash) {
- set->items[iter] = 1;
- set->total --;
-
- return 1;
- }
- iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
- }
-
- return 0;
-}
-
-/* check if item is set */
-int util_hshas(hash_set_t *set, void *item) {
- size_t hash = (size_t)item;
- size_t iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
-
- while (set->items[iter]) {
- if (set->items[iter] == hash)
- return 1;
+ tmp = *pair;
+ if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
+ return;
- iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
- }
+ if (cb)
+ (*cb)(tmp->value);
- return 0;
+ *pair = tmp->next;
+ mem_d(tmp->key);
+ mem_d(tmp);
}
-hash_set_t *util_hsnew(void) {
- hash_set_t *set;
-
- 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 = (size_t*)mem_a(set->capacity * sizeof(size_t));
-
- if (!set->items) {
- util_hsdel(set);
- return NULL;
- }
-
- return set;
+void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
+ util_htrmh(ht, key, util_hthash(ht, key), cb);
}
-void util_hsdel(hash_set_t *set) {
- if (!set) return;
-
- if (set->items)
- mem_d(set->items);
-
- mem_d(set);
+void util_htdel(hash_table_t *ht) {
+ util_htrem(ht, NULL);
}
-#undef GMQCC_HASHSET_PRIME0
-#undef GMQCC_HASHSET_PRIME1
-
/*
* Portable implementation of vasprintf/asprintf. Assumes vsnprintf