Compare commits

..

No commits in common. "main" and "0.2.9" have entirely different histories.
main ... 0.2.9

162 changed files with 19952 additions and 19986 deletions

15
.gitignore vendored
View file

@ -1,15 +1,14 @@
*.o
*.d
*.orig
*.rej
*.patch
*.diff
*.exe
gmqcc
gmqpak
qcvm
testsuite
build/
.idea/
distro/archlinux/*
distro/archbsd/*
!distro/archlinux/git/PKGBUILD
!distro/archlinux/release/PKGBUILD
!distro/archbsd/release/PKGBUILD
!distro/archbsd/git/PKGBUILD
!distro/archlinux/this/Makefile

14
.travis.yml Normal file
View file

@ -0,0 +1,14 @@
language: c
compiler:
- gcc
- clang
# Change this to your needs
script: make && make check
notifications:
irc:
channels:
- "irc.freenode.org#kf-engine"
template:
- "[%{commit} : %{author}] %{message}"
- "%{build_url}"
skip_join: true

View file

@ -1,10 +1,9 @@
Authors:
Dale `graphitemaster` Weiler - Charismatic Visionary / Programmer
Wolfgang `Blub\w` Bumiller - Main Programmer
Dale Weiler - Charismatic Visionary / Programmer
Wolfgang Bumiller - Main Programmer
Thanks to:
Forest `LordHavoc` Hale - Technical support and assistance
Rudolf `divVerent` Polzer - Technical support and assistance
Matthias `matthiaskrgr` Krüger - Miscellaneous assistance
Samual `Samual` Lenks - Preprocessor assistance
Igor `ignatenkobrain` Gnatenko - Fedora packages

90
CHANGES Normal file
View file

@ -0,0 +1,90 @@
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.
- Added generic attribute syntax and reintroduced `noreturn`
as [[noreturn]].
- Added [[deprecated]] and [[deprecated("message")]].
- Support for `static` variables in functions.
- Support for labeled loops.
- UTF-8 Support
- enum support: without enum-types
(ie no `typedef enum { } foo;`)
- Accessing vector components via the dot operator on all
expressions. Eg: (3 * v).y
- 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
variables on error.
- Some problems with VM related vector-instructions issues
have been solved in both DP and our own executor. A new
compatbility option (enabled by default) has been added for
now: -flegacy-vector-maths
- Compiler intrinsics: __builtin_floor, __builtin_mod,
__builtin_exp, __builtin_isnan
- Improved memory tracing
- Speed improvements
* QCVM:
- Improved commandline argument handling.
- More builtins: sqrt(), normalize(), floor()
* Commandline:
- Nicer memory dumps
- Support for making individual warnings an error
- via -Werror-<warning>
- added --add-info
* Testsuite:
- Support for QCFLAGS to run tests with several additional
flags.
- Added support for preprocessor tests
- Added preprocessor tests
- Added defs.qh (auto included) for qcvm definitions
* Syntax Highlighting:
- Added various syntax highlighting description files for
various text editors / integrated development envirorments,
including support for: geany, kate, kwrite, kdevelop, QtCreator,
gtksourceview, gedit, sany, nano, jedit
* Build:
- Build scripts for building debian, archlinux and archbsd
packages for x86, and x86_64.
- Makefile targets for gource visualization, and render of
gource visualization.
2012-12-27 Hotfix v0.2.2
* Liferanges
* Crashes
2012-12-23 Hotfix v0.2.1
* General bugfixes
2012-12-23 Release 0.2
* Preprocessor:
- Added xonotic compatible preprocessor.
* Language
- Basic xonotic compatibility
- Array support
- Added fteqcc's string escape sequences.
- Support for `noref`.
- Support for `goto` with labels like in fteqcc.
- `break` and `continue`.
- Short circuit logic.
- Support for translatable strings via _("str") like in
fteqcc.
* Compilation
- Warnings about uninitialized values
2012-11-17 Release 0.1
* Compiles id1 code

View file

@ -1,35 +0,0 @@
cmake_minimum_required(VERSION 2.8)
project(gmqcc)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
set(SOURCE_FILES
algo.h
ast.cpp ast.h
code.cpp
conout.cpp
fold.cpp fold.h
ftepp.cpp
gmqcc.h
intrin.cpp intrin.h
ir.cpp
ir.h
lexer.cpp lexer.h
opts.cpp
parser.cpp parser.h
stat.cpp
utf8.cpp
util.cpp)
add_library(gmqcclib ${SOURCE_FILES})
add_executable(gmqcc main.cpp)
target_link_libraries(gmqcc gmqcclib)
add_executable(testsuite test.cpp)
target_link_libraries(testsuite gmqcclib)
add_executable(qcvm exec.cpp)
target_link_libraries(qcvm gmqcclib)

44
INSTALL Normal file
View file

@ -0,0 +1,44 @@
Installing gmqcc
1. Prerequisites
- A C-Compiler such as gcc or clang
- GNU Make. This document will assume GNU-Make to be executed via
`make'. On BSD systems you probably have to use `gmake' instead.
2. Compilation
Run the GNU make program `make' or `gmake'.
make
If no error appears, the following binary files will have been
created:
- gmqcc
- qcvm
3. Installation
The `install' target will install the 2 binaries to /usr/local/bin
by default.
The Makefile honors the following variables:
- DESTDIR: The installation directory root.
- PREFIX: The installation prefix, default: /usr/local
- BINDIR: Directory for binary executables,
deafult: $PREFIX/bin
To install to /usr/local run:
make install
To install to /usr run:
make PREFIX=/usr install
To install to a package-staging directory such as $pkgdir when
writing a build script file:
make DESTDIR=$pkgdir install
ArchLinux PKGBUILDs (release and git build) can be found in the
respective folders in ./distro/arch

View file

@ -1,4 +1,4 @@
Copyright (C) 2012, 2013, 2014, 2015
Copyright (C) 2012, 2013
Dale Weiler
Wolfgang Bumiller

420
Makefile
View file

@ -1,204 +1,266 @@
# Compilation options:
# * LTO - Link time optimization [default=0]
# * ASAN - Address sanitizer [default=0]
# * UBSAN - Undefined behavior sanitizer [default=0]
# * DEBUG - Debug build [default=0]
# * UNUSED - Remove unused references [default=1]
# * SRCDIR - Out of tree builds [default=./]
LTO ?= 0
ASAN ?= 0
UBSAN ?= 0
DEBUG ?= 0
UNUSED ?= 1
SRCDIR ?= ./
DESTDIR :=
OPTIONAL:=
PREFIX := /usr/local
BINDIR := $(PREFIX)/bin
DATADIR := $(PREFIX)/share
MANDIR := $(DATADIR)/man
# Determine if we're building for Windows or not so we can set the right file
# extensions for the binaries and exclude the testsuite because it doesn't build
# for that platform.
ifeq ($(OS),Windows_NT)
GMQCC := gmqcc.exe
QCVM := qcvm.exe
UNAME ?= $(shell uname)
CYGWIN = $(findstring CYGWIN, $(UNAME))
MINGW = $(findstring MINGW32, $(UNAME))
CC ?= clang
# linker flags and optional additional libraries if required
LDFLAGS +=
LIBS += -lm
CFLAGS += -Wall -Wextra -Werror -fno-strict-aliasing $(OPTIONAL)
ifneq ($(shell git describe --always 2>/dev/null),)
CFLAGS += -DGMQCC_GITINFO="\"$(shell git describe --always)\""
endif
#turn on tons of warnings if clang is present
# but also turn off the STUPID ONES
ifeq ($(CC), clang)
CFLAGS += \
-Weverything \
-Wno-padded \
-Wno-format-nonliteral \
-Wno-disabled-macro-expansion \
-Wno-conversion \
-Wno-missing-prototypes \
-Wno-float-equal \
-Wno-unknown-warning-option
else
GMQCC := gmqcc
QCVM := qcvm
TESTSUITE := testsuite
endif
# C++ compiler
CXX ?= clang++
# Build artifact directories
OBJDIR := .build/objs
DEPDIR := .build/deps
# Collect all the source files for GMQCC.
GSRCS := ast.cpp
GSRCS += code.cpp
GSRCS += conout.cpp
GSRCS += fold.cpp
GSRCS += ftepp.cpp
GSRCS += intrin.cpp
GSRCS += ir.cpp
GSRCS += lexer.cpp
GSRCS += main.cpp
GSRCS += opts.cpp
GSRCS += parser.cpp
GSRCS += stat.cpp
GSRCS += utf8.cpp
GSRCS += util.cpp
# Collect all the source files for QCVM.
QSRCS := exec.cpp
QSRCS += stat.cpp
QSRCS += util.cpp
# Collect all the source files for TESTSUITE.
TSRCS := conout.cpp
TSRCS += opts.cpp
TSRCS += stat.cpp
TSRCS += test.cpp
TSRCS += util.cpp
#
# Compilation flags
#
CXXFLAGS := -Wall
CXXFLAGS += -Wextra
CXXFLAGS += -Wno-parentheses
CXXFLAGS += -Wno-class-memaccess
CXXFLAGS += -Wno-implicit-fallthrough
CXXFLAGS += -std=c++11
# Disable some unneeded features.
CXXFLAGS += -fno-exceptions
CXXFLAGS += -fno-rtti
CXXFLAGS += -fno-asynchronous-unwind-tables
# Give each function and data it's own section so the linker can remove unused
# references to each, producing smaller, tighter binaries.
ifeq ($(UNUSED),1)
CXXFLAGS += -ffunction-sections
CXXFLAGS += -fdata-sections
endif
# Enable link-time optimizations if requested.
ifeq ($(LTO),1)
CXXFLAGS += -flto
endif
ifeq ($(DEBUG),1)
# Ensure there is a frame-pointer in debug builds.
CXXFLAGS += -fno-omit-frame-pointer
# Disable all optimizations in debug builds.
CXXFLAGS += -O0
# Enable debug symbols.
CXXFLAGS += -g
else
# Disable all the stack protection features in release builds.
CXXFLAGS += -fno-stack-protector
CXXFLAGS += -fno-stack-check
# Disable frame pointer in release builds when AddressSanitizer isn't present.
ifeq ($(ASAN),1)
CXXFLAGS += -fno-omit-frame-pointer
#Tiny C Compiler doesn't know what -pedantic-errors is
# and instead of ignoring .. just errors.
ifneq ($(CC), tcc)
CFLAGS += -pedantic-errors
else
CXXFLAGS += -fomit-frame-pointer
CFLAGS += -Wno-pointer-sign -fno-common
endif
# Highest optimization flag in release builds.
CXXFLAGS += -O3
endif
# Sanitizer selection
ifeq ($(ASAN),1)
CXXFLAGS += -fsanitize=address
endif
ifeq ($(UBSAN),1)
CXXFLAGS += -fsanitize=undefined
ifeq ($(track), no)
CFLAGS += -DNOTRACK
endif
#
# Dependency flags
#
DEPFLAGS := -MMD
DEPFLAGS += -MP
OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o fs.o utf8.o correct.o
OBJ_P = util.o fs.o conout.o opts.o pak.o
OBJ_T = test.o util.o conout.o fs.o
OBJ_C = main.o lexer.o parser.o fs.o
OBJ_X = exec-standalone.o util.o conout.o fs.o
#
# Linker flags
#
LDFLAGS :=
# Remove unreferenced sections
ifeq ($(UNUSED),1)
LDFLAGS += -Wl,--gc-sections
endif
# Enable link-time optimizations if request.
ifeq ($(LTO),1)
LDFLAGS += -flto
endif
# Sanitizer selection
ifeq ($(ASAN),1)
LDFLAGS += -fsanitize=address
endif
ifeq ($(UBSAN),1)
LDFLAGS += -fsanitize=undefined
endif
# Strip the binaries when not a debug build
ifneq (,$(findstring -g,$(CXXFLAGS)))
STRIP := true
ifneq ("$(CYGWIN)", "")
#nullify the common variables that
#most *nix systems have (for windows)
PREFIX :=
BINDIR :=
DATADIR :=
MANDIR :=
QCVM = qcvm.exe
GMQCC = gmqcc.exe
TESTSUITE = testsuite.exe
PAK = pak.exe
else
STRIP := strip
ifneq ("$(MINGW)", "")
#nullify the common variables that
#most *nix systems have (for windows)
PREFIX :=
BINDIR :=
DATADIR :=
MANDIR :=
QCVM = qcvm.exe
GMQCC = gmqcc.exe
TESTSUITE = testsuite.exe
PAK = pak.exe
else
QCVM = qcvm
GMQCC = gmqcc
TESTSUITE = testsuite
PAK = pak
endif
endif
all: $(GMQCC) $(QCVM) $(TESTSUITE)
#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 \
--logo doc/html/gmqcc.png \
-1280x720
# Build artifact directories.
$(DEPDIR):
@mkdir -p $(DEPDIR)
$(OBJDIR):
@mkdir -p $(OBJDIR)
#ffmpeg flags for gource
FFMPEGFLAGS= \
-y \
-r 60 \
-f image2pipe \
-vcodec ppm \
-i - \
-vcodec libx264 \
-preset ultrafast \
-crf 1 \
-threads 0 \
-bf 0
$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d | $(OBJDIR) $(DEPDIR)
$(CXX) -MT $@ $(DEPFLAGS) -MF $(DEPDIR)/$*.Td $(CXXFLAGS) -c -o $@ $<
@mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
#splint flags
SPLINTFLAGS = \
-redef \
-noeffect \
-nullderef \
-usedef \
-type \
-mustfreeonly \
-nullstate \
-varuse \
-mustfreefresh \
-compdestroy \
-compmempass \
-nullpass \
-onlytrans \
-predboolint \
-boolops \
-exportlocal \
-incondefs \
-macroredef \
-retvalint \
-nullret \
-predboolothers \
-globstate \
-dependenttrans \
-branchstate \
-compdef \
-temptrans \
-usereleased \
-warnposix \
-shiftimplementation \
+charindex \
-kepttrans \
-unqualifiedtrans \
+matchanyintegral \
+voidabstract \
-nullassign \
-unrecog \
-casebreak \
-retvalbool \
-retvalother \
-mayaliasunique \
-realcompare \
-observertrans \
-shiftnegative \
-freshtrans \
-abstract \
-statictrans \
-castfcnptr
$(GMQCC): $(filter %.o,$(GSRCS:%.cpp=$(OBJDIR)/%.o))
$(CXX) $^ $(LDFLAGS) -o $@
$(STRIP) $@
#standard rules
default: all
%.o: %.c
$(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS)
$(QCVM): $(filter %.o,$(QSRCS:%.cpp=$(OBJDIR)/%.o))
$(CXX) $^ $(LDFLAGS) -o $@
$(STRIP) $@
exec-standalone.o: exec.c
$(CC) -c $< -o $@ $(CPPFLAGS) $(CFLAGS) -DQCVM_EXECUTOR=1
$(TESTSUITE): $(filter %.o,$(TSRCS:%.cpp=$(OBJDIR)/%.o))
$(CXX) $^ $(LDFLAGS) -o $@
$(STRIP) $@
$(QCVM): $(OBJ_X)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
# Determine if the tests should be run.
RUNTESTS := true
ifdef TESTSUITE
RUNTESTS := ./$(TESTSUITE)
endif
$(GMQCC): $(OBJ_C) $(OBJ_D)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
test: $(QCVM) $(TESTSUITE)
@$(RUNTESTS)
$(TESTSUITE): $(OBJ_T)
$(CC) -o $@ $^ $(LDFLAGS) $(LIBS)
$(PAK): $(OBJ_P)
$(CC) -o $@ $^ $(LDFLAGS)
all: $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK)
check: all
@ ./$(TESTSUITE)
test: all
@ ./$(TESTSUITE)
clean:
rm -rf $(DEPDIR) $(OBJDIR)
rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) $(PAK) *.dat gource.mp4 *.exe
.PHONY: test clean $(DEPDIR) $(OBJDIR)
splint:
@ splint $(SPLINTFLAGS) *.c *.h
# Dependencies
$(filter %.d,$(GSRCS:%.cpp=$(DEPDIR)/%.d)):
include $(wildcard $@)
gource:
@ gource $(GOURCEFLAGS)
$(filter %.d,$(QSRCS:%.cpp=$(DEPDIR)/%.d)):
include $(wildcard $@)
gource-record:
@ gource $(GOURCEFLAGS) -o - | ffmpeg $(FFMPEGFLAGS) gource.mp4
$(filter %.d,$(TSRCS:%.cpp=$(DEPDIR)/%.d)):
include $(wildcard $@)
depend:
@makedepend -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_D))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_T))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_C))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_X))
@makedepend -a -Y -w 65536 2> /dev/null \
$(subst .o,.c,$(OBJ_P))
#install rules
install: install-gmqcc install-qcvm install-doc
install-gmqcc: $(GMQCC)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(GMQCC) $(DESTDIR)$(BINDIR)/$(GMQCC)
install-qcvm: $(QCVM)
install -d -m755 $(DESTDIR)$(BINDIR)
install -m755 $(QCVM) $(DESTDIR)$(BINDIR)/$(QCVM)
install-doc:
install -d -m755 $(DESTDIR)$(MANDIR)/man1
install -m644 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/
install -m644 doc/qcvm.1 $(DESTDIR)$(MANDIR)/man1/
uninstall:
rm $(DESTDIR)$(BINDIR)/gmqcc
rm $(DESTDIR)$(BINDIR)/qcvm
rm $(DESTDIR)$(MANDIR)/man1/doc/gmqcc.1
rm $(DESTDIR)$(MANDIR)/man1/doc/qcvm.1
# DO NOT DELETE
util.o: gmqcc.h opts.def
code.o: gmqcc.h opts.def
ast.o: gmqcc.h opts.def ast.h ir.h
ir.o: gmqcc.h opts.def ir.h
conout.o: gmqcc.h opts.def
ftepp.o: gmqcc.h opts.def lexer.h
opts.o: gmqcc.h opts.def
fs.o: gmqcc.h opts.def
utf8.o: gmqcc.h opts.def
correct.o: gmqcc.h opts.def
test.o: gmqcc.h opts.def
util.o: gmqcc.h opts.def
conout.o: gmqcc.h opts.def
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 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

15
README
View file

@ -1 +1,14 @@
An improved QuakeC compiler
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 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

157
TODO Normal file
View file

@ -0,0 +1,157 @@
GMQCC is quite feature complete. But that doesn't address the fact that
it can be improved. This is a list of things that we'd like to support
in the distant future. When the time comes, we can just select a topic
from here and open a ticket for it on the issue tracker. But for the
meantime, this is sort of a cultivating flat file database.
Optimizations:
The following are optimizations that can be implemented after the
transformation into static-single assignment (SSA).
Global Value Numbering:
Eliminate redundancy by constructing a value graph of the source
then determining which values are computed by equivalent expressions.
Similar to Common Subexpression Elimination (CSE), however expressions
are determined via underlying equivalence, opposed to lexically identical
expressions (CSE).
Spare Conditional Constant Propagation:
Simultaneously remove dead code and propagates constants. This is
not the same as individual dead code elimination and constant propagation
passes. This is multipass.
The following are optimizations that can be implemented before the
transformation into a binary (code generator).
Code factoring:
The process of finding sequences of code that are identical,
or can be parameterized or reordered to be identical.
Which can be replaced with calls to a shared subroutine. To
reduce duplicated code. (Size optimization)
The following are optimizations that can be implemented anywhere, ideally
these are functional language optimizations.
Removing Recursion:
Tail recursive algorithms can be converted to iteration, which
does not have to have call overhead.
Language Features:
The following are language features that we'd like to see implemented in the
future.
Enumerations:
Like C
AST Macros:
Macros with sanity. Not textual substiution.
Classes:
Like C++, but minus the stupidity:
- No type operator overloads
- Keep operator overloading for basic operators though.
- No inheritance
- No virtuals / pure virtuals
- Essentially "C structs but with operators" :)
Arrays:
They're currently implemented, but support in the engine
plus implicit bounds checks (and ability to turn the bounds
checking off)
Exceptions:
I feel like all languages suck at implementing this. This would
require support from the engine, but it would help catch bugs. We
could make it fast using a neat method of "frame pointers".
Overloaded Functions:
Ability to make individual functions with the same name, but take
different amount of arguments or type of arguments.
Default Argument Substiution:
Ability to specify default values for arguments in functions.
void foo(string bar, string baz="default");
Supplying just one argument will expand the second argument to
become "default", otherwise if two arguments are specified then
the "default" string is overrode with what ever the user passes.
Character Type:
A char type would be nice to have. Essentially implemented as a
string, we can both "get" and "set" indices inside strings with
the help of builtin functions.
{
string foo = "test";
foo[0] = 'r';
print("it's time to ", foo);
}
Array Accessor With C-Semantics:
Also the ability to use them as array accessors:
{
float hugearray['Z'];
hugearray['a'] = 100.0f;
}
Keep existing "pointer-like" semantics as well. In C arrays
simple work as pointers, a[1] -> *(a+1), or 1[a] -> *(1+a)
so we should allow both forms of syntax. As well as operand
reversal.
{
float h['Z'];
*(h+'a') = 100;
*('a'+h) = 'a'[h];
}
FTEQCC Inline Assembly:
This is still up for debate, mainly because a) it's syntax is
just utter crap. b) If we do an assembler, it should be nice.
we could provide a -std=fteqcc for the assembler itself :P
just like the compiler; although I think that's just insane.
Please see Assembler below.
Namespaces:
There is already a ticket open on this. They'd work just like C++
identically even.
Standalone QCVM:
The following are QCVM additions:
Proper ASM disassembly:
Proper disassembly of compiled .dat files. Annotated if possible
when -g (is used during compilation)
Debugging:
A step-through debugger -d (with separate compilation as well)
Called -> qcdb Optionally alias to qcvm -d :)
We should be able to see the assembly and source it matches to
and the state of OFS_* and calls.
Testsuite:
The following are things we'd like to see added to the testsuite
in the distant future:
Multithreading:
Chances are when we start adding more and more tests, executing
them individually will be midly slow (even if that's a whole minute)
It would be nice to add a -j paramater to allow multiple threads to
be used and so we can execute many tests in parallel.
Interface:
Ability to select individual tests, or set parameters manually
opposed to using the static task-template files. (A method to
override them rather).
Assembler:
Possibly support for a future assembler for QCASM. But we're not
entirely sure if it makes sense.

18
algo.h
View file

@ -1,18 +0,0 @@
#ifndef GMQCC_ALGO_HDR
#define GMQCC_ALGO_HDR
namespace algo {
template<typename ITER>
void shiftback(ITER element, ITER end) {
//typename ITER::value_type backup(move(*element)); // hold the element
typename std::remove_reference<decltype(*element)>::type backup(move(*element)); // hold the element
ITER p = element++;
for (; element != end; p = element++)
*p = move(*element);
*p = move(backup);
}
} // ::algo
#endif

3084
ast.c Normal file

File diff suppressed because it is too large Load diff

3125
ast.cpp

File diff suppressed because it is too large Load diff

841
ast.h

File diff suppressed because it is too large Load diff

276
code.c Normal file
View file

@ -0,0 +1,276 @@
/*
* Copyright (C) 2012, 2013
* Dale Weiler
* Wolfgang Bumiller
*
* 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.
*/
#include "gmqcc.h"
/* This is outrageous! */
#define QCINT_ENTRY void*
#define QCINT_TO_HASH_ENTRY(q) ((void*)(uintptr_t)(q))
#define HASH_ENTRY_TO_QCINT(h) ((qcint)(uintptr_t)(h))
void code_push_statement(code_t *code, prog_section_statement *stmt, int linenum)
{
vec_push(code->statements, *stmt);
vec_push(code->linenums, linenum);
}
void code_pop_statement(code_t *code)
{
vec_pop(code->statements);
vec_pop(code->linenums);
}
code_t *code_init() {
static prog_section_function empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
static prog_section_statement empty_statement = {0,{0},{0},{0}};
static prog_section_def empty_def = {0, 0, 0};
code_t *code = (code_t*)mem_a(sizeof(code_t));
int i = 0;
memset(code, 0, sizeof(code_t));
code->entfields = 0;
code->string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
/*
* The way progs.dat is suppose to work is odd, there needs to be
* some null (empty) statements, functions, and 28 globals
*/
for(; i < 28; i++)
vec_push(code->globals, 0);
vec_push(code->chars, '\0');
vec_push(code->functions, empty_function);
code_push_statement(code, &empty_statement, 0);
vec_push(code->defs, empty_def);
vec_push(code->fields, empty_def);
return code;
}
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
uint32_t code_genstring(code_t *code, const char *str)
{
uint32_t off;
size_t hash;
QCINT_ENTRY existing;
if (!str)
return 0;
if (!*str) {
if (!code->string_cached_empty) {
code->string_cached_empty = vec_size(code->chars);
vec_push(code->chars, 0);
}
return code->string_cached_empty;
}
if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
hash = ((unsigned char*)str)[strlen(str)-1];
existing = code_util_str_htgeth(code->string_cache, str, hash);
} else {
hash = util_hthash(code->string_cache, str);
existing = util_htgeth(code->string_cache, str, hash);
}
if (existing)
return HASH_ENTRY_TO_QCINT(existing);
off = vec_size(code->chars);
vec_upload(code->chars, str, strlen(str)+1);
util_htseth(code->string_cache, str, hash, QCINT_TO_HASH_ENTRY(off));
return off;
}
qcint code_alloc_field (code_t *code, size_t qcsize)
{
qcint pos = (qcint)code->entfields;
code->entfields += qcsize;
return pos;
}
bool code_write(code_t *code, const char *filename, const char *lnofile) {
prog_header code_header;
FILE *fp = NULL;
size_t it = 2;
code_header.statements.offset = sizeof(prog_header);
code_header.statements.length = vec_size(code->statements);
code_header.defs.offset = code_header.statements.offset + (sizeof(prog_section_statement) * vec_size(code->statements));
code_header.defs.length = vec_size(code->defs);
code_header.fields.offset = code_header.defs.offset + (sizeof(prog_section_def) * vec_size(code->defs));
code_header.fields.length = vec_size(code->fields);
code_header.functions.offset = code_header.fields.offset + (sizeof(prog_section_field) * vec_size(code->fields));
code_header.functions.length = vec_size(code->functions);
code_header.globals.offset = code_header.functions.offset + (sizeof(prog_section_function) * vec_size(code->functions));
code_header.globals.length = vec_size(code->globals);
code_header.strings.offset = code_header.globals.offset + (sizeof(int32_t) * vec_size(code->globals));
code_header.strings.length = vec_size(code->chars);
code_header.version = 6;
if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
code_header.crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
else
code_header.crc16 = code->crc;
code_header.entfield = code->entfields;
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
util_debug("GEN", "Patching stringtable for -fdarkplaces-stringtablebug\n");
/* >= + P */
vec_push(code->chars, '\0'); /* > */
vec_push(code->chars, '\0'); /* = */
vec_push(code->chars, '\0'); /* P */
}
/* ensure all data is in LE format */
util_endianswap(&code_header.version, 1, sizeof(code_header.version));
util_endianswap(&code_header.crc16, 1, sizeof(code_header.crc16));
util_endianswap(&code_header.statements, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.defs, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.fields, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.functions, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.strings, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.globals, 2, sizeof(code_header.statements.offset));
util_endianswap(&code_header.entfield, 1, sizeof(code_header.entfield));
util_endianswap(code->statements, vec_size(code->statements), sizeof(prog_section_statement));
util_endianswap(code->defs, vec_size(code->defs), sizeof(prog_section_def));
util_endianswap(code->fields, vec_size(code->fields), sizeof(prog_section_field));
util_endianswap(code->functions, vec_size(code->functions), sizeof(prog_section_function));
util_endianswap(code->globals, vec_size(code->globals), sizeof(int32_t));
if (lnofile) {
uint32_t version = 1;
fp = fs_file_open(lnofile, "wb");
if (!fp)
return false;
util_endianswap(&version, 1, sizeof(version));
util_endianswap(code->linenums, vec_size(code->linenums), sizeof(code->linenums[0]));
if (fs_file_write("LNOF", 4, 1, fp) != 1 ||
fs_file_write(&version, sizeof(version), 1, fp) != 1 ||
fs_file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
fs_file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
fs_file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
fs_file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
fs_file_write(code->linenums, sizeof(code->linenums[0]), vec_size(code->linenums), fp) != vec_size(code->linenums))
{
con_err("failed to write lno file\n");
}
fs_file_close(fp);
fp = NULL;
}
fp = fs_file_open(filename, "wb");
if (!fp)
return false;
if (1 != fs_file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
vec_size(code->statements) != fs_file_write(code->statements, sizeof(prog_section_statement), vec_size(code->statements), fp) ||
vec_size(code->defs) != fs_file_write(code->defs, sizeof(prog_section_def) , vec_size(code->defs) , fp) ||
vec_size(code->fields) != fs_file_write(code->fields, sizeof(prog_section_field) , vec_size(code->fields) , fp) ||
vec_size(code->functions) != fs_file_write(code->functions, sizeof(prog_section_function) , vec_size(code->functions) , fp) ||
vec_size(code->globals) != fs_file_write(code->globals, sizeof(int32_t) , vec_size(code->globals) , fp) ||
vec_size(code->chars) != fs_file_write(code->chars, 1 , vec_size(code->chars) , fp))
{
fs_file_close(fp);
return false;
}
util_debug("GEN","HEADER:\n");
util_debug("GEN"," version: = %d\n", code_header.version );
util_debug("GEN"," crc16: = %d\n", code_header.crc16 );
util_debug("GEN"," entfield: = %d\n", code_header.entfield);
util_debug("GEN"," statements = {.offset = % 8d, .length = % 8d}\n", code_header.statements.offset, code_header.statements.length);
util_debug("GEN"," defs = {.offset = % 8d, .length = % 8d}\n", code_header.defs .offset, code_header.defs .length);
util_debug("GEN"," fields = {.offset = % 8d, .length = % 8d}\n", code_header.fields .offset, code_header.fields .length);
util_debug("GEN"," functions = {.offset = % 8d, .length = % 8d}\n", code_header.functions .offset, code_header.functions .length);
util_debug("GEN"," globals = {.offset = % 8d, .length = % 8d}\n", code_header.globals .offset, code_header.globals .length);
util_debug("GEN"," strings = {.offset = % 8d, .length = % 8d}\n", code_header.strings .offset, code_header.strings .length);
/* FUNCTIONS */
util_debug("GEN", "FUNCTIONS:\n");
for (; it < vec_size(code->functions); it++) {
size_t j = code->functions[it].entry;
util_debug("GEN", " {.entry =% 5d, .firstlocal =% 5d, .locals =% 5d, .profile =% 5d, .name =% 5d, .file =% 5d, .nargs =% 5d, .argsize ={%d,%d,%d,%d,%d,%d,%d,%d} }\n",
code->functions[it].entry,
code->functions[it].firstlocal,
code->functions[it].locals,
code->functions[it].profile,
code->functions[it].name,
code->functions[it].file,
code->functions[it].nargs,
code->functions[it].argsize[0],
code->functions[it].argsize[1],
code->functions[it].argsize[2],
code->functions[it].argsize[3],
code->functions[it].argsize[4],
code->functions[it].argsize[5],
code->functions[it].argsize[6],
code->functions[it].argsize[7]
);
util_debug("GEN", " NAME: %s\n", &code->chars[code->functions[it].name]);
/* Internal functions have no code */
if (code->functions[it].entry >= 0) {
util_debug("GEN", " CODE:\n");
for (;;) {
if (code->statements[j].opcode != INSTR_DONE)
util_debug("GEN", " %-12s {% 5i,% 5i,% 5i}\n",
asm_instr[code->statements[j].opcode].m,
code->statements[j].o1.s1,
code->statements[j].o2.s1,
code->statements[j].o3.s1
);
else {
util_debug("GEN", " DONE {0x00000,0x00000,0x00000}\n");
break;
}
j++;
}
}
}
vec_free(code->statements);
vec_free(code->linenums);
vec_free(code->defs);
vec_free(code->fields);
vec_free(code->functions);
vec_free(code->globals);
vec_free(code->chars);
util_htdel(code->string_cache);
fs_file_close(fp);
mem_d(code);
return true;
}

348
code.cpp
View file

