Merge branch 'cooking'

This commit is contained in:
Dale Weiler 2013-04-21 10:09:08 +00:00
commit 462c06d56b
49 changed files with 2075 additions and 583 deletions

6
.gitignore vendored
View file

@ -7,10 +7,12 @@
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

27
CHANGES
View file

@ -1,10 +1,12 @@
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.
@ -21,6 +23,8 @@ Release v0.2.4
- 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
@ -29,16 +33,35 @@ Release v0.2.4
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

View file

@ -1,4 +1,5 @@
DESTDIR :=
OPTIONAL:=
PREFIX := /usr/local
BINDIR := $(PREFIX)/bin
DATADIR := $(PREFIX)/share
@ -9,7 +10,7 @@ CYGWIN = $(findstring CYGWIN, $(UNAME))
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
@ -17,28 +18,28 @@ 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
@ -84,6 +85,36 @@ else
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 \
@ -119,7 +150,6 @@ SPLINTFLAGS = \
-kepttrans \
-unqualifiedtrans \
+matchanyintegral \
-bufferoverflowhigh \
+voidabstract \
-nullassign \
-unrecog \
@ -147,7 +177,7 @@ $(QCVM): $(OBJ_X)
$(CC) -o $@ $^ $(CFLAGS) -lm
$(GMQCC): $(OBJ_C) $(OBJ_D)
$(CC) -o $@ $^ $(CFLAGS)
$(CC) -o $@ $^ $(CFLAGS) -lm
$(TESTSUITE): $(OBJ_T)
$(CC) -o $@ $^ $(CFLAGS) -lm
@ -163,11 +193,17 @@ test: all
@ ./$(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))
@ -219,9 +255,15 @@ fs.o: gmqcc.h opts.def
main.o: gmqcc.h opts.def lexer.h
lexer.o: gmqcc.h opts.def lexer.h
parser.o: gmqcc.h opts.def lexer.h ast.h ir.h
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

12
README
View file

@ -1,10 +1,14 @@
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

24
ast.c
View file

@ -42,7 +42,7 @@ static GMQCC_NORETURN void _ast_node_destroy(ast_node *self)
{
(void)self;
con_err("ast node missing destroy()\n");
abort();
exit(EXIT_FAILURE);
}
/* Initialize main ast node aprts */
@ -87,6 +87,8 @@ static void ast_expression_delete(ast_expression *self)
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)
@ -218,7 +220,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
if (!e) {
if (pos + 6 >= bufsize)
goto full;
strcpy(buf + pos, "(null)");
strncpy(buf + pos, "(null)", 6);
return pos + 6;
}
@ -227,7 +229,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
switch (e->expression.vtype) {
case TYPE_VARIANT:
strcpy(buf + pos, "(variant)");
strncpy(buf + pos, "(variant)", 9);
return pos + 9;
case TYPE_FIELD:
@ -284,7 +286,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi
typelen = strlen(typestr);
if (pos + typelen >= bufsize)
goto full;
strcpy(buf + pos, typestr);
strncpy(buf + pos, typestr, typelen);
return pos + typelen;
}
@ -584,6 +586,7 @@ void ast_member_delete(ast_member *self)
* purpose that is not garbage-collected.
*/
ast_expression_delete((ast_expression*)self);
mem_d(self->name);
mem_d(self);
}
@ -1216,7 +1219,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
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;
@ -1274,7 +1277,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
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;
@ -1407,7 +1410,7 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
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);
@ -1416,20 +1419,21 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param)
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
{
@ -1538,7 +1542,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
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;
}

View file

@ -168,7 +168,7 @@ static int win_fputs(FILE *h, const char *str) {
state = -1;
}
} else {
fs_file_putc(h, *str);
fs_file_write(str, 1, 1, stdout);
length ++;
}
str++;
@ -282,10 +282,6 @@ int con_change(const char *out, const char *err) {
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;
}

34
distro/Makefile Normal file
View file

@ -0,0 +1,34 @@
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

View file

@ -47,9 +47,9 @@ check() {
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
}

View file

@ -30,9 +30,9 @@ check() {
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
}

View file

@ -1,7 +1,11 @@
# 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')
@ -11,40 +15,25 @@ provides=('gmqcc=0.2.4')
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."

View file

@ -9,7 +9,7 @@ depends=('glibc')
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/

View file

