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 testsuite
qcvm qcvm
gmqcc gmqcc
pak
distro/arch/* distro/archlinux/*
distro/archbsd/*
!distro/archlinux/git/PKGBUILD !distro/archlinux/git/PKGBUILD
!distro/archlinux/release/PKGBUILD !distro/archlinux/release/PKGBUILD
!distro/archbsd/release/PKGBUILD !distro/archbsd/release/PKGBUILD
!distro/archbsd/git/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: * Preprocessor:
- __VA_ARGS__ support - __VA_ARGS__ support
_ __VA_ARGS__ indexing
- Predefined macros like __DATE__, __TIME__, ... - Predefined macros like __DATE__, __TIME__, ...
(check the manpage for a full list) (check the manpage for a full list)
- Signed numbers as single token in the - Signed numbers as single token in the
- Fixes some issues with #if operations on macros. - Fixes some issues with #if operations on macros.
- Speed improvements
* Language: * Language:
- Untyped `nil` keyword. - Untyped `nil` keyword.
- Removed the `noreturn` keyword. - Removed the `noreturn` keyword.
@ -21,6 +23,8 @@ Release v0.2.4
- Type restricted variadict parameters: - Type restricted variadict parameters:
ie: void print(string...); ie: void print(string...);
- Accessing varargs from QC via: ...(index, type) - Accessing varargs from QC via: ...(index, type)
- New operators: ** (exponentiation), % (modulo), etc
- Enumeration attributes: flag, reverse
* Compilation: * Compilation:
- Various optimizations and progs-size reductions. - Various optimizations and progs-size reductions.
- A new spell-checking algorithm tries to hint you at existing - 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 have been solved in both DP and our own executor. A new
compatbility option (enabled by default) has been added for compatbility option (enabled by default) has been added for
now: -flegacy-vector-maths 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. - Improved commandline argument handling.
- More builtins: sqrt(), normalize() - More builtins: sqrt(), normalize()
* Commandline: * Commandline:
- Nicer memory dumps
- Support for making individual warnings an error - Support for making individual warnings an error
- via -Werror-<warning> - via -Werror-<warning>
- added --add-info - added --add-info
* Testsuite: * Testsuite:
- Support for QCFLAGS to run tests with several additional - Support for QCFLAGS to run tests with several additional
flags. 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 2012-12-27 Hotfix v0.2.2
* Liferanges * Liferanges

View file

@ -1,4 +1,5 @@
DESTDIR := DESTDIR :=
OPTIONAL:=
PREFIX := /usr/local PREFIX := /usr/local
BINDIR := $(PREFIX)/bin BINDIR := $(PREFIX)/bin
DATADIR := $(PREFIX)/share DATADIR := $(PREFIX)/share
@ -9,7 +10,7 @@ CYGWIN = $(findstring CYGWIN, $(UNAME))
MINGW = $(findstring MINGW32, $(UNAME)) MINGW = $(findstring MINGW32, $(UNAME))
CC ?= clang 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),) ifneq ($(shell git describe --always 2>/dev/null),)
CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\"" CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\""
endif endif
@ -17,28 +18,28 @@ endif
# but also turn off the STUPID ONES # but also turn off the STUPID ONES
ifeq ($(CC), clang) ifeq ($(CC), clang)
CFLAGS += \ CFLAGS += \
-Weverything \ -Weverything \
-Wno-padded \ -Wno-padded \
-Wno-format-nonliteral \ -Wno-format-nonliteral \
-Wno-disabled-macro-expansion \ -Wno-disabled-macro-expansion \
-Wno-conversion \ -Wno-conversion \
-Wno-missing-prototypes \ -Wno-missing-prototypes \
-Wno-float-equal \ -Wno-float-equal \
-Wno-cast-align \ -Wno-cast-align \
-Wno-missing-variable-declarations \ -Wno-missing-variable-declarations \
-Wno-unknown-warning-option -Wno-unknown-warning-option
else else
#Tiny C Compiler doesn't know what -pedantic-errors is #Tiny C Compiler doesn't know what -pedantic-errors is
# and instead of ignoring .. just errors. # and instead of ignoring .. just errors.
ifneq ($(CC), tcc) ifneq ($(CC), tcc)
CFLAGS +=-pedantic-errors CFLAGS +=-pedantic-errors -ffunction-sections -fdata-sections -Wl,-gc-sections
else else
CFLAGS += -Wno-pointer-sign -fno-common CFLAGS += -Wno-pointer-sign -fno-common
endif endif
endif endif
ifeq ($(track), no) ifeq ($(track), no)
CFLAGS += -DNOTRACK CFLAGS += -DNOTRACK
endif endif
OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o 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
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 #splint flags
SPLINTFLAGS = \ SPLINTFLAGS = \
-redef \ -redef \
@ -119,7 +150,6 @@ SPLINTFLAGS = \
-kepttrans \ -kepttrans \
-unqualifiedtrans \ -unqualifiedtrans \
+matchanyintegral \ +matchanyintegral \
-bufferoverflowhigh \
+voidabstract \ +voidabstract \
-nullassign \ -nullassign \
-unrecog \ -unrecog \
@ -147,7 +177,7 @@ $(QCVM): $(OBJ_X)
$(CC) -o $@ $^ $(CFLAGS) -lm $(CC) -o $@ $^ $(CFLAGS) -lm
$(GMQCC): $(OBJ_C) $(OBJ_D) $(GMQCC): $(OBJ_C) $(OBJ_D)
$(CC) -o $@ $^ $(CFLAGS) $(CC) -o $@ $^ $(CFLAGS) -lm
$(TESTSUITE): $(OBJ_T) $(TESTSUITE): $(OBJ_T)
$(CC) -o $@ $^ $(CFLAGS) -lm $(CC) -o $@ $^ $(CFLAGS) -lm
@ -163,11 +193,17 @@ test: all
@ ./$(TESTSUITE) @ ./$(TESTSUITE)
clean: clean:
rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4
splint: splint:
@ splint $(SPLINTFLAGS) *.c *.h @ splint $(SPLINTFLAGS) *.c *.h
gource:
@ gource $(GOURCEFLAGS)
gource-record:
@ gource $(GOURCEFLAGS) -o - | ffmpeg $(FFMPEGFLAGS) gource.mp4
depend: depend:
@makedepend -Y -w 65536 2> /dev/null \ @makedepend -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_D)) $(subst .o,.c,$(OBJ_D))
@ -219,9 +255,15 @@ fs.o: gmqcc.h opts.def
main.o: gmqcc.h opts.def lexer.h main.o: gmqcc.h opts.def lexer.h
lexer.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 fs.o: gmqcc.h opts.def
util.o: gmqcc.h opts.def util.o: gmqcc.h opts.def
conout.o: gmqcc.h opts.def conout.o: gmqcc.h opts.def
fs.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 GMQCC: An improved Quake C compiler
For licensing: see the LICENSE file. For licensing: see the LICENSE file.
For installation notes: see the INSTALL file. For installation notes: see the INSTALL file.
For a list of authors: see the AUTHORS file. For a list of authors: see the AUTHORS file.
For a list of changes: see the CHANGES file. For a list of changes: see the CHANGES file.
For documentation: For documentation:
See the manpages, or visit the documentation online at See the manpages, or visit the documentation online at
http://graphitemaster.github.com/gmqcc/doc.html 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; (void)self;
con_err("ast node missing destroy()\n"); con_err("ast node missing destroy()\n");
abort(); exit(EXIT_FAILURE);
} }
/* Initialize main ast node aprts */ /* Initialize main ast node aprts */
@ -87,6 +87,8 @@ static void ast_expression_delete(ast_expression *self)
ast_delete(self->expression.params[i]); ast_delete(self->expression.params[i]);
} }
vec_free(self->expression.params); vec_free(self->expression.params);
if (self->expression.varparam)
ast_delete(self->expression.varparam);
} }
static void ast_expression_delete_full(ast_expression *self) 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 (!e) {
if (pos + 6 >= bufsize) if (pos + 6 >= bufsize)
goto full; goto full;
strcpy(buf + pos, "(null)"); strncpy(buf + pos, "(null)", 6);
return pos + 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) { switch (e->expression.vtype) {
case TYPE_VARIANT: case TYPE_VARIANT:
strcpy(buf + pos, "(variant)"); strncpy(buf + pos, "(variant)", 9);
return pos + 9; return pos + 9;
case TYPE_FIELD: 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); typelen = strlen(typestr);
if (pos + typelen >= bufsize) if (pos + typelen >= bufsize)
goto full; goto full;
strcpy(buf + pos, typestr); strncpy(buf + pos, typestr, typelen);
return pos + typelen; return pos + typelen;
} }
@ -584,6 +586,7 @@ void ast_member_delete(ast_member *self)
* purpose that is not garbage-collected. * purpose that is not garbage-collected.
*/ */
ast_expression_delete((ast_expression*)self); ast_expression_delete((ast_expression*)self);
mem_d(self->name);
mem_d(self); mem_d(self);
} }
@ -1216,7 +1219,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield)
namelen = strlen(self->name); namelen = strlen(self->name);
name = (char*)mem_a(namelen + 16); 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 = (ir_value**)mem_a(sizeof(array->ir_values[0]) * array->expression.count);
array->ir_values[0] = v; 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); namelen = strlen(self->name);
name = (char*)mem_a(namelen + 16); 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 = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count);
self->ir_values[0] = v; 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); v = ir_function_create_local(func, self->name, vtype, param);
if (!v) { 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; return false;
} }
v->context = ast_ctx(self); 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); namelen = strlen(self->name);
name = (char*)mem_a(namelen + 16); name = (char*)mem_a(namelen + 16);
strcpy(name, self->name); strncpy(name, self->name, namelen);
self->ir_values[0] = v; self->ir_values[0] = v;
for (ai = 1; ai < self->expression.count; ++ai) { for (ai = 1; ai < self->expression.count; ++ai) {
snprintf(name + namelen, 16, "[%u]", (unsigned int)ai); snprintf(name + namelen, 16, "[%u]", (unsigned int)ai);
self->ir_values[ai] = ir_function_create_local(func, name, vtype, param); self->ir_values[ai] = ir_function_create_local(func, name, vtype, param);
if (!self->ir_values[ai]) { 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; return false;
} }
self->ir_values[ai]->context = ast_ctx(self); self->ir_values[ai]->context = ast_ctx(self);
self->ir_values[ai]->unique_life = true; self->ir_values[ai]->unique_life = true;
self->ir_values[ai]->locked = true; self->ir_values[ai]->locked = true;
} }
mem_d(name);
} }
else else
{ {
@ -1538,7 +1542,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir)
irf = self->ir_func; irf = self->ir_func;
if (!irf) { 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; return false;
} }