@ -1,348 +0,0 @@
#include <string.h>
#include "gmqcc.h"
/*
* We could use the old method of casting to uintptr_t then to void*
* or qcint_t; however, it's incredibly unsafe for two reasons.
* 1) The compilers aliasing optimization can legally make it unstable
* (it's undefined behaviour).
*
* 2) The cast itself depends on fresh storage (newly allocated in which
* ever function is using the cast macros), the contents of which are
* transferred in a way that the obligation to release storage is not
* propagated.
*/
typedef union {
void *enter;
qcint_t leave;
} code_hash_entry_t;
/* Some sanity macros */
#define CODE_HASH_ENTER(ENTRY) ((ENTRY).enter)
#define CODE_HASH_LEAVE(ENTRY) ((ENTRY).leave)
void code_push_statement(code_t *code, prog_section_statement_t *stmt_in, lex_ctx_t ctx)
{
prog_section_statement_t stmt = *stmt_in;
if (OPTS_FLAG(TYPELESS_STORES)) {
switch (stmt.opcode) {
case INSTR_LOAD_S:
case INSTR_LOAD_ENT:
case INSTR_LOAD_FLD:
case INSTR_LOAD_FNC:
stmt.opcode = INSTR_LOAD_F;
break;
case INSTR_STORE_S:
case INSTR_STORE_ENT:
case INSTR_STORE_FLD:
case INSTR_STORE_FNC:
stmt.opcode = INSTR_STORE_F;
break;
case INSTR_STOREP_S:
case INSTR_STOREP_ENT:
case INSTR_STOREP_FLD:
case INSTR_STOREP_FNC:
stmt.opcode = INSTR_STOREP_F;
break;
}
}
if (OPTS_FLAG(SORT_OPERANDS)) {
uint16_t pair;
switch (stmt.opcode) {
case INSTR_MUL_F:
case INSTR_MUL_V:
case INSTR_ADD_F:
case INSTR_EQ_F:
case INSTR_EQ_S:
case INSTR_EQ_E:
case INSTR_EQ_FNC:
case INSTR_NE_F:
case INSTR_NE_V:
case INSTR_NE_S:
case INSTR_NE_E:
case INSTR_NE_FNC:
case INSTR_AND:
case INSTR_OR:
case INSTR_BITAND:
case INSTR_BITOR:
if (stmt.o1.u1 < stmt.o2.u1) {
uint16_t a = stmt.o2.u1;
stmt.o1.u1 = stmt.o2.u1;
stmt.o2.u1 = a;
}
break;
case INSTR_MUL_VF: pair = INSTR_MUL_FV; goto case_pair_gen;
case INSTR_MUL_FV: pair = INSTR_MUL_VF; goto case_pair_gen;
case INSTR_LT: pair = INSTR_GT; goto case_pair_gen;
case INSTR_GT: pair = INSTR_LT; goto case_pair_gen;
case INSTR_LE: pair = INSTR_GE; goto case_pair_gen;
case INSTR_GE: pair = INSTR_LE;
case_pair_gen:
if (stmt.o1.u1 < stmt.o2.u1) {
uint16_t x = stmt.o1.u1;
stmt.o1.u1 = stmt.o2.u1;
stmt.o2.u1 = x;
stmt.opcode = pair;
}
break;
}
}
code->statements.push_back(stmt);
code->linenums.push_back(ctx.line);
code->columnnums.push_back(ctx.column);
}
void code_pop_statement(code_t *code)
{
code->statements.pop_back();
code->linenums.pop_back();
code->columnnums.pop_back();
}
void *code_t::operator new(std::size_t bytes) {
return mem_a(bytes);
}
void code_t::operator delete(void *ptr) {
mem_d(ptr);
}
code_t::code_t()
{
static lex_ctx_t empty_ctx = {0, 0, 0};
static prog_section_function_t empty_function = {0,0,0,0,0,0,0,{0,0,0,0,0,0,0,0}};
static prog_section_statement_t empty_statement = {0,{0},{0},{0}};
static prog_section_def_t empty_def = {0, 0, 0};
string_cache = util_htnew(OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS) ? 0x100 : 1024);
// The way progs.dat is suppose to work is odd, there needs to be
// some null (empty) statements, functions, and 28 globals
globals.insert(globals.begin(), 28, 0);
chars.push_back('\0');
functions.push_back(empty_function);
code_push_statement(this, &empty_statement, empty_ctx);
defs.push_back(empty_def);
fields.push_back(empty_def);
}
code_t::~code_t()
{
util_htdel(string_cache);
}
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
uint32_t code_genstring(code_t *code, const char *str) {
size_t hash;
code_hash_entry_t existing;
if (!str)
return 0;
if (!*str) {
if (!code->string_cached_empty) {
code->string_cached_empty = code->chars.size();
code->chars.push_back(0);
}
return code->string_cached_empty;
}
if (OPTS_OPTIMIZATION(OPTIM_OVERLAP_STRINGS)) {
hash = ((unsigned char*)str)[strlen(str)-1];
CODE_HASH_ENTER(existing) = code_util_str_htgeth(code->string_cache, str, hash);
} else {
hash = util_hthash(code->string_cache, str);
CODE_HASH_ENTER(existing) = util_htgeth(code->string_cache, str, hash);
}
if (CODE_HASH_ENTER(existing))
return CODE_HASH_LEAVE(existing);
CODE_HASH_LEAVE(existing) = code->chars.size();
code->chars.insert(code->chars.end(), str, str + strlen(str) + 1);
util_htseth(code->string_cache, str, hash, CODE_HASH_ENTER(existing));
return CODE_HASH_LEAVE(existing);
}
qcint_t code_alloc_field (code_t *code, size_t qcsize)
{
qcint_t pos = (qcint_t)code->entfields;
code->entfields += qcsize;
return pos;
}
static size_t code_size_generic(code_t *code, prog_header_t *code_header, bool lno) {
size_t size = 0;
if (lno) {
size += 4; /* LNOF */
size += sizeof(uint32_t); /* version */
size += sizeof(code_header->defs.length);
size += sizeof(code_header->globals.length);
size += sizeof(code_header->fields.length);
size += sizeof(code_header->statements.length);
size += sizeof(code->linenums[0]) * code->linenums.size();
size += sizeof(code->columnnums[0]) * code->columnnums.size();
} else {
size += sizeof(prog_header_t);
size += sizeof(prog_section_statement_t) * code->statements.size();
size += sizeof(prog_section_def_t) * code->defs.size();
size += sizeof(prog_section_field_t) * code->fields.size();
size += sizeof(prog_section_function_t) * code->functions.size();
size += sizeof(int32_t) * code->globals.size();
size += 1 * code->chars.size();
}
return size;
}
#define code_size_binary(C, H) code_size_generic((C), (H), false)
#define code_size_debug(C, H) code_size_generic((C), (H), true)
static void code_create_header(code_t *code, prog_header_t *code_header, const char *filename, const char *lnofile) {
size_t i;
code_header->statements.offset = sizeof(prog_header_t);
code_header->statements.length = code->statements.size();
code_header->defs.offset = code_header->statements.offset + (sizeof(prog_section_statement_t) * code->statements.size());
code_header->defs.length = code->defs.size();
code_header->fields.offset = code_header->defs.offset + (sizeof(prog_section_def_t) * code->defs.size());
code_header->fields.length = code->fields.size();
code_header->functions.offset = code_header->fields.offset + (sizeof(prog_section_field_t) * code->fields.size());
code_header->functions.length = code->functions.size();
code_header->globals.offset = code_header->functions.offset + (sizeof(prog_section_function_t) * code->functions.size());
code_header->globals.length = code->globals.size();
code_header->strings.offset = code_header->globals.offset + (sizeof(int32_t) * code->globals.size());
code_header->strings.length = code->chars.size();
code_header->version = 6;
code_header->skip = 0;
if (OPTS_OPTION_BOOL(OPTION_FORCECRC))
code_header->crc16 = OPTS_OPTION_U16(OPTION_FORCED_CRC);
else
code_header->crc16 = code->crc;
code_header->entfield = code->entfields;
if (OPTS_FLAG(DARKPLACES_STRING_TABLE_BUG)) {
/* >= + P */
code->chars.push_back('\0'); /* > */
code->chars.push_back('\0'); /* = */
code->chars.push_back('\0'); /* P */
}
/* ensure all data is in LE format */
util_swap_header(*code_header);
util_swap_statements(code->statements);
util_swap_defs_fields(code->defs);
util_swap_defs_fields(code->fields);
util_swap_functions(code->functions);
util_swap_globals(code->globals);
if (!OPTS_OPTION_BOOL(OPTION_QUIET)) {
if (lnofile)
con_out("writing '%s' and '%s'...\n", filename, lnofile);
else
con_out("writing '%s'\n", filename);
}
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
{
char buffer[1024];
con_out("\nOptimizations:\n");
for (i = 0; i < COUNT_OPTIMIZATIONS; ++i) {
if (opts_optimizationcount[i]) {
util_optimizationtostr(opts_opt_list[i].name, buffer, sizeof(buffer));
con_out(
" %s: %u\n",
buffer,
(unsigned int)opts_optimizationcount[i]
);
}
}
}
}
static void code_stats(const char *filename, const char *lnofile, code_t *code, prog_header_t *code_header) {
if (OPTS_OPTION_BOOL(OPTION_QUIET) ||
OPTS_OPTION_BOOL(OPTION_PP_ONLY))
return;
con_out("\nFile statistics:\n");
con_out(" dat:\n");
con_out(" name: %s\n", filename);
con_out(" size: %u (bytes)\n", code_size_binary(code, code_header));
con_out(" crc: 0x%04X\n", code->crc);
if (lnofile) {
con_out(" lno:\n");
con_out(" name: %s\n", lnofile);
con_out(" size: %u (bytes)\n", code_size_debug(code, code_header));
}
con_out("\n");
}
bool code_write(code_t *code, const char *filename, const char *lnofile) {
prog_header_t code_header;
FILE *fp = nullptr;
code_create_header(code, &code_header, filename, lnofile);
if (lnofile) {
uint32_t version = 1;
fp = fopen(lnofile, "wb");
if (!fp)
return false;
util_endianswap(&version, 1, sizeof(version));
util_endianswap(&code->linenums[0], code->linenums.size(), sizeof(code->linenums[0]));
util_endianswap(&code->columnnums[0], code->columnnums.size(), sizeof(code->columnnums[0]));
if (fwrite("LNOF", 4, 1, fp) != 1 ||
fwrite(&version, sizeof(version), 1, fp) != 1 ||
fwrite(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
fwrite(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
fwrite(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
fwrite(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
fwrite(&code->linenums[0], sizeof(code->linenums[0]), code->linenums.size(), fp) != code->linenums.size() ||
fwrite(&code->columnnums[0], sizeof(code->columnnums[0]), code->columnnums.size(), fp) != code->columnnums.size())
{
con_err("failed to write lno file\n");
}
fclose(fp);
fp = nullptr;
}
fp = fopen(filename, "wb");
if (!fp)
return false;
if (1 != fwrite(&code_header, sizeof(prog_header_t) , 1 , fp) ||
code->statements.size() != fwrite(&code->statements[0], sizeof(prog_section_statement_t), code->statements.size(), fp) ||
code->defs.size() != fwrite(&code->defs[0], sizeof(prog_section_def_t) , code->defs.size() , fp) ||
code->fields.size() != fwrite(&code->fields[0], sizeof(prog_section_field_t) , code->fields.size() , fp) ||
code->functions.size() != fwrite(&code->functions[0], sizeof(prog_section_function_t) , code->functions.size() , fp) ||
code->globals.size() != fwrite(&code->globals[0], sizeof(int32_t) , code->globals.size() , fp) ||
code->chars.size() != fwrite(&code->chars[0], 1 , code->chars.size() , fp))
{
fclose(fp);
return false;
}
fclose(fp);
code_stats(filename, lnofile, code, &code_header);
return true;
}

448
conout.c Normal file
View file

@ -0,0 +1,448 @@
/*
* 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.
*/
#include "gmqcc.h"
#include <stdio.h>
/*
* isatty/STDERR_FILENO/STDOUT_FILNO
* + some other things likewise.
*/
#ifndef _WIN32
# include <unistd.h>
#else
# include <io.h>
/*
* Windows and it's posix underscore bullshit. We simply fix this
* with yay, another macro :P
*/
# define isatty _isatty
#endif
#define GMQCC_IS_STDOUT(X) ((FILE*)((void*)X) == stdout)
#define GMQCC_IS_STDERR(X) ((FILE*)((void*)X) == stderr)
#define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X))
typedef struct {
FILE *handle_err;
FILE *handle_out;
int color_err;
int color_out;
} con_t;
/*
* Doing colored output on windows is fucking stupid. The linux way is
* the real way. So we emulate it on windows :)
*/
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
/*
* Windows doesn't have constants for FILENO, sadly but the docs tell
* use the constant values.
*/
#undef STDERR_FILENO
#undef STDOUT_FILENO
#define STDERR_FILENO 2
#define STDOUT_FILENO 1
enum {
RESET = 0,
BOLD = 1,
BLACK = 30,
RED,
GREEN,
YELLOW,
BLUE,
MAGENTA,
CYAN,
GRAY,
WHITE = GRAY
};
enum {
WBLACK,
WBLUE,
WGREEN = 2,
WRED = 4,
WINTENSE = 8,
WCYAN = WBLUE | WGREEN,
WMAGENTA = WBLUE | WRED,
WYELLOW = WGREEN | WRED,
WWHITE = WBLUE | WGREEN | WRED
};
static const int ansi2win[] = {
WBLACK,
WRED,
WGREEN,
WYELLOW,
WBLUE,
WMAGENTA,
WCYAN,
WWHITE
};
static int win_fputs(FILE *h, const char *str) {
/* state for translate */
int acolor;
int wcolor;
int icolor;
int state = 0;
/* attributes */
int intense = -1;
int colors[] = {-1, -1 };
int colorpos = 1;
int length = 0;
CONSOLE_SCREEN_BUFFER_INFO cinfo;
GetConsoleScreenBufferInfo (
(GMQCC_IS_STDOUT(h)) ?
GetStdHandle(STD_OUTPUT_HANDLE) :
GetStdHandle(STD_ERROR_HANDLE), &cinfo
);
icolor = cinfo.wAttributes;
while (*str) {
if (*str == '\x1B')
state = '\x1B';
else if (state == '\x1B' && *str == '[')
state = '[';
else if (state == '[') {
if (*str != 'm') {
colors[colorpos] = *str;
colorpos--;
} else {
int find;
int mult;
for (find = colorpos + 1, acolor = 0, mult = 1; find < 2; find++) {
acolor += (colors[find] - 48) * mult;
mult *= 10;
}
/* convert to windows color */
if (acolor == BOLD)
intense = WINTENSE;
else if (acolor == RESET) {
intense = WBLACK;
wcolor = icolor;
}
else if (BLACK <= acolor && acolor <= WHITE)
wcolor = ansi2win[acolor - 30];
else if (acolor == 90) {
/* special gray really white man */
wcolor = WWHITE;
intense = WBLACK;
}
SetConsoleTextAttribute (
(GMQCC_IS_STDOUT(h)) ?
GetStdHandle(STD_OUTPUT_HANDLE) :
GetStdHandle(STD_ERROR_HANDLE),
wcolor | intense | (icolor & 0xF0)
);
colorpos = 1;
state = -1;
}
} else {
fs_file_write(str, 1, 1, stdout);
length ++;
}
str++;
}
/* restore */
SetConsoleTextAttribute(
(GMQCC_IS_STDOUT(h)) ?
GetStdHandle(STD_OUTPUT_HANDLE) :
GetStdHandle(STD_ERROR_HANDLE),
icolor
);
return length;
}
#endif
/*
* We use standard files as default. These can be changed at any time
* with con_change(F, F)
*/
static con_t console;
/*
* Enables color on output if supported.
* NOTE: The support for checking colors is NULL. On windows this will
* always work, on *nix it depends if the term has colors.
*
* NOTE: This prevents colored output to piped stdout/err via isatty
* checks.
*/
static void con_enablecolor() {
if (console.handle_err == stderr || console.handle_err == stdout)
console.color_err = !!(isatty(STDERR_FILENO));
if (console.handle_out == stderr || console.handle_out == stdout)
console.color_out = !!(isatty(STDOUT_FILENO));
}
/*
* Does a write to the handle with the format string and list of
* arguments. This colorizes for windows as well via translate
* step.
*/
static int con_write(FILE *handle, const char *fmt, va_list va) {
int ln;
#ifndef _WIN32
ln = vfprintf(handle, fmt, va);
#else
{
char data[4096];
memset(data, 0, sizeof(data));
#ifdef _MSC_VER
vsnprintf_s(data, sizeof(data), sizeof(data), fmt, va);
#else
vsnprintf(data, sizeof(data), fmt, va);
#endif
ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(handle, data) : fs_file_puts(handle, data);
}
#endif
return ln;
}
/**********************************************************************
* EXPOSED INTERFACE BEGINS
*********************************************************************/
void con_close() {
if (!GMQCC_IS_DEFINE(console.handle_err))
fs_file_close(console.handle_err);
if (!GMQCC_IS_DEFINE(console.handle_out))
fs_file_close(console.handle_out);
}
void con_color(int state) {
if (state)
con_enablecolor();
else {
console.color_err = 0;
console.color_out = 0;
}
}
void con_init() {
console.handle_err = stderr;
console.handle_out = stdout;
con_enablecolor();
}
void con_reset() {
con_close();
con_init ();
}
/*
* This is clever, say you want to change the console to use two
* files for out/err. You pass in two strings, it will properly
* close the existing handles (if they're not std* handles) and
* open them. Now say you want TO use stdout and stderr, this
* allows you to do that so long as you cast them to (char*).
* Say you need stdout for out, but want a file for error, you can
* do this too, just cast the stdout for (char*) and stick to a
* string for the error file.
*/
int con_change(const char *out, const char *err) {
con_close();
if (!out) out = (const char *)((!console.handle_out) ? stdout : console.handle_out);
if (!err) err = (const char *)((!console.handle_err) ? stderr : console.handle_err);
if (GMQCC_IS_DEFINE(out)) {
console.handle_out = GMQCC_IS_STDOUT(out) ? stdout : stderr;
con_enablecolor();
} else if (!(console.handle_out = fs_file_open(out, "w"))) return 0;
if (GMQCC_IS_DEFINE(err)) {
console.handle_err = GMQCC_IS_STDOUT(err) ? stdout : stderr;
con_enablecolor();
} else if (!(console.handle_err = fs_file_open(err, "w"))) return 0;
return 1;
}
/*
* Defaultizer because stdio.h shouldn't be used anywhere except here
* and inside file.c To prevent mis-match of wrapper-interfaces.
*/
FILE *con_default_out() {
return (console.handle_out = stdout);
}
FILE *con_default_err() {
return (console.handle_err = stderr);
}
int con_verr(const char *fmt, va_list va) {
return con_write(console.handle_err, fmt, va);
}
int con_vout(const char *fmt, va_list va) {
return con_write(console.handle_out, fmt, va);
}
/*
* Standard stdout/stderr printf functions used generally where they need
* to be used.
*/
int con_err(const char *fmt, ...) {
va_list va;
int ln = 0;
va_start(va, fmt);
con_verr(fmt, va);
va_end (va);
return ln;
}
int con_out(const char *fmt, ...) {
va_list va;
int ln = 0;
va_start(va, fmt);
con_vout(fmt, va);
va_end (va);
return ln;
}
/*
* Utility console message writes for lexer contexts. These will allow
* for reporting of file:line based on lexer context, These are used
* heavily in the parser/ir/ast.
*/
void con_vprintmsg_c(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap, const char *condname) {
/* color selection table */
static int sel[] = {
CON_WHITE,
CON_CYAN,
CON_RED
};
int err = !!(level == LVL_ERROR);
int color = (err) ? console.color_err : console.color_out;
int (*print) (const char *, ...) = (err) ? &con_err : &con_out;
int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout;
if (color)
print("\033[0;%dm%s:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, sel[level], msgtype);
else
print("%s:%d: %s: ", name, (int)line, msgtype);
vprint(msg, ap);
if (condname)
print(" [%s]\n", condname);
else
print("\n");
}
void con_vprintmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg_c(level, name, line, msgtype, msg, ap, NULL);
}
void con_printmsg(int level, const char *name, size_t line, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_vprintmsg(level, name, line, msgtype, msg, va);
va_end (va);
}
void con_cvprintmsg(void *ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg(lvl, ((lex_ctx*)ctx)->file, ((lex_ctx*)ctx)->line, msgtype, msg, ap);
}
void con_cprintmsg (void *ctx, int lvl, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_cvprintmsg(ctx, lvl, msgtype, msg, va);
va_end (va);
}
/* General error interface */
size_t compile_errors = 0;
size_t compile_warnings = 0;
size_t compile_Werrors = 0;
static lex_ctx first_werror;
void compile_show_werrors()
{
con_cprintmsg((void*)&first_werror, LVL_ERROR, "first warning", "was here");
}
void vcompile_error(lex_ctx ctx, const char *msg, va_list ap)
{
++compile_errors;
con_cvprintmsg((void*)&ctx, LVL_ERROR, "error", msg, ap);
}
void compile_error(lex_ctx ctx, const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vcompile_error(ctx, msg, ap);
va_end(ap);
}
bool GMQCC_WARN vcompile_warning(lex_ctx ctx, int warntype, const char *fmt, va_list ap)
{
const char *msgtype = "warning";
int lvl = LVL_WARNING;
char warn_name[1024];
if (!OPTS_WARN(warntype))
return false;
warn_name[0] = '-';
warn_name[1] = 'W';
(void)util_strtononcmd(opts_warn_list[warntype].name, warn_name+2, sizeof(warn_name)-2);
++compile_warnings;
if (OPTS_WERROR(warntype)) {
if (!compile_Werrors)
first_werror = ctx;
++compile_Werrors;
msgtype = "Werror";
if (OPTS_FLAG(BAIL_ON_WERROR)) {
msgtype = "error";
++compile_errors;
}
lvl = LVL_ERROR;
}
con_vprintmsg_c(lvl, ctx.file, ctx.line, msgtype, fmt, ap, warn_name);
return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR);
}
bool GMQCC_WARN compile_warning(lex_ctx ctx, int warntype, const char *fmt, ...)
{
bool r;
va_list ap;
va_start(ap, fmt);
r = vcompile_warning(ctx, warntype, fmt, ap);
va_end(ap);
return r;
}

View file

@ -1,226 +0,0 @@
#include <stdio.h>
#include "gmqcc.h"
#define GMQCC_IS_STDOUT(X) ((X) == stdout)
#define GMQCC_IS_STDERR(X) ((X) == stderr)
#define GMQCC_IS_DEFINE(X) (GMQCC_IS_STDERR(X) || GMQCC_IS_STDOUT(X))
struct con_t {
FILE *handle_err;
FILE *handle_out;
int color_err;
int color_out;
};
static con_t console;
/*
* Enables color on output if supported.
* NOTE: The support for checking colors is nullptr. On windows this will
* always work, on *nix it depends if the term has colors.
*
* NOTE: This prevents colored output to piped stdout/err via isatty
* checks.
*/
static void con_enablecolor(void) {
console.color_err = util_isatty(console.handle_err);
console.color_out = util_isatty(console.handle_out);
}
/*
* Does a write to the handle with the format string and list of
* arguments. This colorizes for windows as well via translate
* step.
*/
static int con_write(FILE *handle, const char *fmt, va_list va) {
return vfprintf(handle, fmt, va);
}
/**********************************************************************
* EXPOSED INTERFACE BEGINS
*********************************************************************/
void con_close() {
if (!GMQCC_IS_DEFINE(console.handle_err))
fclose(console.handle_err);
if (!GMQCC_IS_DEFINE(console.handle_out))
fclose(console.handle_out);
}
void con_color(int state) {
if (state)
con_enablecolor();
else {
console.color_err = 0;
console.color_out = 0;
}
}
void con_init() {
console.handle_err = stderr;
console.handle_out = stdout;
con_enablecolor();
}
void con_reset() {
con_close();
con_init();
}
/*
* Defaultizer because stdio.h shouldn't be used anywhere except here
* and inside file.c To prevent mis-match of wrapper-interfaces.
*/
FILE *con_default_out() {
return console.handle_out = stdout;
}
FILE *con_default_err() {
return console.handle_err = stderr;
}
int con_verr(const char *fmt, va_list va) {
return con_write(console.handle_err, fmt, va);
}
int con_vout(const char *fmt, va_list va) {
return con_write(console.handle_out, fmt, va);
}
/*
* Standard stdout/stderr printf functions used generally where they need
* to be used.
*/
int con_err(const char *fmt, ...) {
va_list va;
int ln = 0;
va_start(va, fmt);
con_verr(fmt, va);
va_end(va);
return ln;
}
int con_out(const char *fmt, ...) {
va_list va;
int ln = 0;
va_start(va, fmt);
con_vout(fmt, va);
va_end (va);
return ln;
}
/*
* Utility console message writes for lexer contexts. These will allow
* for reporting of file:line based on lexer context, These are used
* heavily in the parser/ir/ast.
*/
static void con_vprintmsg_c(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap, const char *condname) {
/* color selection table */
static int sel[] = {
CON_WHITE,
CON_CYAN,
CON_RED
};
int err = !!(level == LVL_ERROR);
int color = (err) ? console.color_err : console.color_out;
int (*print) (const char *, ...) = (err) ? &con_err : &con_out;
int (*vprint)(const char *, va_list) = (err) ? &con_verr : &con_vout;
if (color)
print("\033[0;%dm%s:%d:%d: \033[0;%dm%s: \033[0m", CON_CYAN, name, (int)line, (int)column, sel[level], msgtype);
else
print("%s:%d:%d: %s: ", name, (int)line, (int)column, msgtype);
vprint(msg, ap);
if (condname)
print(" [%s]\n", condname);
else
print("\n");
}
void con_vprintmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg_c(level, name, line, column, msgtype, msg, ap, nullptr);
}
void con_printmsg(int level, const char *name, size_t line, size_t column, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_vprintmsg(level, name, line, column, msgtype, msg, va);
va_end (va);
}
void con_cvprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, va_list ap) {
con_vprintmsg(lvl, ctx.file, ctx.line, ctx.column, msgtype, msg, ap);
}
void con_cprintmsg(lex_ctx_t ctx, int lvl, const char *msgtype, const char *msg, ...) {
va_list va;
va_start(va, msg);
con_cvprintmsg(ctx, lvl, msgtype, msg, va);
va_end (va);
}
/* General error interface: TODO seperate as part of the compiler front-end */
size_t compile_errors = 0;
size_t compile_warnings = 0;
size_t compile_Werrors = 0;
static lex_ctx_t first_werror;
void compile_show_werrors()
{
con_cprintmsg(first_werror, LVL_ERROR, "first warning", "was here");
}
void vcompile_error(lex_ctx_t ctx, const char *msg, va_list ap)
{
++compile_errors;
con_cvprintmsg(ctx, LVL_ERROR, "error", msg, ap);
}
void compile_error_(lex_ctx_t ctx, const char *msg, ...)
{
va_list ap;
va_start(ap, msg);
vcompile_error(ctx, msg, ap);
va_end(ap);
}
bool GMQCC_WARN vcompile_warning(lex_ctx_t ctx, int warntype, const char *fmt, va_list ap)
{
const char *msgtype = "warning";
int lvl = LVL_WARNING;
char warn_name[1024];
if (!OPTS_WARN(warntype))
return false;
warn_name[0] = '-';
warn_name[1] = 'W';
(void)util_strtononcmd(opts_warn_list[warntype].name, warn_name+2, sizeof(warn_name)-2);
++compile_warnings;
if (OPTS_WERROR(warntype)) {
if (!compile_Werrors)
first_werror = ctx;
++compile_Werrors;
msgtype = "Werror";
if (OPTS_FLAG(BAIL_ON_WERROR)) {
msgtype = "error";
++compile_errors;
}
lvl = LVL_ERROR;
}
con_vprintmsg_c(lvl, ctx.file, ctx.line, ctx.column, msgtype, fmt, ap, warn_name);
return OPTS_WERROR(warntype) && OPTS_FLAG(BAIL_ON_WERROR);
}
bool GMQCC_WARN compile_warning_(lex_ctx_t ctx, int warntype, const char *fmt, ...)
{
bool r;
va_list ap;
va_start(ap, fmt);
r = vcompile_warning(ctx, warntype, fmt, ap);
va_end(ap);
return r;
}

547
correct.c Normal file
View file

@ -0,0 +1,547 @@
/*
* Copyright (C) 2012, 2013
* Dale Weiler
* Wolfgang Bumiller
*
* 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.
*/
#include "gmqcc.h"
/*
* This is a very clever method for correcting mistakes in QuakeC code
* most notably when invalid identifiers are used or inproper assignments;
* we can proprly lookup in multiple dictonaries (depening on the rules
* of what the task is trying to acomplish) to find the best possible
* match.
*
*
* A little about how it works, and probability theory:
*
* When given an identifier (which we will denote I), we're essentially
* just trying to choose the most likely correction for that identifier.
* (the actual "correction" can very well be the identifier itself).
* There is actually no way to know for sure that certian identifers
* such as "lates", need to be corrected to "late" or "latest" or any
* other permutations that look lexically the same. This is why we
* must advocate the usage of probabilities. This means that instead of
* just guessing, instead we're trying to find the correction for C,
* out of all possible corrections that maximizes the probability of C
* for the original identifer I.
*
* Thankfully there exists some theroies for probalistic interpretations
* of data. Since we're operating on two distictive intepretations, the
* transposition from I to C. We need something that can express how much
* degree of I should rationally change to become C. this is called the
* Bayesian interpretation. You can read more about it from here:
* http://www.celiagreen.com/charlesmccreery/statistics/bayestutorial.pdf
* (which is probably the only good online documentation for bayes theroy
* no lie. Everything else just sucks ..)
*
* Bayes' Thereom suggests something like the following:
* AC P(I|C) P(C) / P(I)
*
* However since P(I) is the same for every possibility of I, we can
* completley ignore it giving just:
* AC P(I|C) P(C)
*
* This greatly helps visualize how the parts of the expression are performed
* there is essentially three, from right to left we perform the following:
*
* 1: P(C), the probability that a proposed correction C will stand on its
* own. This is called the language model.
*
* 2: P(I|C), the probability that I would be used, when the programmer
* really meant C. This is the error model.
*
* 3: AC, the control mechanisim, an enumerator if you will, one that
* enumerates all feasible values of C, to determine the one that
* gives the greatest probability score.
*
* In reality the requirement for a more complex expression involving
* two seperate models is considerably a waste. But one must recognize
* that P(C|I) is already conflating two factors. It's just much simpler
* to seperate the two models and deal with them explicitaly. To properly
* estimate P(C|I) you have to consider both the probability of C and
* probability of the transposition from C to I. It's simply much more
* cleaner, and direct to seperate the two factors.
*
* Research tells us that 80% to 95% of all spelling errors have an edit
* distance no greater than one. Knowing this we can optimize for most
* cases of mistakes without taking a performance hit. Which is what we
* base longer edit distances off of. Opposed to the original method of
* I had concieved of checking everything.
*
* A little information on additional algorithms used:
*
* Initially when I implemented this corrector, it was very slow.
* Need I remind you this is essentially a brute force attack on strings,
* and since every transformation requires dynamic memory allocations,
* you can easily imagine where most of the runtime conflated. Yes
* It went right to malloc. More than THREE MILLION malloc calls are
* performed for an identifier about 16 bytes long. This was such a
* shock to me. A forward allocator (or as some call it a bump-point
* allocator, or just a memory pool) was implemented. To combat this.
*
* But of course even other factors were making it slow. Initially
* this used a hashtable. And hashtables have a good constant lookup
* time complexity. But the problem wasn't in the hashtable, it was
* in the hashing (despite having one of the fastest hash functions
* known). Remember those 3 million mallocs? Well for every malloc
* there is also a hash. After 3 million hashes .. you start to get
* very slow. To combat this I had suggested burst tries to Blub.
* The next day he had implemented them. Sure enough this brought
* down the runtime by a factor > 100%
*
* The trie initially was designed to work on all strings, but later it
* became aparent that not only was this not a requirement. It was also
* slowing down get/sets' for the trie. To fully understand, only
* correct_alpha needs to be understood by the trie system, knowing this
* We can combat the slowness using a very clever but evil optimization.
* By Setting a fixed sized amount of branches for the trie using a
* char-to-index map into the branches. We've complelty made the trie
* accesses entierly constant in lookup time. No really, a lookup is
* literally trie[str[0]] [str[1]] [2] .... .value.
*
*
* Future Work (If we really need it)
*
* Currently we can only distinguish one source of error in the
* language model we use. This could become an issue for identifiers
* that have close colliding rates, e.g colate->coat yields collate.
*
* Currently the error model has been fairly trivial, the smaller the
* edit distance the smaller the error. This usually causes some un-
* expected problems. e.g reciet->recite yields recipt. For QuakeC
* this could become a problem when lots of identifiers are involved.
*/
#define CORRECT_POOL_SIZE (128*1024*1024)
/*
* A forward allcator for the corrector. This corrector requires a lot
* of allocations. This forward allocator combats all those allocations
* and speeds us up a little. It also saves us space in a way since each
* allocation isn't wasting a little header space for when NOTRACK isn't
* defined.
*/
static unsigned char **correct_pool_data = NULL;
static unsigned char *correct_pool_this = NULL;
static size_t correct_pool_addr = 0;
static GMQCC_INLINE void correct_pool_new(void) {
correct_pool_addr = 0;
correct_pool_this = (unsigned char *)mem_a(CORRECT_POOL_SIZE);
vec_push(correct_pool_data, correct_pool_this);
}
static GMQCC_INLINE void *correct_pool_alloc(size_t bytes) {
void *data;
if (correct_pool_addr + bytes>= CORRECT_POOL_SIZE)
correct_pool_new();
data = (void*)correct_pool_this;
correct_pool_this += bytes;
correct_pool_addr += bytes;
return data;
}
static GMQCC_INLINE void correct_pool_delete(void) {
size_t i;
for (i = 0; i < vec_size(correct_pool_data); ++i)
mem_d(correct_pool_data[i]);
correct_pool_data = NULL;
correct_pool_this = NULL;
correct_pool_addr = 0;
}
static GMQCC_INLINE char *correct_pool_claim(const char *data) {
char *claim = util_strdup(data);
return claim;
}
/*
* _ is valid in identifiers. I've yet to implement numerics however
* because they're only valid after the first character is of a _, or
* alpha character.
*/
static const char correct_alpha[] = "abcdefghijklmnopqrstuvwxyz"
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"_"; /* TODO: Numbers ... */
static const size_t correct_alpha_index[0x80] = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 0, 0, 0, 0, 52,
0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 0, 0, 0, 0, 0
};
/*
* A fast space efficent trie for a dictionary of identifiers. This is
* faster than a hashtable for one reason. A hashtable itself may have
* fast constant lookup time, but the hash itself must be very fast. We
* have one of the fastest hash functions for strings, but if you do a
* lost of hashing (which we do, almost 3 million hashes per identifier)
* a hashtable becomes slow.
*/
correct_trie_t* correct_trie_new() {
correct_trie_t *t = (correct_trie_t*)mem_a(sizeof(correct_trie_t));
t->value = NULL;
t->entries = NULL;
return t;
}
static GMQCC_INLINE void correct_trie_del_sub(correct_trie_t *t) {
size_t i;
if (!t->entries)
return;
for (i = 0; i < sizeof(correct_alpha)-1; ++i) {
correct_trie_del_sub(&t->entries[i]);
}
mem_d(t->entries);
}
static GMQCC_INLINE void correct_trie_del(correct_trie_t *t) {
size_t i;
if (t->entries) {
for (i = 0; i < sizeof(correct_alpha)-1; ++i)
correct_trie_del_sub(&t->entries[i]);
mem_d(t->entries);
}
mem_d(t);
}
static GMQCC_INLINE void* correct_trie_get(const correct_trie_t *t, const char *key) {
const unsigned char *data = (const unsigned char*)key;
while (*data) {
if (!t->entries)
return NULL;
t = t->entries + correct_alpha_index[*data];
++data;
}
return t->value;
}
static GMQCC_INLINE void correct_trie_set(correct_trie_t *t, const char *key, void * const value) {
const unsigned char *data = (const unsigned char*)key;
while (*data) {
if (!t->entries) {
t->entries = (correct_trie_t*)mem_a(sizeof(correct_trie_t)*(sizeof(correct_alpha)-1));
memset(t->entries, 0, sizeof(correct_trie_t)*(sizeof(correct_alpha)-1));
}
t = t->entries + correct_alpha_index[*data];
++data;
}
t->value = value;
}
/*
* Implementation of the corrector algorithm commences. A very efficent
* brute-force attack (thanks to tries and mempool :-)).
*/
static GMQCC_INLINE size_t *correct_find(correct_trie_t *table, const char *word) {
return (size_t*)correct_trie_get(table, word);
}
static GMQCC_INLINE bool correct_update(correct_trie_t* *table, const char *word) {
size_t *data = correct_find(*table, word);
if (!data)
return false;
(*data)++;
return true;
}
void correct_add(correct_trie_t* table, size_t ***size, const char *ident) {
size_t *data = NULL;
const char *add = ident;
if (!correct_update(&table, add)) {
data = (size_t*)mem_a(sizeof(size_t));
*data = 1;
vec_push((*size), data);
correct_trie_set(table, add, data);
}
}
void correct_del(correct_trie_t* dictonary, size_t **data) {
size_t i;
const size_t vs = vec_size(data);
for (i = 0; i < vs; i++)
mem_d(data[i]);
vec_free(data);
correct_trie_del(dictonary);
}
/*
* correcting logic for the following forms of transformations:
* 1) deletion
* 2) transposition
* 3) alteration
* 4) insertion
*
* These functions could take an additional size_t **size paramater
* and store back the results of their new length in an array that
* is the same as **array for the memcmp in correct_exists. I'm just
* not able to figure out how to do that just yet. As my brain is
* not in the mood to figure out that logic. This is a reminder to
* do it, or for someone else to :-) correct_edit however would also
* need to take a size_t ** to carry it along (would all the argument
* overhead be worth it?)
*/
static GMQCC_INLINE size_t correct_deletion(const char *ident, char **array) {
size_t itr = 0;
const size_t len = strlen(ident);
for (; itr < len; itr++) {
char *a = (char*)correct_pool_alloc(len+1);
memcpy(a, ident, itr);
memcpy(a + itr, ident + itr + 1, len - itr);
array[itr] = a;
}
return itr;
}
static GMQCC_INLINE size_t correct_transposition(const char *ident, char **array) {
size_t itr = 0;
const size_t len = strlen(ident);
for (; itr < len - 1; itr++) {
char tmp;
char *a = (char*)correct_pool_alloc(len+1);
memcpy(a, ident, len+1);
tmp = a[itr];
a[itr ] = a[itr+1];
a[itr+1] = tmp;
array[itr] = a;
}
return itr;
}
static GMQCC_INLINE size_t correct_alteration(const char *ident, char **array) {
size_t itr = 0;
size_t jtr = 0;
size_t ktr = 0;
const size_t len = strlen(ident);
for (; itr < len; itr++) {
for (jtr = 0; jtr < sizeof(correct_alpha)-1; jtr++, ktr++) {
char *a = (char*)correct_pool_alloc(len+1);
memcpy(a, ident, len+1);
a[itr] = correct_alpha[jtr];
array[ktr] = a;
}
}
return ktr;
}
static GMQCC_INLINE size_t correct_insertion(const char *ident, char **array) {
size_t itr = 0;
size_t jtr = 0;
const size_t len = strlen(ident);
for (; itr <= len; itr++) {
for (jtr = 0; jtr < sizeof(correct_alpha)-1; jtr++) {
char *a = (char*)correct_pool_alloc(len+2);
memcpy(a, ident, itr);
memcpy(a + itr + 1, ident + itr, len - itr + 1);
a[itr] = correct_alpha[jtr];
array[itr * (sizeof(correct_alpha)-1) + jtr] = a;
}
}
return (len+1)*(sizeof(correct_alpha)-1);
}
static GMQCC_INLINE size_t correct_size(const char *ident) {
/*
* deletion = len
* transposition = len - 1
* alteration = len * sizeof(correct_alpha)
* insertion = (len + 1) * sizeof(correct_alpha)
*/
register size_t len = strlen(ident);
return (len) + (len - 1) + (len * (sizeof(correct_alpha)-1)) + ((len + 1) * (sizeof(correct_alpha)-1));
}
static GMQCC_INLINE char **correct_edit(const char *ident, size_t **lens) {
size_t next;
size_t size = correct_size(ident);
char **find = (char**)correct_pool_alloc(size * sizeof(char*));
if (!find || !(*lens = (size_t*)correct_pool_alloc(size * sizeof(size_t))))
return NULL;
next = correct_deletion (ident, find);
next += correct_transposition(ident, find+next);
next += correct_alteration (ident, find+next);
/*****/ correct_insertion (ident, find+next);
/* precompute lengths */
for (next = 0; next < size; next++)
(*lens)[next] = strlen(find[next]);
return find;
}
static GMQCC_INLINE int correct_exist(char **array, register size_t *sizes, size_t rows, char *ident, register size_t len) {
size_t itr;
for (itr = 0; itr < rows; itr++) {
/*
* We can save tons of calls to memcmp if we simply ignore comparisions
* that we know cannot contain the same length.
*/
if (sizes[itr] == len && !memcmp(array[itr], ident, len))
return 1;
}
return 0;
}
static GMQCC_INLINE char **correct_known_resize(char **res, size_t *allocated, size_t size) {
size_t oldallocated = *allocated;
char **out;
if (size < oldallocated)
return res;
out = (char**)correct_pool_alloc(sizeof(*res) * oldallocated + 32);
memcpy(out, res, sizeof(*res) * oldallocated);
*allocated += 32;
return out;
}
static char **correct_known(correction_t *corr, correct_trie_t* table, char **array, size_t rows, size_t *next) {
size_t itr = 0;
size_t jtr = 0;
size_t len = 0;
size_t row = 0;
size_t nxt = 8;
char **res = (char**)correct_pool_alloc(sizeof(char *) * nxt);
char **end = NULL;
size_t *bit = NULL;
for (; itr < rows; itr++) {
if (!array[itr][0])
continue;
if (vec_size(corr->edits) > itr+1) {
end = corr->edits[itr+1];
bit = corr->lens [itr+1];
} else {
end = correct_edit(array[itr], &bit);
vec_push(corr->edits, end);
vec_push(corr->lens, bit);
}
row = correct_size(array[itr]);
for (jtr = 0; jtr < row; jtr++) {
if (correct_find(table, end[jtr]) && !correct_exist(res, bit, len, end[jtr], bit[jtr])) {
res = correct_known_resize(res, &nxt, len+1);
res[len++] = end[jtr];
}
}
}
*next = len;
return res;
}
static GMQCC_INLINE char *correct_maximum(correct_trie_t* table, char **array, size_t rows) {
char *str = NULL;
size_t *itm = NULL;
size_t itr = 0;
size_t top = 0;
for (; itr < rows; itr++) {
if ((itm = correct_find(table, array[itr])) && (*itm > top)) {
top = *itm;
str = array[itr];
}
}
return str;
}
/*
* This is the exposed interface:
* takes a table for the dictonary a vector of sizes (used for internal
* probability calculation), and an identifier to "correct".
*/
void correct_init(correction_t *c)
{
correct_pool_new();
c->edits = NULL;
c->lens = NULL;
}
void correct_free(correction_t *c)
{
vec_free(c->edits);
vec_free(c->lens);
correct_pool_delete();
}
char *correct_str(correction_t *corr, correct_trie_t* table, const char *ident) {
char **e1 = NULL;
char **e2 = NULL;
char *e1ident = NULL;
char *e2ident = NULL;
size_t e1rows = 0;
size_t e2rows = 0;
size_t *bits = NULL;
/* needs to be allocated for free later */
if (correct_find(table, ident))
return correct_pool_claim(ident);
if ((e1rows = correct_size(ident))) {
if (vec_size(corr->edits) > 0)
e1 = corr->edits[0];
else {
e1 = correct_edit(ident, &bits);
vec_push(corr->edits, e1);
vec_push(corr->lens, bits);
}
if ((e1ident = correct_maximum(table, e1, e1rows)))
return correct_pool_claim(e1ident);
}
e2 = correct_known(corr, table, e1, e1rows, &e2rows);
if (e2rows && ((e2ident = correct_maximum(table, e2, e2rows))))
return correct_pool_claim(e2ident);
return util_strdup(ident);
}