@ -9,9 +9,17 @@ CARCH := $(shell uname -m)
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)
@ -34,7 +42,7 @@ base: clean
@rm -rf $(PKGDIR)
clean:
@rm -f $(PKG)
$(MAKE) -C $(BASEDIR) clean
@rm -f *.pkg.tar.xz
all: base

View file

@ -5,19 +5,39 @@ MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)`
MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)`
PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)`
DEBDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)
DEB := $(DEBDIR).deb
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

View file

@ -1,13 +0,0 @@
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.

14
exec.c
View file

@ -455,7 +455,6 @@ static void prog_print_statement(qc_program *prog, prog_section_statement *st)
else printf("(none)");
printf("\n");
}
fflush(stdout);
}
static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
@ -825,6 +824,16 @@ static int qc_strcmp(qc_program *prog)
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 */
@ -839,7 +848,8 @@ static prog_builtin qc_builtins[] = {
&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]);

23
fs.c
View file

@ -53,7 +53,7 @@
) {
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() {
@ -165,16 +165,6 @@ int fs_file_seek(FILE *fp, long int off, int whence) {
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);
@ -238,7 +228,7 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
if (!dir)
return NULL;
strcpy(dir->dd_name, name);
strncpy(dir->dd_name, name, strlen(name));
return dir;
}
@ -258,8 +248,8 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
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("\\*.*")))
@ -303,7 +293,6 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
#else
# if !defined(__MINGW32__)
# include <sys/stat.h> /* mkdir */
# include <unistd.h> /* chdir */
int fs_dir_make(const char *path) {
return mkdir(path, 0700);
@ -326,8 +315,4 @@ struct dirent *fs_dir_read(DIR *dir) {
return readdir(dir);
}
int fs_dir_change(const char *path) {
return chdir(path);
}
#endif /*! defined(_WIN32) && !defined(__MINGW32__) */

123
ftepp.c
View file

@ -25,6 +25,7 @@
#include "gmqcc.h"
#include "lexer.h"
#define HT_MACROS 1024
typedef struct {
bool on;
bool was_on;
@ -55,14 +56,15 @@ typedef struct {
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;
@ -124,7 +126,7 @@ char *ftepp_predef_line(lex_file *context) {
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;
}
@ -225,7 +227,7 @@ static pptoken *pptoken_make(ftepp_t *ftepp)
return token;
}
static void pptoken_delete(pptoken *self)
static GMQCC_INLINE void pptoken_delete(pptoken *self)
{
mem_d(self->value);
mem_d(self);
@ -261,27 +263,27 @@ static ftepp_t* ftepp_new()
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);
@ -300,7 +302,7 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond)
}
}
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;
@ -308,25 +310,14 @@ static void ftepp_update_output_condition(ftepp_t *ftepp)
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)
@ -394,6 +385,7 @@ static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro)
return false;
}
} while (ftepp->token == ',');
if (ftepp->token != ')') {
ftepp_error(ftepp, "expected closing paren after macro parameter list");
return false;
@ -422,7 +414,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
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");
@ -471,7 +463,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
static bool ftepp_define(ftepp_t *ftepp)
{
ppmacro *macro;
ppmacro *macro = NULL;
size_t l = ftepp_ctx(ftepp).line;
(void)ftepp_next(ftepp);
@ -499,18 +491,28 @@ static bool ftepp_define(ftepp_t *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);
}
@ -830,7 +832,7 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param
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);
}
@ -1706,9 +1708,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp)
/* 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)) {
@ -1724,7 +1724,7 @@ static bool ftepp_preprocess_done()
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);
@ -1734,10 +1734,10 @@ bool ftepp_preprocess_file(const char *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);
@ -1747,16 +1747,16 @@ bool ftepp_preprocess_string(const char *name, const char *str)
}
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;
}
@ -1766,26 +1766,27 @@ void ftepp_add_macro(const char *name, const char *value) {
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';
@ -1795,15 +1796,15 @@ bool ftepp_init()
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';
@ -1814,35 +1815,35 @@ bool ftepp_init()
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;
}

103
gmqcc.h
View file

@ -300,7 +300,8 @@ void util_meminfo ();
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);
@ -317,15 +318,20 @@ int util_asprintf (char **ret, const char *fmt, ...);
#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
@ -374,14 +380,6 @@ typedef struct hash_table_t {
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:
*
@ -413,52 +411,17 @@ typedef struct hash_set_t {
* 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 ===============================*/
/*===================================================================*/
@ -466,10 +429,8 @@ void util_hsdel(hash_set_t *);
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 *);
@ -480,11 +441,10 @@ FILE *fs_file_open (const char *, const char *);
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 *);
/*===================================================================*/
@ -1025,17 +985,20 @@ qcint prog_tempstring(qc_program *prog, const char *_str);
/*===================================================================*/
/*===================== 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 *);
@ -1047,14 +1010,14 @@ typedef struct {
*/
#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];

418
intrin.h Normal file
View file

@ -0,0 +1,418 @@
/*
* 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;
}

19
ir.c
View file

@ -1206,22 +1206,11 @@ bool ir_value_set_field(ir_value *self, ir_value *fld)
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;
}
@ -1651,7 +1640,7 @@ void ir_phi_add(ir_instr* self, ir_block *b, ir_value *v)
* is doing something wrong.
*/
irerror(self->context, "Invalid entry block for PHI");
abort();
exit(EXIT_FAILURE);
}
pe.value = v;
@ -3292,6 +3281,8 @@ static void gen_vector_defs(prog_section_def def, const char *name)
def.offset++;
component[len-1]++;
}
mem_d(component);
}
static void gen_vector_fields(prog_section_field fld, const char *name)
@ -3320,6 +3311,8 @@ 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)

25
lexer.c
View file

@ -272,7 +272,7 @@ void lex_close(lex_file *lex)
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;
@ -483,6 +483,9 @@ static bool lex_try_pragma(lex_file *lex)
lex->line = line;
while (ch != '\n' && ch != EOF)
ch = lex_getch(lex);
vec_free(command);
vec_free(param);
vec_free(pragma);
return true;
unroll:
@ -495,13 +498,13 @@ 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) {
@ -1352,7 +1355,7 @@ int lex_do(lex_file *lex)
lex_tokench(lex, ch);
nextch = lex_getch(lex);
if (nextch == '=') {
if (nextch == '=' || nextch == '*') {
lex_tokench(lex, nextch);
} else
lex_ungetch(lex, nextch);
@ -1361,6 +1364,12 @@ int lex_do(lex_file *lex)
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;

17
lexer.h
View file

@ -166,18 +166,21 @@ typedef struct {
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 },

60
main.c
View file

@ -146,9 +146,10 @@ static bool options_parse(int argc, char **argv) {
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;
@ -223,6 +224,10 @@ static bool options_parse(int argc, char **argv) {
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")) {
@ -403,7 +408,7 @@ static bool options_parse(int argc, char **argv) {
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 {
@ -541,12 +546,14 @@ static bool progs_nextline(char **out, size_t *alen,FILE *src) {
}
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 ();
@ -621,7 +628,7 @@ int main(int argc, char **argv) {
}
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;
@ -629,7 +636,7 @@ int main(int argc, char **argv) {
}
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;
@ -644,7 +651,7 @@ int main(int argc, char **argv) {
/* 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 */
@ -703,6 +710,7 @@ srcdone:
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))
@ -717,33 +725,33 @@ srcdone:
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;
}
@ -756,9 +764,10 @@ srcdone:
}
}
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;
}
@ -778,13 +787,14 @@ srcdone:
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)

4
opts.c
View file

@ -64,6 +64,7 @@ static void opts_setdefault() {
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() {
@ -96,6 +97,7 @@ void opts_init(const char *output, int standard, size_t arraysize) {
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) {
@ -257,7 +259,7 @@ static size_t opts_ini_parse (
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) {

View file

@ -109,6 +109,7 @@
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)

33
pak.c
View file

@ -259,9 +259,10 @@ bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
/*
* 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)) {
@ -278,15 +279,20 @@ bool pak_extract_one(pak_file_t *pak, const char *file) {
*/
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);
@ -310,11 +316,8 @@ bool pak_extract_all(pak_file_t *pak, const char *dir) {
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;
}
@ -361,7 +364,7 @@ bool pak_insert_one(pak_file_t *pak, const char *file) {
return false;
}
strcpy(dir.name, file);
strncpy(dir.name, file, strlen(file));
/*
* Allocate some memory for loading in the data that will be
@ -477,7 +480,6 @@ int main(int argc, char **argv) {
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;
@ -498,8 +500,6 @@ int main(int argc, char **argv) {
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;
@ -542,7 +542,7 @@ int main(int argc, char **argv) {
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);
@ -562,13 +562,6 @@ int main(int argc, char **argv) {
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);

155
parser.c
View file

@ -23,6 +23,7 @@
*/
#include <stdio.h>
#include <stdarg.h>
#include <math.h>
#include "gmqcc.h"
#include "lexer.h"
@ -34,7 +35,7 @@
#define PARSER_HT_SIZE 128
#define TYPEDEF_HT_SIZE 16
typedef struct {
typedef struct parser_s {
lex_file *lex;
int tok;
@ -46,6 +47,8 @@ typedef struct {
ast_value **imm_vector;
size_t translated;
ht ht_imm_string;
/* must be deleted first, they reference immediates and values */
ast_value **accessors;
@ -252,12 +255,22 @@ static char *parser_strdup(const char *str)
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++));
@ -268,6 +281,7 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do
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;
}
@ -379,6 +393,9 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t
return NULL;
}
/* include intrinsics */
#include "intrin.h"
typedef struct
{
size_t etype; /* 0 = expression, others are operators */
@ -961,10 +978,35 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
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)) {
@ -1071,6 +1113,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
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));
@ -1416,7 +1478,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy)
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
@ -1830,6 +1893,15 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels)
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;
@ -3654,6 +3726,8 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression *
static bool parse_enum(parser_t *parser)
{
bool flag = false;
bool reverse = false;
qcfloat num = 0;
ast_value **values = NULL;
ast_value *var = NULL;
@ -3661,11 +3735,37 @@ static bool parse_enum(parser_t *parser)
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 == '}') {
@ -3689,8 +3789,9 @@ static bool parse_enum(parser_t *parser)
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)) {
@ -3729,6 +3830,13 @@ static bool parse_enum(parser_t *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;
@ -4696,6 +4804,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
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]);
@ -5814,16 +5924,15 @@ static void generate_checksum(parser_t *parser)
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));
@ -5836,7 +5945,7 @@ bool parser_init()
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));
@ -5846,6 +5955,8 @@ bool parser_init()
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);
@ -5872,10 +5983,11 @@ bool parser_init()
} 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;
@ -5907,27 +6019,27 @@ bool parser_compile()
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) {
@ -5957,6 +6069,7 @@ void parser_cleanup()
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);
@ -5998,10 +6111,12 @@ void parser_cleanup()
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;

24
syntax/README Normal file
View file

@ -0,0 +1,24 @@
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.

8
syntax/geany/README Normal file
View file

@ -0,0 +1,8 @@
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/

55
syntax/geany/filetypes.qc Normal file
View file

@ -0,0 +1,55 @@
[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"

View file

@ -0,0 +1,5 @@
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/

View file

@ -0,0 +1,173 @@
<?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*
(".*?"|&lt;.*&gt;)
</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">
(?&lt;![\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">
(?&lt;![\w\.])
0[xX][a-fA-F0-9]+[uUlL]*
(?![\w\.])
</match>
</context>
<context id="invalid-hexadecimal" style-ref="error">
<match extended="true">
(?&lt;![\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">
(?&lt;![\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>

26
syntax/jedit/README Normal file
View file

@ -0,0 +1,26 @@
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>

271
syntax/jedit/qc.xml Normal file
View file

@ -0,0 +1,271 @@
<?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=",+-=&lt;&gt;/?^&amp;*" />
<!-- 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">??&lt;</SEQ>
<SEQ TYPE="LITERAL4">??!</SEQ>
<SEQ TYPE="LITERAL4">??&gt;</SEQ>
<SEQ TYPE="LITERAL4">??-</SEQ>
<SEQ TYPE="LITERAL4">??=</SEQ>
<!-- Alternate tokens -->
<SEQ TYPE="LITERAL4">&lt;:</SEQ>
<SEQ TYPE="LITERAL4">:&gt;</SEQ>
<SEQ TYPE="LITERAL4">&lt;%</SEQ>
<SEQ TYPE="LITERAL4">%&gt;</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">&gt;</SEQ>
<SEQ TYPE="OPERATOR">&lt;</SEQ>
<SEQ TYPE="OPERATOR">%</SEQ>
<SEQ TYPE="OPERATOR">&amp;</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>/**&lt;</BEGIN>
<END>*/</END>
</SPAN>
<SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
<BEGIN>/**</BEGIN>
<END>*/</END>
</SPAN>
<EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">///&lt;</EOL_SPAN>
<EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">///</EOL_SPAN>
<!-- Doxygen comment, Qt style -->
<SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
<BEGIN>/*!&lt;</BEGIN>
<END>*/</END>
</SPAN>
<SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">
<BEGIN>/*!</BEGIN>
<END>*/</END>
</SPAN>
<EOL_SPAN TYPE="COMMENT3" DELEGATE="doxygen::DOXYGEN">//!&lt;</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>&lt;</BEGIN>
<END>&gt;</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>

9
syntax/kate/README Normal file
View file

@ -0,0 +1,9 @@
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

155
syntax/kate/qc.xml Normal file
View file

@ -0,0 +1,155 @@
<?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="&quot;"/>
<Detect2Chars attribute="Comment" context="Commentar 1" char="/" char1="/"/>
<Detect2Chars attribute="Comment" context="Commentar 2" char="/" char1="*" beginRegion="Comment"/>
<AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;?[]|~^&#59;"/>
</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="&quot;"/>
</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="&quot;" char1="&quot;"/>
<RangeDetect attribute="Prep. Lib" context="#stay" char="&lt;" char1="&gt;"/>
<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="&quot;"/>
<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="&quot;"/>
<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="'&quot;" />
</general>
</language>

12
syntax/nano/README Normal file
View file

@ -0,0 +1,12 @@
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

22
syntax/nano/qc.nanorc Normal file
View file

@ -0,0 +1,22 @@
# 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:]]+$"

260
test.c
View file

@ -176,8 +176,8 @@ int task_pclose(FILE **handles) {
}
#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
@ -269,6 +269,7 @@ typedef struct {
*/
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)
@ -315,7 +316,7 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s
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
@ -333,6 +334,9 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s
pad[0] = desclen;
}
if ((filelen = strlen(file)) > pad[2])
pad[2] = filelen;
return true;
}
@ -428,7 +432,7 @@ bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size
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));
@ -563,7 +567,13 @@ task_template_t *task_template_compile(const char *file, const char *dir, size_t
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;
@ -644,10 +654,10 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
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");
@ -679,7 +689,8 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
* 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
@ -693,45 +704,66 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
* 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
);
}
@ -777,7 +809,7 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
found
);
closedir(dir);
fs_dir_close(dir);
return success;
}
@ -790,9 +822,9 @@ void task_precleanup(const char *curdir) {
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"))
@ -805,7 +837,7 @@ void task_precleanup(const char *curdir) {
}
}
closedir(dir);
fs_dir_close(dir);
}
void task_destroy(void) {
@ -854,40 +886,50 @@ 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.
@ -913,6 +955,13 @@ bool task_execute(task_template_t *tmpl, char ***line) {
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;
@ -933,10 +982,25 @@ bool task_execute(task_template_t *tmpl, char ***line) {
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
@ -945,6 +1009,7 @@ bool task_execute(task_template_t *tmpl, char ***line) {
*/
#include <math.h>
void task_schedualize(size_t *pad) {
char space[2][64];
bool execute = false;
char *data = NULL;
char **match = NULL;
@ -952,15 +1017,21 @@ void task_schedualize(size_t *pad) {
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
@ -979,8 +1050,6 @@ void task_schedualize(size_t *pad) {
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) {
/*
@ -997,25 +1066,26 @@ void task_schedualize(size_t *pad) {
}
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;
@ -1023,14 +1093,24 @@ void task_schedualize(size_t *pad) {
/*
* 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)"
);
/*
@ -1038,7 +1118,7 @@ void task_schedualize(size_t *pad) {
* 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)
);
@ -1046,10 +1126,10 @@ void task_schedualize(size_t *pad) {
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]);
}
/*
@ -1059,7 +1139,7 @@ void task_schedualize(size_t *pad) {
*/
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)]
);
}
@ -1075,11 +1155,12 @@ void task_schedualize(size_t *pad) {
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)
);
}
@ -1104,7 +1185,8 @@ GMQCC_WARN bool test_perform(const char *curdir, const char *defs) {
static const char *default_defs = "defs.qh";
size_t pad[] = {
0, 0
/* test ### [succeed/fail]: `description` [tests/template.tmpl] [type] */
0, 0, 0
};
/*

View file

@ -16,3 +16,4 @@ string (...) strcat = #10;
float (string, string) strcmp = #11;
vector (vector) normalize = #12;
float (float) sqrt = #13;
float (float) floor = #14;

View file

@ -1,4 +1,4 @@
void(string, ...) print = #1;enum {
enum {
// this behaviour is confusing, but I like that
// we support it.
__ = (__ - 1),
@ -27,6 +27,20 @@ enum {
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");
@ -42,4 +56,13 @@ void main() {
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");
};

View file

@ -16,3 +16,10 @@ M: 10
M: 11
M: 12
M: 13
M: 2
M: 4
M: 8
M: 3
M: 2
M: 1
M: 0

14
tests/exponentiation.qc Normal file
View file

@ -0,0 +1,14 @@
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
}

View file

@ -0,0 +1,9 @@
# 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

13
tests/ppcat.qc Normal file
View file

@ -0,0 +1,13 @@
#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))

10
tests/ppcat.tmpl Normal file
View file

@ -0,0 +1,10 @@
I: ppcat.qc
D: test preprocessor concatenation
T: -pp
C: -std=gmqcc
F: -no-defs
M: helloworld
M: helloworldworldhello
M: helloworld
M: hellohellohellohelloworldworldworldworld

View file

@ -1,4 +1,4 @@
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

4
utf8.c
View file

@ -198,7 +198,7 @@ uchar_t u8_getchar(const char *_s, const char **_end)
if (!u8_analyze(_s, &st, &ln, &ch, 0x10))
ch = 0;
if (_end)
else if (_end)
*_end = _s + st + ln;
return ch;
}
@ -210,7 +210,7 @@ uchar_t u8_getnchar(const char *_s, const char **_end, size_t _maxlen)
if (!u8_analyze(_s, &st, &ln, &ch, _maxlen))
ch = 0;
if (_end)
else if (_end)
*_end = _s + st + ln;
return ch;
}

267
util.c
View file

@ -30,6 +30,8 @@ uint64_t mem_ab = 0;
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;
@ -39,6 +41,12 @@ struct memblock_t {
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) {
@ -56,6 +64,9 @@ 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;
}
@ -67,6 +78,7 @@ void util_memory_d(void *ptrn) {
info = ((struct memblock_t*)ptrn - 1);
mem_db += info->byte;
mem_hw -= info->byte;
mem_dt++;
if (info->prev)
@ -122,51 +134,111 @@ void *util_memory_r(void *ptrn, size_t byte, unsigned int line, const char *file
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';
}
@ -253,7 +325,7 @@ void util_endianswap(void *_data, size_t length, unsigned int typesize) {
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
@ -425,7 +497,7 @@ hash_node_t *_util_htnewpair(const char *key, void *value) {
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;
}
@ -548,7 +620,7 @@ void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
* 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];
@ -558,6 +630,8 @@ void util_htdel(hash_table_t *ht) {
while (n) {
if (n->key)
mem_d(n->key);
if (callback)
callback(n->value);
p = n;
n = n->next;
mem_d(p);
@ -569,146 +643,33 @@ void util_htdel(hash_table_t *ht) {
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
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;
static int util_hsput(hash_set_t *set, void *item) {
size_t hash = (size_t)item; /* shouldn't drop the bits */
size_t iter;
while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
pair = &(*pair)->next;
/* a == 0 || a == 1 */
if (hash >> 1)
return -1;
tmp = *pair;
if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
return;
iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
if (cb)
(*cb)(tmp->value);
/* while (set->items[iter] != 0 && set->items[iter] != 1) */
while (!(set->items[iter] >> 1)) {
if (set->items[iter] == hash)
return 0;
iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
}
set->total ++;
set->items[iter] = hash;
return 1;
*pair = tmp->next;
mem_d(tmp->key);
mem_d(tmp);
}
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);
}
void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
util_htrmh(ht, key, util_hthash(ht, key), cb);
}
/*
* 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;
void util_htdel(hash_table_t *ht) {
util_htrem(ht, NULL);
}
/* 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;
iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
}
return 0;
}
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_hsdel(hash_set_t *set) {
if (!set) return;
if (set->items)
mem_d(set->items);
mem_d(set);
}
#undef GMQCC_HASHSET_PRIME0
#undef GMQCC_HASHSET_PRIME1
/*
* Portable implementation of vasprintf/asprintf. Assumes vsnprintf
* exists, otherwise compiler error.