diff --git a/.gitignore b/.gitignore index 596972e..342039c 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,12 @@ testsuite qcvm gmqcc +pak -distro/arch/* +distro/archlinux/* +distro/archbsd/* !distro/archlinux/git/PKGBUILD !distro/archlinux/release/PKGBUILD !distro/archbsd/release/PKGBUILD !distro/archbsd/git/PKGBUILD -!distro/arch/this/Makefile +!distro/archlinux/this/Makefile diff --git a/CHANGES b/CHANGES index 7a0013b..0b88fd9 100644 --- a/CHANGES +++ b/CHANGES @@ -1,10 +1,12 @@ -Release v0.2.4 +Release v0.2.9 * Preprocessor: - __VA_ARGS__ support + _ __VA_ARGS__ indexing - Predefined macros like __DATE__, __TIME__, ... (check the manpage for a full list) - Signed numbers as single token in the - Fixes some issues with #if operations on macros. + - Speed improvements * Language: - Untyped `nil` keyword. - Removed the `noreturn` keyword. @@ -21,6 +23,8 @@ Release v0.2.4 - Type restricted variadict parameters: ie: void print(string...); - Accessing varargs from QC via: ...(index, type) + - New operators: ** (exponentiation), % (modulo), etc + - Enumeration attributes: flag, reverse * Compilation: - Various optimizations and progs-size reductions. - A new spell-checking algorithm tries to hint you at existing @@ -29,16 +33,35 @@ Release v0.2.4 have been solved in both DP and our own executor. A new compatbility option (enabled by default) has been added for now: -flegacy-vector-maths - * qcvm: + - Compiler intrinsics: __builtin_floor, __builtin_mod, + __builtin_exp, __builtin_isnan + - Improved memory tracing + - Speed improvements + * QCVM: - Improved commandline argument handling. - More builtins: sqrt(), normalize() * Commandline: + - Nicer memory dumps - Support for making individual warnings an error - via -Werror- - added --add-info * Testsuite: - Support for QCFLAGS to run tests with several additional flags. + - Added support for preprocessor tests + - Added preprocessor tests + - Added defs.qh (auto included) for qcvm definitions + * Syntax Highlighting: + - Added various syntax highlighting description files for + various text editors / integrated development envirorments, + including support for: geany, kate, kwrite, kdevelop, QtCreator, + gtksourceview, gedit, sany, nano, jedit + * Build: + - Build scripts for building debian, archlinux and archbsd + packages for x86, and x86_64. + - Makefile targets for gource visualization, and render of + gource visualization. + 2012-12-27 Hotfix v0.2.2 * Liferanges diff --git a/Makefile b/Makefile index 410a97d..20152c5 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ DESTDIR := +OPTIONAL:= PREFIX := /usr/local BINDIR := $(PREFIX)/bin DATADIR := $(PREFIX)/share @@ -9,7 +10,7 @@ CYGWIN = $(findstring CYGWIN, $(UNAME)) MINGW = $(findstring MINGW32, $(UNAME)) CC ?= clang -CFLAGS += -Wall -Wextra -Werror -I. -fno-strict-aliasing -fsigned-char +CFLAGS += -Wall -Wextra -Werror -I. -fno-strict-aliasing -fsigned-char $(OPTIONAL) ifneq ($(shell git describe --always 2>/dev/null),) CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\"" endif @@ -17,28 +18,28 @@ endif # but also turn off the STUPID ONES ifeq ($(CC), clang) CFLAGS += \ - -Weverything \ - -Wno-padded \ - -Wno-format-nonliteral \ - -Wno-disabled-macro-expansion \ - -Wno-conversion \ - -Wno-missing-prototypes \ - -Wno-float-equal \ - -Wno-cast-align \ - -Wno-missing-variable-declarations \ - -Wno-unknown-warning-option + -Weverything \ + -Wno-padded \ + -Wno-format-nonliteral \ + -Wno-disabled-macro-expansion \ + -Wno-conversion \ + -Wno-missing-prototypes \ + -Wno-float-equal \ + -Wno-cast-align \ + -Wno-missing-variable-declarations \ + -Wno-unknown-warning-option else #Tiny C Compiler doesn't know what -pedantic-errors is # and instead of ignoring .. just errors. ifneq ($(CC), tcc) - CFLAGS +=-pedantic-errors + CFLAGS +=-pedantic-errors -ffunction-sections -fdata-sections -Wl,-gc-sections else CFLAGS += -Wno-pointer-sign -fno-common endif endif ifeq ($(track), no) - CFLAGS += -DNOTRACK + CFLAGS += -DNOTRACK endif OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o @@ -84,6 +85,36 @@ else endif endif +#gource flags +GOURCEFLAGS= \ + --date-format "%d %B, %Y" \ + --seconds-per-day 0.01 \ + --auto-skip-seconds 1 \ + --title "GMQCC" \ + --key \ + --camera-mode overview \ + --highlight-all-users \ + --file-idle-time 0 \ + --hide progress,mouse \ + --stop-at-end \ + --max-files 99999999999 \ + --max-file-lag 0.000001 \ + --bloom-multiplier 1.3 \ + -1280x720 + +#ffmpeg flags for gource +FFMPEGFLAGS= \ + -y \ + -r 60 \ + -f image2pipe \ + -vcodec ppm \ + -i - \ + -vcodec libx264 \ + -preset ultrafast \ + -crf 1 \ + -threads 0 \ + -bf 0 + #splint flags SPLINTFLAGS = \ -redef \ @@ -119,7 +150,6 @@ SPLINTFLAGS = \ -kepttrans \ -unqualifiedtrans \ +matchanyintegral \ - -bufferoverflowhigh \ +voidabstract \ -nullassign \ -unrecog \ @@ -147,7 +177,7 @@ $(QCVM): $(OBJ_X) $(CC) -o $@ $^ $(CFLAGS) -lm $(GMQCC): $(OBJ_C) $(OBJ_D) - $(CC) -o $@ $^ $(CFLAGS) + $(CC) -o $@ $^ $(CFLAGS) -lm $(TESTSUITE): $(OBJ_T) $(CC) -o $@ $^ $(CFLAGS) -lm @@ -163,11 +193,17 @@ test: all @ ./$(TESTSUITE) clean: - rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat + rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4 splint: @ splint $(SPLINTFLAGS) *.c *.h +gource: + @ gource $(GOURCEFLAGS) + +gource-record: + @ gource $(GOURCEFLAGS) -o - | ffmpeg $(FFMPEGFLAGS) gource.mp4 + depend: @makedepend -Y -w 65536 2> /dev/null \ $(subst .o,.c,$(OBJ_D)) @@ -219,9 +255,15 @@ fs.o: gmqcc.h opts.def main.o: gmqcc.h opts.def lexer.h lexer.o: gmqcc.h opts.def lexer.h -parser.o: gmqcc.h opts.def lexer.h ast.h ir.h +parser.o: gmqcc.h opts.def lexer.h ast.h ir.h intrin.h fs.o: gmqcc.h opts.def util.o: gmqcc.h opts.def conout.o: gmqcc.h opts.def fs.o: gmqcc.h opts.def + +util.o: gmqcc.h opts.def +fs.o: gmqcc.h opts.def +conout.o: gmqcc.h opts.def +opts.o: gmqcc.h opts.def +pak.o: gmqcc.h opts.def diff --git a/README b/README index 2529864..bd71be0 100644 --- a/README +++ b/README @@ -1,10 +1,14 @@ GMQCC: An improved Quake C compiler -For licensing: see the LICENSE file. -For installation notes: see the INSTALL file. -For a list of authors: see the AUTHORS file. -For a list of changes: see the CHANGES file. +For licensing: see the LICENSE file. +For installation notes: see the INSTALL file. +For a list of authors: see the AUTHORS file. +For a list of changes: see the CHANGES file. For documentation: See the manpages, or visit the documentation online at http://graphitemaster.github.com/gmqcc/doc.html + +For syntax highlighting description files, or information +regarding how to install them: + See the README in syntax directory diff --git a/ast.c b/ast.c index c0c92fb..22c4def 100644 --- a/ast.c +++ b/ast.c @@ -42,7 +42,7 @@ static GMQCC_NORETURN void _ast_node_destroy(ast_node *self) { (void)self; con_err("ast node missing destroy()\n"); - abort(); + exit(EXIT_FAILURE); } /* Initialize main ast node aprts */ @@ -87,6 +87,8 @@ static void ast_expression_delete(ast_expression *self) ast_delete(self->expression.params[i]); } vec_free(self->expression.params); + if (self->expression.varparam) + ast_delete(self->expression.varparam); } static void ast_expression_delete_full(ast_expression *self) @@ -218,7 +220,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi if (!e) { if (pos + 6 >= bufsize) goto full; - strcpy(buf + pos, "(null)"); + strncpy(buf + pos, "(null)", 6); return pos + 6; } @@ -227,7 +229,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi switch (e->expression.vtype) { case TYPE_VARIANT: - strcpy(buf + pos, "(variant)"); + strncpy(buf + pos, "(variant)", 9); return pos + 9; case TYPE_FIELD: @@ -284,7 +286,7 @@ static size_t ast_type_to_string_impl(ast_expression *e, char *buf, size_t bufsi typelen = strlen(typestr); if (pos + typelen >= bufsize) goto full; - strcpy(buf + pos, typestr); + strncpy(buf + pos, typestr, typelen); return pos + typelen; } @@ -584,6 +586,7 @@ void ast_member_delete(ast_member *self) * purpose that is not garbage-collected. */ ast_expression_delete((ast_expression*)self); + mem_d(self->name); mem_d(self); } @@ -1216,7 +1219,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield) namelen = strlen(self->name); name = (char*)mem_a(namelen + 16); - strcpy(name, self->name); + strncpy(name, self->name, namelen); array->ir_values = (ir_value**)mem_a(sizeof(array->ir_values[0]) * array->expression.count); array->ir_values[0] = v; @@ -1274,7 +1277,7 @@ bool ast_global_codegen(ast_value *self, ir_builder *ir, bool isfield) namelen = strlen(self->name); name = (char*)mem_a(namelen + 16); - strcpy(name, self->name); + strncpy(name, self->name, namelen); self->ir_values = (ir_value**)mem_a(sizeof(self->ir_values[0]) * self->expression.count); self->ir_values[0] = v; @@ -1407,7 +1410,7 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param) v = ir_function_create_local(func, self->name, vtype, param); if (!v) { - compile_error(ast_ctx(self), "ir_function_create_local failed"); + compile_error(ast_ctx(self), "internal error: ir_function_create_local failed"); return false; } v->context = ast_ctx(self); @@ -1416,20 +1419,21 @@ bool ast_local_codegen(ast_value *self, ir_function *func, bool param) namelen = strlen(self->name); name = (char*)mem_a(namelen + 16); - strcpy(name, self->name); + strncpy(name, self->name, namelen); self->ir_values[0] = v; for (ai = 1; ai < self->expression.count; ++ai) { snprintf(name + namelen, 16, "[%u]", (unsigned int)ai); self->ir_values[ai] = ir_function_create_local(func, name, vtype, param); if (!self->ir_values[ai]) { - compile_error(ast_ctx(self), "ir_builder_create_global failed on `%s`", name); + compile_error(ast_ctx(self), "internal_error: ir_builder_create_global failed on `%s`", name); return false; } self->ir_values[ai]->context = ast_ctx(self); self->ir_values[ai]->unique_life = true; self->ir_values[ai]->locked = true; } + mem_d(name); } else { @@ -1538,7 +1542,7 @@ bool ast_function_codegen(ast_function *self, ir_builder *ir) irf = self->ir_func; if (!irf) { - compile_error(ast_ctx(self), "ast_function's related ast_value was not generated yet"); + compile_error(ast_ctx(self), "internal error: ast_function's related ast_value was not generated yet"); return false; } diff --git a/conout.c b/conout.c index 8613ece..00b8158 100644 --- a/conout.c +++ b/conout.c @@ -168,7 +168,7 @@ static int win_fputs(FILE *h, const char *str) { state = -1; } } else { - fs_file_putc(h, *str); + fs_file_write(str, 1, 1, stdout); length ++; } str++; @@ -282,10 +282,6 @@ int con_change(const char *out, const char *err) { con_enablecolor(); } else if (!(console.handle_err = fs_file_open(err, "w"))) return 0; - /* no buffering */ - setvbuf(console.handle_out, NULL, _IONBF, 0); - setvbuf(console.handle_err, NULL, _IONBF, 0); - return 1; } diff --git a/distro/Makefile b/distro/Makefile new file mode 100644 index 0000000..d29cff8 --- /dev/null +++ b/distro/Makefile @@ -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 diff --git a/distro/archbsd/git/PKGBUILD b/distro/archbsd/git/PKGBUILD index 2144110..006c8d0 100644 --- a/distro/archbsd/git/PKGBUILD +++ b/distro/archbsd/git/PKGBUILD @@ -47,9 +47,9 @@ check() { package() { cd "$srcdir"/"$_gitname" msg "Compiling and installing to pkgdir this time..." - gmake install DESTDIR=$pkgdir PREFIX=/usr/local MANDIR=/usr/local/man + gmake install DESTDIR=$pkgdir PREFIX=/usr msg "Compiling done." - install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc - install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE + install -dm755 ${pkgdir}/usr/share/licenses/gmqcc + install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE } diff --git a/distro/archbsd/release/PKGBUILD b/distro/archbsd/release/PKGBUILD index 8c449c6..246cf73 100644 --- a/distro/archbsd/release/PKGBUILD +++ b/distro/archbsd/release/PKGBUILD @@ -30,9 +30,9 @@ check() { package() { cd "$srcdir"/"$_gitname" msg "Compiling and installing to pkgdir this time..." - gmake install DESTDIR=$pkgdir PREFIX=/usr/local MANDIR=/usr/local/man + gmake install DESTDIR=$pkgdir PREFIX=/usr msg "Compiling done." - install -dm755 ${pkgdir}/usr/local/share/licenses/gmqcc - install -m644 LICENSE ${pkgdir}/usr/local/share/licenses/gmqcc/LICENSE + install -dm755 ${pkgdir}/usr/share/licenses/gmqcc + install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE } diff --git a/distro/archlinux/git/PKGBUILD b/distro/archlinux/git/PKGBUILD index 4a9127c..0ac2423 100644 --- a/distro/archlinux/git/PKGBUILD +++ b/distro/archlinux/git/PKGBUILD @@ -1,7 +1,11 @@ # Contributor: matthiaskrgr pkgname=gmqcc-git -pkgver=20130127 +pkgver=0.2.524.gc6bd5e6 +pkgver(){ + cd gmqcc + git describe --tags | sed -e 's/^gmqcc\-//' -e 's/-/./g' +} pkgrel=1 pkgdesc="An Improved Quake C Compiler" arch=('i686' 'x86_64') @@ -11,40 +15,25 @@ provides=('gmqcc=0.2.4') makedepends=('git') url="https://github.com/graphitemaster/gmqcc.git" license=('MIT') +source=('gmqcc::git://github.com/graphitemaster/gmqcc.git') +sha1sums=('SKIP') -_gitroot="git://github.com/graphitemaster/gmqcc.git" -_gitname="gmqcc" build() { - cd $srcdir - msg "Connecting to the GIT server..." - if [[ -d $srcdir/$_gitname ]] ; then - cd $_gitname - msg "Removing build files..." - git clean -dfx - msg "Updating..." - git pull --no-tags - msg "The local files are updated." - else - msg "Cloning..." - git clone $_gitroot $_gitname --depth 1 - msg "Clone done." - fi - msg "Starting compilation..." - cd "$srcdir"/"$_gitname" + cd "$srcdir"/"gmqcc" msg "Compiling..." make } check() { - cd "$srcdir"/"$_gitname" + cd "$srcdir"/"gmqcc" make check } package() { - cd "$srcdir"/"$_gitname" + cd "$srcdir"/"gmqcc" msg "Compiling and installing to pkgdir this time..." make install DESTDIR=$pkgdir PREFIX=/usr msg "Compiling done." diff --git a/distro/archlinux/release/PKGBUILD b/distro/archlinux/release/PKGBUILD index 1f8f66e..ef7de8f 100644 --- a/distro/archlinux/release/PKGBUILD +++ b/distro/archlinux/release/PKGBUILD @@ -9,7 +9,7 @@ depends=('glibc') url="https://github.com/graphitemaster/gmqcc.git" license=('MIT') source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver) -sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353') +sha1sums=('e0fe99af9a55d36cd9e0909a96d1b14f2db8b757') _gitname=graphitemaster-gmqcc-de24486/ diff --git a/distro/archlinux/this/Makefile b/distro/archlinux/this/Makefile index 6d3b557..bf5a3e8 100644 --- a/distro/archlinux/this/Makefile +++ b/distro/archlinux/this/Makefile @@ -9,9 +9,17 @@ CARCH := $(shell uname -m) PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH) PKG := $(PKGDIR).pkg.tar.xz PKGINFO := $(PKGDIR)/.PKGINFO +DESTDIR := distro/archlinux/this/$(PKGDIR) +CFLAGS := -base: clean - $(MAKE) -C $(BASEDIR) DESTDIR=distro/archlinux/this/$(PKGDIR) PREFIX=$(PREFIX) install + +ifneq (, $(findstring i686, $(CARCH))) + CFLAGS := -m32 +endif + +base: + $(MAKE) -C $(BASEDIR) clean + $(MAKE) -C $(BASEDIR) OPTIONAL=$(CFLAGS) DESTDIR=$(DESTDIR) PREFIX=$(PREFIX) install @echo "pkgname = gmqcc" > $(PKGINFO) @echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO) @echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO) @@ -34,7 +42,7 @@ base: clean @rm -rf $(PKGDIR) clean: - @rm -f $(PKG) - + $(MAKE) -C $(BASEDIR) clean + @rm -f *.pkg.tar.xz all: base diff --git a/distro/deb/Makefile b/distro/deb/Makefile index 535cf1c..df23602 100644 --- a/distro/deb/Makefile +++ b/distro/deb/Makefile @@ -5,19 +5,39 @@ MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)` MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)` PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)` DEBDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH) -DEB := $(DEBDIR).deb +CARCH := $(shell uname -m) +DEB := $(DEBDIR)-$(CARCH).deb +CONTROL := $(DEBDIR)/DEBIAN/control + +ifneq (, $(findstring i686, $(CARCH))) + CFLAGS := -m32 +endif base: + $(MAKE) -C $(BASEDIR) clean $(MAKE) -C $(BASEDIR) DESTDIR=distro/deb/$(DEBDIR) PREFIX=$(PREFIX) install @install -d -m755 $(DEBDIR)/DEBIAN - @cp control $(DEBDIR)/DEBIAN/control + @echo "Package: gmqcc" > $(CONTROL) + @echo "Version: $(MAJOR).$(MINOR).$(PATCH)" >> $(CONTROL) + @echo "Section: user/hidden" >> $(CONTROL) + @echo "Priority: optional" >> $(CONTROL) + @echo "Architecture: $(CARCH)" >> $(CONTROL) + @echo "Installed-Size: `du -ks $($(DEBDIR)/usr) | cut -f 1`" >> $(CONTROL) + @echo "Maintainer: Dale Weiler " >> $(CONTROL) + @echo "Description: An improved Quake C Compiler" >> $(CONTROL) + @echo " For an enduring period of time the options for a decent compiler for the Quake C programming language" >> $(CONTROL) + @echo " were confined to a specific compiler known as QCC. Attempts were made to extend and improve upon the" >> $(CONTROL) + @echo " design of QCC, but many foreseen the consequences of building on a broken foundation. The solution" >> $(CONTROL) + @echo " was obvious, a new compiler; one born from the NIH realm of sarcastic wit. We welcome you. You won't" >> $(CONTROL) + @echo " find a better Quake C compiler." >> $(CONTROL) @tar czf data.tar.gz -C $(DEBDIR)/ . --exclude=DEBIAN @tar czf control.tar.gz -C $(DEBDIR)/DEBIAN/ . @echo 2.0 > debian-binary @ar r $(DEB) debian-binary control.tar.gz data.tar.gz @rm -rf debian-binary control.tar.gz data.tar.gz $(DEBDIR) -clean: - @rm -f $(DEB) +clean: + $(MAKE) -C $(BASEDIR) clean + @rm -f *.deb all: base diff --git a/distro/deb/control b/distro/deb/control deleted file mode 100644 index 57cde3a..0000000 --- a/distro/deb/control +++ /dev/null @@ -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 -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. diff --git a/exec.c b/exec.c index f44bd62..8b0ee5e 100644 --- a/exec.c +++ b/exec.c @@ -455,7 +455,6 @@ static void prog_print_statement(qc_program *prog, prog_section_statement *st) else printf("(none)"); printf("\n"); } - fflush(stdout); } static qcint prog_enterfunction(qc_program *prog, prog_section_function *func) @@ -825,6 +824,16 @@ static int qc_strcmp(qc_program *prog) return 0; } +static int qc_floor(qc_program *prog) +{ + qcany *num, out; + CheckArgs(1); + num = GetArg(0); + out._float = floor(num->_float); + Return(out); + return 0; +} + static prog_builtin qc_builtins[] = { NULL, &qc_print, /* 1 */ @@ -839,7 +848,8 @@ static prog_builtin qc_builtins[] = { &qc_strcat, /* 10 */ &qc_strcmp, /* 11 */ &qc_normalize, /* 12 */ - &qc_sqrt /* 13 */ + &qc_sqrt, /* 13 */ + &qc_floor /* 14 */ }; static size_t qc_builtins_count = sizeof(qc_builtins) / sizeof(qc_builtins[0]); diff --git a/fs.c b/fs.c index 8d73b1d..997b368 100644 --- a/fs.c +++ b/fs.c @@ -53,7 +53,7 @@ ) { wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression); wprintf(L"Aborting ...\n"); - abort(); + exit(EXIT_FAILURE); } static void file_init() { @@ -165,16 +165,6 @@ int fs_file_seek(FILE *fp, long int off, int whence) { return fseek(fp, off, whence); } -int fs_file_putc(FILE *fp, int ch) { - /* Invokes file_exception on windows if fp is null */ - return fputc(ch, fp); -} - -int fs_file_flush(FILE *fp) { - /* Invokes file_exception on windows if fp is null */ - return fflush(fp); -} - long int fs_file_tell(FILE *fp) { /* Invokes file_exception on windows if fp is null */ return ftell(fp); @@ -238,7 +228,7 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) { if (!dir) return NULL; - strcpy(dir->dd_name, name); + strncpy(dir->dd_name, name, strlen(name)); return dir; } @@ -258,8 +248,8 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) { if (*dir->dd_name) { size_t n = strlen(dir->dd_name); if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) { - strcpy(dirname, dir->dd_name); - strcpy(dirname + n, "\\*.*"); /* 4 + 1 */ + strncpy(dirname, dir->dd_name, n); + strncpy(dirname + n, "\\*.*", 4); /* 4 + 1 */ } } else { if (!(dirname = util_strdup("\\*.*"))) @@ -303,7 +293,6 @@ int fs_file_getline(char **lineptr, size_t *n, FILE *stream) { #else # if !defined(__MINGW32__) # include /* mkdir */ -# include /* chdir */ int fs_dir_make(const char *path) { return mkdir(path, 0700); @@ -326,8 +315,4 @@ struct dirent *fs_dir_read(DIR *dir) { return readdir(dir); } -int fs_dir_change(const char *path) { - return chdir(path); -} - #endif /*! defined(_WIN32) && !defined(__MINGW32__) */ diff --git a/ftepp.c b/ftepp.c index 68491ed..8034665 100644 --- a/ftepp.c +++ b/ftepp.c @@ -25,6 +25,7 @@ #include "gmqcc.h" #include "lexer.h" +#define HT_MACROS 1024 typedef struct { bool on; bool was_on; @@ -55,14 +56,15 @@ typedef struct { pptoken **output; } ppmacro; -typedef struct { +typedef struct ftepp_s { lex_file *lex; int token; unsigned int errors; bool output_on; ppcondition *conditions; - ppmacro **macros; + /*ppmacro **macros;*/ + ht macros; /* hashtable */ char *output_string; @@ -124,7 +126,7 @@ char *ftepp_predef_line(lex_file *context) { char *ftepp_predef_file(lex_file *context) { size_t length = strlen(context->name) + 3; /* two quotes and a terminator */ char *value = (char*)mem_a(length); - sprintf(value, "\"%s\"", context->name); + snprintf(value, length, "\"%s\"", context->name); return value; } @@ -225,7 +227,7 @@ static pptoken *pptoken_make(ftepp_t *ftepp) return token; } -static void pptoken_delete(pptoken *self) +static GMQCC_INLINE void pptoken_delete(pptoken *self) { mem_d(self->value); mem_d(self); @@ -261,27 +263,27 @@ static ftepp_t* ftepp_new() ftepp = (ftepp_t*)mem_a(sizeof(*ftepp)); memset(ftepp, 0, sizeof(*ftepp)); + ftepp->macros = util_htnew(HT_MACROS); ftepp->output_on = true; return ftepp; } -static void ftepp_flush_do(ftepp_t *self) +static GMQCC_INLINE void ftepp_flush_do(ftepp_t *self) { vec_free(self->output_string); } static void ftepp_delete(ftepp_t *self) { - size_t i; ftepp_flush_do(self); if (self->itemname) mem_d(self->itemname); if (self->includename) vec_free(self->includename); - for (i = 0; i < vec_size(self->macros); ++i) - ppmacro_delete(self->macros[i]); - vec_free(self->macros); + + util_htrem(self->macros, (void (*)(void*))&ppmacro_delete); + vec_free(self->conditions); if (self->lex) lex_close(self->lex); @@ -300,7 +302,7 @@ static void ftepp_out(ftepp_t *ftepp, const char *str, bool ignore_cond) } } -static void ftepp_update_output_condition(ftepp_t *ftepp) +static GMQCC_INLINE void ftepp_update_output_condition(ftepp_t *ftepp) { size_t i; ftepp->output_on = true; @@ -308,25 +310,14 @@ static void ftepp_update_output_condition(ftepp_t *ftepp) ftepp->output_on = ftepp->output_on && ftepp->conditions[i].on; } -static ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) +static GMQCC_INLINE ppmacro* ftepp_macro_find(ftepp_t *ftepp, const char *name) { - size_t i; - for (i = 0; i < vec_size(ftepp->macros); ++i) { - if (!strcmp(name, ftepp->macros[i]->name)) - return ftepp->macros[i]; - } - return NULL; + return util_htget(ftepp->macros, name); } -static void ftepp_macro_delete(ftepp_t *ftepp, const char *name) +static GMQCC_INLINE void ftepp_macro_delete(ftepp_t *ftepp, const char *name) { - size_t i; - for (i = 0; i < vec_size(ftepp->macros); ++i) { - if (!strcmp(name, ftepp->macros[i]->name)) { - vec_remove(ftepp->macros, i, 1); - return; - } - } + util_htrm(ftepp->macros, name, NULL); } static GMQCC_INLINE int ftepp_next(ftepp_t *ftepp) @@ -394,6 +385,7 @@ static bool ftepp_define_params(ftepp_t *ftepp, ppmacro *macro) return false; } } while (ftepp->token == ','); + if (ftepp->token != ')') { ftepp_error(ftepp, "expected closing paren after macro parameter list"); return false; @@ -422,7 +414,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) return false; } - index = atoi(ftepp_tokval(ftepp)); + index = (int)strtol(ftepp_tokval(ftepp), NULL, 10); if (ftepp_next(ftepp) != ']') { ftepp_error(ftepp, "expected `]` in __VA_ARGS__ subscript"); @@ -471,7 +463,7 @@ static bool ftepp_define_body(ftepp_t *ftepp, ppmacro *macro) static bool ftepp_define(ftepp_t *ftepp) { - ppmacro *macro; + ppmacro *macro = NULL; size_t l = ftepp_ctx(ftepp).line; (void)ftepp_next(ftepp); @@ -499,18 +491,28 @@ static bool ftepp_define(ftepp_t *ftepp) if (ftepp->token == '(') { macro->has_params = true; - if (!ftepp_define_params(ftepp, macro)) + if (!ftepp_define_params(ftepp, macro)) { + ppmacro_delete(macro); return false; + } } - if (!ftepp_skipspace(ftepp)) + if (!ftepp_skipspace(ftepp)) { + ppmacro_delete(macro); return false; + } - if (!ftepp_define_body(ftepp, macro)) + if (!ftepp_define_body(ftepp, macro)) { + ppmacro_delete(macro); return false; + } +#if 0 if (ftepp->output_on) vec_push(ftepp->macros, macro); +#endif + if (ftepp->output_on) + util_htset(ftepp->macros, macro->name, (void*)macro); else { ppmacro_delete(macro); } @@ -830,7 +832,7 @@ static bool ftepp_macro_expand(ftepp_t *ftepp, ppmacro *macro, macroparam *param if (resetline && !ftepp->in_macro) { char lineno[128]; - sprintf(lineno, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline)); + snprintf(lineno, 128, "\n#pragma line(%lu)\n", (unsigned long)(old_lexer->sline)); ftepp_out(ftepp, lineno, false); } @@ -1706,9 +1708,7 @@ static bool ftepp_preprocess(ftepp_t *ftepp) /* Like in parser.c - files keep the previous state so we have one global * preprocessor. Except here we will want to warn about dangling #ifs. */ -static ftepp_t *ftepp; - -static bool ftepp_preprocess_done() +static bool ftepp_preprocess_done(ftepp_t *ftepp) { bool retval = true; if (vec_size(ftepp->conditions)) { @@ -1724,7 +1724,7 @@ static bool ftepp_preprocess_done() return retval; } -bool ftepp_preprocess_file(const char *filename) +bool ftepp_preprocess_file(ftepp_t *ftepp, const char *filename) { ftepp->lex = lex_open(filename); ftepp->itemname = util_strdup(filename); @@ -1734,10 +1734,10 @@ bool ftepp_preprocess_file(const char *filename) } if (!ftepp_preprocess(ftepp)) return false; - return ftepp_preprocess_done(); + return ftepp_preprocess_done(ftepp); } -bool ftepp_preprocess_string(const char *name, const char *str) +bool ftepp_preprocess_string(ftepp_t *ftepp, const char *name, const char *str) { ftepp->lex = lex_open_string(str, strlen(str), name); ftepp->itemname = util_strdup(name); @@ -1747,16 +1747,16 @@ bool ftepp_preprocess_string(const char *name, const char *str) } if (!ftepp_preprocess(ftepp)) return false; - return ftepp_preprocess_done(); + return ftepp_preprocess_done(ftepp); } -void ftepp_add_macro(const char *name, const char *value) { +void ftepp_add_macro(ftepp_t *ftepp, const char *name, const char *value) { char *create = NULL; /* use saner path for empty macros */ if (!value) { - ftepp_add_define("__builtin__", name); + ftepp_add_define(ftepp, "__builtin__", name); return; } @@ -1766,26 +1766,27 @@ void ftepp_add_macro(const char *name, const char *value) { vec_upload(create, value, strlen(value)); vec_push (create, 0); - ftepp_preprocess_string("__builtin__", create); + ftepp_preprocess_string(ftepp, "__builtin__", create); vec_free (create); } -bool ftepp_init() +ftepp_t *ftepp_create() { + ftepp_t *ftepp; char minor[32]; char major[32]; ftepp = ftepp_new(); if (!ftepp) - return false; + return NULL; memset(minor, 0, sizeof(minor)); memset(major, 0, sizeof(major)); /* set the right macro based on the selected standard */ - ftepp_add_define(NULL, "GMQCC"); + ftepp_add_define(ftepp, NULL, "GMQCC"); if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) { - ftepp_add_define(NULL, "__STD_FTEQCC__"); + ftepp_add_define(ftepp, NULL, "__STD_FTEQCC__"); /* 1.00 */ major[0] = '"'; major[1] = '1'; @@ -1795,15 +1796,15 @@ bool ftepp_init() minor[1] = '0'; minor[2] = '"'; } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) { - ftepp_add_define(NULL, "__STD_GMQCC__"); - sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR); - sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR); + ftepp_add_define(ftepp, NULL, "__STD_GMQCC__"); + snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR); + snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR); } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCCX) { - ftepp_add_define(NULL, "__STD_QCCX__"); - sprintf(major, "\"%d\"", GMQCC_VERSION_MAJOR); - sprintf(minor, "\"%d\"", GMQCC_VERSION_MINOR); + ftepp_add_define(ftepp, NULL, "__STD_QCCX__"); + snprintf(major, 32, "\"%d\"", GMQCC_VERSION_MAJOR); + snprintf(minor, 32, "\"%d\"", GMQCC_VERSION_MINOR); } else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_QCC) { - ftepp_add_define(NULL, "__STD_QCC__"); + ftepp_add_define(ftepp, NULL, "__STD_QCC__"); /* 1.0 */ major[0] = '"'; major[1] = '1'; @@ -1814,35 +1815,35 @@ bool ftepp_init() minor[2] = '"'; } - ftepp_add_macro("__STD_VERSION_MINOR__", minor); - ftepp_add_macro("__STD_VERSION_MAJOR__", major); + ftepp_add_macro(ftepp, "__STD_VERSION_MINOR__", minor); + ftepp_add_macro(ftepp, "__STD_VERSION_MAJOR__", major); - return true; + return ftepp; } -void ftepp_add_define(const char *source, const char *name) +void ftepp_add_define(ftepp_t *ftepp, const char *source, const char *name) { ppmacro *macro; lex_ctx ctx = { "__builtin__", 0 }; ctx.file = source; macro = ppmacro_new(ctx, name); - vec_push(ftepp->macros, macro); + /*vec_push(ftepp->macros, macro);*/ + util_htset(ftepp->macros, name, macro); } -const char *ftepp_get() +const char *ftepp_get(ftepp_t *ftepp) { return ftepp->output_string; } -void ftepp_flush() +void ftepp_flush(ftepp_t *ftepp) { ftepp_flush_do(ftepp); } -void ftepp_finish() +void ftepp_finish(ftepp_t *ftepp) { if (!ftepp) return; ftepp_delete(ftepp); - ftepp = NULL; } diff --git a/gmqcc.h b/gmqcc.h index 2cc0864..32028c1 100644 --- a/gmqcc.h +++ b/gmqcc.h @@ -300,7 +300,8 @@ void util_meminfo (); bool util_filexists (const char *); bool util_strupper (const char *); bool util_strdigit (const char *); -char *util_strdup (const char *); +char *_util_Estrdup (const char *, const char *, size_t); +char *_util_Estrdup_empty(const char *, const char *, size_t); void util_debug (const char *, const char *, ...); void util_endianswap (void *, size_t, unsigned int); @@ -317,22 +318,27 @@ int util_asprintf (char **ret, const char *fmt, ...); #ifdef NOTRACK -# define mem_a(x) malloc (x) -# define mem_d(x) free ((void*)x) -# define mem_r(x, n) realloc((void*)x, n) +# define mem_a(x) malloc (x) +# define mem_d(x) free ((void*)x) +# define mem_r(x, n) realloc((void*)x, n) +# define mem_af(x,f,l) malloc (x) #else -# define mem_a(x) util_memory_a((x), __LINE__, __FILE__) -# define mem_d(x) util_memory_d((void*)(x)) -# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__) +# define mem_a(x) util_memory_a((x), __LINE__, __FILE__) +# define mem_d(x) util_memory_d((void*)(x)) +# define mem_r(x, n) util_memory_r((void*)(x), (n), __LINE__, __FILE__) +# define mem_af(x,f,l) util_memory_a((x), __LINE__, __FILE__) #endif /*! NOTRACK */ +#define util_strdup(X) _util_Estrdup((X), __FILE__, __LINE__) +#define util_strdupe(X) _util_Estrdup_empty((X), __FILE__, __LINE__) + /* * A flexible vector implementation: all vector pointers contain some * data about themselfs exactly - sizeof(vector_t) behind the pointer * this data is represented in the structure below. Doing this allows * us to use the array [] to access individual elements from the vector * opposed to using set/get methods. - */ + */ typedef struct { size_t allocated; size_t used; @@ -374,14 +380,6 @@ typedef struct hash_table_t { struct hash_node_t **table; } hash_table_t, *ht; -typedef struct hash_set_t { - size_t bits; - size_t mask; - size_t capacity; - size_t *items; - size_t total; -} hash_set_t, *hs; - /* * hashtable implementation: * @@ -413,51 +411,16 @@ typedef struct hash_set_t { * util_htdel(foo); */ hash_table_t *util_htnew (size_t size); +void util_htrem (hash_table_t *ht, void (*callback)(void *data)); void util_htset (hash_table_t *ht, const char *key, void *value); void util_htdel (hash_table_t *ht); size_t util_hthash(hash_table_t *ht, const char *key); void util_htseth(hash_table_t *ht, const char *key, size_t hash, void *value); +void util_htrmh (hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)); +void util_htrm (hash_table_t *ht, const char *key, void (*cb)(void*)); void *util_htget (hash_table_t *ht, const char *key); void *util_htgeth(hash_table_t *ht, const char *key, size_t hash); - -/* - * hashset implementation: - * This was designed for pointers: you manage the life of the object yourself - * if you do use this for non-pointers please be warned that the object may not - * be valid if the duration of it exceeds (i.e on stack). So you need to allocate - * yourself, or put those in global scope to ensure duration is for the whole - * runtime. - * - * util_hsnew() -- to make a new hashset - * util_hsadd(set, key) -- to add something in the set - * util_hshas(set, key) -- to check if something is in the set - * util_hsrem(set, key) -- to remove something in the set - * util_hsdel(set) -- to delete the set - * - * example of use: - * - * hs foo = util_hsnew(); - * char *bar = "hello blub\n"; - * char *baz = "hello dale\n"; - * - * util_hsadd(foo, bar); - * util_hsadd(foo, baz); - * util_hsrem(foo, baz); - * - * printf("bar %d | baz %d\n", - * util_hshas(foo, bar), - * util_hshad(foo, baz) - * ); - * - * util_hsdel(foo); - */ - -hash_set_t *util_hsnew(void); -int util_hsadd(hash_set_t *, void *); -int util_hshas(hash_set_t *, void *); -int util_hsrem(hash_set_t *, void *); -void util_hsdel(hash_set_t *); /*===================================================================*/ /*============================ file.c ===============================*/ @@ -466,10 +429,8 @@ void util_hsdel(hash_set_t *); void fs_file_close (FILE *); int fs_file_error (FILE *); int fs_file_getc (FILE *); -int fs_file_flush (FILE *); int fs_file_printf (FILE *, const char *, ...); int fs_file_puts (FILE *, const char *); -int fs_file_putc (FILE *, int); int fs_file_seek (FILE *, long int, int); long int fs_file_tell (FILE *); @@ -480,11 +441,10 @@ FILE *fs_file_open (const char *, const char *); int fs_file_getline(char **, size_t *, FILE *); /* directory handling */ +int fs_dir_make (const char *); DIR *fs_dir_open (const char *); int fs_dir_close (DIR *); struct dirent *fs_dir_read (DIR *); -int fs_dir_make (const char *); -int fs_dir_change (const char *); /*===================================================================*/ @@ -1025,17 +985,20 @@ qcint prog_tempstring(qc_program *prog, const char *_str); /*===================================================================*/ /*===================== parser.c commandline ========================*/ /*===================================================================*/ +struct parser_s; -bool parser_init (); -bool parser_compile_file (const char *); -bool parser_compile_string(const char *, const char *, size_t); -bool parser_finish (const char *); -void parser_cleanup (); +struct parser_s *parser_create (); +bool parser_compile_file (struct parser_s *parser, const char *); +bool parser_compile_string(struct parser_s *parser, const char *, const char *, size_t); +bool parser_finish (struct parser_s *parser, const char *); +void parser_cleanup (struct parser_s *parser); /*===================================================================*/ /*====================== ftepp.c commandline ========================*/ /*===================================================================*/ struct lex_file_s; +struct ftepp_s; + typedef struct { const char *name; char *(*func)(struct lex_file_s *); @@ -1047,14 +1010,14 @@ typedef struct { */ #define FTEPP_PREDEF_COUNT 8 -bool ftepp_init (); -bool ftepp_preprocess_file (const char *filename); -bool ftepp_preprocess_string(const char *name, const char *str); -void ftepp_finish (); -const char *ftepp_get (); -void ftepp_flush (); -void ftepp_add_define (const char *source, const char *name); -void ftepp_add_macro (const char *name, const char *value); +struct ftepp_s *ftepp_create (); +bool ftepp_preprocess_file (struct ftepp_s *ftepp, const char *filename); +bool ftepp_preprocess_string(struct ftepp_s *ftepp, const char *name, const char *str); +void ftepp_finish (struct ftepp_s *ftepp); +const char *ftepp_get (struct ftepp_s *ftepp); +void ftepp_flush (struct ftepp_s *ftepp); +void ftepp_add_define (struct ftepp_s *ftepp, const char *source, const char *name); +void ftepp_add_macro (struct ftepp_s *ftepp, const char *name, const char *value); extern const ftepp_predef_t ftepp_predefs[FTEPP_PREDEF_COUNT]; diff --git a/intrin.h b/intrin.h new file mode 100644 index 0000000..69490dc --- /dev/null +++ b/intrin.h @@ -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, "", 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, "", 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, "", 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, "", 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; +} diff --git a/ir.c b/ir.c index a28b07e..c614594 100644 --- a/ir.c +++ b/ir.c @@ -1206,22 +1206,11 @@ bool ir_value_set_field(ir_value *self, ir_value *fld) return true; } -static char *ir_strdup(const char *str) -{ - if (str && !*str) { - /* actually dup empty strings */ - char *out = (char*)mem_a(1); - *out = 0; - return out; - } - return util_strdup(str); -} - bool ir_value_set_string(ir_value *self, const char *str) { if (self->vtype != TYPE_STRING) return false; - self->constval.vstring = ir_strdup(str); + self->constval.vstring = util_strdupe(str); self->hasvalue = true; return true; } @@ -1651,7 +1640,7 @@ void ir_phi_add(ir_instr* self, ir_block *b, ir_value *v) * is doing something wrong. */ irerror(self->context, "Invalid entry block for PHI"); - abort(); + exit(EXIT_FAILURE); } pe.value = v; @@ -3292,6 +3281,8 @@ static void gen_vector_defs(prog_section_def def, const char *name) def.offset++; component[len-1]++; } + + mem_d(component); } static void gen_vector_fields(prog_section_field fld, const char *name) @@ -3320,6 +3311,8 @@ static void gen_vector_fields(prog_section_field fld, const char *name) fld.offset++; component[len-1]++; } + + mem_d(component); } static bool ir_builder_gen_global(ir_builder *self, ir_value *global, bool islocal) diff --git a/lexer.c b/lexer.c index 5f23952..7f5736d 100644 --- a/lexer.c +++ b/lexer.c @@ -272,7 +272,7 @@ void lex_close(lex_file *lex) static int lex_fgetc(lex_file *lex) { if (lex->file) - return fgetc(lex->file); + return fs_file_getc(lex->file); if (lex->open_string) { if (lex->open_string_pos >= lex->open_string_length) return EOF; @@ -483,6 +483,9 @@ static bool lex_try_pragma(lex_file *lex) lex->line = line; while (ch != '\n' && ch != EOF) ch = lex_getch(lex); + vec_free(command); + vec_free(param); + vec_free(pragma); return true; unroll: @@ -495,13 +498,13 @@ unroll: vec_free(command); lex_ungetch(lex, ' '); } - if (command) { - vec_pop(command); - while (vec_size(command)) { - lex_ungetch(lex, (unsigned char)vec_last(command)); - vec_pop(command); + if (param) { + vec_pop(param); + while (vec_size(param)) { + lex_ungetch(lex, (unsigned char)vec_last(param)); + vec_pop(param); } - vec_free(command); + vec_free(param); lex_ungetch(lex, ' '); } if (pragma) { @@ -1232,7 +1235,7 @@ int lex_do(lex_file *lex) /* case '+': case '-': - */ + */ case '*': case '/': case '<': @@ -1352,7 +1355,7 @@ int lex_do(lex_file *lex) lex_tokench(lex, ch); nextch = lex_getch(lex); - if (nextch == '=') { + if (nextch == '=' || nextch == '*') { lex_tokench(lex, nextch); } else lex_ungetch(lex, nextch); @@ -1361,6 +1364,12 @@ int lex_do(lex_file *lex) return (lex->tok.ttype = TOKEN_OPERATOR); } + if (ch == '%') { + lex_tokench(lex, ch); + lex_endtoken(lex); + return (lex->tok.ttype = TOKEN_OPERATOR); + } + if (isident_start(ch)) { const char *v; diff --git a/lexer.h b/lexer.h index 9724a7b..9f080dc 100644 --- a/lexer.h +++ b/lexer.h @@ -166,18 +166,21 @@ typedef struct { static const oper_info c_operators[] = { { "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */ - { "++", 1, opid3('S','+','+'), ASSOC_LEFT, 15, OP_SUFFIX}, - { "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX}, - { ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 }, - { "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */ - { "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */ + { "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX}, + { "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX}, + { ".", 2, opid1('.'), ASSOC_LEFT, 17, 0 }, + { "(", 0, opid1('('), ASSOC_LEFT, 17, 0 }, /* function call */ + { "[", 2, opid1('['), ASSOC_LEFT, 17, 0 }, /* array subscript */ + + { "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX }, + { "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX }, + + { "**", 2, opid2('*', '*'), ASSOC_RIGHT, 15, 0 }, { "!", 1, opid2('!', 'P'), ASSOC_RIGHT, 14, OP_PREFIX }, { "~", 1, opid2('~', 'P'), ASSOC_RIGHT, 14, OP_PREFIX }, { "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX }, { "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX }, - { "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX }, - { "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX }, /* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX }, */ { "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 }, diff --git a/main.c b/main.c index e801aaf..0a22b1b 100644 --- a/main.c +++ b/main.c @@ -146,9 +146,10 @@ static bool options_parse(int argc, char **argv) { bool argend = false; size_t itr; char buffer[1024]; - char *redirout = NULL; - char *redirerr = NULL; - char *config = NULL; + char *redirout = NULL; + char *redirerr = NULL; + char *config = NULL; + char *memdumpcols = NULL; while (!argend && argc > 1) { char *argarg; @@ -223,6 +224,10 @@ static bool options_parse(int argc, char **argv) { config = argarg; continue; } + if (options_long_gcc("memdumpcols", &argc, &argv, &memdumpcols)) { + OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = (uint16_t)strtol(memdumpcols, NULL, 10); + continue; + } /* show defaults (like pathscale) */ if (!strcmp(argv[0]+1, "show-defaults")) { @@ -403,7 +408,7 @@ static bool options_parse(int argc, char **argv) { return false; } if (isdigit(argarg[0])) { - uint32_t val = atoi(argarg); + uint32_t val = (uint32_t)strtol(argarg, NULL, 10); OPTS_OPTION_U32(OPTION_O) = val; opts_setoptimlevel(val); } else { @@ -541,12 +546,14 @@ static bool progs_nextline(char **out, size_t *alen,FILE *src) { } int main(int argc, char **argv) { - size_t itr; - int retval = 0; - bool opts_output_free = false; - bool operators_free = false; - bool progs_src = false; - FILE *outfile = NULL; + size_t itr; + int retval = 0; + bool opts_output_free = false; + bool operators_free = false; + bool progs_src = false; + FILE *outfile = NULL; + struct parser_s *parser = NULL; + struct ftepp_s *ftepp = NULL; app_name = argv[0]; con_init (); @@ -621,7 +628,7 @@ int main(int argc, char **argv) { } if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { - if (!parser_init()) { + if (!(parser = parser_create())) { con_err("failed to initialize parser\n"); retval = 1; goto cleanup; @@ -629,7 +636,7 @@ int main(int argc, char **argv) { } if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) { - if (!ftepp_init()) { + if (!(ftepp = ftepp_create())) { con_err("failed to initialize parser\n"); retval = 1; goto cleanup; @@ -644,7 +651,7 @@ int main(int argc, char **argv) { /* add macros */ if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) { for (itr = 0; itr < vec_size(ppems); itr++) { - ftepp_add_macro(ppems[itr].name, ppems[itr].value); + ftepp_add_macro(ftepp, ppems[itr].name, ppems[itr].value); mem_d(ppems[itr].name); /* can be null */ @@ -703,6 +710,7 @@ srcdone: con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual")); con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items)); } + for (itr = 0; itr < vec_size(items); ++itr) { if (!OPTS_OPTION_BOOL(OPTION_QUIET) && !OPTS_OPTION_BOOL(OPTION_PP_ONLY)) @@ -717,33 +725,33 @@ srcdone: if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { const char *out; - if (!ftepp_preprocess_file(items[itr].filename)) { + if (!ftepp_preprocess_file(ftepp, items[itr].filename)) { retval = 1; goto cleanup; } - out = ftepp_get(); + out = ftepp_get(ftepp); if (out) fs_file_printf(outfile, "%s", out); - ftepp_flush(); + ftepp_flush(ftepp); } else { if (OPTS_FLAG(FTEPP)) { const char *data; - if (!ftepp_preprocess_file(items[itr].filename)) { + if (!ftepp_preprocess_file(ftepp, items[itr].filename)) { retval = 1; goto cleanup; } - data = ftepp_get(); + data = ftepp_get(ftepp); if (vec_size(data)) { - if (!parser_compile_string(items[itr].filename, data, vec_size(data))) { + if (!parser_compile_string(parser, items[itr].filename, data, vec_size(data))) { retval = 1; goto cleanup; } } - ftepp_flush(); + ftepp_flush(ftepp); } else { - if (!parser_compile_file(items[itr].filename)) { + if (!parser_compile_file(parser, items[itr].filename)) { retval = 1; goto cleanup; } @@ -756,9 +764,10 @@ srcdone: } } - ftepp_finish(); + ftepp_finish(ftepp); + ftepp = NULL; if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) { - if (!parser_finish(OPTS_OPTION_STR(OPTION_OUTPUT))) { + if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) { retval = 1; goto cleanup; } @@ -778,13 +787,14 @@ srcdone: cleanup: util_debug("COM", "cleaning ...\n"); - ftepp_finish(); + if (ftepp) + ftepp_finish(ftepp); con_close(); vec_free(items); vec_free(ppems); if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) - parser_cleanup(); + parser_cleanup(parser); if (opts_output_free) mem_d(OPTS_OPTION_STR(OPTION_OUTPUT)); if (operators_free) diff --git a/opts.c b/opts.c index 4b76568..e37ba73 100644 --- a/opts.c +++ b/opts.c @@ -64,6 +64,7 @@ static void opts_setdefault() { opts_set(opts.flags, BAIL_ON_WERROR, true); opts_set(opts.flags, LEGACY_VECTOR_MATHS, true); opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true); + } void opts_backup_non_Wall() { @@ -96,6 +97,7 @@ void opts_init(const char *output, int standard, size_t arraysize) { OPTS_OPTION_STR(OPTION_OUTPUT) = (char*)output; OPTS_OPTION_U32(OPTION_STANDARD) = standard; OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize; + OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = 16; } static bool opts_setflag_all(const char *name, bool on, uint32_t *flags, const opts_flag_def *list, size_t listsize) { @@ -257,7 +259,7 @@ static size_t opts_ini_parse ( static bool opts_ini_bool(const char *value) { if (!strcmp(value, "true")) return true; if (!strcmp(value, "false")) return false; - return !!atoi(value); + return !!strtol(value, NULL, 10); } static char *opts_ini_load(const char *section, const char *name, const char *value) { diff --git a/opts.def b/opts.def index 90e6fce..f7b5cf1 100644 --- a/opts.def +++ b/opts.def @@ -109,6 +109,7 @@ GMQCC_DEFINE_FLAG(G) GMQCC_DEFINE_FLAG(STANDARD) GMQCC_DEFINE_FLAG(DEBUG) + GMQCC_DEFINE_FLAG(MEMDUMPCOLS) GMQCC_DEFINE_FLAG(MEMCHK) GMQCC_DEFINE_FLAG(DUMPFIN) GMQCC_DEFINE_FLAG(DUMP) diff --git a/pak.c b/pak.c index 043ef89..a9ef112 100644 --- a/pak.c +++ b/pak.c @@ -259,9 +259,10 @@ bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) { /* * Extraction abilities. These work as you expect them to. */ -bool pak_extract_one(pak_file_t *pak, const char *file) { - pak_directory_t *dir = NULL; - unsigned char *dat = NULL; +bool pak_extract_one(pak_file_t *pak, const char *file, const char *outdir) { + pak_directory_t *dir = NULL; + unsigned char *dat = NULL; + char *local = NULL; FILE *out; if (!pak_exists(pak, file, &dir)) { @@ -278,15 +279,20 @@ bool pak_extract_one(pak_file_t *pak, const char *file) { */ pak_tree_build(file); + /* TODO portable path seperators */ + util_asprintf(&local, "%s/%s", outdir, file); + /* * Now create the file, if this operation fails. Then abort * It shouldn't fail though. */ - if (!(out = fs_file_open(file, "wb"))) { + if (!(out = fs_file_open(local, "wb"))) { mem_d(dat); return false; } + /* free memory for directory string */ + mem_d(local); /* read */ fs_file_seek (pak->handle, dir->pos, SEEK_SET); @@ -310,11 +316,8 @@ bool pak_extract_all(pak_file_t *pak, const char *dir) { if (!fs_dir_make(dir)) return false; - if (fs_dir_change(dir)) - return false; - for (itr = 0; itr < vec_size(pak->directories); itr++) { - if (!pak_extract_one(pak, pak->directories[itr].name)) + if (!pak_extract_one(pak, pak->directories[itr].name, dir)) return false; } @@ -361,7 +364,7 @@ bool pak_insert_one(pak_file_t *pak, const char *file) { return false; } - strcpy(dir.name, file); + strncpy(dir.name, file, strlen(file)); /* * Allocate some memory for loading in the data that will be @@ -477,7 +480,6 @@ int main(int argc, char **argv) { bool extract = true; char *redirout = (char*)stdout; char *redirerr = (char*)stderr; - char *directory = NULL; char *file = NULL; char **files = NULL; pak_file_t *pak = NULL; @@ -498,8 +500,6 @@ int main(int argc, char **argv) { continue; if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false)) continue; - if (parsecmd("directory", &argc, &argv, &directory, 1, false)) - continue; if (parsecmd("file", &argc, &argv, &file, 1, false)) continue; @@ -542,7 +542,7 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - if (!pak_extract_all(pak, (directory) ? directory : "./")) { + if (!pak_extract_all(pak, "./")) { con_err("failed to extract PAK %s (files may be missing)\n", file); pak_close(pak); vec_free(files); @@ -562,13 +562,6 @@ int main(int argc, char **argv) { return EXIT_FAILURE; } - if (directory && !fs_dir_change(directory)) { - con_err("failed to change directory %s\n", directory); - pak_close(pak); - vec_free(files); - return EXIT_FAILURE; - } - for (iter = 0; iter < vec_size(files); iter++) { if (!(pak_insert_one(pak, files[iter]))) { con_err("failed inserting %s for PAK %s\n", files[iter], file); diff --git a/parser.c b/parser.c index 681207c..c1d3fb1 100644 --- a/parser.c +++ b/parser.c @@ -23,6 +23,7 @@ */ #include #include +#include #include "gmqcc.h" #include "lexer.h" @@ -34,7 +35,7 @@ #define PARSER_HT_SIZE 128 #define TYPEDEF_HT_SIZE 16 -typedef struct { +typedef struct parser_s { lex_file *lex; int tok; @@ -46,6 +47,8 @@ typedef struct { ast_value **imm_vector; size_t translated; + ht ht_imm_string; + /* must be deleted first, they reference immediates and values */ ast_value **accessors; @@ -252,12 +255,22 @@ static char *parser_strdup(const char *str) static ast_value* parser_const_string(parser_t *parser, const char *str, bool dotranslate) { - size_t i; + size_t hash = util_hthash(parser->ht_imm_string, str); ast_value *out; + if ( (out = util_htgeth(parser->ht_imm_string, str, hash)) ) { + if (dotranslate && out->name[0] == '#') { + char name[32]; + snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); + ast_value_set_name(out, name); + } + return out; + } + /* for (i = 0; i < vec_size(parser->imm_string); ++i) { if (!strcmp(parser->imm_string[i]->constval.vstring, str)) return parser->imm_string[i]; } + */ if (dotranslate) { char name[32]; snprintf(name, sizeof(name), "dotranslate_%lu", (unsigned long)(parser->translated++)); @@ -268,6 +281,7 @@ static ast_value* parser_const_string(parser_t *parser, const char *str, bool do out->hasvalue = true; out->constval.vstring = parser_strdup(str); vec_push(parser->imm_string, out); + util_htseth(parser->ht_imm_string, str, hash, out); return out; } @@ -379,6 +393,9 @@ static ast_value* parser_find_typedef(parser_t *parser, const char *name, size_t return NULL; } +/* include intrinsics */ +#include "intrin.h" + typedef struct { size_t etype; /* 0 = expression, others are operators */ @@ -961,10 +978,35 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) return false; } break; + case opid1('%'): + if (NotSameType(TYPE_FLOAT)) { + compile_error(ctx, "invalid types used in expression: cannot perform modulo operation between types %s and %s", + type_name[exprs[0]->expression.vtype], + type_name[exprs[1]->expression.vtype]); + return false; + } + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_float(parser, + (float)(((qcint)ConstF(0)) % ((qcint)ConstF(1)))); + } else { + /* generate a call to __builtin_mod */ + ast_expression *mod = intrin_func(parser, "mod"); + ast_call *call = NULL; + if (!mod) return false; /* can return null for missing floor */ + + call = ast_call_new(parser_ctx(parser), mod); + vec_push(call->params, exprs[0]); + vec_push(call->params, exprs[1]); + + out = (ast_expression*)call; + } + break; + case opid2('%','='): - compile_error(ctx, "qc does not have a modulo operator"); + compile_error(ctx, "%= is unimplemented"); return false; + case opid1('|'): case opid1('&'): if (NotSameType(TYPE_FLOAT)) { @@ -1071,6 +1113,26 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) out = (ast_expression*)ast_ternary_new(ctx, exprs[0], exprs[1], exprs[2]); break; + case opid2('*', '*'): + if (NotSameType(TYPE_FLOAT)) { + ast_type_to_string(exprs[0], ty1, sizeof(ty1)); + ast_type_to_string(exprs[1], ty2, sizeof(ty2)); + compile_error(ctx, "invalid types used in exponentiation: %s and %s", + ty1, ty2); + + return false; + } + + if (CanConstFold(exprs[0], exprs[1])) { + out = (ast_expression*)parser_const_float(parser, powf(ConstF(0), ConstF(1))); + } else { + ast_call *gencall = ast_call_new(parser_ctx(parser), intrin_func(parser, "pow")); + vec_push(gencall->params, exprs[0]); + vec_push(gencall->params, exprs[1]); + out = (ast_expression*)gencall; + } + break; + case opid3('<','=','>'): /* -1, 0, or 1 */ if (NotSameType(TYPE_FLOAT)) { ast_type_to_string(exprs[0], ty1, sizeof(ty1)); @@ -1416,7 +1478,8 @@ static bool parser_sy_apply_operator(parser_t *parser, shunt *sy) if(CanConstFold1(exprs[0])) out = (ast_expression*)parser_const_float(parser, ~(qcint)ConstF(0)); else - out = (ast_expression*)ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]); + out = (ast_expression*) + ast_binary_new(ctx, INSTR_SUB_F, (ast_expression*)parser_const_float_neg1(parser), exprs[0]); break; } #undef NotSameType @@ -1830,6 +1893,15 @@ static bool parse_sya_operand(parser_t *parser, shunt *sy, bool with_labels) if (!strcmp(parser_tokval(parser), "__builtin_debug_typestring")) { var = (ast_expression*)intrinsic_debug_typestring; } + /* now we try for the real intrinsic hashtable. If the string + * begins with __builtin, we simply skip past it, otherwise we + * use the identifier as is. + */ + else if (!strncmp(parser_tokval(parser), "__builtin_", 10)) { + var = intrin_func(parser, parser_tokval(parser) + 10 /* skip __builtin */); + } else { + var = intrin_func(parser, parser_tokval(parser)); + } if (!var) { char *correct = NULL; @@ -3654,6 +3726,8 @@ static bool parse_statement(parser_t *parser, ast_block *block, ast_expression * static bool parse_enum(parser_t *parser) { + bool flag = false; + bool reverse = false; qcfloat num = 0; ast_value **values = NULL; ast_value *var = NULL; @@ -3661,11 +3735,37 @@ static bool parse_enum(parser_t *parser) ast_expression *old; - if (!parser_next(parser) || parser->tok != '{') { - parseerror(parser, "expected `{` after `enum` keyword"); + if (!parser_next(parser) || (parser->tok != '{' && parser->tok != ':')) { + parseerror(parser, "expected `{` or `:` after `enum` keyword"); return false; } + /* enumeration attributes (can add more later) */ + if (parser->tok == ':') { + if (!parser_next(parser) || parser->tok != TOKEN_IDENT){ + parseerror(parser, "expected `flag` or `reverse` for enumeration attribute"); + return false; + } + + /* attributes? */ + if (!strcmp(parser_tokval(parser), "flag")) { + num = 1; + flag = true; + } + else if (!strcmp(parser_tokval(parser), "reverse")) { + reverse = true; + } + else { + parseerror(parser, "invalid attribute `%s` for enumeration", parser_tokval(parser)); + return false; + } + + if (!parser_next(parser) || parser->tok != '{') { + parseerror(parser, "expected `{` after enum attribute "); + return false; + } + } + while (true) { if (!parser_next(parser) || parser->tok != TOKEN_IDENT) { if (parser->tok == '}') { @@ -3689,8 +3789,9 @@ static bool parse_enum(parser_t *parser) vec_push(values, var); var->cvq = CV_CONST; var->hasvalue = true; - var->constval.vfloat = num++; + /* for flagged enumerations increment in POTs of TWO */ + var->constval.vfloat = (flag) ? (num *= 2) : (num ++); parser_addglobal(parser, var->name, (ast_expression*)var); if (!parser_next(parser)) { @@ -3729,6 +3830,13 @@ static bool parse_enum(parser_t *parser) } } + /* patch them all (for reversed attribute) */ + if (reverse) { + size_t i; + for (i = 0; i < vec_size(values); i++) + values[i]->constval.vfloat = vec_size(values) - i - 1; + } + if (parser->tok != '}') { parseerror(parser, "internal error: breaking without `}`"); goto onerror; @@ -4696,6 +4804,8 @@ static ast_value *parse_parameter_list(parser_t *parser, ast_value *var) on_error: if (argcounter) mem_d(argcounter); + if (varparam) + ast_delete(varparam); ast_delete(var); for (i = 0; i < vec_size(params); ++i) ast_delete(params[i]); @@ -5814,16 +5924,15 @@ static void generate_checksum(parser_t *parser) code_crc = crc; } -static parser_t *parser; - -bool parser_init() +parser_t *parser_create() { + parser_t *parser; lex_ctx empty_ctx; size_t i; parser = (parser_t*)mem_a(sizeof(parser_t)); if (!parser) - return false; + return NULL; memset(parser, 0, sizeof(*parser)); @@ -5836,7 +5945,7 @@ bool parser_init() if (!parser->assign_op) { printf("internal error: initializing parser: failed to find assign operator\n"); mem_d(parser); - return false; + return NULL; } vec_push(parser->variables, parser->htfields = util_htnew(PARSER_HT_SIZE)); @@ -5846,6 +5955,8 @@ bool parser_init() parser->aliases = util_htnew(PARSER_HT_SIZE); + parser->ht_imm_string = util_htnew(512); + /* corrector */ vec_push(parser->correct_variables, correct_trie_new()); vec_push(parser->correct_variables_score, NULL); @@ -5872,10 +5983,11 @@ bool parser_init() } else { parser->reserved_version = NULL; } - return true; + + return parser; } -bool parser_compile() +bool parser_compile(parser_t *parser) { /* initial lexer/parser state */ parser->lex->flags.noops = true; @@ -5907,27 +6019,27 @@ bool parser_compile() return !compile_errors; } -bool parser_compile_file(const char *filename) +bool parser_compile_file(parser_t *parser, const char *filename) { parser->lex = lex_open(filename); if (!parser->lex) { con_err("failed to open file \"%s\"\n", filename); return false; } - return parser_compile(); + return parser_compile(parser); } -bool parser_compile_string(const char *name, const char *str, size_t len) +bool parser_compile_string(parser_t *parser, const char *name, const char *str, size_t len) { parser->lex = lex_open_string(str, len, name); if (!parser->lex) { con_err("failed to create lexer for string \"%s\"\n", name); return false; } - return parser_compile(); + return parser_compile(parser); } -void parser_cleanup() +void parser_cleanup(parser_t *parser) { size_t i; for (i = 0; i < vec_size(parser->accessors); ++i) { @@ -5957,6 +6069,7 @@ void parser_cleanup() vec_free(parser->functions); vec_free(parser->imm_vector); vec_free(parser->imm_string); + util_htdel(parser->ht_imm_string); vec_free(parser->imm_float); vec_free(parser->globals); vec_free(parser->fields); @@ -5998,10 +6111,12 @@ void parser_cleanup() util_htdel(parser->aliases); + intrin_intrinsics_destroy(parser); + mem_d(parser); } -bool parser_finish(const char *output) +bool parser_finish(parser_t *parser, const char *output) { size_t i; ir_builder *ir; diff --git a/syntax/README b/syntax/README new file mode 100644 index 0000000..5a5c4b4 --- /dev/null +++ b/syntax/README @@ -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. diff --git a/syntax/geany/README b/syntax/geany/README new file mode 100644 index 0000000..c527d3f --- /dev/null +++ b/syntax/geany/README @@ -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/ diff --git a/syntax/geany/filetypes.qc b/syntax/geany/filetypes.qc new file mode 100644 index 0000000..d84bb7e --- /dev/null +++ b/syntax/geany/filetypes.qc @@ -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" diff --git a/syntax/gtksourceview/README b/syntax/gtksourceview/README new file mode 100644 index 0000000..73a6072 --- /dev/null +++ b/syntax/gtksourceview/README @@ -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/ diff --git a/syntax/gtksourceview/qc.lang b/syntax/gtksourceview/qc.lang new file mode 100644 index 0000000..827290f --- /dev/null +++ b/syntax/gtksourceview/qc.lang @@ -0,0 +1,173 @@ + + + + *.qc + // + /* + */ + + + +