53
distro/Makefile Normal file
View file

@ -0,0 +1,53 @@
DROPBOX := dropbox_uploader.sh
UNAME := $(shell uname -m)
DOWNLOAD:= ../doc/html/download.c
BRANCH := $(shell git branch | sed -n -e 's/^\* \(.*\)/\1/p')
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
$(MAKE) -C win32/
@mv deb/*.deb ./
@mv archlinux/this/*pkg.tar.xz ./
@mv win32/*.zip ./
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\|zip\)" -exec ./$$(basename $(DROPBOX)) upload {} \;
@rm dropbox_config dropbox_uploader.sh
website:
$(CC) $(DOWNLOAD) -o html.gen
@./html.gen ../
@rm html.gen
@git stash
@git checkout gh-pages
@rm -f ../download.html
@mv -f download.html ../download.html
@cd ..; git add download.html; git commit -m 'update download page'; git push origin gh-pages;
@git checkout $(BRANCH)
@git stash apply
clean:
@rm -f *.deb
@rm -f *.pkg.tar.xz
@rm -f *.zip
@rm -f *.gen
@rm -f *.html
all: base upload

View file

@ -0,0 +1,55 @@
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
# Contributor: Wolfgang Bumiller <blub@speed.at>
pkgname=gmqcc-git
pkgver=20130127
pkgrel=1
pkgdesc="An Improved Quake C Compiler"
arch=('i686' 'x86_64')
depends=()
conflicts=('gmqcc')
provides=('gmqcc=0.2.4')
makedepends=('git')
url="https://github.com/graphitemaster/gmqcc.git"
license=('MIT')
_gitroot="git://github.com/graphitemaster/gmqcc.git"
_gitname="gmqcc"
build() {
cd $srcdir
msg "Connecting to the GIT server..."
if [[ -d $srcdir/$_gitname ]] ; then
cd $_gitname
msg "Removing build files..."
git clean -dfx
msg "Updating..."
git pull --no-tags
msg "The local files are updated."
else
msg "Cloning..."
git clone $_gitroot $_gitname --depth 1
msg "Clone done."
fi
msg "Starting compilation..."
cd "$srcdir"/"$_gitname"
msg "Compiling..."
gmake
}
check() {
cd "$srcdir"/"$_gitname"
gmake check
}
package() {
cd "$srcdir"/"$_gitname"
msg "Compiling and installing to pkgdir this time..."
gmake install DESTDIR=$pkgdir PREFIX=/usr
msg "Compiling done."
install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
}

View file

@ -0,0 +1,38 @@
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
# Contributor: Wolfgang Bumiller <blub@speed.at>
pkgname=gmqcc
pkgver=0.2.2
pkgrel=1
pkgdesc="An Improved Quake C Compiler"
arch=('i686' 'x86_64')
depends=()
url="https://github.com/graphitemaster/gmqcc.git"
license=('MIT')
source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
sha1sums=('8cd91dc13f70cd9d3767602bf3eb47a1906d9353')
_gitname=graphitemaster-gmqcc-de24486/
build() {
msg "Starting compilation..."
cd "$srcdir"/"$_gitname"
msg "Compiling..."
gmake
}
check() {
cd "$srcdir"/"$_gitname"
gmake check
}
package() {
cd "$srcdir"/"$_gitname"
msg "Compiling and installing to pkgdir this time..."
gmake install DESTDIR=$pkgdir PREFIX=/usr
msg "Compiling done."
install -dm755 ${pkgdir}/usr/share/licenses/gmqcc
install -m644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
}

View file

@ -0,0 +1,48 @@
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
pkgname=gmqcc-git
pkgver=0.2.612.g160e7cf
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')
depends=('glibc')
conflicts=('gmqcc')
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')
build() {
msg "Starting compilation..."
cd "$srcdir"/"gmqcc"
msg "Compiling..."
make
}
check() {
cd "$srcdir"/"gmqcc"
make check
}
package() {
cd "$srcdir"/"gmqcc"
msg "Compiling and installing to pkgdir this time..."
make install DESTDIR=$pkgdir PREFIX=/usr
msg "Compiling done."
install -Dm644 syntax/geany/filetypes.qc ${pkgdir}/usr/share/geany/filetypes.qc
install -Dm644 syntax/gtksourceview/qc.lang ${pkgdir}/usr/share/gtksourceview-3.0/language-specs/qc.lang
# jedit
install -Dm644 syntax/kate/qc.xml ${pkgdir}/usr/share/apps/katepart/syntax/qc.xml
install -Dm644 syntax/nano/qc.nanorc ${pkgdir}/usr/share/nano/qc.nanorc
install -Dm644 LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
}

View file

@ -0,0 +1,36 @@
# Contributor: matthiaskrgr <matthiaskrgr _strange_curverd_character_ freedroid D0T org>
pkgname=gmqcc
pkgver=0.2.2
pkgrel=1
pkgdesc="An Improved Quake C Compiler"
arch=('i686' 'x86_64')
depends=('glibc')
url="https://github.com/graphitemaster/gmqcc.git"
license=('MIT')
source=(gmqcc-$pkgver.zip::https://github.com/graphitemaster/gmqcc/zipball/$pkgver)
sha1sums=('e0fe99af9a55d36cd9e0909a96d1b14f2db8b757')
_gitname=graphitemaster-gmqcc-de24486/
build() {
msg "Starting compilation..."
cd "$srcdir"/"$_gitname"
msg "Compiling..."
make
}
check() {
cd "$srcdir"/"$_gitname"
make check
}
package() {
cd "$srcdir"/"$_gitname"
msg "Compiling and installing to pkgdir this time..."
make install DESTDIR=$pkgdir PREFIX=/usr
msg "Compiling done."
install -D LICENSE ${pkgdir}/usr/share/licenses/gmqcc/LICENSE
}

View file

@ -0,0 +1,50 @@
BASEDIR := ../../../
PREFIX := /usr
HEADER := $(BASEDIR)/gmqcc.h
MAJOR := $(shell sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER))
MINOR := $(shell sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER))
PATCH := $(shell sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER))
PKGREL := 1
CARCH := $(shell uname -m)
PKGDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)-$(CARCH)
PKG := $(PKGDIR).pkg.tar.xz
PKGINFO := $(PKGDIR)/.PKGINFO
DESTDIR := distro/archlinux/this/$(PKGDIR)
CFLAGS :=
ifneq (, $(findstring i686, $(CARCH)))
CFLAGS += -m32
LDFLAGS += -m32
endif
base:
$(MAKE) -C $(BASEDIR) clean
CFLAGS="$(CFLAGS)" LDFLAGS="$(LDFLAGS)" \
$(MAKE) -C $(BASEDIR) "DESTDIR=$(DESTDIR)" "PREFIX=$(PREFIX)" install
@echo "pkgname = gmqcc" > $(PKGINFO)
@echo "pkgver = $(MAJOR).$(MINOR).$(PATCH)-$(PKGREL)" >> $(PKGINFO)
@echo "pkgdesc = An Improved Quake C Compiler" >> $(PKGINFO)
@echo "url = https://github.com/graphitemaster/gmqcc.git" >> $(PKGINFO)
@echo "builddate = `date -u \"+%s\"`" >> $(PKGINFO)
@echo "packager = Unknown Packager" >> $(PKGINFO)
@echo "size = `du -sk $(PKGDIR) | awk '{print $$1 * 1024}'`" >> $(PKGINFO)
@echo "arch = $(CARCH)" >> $(PKGINFO)
@echo "license = MIT" >> $(PKGINFO)
@echo "conflict = gmqcc" >> $(PKGINFO)
@echo "depend = glibc" >> $(PKGINFO)
@echo "makepkgopt = strip" >> $(PKGINFO)
@echo "makepkgopt = docs" >> $(PKGINFO)
@echo "makepkgopt = libtool" >> $(PKGINFO)
@echo "makepkgopt = emptydirs" >> $(PKGINFO)
@echo "makepkgopt = zipman" >> $(PKGINFO)
@echo "makepkgopt = purge" >> $(PKGINFO)
@echo "makepkgopt = !upx" >> $(PKGINFO)
@tar -cJvf $(PKG) -C $(PKGDIR)/ .PKGINFO usr/
@rm -rf $(PKGDIR)
clean:
$(MAKE) -C $(BASEDIR) clean
@rm -f *.pkg.tar.xz
all: base

43
distro/deb/Makefile Normal file
View file

@ -0,0 +1,43 @@
BASEDIR := ../..
PREFIX := /usr
HEADER := $(BASEDIR)/gmqcc.h
MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)`
MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)`
PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)`
DEBDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)
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
@echo "Package: gmqcc" > $(CONTROL)
@echo "Version: $(MAJOR).$(MINOR).$(PATCH)" >> $(CONTROL)
@echo "Section: user/hidden" >> $(CONTROL)
@echo "Priority: optional" >> $(CONTROL)
@echo "Architecture: $(CARCH)" >> $(CONTROL)
@echo "Installed-Size: `du -ks $($(DEBDIR)/usr) | cut -f 1`" >> $(CONTROL)
@echo "Maintainer: Dale Weiler <killfieldengine@gmail.com>" >> $(CONTROL)
@echo "Description: An improved Quake C Compiler" >> $(CONTROL)
@echo " For an enduring period of time the options for a decent compiler for the Quake C programming language" >> $(CONTROL)
@echo " were confined to a specific compiler known as QCC. Attempts were made to extend and improve upon the" >> $(CONTROL)
@echo " design of QCC, but many foreseen the consequences of building on a broken foundation. The solution" >> $(CONTROL)
@echo " was obvious, a new compiler; one born from the NIH realm of sarcastic wit. We welcome you. You won't" >> $(CONTROL)
@echo " find a better Quake C compiler." >> $(CONTROL)
@tar czf data.tar.gz -C $(DEBDIR)/ . --exclude=DEBIAN
@tar czf control.tar.gz -C $(DEBDIR)/DEBIAN/ .
@echo 2.0 > debian-binary
@ar r $(DEB) debian-binary control.tar.gz data.tar.gz
@rm -rf debian-binary control.tar.gz data.tar.gz $(DEBDIR)
clean:
$(MAKE) -C $(BASEDIR) clean
@rm -f *.deb
all: base

17
distro/win32/Makefile Normal file
View file

@ -0,0 +1,17 @@
BASEDIR := ../..
HEADER := $(BASEDIR)/gmqcc.h
MAJOR := `sed -n -e '/GMQCC_VERSION_MAJOR/{s/.* .* //;p;q;}' $(HEADER)`
MINOR := `sed -n -e '/GMQCC_VERSION_MINOR/{s/.* .* //;p;q;}' $(HEADER)`
PATCH := `sed -n -e '/GMQCC_VERSION_PATCH/{s/.* .* //;p;q;}' $(HEADER)`
BINDIR := gmqcc-$(MAJOR).$(MINOR).$(PATCH)
base:
$(MAKE) CC=i486-mingw32-gcc UNAME=MINGW32 -C $(BASEDIR) clean
$(MAKE) CC=i486-mingw32-gcc UNAME=MINGW32 -C $(BASEDIR) DESTDIR=distro/win32/$(BINDIR) PREFIX=/ install
@zip -r $(BINDIR)-win32.zip $(BINDIR)
@rm -rf $(BINDIR)
clean:
$(MAKE) -C $(BASEDIR) clean
@rm -f *.zip
all: base

View file

@ -153,7 +153,7 @@ Adds compiler information to the generated binary file. Currently
this includes the following globals:
.Bl -tag -width indent -compact
.It Li reserved:version
String containing the compiler version as printed by the \-\-version
String containing the compiler version as printed by the --version
parameter.
.El
.It Fl -correct , Fl -no-correct
@ -168,11 +168,6 @@ DEBUG OPTION. Print the code's intermediate representation after the
optimization and finalization passes to stdout before generating the
binary. The instructions will be enumerated, and values will contain a
list of liferanges.
.It Fl force-crc= Ns Ar CRC
Force the produced progs file to use the specified CRC.
.It Fl state-fps= Ns Ar NUM
Activate \-femulate-state and set the emulated FPS to
.Ar NUM Ns .
.El
.Sh COMPILE WARNINGS
.Bl -tag -width Ds
@ -186,9 +181,6 @@ variables can be opened using
.Ql #pragma noref 1
and closed via
.Ql #pragma noref 0 Ns .
.It Fl W Ns Cm unused-component
Generate a warning about vector variables which are declared but not all their
components are used.
.It Fl W Ns Cm used-uninitialized
Generate a warning if it is possible that a variable can be used
without prior initialization. Note that this warning is not
@ -328,34 +320,6 @@ marked as such.
Warn about possible mistakes caused by missing or wrong parenthesis,
like an assignment in an 'if' condition when there's no additional set
of parens around the assignment.
.It Fl W Ns Cm unsafe-types
When passing variadic parameters via
.Li ...(N)
it can happen that incompatible types are passed to functions. This
enables several warnings when static typechecking cannot guarantee
consistent behavior.
.It Fl W Ns Cm breakdef
When compiling original id1 QC there is a definition for `break`
which conflicts with the 'break' keyword in GMQCC. Enabling this
will print a warning when the definition occurs. The definition is
ignored for both cases.
.It Fl W Ns Cm const-overwrite
When compiling original QuakeWorld QC there are instances where
code overwrites constants. This is considered an error, however
for QuakeWorld to compile it needs to be treated as a warning
instead, as such this warning only works when \-std=qcc.
.It Fl W Ns Cm directive-inmacro
Warn about the use of preprocessor directives inside macros.
.It Fl W Ns Cm builtins
When using a function that is not explicitly defined, the compiler
will search its intrinsics table for something that matches that
function name by appending "__builtin_" to it. This behaviour may
be unexpected, so enabling this will produce a diagnostic when
such a function is resolved to a builtin.
.It Fl W Ns Cm inexact-compares
When comparing an inexact value such as `1.0/3.0' the result is
pathologically wrong. Enabling this will trigger a compiler warning
on such expressions.
.El
.Sh COMPILE FLAGS
.Bl -tag -width Ds
@ -376,7 +340,7 @@ features used in the Xonotic codebase. If you need more, write a
ticket.
.It Fl f Ns Cm ftepp-predefs
Enable some predefined macros. This only works in combination with
\'\-fftepp' and is currently not included by '\-std=fteqcc'. The
\'-fftepp' and is currently not included by '-std=fteqcc'. The
following macros will be added:
.Bd -literal -offset indent
__LINE__
@ -409,45 +373,6 @@ only the first component will be 0, while the other two will become
the first to of the global return value. This behavior is odd and
relying on it should be discouraged, and thus is not supported by
gmqcc.
.It Fl f Ns Cm ftepp-mathdefs
Enable math constant definitions. This only works in combination
with \'\-fftepp' and is currently not included by '\-std=fteqcc'.
The following macros will be added:
.Bd -literal -offset indent
M_E
M_LOG2E
M_LOG10E
M_LN2
M_LN10
M_PI
M_PI_2
M_PI_4
M_1_PI
M_2_PI
M_2_SQRTPI
M_SQRT2
M_SQRT1_2
M_TAU
.Ed
.It Fl f Ns Cm ftepp-indirect-expansion
Enable indirect macro expansion. This only works in combination
with '-fftepp' and is currently not included by '-std=fteqcc'.
Enabling this behavior will allow the preprocessor to operate more
like the standard C preprocessor in that it will allow arguments
of macros which are macro-expanded to be substituted into the
definition of the macro.
.Pp
As an example:
.Bd -literal -offset indent
#define STR1(x) #x
#define STR2(x) STR1(x)
#define THE_ANSWER 42
#define THE_ANSWER_STR STR2(THE_ANSWER) /* "42" */
.Ed
With this enabled, an expansion of THE_ANSWER_STR will yield
the string "42". With this disabled an expansion of THE_ANSWER_STR
will yield "THE_ANSWER"
.It Fl f Ns Cm relaxed-switch
Allow switch cases to use non constant variables.
.It Fl f Ns Cm short-logic
@ -581,60 +506,6 @@ Example:
void printA() = #1; // the usual way
void printB() = #2-1; // with a constant expression
.Ed
.It Fl f Ns Cm return-assignments
Enabiling this option will allow assigning values or expressions to the
return keyword as if it were a local variable of the same type as the
function's signature's return type.
.Pp
Example:
.Bd -literal -offset indent
float bar() { return 1024; }
float fun() {
return = bar();
return; // returns value of bar
}
.Ed
.It Fl f Ns Cm unsafe-varargs
When passing on varargs to a different functions, this turns some
static error cases into warnings. Like when the caller's varargs are
restricted to a different type than the callee's parameter. Or a list
of unrestricted varargs is passed into restricted varargs.
.It Fl f Ns Cm typeless-stores
Always use STORE_F, LOAD_F, STOREP_F when accessing scalar variables.
This is somewhat incorrect assembly instruction use, but in all engines
they do exactly the same. This makes disassembly output harder to read,
breaks decompilers, but causes the output file to be better compressible.
.It Fl f Ns Cm sort-operands
In commutative instructions, always put the lower-numbered operand first.
This shaves off 1 byte of entropy from all these instructions, reducing
compressed size of the output file.
.It Fl f Ns Cm emulate-state
Emulate OP_STATE operations in code rather than using the instruction.
The desired fps can be set via -state-fps=NUM, defaults to 10.
Specifying \-state-fps implicitly sets this flag. Defaults to off in all
standards.
.It Fl f Ns Cm arithmetic-exceptions
Turn on arithmetic exception tests in the compiler. In constant expressions
which trigger exceptions like division by zero, overflow, underflow, etc,
the following flag will produce diagnostics for what triggered that
exception.
.It Fl f Ns Cm split-vector-parameters
With this flag immediate vector literals which only ever appear as function
parameters won't be stored as vector immediates. Instead, the 3 floats making
up the vector will be copied separately. Essentially this turns a vector-store
instruction into 3 float-store instructions for such cases. This increases
code size but can dramatically reduce the amount of vector globals, which is
after all limited to 64k. There's at least one known codebase where this
lowers the number of globals from over 80k down to around 3k. In other code
bases it doesn't reduce the globals at all but only increases code size.
Just try it and see whether it helps you.
.It Fl f Ns Cm default-eraseable
Force all expressions to be "eraseable" which permits the compiler to
remove unused functions, variables and statements. This is equivlant to
putting [[eraseable]] on all definitions. This is dangerous as it breaks
auto cvars, definitions for functions the engine may be looking for and
translatable strings. Instead, you can mark a definition with [[noerase]]
to prevent this from happening.
.El
.Sh OPTIMIZATIONS
.Bl -tag -width Ds
@ -679,7 +550,7 @@ string being added.
.Pp
For example the following code will only generate 1 string:
.Bd -literal -offset indent
print("Hello you!\\n");
print("Hell you!\\n");
print("you!\\n"); // trailing substring of "Hello you!\\n"
.Ed
.Pp
@ -711,10 +582,6 @@ in this case, the y component of a vector. This optimization will turn
such a multiplication into a direct component access. If the factor is
anything other than 1, a float-multiplication will be added, which is
still faster than a vector multiplication.
.It Fl O Ns Cm const-fold-dce
For constant expressions that result in dead code (such as a branch whos
condition can be evaluated at compile-time), this will eliminate the branch
and else body (if present) to produce more optimal code.
.El
.Sh CONFIG
The configuration file is similar to regular .ini files. Comments
@ -764,7 +631,7 @@ A documented example for a gmqcc.ini file.
.Sh AUTHOR
See <http://graphitemaster.github.com/gmqcc>.
.Sh BUGS
Currently the '\-fftepp-predefs' flag is not included by '\-std=fteqcc',
Currently the '-fftepp-predefs' flag is not included by '-std=fteqcc',
partially because it is not entirely conformant to fteqcc.
.Pp
Please report bugs on <http://github.com/graphitemaster/gmqcc/issues>,

284
doc/html/download.c Normal file
View file

@ -0,0 +1,284 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/*
* protect some information, not that I care, but this is just to stay
* safer.
*/
#define SECURITY_BASE "\
ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
#define SECURITY_TOKEN "\
P29hdXRoX2NvbnN1bWVyX2tleT03NnZoM3E0Mmhudm16bTMmb2F1dGhfdG9rZW49\
dzBieHpmMGRmdDhlZGZxJm9hdXRoX3NpZ25hdHVyZV9tZXRob2Q9UExBSU5URVhU\
Jm9hdXRoX3NpZ25hdHVyZT10bWVlY2h0MmNtaDcyeGElMjY5dm9zeDd4OGd5NGtn\
amsmb2F1dGhfdGltZXN0YW1wPSZvYXV0aF9ub25jZT0xMjE2NQo="
int isbase64(char c) {
return !!(c && strchr(SECURITY_BASE, c) != NULL);
}
char value(char c) {
const char *load = SECURITY_BASE;
const char *find = strchr(load, c);
return (find) ? find - load : 0;
}
int security_decode(unsigned char *dest, const unsigned char *src, int srclen) {
unsigned char *p;
if(!*src)
return 0;
*dest = 0;
p = dest;
do {
*p++ = (value(src[0]) << 2) | (value(src[1]) >> 4);
*p++ = (value(src[1]) << 4) | (value(src[2]) >> 2);
*p++ = (value(src[2]) << 6) | (value(src[3]) >> 0);
if(!isbase64(src[1])) {
p -= 2;
break;
}
else if(!isbase64(src[2])) {
p -= 2;
break;
}
else if(!isbase64(src[3])) {
p--;
break;
}
src += 4;
while(*src && (*src == 13 || *src == 10))
src++;
} while(srclen-= 4);
*p = 0;
return p-dest;
}
#define BASEURL " https://api-content.dropbox.com/1/files/sandbox/"
/*
* If more platforms are supported add the entries between the start
* tag here, and the end tag below. Nothing else needs to be done
* <tag> (the table needs to match the HTML too)
*/
#define ARCHLINUX_32_REF "%sgmqcc-%c.%c.%c-1-i686.pkg.tar.xz%s"
#define ARCHLINUX_64_REF "%sgmqcc-%c.%c.%c-1-x86_64.pkg.tar.xz%s"
#define DEBIAN_32_REF "%sgmqcc-%c.%c.%c-i686.deb%s"
#define DEBIAN_64_REF "%sgmqcc-%c.%c.%c-x86_64.deb%s"
#define WINDOWS_32_REF "%sgmqcc-%c.%c.%c-win32.zip%s"
#define WINDOWS_64_REF "%sgmqcc-%c.%c.%c-win64.zip%s"
#define HTML "\
<!doctype html>\
<html>\
<head>\
<meta charset=\"utf-8\">\
<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\">\
<title>GMQCC</title>\
<link rel=\"stylesheet\" href=\"stylesheets/styles.css\">\
<link rel=\"stylesheet\" href=\"stylesheets/pygment_trac.css\">\
<script src=\"javascripts/scale.fix.js\"></script>\
<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, user-scalable=no\">\
<!--[if lt IE 9]>\
<script src=\"//html5shiv.googlecode.com/svn/trunk/html5.js\"></script>\
<![endif]-->\
</head>\
<body>\
<a href=\"https://github.com/graphitemaster/gmqcc\"><div class=\"fork\"></div></a>\
<div class=\"wrapper\">\
<header>\
<h1 class=\"header\">GMQCC</h1>\
<p class=\"header\">An Improved Quake C Compiler</p>\
<ul>\
<li class=\"buttons\"><a href=index.html>Index</a></li>\
<li class=\"download\"><a href=\"download.html\">Download</a></li>\
<li class=\"buttons\"><a href=\"https://github.com/graphitemaster/gmqcc/issues\">Issues</a></li>\
<li class=\"buttons\"><a href=\"doc.html\">Documentation</a></li>\
<li class=\"buttons\"><a href=\"https://github.com/graphitemaster/gmqcc\">View On GitHub</a></li>\
</ul>\
</header>\
<section>\
<table>\
<tr>\
<th>Operating System / Distribution</th>\
<th>x86 Architecture</th>\
<th>x86_64 Architecture</th>\
</tr>\
<tr>\
<td>Archlinux</td>\
<td><a href=\"%s\">Download</a></td>\
<td><a href=\"%s\">Download</a></td>\
</tr>\
<tr>\
<td>Debian</td>\
<td><a href=\"%s\">Download</a></td>\
<td><a href=\"%s\">Download</a></td>\
</tr>\
<tr>\
<td>Windows</td>\
<td><a href=\"%s\">Download</a></td>\
<td><a href=\"%s\">Download</a></td>\
</tr>\
</table>\
</section>\
<footer>\
<script type=\"text/javascript\" src=\"http://www.ohloh.net/p/602517/widgets/project_partner_badge.js\"></script>\
</footer>\
</div>\
<!--[if !IE]><script>fixScale(document);</script><![endif]-->\
</body>\
</html>\
"
static char build_table[][4096] = {
ARCHLINUX_32_REF, ARCHLINUX_64_REF,
DEBIAN_32_REF, DEBIAN_64_REF,
WINDOWS_32_REF, WINDOWS_64_REF
};
/* </tag> */
#define ISXDIGIT(c) ((c >= 48 && c <= 57) || ((c & ~0x20) >= 65 && (c & ~0x20) <= 70))
typedef struct {
char *data;
unsigned int len;
unsigned int size;
} url_t;
void escape(url_t *str) {
char *p, *ptr;
char hexstr[3];
unsigned int i=0;
unsigned long l=0;
p = str->data;
for(i=0; i < str->len; i++) {
if((p - str->data) >= str->len)
break;
if(*p == '%' &&
((p - str->data)+2) < str->len &&
ISXDIGIT(*(p+1)) &&
ISXDIGIT(*(p+2))
) {
p++;
hexstr[0] = *p++;
hexstr[1] = *p++;
hexstr[2] = 0;
l = strtoul(hexstr, &ptr, 16);
str->data[i] = (char)(l & 0x7f);
continue;
}
if(*p == '+') {
*p = ' ';
}
str->data[i] = *p++;
}
str->data[i] = 0;
str->len = i;
}
void version(const char *directory, char *major, char *minor, char *patch) {
FILE *handle;
char file[4096];
size_t size = 0;
char *data = NULL;
snprintf(file, sizeof(file), "%s/gmqcc.h", directory);
handle = fopen(file, "r");
if (!handle) {
fprintf(stderr, "failed to open %s for reading version (%s)\n",
file, strerror(errno)
);
abort();
}
while (getline(&data, &size, handle) != EOF) {
#define TEST(TYPE, STORE) \
if (strstr(data, "#define GMQCC_VERSION_" TYPE )) { \
char *get = data; \
while (!isdigit(*get)) \
get++; \
*STORE = *get; \
}
TEST("MAJOR", major)
TEST("MINOR", minor)
TEST("PATCH", patch)
#undef TEST
}
free(data);
}
void genhtml() {
FILE *fp = fopen("download.html", "w");
if (!fp) {
fprintf(stderr, "failed to generate HTML: %s\n", strerror(errno));
abort();
}
fprintf(fp, HTML,
build_table[0], build_table[1],
build_table[2], build_table[3],
build_table[4], build_table[5]
);
fclose (fp);
}
/*
* Builds a list of download links with the right version and handles the
* rest of the magic.
*/
void build(const char *directory) {
/* Figure out version number */
char find[3];
char decode[4096];
size_t size;
version(directory, &find[0], &find[1], &find[2]);
/*
* decode the secuity stuff for preparing the URLs which will be used
* as links.
*/
memset(decode, 0, sizeof(decode));
security_decode(decode, SECURITY_TOKEN, strlen(SECURITY_TOKEN));
for (size = 0; size < sizeof(build_table) / sizeof(*build_table); size++) {
char *load = strdup(build_table[size]);
url_t esc = { NULL, 0 };
snprintf(build_table[size], 4096, load, BASEURL, find[0], find[1], find[2], decode);
esc.data = strdup(build_table[size]);
esc.size = strlen(build_table[size]);
esc.len = esc.size;
/* Yes we also need to escape URLs just incase */
escape(&esc);
free(load);
}
/*
* Now generate the HTML file for those download links by asking tinyurl to
*/
genhtml();
}
int main(int argc, char **argv) {
size_t itr;
argc--;
argv++;
if (!argc) {
printf("usage: %s [gmqcc.h location]\n", argv[-1]);
return 0;
}
build(*argv);
}

BIN
doc/html/gmqcc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

92
doc/html/style.css Normal file
View file

@ -0,0 +1,92 @@
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font: inherit;
vertical-align: baseline;
}
body {
font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
margin: 0;
line-height: 1.8em;
-webkit-font-smoothing: antialiased;
background: #CDC9C9;
}
h1, h2, h3, h4, h5, h6 {
color:#232323;
margin:36px 0 10px;
}
.head-ltitle, .head-rtitle, .head-vol {
font: bold 0.8em Arial, Helvectia, sans-serif;
}
.head-vol {
visibility: hidden;
}
.name {
font: bold 0.8em Monospace, serif;
}
.ftype {
font: normal 1em Monospace, serif;
}
h1 {
color: #c30000;
margin-top: 0.3em;
margin-bottom: 0.3em;
line-height: 1.3;
font: normal 1.4em Arvo, Monaco, sans-serif;
/*font: bold 1.4em Arial, Helvetica, sans-serif*/
}
.section {
margin-bottom: 1em;
margin-top: 1em;
padding-left: 1em;
border-top: 1px #cccccc solid;
font: normal 0.9em Arial, Helvetica, sans-serif;
text-align: justify;
border: 0;
padding-bottom: 1em;
border-bottom: 1px solid #f0e0e0;
}
.list-tag {
padding-left: 1em;
}
pre {
border: 1px dashed #ffffff;
background: #ddd8d8;
}
.flag {
font: normal 1em Monospace, serif;
}
a {
color:#C30000;
font-weight:200;
text-decoration:none;
}
a:hover {
text-decoration: underline;
}

View file

@ -69,9 +69,7 @@ Append a string parameter to be passed to
The following builtin functions are available:
.Bl -ohang
.It Li 1) void print(string...) = #1;
.Bd -unfilled -offset indent -compact
Print the passed strings to stdout. At most 8 strings are allowed.
.Ed
.D1 Print the passed strings to stdout. At most 8 strings are allowed.
.It Li 2) string ftos(float) = #2;
.D1 Convert a float to a string.
.It Li 3) entity spawn() = #3;
@ -81,7 +79,7 @@ Print the passed strings to stdout. At most 8 strings are allowed.
.It Li 5) string vtos(vector) = #5;
.D1 Convert a vector to a string.
.It Li 6) void error(string...) = #6;
.D1 Print strings to stdout and then exit with an error (limited to 8 arguments)
.D1 Print at most 8 strings to stdout and then exit with an error.
.It Li 7) float vlen(vector) = #7;
.D1 Get the length of a vector.
.It Li 8) string etos(entity) = #8;

759
doc/specification.tex Normal file
View file