View file

@ -168,7 +168,7 @@ static int win_fputs(FILE *h, const char *str) {
state = -1; state = -1;
} }
} else { } else {
fs_file_putc(h, *str); fs_file_write(str, 1, 1, stdout);
length ++; length ++;
} }
str++; str++;
@ -282,10 +282,6 @@ int con_change(const char *out, const char *err) {
con_enablecolor(); con_enablecolor();
} else if (!(console.handle_err = fs_file_open(err, "w"))) return 0; } 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; 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() { package() {
cd "$srcdir"/"$_gitname" cd "$srcdir"/"$_gitname"
msg "Compiling and installing to pkgdir this time..." 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." msg "Compiling done."
install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
} }

View file

@ -30,9 +30,9 @@ check() {
package() { package() {
cd "$srcdir"/"$_gitname" cd "$srcdir"/"$_gitname"
msg "Compiling and installing to pkgdir this time..." 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." msg "Compiling done."
install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
} }

View file

@ -1,7 +1,11 @@
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org> # Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
pkgname=gmqcc-git 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 pkgrel=1
pkgdesc="An Improved Quake C Compiler" pkgdesc="An Improved Quake C Compiler"
arch=('i686' 'x86_64') arch=('i686' 'x86_64')
@ -11,40 +15,25 @@ provides=('gmqcc=0.2.4')
makedepends=('git') makedepends=('git')
url="https://github.com/graphitemaster/gmqcc.git" url="https://github.com/graphitemaster/gmqcc.git"
license=('MIT') license=('MIT')
source=('gmqcc::git://github.com/graphitemaster/gmqcc.git')
sha1sums=('SKIP')
_gitroot="git://github.com/graphitemaster/gmqcc.git"
_gitname="gmqcc"
build() { 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..." msg "Starting compilation..."
cd "$srcdir"/"$_gitname" cd "$srcdir"/"gmqcc"
msg "Compiling..." msg "Compiling..."
make make
} }
check() { check() {
cd "$srcdir"/"$_gitname" cd "$srcdir"/"gmqcc"
make check make check
} }
package() { package() {
cd "$srcdir"/"$_gitname" cd "$srcdir"/"gmqcc"
msg "Compiling and installing to pkgdir this time..." msg "Compiling and installing to pkgdir this time..."
make install DESTDIR=$pkgdir PREFIX=/usr make install DESTDIR=$pkgdir PREFIX=/usr
msg "Compiling done." msg "Compiling done."

View file

@ -9,7 +9,7 @@ depends=('glibc')
url="https://github.com/graphitemaster/gmqcc.git" url="https://github.com/graphitemaster/gmqcc.git"
license=('MIT') license=('MIT')
source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver) source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353') sha1sums=('e0fe99af9a55d36cd9e0909a96d1b14f2db8b757')
_gitname=graphitemaster-gmqcc-de24486/ _gitname=graphitemaster-gmqcc-de24486/

View file

@ -9,9 +9,17 @@ CARCH := $(shell uname -m)
PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH) PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH)
PKG := $(PKGDIR).pkg.tar.xz PKG := $(PKGDIR).pkg.tar.xz
PKGINFO := $(PKGDIR)/.PKGINFO 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 "pkgname = gmqcc" > $(PKGINFO)
@echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO) @echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO)
@echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO) @echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO)
@ -34,7 +42,7 @@ base: clean
@rm -rf $(PKGDIR) @rm -rf $(PKGDIR)
clean: clean:
@rm -f $(PKG) $(MAKE) -C $(BASEDIR) clean
@rm -f *.pkg.tar.xz
all: base 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)` MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)`
PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)` PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)`
DEBDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH) 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: base:
$(MAKE) -C $(BASEDIR) clean
$(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install $(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install
@install -d -m755 $(DEBDIR)/DEBIAN @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 data.tar.gz -C $(DEBDIR)/ . --exclude=DEBIAN
@tar czf control.tar.gz -C $(DEBDIR)/DEBIAN/ . @tar czf control.tar.gz -C $(DEBDIR)/DEBIAN/ .
@echo 2.0 > debian-binary @echo 2.0 > debian-binary
@ar r $(DEB) debian-binary control.tar.gz data.tar.gz @ar r $(DEB) debian-binary control.tar.gz data.tar.gz
@rm -rf debian-binary control.tar.gz data.tar.gz $(DEBDIR) @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 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)"); else printf("(none)");
printf("\n"); printf("\n");
} }
fflush(stdout);
} }
static qcint prog_enterfunction(qc_program *prog, prog_section_function *func) static qcint prog_enterfunction(qc_program *prog, prog_section_function *func)
@ -825,6 +824,16 @@ static int qc_strcmp(qc_program *prog)
return 0; 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[] = { static prog_builtin qc_builtins[] = {
NULL, NULL,
&qc_print, /* 1 */ &qc_print, /* 1 */
@ -839,7 +848,8 @@ static prog_builtin qc_builtins[] = {
&qc_strcat, /* 10 */ &qc_strcat, /* 10 */
&qc_strcmp, /* 11 */ &qc_strcmp, /* 11 */
&qc_normalize, /* 12 */ &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]); 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"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression);
wprintf(L"Aborting ...\n"); wprintf(L"Aborting ...\n");
abort(); exit(EXIT_FAILURE);
} }
static void file_init() { static void file_init() {
@ -165,16 +165,6 @@ int fs_file_seek(FILE *fp, long int off, int whence) {
return fseek(fp, off, 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) { long int fs_file_tell(FILE *fp) {
/* Invokes file_exception on windows if fp is null */ /* Invokes file_exception on windows if fp is null */
return ftell(fp); return ftell(fp);
@ -238,7 +228,7 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
if (!dir) if (!dir)
return NULL; return NULL;
strcpy(dir->dd_name, name); strncpy(dir->dd_name, name, strlen(name));
return dir; return dir;
} }
@ -258,8 +248,8 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
if (*dir->dd_name) { if (*dir->dd_name) {
size_t n = strlen(dir->dd_name); size_t n = strlen(dir->dd_name);
if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) { if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) {
strcpy(dirname, dir->dd_name); strncpy(dirname, dir->dd_name, n);
strcpy(dirname + n, "\\*.*"); /* 4 + 1 */ strncpy(dirname + n, "\\*.*", 4); /* 4 + 1 */
} }
} else { } else {
if (!(dirname = util_strdup("\\*.*"))) if (!(dirname = util_strdup("\\*.*")))
@ -303,7 +293,6 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
#else #else
# if !defined(__MINGW32__) # if !defined(__MINGW32__)
# include <sys/stat.h> /* mkdir */ # include <sys/stat.h> /* mkdir */
# include <unistd.h> /* chdir */
int fs_dir_make(const char *path) { int fs_dir_make(const char *path) {
return mkdir(path, 0700); return mkdir(path, 0700);
@ -326,8 +315,4 @@ struct dirent *fs_dir_read(DIR *dir) {
return readdir(dir); return readdir(dir);
} }
int fs_dir_change(const char *path) {
return chdir(path);
}
#endif /*! defined(_WIN32) && !defined(__MINGW32__) */ #endif /*! defined(_WIN32) && !defined(__MINGW32__) */

123
ftepp.c
View file

@ -25,6 +25,7 @@
#include "gmqcc.h" #include "gmqcc.h"
#include "lexer.h" #include "lexer.h"
#define HT_MACROS 1024
typedef struct { typedef struct {
bool on; bool on;
bool was_on; bool was_on;
@ -55,14 +56,15 @@ typedef struct {
pptoken **output; pptoken **output;
} ppmacro; } ppmacro;
typedef struct { typedef struct ftepp_s {
lex_file *lex; lex_file *lex;
int token; int token;
unsigned int errors; unsigned int errors;
bool output_on; bool output_on;
ppcondition *conditions; ppcondition *conditions;
ppmacro **macros; /*ppmacro **macros;*/
ht macros; /* hashtable<string, ppmacro*> */
char *output_string; char *output_string;
@ -124,7 +126,7 @@ char *ftepp_predef_line(lex_file *context) {
char *ftepp_predef_file(lex_file *context) { char *ftepp_predef_file(lex_file *context) {
size_t length = strlen(context->name) + 3; /* two quotes and a terminator */ size_t length = strlen(context->name) + 3; /* two quotes and a terminator */
char *value = (char*)mem_a(length); char *value = (char*)mem_a(length);
sprintf(value, "\"%s\"", context->name); snprintf(value, length, "\"%s\"", context->name);
return value; return value;
} }
@ -225,7 +227,7 @@ static pptoken *pptoken_make(ftepp_t *ftepp)
return token; return token;
} }
static void pptoken_delete(pptoken *self) static GMQCC_INLINE void pptoken_delete(pptoken *self)
{ {
mem_d(self->value); mem_d(self->value);
mem_d(self); mem_d(self);
@ -261,27 +263,27 @@ static ftepp_t* ftepp_new()
ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); ftepp = (ftepp_t*)mem_a(sizeof(*ftepp));
memset(ftepp, 0, sizeof(*ftepp)); memset(ftepp, 0, sizeof(*ftepp));
ftepp->macros = util_htnew(HT_MACROS);
ftepp->output_on = true; ftepp->output_on = true;
return ftepp; 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); vec_free(self->output_string);
} }
static void ftepp_delete(ftepp_t *self) static void ftepp_delete(ftepp_t *self)
{ {
size_t i;
ftepp_flush_do(self); ftepp_flush_do(self);
if (self->itemname) if (self->itemname)
mem_d(self->itemname); mem_d(self->itemname);
if (self->includename) if (self->includename)
vec_free(self->includename); vec_free(self->includename);
for (i = 0; i < vec_size(self->macros); ++i)
ppmacro_delete(self->macros[i]); util_htrem(self->macros, (void (*)(void*))&ppmacro_delete);
vec_free(self->macros);
vec_free(self->conditions); vec_free(self->conditions);
if (self->lex) if (self->lex)
lex_close(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; size_t i;
ftepp->output_on = true; 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; 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; return util_htget(ftepp->macros, name);
for (i = 0; i < vec_size(ftepp->macros); ++i) {
if (!strcmp(name, ftepp->macros[i]->name))
return ftepp->macros[i];
}
return NULL;
} }
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; util_htrm(ftepp->macros, name, NULL);
for (i = 0; i < vec_size(ftepp->macros); ++i) {
if (!strcmp(name, ftepp->macros[i]->name)) {
vec_remove(ftepp->macros, i, 1);
return;
}
}
} }
static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp) 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; return false;
} }
} while (ftepp->token == ','); } while (ftepp->token == ',');
if (ftepp->token != ')') { if (ftepp->token != ')') {
ftepp_error(ftepp, "expected closing paren after macro parameter list"); ftepp_error(ftepp, "expected closing paren after macro parameter list");
return false; return false;
@ -422,7 +414,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro)
return false; return false;
} }
index = atoi(ftepp_tokval(ftepp)); index = (int)strtol(ftepp_tokval(ftepp), NULL, 10);
if (ftepp_next(ftepp) != ']') { if (ftepp_next(ftepp) != ']') {
ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript"); 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) static bool ftepp_define(ftepp_t *ftepp)
{ {
ppmacro *macro; ppmacro *macro = NULL;
size_t l = ftepp_ctx(ftepp).line; size_t l = ftepp_ctx(ftepp).line;
(void)ftepp_next(ftepp); (void)ftepp_next(ftepp);
@ -499,18 +491,28 @@ static bool ftepp_define(ftepp_t *ftepp)
if (ftepp->token == '(') { if (ftepp->token == '(') {
macro->has_params = true; macro->has_params = true;
if (!ftepp_define_params(ftepp, macro)) if (!ftepp_define_params(ftepp, macro)) {
ppmacro_delete(macro);
return false; return false;
}
} }
if (!ftepp_skipspace(ftepp)) if (!ftepp_skipspace(ftepp)) {
ppmacro_delete(macro);
return false; return false;
}
if (!ftepp_define_body(ftepp, macro)) if (!ftepp_define_body(ftepp, macro)) {
ppmacro_delete(macro);
return false; return false;
}
#if 0
if (ftepp->output_on) if (ftepp->output_on)
vec_push(ftepp->macros, macro); vec_push(ftepp->macros, macro);
#endif
if (ftepp->output_on)
util_htset(ftepp->macros, macro->name, (void*)macro);
else { else {
ppmacro_delete(macro); ppmacro_delete(macro);
} }
@ -830,7 +832,7 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param
if (resetline && !ftepp->in_macro) { if (resetline && !ftepp->in_macro) {
char lineno[128]; 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); 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 /* 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. * preprocessor. Except here we will want to warn about dangling #ifs.
*/ */
static ftepp_t *ftepp; static bool ftepp_preprocess_done(ftepp_t *ftepp)
static bool ftepp_preprocess_done()
{ {
bool retval = true; bool retval = true;
if (vec_size(ftepp->conditions)) { if (vec_size(ftepp->conditions)) {
@ -1724,7 +1724,7 @@ static bool ftepp_preprocess_done()
return retval; 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->lex = lex_open(filename);
ftepp->itemname = util_strdup(filename); ftepp->itemname = util_strdup(filename);
@ -1734,10 +1734,10 @@ bool ftepp_preprocess_file(const char *filename)
} }
if (!ftepp_preprocess(ftepp)) if (!ftepp_preprocess(ftepp))
return false; 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->lex = lex_open_string(str, strlen(str), name);
ftepp->itemname = util_strdup(name); ftepp->itemname = util_strdup(name);
@ -1747,16 +1747,16 @@ bool ftepp_preprocess_string(const char *name, const char *str)
} }
if (!ftepp_preprocess(ftepp)) if (!ftepp_preprocess(ftepp))
return false; 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; char *create = NULL;
/* use saner path for empty macros */ /* use saner path for empty macros */
if (!value) { if (!value) {
ftepp_add_define("__builtin__", name); ftepp_add_define(ftepp, "__builtin__", name);
return; return;
} }
@ -1766,26 +1766,27 @@ void ftepp_add_macro(const char *name, const char *value) {
vec_upload(create, value, strlen(value)); vec_upload(create, value, strlen(value));
vec_push (create, 0); vec_push (create, 0);
ftepp_preprocess_string("__builtin__", create); ftepp_preprocess_string(ftepp, "__builtin__", create);
vec_free (create); vec_free (create);
} }
bool ftepp_init() ftepp_t *ftepp_create()
{ {
ftepp_t *ftepp;
char minor[32]; char minor[32];
char major[32]; char major[32];
ftepp = ftepp_new(); ftepp = ftepp_new();
if (!ftepp) if (!ftepp)
return false; return NULL;
memset(minor, 0, sizeof(minor)); memset(minor, 0, sizeof(minor));
memset(major, 0, sizeof(major)); memset(major, 0, sizeof(major));
/* set the right macro based on the selected standard */ /* 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) { if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
ftepp_add_define(NULL, "__STD_FTEQCC__"); ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__");
/* 1.00 */ /* 1.00 */
major[0] = '"'; major[0] = '"';
major[1] = '1'; major[1] = '1';
@ -1795,15 +1796,15 @@ bool ftepp_init()
minor[1] = '0'; minor[1] = '0';
minor[2] = '"'; minor[2] = '"';
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) { } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
ftepp_add_define(NULL, "__STD_GMQCC__"); ftepp_add_define(ftepp, NULL, "__STD_GMQCC__");
sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR); snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR); snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) { } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) {
ftepp_add_define(NULL, "__STD_QCCX__"); ftepp_add_define(ftepp, NULL, "__STD_QCCX__");
sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR); snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR);
sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR); snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR);
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) {
ftepp_add_define(NULL, "__STD_QCC__"); ftepp_add_define(ftepp, NULL, "__STD_QCC__");
/* 1.0 */ /* 1.0 */
major[0] = '"'; major[0] = '"';
major[1] = '1'; major[1] = '1';
@ -1814,35 +1815,35 @@ bool ftepp_init()
minor[2] = '"'; minor[2] = '"';
} }
ftepp_add_macro("__STD_VERSION_MINOR__", minor); ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor);
ftepp_add_macro("__STD_VERSION_MAJOR__", major); 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; ppmacro *macro;
lex_ctx ctx = { "__builtin__", 0 }; lex_ctx ctx = { "__builtin__", 0 };
ctx.file = source; ctx.file = source;
macro = ppmacro_new(ctx, name); 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; return ftepp->output_string;
} }
void ftepp_flush() void ftepp_flush(ftepp_t *ftepp)
{ {
ftepp_flush_do(ftepp); ftepp_flush_do(ftepp);
} }
void ftepp_finish() void ftepp_finish(ftepp_t *ftepp)
{ {
if (!ftepp) if (!ftepp)
return; return;
ftepp_delete(ftepp); ftepp_delete(ftepp);
ftepp = NULL;
} }