@ -0,0 +1,759 @@
\documentclass{article}
%%% PACKAGES
\usepackage{geometry}
\usepackage[utf8]{inputenc}
\usepackage[parfill]{parskip}
\usepackage{subfig}
\usepackage{listings}
\usepackage{color}
\usepackage{sectsty}
%%% GEOMETRY FOR DOCUMENT
\geometry{a4paper}
%%% HEADERS/FOOTERS APPEARANCE
\usepackage{fancyhdr} % This should be set AFTER setting up the page geometry
\pagestyle{fancy} % options: empty , plain , fancy
\renewcommand{\headrulewidth}{0pt} % customise the layout...
\lhead{}\chead{}\rhead{}
\lfoot{}\cfoot{\thepage}\rfoot{}
%%% SECTION TITLE APPEARANCE
\allsectionsfont{\sffamily\mdseries\upshape} % (See the fntguide.pdf for font help)
%%% ToC APPEARANCE
\usepackage[nottoc,notlof,notlot]{tocbibind} % Put the bibliography in the ToC
\usepackage[titles,subfigure]{tocloft} % Alter the style of the Table of Contents
\renewcommand{\cftsecfont}{\rmfamily\mdseries\upshape}
\renewcommand{\cftsecpagefont}{\rmfamily\mdseries\upshape} % No bold!
%%% listing language definitions
%%% BNF for now, QuakeC will be later
\definecolor{keyword1}{RGB}{0,102,153}
\definecolor{keyword2}{RGB}{0,153,102}
\definecolor{keyword3}{RGB}{0,153,255}
\definecolor{comment}{RGB}{204,0,0}
\definecolor{function}{RGB}{153,102,255}
\definecolor{digit}{RGB}{255,0,0}
\definecolor{string}{RGB}{255,0,204}
\definecolor{rule}{RGB}{192,192,192}
\definecolor{back}{RGB}{250,250,250}
\lstdefinelanguage{bnf}{
keywordstyle={\color{keyword2}\bfseries},
keywords={},
otherkeywords={::=,|},
morecomment=[s][\color{comment}]{(*}{*)},
stringstyle=\color{string},
showstringspaces=false,
frame=none,
rulecolor=\color{rule},
backgroundcolor=\color{back}
}
%% Title Information %%
\title{The GMQCC QuakeC Programming Language}
\author{Dale Weiler}
\date{\today}
\begin{document}
%% Title Page %%
\maketitle
\thispagestyle{empty}
\raggedright
\abstract
This document specifies the form and establishes the interpretation of programs written in
the GMQCC QuakeC programming language variant (refereed simply as QuakeC throughout this
document). It specifies:
\begin{itemize}
\item the representation of QuakeC programs;
\item the syntax and constraints of the QuakeC language;
\item the semantic rules for interpreting QuakeC programs;
\item the representation of input data to be processed by QuakeC programs;
\item the representation of output data produced by QuakeC programs;
\item the restrictions and limits imposed by a conforming implementation of QuakeC.
\end{itemize}
This document does not specify
\begin{itemize}
\item the mechanism by which QuakeC programs are transformed for use by a data-
processing system;
\item the mechanism by which QuakeC programs are invoked for use by a data-processing
system;
\item the mechanism by which input data are transformed for use by a QuakeC program;
\item the size or complexity of a program and its data that will exceed the capacity
of any specific data-processing system or the capacity of a particular
execution environment;
\item all minimal requirements of a data-processing system that is capable of
supporting a conforming implementation.
\end{itemize}
%% Table Of Contents %%
\newpage
\thispagestyle{empty}
\tableofcontents
\newpage
%% Begin Contents %%
\raggedright % No weird TEX spacing on lines to fill page
%% -> Terms, definitions, and symbols %%
\section{Terms, definitions, and symbols}
\subsection*{argument}
Expression in the comma-separated list bounded by the parentheses in a function call
expression, or a sequence of preprocessing tokens in the comma-separated list bounded
by the parentheses in a function-like macro invocation.
\subsection*{behavior}
External appearance or action
\subsection*{implementation-defined behavior}
Unspecified behavior where each implementation documents how the choice is made.
\subsection*{undefined behavior}
Behavior, upon use of a non-portable or erroneous program construct or of erroneous data,
for which this document imposes no actual requirements.
\subsection*{unspecified behavior}
Use of an unspecified value, or other behavior where this document provides two or more
possibilities and imposes no further requirements on which is chosen in any instance.
\subsection*{constraint}
Restriction, either syntactic or semantic, by which the exposition of language elements
is to be interpreted.
\subsection*{diagnostic message}
Message belonging to an implementation-defined subset of the implementation's message
output.
\subsection*{object}
Region of data storage in the execution environment, the contents of which can represent
values.
\subsection*{parameter}
Object declared as part of a function declaration or definition that acquires a value on
entry to the function, or an identifier from the comma-separated list bounded by the
parentheses immediately following the macro name in a function-like macro definition.
\subsection*{recommended practice}
Specification that is strongly recommended as being in keeping with the intent of this
document, but that may be impractical for some implementations.
\subsection*{value}
Precise meaning of the contents of an object when interpreted as having a specific type.
\subsection*{implementation}
Particular set of software, running in a particular translation environment under
particular control options, that performs translation of programs for, and supports
execution of functions in, a particular execution environment.
\subsection*{implementation-defined value}
Unspecified value where each implementation documents how the choice is made.
\subsection*{unspecified value}
Valid value of the relevant type where this document imposes no requirements on which
value is chosen in any instance.
%% -> Conformance %%
\section{Conformance}
In this document, "shall" is to be interpreted as a requirement on an implementation
or on a program; conversely, "shall not" is to be interpreted as a prohibition. \\
If a "shall" or "shall not" requirement that appears outside of a constraint is violated,
the behavior is undefined. Undefined behavior is otherwise indicated in this document by
the words "undefined behavior" or by the omission of any explicit definition of behavior.
There is no difference in emphasis among these three; they all describe "behavior that is
undefined".
%% -> Enviroment %%
\section{Environment}
An implementation that translates QuakeC source files and executes QuakeC programs in two
data processing-system environments, which will be called the translation environment and
the execution environment in this document. Their characteristics define and constrain the
results of executing QuakeC programs constructed according to the syntactic and semantic
rules for conforming implementations.
\subsection{Conceptual models}
\subsubsection{Translation environment}
\paragraph*{Translation steps}
The precedence among the syntax rules of translation is specified by the following steps
\begin{enumerate}
\item Physical source file characters are mapped, in an implementation-defined manner,
to the source character set (introducing new-line characters for end-of-line
indicators) if necessary. Trigraph and digraph sequences are replaced by their
corresponding single-character internal representations.
\item The source file is decomposed into preprocessing tokens and sequences of white-
space characters (including comments). A source file shall not end in a partial
preprocessing token or in a partial comment. Each comment is replaced by one
space character. New-line characters are retained. Whether each nonempty
sequences of white-space characters other than new-line is retained or replaced
by one space character is implementation-defined.
\item Preprocessing directives are executed, macro invocations are expanded
recursively. A \#include preprocessing directive causes the named header or
source file to be processed from step one through step three, recursively. All
preprocessing directives are then deleted.
\item Each source character set member and escape sequence in character constants and
string literals is converted to the corresponding member of the execution
character set; if there is no corresponding member, it is converted to an
implementation-defined member other than the null character.
\item Adjacent string literal tokens are concatenated.
\item White-space characters seperating tokens are no longer significant. Each
preprocessing token is converted into a token. The resulting tokens are then
syntactically and semantically analyzed and translated.
\end{enumerate}
\subparagraph*{Footnotes}
Implementations shall behave as if these steps occur separately, even though many are likely
to be folded together in practice. Source files need not be stored as file, nor need there
be any one-to-one correspondence between these items and any external representation. The
description is conceptual only, and does not specify any particular implementation.
\paragraph*{Diagnostics}
A conforming implementation shall produce at least on diagnostic message(identified in an
implementation-defined manner) if a source file contains a violation of any syntax rule or
constraint, even if the behavior is also explicitly specified as undefined or
implementation-defined. Diagnostic messages need not be produced in other circumstances.
%% ->-> Execution environments %%
\subsubsection{Execution environment}
A conforming execution environment shall provide at minimal the following 15 definitions
for built in functions, with an accompanying header or source file that defines them.
\begin{enumerate}
\item entity () spawn
\item void (entity) remove
\item string (float) ftos
\item string (vector) vtos
\item string (entity) etos
\item float (string) stof
\item void (string, ...) dprint
\item void (entity) eprint
\item float (float) rint
\item float (float) floor
\item float (float) ceil
\item float (float) fabs
\item float (float) sin
\item float (float) cos
\item float (float) sqrt
\end{enumerate}
The numbers of which these built-ins are assigned is implementation-defined;
an implementation is allowed to use these built-ins however it sees fit.
\pagebreak
%% -> Language %%
\section{Language}
\subsection{Notation}
The syntax notation used in this document is that of a BNF specification. A set of
derivation rules, often written as:
\begin{lstlisting}[language=bnf]
symbol ::= expression
\end{lstlisting}
Where symbol is a nonterminal, and the expression consists of one or more sequences of
symbols; more sequences are separated by a vertical bar \textbar, indicating a choice,
the whole being a possible substitution for the symbol on the left. Symbols that never
appear on the left side are terminals.
\linebreak
This document defines language syntax throughout it's way at defining language
constructs If you're interested in a summary of the language syntax, one is given in
annex A.
%% -> Concepts %%
\subsection{Concepts}
%% ->-> Scopes of identifiers %%
\subsubsection{Scopes of identifiers}
An identifier can denote an object; a function, or enumeration; a label name; a macro
name; or a macro parameter. The same identifier can denote different items at different
points in the program. A member of an enumeration is called an enumeration constant.
Macro names and macro parameters are not considered further here, because prior to the
semantic phase of program translation any occurrences of macro names in the source file
are replaced by the preprocessing token sequences that constitute their macro definitions.
\linebreak
For each different item that an identifier designates, the identifier is visible (i.e,
can be used) only within a region of program text called its scope. Different items
designated by the same identifier either have different scopes, or are in different name
spaces. There are four kinds of scopes: function, file, block and function prototype.
(A function prototype is a declaration of a function that declares the types of its
parameters.)
\linebreak
A label name is the only kind of identifier that has function scope. It can be used (in
a goto statement) anywhere in the function in which it appears, and is declared
implicitly by its syntactic appearance (prefixed by a colon :, and suffixed with a
statement).
\linebreak
Every other identifier has scope determined by the placement of its declaration (in a
declarator or type specifier). If the declarator or type specifier that declares the
identifier appears outside any block or list of parameters, the identifier has file
scope, which terminates at the end of the file. If the declartor or type specifier that
declares the identifier appears inside a block or within the list of parameter
declarations in a function definition, the identifier has block scope, which terminates
at the end of the associated block. If the declarator or type specifier that declares
the identifier appears within the list of parameter declarations in a function prototype
(not part of a function definition), the identifier has function prototype scope, which
terminates at the end of the function declarator. If an identifier designates two
different items in the same name space, the scopes might overlap. If so, the scope of
one item (the inner scope) will be a strict subset of the scope of the other item (the
outer scope). Within the inner scope, the identifier designates the item declared in the
inner scope; the item declared in the outer scope is hidden (and not visible) within
the inner scope.
\linebreak
Unless explicitly stated otherwise, where this document uses the term "identifier" to
refer to some item (as opposed to the syntactic construct), it refers to the item in the
relevant name space whose declaration is visible at the point the identifier occurs.
\linebreak
Two identifiers have the same scope if and only if their scopes terminate at the same
point.
\linebreak
Each enumeration constant has scope that begins just after the appearance of its defining
enumerator in an enumerator list. Any other identifier has scope that begins just after
the completion of its declarator.
%% ->-> Name spaces of identifiers %%
\subsubsection{Name spaces of identifiers}
If more than one declaration of a particular identifier is visible at any point in a
source file, the syntactic context disambiguates uses that refer to different items.
Thus, there are separate name spaces for various categories of identifiers, as follows:
\linebreak
\begin{itemize}
\item Label names (disambiguated by the syntax of the label declaration and use);
\item Enumerations (disambiguated by following the keyword enum);
\item All other identifiers, called ordinary identifiers (declared in ordinary
declarators or as enumeration constants).
\end{itemize}
%% ->-> Types %%
\subsubsection{Types}
The meaning of a value stored in an object returned by a function is determined by the
type of the expression used to access it. (An identifier declared to be an object is the simplest
such expression; the type is specified in the declaration of the identifier.) Types are
partitioned into object types (types that fully describe objects), function types(types
that describe functions), and incomplete types(types that describe objects but lack
information).
\linebreak
An object declared type bool is large enough to store the values 0 and 1.
\linebreak
An object declared type float is a real type; An object declared type vector is a
comprised set of three floats that respectively represent the \underline{x,y,z}
components of a three-dimensional vector.
\linebreak
An enumeration comprises a set of named integer constant values. Each distinct
enumeration constitutes a different enumerated type.
\linebreak
Enumeration types and float are collectively called arithmetic types. Each arithmetic
type belongs to one type domain.
\linebreak
The void type comprises an empty set of values; it is an incomplete type that cannot be
completed.
\linebreak
A number of derived types can be constructed from the object, function and incomplete
types, as follows:
\linebreak
\begin{itemize}
\item An array type describes a contiguously allocated nonempty set of objects with a
particular object type, called the element type. Array types are characterized
by their element type and by the number of elements in the array. An array type
is said to be derived from its element type, and if its element is type T, the
array type is sometimes called "array of T". The construction of an array type
from an element type is called "array type derivation".
\item A function type describes a function with a specified return type. A function
type is characterized by its return type and the number and types of its
parameters. A function type is said to be derived from its return type, and if
its return type is T, the function type is sometimes called "function returning
T". The construction of a function type from a return type is called "function
type derivation".
\end{itemize}
Arithmetic types are collectively called scalar types. Arrays and vectors are
collectively called aggregate types.
\linebreak
An array of unknown size is an incomplete type. It is completed, for an identifier of
that type, by specifying the size in a later declaration. Arrays are required to have
known constant size.
\linebreak
A type is characterized by its type category, which is either the outermost derivation
of a derived type (as noted above in the construction of derived types), or the type
itself if the type consists of no derived types.
\linebreak
Any type so far mentioned is an unqualified type. Each unqualified type has several
qualified versions of its type, corresponding to the combinations of one, two, or all
two of const and volatile qualifiers. The qualified or unqualified versions of a type
are distinct types that belong to the same type category and have the same representation.
A derived type is not qualified by the qualifiers (if any) of the type from which it
is derived.
\linebreak
%% ->-> Compatible types and composite type %%
\subsubsection{Compatible types and composite type}
Two types have compatible type if their types are the same.
\linebreak
All declarations that refer to the same object or function shall have compatible type;
otherwise the behavior is undefined.
\linebreak
A composite type can be constructed from two types that are compatible; it is a type that
is compatible with both of the two types and satisfies the following conditions:
\begin{itemize}
\item If one type is an array, the composite type is an array of that size.
\item If only one type is a function type with a parameter type list(a function
prototype), the composite type is a function prototype with the parameter type
list.
\item If both types are function types with parameter type lists, the type of each
parameter in the composite parameter type list is the composite type of the
corresponding parameters.
\end{itemize}
These rules apply recursively to types from which the two types are derived.
\linebreak
%% ->Conversions %%
\subsection{Conversions}
Several operators convert operand values from one type to another automatically. This
sub-clause specifies the result required from such an implicit conversion.
\linebreak
Conversion from an operand value to a compatible type causes no change to the value or
the representation.
\linebreak
TODO: Specify all implicit conversions.
%% ->->Aritmetic operands %%
\subsubsection{Arithmetic operands}
\paragraph*{Boolean type}
When any scalar value is converted to bool, the result is 0 if the value compares equal
to 0; otherwise the result is 1.
%% ->->Other operands %%
\subsubsection{Other operands}
\paragraph{Lvalues, arrays and function designators}
An lvalue is an expression with an object type or an incomplete type other than void;
if an lvalue does not designate an object when it is evaluated, the behavior is undefined.
When an object is said to have a particular type, the type is specified by the lvalue
used to designate the object. A modifiable lvalue is an lvalue that does not have an
array type, does not have an incomplete type, and does not have a const-qualified type.
\linebreak
Except when it is the operand of the unary \& operator, the ++ operator, the -- operator,
or the left operand of the . operator or an assignment operator, an lvalue that does not
have array type is converted to the value stored in the designated object (and is no
longer an lvalue). If the lvalue has qualified type, the value has the unqualified
version of the type of the lvalue; otherwise, the value has the type of the lvalue. If
the lvalue has an incomplete type and does not have array type, the behavior is undefined.
\linebreak
A function designator is an expression that has function type.
\paragraph*{void}
The (nonexistent) value of a void expression (an expression that has type void) shall not
be used in any way, and implicit conversions (except to void) shall not be applied to
such an expression. If an expression of any other type is evaluated as a void expression,
its value or designator is discarded. (A void expression is only evaluated for its
side effects.)
\pagebreak
\subsection{Lexical elements}
\paragraph*{Syntax}
\begin{lstlisting}[language=bnf]
token ::= keyword
| identifier
| constant
| string-literal
| punctuator
preprocessing-token ::= header-name
| identifier
| pp-number
| string-literal
| punctuator
\end{lstlisting}
\paragraph*{Constraints}
Each preprocessing token that is converted to a token shall have the lexical form of a
keyword, an identifier, a constant, a string literal, or a punctuator.
\paragraph*{Semantics}
A token is the minimal lexical element of the language in translation steps six and seven.
The categories of tokens are: keywords, identifiers, constants, string literals, and
punctuators. A preprocessing token is the minimal lexical element of the language in
translation steps three through five. The categories of preprocessing tokens are: header
names, identifiers, preprocessing numbers, string literals, punctuators and other single
non-white-space characters that do not lexically match the other preprocessing token
categories. If a ' or a " character matches the last category, the behavior is undefined.
Preprocessing tokens can be separated by white space; this consists of comments (described
later), or white-space characters (space, horizontal tab, new-line, vertical tab, and form
-feed), or both. In certain circumstances during translation step four, white space (or
the absence thereof) serves as more than preprocessing token separation. White space may
appear within a preprocessing token only as part of a header name or between the quotation
characters in a string literal.
\linebreak
If the input stream has been parsed into preprocessing tokens up to a given character, the
next preprocessing token is the longest sequence of characters that could constitute a
preprocessing token. There is one exception to this rule: header name preprocessing tokens
are recognized only within \#include preprocessing directives and in implementation-defined
locations within \#pragma directives. In such contexts, a sequence of characters that
could be either a header name or string literal is recognized as the former.
%% ->-> Keywords %%
\subsubsection{Keywords}
\paragraph*{Syntax}
\begin{lstlisting}[language=bnf]
keyword ::= enum | break | return | void
| case | float | volatile | for
| while | const | goto | bool
| continue | if | static | default
| inline | do | switch | else
| vector | entity
\end{lstlisting}
\paragraph*{Semantics}
The above tokens (case sensitive) are reserved (in translation step seven and eight) for
use as keywords, and shall not be used otherwise.
%% ->->Identifiers %%
\subsubsection{Identifiers}
\begin{lstlisting}[language=bnf]
identifier ::= nondigit
| identifier nondigit
| identifier digit
nondigit ::= _ | a | b | c | d | e | f | g | h | i
| j | k | l | m | n | o | p | q | r | s
| t | u | v | w | x | y | z | A | B | C
| D | E | F | G | H | I | J | K | L | M
| N | P | Q | R | S | T | U | V | W | X
| Y | Z
digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9
\end{lstlisting}
\paragraph*{Semantics}
An identifier is a sequence of nondigit characters (including the underscore \_, the lower
case and upper case Latin letters, and other characters) and digits, which designates one
or more items. Lowercase and uppercase letters are distinct. There is a specific limit of
65535 characters for an identifier.
\linebreak
When preprocessing tokens are converted to tokens during translation step six, if a
preprocessing token could not be converted to either a keyword or an identifier, it is
converted to a keyword.
\paragraph*{Predefined identifiers}
Any identifiers that begin with the prefix \_\_builtin, or are within the reserved name
space are reserved by the implementation.
%% ->->Constants %%
\subsubsection{Constants}
\begin{lstlisting}[language=bnf]
constant ::= integer-constant
| floating-constant
| enumeration-constant
| character-constant
| vector-constant
integer-constant ::= decimal-constant
| octal-constant
| hexadecimal-constant
decimal-constant ::= nonzero-digit
| decimal-constant digit
octal-constant ::= 0
| octal-constant octal-digit
hexadecimal-constant ::= hexdecimal-prefix
hexadecimal-digit
| hexadecimal-digit
hexadecimal-constant
hexadecimal-prefix: ::= 0x | 0X
nonzero-digit ::= 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8
| 9
octal-digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
hexadecimal-digit ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
| 8 | 9 | a | b | c | d | e | f
| A | B | C | D | E | F
\end{lstlisting}
%% ->-> String literals %%
\subsubsection{String literals}
\begin{lstlisting}[language=bnf]
string-literal := " s-char-sequence "
s-char-sequence := s-char
| s-char-sequence s-char
s-char := ` | ! | @ | # | $ | % | ^ | & | *
| ( | ) | _ | - | + | = | { | } | [
| ] | | | : | ; | ' | < | , | > | .
| ? | / | 1 | 2 | 3 | 4 | 5 | 6 | 7
| 8 | 9 | 0 | q | w | e | r | t | y
| u | i | o | p | a | s | d | f | g
| h | j | k | l | z | x | c | v | b
| n | m | Q | W | E | R | T | Y | U
| I | O | P | A | S | D | F | G | |
| H | J | K | L | Z | X | C | V | B
| N | M
\end{lstlisting}
\paragraph*{Description}
A character string literal is a sequence of zero or more characters enclosed in
double-quotes, as in "xyz".
\linebreak
The same considerations apply to each element of the sequence in a character string
literal as if it where an integer character constant, except that the single-quote
' is representable either by itself or by the escape sequence \textbackslash', but
the double-quote " shall be represented by the escape sequence \textbackslash".
\paragraph*{Semantics}
In translation stage six, the character sequences specified by any sequence of adjacent
character string literal tokens are concatenated into a single character sequence.
%% ->-> Punctuators %%
\subsubsection{Punctuators}
TODO: BNF
A punctuator is a symbol that has independent syntactic and semantic significance.
Depending on context, it may specify an operation to be performed (which in turn
may yield a value or a function designator, produce a side effect, or some combination
thereof) in which case it is known as an operator (other forms of operator also exist
in some contexts). An operand is an item on which an operator acts.
\linebreak
TODO: Trigraphs \& Digraphs
\subsubsection{Header names}
TODO
\subsubsection{Preprocessing numbers}
TODO
\subsubsection{Comments}
Except within a character constant, a string literal, or a comment, the characters /*
introduce a comment. The contents of such a comment are examined only to identify
characters and to find the characters */ that terminate it.
\linebreak
Except within a character constant, a string literal, or a comment, the characters //
introduce a comment that includes all characters up to, but not including, the next
new-line character. The contents of such a comment are examined only to identify
characters and to find the terminating new-line character.
\linebreak
%% -> Expressions %%
\subsection{Expressions}
An expression is a sequence of operators and operands that specifies computation of a
value, or that designates an object or function, or that generates side effects, or that
performs a combination thereof.
\linebreak
Between the previous and next sequence point an object shall have its stored value
modified at most once by the evaluation of an expression. Furthermore, the prior value
shall be read only to determine the value to be stored.
\linebreak
The grouping of operators and operands is indicated by the syntax. Except as specified
later (for the function call (), \&\&, \textbar\textbar ?:, and comma operators), the
order of evaluation of sub-expressions and the order in which side effects take place
are both unspecified.
\linebreak
Some operators (the unary \textasciitilde operator, and the binary operators \textless
\textless, \textgreater\textgreater, \&, \^, and \textbar, collectively describe bitwise
operators) are required to have operands that are either integer, or floating point with
zero points of decimal precision.
\linebreak
If an exceptional condition occurs during the evaluation of an expression (that is, if
the result is not mathematically defined or not in the range or representable values for
its type), the behavior is undefined.
%% ->-> Primary expressions %%
\subsubsection{Primary expressions}
\paragraph*{Syntax}
\begin{lstlisting}[language=bnf]
primary-expression ::= identifier
| constant
| string-literal
( expression )
\end{lstlisting}
\paragraph*{Semantics}
An identifier is a primary expression, provided it has been declared as designating an
object(in which case it is an lvalue) or a function(in which case it is a function
designator).
\linebreak
A constant is a primary expression. Its type depends on its form and value.
\linebreak
A string literal is a primary expression. It is an lvalue.
\linebreak
A parenthesized expression is a primary expression. Its type and value identical to
those of the unparenthesized expression. It is an lvalue, a function designator, or a
void expression if the unparenthesized expression is, respectively, an lvalue, a
function designator, or a void expression.
%% ->-> Constant expressions %%
\subsubsection{Constant expressions}
\paragraph*{Syntax}
\begin{lstlisting}[language=bnf]
constant-expression ::= conditional-expression
\end{lstlisting}
\paragraph*{Description}
A constant expression can be evaluated during translation rather than runtime, and
accordingly may be used in any place that a constant may be.
\paragraph*{Constraints}
\begin{itemize}
\item Constant expressions shall not contain assignment, increment, decrement,
function-call, or comma operators, except when contained within a subexpression
that is not evaluated.
\item Each constant expression shall evaluate to a constant that is in range of
representable values for its type.
\end{itemize}
\paragraph*{Semantics}
An expression that evaluates to a constant is required in several contexts. If a floating
point expression is evaluated in the translation environment, the arithmetic precision range
shall be as great is if the expression were being evaluated in the execution environment.
\linebreak
An integer constant expression shall have integer type and shall only have operands that
are integer constants, enumeration constants, character constants, and floating constants
that are the immediate operand of casts. Cast operators in an integer constant expression
shall only convert arithmetic types to integer types.
\linebreak
More latitude is permitted for constant expressions in initializers. Such a constant expression
shall be, or evaluate to an arithmetic constant expression.
\linebreak
An arithmetic constant expression shall have arithmetic type and shall only have operands that
are integer constants, floating constants, enumeration constants, and character constants. Cast
operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic
types.
\linebreak
An implementation may accept other forms of constant expressions.
\linebreak
The semantic rules for the evaluation of a constant expression are the same as for nonconstant
expressions.
\bibliographystyle{abbrv}
\bibliography{main}
\end{document}

File diff suppressed because it is too large Load diff

1679
fold.cpp

File diff suppressed because it is too large Load diff

121
fold.h
View file

@ -1,121 +0,0 @@
#ifndef GMQCC_FOLD_HDR
#define GMQCC_FOLD_HDR
#include "lexer.h"
#include "gmqcc.h"
struct ir_builder;
struct ir_value;
struct ast_function;
struct ast_ifthen;
struct ast_ternary;
struct ast_expression;
struct ast_value;
struct parser_t;
struct fold {
fold();
fold(parser_t *parser);
~fold();
// Bitmask describing which branches of a conditional to take after folding.
// Zero indicates all the branches can be removed.
// ON_TRUE means ON_FALSE can be removed.
// ON_FALSE means ON_TRUE can be removed.
// ON_TRUE | ON_FALSE means nothing can be removed.
enum {
ON_TRUE = 1 << 0,
ON_FALSE = 1 << 1,
};
bool generate(ir_builder *ir);
ast_expression *op(const oper_info *info, ast_expression **opexprs);
ast_expression *intrinsic(const char *intrinsic, size_t n_args, ast_expression **args);
static uint32_t cond_ternary(ast_value *condval, ast_ternary *branch);
static uint32_t cond_ifthen(ast_value *condval, ast_ifthen *branch);
static ast_expression *superfluous(ast_expression *left, ast_expression *right, int op);
static ast_expression *binary(lex_ctx_t ctx, int op, ast_expression *left, ast_expression *right);
ast_expression *constgen_float(qcfloat_t value, bool inexact);
ast_expression *constgen_vector(vec3_t value);
ast_expression *constgen_string(const char *str, bool translate);
ast_expression *constgen_string(const std::string &str, bool translate);
ast_value *imm_float(size_t index) const { return m_imm_float[index]; }
ast_value *imm_vector(size_t index) const { return m_imm_vector[index]; }
protected:
static qcfloat_t immvalue_float(ast_value *value);
static vec3_t immvalue_vector(ast_value *value);
static const char *immvalue_string(ast_value *value);
lex_ctx_t ctx();
bool immediate_true(ast_value *v);
bool check_except_float_impl(void (*callback)(void), ast_value *a, ast_value *b);
bool check_inexact_float(ast_value *a, ast_value *b);
ast_expression *op_mul_vec(vec3_t vec, ast_value *sel, const char *set);
ast_expression *op_neg(ast_value *a);
ast_expression *op_not(ast_value *a);
ast_expression *op_add(ast_value *a, ast_value *b);
ast_expression *op_sub(ast_value *a, ast_value *b);
ast_expression *op_mul(ast_value *a, ast_value *b);
ast_expression *op_div(ast_value *a, ast_value *b);
ast_expression *op_mod(ast_value *a, ast_value *b);
ast_expression *op_bor(ast_value *a, ast_value *b);
ast_expression *op_band(ast_value *a, ast_value *b);
ast_expression *op_xor(ast_value *a, ast_value *b);
ast_expression *op_lshift(ast_value *a, ast_value *b);
ast_expression *op_rshift(ast_value *a, ast_value *b);
ast_expression *op_andor(ast_value *a, ast_value *b, float expr);
ast_expression *op_tern(ast_value *a, ast_value *b, ast_value *c);
ast_expression *op_exp(ast_value *a, ast_value *b);
ast_expression *op_lteqgt(ast_value *a, ast_value *b);
ast_expression *op_ltgt(ast_value *a, ast_value *b, bool lt);
ast_expression *op_cmp(ast_value *a, ast_value *b, bool ne);
ast_expression *op_bnot(ast_value *a);
ast_expression *op_cross(ast_value *a, ast_value *b);
ast_expression *op_length(ast_value *a);
ast_expression *intrinsic_isfinite(ast_value *a);
ast_expression *intrinsic_isinf(ast_value *a);
ast_expression *intrinsic_isnan(ast_value *a);
ast_expression *intrinsic_isnormal(ast_value *a);
ast_expression *intrinsic_signbit(ast_value *a);
ast_expression *intrinsic_acosh(ast_value *a);
ast_expression *intrinsic_asinh(ast_value *a);
ast_expression *intrinsic_atanh(ast_value *a);
ast_expression *intrinsic_exp(ast_value *a);
ast_expression *intrinsic_exp2(ast_value *a);
ast_expression *intrinsic_expm1(ast_value *a);
ast_expression *intrinsic_pow(ast_value *lhs, ast_value *rhs);
ast_expression *intrinsic_mod(ast_value *lhs, ast_value *rhs);
ast_expression *intrinsic_fabs(ast_value *a);
ast_expression* intrinsic_nan(void);
ast_expression* intrinsic_epsilon(void);
ast_expression* intrinsic_inf(void);
static qcfloat_t immvalue_float(ir_value *value);
static vec3_t immvalue_vector(ir_value *value);
static uint32_t cond(ast_value *condval, ast_ifthen *branch);
private:
friend struct intrin;
std::vector<ast_value*> m_imm_float;
std::vector<ast_value*> m_imm_vector;
std::vector<ast_value*> m_imm_string;
hash_table_t *m_imm_string_untranslate; /* map<string, ast_value*> */
hash_table_t *m_imm_string_dotranslate; /* map<string, ast_value*> */
parser_t *m_parser;
bool m_initialized;
};
#endif

312
fs.c Normal file
View file

@ -0,0 +1,312 @@
/*
* 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.
*/
#include "gmqcc.h"
/*
* This is essentially a "wrapper" interface around standard C's IO
* library. There is two reason we implement this, 1) visual studio
* hearts for "secure" varations, as part of it's "Security Enhancements
* in the CRT" (http://msdn.microsoft.com/en-us/library/8ef0s5kh.aspx).
* 2) But one of the greater reasons is for the possibility of large file
* support in the future. I don't expect to reach the 2GB limit any
* time soon (mainly because that would be insane). But when it comes
* to adding support for some other larger IO tasks (in the test-suite,
* or even the QCVM we'll need it). There is also a third possibility of
* building .dat files directly from zip files (which would be very cool
* at least I think so).
*/
#ifdef _MSC_VER
#include <crtdbg.h> /* _CrtSetReportMode, _CRT_ASSERT */
/* {{{ */
/*
* Visual Studio has security CRT features which I actually want to support
* if we ever port to Windows 8, and want GMQCC to be API safe.
*
* We handle them here, for all file-operations.
*/
static void file_exception (
const wchar_t *expression,
const wchar_t *function,
const wchar_t *file,
unsigned int line,
uintptr_t reserved
) {
wprintf(L"Invalid parameter dectected %s:%d %s [%s]\n", file, line, function, expression);
wprintf(L"Aborting ...\n");
exit(EXIT_FAILURE);
}
static void file_init() {
static bool init = false;
if (init)
return;
_set_invalid_parameter_handler(&file_exception);
/*
* Turnoff the message box for CRT asserations otherwise
* we don't get the error reported to the console as we should
* otherwise get.
*/
_CrtSetReportMode(_CRT_ASSERT, 0);
init = !init;
}
FILE *fs_file_open(const char *filename, const char *mode) {
FILE *handle = NULL;
file_init();
return (fopen_s(&handle, filename, mode) != 0) ? NULL : handle;
}
size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) {
file_init();
return fread_s(buffer, size*count, size, count, fp);
}
int fs_file_printf(FILE *fp, const char *format, ...) {
int rt;
va_list va;
va_start(va, format);
file_init();
rt = vfprintf_s(fp, format, va);
va_end (va);
return rt;
}
/* }}} */
#else
/* {{{ */
/*
* All other compilers/platforms that don't restrict insane policies on
* IO for no aparent reason.
*/
FILE *fs_file_open(const char *filename, const char *mode) {
return fopen(filename, mode);
}
size_t fs_file_read(void *buffer, size_t size, size_t count, FILE *fp) {
return fread(buffer, size, count, fp);
}
int fs_file_printf(FILE *fp, const char *format, ...) {
int rt;
va_list va;
va_start(va, format);
rt = vfprintf(fp, format, va);
va_end (va);
return rt;
}
/* }}} */
#endif
/*
* These are implemented as just generic wrappers to keep consistency in
* the API. Not as macros though
*/
void fs_file_close(FILE *fp) {
/* Invokes file_exception on windows if fp is null */
fclose (fp);
}
size_t fs_file_write (
const void *buffer,
size_t size,
size_t count,
FILE *fp
) {
/* Invokes file_exception on windows if fp is null */
return fwrite(buffer, size, count, fp);
}
int fs_file_error(FILE *fp) {
/* Invokes file_exception on windows if fp is null */
return ferror(fp);
}
int fs_file_getc(FILE *fp) {
/* Invokes file_exception on windows if fp is null */
return fgetc(fp);
}
int fs_file_puts(FILE *fp, const char *str) {
/* Invokes file_exception on windows if fp is null */
return fputs(str, fp);
}
int fs_file_seek(FILE *fp, long int off, int whence) {
/* Invokes file_exception on windows if fp is null */
return fseek(fp, off, whence);
}
long int fs_file_tell(FILE *fp) {
/* Invokes file_exception on windows if fp is null */
return ftell(fp);
}
/*
* Implements libc getline for systems that don't have it, which is
* assmed all. This works the same as getline().
*/
int fs_file_getline(char **lineptr, size_t *n, FILE *stream) {
int chr;
int ret;
char *pos;
if (!lineptr || !n || !stream)
return -1;
if (!*lineptr) {
if (!(*lineptr = (char*)mem_a((*n=64))))
return -1;
}
chr = *n;
pos = *lineptr;
for (;;) {
int c = fs_file_getc(stream);
if (chr < 2) {
*n += (*n > 16) ? *n : 64;
chr = *n + *lineptr - pos;
if (!(*lineptr = (char*)mem_r(*lineptr,*n)))
return -1;
pos = *n - chr + *lineptr;
}
if (ferror(stream))
return -1;
if (c == EOF) {
if (pos == *lineptr)
return -1;
else
break;
}
*pos++ = c;
chr--;
if (c == '\n')
break;
}
*pos = '\0';
return (ret = pos - *lineptr);
}
/*
* Now we implement some directory functionality. Windows lacks dirent.h
* this is such a pisss off, we implement it here.
*/
#if defined(_WIN32) && !defined(__MINGW32__)
DIR *fs_dir_open(const char *name) {
DIR *dir = (DIR*)mem_a(sizeof(DIR) + strlen(name));
if (!dir)
return NULL;
util_strncpy(dir->dd_name, name, strlen(name));
return dir;
}
int fs_dir_close(DIR *dir) {
FindClose((HANDLE)dir->dd_handle);
mem_d ((void*)dir);
return 0;
}
struct dirent *fs_dir_read(DIR *dir) {
WIN32_FIND_DATA info;
struct dirent *data;
int rets;
if (!dir->dd_handle) {
char *dirname;
if (*dir->dd_name) {
size_t n = strlen(dir->dd_name);
if ((dirname = (char*)mem_a(n + 5) /* 4 + 1 */)) {
util_strncpy(dirname, dir->dd_name, n);
util_strncpy(dirname + n, "\\*.*", 4); /* 4 + 1 */
}
} else {
if (!(dirname = util_strdup("\\*.*")))
return NULL;
}
dir->dd_handle = (long)FindFirstFile(dirname, &info);
mem_d(dirname);
rets = !(!dir->dd_handle);
} else if (dir->dd_handle != -11) {
rets = FindNextFile ((HANDLE)dir->dd_handle, &info);
} else {
rets = 0;
}
if (!rets)
return NULL;
if ((data = (struct dirent*)mem_a(sizeof(struct dirent)))) {
util_strncpy(data->d_name, info.cFileName, FILENAME_MAX - 1);
data->d_name[FILENAME_MAX - 1] = '\0'; /* terminate */
data->d_namlen = strlen(data->d_name);
}
return data;
}
int fs_dir_change(const char *path) {
return !SetCurrentDirectory(path);
}
int fs_dir_make(const char *path) {
return !CreateDirectory(path, NULL);
}
#else
# if !defined(__MINGW32__)
# include <sys/stat.h> /* mkdir */
int fs_dir_make(const char *path) {
return mkdir(path, 0700);
}
# else
int fs_dir_make(const char *path) {
return mkdir(path);
}
# endif /*! !defined(__MINGW32__) */
DIR *fs_dir_open(const char *name) {
return opendir(name);
}
int fs_dir_close(DIR *dir) {
return closedir(dir);
}
struct dirent *fs_dir_read(DIR *dir) {
return readdir(dir);
}
#endif /*! defined(_WIN32) && !defined(__MINGW32__) */

File diff suppressed because it is too large Load diff

992
gmqcc.h

File diff suppressed because it is too large Load diff

View file

@ -1,745 +1,267 @@
#This configuration file is similar to a regular .ini file. Comments start
#with hashtags or semicolons, sections are written in square brackets and
#in each section there can be arbitrary many key-value pairs.
# This is an example INI file that can be used to set compiler options
# without the rquirement for supplying them as arguments on the command
# line, this can be coupled with progs.src. To utilize this file there
# are two options availble, if it's named "gmqcc.ini" or "gmqcc.cfg" and
# the file exists in the directory that GMQCC is invoked from, the compiler
# will implicitally find and execute regardless. For more freedom you may
# use -config=<file> from the command line to specify a more explicit
# directory/name.ext for the configuration file.
#There are 3 sections currently: flags, warnings, optimizations.
#They contain a list of boolean values of the form VARNAME = true or
#VARNAME = false. The variable names are the same as for the corre
#sponding -W, -f or -O flag written with only capital letters and dashes
#replaced by underscores.
# These are common compiler flags usually represented via the -f prefix
# from the command line.
[flags]
#Add some additional characters to the string table in order to
#compensate for a wrong boundcheck in some specific version of the
#darkplaces engine.
DARKPLACES_STRING_TABLE_BUG = true
#When assigning to field pointers of type .vector the common be
#haviour in compilers like fteqcc is to only assign the x-compo
#nent of the pointer. This means that you can use the vector as
#such, but you cannot use its y and z components directly. This
#flag fixes this behaviour. Before using it make sure your code
#does not depend on the buggy behaviour.
ADJUST_VECTOR_FIELDS = true
#Enable a partially fteqcc-compatible preprocessor. It supports
#all the features used in the Xonotic codebase. If you need more,
#write a ticket.
FTEPP = true
#Enable some predefined macros. This only works in combination
#with '-fftepp' and is currently not included by '-std=fteqcc'.
#The following macros will be added:
#
# __LINE__
# __FILE__
# __COUNTER__
# __COUNTER_LAST__
# __RANDOM__
# __RANDOM_LAST__
# __DATE__
# __TIME__
# __FUNC__
#
#Note that __FUNC__ is not actually a preprocessor macro, but is
#recognized by the parser even with the preprocessor disabled.
#
#Note that fteqcc also defines __NULL__ which becomes the first
#global. Assigning it to a vector does not yield the same result
#as in gmqcc where __NULL__ is defined to nil (See -funtyped-nil
#), which will cause the vector to be zero in all components. With
#fteqcc only the first component will be 0, while the other two
#will become the first to of the global return value. This behav
#ior is odd and relying on it should be discouraged, and thus is
#not supported by gmqcc.
FTEPP_PREDEFS = false
#Enable math constant definitions. This only works in combination
#with '-fftepp' and is currently not included by '-std=fteqcc'.
#The following macros will be added:
#
# M_E
# M_LOG2E
# M_LOG10E
# M_LN2
# M_LN10
# M_PI
# M_PI_2
# M_PI_4
# M_1_PI
# M_2_PI
# M_2_SQRTPI
# M_SQRT2
# M_SQRT1_2
# M_TAU
FTEPP_MATHDEFS = false
#Enable indirect macro expansion. This only works in combination
#with '-fftepp' and is currently not included by '-std=fteqcc'.
#Enabling this behavior will allow the preprocessor to operate more
#like the standard C preprocessor in that it will allow arguments
#of macros which are macro-expanded to be substituted into the
#definition of the macro. As an example:
#
# #define STR1(x) #x
# #define STR2(x) STR1(x)
# #define THE_ANSWER 42
# #define THE_ANSWER_STR STR2(THE_ANSWER) /* "42" */
#
#With this enabled, an expansion of THE_ANSWER_STR will yield
#the string "42". With this disabled an expansion of THE_ANSWER_STR
#will yield "THE_ANSWER"
FTEPP_INDIRECT_EXPANSION = false
#Allow switch cases to use non constant variables.
RELAXED_SWITCH = true
#Perform early out in logical AND and OR expressions. The final
#result will be either a 0 or a 1, see the next flag for more pos
#sibilities.
SHORT_LOGIC = true
#In many languages, logical expressions perform early out in a
#special way: If the left operand of an AND yeilds true, or the
#one of an OR yields false, the complete expression evaluates to
#the right side. Thus true && 5 evaluates to 5 rather than 1.
PERL_LOGIC = false
#Enable the underscore intrinsic: Using _("A string constant")
#will cause the string immediate to get a name with a "dotrans
#late_" prefix. The darkplaces engine recognizes these and trans
#lates them in a way similar to how gettext works.
TRANSLATABLE_STRINGS = true
#Don't implicitly convert initialized variables to constants. With
#this flag, the const keyword is required to make a constant.
INITIALIZED_NONCONSTANTS = false
#If this flag is not set, (and it is set by default in the qcc and
#fteqcc standards), assigning function pointers of mismatching
#signatures will result in an error rather than a warning.
ASSIGN_FUNCTION_TYPES = true
#Produce a linenumber file along with the output .dat file.
LNO = false
#Use C's operator precedence for ternary expressions. Unless your
#code depends on fteqcc-compatible behaviour, you'll want to use
#this option.
CORRECT_TERNARY = true
#Normally vectors generate 4 defs, once for the vector, and once
#for its components with _x, _y, _z suffixes. This option prevents
#components from being listed.
SINGLE_VECTOR_DEFS = true
#Most QC compilers translate if(a_vector) directly as an IF on
#the vector, which means only the x-component is checked. This
#option causes vectors to be cast to actual booleans via a NOT_V
#and, if necessary, a NOT_F chained to it.
#
# if (a_vector) // becomes
# if not(!a_vector)
# // likewise
# a = a_vector && a_float // becomes
# a = !!a_vector && a_float
CORRECT_LOGIC = true
#An empty string is considered to be true everywhere. The NOT_S
#instruction usually considers an empty string to be false, this
#option effectively causes the unary not in strings to use NOT_F
#instead.
TRUE_EMPTY_STRINGS = false
#An empty string is considered to be false everywhere. This means
#loops and if statements which depend on a string will perform a
#NOT_S instruction on the string before using it.
FALSE_EMPTY_STRINGS = true
#Enable utf8 characters. This allows utf-8 encoded character con
#stants, and escape sequence codepoints in the valid utf-8 range.
#Effectively enabling escape sequences like '\{x2211}'.
UTF8 = true
#When a warning is treated as an error, and this option is set
#(which it is by default), it is like any other error and will
#cause compilation to stop. When disabling this flag by using
#-fno-bail-on-werror, compilation will continue until the end, but
#no output is generated. Instead the first such error message's
#context is shown.
BAIL_ON_WERROR = false
#Allow loops to be labeled, and allow 'break' and 'continue' to
#take an optional label to decide which loop to actually jump out
#of or continue.
#
# for :outer (i = 0; i < n; ++i) {
# while (inner) {
# ...;
# if (something)
# continue outer;
# }
# }
LOOP_LABELS = true
#Adds a global named 'nil' which is of no type and can be assigned
#to anything. No typechecking will be performed on assignments.
#Assigning to it is forbidden, using it in any other kind of
#expression is also not allowed.
#
#Note that this is different from fteqcc's __NULL__: In fteqcc,
#__NULL__ maps to the integer written as '0i'. It's can be
#assigned to function pointers and integers, but it'll error about
#invalid instructions when assigning it to floats without enabling
#the FTE instruction set. There's also a bug which allows it to be
#assigned to vectors, for which the source will be the global at
#offset 0, meaning the vector's y and z components will contain
#the OFS_RETURN x and y components.#
#
#In that gmqcc the nil global is an actual global filled with
#zeroes, and can be assigned to anything including fields, vectors
#or function pointers, and they end up becoming zeroed.
UNTYPED_NIL = true
#Various effects, usually to weaken some conditions.
# with -funtyped-nil
# Allow local variables named nil. (This will not
# allow declaring a global of that name.)
PERMISSIVE = false
#Allow variadic parameters to be accessed by QC code. This can be
#achieved via the '...' function, which takes a parameter index
#and a typename.
#
#Example:
#
# void vafunc(string...count) {
# float i;
# for (i = 0; i < count; ++i)
# print(...(i, string), "\n");
# }
VARIADIC_ARGS = true
#Most Quake VMs, including the one from FTEQW or up till recently
#Darkplaces, do not cope well with vector instructions with over
#lapping input and output. This option will avoid producing such
#code.
LEGACY_VECTOR_MATHS = false
#Usually builtin-numbers are just immediate constants. With this
#flag expressions can be used, as long as they are compile-time
#constant.
#
#Example:
#
# void printA() = #1; // the usual way
# void printB() = #2-1; // with a constant expression
EXPRESSIONS_FOR_BUILTINS = true
#Enabiling this option will allow assigning values or expressions
#to the return keyword as if it were a local variable of the same
#type as the function's signature's return type.
#
#Example:
#
# float bar() { return 1024; }
# float fun() {
# return = bar();
# return; // returns value of bar (this can be omitted)
# }
RETURN_ASSIGNMENTS = true
#When passing on varargs to a different functions, this turns some
#static error cases into warnings. Like when the caller's varargs
#are restricted to a different type than the callee's parameter.
#Or a list of unrestricted varargs is passed into restricted
#varargs.
UNSAFE_VARARGS = false
#Always use STORE_F, LOAD_F, STOREP_F when accessing scalar variables.
#This is somewhat incorrect assembly instruction use, but in all engines
#they do exactly the same. This makes disassembly output harder to read,
#breaks decompilers, but causes the output file to be better compressible.
TYPELESS_STORES = false
#In commutative instructions, always put the lower-numbered operand first.
#This shaves off 1 byte of entropy from all these instructions, reducing
#compressed size of the output file.
SORT_OPERANDS = false
#Emulate OP_STATE operations in code rather than using the instruction.
#The desired fps can be set via -state-fps=NUM, defaults to 10.
EMULATE_STATE = false
#Turn on arithmetic exception tests in the compiler. In constant expressions
#which trigger exceptions like division by zero, overflow, underflow, etc,
#the following flag will produce diagnostics for what triggered that
#exception.
ARITHMETIC_EXCEPTIONS = false
#Split vector-literals which are only used dirctly as function parameters
#into 3 floats stored separately to reduce the number of globals at the
#expense of additional instructions.
SPLIT_VECTOR_PARAMETERS = false
#Force all expressions to be "eraseable" which permits the compiler
#to remove unused functions, variables and statements. This is
#equivlant to putting [[eraseable]] on all definitions. This is
#dangerous as it breaks auto cvars, definitions for functions the
#engine may be looking for and translatable strings. Instead, you
#can mark a definition with [[noerase]] to prevent this from happening.
DEFAULT_ERASEABLE = false
# Enabling this can potentially reduces code size by overlapping
# locals where possible.
OVERLAP_LOCALS = false
# in some older versions of the Darkplaces engine the string table
# size is computed wrong causing compiled progs.dat to fail to load
# Enabling this works around the bug by writing a few additional
# null bytes to the end of the string table to compensate.
DARKPLACES_STRING_TABLE_BUG = false
# Enabling this corrects the assignment of vector field pointers via
# subsituting STORE_FLD with STORE_V.
ADJUST_VECTOR_FIELDS = true
# Enabling this allows the use of the FTEQ preprocessor, as well as
# additional preprocessing directives such as #error and #warning.
FTEPP = true
# Enabling this relaxes switch statement semantics
RELAXED_SWITCH = false
# Enabling this allows short-circut evaluation and logic, opposed
# to full evaluation.
SHORT_LOGIC = false
# Enabling this allows perl-like evaluation/logic.
PERL_LOGIC = true
# Enabling this allows the use of the "translatable strings" extension
# assisted by .po files.
TRANSLATABLE_STRINGS = false
# Enabling this prevents initializations from becoming constant unless
# 'const' is specified as a type qualifier.
INITIALIZED_NONCONSTANTS = false
# Enabling this allows function types to be assignable even if their
# signatures are invariant of each other.
ASSIGN_FUNCTION_TYPES = false
# Enabling this will allow the generation of .lno files for engine
# virtual machine backtraces (this is enabled with -g as well).
LNO = false
# Enabling this corrects ternary percedence bugs present in fteqcc.
CORRECT_TERNARY = true
# Prevent the creation of _x, _y and _z progdefs for vectors
SINGLE_VECTOR_DEFS = false
# Cast vectors to real booleans when used in logic expressions.
# This is achieved by using NOT_V.
CORRECT_LOGIC = false
# Always treat empty strings as true. Usuall !"" yields true, because
# the string-NOT instruction considers empty strings to be false, while
# an empty string as condition for 'if' will be considered true, since
# only the numerical value of the global is looked at.
TRUE_EMPTY_STRINGS = false
# Opposite of the above, empty strings are always false. Similar to
# CORRECT_LOGIC this will always use NOT_S to cast a string to a real
# boolean value.
FALSE_EMPTY_STRINGS = false
# Recognize utf-8 characters in character constants, and encode
# codepoint escape sequences in strings as utf-8. This essentially allows
# \{1234} escape sequences to be higher than 255.
UTF8
# When a warning is printed and it is set to be treated as error via
# a -Werror switch, compilation will be stopped, unless this is false.
# When this is false, the rest of the code will be compiled, and at the end
# the file and line of the first warning will be shown.
BAIL_ON_WERROR = true
# Allow loops and switches to be labeled and break and continue to take an
# optional label to target a specific loop/switch.
LOOP_LABELS = false
# Enable the 'nil' null constant, which has no type. It can be used as the
# right hand of any assignment regardless of the required type.
UNTYPED_NIL = false
# Be "permissive". For instance, when -funtyped-nil is used, this allows local
# variables with the name 'nil' to be declared.
PREMISSIVE = false
# Allow vararg access from within QC of the form: ...(argnumber, type)
VARIADIC_ARGS = true
# Most Quake VMs, including the one from FTEQW or up till recently
# Darkplaces, do not cope well with vector instructions with overlapping
# input and output. This option will avoid producing such code.
LEGACY_VECTOR_MATHS = true
# Builtin-numbers are usually just immediate constants.
# The following allows whole expressions to be used, as long as they
# are compile-time constant.
EXPRESSIONS_FOR_BUILTINS = false
# These are all the warnings, usually present via the -W prefix from
# the command line.
[warnings]
#Generate a warning about variables which are declared but never
#used. This can be avoided by adding the noref keyword in front
#of the variable declaration. Additionally a complete section of
#unreferenced variables can be opened using #pragma noref 1 and
#closed via #pragma noref 0.
# ?? Used for debugging ??
DEBUG = false
UNUSED_VARIABLE = false
# Enables warnings about unused variables.
UNUSED_VARIABLE = true
# Enables warnings about uninitialized variables.
USED_UNINITIALIZED = true
#Generate a warning about vector variables which are declared but
#components of it are never used.
# Enables warnings about the unknown control sequences in the source
# stream.
UNKNOWN_CONTROL_SEQUENCE = true
UNUSED_COMPONENT = false
# Enables warnings about the use of (an) extension(s).
EXTENSIONS = true
#Generate a warning if it is possible that a variable can be used
#without prior initialization. Note that this warning is not nec
#essarily reliable if the initialization happens only under cer
#tain conditions. The other way is not possible: that the warning
#is not generated when uninitialized use is possible.
# Enables warnings about redeclared fields.
FIELD_REDECLARED = true
USED_UNINITIALIZED = false
# Enables warnings about missing return values.
MISSING_RETURN_VALUES = true
# Enables warnings about missing parameters for function calls.
INVALID_PARAMETER_COUNT = true
#Generate an error when an unrecognized control sequence in a
#string is used. Meaning: when there's a character after a back
#slash in a string which has no known meaning.
# Enables warnings about locals shadowing parameters or other locals.
LOCAL_SHADOWS = true
UNKNOWN_CONTROL_SEQUENCE = false
# Enables warnings about constants specified as locals.
LOCAL_CONSTANTS = true
# Enables warnings about variables declared as type void.
VOID_VARIABLES = true
#Warn when using special extensions which are not part of the
#selected standard.
# Enables warnings about implicitally declared function pointers.
IMPLICIT_FUNCTION_POINTER = true
EXTENSIONS = false
# Enables warnings for use of varadics for non-builtin functions.
VARIADIC_FUNCTION = true
# Enables warnings about duplicated frame macros.
FRAME_MACROS = true
#Generally QC compilers ignore redeclaration of fields. Here you
#can optionally enable a warning.
# Enables warnings about effectivless statements.
EFFECTLESS_STATEMENT = true
FIELD_REDECLARED = false
# Enables warnings of "end_sys_fields" beiing declared a field.
END_SYS_FIELDS = true
# Enables warnings for infompatible function pointer signatures used
# in assignment.
ASSIGN_FUNCTION_TYPES = true
#Functions which aren't of type void will warn if it possible to
#reach the end without returning an actual value.
# Enables warnings about redefined macros in the preprocessor
PREPROCESSOR = true
MISSING_RETURN_VALUES = false
# Enables warnings about multi-file if statements
MULTIFILE_IF = true
# Enables warnings about double declarations
DOUBLE_DECLARATION = true
#Warn about a function call with an invalid number of parameters.
# Enables warnings about type qualifiers containing both 'var' and
# 'const'
CONST_VAR = true
INVALID_PARAMETER_COUNT = false
# Enables warnings about the use of multibytes characters / constants
MULTIBYTE_CHARACTER = true
# Enables warnings about ternary expressions whos precedence may be
# not what was initially expected.
TERNARY_PRECEDENCE = true
#Warn when a locally declared variable shadows variable.
# Enables warnings about unknown pragmas.
UNKNOWN_PRAGMAS = true
LOCAL_SHADOWS = false
# Enables warnings about unreachable code.
UNREACHABLE_CODE = true
# Enables preprocessor "#warnings"
CPP = true
#Warn when the initialization of a local variable turns the vari
#able into a constant. This is default behaviour unless
#-finitialized-nonconstants is used.
# With the [[attribute]] syntax enabled, warn when an unknown
# attribute is encountered. Its first token will be included in the
# message.
UNKNOWN_ATTRIBUTE = true
LOCAL_CONSTANTS = false
# Warn when declaring variables or fields with a reserved name like 'nil'
RESERVED_NAMES = true
# Warn about 'const'-qualified global variables with no initializing value.
UNINITIALIZED_CONSTANT = true
#There are only 2 known global variables of type void:
#end_sys_globals and end_sys_fields. Any other void-variable
#will warn.
# Warn about non-constant global variables with no initializing value.
UNINITIALIZED_GLOBAL = true
VOID_VARIABLES = false
# Redeclaring a 'const' as 'var' or the other way round.
DIFFERENT_QUALIFIERS = true
# Redeclaring a function with different attributes such as
# [[noreturn]]
DIFFERENT_ATTRIBUTES = true
#A global function which is not declared with the var keyword is
#expected to have an implementing body, or be a builtin. If nei
#ther is the case, it implicitly becomes a function pointer, and a
#warning is generated.
IMPLICIT_FUNCTION_POINTER = false
#Currently there's no way for an in QC implemented function to
#access variadic parameters. If a function with variadic parame
#ters has an implementing body, a warning will be generated.
VARIADIC_FUNCTION = false
#Generate warnings about $frame commands, for instance about
#duplicate frame definitions.
FRAME_MACROS = false
#Warn about statements which have no effect. Any expression which
#does not call a function or assigns a variable.
EFFECTLESS_STATEMENT = false
#The end_sys_fields variable is supposed to be a global variable
#of type void. It is also recognized as a field but this will
#generate a warning.
END_SYS_FIELDS = false
#Warn when assigning to a function pointer with an unmatching sig
#nature. This usually happens in cases like assigning the null
#function to an entity's .think function pointer.
ASSIGN_FUNCTION_TYPES = false
#Show warnings created using the preprocessor's '#warning' directive
CPP = true
#Warn if there's a preprocessor #if spanning across several files.
MULTIFILE_IF = true
#Warn about multiple declarations of globals. This seems pretty
#common in QC code so you probably do not want this unless you
#want to clean up your code.
DOUBLE_DECLARATION = false
#The combination of const and var is not illegal, however differ
#ent compilers may handle them differently. We were told, the
#intention is to create a function-pointer which is not assigna
#ble. This is exactly how we interpret it. However for this
#interpretation the var keyword is considered superfluous (and
#philosophically wrong), so it is possible to generate a warning
#about this.
CONST_VAR = true
#Warn about multibyte character constants, they do not work right
#now.
MULTIBYTE_CHARACTER = false
#Warn if a ternary expression which contains a comma operator is
#used without enclosing parenthesis, since this is most likely not
#what you actually want. We recommend the -fcorrect-ternary
#option.
TERNARY_PRECEDENCE = false
#Warn when encountering an unrecognized #pragma line.
UNKNOWN_PRAGMAS = true
#Warn about unreachable code. That is: code after a return state
#ment, or code after a call to a function marked as 'noreturn'.
UNREACHABLE_CODE = true
#Enable some warnings added in order to help debugging in the com
#piler. You won't need this.
DEBUG = false
#Warn on an unknown attribute. The warning will inlclude only the
#first token inside the enclosing attribute-brackets. This may
#change when the actual attribute syntax is better defined.
UNKNOWN_ATTRIBUTE = true
#Warn when using reserved names such as nil.
RESERVED_NAMES = true
#Warn about global constants (using the const keyword) with no
#assigned value.
UNINITIALIZED_CONSTANT = true
#Warn about global variables with no initializing value. This is
#off by default, and is added mostly to help find null-values
#which are supposed to be replaced by the untyped 'nil' constant.
UNINITIALIZED_GLOBAL = true
#Warn when a variables is redeclared with a different qualifier.
#For example when redeclaring a variable as 'var' which was previ
#ously marked 'const'.
DIFFERENT_QUALIFIERS = true
#Similar to the above but for attributes like [[noreturn]].
DIFFERENT_ATTRIBUTES = true
#Warn when a function is marked with the attribute "[[depre
#cated]]". This flag enables a warning on calls to functions
#marked as such.
DEPRECATED = true
#Warn about possible mistakes caused by missing or wrong parenthe
#sis, like an assignment in an 'if' condition when there's no
#additional set of parens around the assignment.
PARENTHESIS = true
#When passing variadic parameters via ...(N) it can happen that
#incompatible types are passed to functions. This enables several
#warnings when static typechecking cannot guarantee consistent
#behavior.
UNSAFE_TYPES = true
#When compiling original id1 QC there is a definition for `break`
#which conflicts with the 'break' keyword in GMQCC. Enabling this
#print a warning when the definition occurs. The definition is
#ignored for both cases.
BREAKDEF = true
#When compiling original QuakeWorld QC there are instances where
#code overwrites constants. This is considered an error, however
#for QuakeWorld to compile it needs to be treated as a warning
#instead, as such this warning only works when -std=qcc.
CONST_OVERWRITE = true
#Warn about the use of preprocessor directives inside macros.
DIRECTIVE_INMACRO = true
#When using a function that is not explicitly defined, the compiler
#will search its intrinsics table for something that matches that
#function name by appending "__builtin_" to it. This behaviour may
#be unexpected, so enabling this will produce a diagnostic when
#such a function is resolved to a builtin.
BUILTINS = true
#When comparing an inexact value such as `1.0/3.0' the result is
#pathologically wrong. Enabling this will trigger a compiler warning
#on such expressions.
INEXACT_COMPARES = true
# Warn when a function is marked with the attribute
# "[[deprecated]]". This flag enables a warning on calls to functions
# marked as such.
DEPRECATED = true
# Warn about possible problems from missing parenthesis, like an
# assignment used as truth value without additional parens around.
PARENTHESIS = true
# Finally these are all the optimizations, usually present via the -O
# prefix from the command line.
[optimizations]
#Some general peephole optimizations. For instance the code `a = b
#+ c` typically generates 2 instructions, an ADD and a STORE. This
#optimization removes the STORE and lets the ADD write directly
#into A.
# Enables peephole optimizations.
PEEPHOLE = true
PEEPHOLE = true
# Enables localtemp omission optimizations.
LOCALTEMPS = true
# Enables tail recrusion optimizationd.
TAIL_RECURSION = true
#Tail recursive function calls will be turned into loops to avoid
#the overhead of the CALL and RETURN instructions.
# Enables tail-call optimizations. (Not implemented)
TAIL_CALLS = true
TAIL_RECURSION = true
# Every function where it is safe to do so will share its local data section
# with the others. The criteria are currently that the function must not have
# any possibly uninitialized locals, or local arrays regardless of how they
# are initialized.
OVERLAP_LOCALS = false
# Strip out the names of constants to save some space in the progs.dat
STRIP_CONSTANT_NAMES = true
#Make all functions which use neither local arrays nor have locals
#which are seen as possibly uninitialized use the same local sec
#tion. This should be pretty safe compared to other compilers
#which do not check for uninitialized values properly. The problem
#is that there's QC code out there which really doesn't initialize
#some values. This is fine as long as this kind of optimization
#isn't used, but also, only as long as the functions cannot be
#called in a recursive manner. Since it's hard to know whether or
#not an array is actually fully initialized, especially when ini
#tializing it via a loop, we assume functions with arrays to be
#too dangerous for this optimization.
# Aggressivly reuse strings in the string-section
OVERLAP_STRINGS = true
OVERLAP_LOCALS = true
# Have expressions which are used as function parameters evaluate directly
# into the parameter-globals if possible.
# This avoids a whole lot of copying.
CALL_STORES = true
# Do not create a RETURN instruction at the end functions of return-type void.
VOID_RETURN = true
#This promotes locally declared variables to "temps". Meaning when
#a temporary result of an operation has to be stored somewhere, a
#local variable which is not 'alive' at that point can be used to
#keep the result. This can reduce the size of the global section.
#This will not have declared variables overlap, even if it was
#possible.
LOCAL_TEMPS = true
#Causes temporary values which do not need to be backed up on a
#CALL to not be stored in the function's locals-area. With this, a
#CALL to a function may need to back up fewer values and thus exe
#cute faster.
GLOBAL_TEMPS = true
#Don't generate defs for immediate values or even declared con
#stants. Meaning variables which are implicitly constant or qual
#ified as such using the 'const' keyword.
STRIP_CONSTANT_NAMES = true
#Aggressively reuse strings in the string section. When a string
#should be added which is the trailing substring of an already
#existing string, the existing string's tail will be returned
#instead of the new string being added.
#
#For example the following code will only generate 1 string:
#
# print("Hello you!\n");
# print("you!\n"); // trailing substring of "Hello you!\n"
#
#There's however one limitation. Strings are still processed in
#order, so if the above print statements were reversed, this opti
#mization would not happen.
OVERLAP_STRINGS = true
#By default, all parameters of a CALL are copied into the parame
#ter-globals right before the CALL instructions. This is the easi
#est and safest way to translate calls, but also adds a lot of
#unnecessary copying and unnecessary temporary values. This opti
#mization makes operations which are used as a parameter evaluate
#directly into the parameter-global if that is possible, which is
#when there's no other CALL instruction in between.
CALL_STORES = true
#Usually an empty RETURN instruction is added to the end of a void
#typed function. However, additionally after every function a DONE
#instruction is added for several reasons. (For example the qcvm's
#disassemble switch uses it to know when the function ends.). This
#optimization replaces that last RETURN with DONE rather than
#adding the DONE additionally.
VOID_RETURN = true
#Because traditional QC code doesn't allow you to access individ
#ual vector components of a computed vector without storing it in
#a local first, sometimes people multiply it by a constant like
#'0 1 0' to get, in this case, the y component of a vector. This
#optimization will turn such a multiplication into a direct compo
#nent access. If the factor is anything other than 1, a float-mul
#tiplication will be added, which is still faster than a vector
#multiplication.
VECTOR_COMPONENTS = true
#For constant expressions that result in dead code (such as a
#branch whos condition can be evaluated at compile-time), this
#will eliminate the branch and else body (if present) to produce
#more optimal code.
CONST_FOLD_DCE = true
#For constant expressions we can fold them to immediate values.
#this option cannot be disabled or enabled, the compiler forces
#it to stay enabled by ignoring the value entierly. There are
#plans to enable some level of constant fold disabling, but right
#now the language can't function without it. This is merley here
#as an exercise to the reader.
CONST_FOLD = true
# Turn extraction-multiplications such as (a_vector * '0 1 0')
# into direct component accesses
VECTOR_COMPONENTS = true

2048
intrin.cpp

File diff suppressed because it is too large Load diff

478
intrin.h
View file

@ -1,74 +1,418 @@
#ifndef GMQCC_INTRIN_HDR
#define GMQCC_INTRIN_HDR
#include "gmqcc.h"
/*
* 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.
*/
struct fold;
struct parser_t;
/*
* 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;
struct ast_function;
struct ast_expression;
struct ast_value;
ht intrin_intrinsics() {
static ht intrinsics = NULL;
if (!intrinsics)
intrinsics = util_htnew(PARSER_HT_SIZE);
struct intrin;
return intrinsics;
}
struct intrin_func_t {
ast_expression *(intrin::*function)();
const char *name;
const char *alias;
size_t args;
#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"}
};
struct intrin {
intrin() = default;
intrin(parser_t *parser);
ast_expression *debug_typestring();
ast_expression *do_fold(ast_value *val, ast_expression **exprs);
ast_expression *func_try(size_t offset, const char *compare);
ast_expression *func_self(const char *name, const char *from);
ast_expression *func(const char *name);
protected:
lex_ctx_t ctx() const;
ast_function *value(ast_value **out, const char *name, qc_type vtype);
void reg(ast_value *const value, ast_function *const func);
ast_expression *nullfunc();
ast_expression *isfinite_();
ast_expression *isinf_();
ast_expression *isnan_();
ast_expression *isnormal_();
ast_expression *signbit_();
ast_expression *acosh_();
ast_expression *asinh_();
ast_expression *atanh_();
ast_expression *exp_();
ast_expression *exp2_();
ast_expression *expm1_();
ast_expression *pow_();
ast_expression *mod_();
ast_expression *fabs_();
ast_expression *epsilon_();
ast_expression *nan_();
ast_expression *inf_();
ast_expression *ln_();
ast_expression *log_variant(const char *name, float base);
ast_expression *log_();
ast_expression *log10_();
ast_expression *log2_();
ast_expression *logb_();
ast_expression *shift_variant(const char *name, size_t instr);
ast_expression *lshift();
ast_expression *rshift();
void error(const char *fmt, ...);
private:
parser_t *m_parser;
fold *m_fold;
std::vector<intrin_func_t> m_intrinsics;
std::vector<ast_expression*> m_generated;
};
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 (ast_expression*)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;
}

3980
ir.c Normal file

File diff suppressed because it is too large Load diff

4094
ir.cpp

File diff suppressed because it is too large Load diff

517
ir.h
View file

@ -1,334 +1,365 @@
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
*
* 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.
*/
#ifndef GMQCC_IR_HDR
#define GMQCC_IR_HDR
#include "gmqcc.h"
/* ir_value */
/*
* Type large enough to hold all the possible IR flags. This should be
* changed if the static assertion at the end of this file fails.
*/
typedef uint8_t ir_flag_t;
struct ir_value;
struct ir_instr;
struct ir_block;
struct ir_function;
struct ir_builder;
struct ir_life_entry_t {
typedef struct
{
/* both inclusive */
size_t start;
size_t end;
};
} ir_life_entry_t;
enum {
IR_FLAG_HAS_ARRAYS = 1 << 0,
IR_FLAG_HAS_UNINITIALIZED = 1 << 1,
IR_FLAG_HAS_GOTO = 1 << 2,
IR_FLAG_INCLUDE_DEF = 1 << 3,
IR_FLAG_ERASABLE = 1 << 4,
IR_FLAG_BLOCK_COVERAGE = 1 << 5,
IR_FLAG_NOREF = 1 << 6,
IR_FLAG_SPLIT_VECTOR = 1 << 7,
struct ir_function_s;
typedef struct ir_value_s {
char *name;
int vtype;
int store;
lex_ctx context;
/* even the IR knows the subtype of a field */
int fieldtype;
/* and the output type of a function */
int outtype;
/* 'const' vs 'var' qualifier */
int cvq;
uint32_t flags;
IR_FLAG_LAST,
IR_FLAG_MASK_NO_OVERLAP = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED),
IR_FLAG_MASK_NO_LOCAL_TEMPS = (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
};
struct ir_instr_s **reads;
struct ir_instr_s **writes;
struct ir_value {
ir_value(std::string&& name, store_type storetype, qc_type vtype);
ir_value(ir_function *owner, std::string&& name, store_type storetype, qc_type vtype);
~ir_value();
ir_value *vectorMember(unsigned int member);
bool GMQCC_WARN setFloat(float);
bool GMQCC_WARN setFunc(int);
bool GMQCC_WARN setString(const char*);
bool GMQCC_WARN setVector(vec3_t);
bool GMQCC_WARN setField(ir_value*);
#if 0
bool GMQCC_WARN setInt(int);
#endif
bool lives(size_t at);
void dumpLife(int (*oprintf)(const char*, ...)) const;
void setCodeAddress(int32_t gaddr);
int32_t codeAddress() const;
bool insertLife(size_t idx, ir_life_entry_t);
bool setAlive(size_t position);
bool mergeLife(const ir_value *other);
std::string m_name;
qc_type m_vtype;
store_type m_store;
lex_ctx_t m_context;
qc_type m_fieldtype; // even the IR knows the subtype of a field
qc_type m_outtype; // and the output type of a function
int m_cvq; // 'const' vs 'var' qualifier
ir_flag_t m_flags;
std::vector<ir_instr *> m_reads;
std::vector<ir_instr *> m_writes;
// constant values
bool m_hasvalue;
/* constantvalues */
bool hasvalue;
union {
qcfloat_t vfloat;
int vint;
vec3_t vvec;
int32_t ivec[3];
char *vstring;
ir_value *vpointer;
ir_function *vfunc;
} m_constval;
float vfloat;
int vint;
vector vvec;
int32_t ivec[3];
char *vstring;
struct ir_value_s *vpointer;
struct ir_function_s *vfunc;
} constval;
struct {
int32_t globaladdr;
int32_t name;
int32_t local; // filled by the local-allocator
int32_t addroffset; // added for members
int32_t fieldaddr; // to generate field-addresses early
} m_code;
/* filled by the local-allocator */
int32_t local;
/* added for members */
int32_t addroffset;
/* to generate field-addresses early */
int32_t fieldaddr;
} code;
// for accessing vectors
ir_value *m_members[3];
ir_value *m_memberof;
/* for acessing vectors */
struct ir_value_s *members[3];
struct ir_value_s *memberof;
bool m_unique_life; // arrays will never overlap with temps
bool m_locked; // temps living during a CALL must be locked
bool m_callparam;
/* arrays will never overlap with temps */
bool unique_life;
/* temps living during a CALL must be locked */
bool locked;
bool callparam;
std::vector<ir_life_entry_t> m_life; // For the temp allocator
/* For the temp allocator */
ir_life_entry_t *life;
} ir_value;
size_t size() const;
int32_t ir_value_code_addr(const ir_value*);
void dump(int (*oprintf)(const char*, ...)) const;
};
/* ir_value can be a variable, or created by an operation */
ir_value* ir_value_var(const char *name, int st, int vtype);
/* if a result of an operation: the function should store
* it to remember to delete it / garbage collect it
*/
ir_value* ir_value_out(struct ir_function_s *owner, const char *name, int st, int vtype);
void ir_value_delete(ir_value*);
bool ir_value_set_name(ir_value*, const char *name);
ir_value* ir_value_vector_member(ir_value*, unsigned int member);
bool GMQCC_WARN vec_ir_value_find(ir_value **vec, const ir_value *what, size_t *idx);
bool GMQCC_WARN ir_value_set_float(ir_value*, float f);
bool GMQCC_WARN ir_value_set_func(ir_value*, int f);
#if 0
bool GMQCC_WARN ir_value_set_int(ir_value*, int i);
#endif
bool GMQCC_WARN ir_value_set_string(ir_value*, const char *s);
bool GMQCC_WARN ir_value_set_vector(ir_value*, vector v);
bool GMQCC_WARN ir_value_set_field(ir_value*, ir_value *fld);
/*bool ir_value_set_pointer_v(ir_value*, ir_value* p); */
/*bool ir_value_set_pointer_i(ir_value*, int i); */
/* merge an instruction into the life-range */
/* returns false if the lifepoint was already known */
bool ir_value_life_merge(ir_value*, size_t);
bool ir_value_life_merge_into(ir_value*, const ir_value*);
/* check if a value lives at a specific point */
bool ir_value_lives(ir_value*, size_t);
/* check if the life-range of 2 values overlaps */
bool ir_values_overlap(const ir_value*, const ir_value*);
void ir_value_dump(ir_value*, int (*oprintf)(const char*,...));
void ir_value_dump_life(const ir_value *self, int (*oprintf)(const char*,...));
/* PHI data */
struct ir_phi_entry_t {
ir_value *value;
ir_block *from;
};
typedef struct ir_phi_entry_s
{
ir_value *value;
struct ir_block_s *from;
} ir_phi_entry_t;
/* instruction */
struct ir_instr {
ir_instr(lex_ctx_t, ir_block *owner, int opcode);
~ir_instr();
typedef struct ir_instr_s
{
int opcode;
lex_ctx context;
ir_value* (_ops[3]);
struct ir_block_s* (bops[2]);
int m_opcode;
lex_ctx_t m_context;
ir_value *(_m_ops[3]) = { nullptr, nullptr, nullptr };
ir_block *(m_bops[2]) = { nullptr, nullptr };
std::vector<ir_phi_entry_t> m_phi;
std::vector<ir_value *> m_params;
// For the temp-allocation
size_t m_eid = 0;
// For IFs
bool m_likely = true;
ir_block *m_owner;
};
/* block */
struct ir_block {
ir_block(ir_function *owner, const std::string& name);
~ir_block();
ir_function *m_owner;
std::string m_label;
lex_ctx_t m_context;
bool m_final = false; /* once a jump is added we're done */
std::vector<ir_instr *> m_instr;
std::vector<ir_block *> m_entries;
std::vector<ir_block *> m_exits;
std::vector<ir_value *> m_living;
ir_phi_entry_t *phi;
ir_value **params;
/* For the temp-allocation */
size_t m_entry_id = 0;
size_t m_eid = 0;
bool m_is_return = false;
size_t eid;
bool m_generated = false;
size_t m_code_start = 0;
};
/* For IFs */
bool likely;
ir_value* ir_block_create_binop(ir_block*, lex_ctx_t, const char *label, int op, ir_value *left, ir_value *right);
ir_value* ir_block_create_unary(ir_block*, lex_ctx_t, const char *label, int op, ir_value *operand);
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx_t, int op, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx_t, ir_value *target, ir_value *what);
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx_t, const char *label, ir_value *ent, ir_value *field, qc_type outype);
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx_t, const char *label, ir_value *entity, ir_value *field);
bool GMQCC_WARN ir_block_create_state_op(ir_block*, lex_ctx_t, ir_value *frame, ir_value *think);
struct ir_block_s *owner;
} ir_instr;
ir_instr* ir_instr_new(lex_ctx ctx, struct ir_block_s *owner, int opcode);
void ir_instr_delete(ir_instr*);
bool GMQCC_WARN vec_ir_instr_find(ir_instr **vec, ir_instr *what, size_t *idx);
bool ir_instr_op(ir_instr*, int op, ir_value *value, bool writing);
void ir_instr_dump(ir_instr* in, char *ind, int (*oprintf)(const char*,...));
/* block */
typedef struct ir_block_s
{
char *label;
lex_ctx context;
bool final; /* once a jump is added we're done */
ir_instr **instr;
struct ir_block_s **entries;
struct ir_block_s **exits;
ir_value **living;
/* For the temp-allocation */
size_t entry_id;
size_t eid;
bool is_return;
struct ir_function_s *owner;
bool generated;
size_t code_start;
} ir_block;
ir_block* ir_block_new(struct ir_function_s *owner, const char *label);
void ir_block_delete(ir_block*);
bool ir_block_set_label(ir_block*, const char *label);
ir_value* ir_block_create_binop(ir_block*, lex_ctx, const char *label, int op,
ir_value *left, ir_value *right);
ir_value* ir_block_create_unary(ir_block*, lex_ctx, const char *label, int op,
ir_value *operand);
bool GMQCC_WARN ir_block_create_store_op(ir_block*, lex_ctx, int op, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_store(ir_block*, lex_ctx, ir_value *target, ir_value *what);
bool GMQCC_WARN ir_block_create_storep(ir_block*, lex_ctx, ir_value *target, ir_value *what);
/* field must be of TYPE_FIELD */
ir_value* ir_block_create_load_from_ent(ir_block*, lex_ctx, const char *label, ir_value *ent, ir_value *field, int outype);
ir_value* ir_block_create_fieldaddress(ir_block*, lex_ctx, const char *label, ir_value *entity, ir_value *field);
/* This is to create an instruction of the form
* <outtype>%label := opcode a, b
*/
ir_instr* ir_block_create_phi(ir_block*, lex_ctx_t, const char *label, qc_type vtype);
ir_value* ir_block_create_general_instr(ir_block *self, lex_ctx, const char *label,
int op, ir_value *a, ir_value *b, int outype);
ir_instr* ir_block_create_phi(ir_block*, lex_ctx, const char *label, int vtype);
ir_value* ir_phi_value(ir_instr*);
void ir_phi_add(ir_instr*, ir_block *b, ir_value *v);
ir_instr* ir_block_create_call(ir_block*, lex_ctx_t, const char *label, ir_value *func, bool noreturn);
ir_instr* ir_block_create_call(ir_block*, lex_ctx, const char *label, ir_value *func, bool noreturn);
ir_value* ir_call_value(ir_instr*);
void ir_call_param(ir_instr*, ir_value*);
bool GMQCC_WARN ir_block_create_return(ir_block*, lex_ctx_t, ir_value *opt_value);
bool GMQCC_WARN ir_block_create_return(ir_block*, lex_ctx, ir_value *opt_value);
bool GMQCC_WARN ir_block_create_if(ir_block*, lex_ctx_t, ir_value *cond,
bool GMQCC_WARN ir_block_create_if(ir_block*, lex_ctx, ir_value *cond,
ir_block *ontrue, ir_block *onfalse);
/*
* A 'goto' is an actual 'goto' coded in QC, whereas
/* A 'goto' is an actual 'goto' coded in QC, whereas
* a 'jump' is a virtual construct which simply names the
* next block to go to.
* A goto usually becomes an OP_GOTO in the resulting code,
* whereas a 'jump' usually doesn't add any actual instruction.
*/
bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx_t, ir_block *to);
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx_t, ir_block *to);
bool GMQCC_WARN ir_block_create_jump(ir_block*, lex_ctx, ir_block *to);
bool GMQCC_WARN ir_block_create_goto(ir_block*, lex_ctx, ir_block *to);
void ir_block_dump(ir_block*, char *ind, int (*oprintf)(const char*,...));
/* function */
struct ir_function {
ir_function(ir_builder *owner, qc_type returntype);
~ir_function();
ir_builder *m_owner;
typedef struct ir_function_s
{
char *name;
int outtype;
int *params;
ir_block **blocks;
std::string m_name;
qc_type m_outtype;
std::vector<int> m_params;
ir_flag_t m_flags = 0;
int m_builtin = 0;
uint32_t flags;
std::vector<std::unique_ptr<ir_block>> m_blocks;
int builtin;
/*
* values generated from operations
ir_value *value;
/* values generated from operations
* which might get optimized away, so anything
* in there needs to be deleted in the dtor.
*/
std::vector<std::unique_ptr<ir_value>> m_values;
std::vector<std::unique_ptr<ir_value>> m_locals; /* locally defined variables */
ir_value *m_value = nullptr;
ir_value **values;
size_t m_allocated_locals = 0;
size_t m_globaltemps = 0;
/* locally defined variables */
ir_value **locals;
ir_block* m_first = nullptr;
ir_block* m_last = nullptr;
size_t allocated_locals;
size_t globaltemps;
lex_ctx_t m_context;
ir_block* first;
ir_block* last;
/*
* for prototypes - first we generate all the
lex_ctx context;
/* for prototypes - first we generate all the
* globals, and we remember teh function-defs
* so we can later fill in the entry pos
*
* remember the ID:
*/
qcint_t m_code_function_def = -1;
qcint code_function_def;
/* for temp allocation */
size_t m_run_id = 0;
size_t run_id;
struct ir_builder_s *owner;
/* vararg support: */
size_t m_max_varargs = 0;
};
size_t max_varargs;
} ir_function;
#define IR_FLAG_HAS_ARRAYS (1<<1)
#define IR_FLAG_HAS_UNINITIALIZED (1<<2)
#define IR_FLAG_HAS_GOTO (1<<3)
#define IR_FLAG_INCLUDE_DEF (1<<4)
#define IR_FLAG_MASK_NO_OVERLAP (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
#define IR_FLAG_MASK_NO_LOCAL_TEMPS (IR_FLAG_HAS_ARRAYS | IR_FLAG_HAS_UNINITIALIZED)
ir_function* ir_function_new(struct ir_builder_s *owner, int returntype);
void ir_function_delete(ir_function*);
void ir_function_collect_value(ir_function*, ir_value *value);
bool ir_function_set_name(ir_function*, const char *name);
ir_value* ir_function_create_local(ir_function *self, const char *name, int vtype, bool param);
ir_value* ir_function_create_local(ir_function *self, const std::string& name, qc_type vtype, bool param);
bool GMQCC_WARN ir_function_finalize(ir_function*);
ir_block* ir_function_create_block(lex_ctx_t ctx, ir_function*, const char *label);
/*
bool ir_function_naive_phi(ir_function*);
bool ir_function_enumerate(ir_function*);
bool ir_function_calculate_liferanges(ir_function*);
*/
ir_block* ir_function_create_block(lex_ctx ctx, ir_function*, const char *label);
void ir_function_dump(ir_function*, char *ind, int (*oprintf)(const char*,...));
/* builder */
#define IR_HT_SIZE 1024
#define IR_MAX_VINSTR_TEMPS 2
#define IR_HT_SIZE 1024
typedef struct ir_builder_s
{
char *name;
ir_function **functions;
ir_value **globals;
ir_value **fields;
struct ir_builder {
ir_builder(const std::string& modulename);
~ir_builder();
ht htfunctions;
ht htglobals;
ht htfields;
ir_function *createFunction(const std::string &name, qc_type outtype);
ir_value *createGlobal(const std::string &name, qc_type vtype);
ir_value *createField(const std::string &name, qc_type vtype);
ir_value *get_va_count();
bool generate(const char *filename);
void dump(int (*oprintf)(const char*, ...)) const;
ir_value **extparams;
ir_value **extparam_protos;
ir_value *generateExtparamProto();
void generateExtparam();
/* the highest func->allocated_locals */
size_t max_locals;
size_t max_globaltemps;
uint32_t first_common_local;
uint32_t first_common_globaltemp;
ir_value *literalFloat(float value, bool add_to_list);
const char **filenames;
qcint *filestrings;
/* we cache the #IMMEDIATE string here */
qcint str_immediate;
/* there should just be this one nil */
ir_value *nil;
ir_value *reserved_va_count;
} ir_builder;
std::string m_name;
std::vector<std::unique_ptr<ir_function>> m_functions;
std::vector<std::unique_ptr<ir_value>> m_globals;
std::vector<std::unique_ptr<ir_value>> m_fields;
// for reusing them in vector-splits, TODO: sort this or use a radix-tree
std::vector<ir_value*> m_const_floats;
ir_builder* ir_builder_new(const char *modulename);
void ir_builder_delete(ir_builder*);
ht m_htfunctions;
ht m_htglobals;
ht m_htfields;
bool ir_builder_set_name(ir_builder *self, const char *name);
// extparams' ir_values reference the ones from extparam_protos
std::vector<std::unique_ptr<ir_value>> m_extparam_protos;
std::vector<ir_value*> m_extparams;
ir_function* ir_builder_get_function(ir_builder*, const char *fun);
ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype);
// the highest func->allocated_locals
size_t m_max_locals = 0;
size_t m_max_globaltemps = 0;
uint32_t m_first_common_local = 0;
uint32_t m_first_common_globaltemp = 0;
ir_value* ir_builder_get_global(ir_builder*, const char *fun);
ir_value* ir_builder_create_global(ir_builder*, const char *name, int vtype);
ir_value* ir_builder_get_field(ir_builder*, const char *fun);
ir_value* ir_builder_create_field(ir_builder*, const char *name, int vtype);
std::vector<const char*> m_filenames;
std::vector<qcint_t> m_filestrings;
ir_value* ir_builder_get_va_count(ir_builder*);
// we cache the #IMMEDIATE string here
qcint_t m_str_immediate = 0;
bool ir_builder_generate(code_t *, ir_builder *self, const char *filename);
// there should just be this one nil
ir_value *m_nil;
ir_value *m_reserved_va_count = nullptr;
ir_value *m_coverage_func = nullptr;
void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
/* some virtual instructions require temps, and their code is isolated
* so that we don't need to keep track of their liveness.
*/
ir_value *m_vinstr_temp[IR_MAX_VINSTR_TEMPS];
/* code generator */
std::unique_ptr<code_t> m_code;
private:
qcint_t filestring(const char *filename);
bool generateGlobal(ir_value*, bool is_local);
bool generateGlobalFunction(ir_value*);
bool generateGlobalFunctionCode(ir_value*);
bool generateFunctionLocals(ir_value*);
};
/*
* This code assumes 32 bit floats while generating binary
* Blub: don't use extern here, it's annoying and shows up in nm
* for some reason :P
* for some reason :P
*/
typedef int static_assert_is_32bit_float [(sizeof(int32_t) == 4) ? 1 : -1];
typedef int static_assert_is_32bit_integer[(sizeof(qcfloat_t) == 4) ? 1 : -1];
/*
* If the condition creates a situation where this becomes -1 size it means there are
* more IR_FLAGs than the type ir_flag_t is capable of holding. So either eliminate
* the IR flag count or change the ir_flag_t typedef to a type large enough to accomodate
* all the flags.
*/
typedef int static_assert_is_ir_flag_safe [((IR_FLAG_LAST) <= (ir_flag_t)(-1)) ? 1 : -1];
typedef int static_assert_is_32bit_float [(sizeof(int32_t) == 4)?1:-1];
typedef int static_assert_is_32bit_integer[(sizeof(qcfloat) == 4)?1:-1];
#endif