103
gmqcc.h
View file

@ -300,7 +300,8 @@ void util_meminfo ();
bool util_filexists (const char *); bool util_filexists (const char *);
bool util_strupper (const char *); bool util_strupper (const char *);
bool util_strdigit (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_debug (const char *, const char *, ...);
void util_endianswap (void *, size_t, unsigned int); void util_endianswap (void *, size_t, unsigned int);
@ -317,15 +318,20 @@ int util_asprintf (char **ret, const char *fmt, ...);
#ifdef NOTRACK #ifdef NOTRACK
# define mem_a(x) malloc (x) # define mem_a(x) malloc (x)
# define mem_d(x) free ((void*)x) # define mem_d(x) free ((void*)x)
# define mem_r(x, n) realloc((void*)x, n) # define mem_r(x, n) realloc((void*)x, n)
# define mem_af(x,f,l) malloc (x)
#else #else
# define mem_a(x) util_memory_a((x), __LINE__, __FILE__) # define mem_a(x) util_memory_a((x), __LINE__, __FILE__)
# define mem_d(x) util_memory_d((void*)(x)) # define mem_d(x) util_memory_d((void*)(x))
# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__) # 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 */ #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 * A flexible vector implementation: all vector pointers contain some
* data about themselfs exactly - sizeof(vector_t) behind the pointer * data about themselfs exactly - sizeof(vector_t) behind the pointer
@ -374,14 +380,6 @@ typedef struct hash_table_t {
struct hash_node_t **table; struct hash_node_t **table;
} hash_table_t, *ht; } 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: * hashtable implementation:
* *
@ -413,52 +411,17 @@ typedef struct hash_set_t {
* util_htdel(foo); * util_htdel(foo);
*/ */
hash_table_t *util_htnew (size_t size); 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_htset (hash_table_t *ht, const char *key, void *value);
void util_htdel (hash_table_t *ht); void util_htdel (hash_table_t *ht);
size_t util_hthash(hash_table_t *ht, const char *key); 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_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_htget (hash_table_t *ht, const char *key);
void *util_htgeth(hash_table_t *ht, const char *key, size_t hash); 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 ===============================*/ /*============================ file.c ===============================*/
/*===================================================================*/ /*===================================================================*/
@ -466,10 +429,8 @@ void util_hsdel(hash_set_t *);
void fs_file_close (FILE *); void fs_file_close (FILE *);
int fs_file_error (FILE *); int fs_file_error (FILE *);
int fs_file_getc (FILE *); int fs_file_getc (FILE *);
int fs_file_flush (FILE *);
int fs_file_printf (FILE *, const char *, ...); int fs_file_printf (FILE *, const char *, ...);
int fs_file_puts (FILE *, const char *); int fs_file_puts (FILE *, const char *);
int fs_file_putc (FILE *, int);
int fs_file_seek (FILE *, long int, int); int fs_file_seek (FILE *, long int, int);
long int fs_file_tell (FILE *); 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 *); int fs_file_getline(char **, size_t *, FILE *);
/* directory handling */ /* directory handling */
int fs_dir_make (const char *);
DIR *fs_dir_open (const char *); DIR *fs_dir_open (const char *);
int fs_dir_close (DIR *); int fs_dir_close (DIR *);
struct dirent *fs_dir_read (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 ========================*/ /*===================== parser.c commandline ========================*/
/*===================================================================*/ /*===================================================================*/
struct parser_s;
bool parser_init (); struct parser_s *parser_create ();
bool parser_compile_file (const char *); bool parser_compile_file (struct parser_s *parser, const char *);
bool parser_compile_string(const char *, const char *, size_t); bool parser_compile_string(struct parser_s *parser, const char *, const char *, size_t);
bool parser_finish (const char *); bool parser_finish (struct parser_s *parser, const char *);
void parser_cleanup (); void parser_cleanup (struct parser_s *parser);
/*===================================================================*/ /*===================================================================*/
/*====================== ftepp.c commandline ========================*/ /*====================== ftepp.c commandline ========================*/
/*===================================================================*/ /*===================================================================*/
struct lex_file_s; struct lex_file_s;
struct ftepp_s;
typedef struct { typedef struct {
const char *name; const char *name;
char *(*func)(struct lex_file_s *); char *(*func)(struct lex_file_s *);
@ -1047,14 +1010,14 @@ typedef struct {
*/ */
#define FTEPP_PREDEF_COUNT 8 #define FTEPP_PREDEF_COUNT 8
bool ftepp_init (); struct ftepp_s *ftepp_create ();
bool ftepp_preprocess_file (const char *filename); bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename);
bool ftepp_preprocess_string(const char *name, const char *str); bool ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str);
void ftepp_finish (); void ftepp_finish (struct ftepp_s *ftepp);
const char *ftepp_get (); const char *ftepp_get (struct ftepp_s *ftepp);
void ftepp_flush (); void ftepp_flush (struct ftepp_s *ftepp);
void ftepp_add_define (const char *source, const char *name); void ftepp_add_define (struct ftepp_s *ftepp, const char *source, const char *name);
void ftepp_add_macro (const char *name, const char *value); void ftepp_add_macro (struct ftepp_s *ftepp, const char *name, const char *value);
extern const ftepp_predef_t ftepp_predefs[FTEPP_PREDEF_COUNT]; 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; 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) bool ir_value_set_string(ir_value *self, const char *str)
{ {
if (self->vtype != TYPE_STRING) if (self->vtype != TYPE_STRING)
return false; return false;
self->constval.vstring = ir_strdup(str); self->constval.vstring = util_strdupe(str);
self->hasvalue = true; self->hasvalue = true;
return true; return true;
} }
@ -1651,7 +1640,7 @@ void ir_phi_add(ir_instr* self, ir_block *b, ir_value *v)
* is doing something wrong. * is doing something wrong.
*/ */
irerror(self->context, "Invalid entry block for PHI"); irerror(self->context, "Invalid entry block for PHI");
abort(); exit(EXIT_FAILURE);
} }
pe.value = v; pe.value = v;
@ -3292,6 +3281,8 @@ static void gen_vector_defs(prog_section_def def, const char *name)
def.offset++; def.offset++;
component[len-1]++; component[len-1]++;
} }
mem_d(component);
} }
static void gen_vector_fields(prog_section_field fld, const char *name) 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++; fld.offset++;
component[len-1]++; component[len-1]++;
} }
mem_d(component);
} }
static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool islocal) 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) static int lex_fgetc(lex_file *lex)
{ {
if (lex->file) if (lex->file)
return fgetc(lex->file); return fs_file_getc(lex->file);
if (lex->open_string) { if (lex->open_string) {
if (lex->open_string_pos >= lex->open_string_length) if (lex->open_string_pos >= lex->open_string_length)
return EOF; return EOF;
@ -483,6 +483,9 @@ static bool lex_try_pragma(lex_file *lex)
lex->line = line; lex->line = line;
while (ch != '\n' && ch != EOF) while (ch != '\n' && ch != EOF)
ch = lex_getch(lex); ch = lex_getch(lex);
vec_free(command);
vec_free(param);
vec_free(pragma);
return true; return true;
unroll: unroll:
@ -495,13 +498,13 @@ unroll:
vec_free(command); vec_free(command);
lex_ungetch(lex, ' '); lex_ungetch(lex, ' ');
} }
if (command) { if (param) {
vec_pop(command); vec_pop(param);
while (vec_size(command)) { while (vec_size(param)) {
lex_ungetch(lex, (unsigned char)vec_last(command)); lex_ungetch(lex, (unsigned char)vec_last(param));
vec_pop(command); vec_pop(param);
} }
vec_free(command); vec_free(param);
lex_ungetch(lex, ' '); lex_ungetch(lex, ' ');
} }
if (pragma) { if (pragma) {
@ -1352,7 +1355,7 @@ int lex_do(lex_file *lex)
lex_tokench(lex, ch); lex_tokench(lex, ch);
nextch = lex_getch(lex); nextch = lex_getch(lex);
if (nextch == '=') { if (nextch == '=' || nextch == '*') {
lex_tokench(lex, nextch); lex_tokench(lex, nextch);
} else } else
lex_ungetch(lex, nextch); lex_ungetch(lex, nextch);
@ -1361,6 +1364,12 @@ int lex_do(lex_file *lex)
return (lex->tok.ttype = TOKEN_OPERATOR); 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)) if (isident_start(ch))
{ {
const char *v; const char *v;

17
lexer.h
View file

@ -166,18 +166,21 @@ typedef struct {
static const oper_info c_operators[] = { static const oper_info c_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */ { "(", 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, 17, OP_SUFFIX},
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX}, { "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX},
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 }, { ".", 2, opid1('.'), ASSOC_LEFT, 17, 0 },
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */ { "(", 0, opid1('('), ASSOC_LEFT, 17, 0 }, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */ { "[", 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, 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 }, */ /* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX }, */
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 }, { "*", 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; bool argend = false;
size_t itr; size_t itr;
char buffer[1024]; char buffer[1024];
char *redirout = NULL; char *redirout = NULL;
char *redirerr = NULL; char *redirerr = NULL;
char *config = NULL; char *config = NULL;
char *memdumpcols = NULL;
while (!argend && argc > 1) { while (!argend && argc > 1) {
char *argarg; char *argarg;
@ -223,6 +224,10 @@ static bool options_parse(int argc, char **argv) {
config = argarg; config = argarg;
continue; 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) */ /* show defaults (like pathscale) */
if (!strcmp(argv[0]+1, "show-defaults")) { if (!strcmp(argv[0]+1, "show-defaults")) {
@ -403,7 +408,7 @@ static bool options_parse(int argc, char **argv) {
return false; return false;
} }
if (isdigit(argarg[0])) { 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_OPTION_U32(OPTION_O) = val;
opts_setoptimlevel(val); opts_setoptimlevel(val);
} else { } else {
@ -541,12 +546,14 @@ static bool progs_nextline(char **out, size_t *alen,FILE *src) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
size_t itr; size_t itr;
int retval = 0; int retval = 0;
bool opts_output_free = false; bool opts_output_free = false;
bool operators_free = false; bool operators_free = false;
bool progs_src = false; bool progs_src = false;
FILE *outfile = NULL; FILE *outfile = NULL;
struct parser_s *parser = NULL;
struct ftepp_s *ftepp = NULL;
app_name = argv[0]; app_name = argv[0];
con_init (); con_init ();
@ -621,7 +628,7 @@ int main(int argc, char **argv) {
} }
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (!parser_init()) { if (!(parser = parser_create())) {
con_err("failed to initialize parser\n"); con_err("failed to initialize parser\n");
retval = 1; retval = 1;
goto cleanup; goto cleanup;
@ -629,7 +636,7 @@ int main(int argc, char **argv) {
} }
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) { if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
if (!ftepp_init()) { if (!(ftepp = ftepp_create())) {
con_err("failed to initialize parser\n"); con_err("failed to initialize parser\n");
retval = 1; retval = 1;
goto cleanup; goto cleanup;
@ -644,7 +651,7 @@ int main(int argc, char **argv) {
/* add macros */ /* add macros */
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) { if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
for (itr = 0; itr < vec_size(ppems); itr++) { 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); mem_d(ppems[itr].name);
/* can be null */ /* can be null */
@ -703,6 +710,7 @@ srcdone:
con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual")); con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items)); con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
} }
for (itr = 0; itr < vec_size(items); ++itr) { for (itr = 0; itr < vec_size(items); ++itr) {
if (!OPTS_OPTION_BOOL(OPTION_QUIET) && if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) !OPTS_OPTION_BOOL(OPTION_PP_ONLY))
@ -717,33 +725,33 @@ srcdone:
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
const char *out; const char *out;
if (!ftepp_preprocess_file(items[itr].filename)) { if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
retval = 1; retval = 1;
goto cleanup; goto cleanup;
} }
out = ftepp_get(); out = ftepp_get(ftepp);
if (out) if (out)
fs_file_printf(outfile, "%s", out); fs_file_printf(outfile, "%s", out);
ftepp_flush(); ftepp_flush(ftepp);
} }
else { else {
if (OPTS_FLAG(FTEPP)) { if (OPTS_FLAG(FTEPP)) {
const char *data; const char *data;
if (!ftepp_preprocess_file(items[itr].filename)) { if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
retval = 1; retval = 1;
goto cleanup; goto cleanup;
} }
data = ftepp_get(); data = ftepp_get(ftepp);
if (vec_size(data)) { 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; retval = 1;
goto cleanup; goto cleanup;
} }
} }
ftepp_flush(); ftepp_flush(ftepp);
} }
else { else {
if (!parser_compile_file(items[itr].filename)) { if (!parser_compile_file(parser, items[itr].filename)) {
retval = 1; retval = 1;
goto cleanup; goto cleanup;
} }
@ -756,9 +764,10 @@ srcdone:
} }
} }
ftepp_finish(); ftepp_finish(ftepp);
ftepp = NULL;
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { 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; retval = 1;
goto cleanup; goto cleanup;
} }
@ -778,13 +787,14 @@ srcdone:
cleanup: cleanup:
util_debug("COM", "cleaning ...\n"); util_debug("COM", "cleaning ...\n");
ftepp_finish(); if (ftepp)
ftepp_finish(ftepp);
con_close(); con_close();
vec_free(items); vec_free(items);
vec_free(ppems); vec_free(ppems);
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
parser_cleanup(); parser_cleanup(parser);
if (opts_output_free) if (opts_output_free)
mem_d(OPTS_OPTION_STR(OPTION_OUTPUT)); mem_d(OPTS_OPTION_STR(OPTION_OUTPUT));
if (operators_free) 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, BAIL_ON_WERROR, true);
opts_set(opts.flags, LEGACY_VECTOR_MATHS, true); opts_set(opts.flags, LEGACY_VECTOR_MATHS, true);
opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true); opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true);
} }
void opts_backup_non_Wall() { 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_STR(OPTION_OUTPUT) = (char*)output;
OPTS_OPTION_U32(OPTION_STANDARD) = standard; OPTS_OPTION_U32(OPTION_STANDARD) = standard;
OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize; OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = 16;
} }
static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) { static bool opts_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) { static bool opts_ini_bool(const char *value) {
if (!strcmp(value, "true")) return true; if (!strcmp(value, "true")) return true;
if (!strcmp(value, "false")) return false; 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) { 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(G)
GMQCC_DEFINE_FLAG(STANDARD) GMQCC_DEFINE_FLAG(STANDARD)
GMQCC_DEFINE_FLAG(DEBUG) GMQCC_DEFINE_FLAG(DEBUG)
GMQCC_DEFINE_FLAG(MEMDUMPCOLS)
GMQCC_DEFINE_FLAG(MEMCHK) GMQCC_DEFINE_FLAG(MEMCHK)
GMQCC_DEFINE_FLAG(DUMPFIN) GMQCC_DEFINE_FLAG(DUMPFIN)
GMQCC_DEFINE_FLAG(DUMP) 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. * Extraction abilities. These work as you expect them to.
*/ */
bool pak_extract_one(pak_file_t *pak, const char *file) { bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) {
pak_directory_t *dir = NULL; pak_directory_t *dir = NULL;
unsigned char *dat = NULL; unsigned char *dat = NULL;
char *local = NULL;
FILE *out; FILE *out;
if (!pak_exists(pak, file, &dir)) { 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); 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 * Now create the file, if this operation fails. Then abort
* It shouldn't fail though. * It shouldn't fail though.
*/ */
if (!(out = fs_file_open(file, "wb"))) { if (!(out = fs_file_open(local, "wb"))) {
mem_d(dat); mem_d(dat);
return false; return false;
} }
/* free memory for directory string */
mem_d(local);
/* read */ /* read */
fs_file_seek (pak->handle, dir->pos, SEEK_SET); 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)) if (!fs_dir_make(dir))
return false; return false;
if (fs_dir_change(dir))
return false;
for (itr = 0; itr < vec_size(pak->directories); itr++) { for (itr = 0; itr < vec_size(pak->directories); itr++) {
if (!pak_extract_one(pak, pak->directories[itr].name)) if (!pak_extract_one(pak, pak->directories[itr].name, dir))
return false; return false;
} }
@ -361,7 +364,7 @@ bool pak_insert_one(pak_file_t *pak, const char *file) {
return false; return false;
} }
strcpy(dir.name, file); strncpy(dir.name, file, strlen(file));
/* /*
* Allocate some memory for loading in the data that will be * Allocate some memory for loading in the data that will be
@ -477,7 +480,6 @@ int main(int argc, char **argv) {
bool extract = true; bool extract = true;
char *redirout = (char*)stdout; char *redirout = (char*)stdout;
char *redirerr = (char*)stderr; char *redirerr = (char*)stderr;
char *directory = NULL;
char *file = NULL; char *file = NULL;
char **files = NULL; char **files = NULL;
pak_file_t *pak = NULL; pak_file_t *pak = NULL;
@ -498,8 +500,6 @@ int main(int argc, char **argv) {
continue; continue;
if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false)) if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false))
continue; continue;
if (parsecmd("directory", &argc, &argv, &directory, 1, false))
continue;
if (parsecmd("file", &argc, &argv, &file, 1, false)) if (parsecmd("file", &argc, &argv, &file, 1, false))
continue; continue;
@ -542,7 +542,7 @@ int main(int argc, char **argv) {
return EXIT_FAILURE; 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); con_err("failed to extract PAK %s (files may be missing)\n", file);
pak_close(pak); pak_close(pak);
vec_free(files); vec_free(files);
@ -562,13 +562,6 @@ int main(int argc, char **argv) {
return EXIT_FAILURE; 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++) { for (iter = 0; iter < vec_size(files); iter++) {
if (!(pak_insert_one(pak, files[iter]))) { if (!(pak_insert_one(pak, files[iter]))) {
con_err("failed inserting %s for PAK %s\n", files[iter], file); 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 <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <math.h>
#include "gmqcc.h" #include "gmqcc.h"
#include "lexer.h" #include "lexer.h"
@ -34,7 +35,7 @@
#define PARSER_HT_SIZE 128 #define PARSER_HT_SIZE 128
#define TYPEDEF_HT_SIZE 16 #define TYPEDEF_HT_SIZE 16
typedef struct { typedef struct parser_s {
lex_file *lex; lex_file *lex;
int tok; int tok;
@ -46,6 +47,8 @@ typedef struct {
ast_value **imm_vector; ast_value **imm_vector;
size_t translated; size_t translated;
ht ht_imm_string;
/* must be deleted first, they reference immediates and values */ /* must be deleted first, they reference immediates and values */
ast_value **accessors; 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) 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; 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) { for (i = 0; i < vec_size(parser->imm_string); ++i) {
if (!strcmp(parser->imm_string[i]->constval.vstring, str)) if (!strcmp(parser->imm_string[i]->constval.vstring, str))
return parser->imm_string[i]; return parser->imm_string[i];
} }
*/
if (dotranslate) { if (dotranslate) {
char name[32]; char name[32];
snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); 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->hasvalue = true;
out->constval.vstring = parser_strdup(str); out->constval.vstring = parser_strdup(str);
vec_push(parser->imm_string, out); vec_push(parser->imm_string, out);
util_htseth(parser->ht_imm_string, str, hash, out);
return out; return out;
} }
@ -379,6 +393,9 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t
return NULL; return NULL;
} }
/* include intrinsics */
#include "intrin.h"
typedef struct typedef struct
{ {
size_t etype; /* 0 = expression, others are operators */ 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; return false;
} }
break; break;
case opid1('%'): 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('%','='): case opid2('%','='):
compile_error(ctx, "qc does not have a modulo operator"); compile_error(ctx, "%= is unimplemented");
return false; return false;
case opid1('|'): case opid1('|'):
case opid1('&'): case opid1('&'):
if (NotSameType(TYPE_FLOAT)) { 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]); out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]);
break; 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 */ case opid3('<','=','>'): /* -1, 0, or 1 */
if (NotSameType(TYPE_FLOAT)) { if (NotSameType(TYPE_FLOAT)) {
ast_type_to_string(exprs[0], ty1, sizeof(ty1)); 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])) if(CanConstFold1(exprs[0]))
out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0)); out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0));
else 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; break;
} }
#undef NotSameType #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")) { if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) {
var = (ast_expression*)intrinsic_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) { if (!var) {
char *correct = NULL; 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) static bool parse_enum(parser_t *parser)
{ {
bool flag = false;
bool reverse = false;
qcfloat num = 0; qcfloat num = 0;
ast_value **values = NULL; ast_value **values = NULL;
ast_value *var = NULL; ast_value *var = NULL;
@ -3661,11 +3735,37 @@ static bool parse_enum(parser_t *parser)
ast_expression *old; ast_expression *old;
if (!parser_next(parser) || parser->tok != '{') { if (!parser_next(parser) || (parser->tok != '{' && parser->tok != ':')) {
parseerror(parser, "expected `{` after `enum` keyword"); parseerror(parser, "expected `{` or `:` after `enum` keyword");
return false; 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) { while (true) {
if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { if (!parser_next(parser) || parser->tok != TOKEN_IDENT) {
if (parser->tok == '}') { if (parser->tok == '}') {
@ -3689,8 +3789,9 @@ static bool parse_enum(parser_t *parser)
vec_push(values, var); vec_push(values, var);
var->cvq = CV_CONST; var->cvq = CV_CONST;
var->hasvalue = true; 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); parser_addglobal(parser, var->name, (ast_expression*)var);
if (!parser_next(parser)) { 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 != '}') { if (parser->tok != '}') {
parseerror(parser, "internal error: breaking without `}`"); parseerror(parser, "internal error: breaking without `}`");
goto onerror; goto onerror;
@ -4696,6 +4804,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var)
on_error: on_error:
if (argcounter) if (argcounter)
mem_d(argcounter); mem_d(argcounter);
if (varparam)
ast_delete(varparam);
ast_delete(var); ast_delete(var);
for (i = 0; i < vec_size(params); ++i) for (i = 0; i < vec_size(params); ++i)
ast_delete(params[i]); ast_delete(params[i]);
@ -5814,16 +5924,15 @@ static void generate_checksum(parser_t *parser)
code_crc = crc; code_crc = crc;
} }
static parser_t *parser; parser_t *parser_create()
bool parser_init()
{ {
parser_t *parser;
lex_ctx empty_ctx; lex_ctx empty_ctx;
size_t i; size_t i;
parser = (parser_t*)mem_a(sizeof(parser_t)); parser = (parser_t*)mem_a(sizeof(parser_t));
if (!parser) if (!parser)
return false; return NULL;
memset(parser, 0, sizeof(*parser)); memset(parser, 0, sizeof(*parser));
@ -5836,7 +5945,7 @@ bool parser_init()
if (!parser->assign_op) { if (!parser->assign_op) {
printf("internal error: initializing parser: failed to find assign operator\n"); printf("internal error: initializing parser: failed to find assign operator\n");
mem_d(parser); mem_d(parser);
return false; return NULL;
} }
vec_push(parser->variables, parser->htfields = util_htnew(PARSER_HT_SIZE)); 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->aliases = util_htnew(PARSER_HT_SIZE);
parser->ht_imm_string = util_htnew(512);
/* corrector */ /* corrector */
vec_push(parser->correct_variables, correct_trie_new()); vec_push(parser->correct_variables, correct_trie_new());
vec_push(parser->correct_variables_score, NULL); vec_push(parser->correct_variables_score, NULL);
@ -5872,10 +5983,11 @@ bool parser_init()
} else { } else {
parser->reserved_version = NULL; parser->reserved_version = NULL;
} }
return true;
return parser;
} }
bool parser_compile() bool parser_compile(parser_t *parser)
{ {
/* initial lexer/parser state */ /* initial lexer/parser state */
parser->lex->flags.noops = true; parser->lex->flags.noops = true;
@ -5907,27 +6019,27 @@ bool parser_compile()
return !compile_errors; 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); parser->lex = lex_open(filename);
if (!parser->lex) { if (!parser->lex) {
con_err("failed to open file \"%s\"\n", filename); con_err("failed to open file \"%s\"\n", filename);
return false; 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); parser->lex = lex_open_string(str, len, name);
if (!parser->lex) { if (!parser->lex) {
con_err("failed to create lexer for string \"%s\"\n", name); con_err("failed to create lexer for string \"%s\"\n", name);
return false; return false;
} }
return parser_compile(); return parser_compile(parser);
} }
void parser_cleanup() void parser_cleanup(parser_t *parser)
{ {
size_t i; size_t i;
for (i = 0; i < vec_size(parser->accessors); ++i) { for (i = 0; i < vec_size(parser->accessors); ++i) {
@ -5957,6 +6069,7 @@ void parser_cleanup()
vec_free(parser->functions); vec_free(parser->functions);
vec_free(parser->imm_vector); vec_free(parser->imm_vector);
vec_free(parser->imm_string); vec_free(parser->imm_string);
util_htdel(parser->ht_imm_string);
vec_free(parser->imm_float); vec_free(parser->imm_float);
vec_free(parser->globals); vec_free(parser->globals);
vec_free(parser->fields); vec_free(parser->fields);
@ -5998,10 +6111,12 @@ void parser_cleanup()
util_htdel(parser->aliases); util_htdel(parser->aliases);
intrin_intrinsics_destroy(parser);
mem_d(parser); mem_d(parser);
} }
bool parser_finish(const char *output) bool parser_finish(parser_t *parser, const char *output)
{ {
size_t i; size_t i;
ir_builder *ir; 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 */ #endif /*! _WIN32 */
#define TASK_COMPILE 0 #define TASK_COMPILE 0
#define TASK_EXECUTE 1 #define TASK_EXECUTE 1
/* /*
* Task template system: * Task template system:
* templates are rules for a specific test, used to create a "task" that * 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) { 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 desclen = 0;
size_t filelen = 0;
char **destval = NULL; char **destval = NULL;
if (!tmpl) if (!tmpl)
@ -315,7 +316,7 @@ bool task_template_generate(task_template_t *tmpl, char tag, const char *file, s
if (strchr(value, '\n')) if (strchr(value, '\n'))
*strrchr(value, '\n')='\0'; *strrchr(value, '\n')='\0';
else /* cppcheck: possible nullpointer dereference */ else /* cppcheck: possible nullpointer dereference */
abort(); exit(EXIT_FAILURE);
/* /*
* Now allocate and set the actual value for the specific tag. Which * 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; pad[0] = desclen;
} }
if ((filelen = strlen(file)) > pad[2])
pad[2] = filelen;
return true; return true;
} }
@ -428,7 +432,7 @@ bool task_template_parse(const char *file, task_template_t *tmpl, FILE *fp, size
if (strrchr(value, '\n')) if (strrchr(value, '\n'))
*strrchr(value, '\n')='\0'; *strrchr(value, '\n')='\0';
else /* cppcheck: possible null pointer dereference */ else /* cppcheck: possible null pointer dereference */
abort(); exit(EXIT_FAILURE);
vec_push(tmpl->comparematch, util_strdup(value)); 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); con_err("template compile warning: %s erroneous tag `E:` when only failing\n", file);
if (tmpl->comparematch) if (tmpl->comparematch)
con_err("template compile warning: %s erroneous tag `M:` when only failing\n", file); 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 { } else {
con_err("template compile error: %s invalid procedure type: %s\n", file, tmpl->proceduretype); con_err("template compile error: %s invalid procedure type: %s\n", file, tmpl->proceduretype);
goto failure; goto failure;
@ -644,10 +654,10 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
char buffer[4096]; char buffer[4096];
size_t found = 0; size_t found = 0;
dir = opendir(curdir); dir = fs_dir_open(curdir);
while ((files = readdir(dir))) { while ((files = fs_dir_read(dir))) {
snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name); snprintf(buffer, sizeof(buffer), "%s/%s", curdir, files->d_name);
if (stat(buffer, &directory) == -1) { if (stat(buffer, &directory) == -1) {
con_err("internal error: stat failed, aborting\n"); 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 * Generate a temportary file name for the output binary
* so we don't trample over an existing one. * 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 * 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 * which will be refered to with a handle in the task for
* reading the data from the pipe. * reading the data from the pipe.
*/ */
if (qcflags) { if (strcmp(tmpl->proceduretype, "-pp")) {
if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) { if (qcflags) {
snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s", if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
task_bins[TASK_COMPILE], snprintf(buf, sizeof(buf), "%s %s/%s %s %s -o %s",
curdir, task_bins[TASK_COMPILE],
tmpl->sourcefile, curdir,
qcflags, tmpl->sourcefile,
tmpl->compileflags, qcflags,
tmpl->tempfilename 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 { } else {
snprintf(buf, sizeof(buf), "%s %s/%s %s/%s %s %s -o %s", if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) {
task_bins[TASK_COMPILE], snprintf(buf, sizeof(buf), "%s %s/%s %s -o %s",
curdir, task_bins[TASK_COMPILE],
defs, curdir,
curdir, tmpl->sourcefile,
tmpl->sourcefile, tmpl->compileflags,
qcflags, tmpl->tempfilename
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 { } else {
/* Preprocessing (qcflags mean shit all here we don't allow them) */
if (tmpl->testflags && !strcmp(tmpl->testflags, "-no-defs")) { 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], task_bins[TASK_COMPILE],
curdir, curdir,
tmpl->sourcefile, tmpl->sourcefile,
tmpl->compileflags,
tmpl->tempfilename tmpl->tempfilename
); );
} else { } 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], task_bins[TASK_COMPILE],
curdir, curdir,
defs, defs,
curdir, curdir,
tmpl->sourcefile, tmpl->sourcefile,
tmpl->compileflags,
tmpl->tempfilename tmpl->tempfilename
); );
} }
@ -777,7 +809,7 @@ bool task_propagate(const char *curdir, size_t *pad, const char *defs) {
found found
); );
closedir(dir); fs_dir_close(dir);
return success; return success;
} }
@ -790,9 +822,9 @@ void task_precleanup(const char *curdir) {
struct dirent *files; struct dirent *files;
char buffer[4096]; 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") || if (strstr(files->d_name, "TMP") ||
strstr(files->d_name, ".stdout") || strstr(files->d_name, ".stdout") ||
strstr(files->d_name, ".stderr")) 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) { void task_destroy(void) {
@ -854,40 +886,50 @@ void task_destroy(void) {
/* /*
* This executes the QCVM task for a specificly compiled progs.dat * This executes the QCVM task for a specificly compiled progs.dat
* using the template passed into it for call-flags and user defined * 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; bool success = true;
FILE *execute; FILE *execute;
char buffer[4096]; char buffer[4096];
memset (buffer,0,sizeof(buffer)); memset (buffer,0,sizeof(buffer));
/* if (strcmp(tmpl->proceduretype, "-pp")) {
* Drop the execution flags for the QCVM if none where /*
* actually specified. * Drop the execution flags for the QCVM if none where
*/ * actually specified.
if (!strcmp(tmpl->executeflags, "$null")) { */
snprintf(buffer, sizeof(buffer), "%s %s", if (!strcmp(tmpl->executeflags, "$null")) {
task_bins[TASK_EXECUTE], snprintf(buffer, sizeof(buffer), "%s %s",
tmpl->tempfilename 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 { } else {
snprintf(buffer, sizeof(buffer), "%s %s %s", /*
task_bins[TASK_EXECUTE], * we're preprocessing, which means we need to read int
tmpl->executeflags, * the produced file and do some really weird shit.
tmpl->tempfilename */
); 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 * Now lets read the lines and compare them to the matches we expect
* and handle accordingly. * and handle accordingly.
@ -913,6 +955,13 @@ bool task_execute(task_template_t *tmpl, char ***line) {
if (strrchr(data, '\n')) if (strrchr(data, '\n'))
*strrchr(data, '\n') = '\0'; *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 (vec_size(tmpl->comparematch) > compare) {
if (strcmp(data, tmpl->comparematch[compare++])) if (strcmp(data, tmpl->comparematch[compare++]))
success = false; success = false;
@ -933,10 +982,25 @@ bool task_execute(task_template_t *tmpl, char ***line) {
mem_d(data); mem_d(data);
data = NULL; data = NULL;
} }
pclose(execute);
if (strcmp(tmpl->proceduretype, "-pp"))
pclose(execute);
else
fs_file_close(execute);
return success; 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 schedualizes all tasks and actually runs them individually
* this is generally easy for just -compile variants. For compile and * 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> #include <math.h>
void task_schedualize(size_t *pad) { void task_schedualize(size_t *pad) {
char space[2][64];
bool execute = false; bool execute = false;
char *data = NULL; char *data = NULL;
char **match = NULL; char **match = NULL;
@ -952,15 +1017,21 @@ void task_schedualize(size_t *pad) {
size_t i = 0; size_t i = 0;
size_t j = 0; size_t j = 0;
snprintf(space[0], sizeof(space[0]), "%d", (int)vec_size(task_tasks));
for (; i < vec_size(task_tasks); i++) { 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); 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 * Generate a task from thin air if it requires execution in
* the QCVM. * 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 * 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; task_tasks[i].compiled = false;
execute = false; execute = false;
} }
fs_file_flush(task_tasks[i].stdoutlog);
} }
while (fs_file_getline(&data, &size, task_tasks[i].runhandles[2]) != EOF) { 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_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")) { 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->description,
task_tasks[i].tmpl->tempfilename, (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
task_tasks[i].tmpl->tempfilename, task_tasks[i].tmpl->rulesfile,
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; continue;
} }
if (!execute) { if (!execute) {
con_out("succeeded: `%s` %*s\n", con_out("succeeded: `%s` %*s %*s\n",
task_tasks[i].tmpl->description, task_tasks[i].tmpl->description,
(pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
(strlen(task_tasks[i].tmpl->rulesfile) - pad[1]), task_tasks[i].tmpl->rulesfile,
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; continue;
@ -1023,14 +1093,24 @@ void task_schedualize(size_t *pad) {
/* /*
* If we made it here that concludes the task is to be executed * 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; 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->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 * handler for the all the given matches in the template file and
* what was actually returned from executing. * 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(task_tasks[i].tmpl->comparematch),
vec_size(match) vec_size(match)
); );
@ -1046,10 +1126,10 @@ void task_schedualize(size_t *pad) {
char *select = task_tasks[i].tmpl->comparematch[d]; char *select = task_tasks[i].tmpl->comparematch[d];
size_t length = 40 - strlen(select); size_t length = 40 - strlen(select);
con_err(" Expected: \"%s\"", select); con_out(" Expected: \"%s\"", select);
while (length --) while (length --)
con_err(" "); con_out(" ");
con_err("| Got: \"%s\"\n", (d >= vec_size(match)) ? "<<nothing else to compare>>" : match[d]); 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)) { 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++) { 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)] match[d + vec_size(task_tasks[i].tmpl->comparematch)]
); );
} }
@ -1075,11 +1155,12 @@ void task_schedualize(size_t *pad) {
mem_d(match[j]); mem_d(match[j]);
vec_free(match); vec_free(match);
con_out("succeeded: `%s` %*s\n", con_out("succeeded: `%s` %*s %*s\n",
task_tasks[i].tmpl->description, task_tasks[i].tmpl->description,
(pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (pad[0] + pad[1] - strlen(task_tasks[i].tmpl->description)) + (strlen(task_tasks[i].tmpl->rulesfile) - pad[1]),
(strlen(task_tasks[i].tmpl->rulesfile) - pad[1]), task_tasks[i].tmpl->rulesfile,
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"; static const char *default_defs = "defs.qh";
size_t pad[] = { 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; float (string, string) strcmp = #11;
vector (vector) normalize = #12; vector (vector) normalize = #12;
float (float) sqrt = #13; 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 // this behaviour is confusing, but I like that
// we support it. // we support it.
__ = (__ - 1), __ = (__ - 1),
@ -27,6 +27,20 @@ enum {
N 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() { void main() {
print(ftos(A), "\n"); print(ftos(A), "\n");
print(ftos(B), "\n"); print(ftos(B), "\n");
@ -42,4 +56,13 @@ void main() {
print(ftos(L), "\n"); print(ftos(L), "\n");
print(ftos(M), "\n"); print(ftos(M), "\n");
print(ftos(N), "\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: 11
M: 12 M: 12
M: 13 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 I: uninit.qc
D: catch another case of unused vector accesses - should fail D: catch another case of unused vector accesses
T: -fail T: -fail
C: -std=fteqcc -Wall -Werror -DUNINIT 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)) if (!u8_analyze(_s, &st, &ln, &ch, 0x10))
ch = 0; ch = 0;
if (_end) else if (_end)
*_end = _s + st + ln; *_end = _s + st + ln;
return ch; 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)) if (!u8_analyze(_s, &st, &ln, &ch, _maxlen))
ch = 0; ch = 0;
if (_end) else if (_end)
*_end = _s + st + ln; *_end = _s + st + ln;
return ch; return ch;
} }

267
util.c
View file

@ -30,6 +30,8 @@ uint64_t mem_ab = 0;
uint64_t mem_db = 0; uint64_t mem_db = 0;
uint64_t mem_at = 0; uint64_t mem_at = 0;
uint64_t mem_dt = 0; uint64_t mem_dt = 0;
uint64_t mem_pk = 0;
uint64_t mem_hw = 0;
struct memblock_t { struct memblock_t {
const char *file; const char *file;
@ -39,6 +41,12 @@ struct memblock_t {
struct memblock_t *prev; 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; static struct memblock_t *mem_start = NULL;
void *util_memory_a(size_t byte, unsigned int line, const char *file) { 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_at++;
mem_ab += info->byte; mem_ab += info->byte;
mem_hw += info->byte;
PEAK_MEM;
return data; return data;
} }
@ -67,6 +78,7 @@ void util_memory_d(void *ptrn) {
info = ((struct memblock_t*)ptrn - 1); info = ((struct memblock_t*)ptrn - 1);
mem_db += info->byte; mem_db += info->byte;
mem_hw -= info->byte;
mem_dt++; mem_dt++;
if (info->prev) 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_start = newinfo;
mem_ab -= oldinfo->byte; mem_ab -= oldinfo->byte;
mem_hw -= oldinfo->byte;
mem_ab += newinfo->byte; mem_ab += newinfo->byte;
mem_hw += newinfo->byte;
PEAK_MEM;
free(oldinfo); free(oldinfo);
return newinfo+1; 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() { void util_meminfo() {
struct memblock_t *info; struct memblock_t *info;
if (!OPTS_OPTION_BOOL(OPTION_MEMCHK))
return;
for (info = mem_start; info; info = info->next) { if (OPTS_OPTION_BOOL(OPTION_DEBUG)) {
util_debug("MEM", "lost: % 8u (bytes) at %s:%u\n", for (info = mem_start; info; info = info->next) {
info->byte, con_out("lost: %u (bytes) at %s:%u\n",
info->file, info->byte,
info->line); info->file,
info->line);
util_dumpmem(info, OPTS_OPTION_U16(OPTION_MEMDUMPCOLS));
}
} }
util_debug("MEM", "Memory information:\n\ if (OPTS_OPTION_BOOL(OPTION_DEBUG) ||
Total allocations: %llu\n\ OPTS_OPTION_BOOL(OPTION_MEMCHK)) {
Total deallocations: %llu\n\ con_out("Memory information:\n\
Total allocated: %llu (bytes)\n\ Total allocations: %llu\n\
Total deallocated: %llu (bytes)\n\ Total deallocations: %llu\n\
Leaks found: lost %llu (bytes) in %d allocations\n", Total allocated: %f (MB)\n\
mem_at, mem_dt, Total deallocated: %f (MB)\n\
mem_ab, mem_db, Total peak memory: %f (MB)\n\
(mem_ab - mem_db), Total leaked memory: %f (MB) in %llu allocations\n",
(mem_at - mem_dt) 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 * Some string utility functions, because strdup uses malloc, and we want
* to track all memory (without replacing malloc). * 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; size_t len = 0;
char *ptr = NULL; char *ptr = NULL;
/* in case of -DNOTRACK */
(void)file;
(void)line;
if (!s) if (!s)
return NULL; 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); memcpy(ptr, s, len);
ptr[len] = '\0'; 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); util_swap64((uint32_t*)_data, length>>3);
return; return;
default: abort(); /* please blow the fuck up! */ default: exit(EXIT_FAILURE); /* please blow the fuck up! */
} }
# endif # endif
#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)))) if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
return NULL; return NULL;
if (!(node->key = util_strdup(key))) { if (!(node->key = util_strdupe(key))) {
mem_d(node); mem_d(node);
return NULL; 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 * Free all allocated data in a hashtable, this is quite the amount
* of work. * of work.
*/ */
void util_htdel(hash_table_t *ht) { void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
size_t i = 0; size_t i = 0;
for (; i < ht->size; i++) { for (; i < ht->size; i++) {
hash_node_t *n = ht->table[i]; hash_node_t *n = ht->table[i];
@ -558,6 +630,8 @@ void util_htdel(hash_table_t *ht) {
while (n) { while (n) {
if (n->key) if (n->key)
mem_d(n->key); mem_d(n->key);
if (callback)
callback(n->value);
p = n; p = n;
n = n->next; n = n->next;
mem_d(p); mem_d(p);
@ -569,146 +643,33 @@ void util_htdel(hash_table_t *ht) {
mem_d(ht); mem_d(ht);
} }
/* void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
* A basic implementation of a hash-set. Unlike a hashtable, a hash hash_node_t **pair = &ht->table[bin];
* set doesn't maintain key-value pairs. It simply maintains a key hash_node_t *tmp;
* that can be set, removed, and checked for.
*
* See EXPOSED interface comment below
*/
#define GMQCC_HASHSET_PRIME0 0x0049
#define GMQCC_HASHSET_PRIME1 0x1391
static int util_hsput(hash_set_t *set, void *item) { while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
size_t hash = (size_t)item; /* shouldn't drop the bits */ pair = &(*pair)->next;
size_t iter;
/* a == 0 || a == 1 */ tmp = *pair;
if (hash >> 1) if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
return -1; return;
iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash); if (cb)
(*cb)(tmp->value);
/* while (set->items[iter] != 0 && set->items[iter] != 1) */ *pair = tmp->next;
while (!(set->items[iter] >> 1)) { mem_d(tmp->key);
if (set->items[iter] == hash) mem_d(tmp);
return 0;
iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
}
set->total ++;
set->items[iter] = hash;
return 1;
} }
static void util_hsupdate(hash_set_t *set) { void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
size_t *old; util_htrmh(ht, key, util_hthash(ht, key), cb);
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_htdel(hash_table_t *ht) {
* EXPOSED interface: all of these functions are exposed to the outside util_htrem(ht, NULL);
* for use. The stuff above is static because it's the "internal" mechanics
* for syncronizing the set for updating, and putting data into the set.
*/
int util_hsadd(hash_set_t *set, void *item) {
int run = util_hsput(set, item); /* inlined */
util_hsupdate(set);
return run;
} }
/* remove item in set */
int util_hsrem(hash_set_t *set, void *item) {
size_t hash = (size_t)item;
size_t iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
while (set->items[iter]) {
if (set->items[iter] == hash) {
set->items[iter] = 1;
set->total --;
return 1;
}
iter = set->mask & (iter + GMQCC_HASHSET_PRIME1);
}
return 0;
}
/* check if item is set */
int util_hshas(hash_set_t *set, void *item) {
size_t hash = (size_t)item;
size_t iter = set->mask & (GMQCC_HASHSET_PRIME0 * hash);
while (set->items[iter]) {
if (set->items[iter] == hash)
return 1;
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 * Portable implementation of vasprintf/asprintf. Assumes vsnprintf
* exists, otherwise compiler error. * exists, otherwise compiler error.