View file

@ -1,5 +1,29 @@
#include <string.h>
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include "gmqcc.h"
#include "lexer.h"
@ -16,6 +40,8 @@ static const char *keywords_qc[] = {
"return",
"const"
};
static size_t num_keywords_qc = sizeof(keywords_qc) / sizeof(keywords_qc[0]);
/* For fte/gmgqcc */
static const char *keywords_fg[] = {
"switch", "case", "default",
@ -26,33 +52,34 @@ static const char *keywords_fg[] = {
"__builtin_debug_printtype"
};
static size_t num_keywords_fg = sizeof(keywords_fg) / sizeof(keywords_fg[0]);
/*
* Lexer code
*/
static char* *lex_filenames;
static void lexerror(lex_file *lex, const char *fmt, ...)
void lexerror(lex_file *lex, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
if (lex)
con_vprintmsg(LVL_ERROR, lex->name, lex->sline, lex->column, "parse error", fmt, ap);
con_vprintmsg(LVL_ERROR, lex->name, lex->sline, "parse error", fmt, ap);
else
con_vprintmsg(LVL_ERROR, "", 0, 0, "parse error", fmt, ap);
con_vprintmsg(LVL_ERROR, "", 0, "parse error", fmt, ap);
va_end(ap);
}
static bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
{
bool r;
lex_ctx_t ctx;
va_list ap;
bool r;
lex_ctx ctx;
va_list ap;
ctx.file = lex->name;
ctx.line = lex->sline;
ctx.column = lex->column;
ctx.file = lex->name;
ctx.line = lex->sline;
va_start(ap, fmt);
r = vcompile_warning(ctx, warntype, fmt, ap);
@ -60,59 +87,125 @@ static bool lexwarn(lex_file *lex, int warntype, const char *fmt, ...)
return r;
}
static void lex_token_new(lex_file *lex)
{
if (lex->tok.value)
vec_shrinkto(lex->tok.value, 0);
lex->tok.constval.t = TYPE_VOID;
lex->tok.ctx.line = lex->sline;
lex->tok.ctx.file = lex->name;
lex->tok.ctx.column = lex->column;
#if 0
token* token_new()
{
token *tok = (token*)mem_a(sizeof(token));
if (!tok)
return NULL;
memset(tok, 0, sizeof(*tok));
return tok;
}
static void lex_ungetch(lex_file *lex, int ch);
static int lex_getch(lex_file *lex);
void token_delete(token *self)
{
if (self->next && self->next->prev == self)
self->next->prev = self->prev;
if (self->prev && self->prev->next == self)
self->prev->next = self->next;
MEM_VECTOR_CLEAR(self, value);
mem_d(self);
}
token* token_copy(const token *cp)
{
token* self = token_new();
if (!self)
return NULL;
/* copy the value */
self->value_alloc = cp->value_count + 1;
self->value_count = cp->value_count;
self->value = (char*)mem_a(self->value_alloc);
if (!self->value) {
mem_d(self);
return NULL;
}
memcpy(self->value, cp->value, cp->value_count);
self->value[self->value_alloc-1] = 0;
/* rest */
self->ctx = cp->ctx;
self->ttype = cp->ttype;
memcpy(&self->constval, &cp->constval, sizeof(self->constval));
return self;
}
void token_delete_all(token *t)
{
token *n;
do {
n = t->next;
token_delete(t);
t = n;
} while(t);
}
token* token_copy_all(const token *cp)
{
token *cur;
token *out;
out = cur = token_copy(cp);
if (!out)
return NULL;
while (cp->next) {
cp = cp->next;
cur->next = token_copy(cp);
if (!cur->next) {
token_delete_all(out);
return NULL;
}
cur->next->prev = cur;
cur = cur->next;
}
return out;
}
#else
static void lex_token_new(lex_file *lex)
{
#if 0
if (lex->tok)
token_delete(lex->tok);
lex->tok = token_new();
#else
if (lex->tok.value)
vec_shrinkto(lex->tok.value, 0);
lex->tok.constval.t = 0;
lex->tok.ctx.line = lex->sline;
lex->tok.ctx.file = lex->name;
#endif
}
#endif
lex_file* lex_open(const char *file)
{
lex_file *lex;
FILE *in = fopen(file, "rb");
uint32_t read;
lex_file *lex;
FILE *in = fs_file_open(file, "rb");
if (!in) {
lexerror(nullptr, "open failed: '%s'\n", file);
return nullptr;
lexerror(NULL, "open failed: '%s'\n", file);
return NULL;
}
lex = (lex_file*)mem_a(sizeof(*lex));
if (!lex) {
fclose(in);
lexerror(nullptr, "out of memory\n");
return nullptr;
fs_file_close(in);
lexerror(NULL, "out of memory\n");
return NULL;
}
memset(lex, 0, sizeof(*lex));
lex->file = in;
lex->name = util_strdup(file);
lex->line = 1; /* we start counting at 1 */
lex->column = 0;
lex->peekpos = 0;
lex->eof = false;
lex->file = in;
lex->name = util_strdup(file);
lex->line = 1; /* we start counting at 1 */
/* handle BOM */
if ((read = (lex_getch(lex) << 16) | (lex_getch(lex) << 8) | lex_getch(lex)) != 0xEFBBBF) {
lex_ungetch(lex, (read & 0x0000FF));
lex_ungetch(lex, (read & 0x00FF00) >> 8);
lex_ungetch(lex, (read & 0xFF0000) >> 16);
} else {
/*
* otherwise the lexer has advanced 3 bytes for the BOM, we need
* to set the column back to 0
*/
lex->column = 0;
}
lex->peekpos = 0;
lex->eof = false;
vec_push(lex_filenames, lex->name);
return lex;
@ -124,22 +217,22 @@ lex_file* lex_open_string(const char *str, size_t len, const char *name)
lex = (lex_file*)mem_a(sizeof(*lex));
if (!lex) {
lexerror(nullptr, "out of memory\n");
return nullptr;
lexerror(NULL, "out of memory\n");
return NULL;
}
memset(lex, 0, sizeof(*lex));
lex->file = nullptr;
lex->file = NULL;
lex->open_string = str;
lex->open_string_length = len;
lex->open_string_pos = 0;
lex->name = util_strdup(name ? name : "<string-source>");
lex->line = 1; /* we start counting at 1 */
lex->name = util_strdup(name ? name : "<string-source>");
lex->line = 1; /* we start counting at 1 */
lex->peekpos = 0;
lex->eof = false;
lex->column = 0;
lex->eof = false;
vec_push(lex_filenames, lex->name);
@ -165,26 +258,24 @@ void lex_close(lex_file *lex)
vec_free(lex->modelname);
if (lex->file)
fclose(lex->file);
fs_file_close(lex->file);
#if 0
if (lex->tok)
token_delete(lex->tok);
#else
vec_free(lex->tok.value);
#endif
/* mem_d(lex->name); collected in lex_filenames */
mem_d(lex);
}
static int lex_fgetc(lex_file *lex)
{
if (lex->file) {
lex->column++;
return fgetc(lex->file);
}
if (lex->file)
return fs_file_getc(lex->file);
if (lex->open_string) {
if (lex->open_string_pos >= lex->open_string_length)
return EOF;
lex->column++;
return lex->open_string[lex->open_string_pos++];
}
return EOF;
@ -195,26 +286,21 @@ static int lex_fgetc(lex_file *lex)
* are working on.
* The are merely wrapping get/put in order to count line numbers.
*/
static void lex_ungetch(lex_file *lex, int ch);
static int lex_try_trigraph(lex_file *lex, int old)
{
int c2, c3;
c2 = lex_fgetc(lex);
if (!lex->push_line && c2 == '\n') {
if (!lex->push_line && c2 == '\n')
lex->line++;
lex->column = 0;
}
if (c2 != '?') {
lex_ungetch(lex, c2);
return old;
}
c3 = lex_fgetc(lex);
if (!lex->push_line && c3 == '\n') {
if (!lex->push_line && c3 == '\n')
lex->line++;
lex->column = 0;
}
switch (c3) {
case '=': return '#';
case '/': return '\\';
@ -261,18 +347,14 @@ static int lex_getch(lex_file *lex)
if (lex->peekpos) {
lex->peekpos--;
if (!lex->push_line && lex->peek[lex->peekpos] == '\n') {
if (!lex->push_line && lex->peek[lex->peekpos] == '\n')
lex->line++;
lex->column = 0;
}
return lex->peek[lex->peekpos];
}
ch = lex_fgetc(lex);
if (!lex->push_line && ch == '\n') {
if (!lex->push_line && ch == '\n')
lex->line++;
lex->column = 0;
}
else if (ch == '?')
return lex_try_trigraph(lex, ch);
else if (!lex->flags.nodigraphs && (ch == '<' || ch == ':' || ch == '%'))
@ -283,11 +365,8 @@ static int lex_getch(lex_file *lex)
static void lex_ungetch(lex_file *lex, int ch)
{
lex->peek[lex->peekpos++] = ch;
lex->column--;
if (!lex->push_line && ch == '\n') {
if (!lex->push_line && ch == '\n')
lex->line--;
lex->column = 0;
}
}
/* classify characters
@ -297,12 +376,12 @@ static void lex_ungetch(lex_file *lex, int ch)
/* Idents are alphanumberic, but they start with alpha or _ */
static bool isident_start(int ch)
{
return util_isalpha(ch) || ch == '_';
return isalpha(ch) || ch == '_';
}
static bool isident(int ch, bool allow_dot)
static bool isident(int ch)
{
return isident_start(ch) || util_isdigit(ch) || (allow_dot && ch == '.');
return isident_start(ch) || isdigit(ch);
}
/* isxdigit_only is used when we already know it's not a digit
@ -329,9 +408,9 @@ static void lex_endtoken(lex_file *lex)
static bool lex_try_pragma(lex_file *lex)
{
int ch;
char *pragma = nullptr;
char *command = nullptr;
char *param = nullptr;
char *pragma = NULL;
char *command = NULL;
char *param = NULL;
size_t line;
if (lex->flags.preprocessing)
@ -392,12 +471,11 @@ static bool lex_try_pragma(lex_file *lex)
goto unroll;
}
else if (!strcmp(command, "file")) {
lex->framevalue = 0;
lex->name = util_strdup(param);
vec_push(lex_filenames, lex->name);
}
else if (!strcmp(command, "line")) {
line = strtol(param, nullptr, 0)-1;
line = strtol(param, NULL, 0)-1;
}
else
goto unroll;
@ -483,7 +561,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
do
{
ch = lex_getch(lex);
while (ch != EOF && util_isspace(ch)) {
while (ch != EOF && isspace(ch)) {
if (ch == '\n') {
if (lex_try_pragma(lex))
continue;
@ -515,6 +593,10 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
if (lex->flags.preprocessing) {
haswhite = true;
/*
lex_tokench(lex, '/');
lex_tokench(lex, '/');
*/
lex_tokench(lex, ' ');
lex_tokench(lex, ' ');
}
@ -536,6 +618,10 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
/* multiline comment */
if (lex->flags.preprocessing) {
haswhite = true;
/*
lex_tokench(lex, '/');
lex_tokench(lex, '*');
*/
lex_tokench(lex, ' ');
lex_tokench(lex, ' ');
}
@ -547,6 +633,10 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
ch = lex_getch(lex);
if (ch == '/') {
if (lex->flags.preprocessing) {
/*
lex_tokench(lex, '*');
lex_tokench(lex, '/');
*/
lex_tokench(lex, ' ');
lex_tokench(lex, ' ');
}
@ -558,7 +648,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
if (ch == '\n')
lex_tokench(lex, '\n');
else
lex_tokench(lex, ' ');
lex_tokench(lex, ' '); /* ch); */
}
}
ch = ' '; /* cause TRUE in the isspace check */
@ -569,7 +659,7 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
ch = '/';
break;
}
} while (ch != EOF && util_isspace(ch));
} while (ch != EOF && isspace(ch));
if (haswhite) {
lex_endtoken(lex);
@ -580,12 +670,12 @@ static int lex_skipwhite(lex_file *lex, bool hadwhite)
}
/* Get a token */
static bool GMQCC_WARN lex_finish_ident(lex_file *lex, bool allow_dot)
static bool GMQCC_WARN lex_finish_ident(lex_file *lex)
{
int ch;
ch = lex_getch(lex);
while (ch != EOF && isident(ch, allow_dot))
while (ch != EOF && isident(ch))
{
lex_tokench(lex, ch);
ch = lex_getch(lex);
@ -605,19 +695,19 @@ static int lex_parse_frame(lex_file *lex)
lex_token_new(lex);
ch = lex_getch(lex);
while (ch != EOF && ch != '\n' && util_isspace(ch))
while (ch != EOF && ch != '\n' && isspace(ch))
ch = lex_getch(lex);
if (ch == '\n')
return 1;
if (!isident_start(ch)) {
lexerror(lex, "invalid framename, must start with one of a-z, or _, got %c", ch);
lexerror(lex, "invalid framename, must start with one of a-z or _, got %c", ch);
return -1;
}
lex_tokench(lex, ch);
if (!lex_finish_ident(lex, true))
if (!lex_finish_ident(lex))
return -1;
lex_endtoken(lex);
return 0;
@ -659,11 +749,10 @@ static bool lex_finish_frames(lex_file *lex)
static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
{
utf8ch_t chr = 0;
int ch = 0, texttype = 0;
uchar_t chr;
int ch = 0;
int nextch;
bool hex;
bool oct;
char u8buf[8]; /* way more than enough */
int u8len, uc;
@ -695,12 +784,13 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
case '\\': break;
case '\'': break;
case '"': break;
case 'a': ch = '\a'; break;
case 'r': ch = '\r'; break;
case 'n': ch = '\n'; break;
case 't': ch = '\t'; break;
case 'f': ch = '\f'; break;
case 'v': ch = '\v'; break;
case 'a': ch = '\a'; break;
case 'b': ch = '\b'; break;
case 'r': ch = '\r'; break;
case 'n': ch = '\n'; break;
case 't': ch = '\t'; break;
case 'f': ch = '\f'; break;
case 'v': ch = '\v'; break;
case 'x':
case 'X':
/* same procedure as in fteqcc */
@ -748,18 +838,17 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
chr = 0;
nextch = lex_getch(lex);
hex = (nextch == 'x');
oct = (nextch == '0');
if (!hex && !oct)
if (!hex)
lex_ungetch(lex, nextch);
for (nextch = lex_getch(lex); nextch != '}'; nextch = lex_getch(lex)) {
if (!hex && !oct) {
if (!hex) {
if (nextch >= '0' && nextch <= '9')
chr = chr * 10 + nextch - '0';
else {
lexerror(lex, "bad character code");
return (lex->tok.ttype = TOKEN_ERROR);
}
} else if (!oct) {
} else {
if (nextch >= '0' && nextch <= '9')
chr = chr * 0x10 + nextch - '0';
else if (nextch >= 'a' && nextch <= 'f')
@ -770,13 +859,6 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
lexerror(lex, "bad character code");
return (lex->tok.ttype = TOKEN_ERROR);
}
} else {
if (nextch >= '0' && nextch <= '9')
chr = chr * 8 + chr - '0';
else {
lexerror(lex, "bad character code");
return (lex->tok.ttype = TOKEN_ERROR);
}
}
if (chr > 0x10FFFF || (!OPTS_FLAG(UTF8) && chr > 255))
{
@ -785,16 +867,14 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
}
}
if (OPTS_FLAG(UTF8) && chr >= 128) {
u8len = utf8_from(u8buf, chr);
u8len = u8_fromchar(chr, u8buf, sizeof(u8buf));
if (!u8len)
ch = 0;
else {
--u8len;
lex->column += u8len;
for (uc = 0; uc < u8len; ++uc)
lex_tokench(lex, u8buf[uc]);
/*
* the last character will be inserted with the tokench() call
/* the last character will be inserted with the tokench() call
* below the switch
*/
ch = u8buf[uc];
@ -803,15 +883,7 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
else
ch = chr;
break;
/* high bit text */
case 'b': case 's':
texttype ^= 128;
continue;
case '\n':
ch = '\n';
break;
case '\n': ch = '\n'; break;
default:
lexwarn(lex, WARN_UNKNOWN_CONTROL_SEQUENCE, "unrecognized control sequence: \\%c", ch);
@ -819,7 +891,7 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
lex_tokench(lex, '\\');
}
/* add the character finally */
lex_tokench(lex, ch | texttype);
lex_tokench(lex, ch);
}
else
lex_tokench(lex, ch);
@ -832,7 +904,6 @@ static int GMQCC_WARN lex_finish_string(lex_file *lex, int quote)
static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
{
bool ishex = false;
bool isoct = false;
int ch = lastch;
@ -845,16 +916,7 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
lex_tokench(lex, ch);
ch = lex_getch(lex);
if (lastch == '0' && util_isdigit(ch)) {
if (ch < '0' || ch > '7') {
lexerror(lex, "invalid octal constant");
return (lex->tok.ttype = TOKEN_ERROR);
}
isoct = true;
}
if (!isoct && ch != '.' && !util_isdigit(ch))
if (ch != '.' && !isdigit(ch))
{
if (lastch != '0' || ch != 'x')
{
@ -875,7 +937,7 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
{
lex_tokench(lex, ch);
ch = lex_getch(lex);
while (util_isdigit(ch) || (ishex && isxdigit_only(ch)))
while (isdigit(ch) || (ishex && isxdigit_only(ch)))
{
lex_tokench(lex, ch);
ch = lex_getch(lex);
@ -890,7 +952,7 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
/* continue digits-only */
ch = lex_getch(lex);
while (util_isdigit(ch))
while (isdigit(ch))
{
lex_tokench(lex, ch);
ch = lex_getch(lex);
@ -902,22 +964,17 @@ static int GMQCC_WARN lex_finish_digit(lex_file *lex, int lastch)
ch = lex_getch(lex);
/* generally we don't want words to follow numbers: */
if (isident(ch, false)) {
if (isident(ch)) {
lexerror(lex, "unexpected trailing characters after number");
return (lex->tok.ttype = TOKEN_ERROR);
}
lex_ungetch(lex, ch);
lex_endtoken(lex);
if (lex->tok.ttype == TOKEN_FLOATCONST) {
lex->tok.constval.f = strtod(lex->tok.value, nullptr);
} else {
/* determine base for strtol */
int base = 10;
if (ishex) base = 16;
if (isoct) base = 8;
lex->tok.constval.i = strtol(lex->tok.value, nullptr, base);
}
if (lex->tok.ttype == TOKEN_FLOATCONST)
lex->tok.constval.f = strtod(lex->tok.value, NULL);
else
lex->tok.constval.i = strtol(lex->tok.value, NULL, 0);
return lex->tok.ttype;
}
@ -927,6 +984,10 @@ int lex_do(lex_file *lex)
bool hadwhite = false;
lex_token_new(lex);
#if 0
if (!lex->tok)
return TOKEN_FATAL;
#endif
while (true) {
ch = lex_skipwhite(lex, hadwhite);
@ -973,7 +1034,7 @@ int lex_do(lex_file *lex)
return lex_do(lex);
}
lex_tokench(lex, ch);
if (!lex_finish_ident(lex, true))
if (!lex_finish_ident(lex))
return (lex->tok.ttype = TOKEN_ERROR);
lex_endtoken(lex);
/* skip the known commands */
@ -994,10 +1055,10 @@ int lex_do(lex_file *lex)
if (!strcmp(v, "framevalue"))
{
ch = lex_getch(lex);
while (ch != EOF && util_isspace(ch) && ch != '\n')
while (ch != EOF && isspace(ch) && ch != '\n')
ch = lex_getch(lex);
if (!util_isdigit(ch)) {
if (!isdigit(ch)) {
lexerror(lex, "$framevalue requires an integer parameter");
return lex_do(lex);
}
@ -1058,11 +1119,11 @@ int lex_do(lex_file *lex)
frame_macro m;
m.value = lex->framevalue;
m.name = lex->modelname;
lex->modelname = nullptr;
lex->modelname = NULL;
vec_push(lex->frames, m);
}
lex->modelname = lex->tok.value;
lex->tok.value = nullptr;
lex->tok.value = NULL;
return lex_do(lex);
}
@ -1155,7 +1216,7 @@ int lex_do(lex_file *lex)
if (ch == '.') {
nextch = lex_getch(lex);
/* digits starting with a dot */
if (util_isdigit(nextch)) {
if (isdigit(nextch)) {
lex_ungetch(lex, nextch);
lex->tok.ttype = lex_finish_digit(lex, ch);
lex_endtoken(lex);
@ -1171,6 +1232,10 @@ int lex_do(lex_file *lex)
*/
switch (ch)
{
/*
case '+':
case '-':
*/
case '*':
case '/':
case '<':
@ -1228,22 +1293,16 @@ int lex_do(lex_file *lex)
}
if (ch == '+' || ch == '-' || /* ++, --, +=, -= and -> as well! */
ch == '>' || ch == '<' || /* <<, >>, <=, >= and >< as well! */
ch == '>' || ch == '<' || /* <<, >>, <=, >= */
ch == '=' || ch == '!' || /* <=>, ==, != */
ch == '&' || ch == '|' || /* &&, ||, &=, |= */
ch == '~' || ch == '^' /* ~=, ~, ^ */
ch == '~' /* ~=, ~ */
) {
lex_tokench(lex, ch);
nextch = lex_getch(lex);
if ((nextch == '=' && ch != '<') || (nextch == '<' && ch == '>'))
nextch = lex_getch(lex);
if ((nextch == '=' && ch != '<') || (nextch == ch && ch != '!')) {
lex_tokench(lex, nextch);
else if (nextch == ch && ch != '!') {
lex_tokench(lex, nextch);
if ((thirdch = lex_getch(lex)) == '=')
lex_tokench(lex, thirdch);
else
lex_ungetch(lex, thirdch);
} else if (ch == '<' && nextch == '=') {
lex_tokench(lex, nextch);
if ((thirdch = lex_getch(lex)) == '>')
@ -1265,7 +1324,7 @@ int lex_do(lex_file *lex)
}
}
else if (lex->flags.preprocessing &&
ch == '-' && util_isdigit(nextch))
ch == '-' && isdigit(nextch))
{
lex->tok.ttype = lex_finish_digit(lex, nextch);
if (lex->tok.ttype == TOKEN_INTCONST)
@ -1282,6 +1341,15 @@ int lex_do(lex_file *lex)
return (lex->tok.ttype = TOKEN_OPERATOR);
}
/*
if (ch == '^' || ch == '~' || ch == '!')
{
lex_tokench(lex, ch);
lex_endtoken(lex);
return (lex->tok.ttype = TOKEN_OPERATOR);
}
*/
if (ch == '*' || ch == '/') /* *=, /= */
{
lex_tokench(lex, ch);
@ -1307,7 +1375,7 @@ int lex_do(lex_file *lex)
const char *v;
lex_tokench(lex, ch);
if (!lex_finish_ident(lex, false)) {
if (!lex_finish_ident(lex)) {
/* error? */
return (lex->tok.ttype = TOKEN_ERROR);
}
@ -1333,16 +1401,14 @@ int lex_do(lex_file *lex)
} else if (!strcmp(v, "vector")) {
lex->tok.ttype = TOKEN_TYPENAME;
lex->tok.constval.t = TYPE_VECTOR;
} else if (!strcmp(v, "_length")) {
lex->tok.ttype = TOKEN_OPERATOR;
} else {
size_t kw;
for (kw = 0; kw < GMQCC_ARRAY_COUNT(keywords_qc); ++kw) {
for (kw = 0; kw < num_keywords_qc; ++kw) {
if (!strcmp(v, keywords_qc[kw]))
return (lex->tok.ttype = TOKEN_KEYWORD);
}
if (OPTS_OPTION_U32(OPTION_STANDARD) != COMPILER_QCC) {
for (kw = 0; kw < GMQCC_ARRAY_COUNT(keywords_fg); ++kw) {
for (kw = 0; kw < num_keywords_fg; ++kw) {
if (!strcmp(v, keywords_fg[kw]))
return (lex->tok.ttype = TOKEN_KEYWORD);
}
@ -1391,10 +1457,14 @@ int lex_do(lex_file *lex)
lex_endtoken(lex);
lex->tok.ttype = TOKEN_CHARCONST;
/* It's a vector if we can successfully scan 3 floats */
if (util_sscanf(lex->tok.value, " %f %f %f ",
/* It's a vector if we can successfully scan 3 floats */
#ifdef _MSC_VER
if (sscanf_s(lex->tok.value, " %f %f %f ",
&lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
#else
if (sscanf(lex->tok.value, " %f %f %f ",
&lex->tok.constval.v.x, &lex->tok.constval.v.y, &lex->tok.constval.v.z) == 3)
#endif
{
lex->tok.ttype = TOKEN_VECTORCONST;
@ -1402,9 +1472,9 @@ int lex_do(lex_file *lex)
else
{
if (!lex->flags.preprocessing && strlen(lex->tok.value) > 1) {
utf8ch_t u8char;
uchar_t u8char;
/* check for a valid utf8 character */
if (!OPTS_FLAG(UTF8) || !utf8_to(&u8char, (const unsigned char *)lex->tok.value, 8)) {
if (!OPTS_FLAG(UTF8) || !u8_analyze(lex->tok.value, NULL, NULL, &u8char, 8)) {
if (lexwarn(lex, WARN_MULTIBYTE_CHARACTER,
( OPTS_FLAG(UTF8) ? "invalid multibyte character sequence `%s`"
: "multibyte character: `%s`" ),
@ -1421,7 +1491,7 @@ int lex_do(lex_file *lex)
return lex->tok.ttype;
}
if (util_isdigit(ch))
if (isdigit(ch))
{
lex->tok.ttype = lex_finish_digit(lex, ch);
lex_endtoken(lex);
@ -1434,6 +1504,6 @@ int lex_do(lex_file *lex)
return (lex->tok.ttype = ch);
}
lexerror(lex, "unknown token: `%c`", ch);
lexerror(lex, "unknown token: `%s`", lex->tok.value);
return (lex->tok.ttype = TOKEN_ERROR);
}

333
lexer.h
View file

@ -1,19 +1,58 @@
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
*
* 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.
*/
#ifndef GMQCC_LEXER_HDR
#define GMQCC_LEXER_HDR
#include "gmqcc.h"
struct token {
typedef struct token_s token;
struct token_s {
int ttype;
char *value;
union {
vec3_t v;
int i;
qcfloat_t f;
qc_type t; /* type */
vector v;
int i;
double f;
int t; /* type */
} constval;
lex_ctx_t ctx;
#if 0
struct token_s *next;
struct token_s *prev;
#endif
lex_ctx ctx;
};
#if 0
token* token_new();
void token_delete(token*);
token* token_copy(const token *cp);
void token_delete_all(token *t);
token* token_copy_all(const token *cp);
#endif
/* Lexer
*
*/
@ -61,13 +100,13 @@ enum {
TOKEN_FATAL /* internal error, eg out of memory */
};
struct frame_macro {
typedef struct {
char *name;
int value;
};
int value;
} frame_macro;
struct lex_file {
FILE *file;
typedef struct lex_file_s {
FILE *file;
const char *open_string;
size_t open_string_length;
size_t open_string_pos;
@ -75,7 +114,6 @@ struct lex_file {
char *name;
size_t line;
size_t sline; /* line at the start of a token */
size_t column;
int peek[256];
size_t peekpos;
@ -85,18 +123,18 @@ struct lex_file {
token tok; /* not a pointer anymore */
struct {
unsigned noops:1;
unsigned nodigraphs:1; /* used when lexing string constants */
unsigned preprocessing:1; /* whitespace and EOLs become actual tokens */
unsigned mergelines:1; /* backslash at the end of a line escapes the newline */
} flags; /* sizeof == 1 */
bool noops;
bool nodigraphs; /* used when lexing string constants */
bool preprocessing; /* whitespace and EOLs become actual tokens */
bool mergelines; /* backslash at the end of a line escapes the newline */
} flags;
int framevalue;
frame_macro *frames;
char *modelname;
size_t push_line;
};
} lex_file;
lex_file* lex_open (const char *file);
lex_file* lex_open_string(const char *str, size_t len, const char *name);
@ -116,188 +154,187 @@ enum {
#define OP_SUFFIX 1
#define OP_PREFIX 2
struct oper_info {
typedef struct {
const char *op;
unsigned int operands;
unsigned int id;
unsigned int assoc;
signed int prec;
unsigned int flags;
bool folds;
};
} oper_info;
/*
* Explicit uint8_t casts since the left operand of shift operator cannot
* be negative, even though it won't happen, this supresses the future
* possibility.
*/
#define opid1(a) ((uint8_t)a)
#define opid2(a,b) (((uint8_t)a<<8) |(uint8_t)b)
#define opid3(a,b,c) (((uint8_t)a<<16)|((uint8_t)b<<8)|(uint8_t)c)
#define opid1(a) (a)
#define opid2(a,b) ((a<<8)|b)
#define opid3(a,b,c) ((a<<16)|(b<<8)|c)
static const oper_info c_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
{ "_length", 1, opid3('l','e','n'), ASSOC_RIGHT, 98, OP_PREFIX, true},
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
{ "++", 1, opid3('S','+','+'), ASSOC_LEFT, 17, OP_SUFFIX, false},
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 17, OP_SUFFIX, false},
{ ".", 2, opid1('.'), ASSOC_LEFT, 17, 0, false},
{ "(", 0, opid1('('), ASSOC_LEFT, 17, 0, false}, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 17, 0, false}, /* 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, false},
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX },
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, OP_PREFIX },
{ "**", 2, opid2('*','*'), ASSOC_RIGHT, 14, 0, true},
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "~", 1, opid2('~','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX, false}, */
{ "**", 2, opid2('*', '*'), ASSOC_RIGHT, 15, 0 },
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 0, true},
{ "><", 2, opid2('>','<'), ASSOC_LEFT, 13, 0, true},
{ "!", 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 }, */
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 0 },
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
{ "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0, true},
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 },
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0 },
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0, true},
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0, true},
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
{ "<=>", 2, opid3('<','=','>'), ASSOC_LEFT, 10, 0 },
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0, true},
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0 },
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0 },
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0, true},
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0 },
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0, true},
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0 },
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0 },
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0, true},
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0, true},
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0 },
{ "=", 2, opid1('='), ASSOC_RIGHT, 2, 0, false},
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0, false},
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0, false},
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0, false},
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0, false},
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0, false},
{ ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0, false},
{ "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0, false},
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0, false},
{ "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0, false},
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0, false},
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0 },
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false},
{ "=", 2, opid1('='), ASSOC_RIGHT, 2, 0 },
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 2, 0 },
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 2, 0 },
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 2, 0 },
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 2, 0 },
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 2, 0 },
{ ">>=", 2, opid3('>','>','='), ASSOC_RIGHT, 2, 0 },
{ "<<=", 2, opid3('<','<','='), ASSOC_RIGHT, 2, 0 },
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 2, 0 },
{ "^=", 2, opid2('^','='), ASSOC_RIGHT, 2, 0 },
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 2, 0 },
{ "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 2, 0 },
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0, false}
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 },
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0 }
};
static const size_t c_operator_count = (sizeof(c_operators) / sizeof(c_operators[0]));
static const oper_info fte_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* 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, false},
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX, false},
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0, false},
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0, false}, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0, false}, /* array subscript */
{ "++", 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, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "!", 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 },
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0, true},
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0, true},
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0, true},
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0 },
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0 },
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0, true},
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 },
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0 },
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0, true},
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0, true},
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 },
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 },
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0, true},
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0 },
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0, false},
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0, false},
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0, false},
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0, false},
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0, false},
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0, false},
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0, false},
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0, false},
{ "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8, 0, false},
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 },
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 },
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 },
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0 },
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0 },
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0 },
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0 },
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0 },
{ "&~=", 2, opid3('&','~','='), ASSOC_RIGHT, 8, 0 },
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0, true},
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 },
/* Leave precedence 3 for : with -fcorrect-ternary */
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0, false},
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false}
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0 },
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 }
};
static const size_t fte_operator_count = (sizeof(fte_operators) / sizeof(fte_operators[0]));
static const oper_info qcc_operators[] = {
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX, false}, /* paren expression - non function call */
{ "(", 0, opid1('('), ASSOC_LEFT, 99, OP_PREFIX}, /* paren expression - non function call */
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0, false},
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0, false}, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0, false}, /* array subscript */
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */
{ "!", 1, opid2('!','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "+", 1, opid2('+','P'), ASSOC_RIGHT, 14, OP_PREFIX, false},
{ "-", 1, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX, true},
{ "!", 1, opid2('!', '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, true},
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0, true},
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0, true},
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0, true},
{ "*", 2, opid1('*'), ASSOC_LEFT, 13, 0 },
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
{ "&", 2, opid1('&'), ASSOC_LEFT, 13, 0 },
{ "|", 2, opid1('|'), ASSOC_LEFT, 13, 0 },
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0, false},
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0, false},
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0, false},
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0, false},
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0, true},
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0, true},
{ "<", 2, opid1('<'), ASSOC_LEFT, 10, 0 },
{ ">", 2, opid1('>'), ASSOC_LEFT, 10, 0 },
{ "<=", 2, opid2('<','='), ASSOC_LEFT, 10, 0 },
{ ">=", 2, opid2('>','='), ASSOC_LEFT, 10, 0 },
{ "==", 2, opid2('=','='), ASSOC_LEFT, 10, 0 },
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 10, 0 },
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0, false},
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0, false},
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0, false},
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0, false},
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0, false},
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0, false},
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0, false},
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0, false},
{ "=", 2, opid1('='), ASSOC_RIGHT, 8, 0 },
{ "+=", 2, opid2('+','='), ASSOC_RIGHT, 8, 0 },
{ "-=", 2, opid2('-','='), ASSOC_RIGHT, 8, 0 },
{ "*=", 2, opid2('*','='), ASSOC_RIGHT, 8, 0 },
{ "/=", 2, opid2('/','='), ASSOC_RIGHT, 8, 0 },
{ "%=", 2, opid2('%','='), ASSOC_RIGHT, 8, 0 },
{ "&=", 2, opid2('&','='), ASSOC_RIGHT, 8, 0 },
{ "|=", 2, opid2('|','='), ASSOC_RIGHT, 8, 0 },
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0, true},
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 5, 0 },
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0, false},
{ ",", 2, opid1(','), ASSOC_LEFT, 2, 0 },
};
static const size_t qcc_operator_count = (sizeof(qcc_operators) / sizeof(qcc_operators[0]));
extern const oper_info *operators;
extern size_t operator_count;
void lexerror(lex_file*, const char *fmt, ...);
#endif

View file

@ -1,20 +1,41 @@
#include <stdlib.h>
#include <string.h>
/*
* Copyright (C) 2012, 2013
* Dale Weiler
* Wolfgang Bumiller
*
* 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.
*/
#include "gmqcc.h"
#include "lexer.h"
#include "parser.h"
#include <time.h>
/* TODO: cleanup this whole file .. it's a fuckign mess */
/* set by the standard */
const oper_info *operators = nullptr;
size_t operator_count = 0;
static bool opts_output_wasset = false;
struct argitem { char *filename; int type; };
struct ppitem { char *name; char *value; };
static argitem *items = nullptr;
static ppitem *ppems = nullptr;
const oper_info *operators = NULL;
size_t operator_count = 0;
static bool opts_output_wasset = false;
typedef struct { char *filename; int type; } argitem;
typedef struct { char *name; char *value; } ppitem;
static argitem *items = NULL;
static ppitem *ppems = NULL;
#define TYPE_QC 0
#define TYPE_ASM 1
@ -22,7 +43,7 @@ static ppitem *ppems = nullptr;
static const char *app_name;
static void version(void) {
static void version() {
con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
GMQCC_VERSION_MAJOR,
GMQCC_VERSION_MINOR,
@ -32,11 +53,12 @@ static void version(void) {
);
}
static int usage(void) {
static int usage() {
con_out("usage: %s [options] [files...]", app_name);
con_out("options:\n"
" -h, --help show this help message\n"
" -debug turns on compiler debug messages\n");
" -debug turns on compiler debug messages\n"
" -memchk turns on compiler memory leak check\n");
con_out(" -o, --output=file output file, defaults to progs.dat\n"
" -s filename add a progs.src file to be used\n");
con_out(" -E stop after preprocessing\n");
@ -61,8 +83,6 @@ static int usage(void) {
" -Ono-<name> disable specific optimization\n"
" -Ohelp list optimizations\n");
con_out(" -force-crc=num force a specific checksum into the header\n");
con_out(" -state-fps=num emulate OP_STATE with the specified FPS\n");
con_out(" -coverage add coverage support\n");
return -1;
}
@ -117,16 +137,19 @@ static bool options_long_gcc(const char *optname, int *argc_, char ***argv_, cha
return options_long_witharg_all(optname, argc_, argv_, out, 1, false);
}
static bool options_parse(int argc, char **argv, bool *has_progs_src) {
static bool options_parse(int argc, char **argv) {
bool argend = false;
size_t itr;
char buffer[1024];
char *config = nullptr;
char buffer[1024];
char *redirout = NULL;
char *redirerr = NULL;
char *config = NULL;
char *memdumpcols = NULL;
while (!argend && argc > 1) {
char *argarg;
argitem item;
ppitem macro;
ppitem macro;
++argv;
--argc;
@ -138,9 +161,6 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
opts_set(opts.flags, CORRECT_LOGIC, true);
opts_set(opts.flags, SHORT_LOGIC, true);
opts_set(opts.flags, UNTYPED_NIL, true);
opts_set(opts.flags, VARIADIC_ARGS, true);
opts_set(opts.flags, FALSE_EMPTY_STRINGS, false);
opts_set(opts.flags, TRUE_EMPTY_STRINGS, true);
opts_set(opts.flags, LOOP_LABELS, true);
@ -149,8 +169,6 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
opts_set(opts.werror, WARN_INVALID_PARAMETER_COUNT, true);
opts_set(opts.werror, WARN_MISSING_RETURN_VALUES, true);
opts_set(opts.flags, EXPRESSIONS_FOR_BUILTINS, true);
opts_set(opts.warn, WARN_BREAKDEF, true);
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_GMQCC;
@ -170,7 +188,6 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
opts_set(opts.flags, CORRECT_TERNARY, false);
opts_set(opts.warn, WARN_TERNARY_PRECEDENCE, true);
opts_set(opts.warn, WARN_BREAKDEF, true);
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_FTEQCC;
@ -188,21 +205,23 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
if (options_long_gcc("force-crc", &argc, &argv, &argarg)) {
OPTS_OPTION_BOOL(OPTION_FORCECRC) = true;
OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, nullptr, 0);
OPTS_OPTION_U16 (OPTION_FORCED_CRC) = strtol(argarg, NULL, 0);
continue;
}
if (options_long_gcc("state-fps", &argc, &argv, &argarg)) {
OPTS_OPTION_U32(OPTION_STATE_FPS) = strtol(argarg, nullptr, 0);
opts_set(opts.flags, EMULATE_STATE, true);
if (options_long_gcc("redirout", &argc, &argv, &redirout)) {
con_change(redirout, redirerr);
continue;
}
if (options_long_gcc("redirerr", &argc, &argv, &redirerr)) {
con_change(redirout, redirerr);
continue;
}
if (options_long_gcc("config", &argc, &argv, &argarg)) {
config = argarg;
continue;
}
if (options_long_gcc("progsrc", &argc, &argv, &argarg)) {
OPTS_OPTION_STR(OPTION_PROGSRC) = argarg;
*has_progs_src = true;
if (options_long_gcc("memdumpcols", &argc, &argv, &memdumpcols)) {
OPTS_OPTION_U16(OPTION_MEMDUMPCOLS) = (uint16_t)strtol(memdumpcols, NULL, 10);
continue;
}
@ -241,12 +260,12 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
OPTS_OPTION_BOOL(OPTION_DUMPFIN) = true;
continue;
}
if (!strcmp(argv[0]+1, "nocolor")) {
con_color(0);
if (!strcmp(argv[0]+1, "memchk")) {
OPTS_OPTION_BOOL(OPTION_MEMCHK) = true;
continue;
}
if (!strcmp(argv[0]+1, "coverage")) {
OPTS_OPTION_BOOL(OPTION_COVERAGE) = true;
if (!strcmp(argv[0]+1, "nocolor")) {
con_color(0);
continue;
}
@ -284,7 +303,7 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
if (!(argarg = strchr(argv[0] + 2, '='))) {
macro.name = util_strdup(argv[0]+2);
macro.value = nullptr;
macro.value = NULL;
} else {
*argarg='\0'; /* terminate for name */
macro.name = util_strdup(argv[0]+2);
@ -330,7 +349,7 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
else if (!strcmp(argv[0]+2, "NO_ERROR") ||
!strcmp(argv[0]+2, "NO_ERROR_ALL"))
{
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr)
opts.werror[itr] = 0;
break;
}
@ -338,19 +357,19 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
!strcmp(argv[0]+2, "ERROR_ALL"))
{
opts_backup_non_Werror_all();
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.werror); ++itr)
for (itr = 0; itr < sizeof(opts.werror)/sizeof(opts.werror[0]); ++itr)
opts.werror[itr] = 0xFFFFFFFFL;
opts_restore_non_Werror_all();
break;
}
else if (!strcmp(argv[0]+2, "NONE")) {
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
opts.warn[itr] = 0;
break;
}
else if (!strcmp(argv[0]+2, "ALL")) {
opts_backup_non_Wall();
for (itr = 0; itr < GMQCC_ARRAY_COUNT(opts.warn); ++itr)
for (itr = 0; itr < sizeof(opts.warn)/sizeof(opts.warn[0]); ++itr)
opts.warn[itr] = 0xFFFFFFFFL;
opts_restore_non_Wall();
break;
@ -384,8 +403,8 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
con_out("option -O requires a numerical argument, or optimization name with an optional 'no-' prefix\n");
return false;
}
if (util_isdigit(argarg[0])) {
uint32_t val = (uint32_t)strtol(argarg, nullptr, 10);
if (isdigit(argarg[0])) {
uint32_t val = (uint32_t)strtol(argarg, NULL, 10);
OPTS_OPTION_U32(OPTION_O) = val;
opts_setoptimlevel(val);
} else {
@ -401,8 +420,7 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
else if (!strcmp(argarg, "ALL"))
opts_setoptimlevel(OPTS_OPTION_U32(OPTION_O) = 9999);
else if (!strncmp(argarg, "NO_", 3)) {
/* constant folding cannot be turned off for obvious reasons */
if (!strcmp(argarg, "NO_CONST_FOLD") || !opts_setoptim(argarg+3, false)) {
if (!opts_setoptim(argarg+3, false)) {
con_out("unknown optimization: %s\n", argarg+3);
return false;
}
@ -435,9 +453,6 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
}
item.filename = argarg;
vec_push(items, item);
if (item.type == TYPE_SRC) {
*has_progs_src = true;
}
break;
case '-':
@ -459,6 +474,14 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
OPTS_OPTION_BOOL(OPTION_QUIET) = true;
break;
}
else if (!strcmp(argv[0]+2, "correct")) {
OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
break;
}
else if (!strcmp(argv[0]+2, "no-correct")) {
OPTS_OPTION_BOOL(OPTION_CORRECTION) = false;
break;
}
else if (!strcmp(argv[0]+2, "add-info")) {
OPTS_OPTION_BOOL(OPTION_ADD_INFO) = true;
break;
@ -493,21 +516,21 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
}
/* returns the line number, or -1 on error */
static bool progs_nextline(char **out, size_t *alen, FILE *src) {
static bool progs_nextline(char **out, size_t *alen,FILE *src) {
int len;
char *line;
char *start;
char *end;
line = *out;
len = util_getline(&line, alen, src);
len = fs_file_getline(&line, alen, src);
if (len == -1)
return false;
/* start at first non-blank */
for (start = line; util_isspace(*start); ++start) {}
for (start = line; isspace(*start); ++start) {}
/* end at the first non-blank */
for (end = start; *end && !util_isspace(*end); ++end) {}
for (end = start; *end && !isspace(*end); ++end) {}
*out = line;
/* move the actual filename to the beginning */
@ -519,21 +542,22 @@ static bool progs_nextline(char **out, size_t *alen, FILE *src) {
}
int main(int argc, char **argv) {
size_t itr;
int retval = 0;
bool operators_free = false;
bool has_progs_src = false;
FILE *outfile = nullptr;
parser_t *parser = nullptr;
ftepp_t *ftepp = nullptr;
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 ();
opts_init("progs.dat", COMPILER_QCC, (1024 << 3));
opts_init("progs.dat", COMPILER_GMQCC, (1024 << 3));
util_seed(time(0));
if (!options_parse(argc, argv, &has_progs_src)) {
if (!options_parse(argc, argv)) {
return usage();
}
@ -545,13 +569,13 @@ int main(int argc, char **argv) {
/* the standard decides which set of operators to use */
if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
operators = c_operators;
operator_count = GMQCC_ARRAY_COUNT(c_operators);
operator_count = c_operator_count;
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
operators = fte_operators;
operator_count = GMQCC_ARRAY_COUNT(fte_operators);
operator_count = fte_operator_count;
} else {
operators = qcc_operators;
operator_count = GMQCC_ARRAY_COUNT(qcc_operators);
operator_count = qcc_operator_count;
}
if (operators == fte_operators) {
@ -587,7 +611,7 @@ int main(int argc, char **argv) {
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (opts_output_wasset) {
outfile = fopen(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
outfile = fs_file_open(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
if (!outfile) {
con_err("failed to open `%s` for writing\n", OPTS_OPTION_STR(OPTION_OUTPUT));
retval = 1;
@ -615,6 +639,8 @@ int main(int argc, char **argv) {
}
}
util_debug("COM", "starting ...\n");
/* add macros */
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
for (itr = 0; itr < vec_size(ppems); itr++) {
@ -627,55 +653,54 @@ int main(int argc, char **argv) {
}
}
if (!vec_size(items) && !has_progs_src) {
FILE *fp = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
if (fp) {
has_progs_src = true;
fclose(fp);
}
}
if (has_progs_src) {
if (!vec_size(items)) {
FILE *src;
char *line = nullptr;
char *line;
size_t linelen = 0;
bool has_first_line = false;
src = fopen(OPTS_OPTION_STR(OPTION_PROGSRC), "rb");
progs_src = true;
src = fs_file_open("progs.src", "rb");
if (!src) {
con_err("failed to open `%s` for reading\n", OPTS_OPTION_STR(OPTION_PROGSRC));
con_err("failed to open `progs.src` for reading\n");
retval = 1;
goto cleanup;
}
while (progs_nextline(&line, &linelen, src)) {
argitem item;
if (!line[0] || (line[0] == '/' && line[1] == '/')) {
continue;
}
if (has_first_line) {
item.filename = util_strdup(line);
item.type = TYPE_QC;
vec_push(items, item);
} else {
if (!opts_output_wasset) {
OPTS_OPTION_DUP(OPTION_OUTPUT) = util_strdup(line);
}
has_first_line = true;
}
line = NULL;
if (!progs_nextline(&line, &linelen, src) || !line[0]) {
con_err("illformatted progs.src file: expected output filename in first line\n");
retval = 1;
goto srcdone;
}
fclose(src);
if (!opts_output_wasset) {
OPTS_OPTION_STR(OPTION_OUTPUT) = util_strdup(line);
opts_output_free = true;
}
while (progs_nextline(&line, &linelen, src)) {
argitem item;
if (!line[0] || (line[0] == '/' && line[1] == '/'))
continue;
item.filename = util_strdup(line);
item.type = TYPE_QC;
vec_push(items, item);
}
srcdone:
fs_file_close(src);
mem_d(line);
}
if (retval)
goto cleanup;
if (vec_size(items)) {
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
{
con_out("Mode: %s\n", (has_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));
}
@ -691,10 +716,6 @@ int main(int argc, char **argv) {
("unknown"))))));
}
if (items[itr].type == TYPE_SRC) {
continue;
}
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
const char *out;
if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
@ -703,7 +724,7 @@ int main(int argc, char **argv) {
}
out = ftepp_get(ftepp);
if (out)
fprintf(outfile, "%s", out);
fs_file_printf(outfile, "%s", out);
ftepp_flush(ftepp);
}
else {
@ -730,14 +751,14 @@ int main(int argc, char **argv) {
}
}
if (has_progs_src) {
if (progs_src) {
mem_d(items[itr].filename);
items[itr].filename = nullptr;
items[itr].filename = NULL;
}
}
ftepp_finish(ftepp);
ftepp = nullptr;
ftepp = NULL;
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) {
retval = 1;
@ -746,7 +767,19 @@ int main(int argc, char **argv) {
}
}
/* stuff */
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
{
for (itr = 0; itr < COUNT_OPTIMIZATIONS; ++itr) {
if (opts_optimizationcount[itr]) {
con_out("%s: %u\n", opts_opt_list[itr].name, (unsigned int)opts_optimizationcount[itr]);
}
}
}
cleanup:
util_debug("COM", "cleaning ...\n");
if (ftepp)
ftepp_finish(ftepp);
con_close();
@ -754,19 +787,13 @@ cleanup:
vec_free(ppems);
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
delete parser;
/* free allocated option strings */
for (itr = 0; itr < OPTION_COUNT; itr++)
if (OPTS_OPTION_DUPED(itr))
mem_d(OPTS_OPTION_STR(itr));
parser_cleanup(parser);
if (opts_output_free)
mem_d(OPTS_OPTION_STR(OPTION_OUTPUT));
if (operators_free)
mem_d((void*)operators);
lex_cleanup();
if (!retval && compile_errors)
retval = 1;
util_meminfo();
return retval;
}

View file

@ -41,11 +41,3 @@ check_opt() {
check_opt FLAGS f
check_opt WARNS W
check_opt OPTIMIZATIONS O
# TODO: linux version
if [ "$(uname -s)" != "Linux" ]; then
for i in doc/*.1;
do
mandoc -Tlint -Wall "$i";
done
fi

38
msvc/gmqcc.sln Executable file
View file

@ -0,0 +1,38 @@

Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gmqcc", "gmqcc\gmqcc.vcxproj", "{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcvm", "qcvm\qcvm.vcxproj", "{DC980E20-C7A8-4112-A517-631DBDA788E7}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "pak", "pak\pak.vcxproj", "{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsuite", "testsuite\testsuite.vcxproj", "{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Debug|Win32.ActiveCfg = Debug|Win32
{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Debug|Win32.Build.0 = Debug|Win32
{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Release|Win32.ActiveCfg = Release|Win32
{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}.Release|Win32.Build.0 = Release|Win32
{DC980E20-C7A8-4112-A517-631DBDA788E7}.Debug|Win32.ActiveCfg = Debug|Win32
{DC980E20-C7A8-4112-A517-631DBDA788E7}.Debug|Win32.Build.0 = Debug|Win32
{DC980E20-C7A8-4112-A517-631DBDA788E7}.Release|Win32.ActiveCfg = Release|Win32
{DC980E20-C7A8-4112-A517-631DBDA788E7}.Release|Win32.Build.0 = Release|Win32
{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Debug|Win32.ActiveCfg = Debug|Win32
{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Debug|Win32.Build.0 = Debug|Win32
{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Release|Win32.ActiveCfg = Release|Win32
{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}.Release|Win32.Build.0 = Release|Win32
{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Debug|Win32.ActiveCfg = Debug|Win32
{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Debug|Win32.Build.0 = Debug|Win32
{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Release|Win32.ActiveCfg = Release|Win32
{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

90
msvc/gmqcc/gmqcc.vcxproj Executable file
View file

@ -0,0 +1,90 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{A6BD74E1-31BB-4D00-A9E0-09FF1BC76ED6}</ProjectGuid>
<RootNamespace>gmqcc</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\ast.c" />
<ClCompile Include="..\..\code.c" />
<ClCompile Include="..\..\conout.c" />
<ClCompile Include="..\..\correct.c" />
<ClCompile Include="..\..\fs.c" />
<ClCompile Include="..\..\ftepp.c" />
<ClCompile Include="..\..\ir.c" />
<ClCompile Include="..\..\lexer.c" />
<ClCompile Include="..\..\main.c" />
<ClCompile Include="..\..\opts.c" />
<ClCompile Include="..\..\parser.c" />
<ClCompile Include="..\..\utf8.c" />
<ClCompile Include="..\..\util.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\ast.h" />
<ClInclude Include="..\..\gmqcc.h" />
<ClInclude Include="..\..\intrin.h" />
<ClInclude Include="..\..\ir.h" />
<ClInclude Include="..\..\lexer.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\opts.def" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="..\..\ast.c" />
<ClCompile Include="..\..\code.c" />
<ClCompile Include="..\..\conout.c" />
<ClCompile Include="..\..\correct.c" />
<ClCompile Include="..\..\fs.c" />
<ClCompile Include="..\..\ftepp.c" />
<ClCompile Include="..\..\ir.c" />
<ClCompile Include="..\..\lexer.c" />
<ClCompile Include="..\..\main.c" />
<ClCompile Include="..\..\opts.c" />
<ClCompile Include="..\..\parser.c" />
<ClCompile Include="..\..\utf8.c" />
<ClCompile Include="..\..\util.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\ast.h" />
<ClInclude Include="..\..\gmqcc.h" />
<ClInclude Include="..\..\intrin.h" />
<ClInclude Include="..\..\ir.h" />
<ClInclude Include="..\..\lexer.h" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\opts.def" />
</ItemGroup>
</Project>

78
msvc/pak/pak.vcxproj Executable file
View file

@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{A6F66BE9-57EF-4E93-AA9D-6E0C8B0990AD}</ProjectGuid>
<RootNamespace>pak</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\conout.c" />
<ClCompile Include="..\..\fs.c" />
<ClCompile Include="..\..\opts.c" />
<ClCompile Include="..\..\pak.c" />
<ClCompile Include="..\..\util.c" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\opts.def" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\gmqcc.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

16
msvc/pak/pak.vcxproj.filters Executable file
View file

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="..\..\conout.c" />
<ClCompile Include="..\..\fs.c" />
<ClCompile Include="..\..\opts.c" />
<ClCompile Include="..\..\pak.c" />
<ClCompile Include="..\..\util.c" />
</ItemGroup>
<ItemGroup>
<None Include="..\..\opts.def" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\gmqcc.h" />
</ItemGroup>
</Project>

76
msvc/qcvm/qcvm.vcxproj Executable file
View file

@ -0,0 +1,76 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{DC980E20-C7A8-4112-A517-631DBDA788E7}</ProjectGuid>
<RootNamespace>qcvm</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>QCVM_EXECUTOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>QCVM_EXECUTOR;%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\conout.c" />
<ClCompile Include="..\..\exec.c" />
<ClCompile Include="..\..\fs.c" />
<ClCompile Include="..\..\util.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\gmqcc.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

12
msvc/qcvm/qcvm.vcxproj.filters Executable file
View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="..\..\conout.c" />
<ClCompile Include="..\..\exec.c" />
<ClCompile Include="..\..\fs.c" />
<ClCompile Include="..\..\util.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\gmqcc.h" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,74 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{7E2839D9-9C1A-4489-9FF9-FDC854EBED3D}</ProjectGuid>
<RootNamespace>testsuite</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>Disabled</Optimization>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\..\conout.c" />
<ClCompile Include="..\..\fs.c" />
<ClCompile Include="..\..\test.c" />
<ClCompile Include="..\..\util.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\gmqcc.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View file

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="..\..\conout.c" />
<ClCompile Include="..\..\fs.c" />
<ClCompile Include="..\..\test.c" />
<ClCompile Include="..\..\util.c" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\gmqcc.h" />
</ItemGroup>
</Project>

View file

@ -1,42 +1,33 @@
#include <string.h>
#include <stdlib.h>
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
* 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.
*/
#include "gmqcc.h"
const unsigned int opts_opt_oflag[COUNT_OPTIMIZATIONS+1] = {
# define GMQCC_TYPE_OPTIMIZATIONS
# define GMQCC_DEFINE_FLAG(NAME, MIN_O) MIN_O,
# include "opts.def"
0
};
const opts_flag_def_t opts_opt_list[COUNT_OPTIMIZATIONS+1] = {
# define GMQCC_TYPE_OPTIMIZATIONS
# define GMQCC_DEFINE_FLAG(NAME, MIN_O) { #NAME, LONGBIT(OPTIM_##NAME) },
# include "opts.def"
{ nullptr, LONGBIT(0) }
};
const opts_flag_def_t opts_warn_list[COUNT_WARNINGS+1] = {
# define GMQCC_TYPE_WARNS
# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(WARN_##X) },
# include "opts.def"
{ nullptr, LONGBIT(0) }
};
const opts_flag_def_t opts_flag_list[COUNT_FLAGS+1] = {
# define GMQCC_TYPE_FLAGS
# define GMQCC_DEFINE_FLAG(X) { #X, LONGBIT(X) },
# include "opts.def"
{ nullptr, LONGBIT(0) }
};
unsigned int opts_optimizationcount[COUNT_OPTIMIZATIONS];
opts_cmd_t opts; /* command line options */
opts_cmd_t opts; /* command lien options */
static void opts_setdefault(void) {
static void opts_setdefault() {
memset(&opts, 0, sizeof(opts_cmd_t));
OPTS_OPTION_STR(OPTION_PROGSRC) = "progs.src";
OPTS_OPTION_BOOL(OPTION_CORRECTION) = true;
/* warnings */
opts_set(opts.warn, WARN_UNUSED_VARIABLE, true);
@ -66,10 +57,6 @@ static void opts_setdefault(void) {
opts_set(opts.warn, WARN_UNINITIALIZED_CONSTANT, true);
opts_set(opts.warn, WARN_DEPRECATED, true);
opts_set(opts.warn, WARN_PARENTHESIS, true);
opts_set(opts.warn, WARN_CONST_OVERWRITE, true);
opts_set(opts.warn, WARN_DIRECTIVE_INMACRO, true);
opts_set(opts.warn, WARN_BUILTINS, true);
opts_set(opts.warn, WARN_INEXACT_COMPARES, true);
/* flags */
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
@ -78,8 +65,6 @@ static void opts_setdefault(void) {
opts_set(opts.flags, LEGACY_VECTOR_MATHS, true);
opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true);
/* options */
OPTS_OPTION_U32(OPTION_STATE_FPS) = 10;
}
void opts_backup_non_Wall() {
@ -109,12 +94,13 @@ void opts_restore_non_Werror_all() {
void opts_init(const char *output, int standard, size_t arraysize) {
opts_setdefault();
OPTS_OPTION_STR(OPTION_OUTPUT) = output;
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_t *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) {
size_t i;
for (i = 0; i < listsize; ++i) {
@ -149,9 +135,9 @@ void opts_set(uint32_t *flags, size_t idx, bool on) {
LONGBIT_SET(lb, idx);
if (on)
flags[lb.idx] |= (1u<<(lb.bit));
flags[lb.idx] |= (1<<(lb.bit));
else
flags[lb.idx] &= ~(1u<<(lb.bit));
flags[lb.idx] &= ~(1<<(lb.bit));
}
void opts_setoptimlevel(unsigned int level) {
@ -171,14 +157,14 @@ void opts_setoptimlevel(unsigned int level) {
* from a progs.src.
*/
static char *opts_ini_rstrip(char *s) {
char *p = s + strlen(s) - 1;
while (p > s && util_isspace(*p))
*p = '\0', p--;
char *p = s + strlen(s);
while(p > s && isspace(*--p))
*p = '\0';
return s;
}
static char *opts_ini_lskip(const char *s) {
while (*s && util_isspace(*s))
while (*s && isspace(*s))
s++;
return (char*)s;
}
@ -186,21 +172,20 @@ static char *opts_ini_lskip(const char *s) {
static char *opts_ini_next(const char *s, char c) {
bool last = false;
while (*s && *s != c && !(last && *s == ';'))
last = !!util_isspace(*s), s++;
last = !!isspace(*s), s++;
return (char*)s;
}
static size_t opts_ini_parse (
FILE *filehandle,
char *(*loadhandle)(const char *, const char *, const char *, char **),
char **errorhandle,
char **parse_file
FILE *filehandle,
char *(*loadhandle)(const char *, const char *, const char *),
char **errorhandle
) {
size_t linesize;
size_t lineno = 1;
size_t error = 0;
char *line = nullptr;
char *line = NULL;
char section_data[2048] = "";
char oldname_data[2048] = "";
@ -210,7 +195,7 @@ static size_t opts_ini_parse (
char *read_name;
char *read_value;
while (util_getline(&line, &linesize, filehandle) != EOF) {
while (fs_file_getline(&line, &linesize, filehandle) != EOF) {
parse_beg = line;
/* handle BOM */
@ -241,7 +226,7 @@ static size_t opts_ini_parse (
} else if (*parse_beg && *parse_beg != ';') {
/* not a comment, must be a name value pair :) */
if (*(parse_end = opts_ini_next(parse_beg, '=')) != '=')
parse_end = opts_ini_next(parse_beg, ':');
parse_end = opts_ini_next(parse_beg, ':');
if (*parse_end == '=' || *parse_end == ':') {
*parse_end = '\0'; /* terminate bro */
@ -255,20 +240,8 @@ static size_t opts_ini_parse (
util_strncpy(oldname_data, read_name, sizeof(oldname_data));
oldname_data[sizeof(oldname_data) - 1] ='\0';
if ((*errorhandle = loadhandle(section_data, read_name, read_value, parse_file)) && !error)
if ((*errorhandle = loadhandle(section_data, read_name, read_value)) && !error)
error = lineno;
} else if (!strcmp(section_data, "includes")) {
/* Includes are special */
if (*(parse_end = opts_ini_next(parse_beg, '=')) == '='
|| *(parse_end = opts_ini_next(parse_beg, ':')) == ':') {
static const char *invalid_include = "invalid use of include";
vec_append(*errorhandle, strlen(invalid_include), invalid_include);
error = lineno;
} else {
read_name = opts_ini_rstrip(parse_beg);
if ((*errorhandle = loadhandle(section_data, read_name, read_name, parse_file)) && !error)
error = lineno;
}
} else if (!error) {
/* otherwise set error to the current line number */
error = lineno;
@ -278,7 +251,6 @@ static size_t opts_ini_parse (
}
mem_d(line);
return error;
}
/*
@ -287,11 +259,11 @@ 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 !!strtol(value, nullptr, 10);
return !!strtol(value, NULL, 10);
}
static char *opts_ini_load(const char *section, const char *name, const char *value, char **parse_file) {
char *error = nullptr;
static char *opts_ini_load(const char *section, const char *name, const char *value) {
char *error = NULL;
bool found = false;
/*
@ -302,26 +274,6 @@ static char *opts_ini_load(const char *section, const char *name, const char *va
#undef GMQCC_TYPE_OPTIMIZATIONS
#undef GMQCC_TYPE_WARNS
/* deal with includes */
if (!strcmp(section, "includes")) {
static const char *include_error_beg = "failed to open file `";
static const char *include_error_end = "' for inclusion";
FILE *file = fopen(value, "r");
found = true;
if (!file) {
vec_append(error, strlen(include_error_beg), include_error_beg);
vec_append(error, strlen(value), value);
vec_append(error, strlen(include_error_end), include_error_end);
} else {
if (opts_ini_parse(file, &opts_ini_load, &error, parse_file) != 0)
found = false;
/* Change the file name */
mem_d(*parse_file);
*parse_file = util_strdup(value);
fclose(file);
}
}
/* flags */
#define GMQCC_TYPE_FLAGS
#define GMQCC_DEFINE_FLAG(X) \
@ -360,29 +312,24 @@ static char *opts_ini_load(const char *section, const char *name, const char *va
/* nothing was found ever! */
if (!found) {
if (strcmp(section, "includes") &&
strcmp(section, "flags") &&
strcmp(section, "warnings") &&
if (strcmp(section, "flags") &&
strcmp(section, "warnings") &&
strcmp(section, "optimizations"))
{
static const char *invalid_section = "invalid_section `";
vec_append(error, strlen(invalid_section), invalid_section);
vec_append(error, strlen(section), section);
vec_push(error, '`');
} else if (strcmp(section, "includes")) {
static const char *invalid_variable = "invalid_variable `";
static const char *in_section = "` in section: `";
vec_append(error, strlen(invalid_variable), invalid_variable);
vec_append(error, strlen(name), name);
vec_append(error, strlen(in_section), in_section);
vec_append(error, strlen(section), section);
vec_push(error, '`');
vec_upload(error, "invalid section `", 17);
vec_upload(error, section, strlen(section));
vec_push (error, '`');
vec_push (error, '\0');
} else {
static const char *expected_something = "expected something";
vec_append(error, strlen(expected_something), expected_something);
vec_upload(error, "invalid variable `", 18);
vec_upload(error, name, strlen(name));
vec_push (error, '`');
vec_upload(error, " in section: `", 14);
vec_upload(error, section, strlen(section));
vec_push (error, '`');
vec_push (error, '\0');
}
}
vec_push(error, '\0');
return error;
}
@ -396,29 +343,27 @@ void opts_ini_init(const char *file) {
* gmqcc.ini
* gmqcc.cfg
*/
char *error = nullptr;
char *parse_file = nullptr;
char *error;
size_t line;
FILE *ini;
FILE *ini;
if (!file) {
/* try ini */
if (!(ini = fopen((file = "gmqcc.ini"), "r")))
if (!(ini = fs_file_open((file = "gmqcc.ini"), "r")))
/* try cfg */
if (!(ini = fopen((file = "gmqcc.cfg"), "r")))
if (!(ini = fs_file_open((file = "gmqcc.cfg"), "r")))
return;
} else if (!(ini = fopen(file, "r")))
} else if (!(ini = fs_file_open(file, "r")))
return;
con_out("found ini file `%s`\n", file);
parse_file = util_strdup(file);
if ((line = opts_ini_parse(ini, &opts_ini_load, &error, &parse_file)) != 0) {
if ((line = opts_ini_parse(ini, &opts_ini_load, &error)) != 0) {
/* there was a parse error with the ini file */
con_printmsg(LVL_ERROR, parse_file, line, 0 /*TODO: column for ini error*/, "error", error);
con_printmsg(LVL_ERROR, file, line, "error", error);
vec_free(error);
}
mem_d(parse_file);
fclose(ini);
fs_file_close(ini);
}

View file

@ -1,3 +1,26 @@
/*
* Copyright (C) 2012, 2013
* Wolfgang Bumiller
* 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.
*/
#ifndef GMQCC_DEFINE_FLAG
# error "bad opts.def usage"
#endif
@ -8,8 +31,6 @@
GMQCC_DEFINE_FLAG(ADJUST_VECTOR_FIELDS)
GMQCC_DEFINE_FLAG(FTEPP)
GMQCC_DEFINE_FLAG(FTEPP_PREDEFS)
GMQCC_DEFINE_FLAG(FTEPP_MATHDEFS)
GMQCC_DEFINE_FLAG(FTEPP_INDIRECT_EXPANSION)
GMQCC_DEFINE_FLAG(RELAXED_SWITCH)
GMQCC_DEFINE_FLAG(SHORT_LOGIC)
GMQCC_DEFINE_FLAG(PERL_LOGIC)
@ -30,14 +51,6 @@
GMQCC_DEFINE_FLAG(VARIADIC_ARGS)
GMQCC_DEFINE_FLAG(LEGACY_VECTOR_MATHS)
GMQCC_DEFINE_FLAG(EXPRESSIONS_FOR_BUILTINS)
GMQCC_DEFINE_FLAG(RETURN_ASSIGNMENTS)
GMQCC_DEFINE_FLAG(UNSAFE_VARARGS)
GMQCC_DEFINE_FLAG(TYPELESS_STORES)
GMQCC_DEFINE_FLAG(SORT_OPERANDS)
GMQCC_DEFINE_FLAG(EMULATE_STATE)
GMQCC_DEFINE_FLAG(ARITHMETIC_EXCEPTIONS)
GMQCC_DEFINE_FLAG(SPLIT_VECTOR_PARAMETERS)
GMQCC_DEFINE_FLAG(DEFAULT_ERASEABLE)
#endif
/* warning flags */
@ -45,7 +58,6 @@
GMQCC_DEFINE_FLAG(UNINITIALIZED_GLOBAL)
GMQCC_DEFINE_FLAG(DEBUG)
GMQCC_DEFINE_FLAG(UNUSED_VARIABLE)
GMQCC_DEFINE_FLAG(UNUSED_COMPONENT)
GMQCC_DEFINE_FLAG(USED_UNINITIALIZED)
GMQCC_DEFINE_FLAG(UNKNOWN_CONTROL_SEQUENCE)
GMQCC_DEFINE_FLAG(EXTENSIONS)
@ -76,12 +88,6 @@
GMQCC_DEFINE_FLAG(DIFFERENT_ATTRIBUTES)
GMQCC_DEFINE_FLAG(DEPRECATED)
GMQCC_DEFINE_FLAG(PARENTHESIS)
GMQCC_DEFINE_FLAG(UNSAFE_TYPES)
GMQCC_DEFINE_FLAG(BREAKDEF)
GMQCC_DEFINE_FLAG(CONST_OVERWRITE)
GMQCC_DEFINE_FLAG(DIRECTIVE_INMACRO)
GMQCC_DEFINE_FLAG(BUILTINS)
GMQCC_DEFINE_FLAG(INEXACT_COMPARES)
#endif
#ifdef GMQCC_TYPE_OPTIMIZATIONS
@ -95,8 +101,6 @@
GMQCC_DEFINE_FLAG(CALL_STORES, 3)
GMQCC_DEFINE_FLAG(VOID_RETURN, 1)
GMQCC_DEFINE_FLAG(VECTOR_COMPONENTS, 1)
GMQCC_DEFINE_FLAG(CONST_FOLD_DCE, 2)
GMQCC_DEFINE_FLAG(CONST_FOLD, 0) /* cannot be turned off */
#endif
#ifdef GMQCC_TYPE_OPTIONS
@ -106,6 +110,8 @@
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)
GMQCC_DEFINE_FLAG(FORCECRC)
@ -113,9 +119,7 @@
GMQCC_DEFINE_FLAG(PP_ONLY)
GMQCC_DEFINE_FLAG(MAX_ARRAY_SIZE)
GMQCC_DEFINE_FLAG(ADD_INFO)
GMQCC_DEFINE_FLAG(PROGSRC)
GMQCC_DEFINE_FLAG(COVERAGE)
GMQCC_DEFINE_FLAG(STATE_FPS)
GMQCC_DEFINE_FLAG(CORRECTION)
#endif
/* some cleanup so we don't have to */

581
pak.c Normal file
View file

@ -0,0 +1,581 @@
/*
* Copyright (C) 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.
*/
#include "gmqcc.h"
/*
* The PAK format uses a FOURCC concept for storing the magic ident within
* the header as a uint32_t.
*/
#define PAK_FOURCC ((uint32_t)(('P' | ('A' << 8) | ('C' << 16) | ('K' << 24))))
typedef struct {
uint32_t magic; /* "PACK" */
/*
* Offset to first directory entry in PAK file. It's often
* best to store the directories at the end of the file opposed
* to the front, since it allows easy insertion without having
* to load the entire file into memory again.
*/
uint32_t diroff;
uint32_t dirlen;
} pak_header_t;
/*
* A directory, is sort of a "file entry". The concept of
* a directory in Quake world is a "file entry/record". This
* describes a file (with directories/nested ones too in it's
* file name). Hence it can be a file, file with directory, or
* file with directories.
*/
typedef struct {
char name[56];
uint32_t pos;
uint32_t len;
} pak_directory_t;
/*
* Used to get the next token from a string, where the
* strings themselfs are seperated by chracters from
* `sep`. This is essentially strsep.
*/
static char *pak_tree_sep(char **str, const char *sep) {
char *beg = *str;
char *end;
if (!beg)
return NULL;
if (*(end = beg + strcspn(beg, sep)))
* end++ = '\0'; /* null terminate */
else
end = 0;
*str = end;
return beg;
}
/*
* When given a string like "a/b/c/d/e/file"
* this function will handle the creation of
* the directory structure, included nested
* directories.
*/
static void pak_tree_build(const char *entry) {
char *directory;
char *elements[28];
char *pathsplit;
char *token;
size_t itr;
size_t jtr;
pathsplit = (char *)mem_a(56);
directory = (char *)mem_a(56);
memset(pathsplit, 0, 56);
util_strncpy(directory, entry, 56);
for (itr = 0; (token = pak_tree_sep(&directory, "/")) != NULL; itr++) {
elements[itr] = token;
}
for (jtr = 0; jtr < itr - 1; jtr++) {
util_strcat(pathsplit, elements[jtr]);
util_strcat(pathsplit, "/");
if (fs_dir_make(pathsplit)) {
mem_d(pathsplit);
mem_d(directory);
/* TODO: undo on fail */
return;
}
}
mem_d(pathsplit);
mem_d(directory);
}
typedef struct {
pak_directory_t *directories;
pak_header_t header;
FILE *handle;
bool insert;
} pak_file_t;
static pak_file_t *pak_open_read(const char *file) {
pak_file_t *pak;
size_t itr;
if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t))))
return NULL;
if (!(pak->handle = fs_file_open(file, "rb"))) {
mem_d(pak);
return NULL;
}
pak->directories = NULL;
pak->insert = false; /* read doesn't allow insert */
memset (&pak->header, 0, sizeof(pak_header_t));
fs_file_read (&pak->header, sizeof(pak_header_t), 1, pak->handle);
util_endianswap(&pak->header, 1, sizeof(pak_header_t));
/*
* Every PAK file has "PACK" stored as FOURCC data in the
* header. If this data cannot compare (as checked here), it's
* probably not a PAK file.
*/
if (pak->header.magic != PAK_FOURCC) {
fs_file_close(pak->handle);
mem_d (pak);
return NULL;
}
/*
* Time to read in the directory handles and prepare the directories
* vector. We're going to be reading some the file inwards soon.
*/
fs_file_seek(pak->handle, pak->header.diroff, SEEK_SET);
/*
* Read in all directories from the PAK file. These are considered
* to be the "file entries".
*/
for (itr = 0; itr < pak->header.dirlen / 64; itr++) {
pak_directory_t dir;
fs_file_read (&dir, sizeof(pak_directory_t), 1, pak->handle);
util_endianswap(&dir, 1, sizeof(pak_directory_t));
vec_push(pak->directories, dir);
}
return pak;
}
static pak_file_t *pak_open_write(const char *file) {
pak_file_t *pak;
if (!(pak = (pak_file_t*)mem_a(sizeof(pak_file_t))))
return NULL;
/*
* Generate the required directory structure / tree for
* writing this PAK file too.
*/
pak_tree_build(file);
if (!(pak->handle = fs_file_open(file, "wb"))) {
/*
* The directory tree that was created, needs to be
* removed entierly if we failed to open a file.
*/
/* TODO backup directory clean */
mem_d(pak);
return NULL;
}
memset(&(pak->header), 0, sizeof(pak_header_t));
/*
* We're in "insert" mode, we need to do things like header
* "patching" and writing the directories at the end of the
* file.
*/
pak->insert = true;
pak->header.magic = PAK_FOURCC;
/* on BE systems we need to swap the byte order of the FOURCC */
util_endianswap(&pak->header.magic, 1, sizeof(uint32_t));
/*
* We need to write out the header since files will be wrote out to
* this even with directory entries, and that not wrote. The header
* will need to be patched in later with a file_seek, and overwrite,
* we could use offsets and other trickery. This is just easier.
*/
fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle);
return pak;
}
pak_file_t *pak_open(const char *file, const char *mode) {
if (!file || !mode)
return NULL;
switch (*mode) {
case 'r': return pak_open_read (file);
case 'w': return pak_open_write(file);
}
return NULL;
}
bool pak_exists(pak_file_t *pak, const char *file, pak_directory_t **dir) {
size_t itr;
if (!pak || !file)
return false;
for (itr = 0; itr < vec_size(pak->directories); itr++) {
if (!strcmp(pak->directories[itr].name, file)) {
/*
* Store back a pointer to the directory that matches
* the request if requested (NULL is not allowed).
*/
if (dir) {
*dir = &(pak->directories[itr]);
}
return true;
}
}
return false;
}
/*
* Extraction abilities. These work as you expect them to.
*/
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)) {
return false;
}
if (!(dat = (unsigned char *)mem_a(dir->len))) {
return false;
}
/*
* Generate the directory structure / tree that will be required
* to store the extracted 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(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);
fs_file_read (dat, 1, dir->len, pak->handle);
/* write */
fs_file_write(dat, 1, dir->len, out);
/* close */
fs_file_close(out);
/* free */
mem_d(dat);
return true;
}
bool pak_extract_all(pak_file_t *pak, const char *dir) {
size_t itr;
if (!fs_dir_make(dir))
return false;
for (itr = 0; itr < vec_size(pak->directories); itr++) {
if (!pak_extract_one(pak, pak->directories[itr].name, dir))
return false;
}
return true;
}
/*
* Insertion functions (the opposite of extraction). Yes for generating
* PAKs.
*/
bool pak_insert_one(pak_file_t *pak, const char *file) {
pak_directory_t dir;
unsigned char *dat;
FILE *fp;
/*
* We don't allow insertion on files that already exist within the
* pak file. Weird shit can happen if we allow that ;). We also
* don't allow insertion if the pak isn't opened in write mode.
*/
if (!pak || !file || !pak->insert || pak_exists(pak, file, NULL))
return false;
if (!(fp = fs_file_open(file, "rb")))
return false;
/*
* Calculate the total file length, since it will be wrote to
* the directory entry, and the actual contents of the file
* to the PAK file itself.
*/
fs_file_seek(fp, 0, SEEK_END);
dir.len = fs_file_tell(fp);
fs_file_seek(fp, 0, SEEK_SET);
dir.pos = fs_file_tell(pak->handle);
/*
* We're limited to 56 bytes for a file name string, that INCLUDES
* the directory and '/' seperators.
*/
if (strlen(file) >= 56) {
fs_file_close(fp);
return false;
}
util_strncpy(dir.name, file, strlen(file));
/*
* Allocate some memory for loading in the data that will be
* redirected into the PAK file.
*/
if (!(dat = (unsigned char *)mem_a(dir.len))) {
fs_file_close(fp);
return false;
}
fs_file_read (dat, dir.len, 1, fp);
fs_file_close(fp);
fs_file_write(dat, dir.len, 1, pak->handle);
/*
* Now add the directory to the directories vector, so pak_close
* can actually write it.
*/
vec_push(pak->directories, dir);
return true;
}
/*
* Like pak_insert_one, except this collects files in all directories
* from a root directory, and inserts them all.
*/
bool pak_insert_all(pak_file_t *pak, const char *dir) {
DIR *dp;
struct dirent *dirp;
if (!(pak->insert))
return false;
if (!(dp = fs_dir_open(dir)))
return false;
while ((dirp = fs_dir_read(dp))) {
if (!(pak_insert_one(pak, dirp->d_name))) {
fs_dir_close(dp);
return false;
}
}
fs_dir_close(dp);
return true;
}
bool pak_close(pak_file_t *pak) {
size_t itr;
if (!pak)
return false;
/*
* In insert mode we need to patch the header, and write
* our directory entries at the end of the file.
*/
if (pak->insert) {
pak->header.dirlen = vec_size(pak->directories) * 64;
pak->header.diroff = ftell(pak->handle);
/* patch header */
fs_file_seek (pak->handle, 0, SEEK_SET);
fs_file_write(&(pak->header), sizeof(pak_header_t), 1, pak->handle);
/* write directories */
fs_file_seek (pak->handle, pak->header.diroff, SEEK_SET);
for (itr = 0; itr < vec_size(pak->directories); itr++) {
fs_file_write(&(pak->directories[itr]), sizeof(pak_directory_t), 1, pak->handle);
}
}
vec_free (pak->directories);
fs_file_close(pak->handle);
mem_d (pak);
return true;
}
/*
* Fancy GCC-like LONG parsing allows things like --opt=param with
* assignment operator. This is used for redirecting stdout/stderr
* console to specific files of your choice.
*/
static bool parsecmd(const char *optname, int *argc_, char ***argv_, char **out, int ds, bool split) {
int argc = *argc_;
char **argv = *argv_;
size_t len = strlen(optname);
if (strncmp(argv[0]+ds, optname, len))
return false;
/* it's --optname, check how the parameter is supplied */
if (argv[0][ds+len] == '=') {
*out = argv[0]+ds+len+1;
return true;
}
if (!split || argc < ds) /* no parameter was provided, or only single-arg form accepted */
return false;
/* using --opt param */
*out = argv[1];
--*argc_;
++*argv_;
return true;
}
int main(int argc, char **argv) {
bool extract = true;
char *redirout = (char*)stdout;
char *redirerr = (char*)stderr;
char *file = NULL;
char **files = NULL;
pak_file_t *pak = NULL;
size_t iter = 0;
con_init();
/*
* Command line option parsing commences now We only need to support
* a few things in the test suite.
*/
while (argc > 1) {
++argv;
--argc;
if (argv[0][0] == '-') {
if (parsecmd("redirout", &argc, &argv, &redirout, 1, false))
continue;
if (parsecmd("redirerr", &argc, &argv, &redirerr, 1, false))
continue;
if (parsecmd("file", &argc, &argv, &file, 1, false))
continue;
con_change(redirout, redirerr);
switch (argv[0][1]) {
case 'e': extract = true; continue;
case 'c': extract = false; continue;
}
if (!strcmp(argv[0]+1, "debug")) {
OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
continue;
}
if (!strcmp(argv[0]+1, "memchk")) {
OPTS_OPTION_BOOL(OPTION_MEMCHK) = true;
continue;
}
if (!strcmp(argv[0]+1, "nocolor")) {
con_color(0);
continue;
}
}
vec_push(files, argv[0]);
}
con_change(redirout, redirerr);
if (!file) {
con_err("-file must be specified for output/input PAK file\n");
vec_free(files);
return EXIT_FAILURE;
}
if (extract) {
if (!(pak = pak_open(file, "r"))) {
con_err("failed to open PAK file %s\n", file);
vec_free(files);
return EXIT_FAILURE;
}
if (!pak_extract_all(pak, "./")) {
con_err("failed to extract PAK %s (files may be missing)\n", file);
pak_close(pak);
vec_free(files);
return EXIT_FAILURE;
}
/* not possible */
pak_close(pak);
vec_free(files);
util_meminfo();
return EXIT_SUCCESS;
}
if (!(pak = pak_open(file, "w"))) {
con_err("failed to open PAK %s for writing\n", file);
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);
pak_close(pak);
vec_free(files);
return EXIT_FAILURE;
}
}
/* not possible */
pak_close(pak);
vec_free(files);
util_meminfo();
return EXIT_SUCCESS;
}

File diff suppressed because it is too large Load diff

View file

@ -1,84 +0,0 @@
#ifndef GMQCC_PARSER_HDR
#define GMQCC_PARSER_HDR
#include "gmqcc.h"
#include "lexer.h"
#include "ast.h"
#include "intrin.h"
#include "fold.h"
struct parser_t;
#define parser_ctx(p) ((p)->lex->tok.ctx)
struct parser_t {
parser_t();
~parser_t();
void remove_ast();
lex_file *lex;
int tok;
bool ast_cleaned;
std::vector<ast_expression *> globals;
std::vector<ast_expression *> fields;
std::vector<ast_function *> functions;
size_t translated;
/* must be deleted first, they reference immediates and values */
std::vector<ast_value *> accessors;
ast_value *nil;
ast_value *reserved_version;
size_t crc_globals;
size_t crc_fields;
ast_function *function;
ht aliases;
/* All the labels the function defined...
* Should they be in ast_function instead?
*/
std::vector<ast_label*> labels;
std::vector<ast_goto*> gotos;
std::vector<const char *> breaks;
std::vector<const char *> continues;
/* A list of hashtables for each scope */
std::vector<ht> variables;
ht htfields;
ht htglobals;
std::vector<ht> typedefs;
/* not to be used directly, we use the hash table */
std::vector<ast_expression*> _locals;
std::vector<size_t> _blocklocals;
std::vector<std::unique_ptr<ast_value>> _typedefs;
std::vector<size_t> _blocktypedefs;
std::vector<lex_ctx_t> _block_ctx;
/* we store the '=' operator info */
const oper_info *assign_op;
/* magic values */
ast_value *const_vec[3];
/* pragma flags */
bool noref;
/* collected information */
size_t max_param_count;
fold m_fold;
intrin m_intrin;
};
/* parser.c */
char *parser_strdup (const char *str);
ast_expression *parser_find_global(parser_t *parser, const char *name);
#endif

250
stat.cpp
View file

@ -1,250 +0,0 @@
#include <string.h>
#include <stdlib.h>
#include "gmqcc.h"
/*
* strdup does it's own malloc, we need to track malloc. We don't want
* to overwrite malloc though, infact, we can't really hook it at all
* without library specific assumptions. So we re implement strdup.
*/
char *stat_mem_strdup(const char *src, bool empty) {
size_t len = 0;
char *ptr = nullptr;
if (!src)
return nullptr;
len = strlen(src);
if ((!empty ? len : true) && (ptr = (char*)mem_a(len + 1))) {
memcpy(ptr, src, len);
ptr[len] = '\0';
}
return ptr;
}
/*
* The reallocate function for resizing vectors.
*/
void _util_vec_grow(void **a, size_t i, size_t s) {
vector_t *d = nullptr;
size_t m = 0;
void *p = nullptr;
if (*a) {
d = vec_meta(*a);
m = 2 * d->allocated + i;
p = mem_r(d, s * m + sizeof(vector_t));
} else {
m = i + 1;
p = mem_a(s * m + sizeof(vector_t));
((vector_t*)p)->used = 0;
}
d = (vector_t*)p;
d->allocated = m;
*a = d + 1;
}
void _util_vec_delete(void *data) {
mem_d(vec_meta(data));
}
/*
* Hash table for generic data, based on dynamic memory allocations
* all around. This is the internal interface, please look for
* EXPOSED INTERFACE comment below
*/
struct hash_node_t {
char *key; /* the key for this node in table */
void *value; /* pointer to the data as void* */
hash_node_t *next; /* next node (linked list) */
};
size_t hash(const char *key);
size_t util_hthash(hash_table_t *ht, const char *key) {
return hash(key) % ht->size;
}
static hash_node_t *_util_htnewpair(const char *key, void *value) {
hash_node_t *node;
if (!(node = (hash_node_t*)mem_a(sizeof(hash_node_t))))
return nullptr;
if (!(node->key = util_strdupe(key))) {
mem_d(node);
return nullptr;
}
node->value = value;
node->next = nullptr;
return node;
}
/*
* EXPOSED INTERFACE for the hashtable implementation
* util_htnew(size) -- to make a new hashtable
* util_htset(table, key, value, sizeof(value)) -- to set something in the table
* util_htget(table, key) -- to get something from the table
* util_htdel(table) -- to delete the table
*/
hash_table_t *util_htnew(size_t size) {
hash_table_t *hashtable = nullptr;
if (size < 1)
return nullptr;
if (!(hashtable = (hash_table_t*)mem_a(sizeof(hash_table_t))))
return nullptr;
if (!(hashtable->table = (hash_node_t**)mem_a(sizeof(hash_node_t*) * size))) {
mem_d(hashtable);
return nullptr;
}
hashtable->size = size;
memset(hashtable->table, 0, sizeof(hash_node_t*) * size);
return hashtable;
}
void util_htseth(hash_table_t *ht, const char *key, size_t bin, void *value) {
hash_node_t *newnode = nullptr;
hash_node_t *next = nullptr;
hash_node_t *last = nullptr;
next = ht->table[bin];
while (next && next->key && strcmp(key, next->key) > 0)
last = next, next = next->next;
/* already in table, do a replace */
if (next && next->key && strcmp(key, next->key) == 0) {
next->value = value;
} else {
/* not found, grow a pair man :P */
newnode = _util_htnewpair(key, value);
if (next == ht->table[bin]) {
newnode->next = next;
ht->table[bin] = newnode;
} else if (!next) {
last->next = newnode;
} else {
newnode->next = next;
last->next = newnode;
}
}
}
void util_htset(hash_table_t *ht, const char *key, void *value) {
util_htseth(ht, key, util_hthash(ht, key), value);
}
void *util_htgeth(hash_table_t *ht, const char *key, size_t bin) {
hash_node_t *pair = ht->table[bin];
while (pair && pair->key && strcmp(key, pair->key) > 0)
pair = pair->next;
if (!pair || !pair->key || strcmp(key, pair->key) != 0)
return nullptr;
return pair->value;
}
void *util_htget(hash_table_t *ht, const char *key) {
return util_htgeth(ht, key, util_hthash(ht, key));
}
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin);
void *code_util_str_htgeth(hash_table_t *ht, const char *key, size_t bin) {
hash_node_t *pair;
size_t len, keylen;
int cmp;
keylen = strlen(key);
pair = ht->table[bin];
while (pair && pair->key) {
len = strlen(pair->key);
if (len < keylen) {
pair = pair->next;
continue;
}
if (keylen == len) {
cmp = strcmp(key, pair->key);
if (cmp == 0)
return pair->value;
if (cmp < 0)
return nullptr;
pair = pair->next;
continue;
}
cmp = strcmp(key, pair->key + len - keylen);
if (cmp == 0) {
uintptr_t up = (uintptr_t)pair->value;
up += len - keylen;
return (void*)up;
}
pair = pair->next;
}
return nullptr;
}
/*
* Free all allocated data in a hashtable, this is quite the amount
* of work.
*/
void util_htrem(hash_table_t *ht, void (*callback)(void *data)) {
size_t i = 0;
for (; i < ht->size; ++i) {
hash_node_t *n = ht->table[i];
hash_node_t *p;
/* free in list */
while (n) {
if (n->key)
mem_d(n->key);
if (callback)
callback(n->value);
p = n;
n = p->next;
mem_d(p);
}
}
/* free table */
mem_d(ht->table);
mem_d(ht);
}
void util_htrmh(hash_table_t *ht, const char *key, size_t bin, void (*cb)(void*)) {
hash_node_t **pair = &ht->table[bin];
hash_node_t *tmp;
while (*pair && (*pair)->key && strcmp(key, (*pair)->key) > 0)
pair = &(*pair)->next;
tmp = *pair;
if (!tmp || !tmp->key || strcmp(key, tmp->key) != 0)
return;
if (cb)
(*cb)(tmp->value);
*pair = tmp->next;
mem_d(tmp->key);
mem_d(tmp);
}
void util_htrm(hash_table_t *ht, const char *key, void (*cb)(void*)) {
util_htrmh(ht, key, util_hthash(ht, key), cb);
}
void util_htdel(hash_table_t *ht) {
util_htrem(ht, nullptr);
}

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:]]+$"

File diff suppressed because it is too large Load diff

View file

@ -1,18 +0,0 @@
#define ACCUMULATE_FUNCTION(FUNC) \
[[accumulate]] void FUNC ()
ACCUMULATE_FUNCTION(foo) {
print("hello ");
}
ACCUMULATE_FUNCTION(foo) {
print("accumulation ");
}
ACCUMULATE_FUNCTION(foo) {
print("world\n");
}
void main() {
foo();
}

View file

@ -1,5 +0,0 @@
I: accumulate.qc
D: test function accumulation
T: -execute
C: -std=gmqcc -fftepp
M: hello accumulation world

View file

@ -1,13 +0,0 @@
const float huge = 340282346638528859811704183484516925440.000000; // FLT_MAX
#ifdef DIVBYZERO
const float a = 1.0 / 0.0;
#endif
#ifdef OVERFLOW
const float a = huge * huge;
#endif
#ifdef UNDERFLOW
const float a = 1 / huge;
#endif

View file

@ -1,4 +0,0 @@
I: arithexcept.qc
D: arithmetic exceptions (divide by zero)
T: -fail
C: -std=fteqcc -farithmetic-exceptions -DDIVBYZERO

View file

@ -1,4 +0,0 @@
I: arithexcept.qc
D: arithmetic exceptions (overflow)
T: -fail
C: -std=fteqcc -farithmetic-exceptions -DOVERFLOW

View file

@ -1,4 +0,0 @@
I: arithexcept.qc
D: arithmetic exceptions (underflow)
T: -fail
C: -std=fteqcc -farithmetic-exceptions -DUNDERFLOW

View file

@ -1,7 +1,7 @@
I: arrays2.qc
D: initialized arrays
I: arrays.qc
D: array accessors and functionality
T: -execute
C: -std=fteqcc
M: 10 20 30 40 50 60 70
M: 100 200 300 400 500 600 0
M: Hello World
M: 1001 1101 1201 1301 1401 1501
M: 1001 1101 1201 1301 1401 1501 1601
M: 1001 1101 1201 1301 1401 1501

View file

@ -1,18 +0,0 @@
float glob1[7] = { 10, 20, 30, 40, 50, 60, 70 };
float glob2[7] = { 100, 200, 300, 400, 500, 600 };
string globs[] = { "Hello ", "World" };
void main() {
float i;
print(ftos(glob1[0]));
for (i = 1; i != 7; ++i)
print(" ", ftos(glob1[i]));
print("\n");
print(ftos(glob2[0]));
for (i = 1; i != 7; ++i)
print(" ", ftos(glob2[i]));
print("\n");
print(globs[0], globs[1], "\n");
}

View file

@ -1,7 +0,0 @@
I: arrays.qc
D: array accessors and functionality
T: -execute
C: -std=fteqcc
M: 1001 1101 1201 1301 1401 1501
M: 1001 1101 1201 1301 1401 1501 1601
M: 1001 1101 1201 1301 1401 1501

View file

@ -3,35 +3,12 @@ void main() {
float b; b = 1;
float c; c = 1;
float d; d = 1;
vector e; e = '1 1 1';
vector f; f = '1 1 1';
#ifdef __STD_FTEQCC__
a &~= 1; // 0
#else
a &= ~1; // 0
#endif
#ifdef __STD_GMQCC__
b &= ~1; // 0
c &= ~d; // 0
#else
b &~= 1; // 0
c &~= 1; // 0
#endif
#ifdef __STD_FTEQCC__
f &~= e; // '0 0 0'
#else
f &= ~e; // '0 0 0'
#endif
#ifdef __STD_GMQCC__
e &= ~e; // '0 0 0'
#else
e &~= e; // '0 0 0'
#endif
print("a: ", ftos(a), "\nb: ",
ftos(b), "\nc: ",
ftos(c), "\n");
print("e: ", vtos(e), "\n");
print("f: ", vtos(f), "\n");
}

View file

@ -1,11 +1,9 @@
# used to test the builtins
I: bitnot.qc
D: test bitwise not operators (fteqcc operators)
D: test bitwise not operators
T: -execute
C: -std=fteqcc
C: -std=gmqcc
E: $null
M: a: 0
M: b: 0
M: c: 0
M: e: '0 0 0'
M: f: '0 0 0'

View file

@ -1,11 +0,0 @@
# used to test the builtins
I: bitnot.qc
D: test bitwise not operators (gmqcc operators)
T: -execute
C: -std=gmqcc -fftepp
E: $null
M: a: 0
M: b: 0
M: c: 0
M: e: '0 0 0'
M: f: '0 0 0'

View file

@ -8,5 +8,5 @@ void(float a, float b, float c) main = {
sum(sum(sum(a, b, c), b, sum(a, b, c)), b, sum(a, b, sum(a, b, c))),
sum(sum(a, b, c), b, c));
print(ftos(f), "\n");
};

View file

@ -2,7 +2,7 @@ I: correct-logic.qc
D: vector logic flags
T: -execute
C: -std=fteqcc -fshort-logic
M: ! & | i N
M: ! & | i N
M: 0, 0 -> 1 0 0 0 1
M: 0, x -> 1 0 1 0 1
M: x, 0 -> 0 0 1 1 0

View file

@ -2,7 +2,7 @@ I: correct-logic.qc
D: vector logic flags
T: -execute
C: -std=fteqcc
M: ! & | i N
M: ! & | i N
M: 0, 0 -> 1 0 0 0 1
M: 0, x -> 1 0 1 0 1
M: x, 0 -> 0 0 1 1 0

View file

@ -2,7 +2,7 @@ I: correct-logic.qc
D: vector logic flags
T: -execute
C: -std=fteqcc -fcorrect-logic -fshort-logic
M: ! & | i N
M: ! & | i N
M: 0, 0 -> 1 0 0 0 1
M: 0, x -> 1 0 1 0 1
M: x, 0 -> 0 0 1 1 0

View file

@ -2,7 +2,7 @@ I: correct-logic.qc
D: vector logic flags
T: -execute
C: -std=fteqcc -fcorrect-logic
M: ! & | i N
M: ! & | i N
M: 0, 0 -> 1 0 0 0 1
M: 0, x -> 1 0 1 0 1
M: x, 0 -> 0 0 1 1 0

View file

@ -3,19 +3,17 @@
// builtins. I no event shall you even consider adding
// these individually per test.
void (string str, ...) print = #1;
string (float val) ftos = #2;
entity () spawn = #3;
void (entity ent) kill = #4;
string (vector vec) vtos = #5;
void (string str) error = #6;
float (vector vec) vlen = #7;
string (entity ent) etos = #8;
float (string str) stof = #9;
string (...) strcat = #10;
float (string str1, string str2) strcmp = #11;
vector (vector vec) normalize = #12;
float (float val) sqrt = #13;
float (float val) floor = #14;
float (float val1, float val2) pow = #15;
vector (string str) stov = #16;
void (string, ...) print = #1;
string (float) ftos = #2;
entity () spawn = #3;
void (entity) kill = #4;
string (vector) vtos = #5;
void (string) error = #6;
float (vector) vlen = #7;
string (entity) etos = #8;
float (string) stof = #9;
string (...) strcat = #10;
float (string, string) strcmp = #11;
vector (vector) normalize = #12;
float (float) sqrt = #13;
float (float) floor = #14;

View file

@ -1,27 +0,0 @@
entity self;
.float f;
..float fp;
...float fpp;
void try(entity e, ...float pp) {
print("and: ", ftos( e.(e.(e.pp)) ), "\n");
}
typedef float Float;
void try2(entity e, ...Float pp) {
print("and: ", ftos( e.(e.(e.pp)) ), "\n");
}
// whereas the varargs are tested in vararg tests
void main() {
self = spawn();
self.f = 123;
self.fp = f;
self.fpp = fp;
print(ftos( self.(self.fp) ), "\n");
print(ftos( self.(self.(self.fpp)) ), "\n");
try(self, fpp);
try2(self, fpp);
}

View file

@ -1,8 +0,0 @@
I: dots.qc
D: TOKEN_DOTS disambiguation
T: -execute
C: -std=fteqcc
M: 123
M: 123
M: and: 123
M: and: 123

View file

@ -1,13 +1,13 @@
void main() {
float hundy = __builtin_pow(10, 2); // 10^2 == 100
print(ftos(hundy), "\n"); // prints: 100
float pow(float x, float y) {
return __builtin_pow(x, y);
}
hundy = pow(10, 2);
print(ftos(hundy), "\n");
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
print(ftos(pow(hundy, 2)), "\n"); // prints: 100
hundy = 10.0f;
print(ftos(__builtin_exp(hundy)), "\n"); // prints: 22026.5

View file

@ -6,6 +6,4 @@ C: -std=gmqcc
E: $null
M: 100
M: 100
M: 100
M: 100
M: 22026.5

View file

@ -1,13 +0,0 @@
.float field;
.float getfield() {
return field;
}
void() main = {
entity e = spawn();
e.field = 42;
print(ftos(e.(getfield())), "\n");
.float memptr = getfield();
print(ftos(e.memptr), "\n");
}

View file

@ -1,6 +0,0 @@
I: fieldfuncs.qc
D: test fields with functions
T: -execute
C: -std=fte
M: 42
M: 42

Some files were not shown because too many files have changed in this diff Show more