mirror of
https://github.com/DarkPlacesEngine/gmqcc.git
synced 2025-04-20 08:50:43 +00:00
Compare commits
8 commits
Author | SHA1 | Date | |
---|---|---|---|
|
de24486e54 | ||
|
e26d1e985a | ||
|
cac827d763 | ||
|
9a87c5aac7 | ||
|
b2165f718b | ||
|
9348649af4 | ||
|
11ffe61314 | ||
|
b54f0a64de |
196 changed files with 18115 additions and 27032 deletions
15
.gitignore
vendored
15
.gitignore
vendored
|
@ -1,15 +0,0 @@
|
|||
*.o
|
||||
*.d
|
||||
*.orig
|
||||
*.rej
|
||||
*.patch
|
||||
*.diff
|
||||
*.exe
|
||||
|
||||
gmqcc
|
||||
gmqpak
|
||||
qcvm
|
||||
testsuite
|
||||
|
||||
build/
|
||||
.idea/
|
13
AUTHORS
13
AUTHORS
|
@ -1,10 +1,3 @@
|
|||
Authors:
|
||||
Dale `graphitemaster` Weiler - Charismatic Visionary / Programmer
|
||||
Wolfgang `Blub\w` 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
|
||||
GMQCC brought to you by:
|
||||
Dale Weiler
|
||||
Wolfgang Bumiller
|
||||
|
|
|
@ -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)
|
39
INSTALL
Normal file
39
INSTALL
Normal file
|
@ -0,0 +1,39 @@
|
|||
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 an ArchLinux PKGBUILD file:
|
||||
|
||||
make DESTDIR=$pkgdir install
|
2
LICENSE
2
LICENSE
|
@ -1,4 +1,4 @@
|
|||
Copyright (C) 2012, 2013, 2014, 2015
|
||||
Copyright (C) 2012
|
||||
Dale Weiler
|
||||
Wolfgang Bumiller
|
||||
|
||||
|
|
279
Makefile
279
Makefile
|
@ -1,204 +1,107 @@
|
|||
# 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 :=
|
||||
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
|
||||
CFLAGS += -Wall -Wextra -I. -pedantic-errors
|
||||
#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-cast-align
|
||||
endif
|
||||
ifeq ($(track), no)
|
||||
CFLAGS += -DNOTRACK
|
||||
endif
|
||||
|
||||
OBJ_D = util.o code.o ast.o ir.o conout.o ftepp.o opts.o file.o
|
||||
OBJ_T = test.o util.o conout.o file.o
|
||||
OBJ_C = main.o lexer.o parser.o file.o
|
||||
OBJ_X = exec-standalone.o util.o conout.o file.o
|
||||
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
else
|
||||
CXXFLAGS += -fomit-frame-pointer
|
||||
endif
|
||||
|
||||
# Highest optimization flag in release builds.
|
||||
CXXFLAGS += -O3
|
||||
QCVM = qcvm
|
||||
GMQCC = gmqcc
|
||||
TESTSUITE = testsuite
|
||||
endif
|
||||
endif
|
||||
|
||||
# Sanitizer selection
|
||||
ifeq ($(ASAN),1)
|
||||
CXXFLAGS += -fsanitize=address
|
||||
endif
|
||||
ifeq ($(UBSAN),1)
|
||||
CXXFLAGS += -fsanitize=undefined
|
||||
endif
|
||||
#standard rules
|
||||
default: all
|
||||
%.o: %.c
|
||||
$(CC) -c $< -o $@ $(CFLAGS)
|
||||
|
||||
#
|
||||
# Dependency flags
|
||||
#
|
||||
DEPFLAGS := -MMD
|
||||
DEPFLAGS += -MP
|
||||
exec-standalone.o: exec.c
|
||||
$(CC) -c $< -o $@ $(CFLAGS) -DQCVM_EXECUTOR=1
|
||||
|
||||
#
|
||||
# Linker flags
|
||||
#
|
||||
LDFLAGS :=
|
||||
$(QCVM): $(OBJ_X)
|
||||
$(CC) -o $@ $^ $(CFLAGS) -lm
|
||||
|
||||
# Remove unreferenced sections
|
||||
ifeq ($(UNUSED),1)
|
||||
LDFLAGS += -Wl,--gc-sections
|
||||
endif
|
||||
$(GMQCC): $(OBJ_C) $(OBJ_D)
|
||||
$(CC) -o $@ $^ $(CFLAGS)
|
||||
|
||||
# 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
|
||||
else
|
||||
STRIP := strip
|
||||
endif
|
||||
$(TESTSUITE): $(OBJ_T)
|
||||
$(CC) -o $@ $^ $(CFLAGS)
|
||||
|
||||
all: $(GMQCC) $(QCVM) $(TESTSUITE)
|
||||
|
||||
# Build artifact directories.
|
||||
$(DEPDIR):
|
||||
@mkdir -p $(DEPDIR)
|
||||
$(OBJDIR):
|
||||
@mkdir -p $(OBJDIR)
|
||||
|
||||
$(OBJDIR)/%.o: %.cpp $(DEPDIR)/%.d | $(OBJDIR) $(DEPDIR)
|
||||
$(CXX) -MT $@ $(DEPFLAGS) -MF $(DEPDIR)/$*.Td $(CXXFLAGS) -c -o $@ $<
|
||||
@mv -f $(DEPDIR)/$*.Td $(DEPDIR)/$*.d
|
||||
|
||||
$(GMQCC): $(filter %.o,$(GSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
$(QCVM): $(filter %.o,$(QSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
$(TESTSUITE): $(filter %.o,$(TSRCS:%.cpp=$(OBJDIR)/%.o))
|
||||
$(CXX) $^ $(LDFLAGS) -o $@
|
||||
$(STRIP) $@
|
||||
|
||||
# Determine if the tests should be run.
|
||||
RUNTESTS := true
|
||||
ifdef TESTSUITE
|
||||
RUNTESTS := ./$(TESTSUITE)
|
||||
endif
|
||||
|
||||
test: $(QCVM) $(TESTSUITE)
|
||||
@$(RUNTESTS)
|
||||
check: all
|
||||
@ ./$(TESTSUITE)
|
||||
|
||||
clean:
|
||||
rm -rf $(DEPDIR) $(OBJDIR)
|
||||
rm -f *.o $(GMQCC) $(QCVM) $(TESTSUITE) *.dat
|
||||
|
||||
.PHONY: test clean $(DEPDIR) $(OBJDIR)
|
||||
# deps
|
||||
$(OBJ_D) $(OBJ_C) $(OBJ_X): gmqcc.h opts.def
|
||||
main.o: lexer.h
|
||||
parser.o: ast.h lexer.h
|
||||
lexer.o: lexer.h
|
||||
ast.o: ast.h ir.h
|
||||
ir.o: ir.h
|
||||
|
||||
# Dependencies
|
||||
$(filter %.d,$(GSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
|
||||
$(filter %.d,$(QSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
|
||||
$(filter %.d,$(TSRCS:%.cpp=$(DEPDIR)/%.d)):
|
||||
include $(wildcard $@)
|
||||
#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 -m755 doc/gmqcc.1 $(DESTDIR)$(MANDIR)/man1/
|
||||
install -m755 doc/qcvm.1 $(DESTDIR)$(MANDIR)/man1/
|
||||
|
|
10
README
10
README
|
@ -1 +1,9 @@
|
|||
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 documentation:
|
||||
See the manpages, or visit the documentation online at
|
||||
http://graphitemaster.github.com/gmqcc/doc.html
|
||||
|
|
157
TODO
Normal file
157
TODO
Normal file
|
@ -0,0 +1,157 @@
|
|||
GMQCC is quite feature compleat. 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 determinging which values are computed by equivalent expressions.
|
||||
Simaler to Common Subexpression Elimination (CSE), however expressions
|
||||
are determined via underlying equivalnce, opposed to lexically identical
|
||||
expressions (CSE).
|
||||
|
||||
Spare Conditional Constant Propogation:
|
||||
Simultaneously remove dead code and propogates constants. This is
|
||||
not the same as indivial dead code elimination and constant propogation
|
||||
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 Recrusion:
|
||||
Tail recrusive 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 subsitution.
|
||||
|
||||
Classes:
|
||||
Like C++, but minus the stupidity:
|
||||
- No type operator overloads
|
||||
- Keep operator overloading for basic operators though.
|
||||
- No inheritence
|
||||
- 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 Subsitution:
|
||||
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 abilit 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 dissasembly:
|
||||
Proper dissasembly of compiled .dat files. Annotated if possible
|
||||
when -g (is used during compilation)
|
||||
|
||||
Debugging:
|
||||
A step-through debuger -d (with seperate compilation as well)
|
||||
Called -> qcdb Optionally alias to qcvm -d :)
|
||||
|
||||
We should beable to see the assembly and source it matches to
|
||||
and the state of OFS_* and calls.
|
||||
|
||||
Testsuite:
|
||||
The followiung 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 paramaters 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
|
||||
entierly sure if it makes sense.
|
||||
|
||||
|
18
algo.h
18
algo.h
|
@ -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
|
259
code.c
Normal file
259
code.c
Normal file
|
@ -0,0 +1,259 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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"
|
||||
|
||||
prog_section_statement *code_statements;
|
||||
int *code_linenums;
|
||||
prog_section_def *code_defs;
|
||||
prog_section_field *code_fields;
|
||||
prog_section_function *code_functions;
|
||||
int *code_globals;
|
||||
char *code_chars;
|
||||
uint16_t code_crc;
|
||||
uint32_t code_entfields;
|
||||
|
||||
void code_push_statement(prog_section_statement *stmt, int linenum)
|
||||
{
|
||||
vec_push(code_statements, *stmt);
|
||||
vec_push(code_linenums, linenum);
|
||||
}
|
||||
|
||||
void code_pop_statement()
|
||||
{
|
||||
vec_pop(code_statements);
|
||||
vec_pop(code_linenums);
|
||||
}
|
||||
|
||||
void code_init() {
|
||||
prog_section_function empty_function = {0,0,0,0,0,0,0,{0}};
|
||||
prog_section_statement empty_statement = {0,{0},{0},{0}};
|
||||
prog_section_def empty_def = {0, 0, 0};
|
||||
int i = 0;
|
||||
|
||||
code_entfields = 0;
|
||||
|
||||
/*
|
||||
* 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(&empty_statement, 0);
|
||||
vec_push(code_defs, empty_def);
|
||||
vec_push(code_fields, empty_def);
|
||||
}
|
||||
|
||||
uint32_t code_genstring(const char *str)
|
||||
{
|
||||
uint32_t off = vec_size(code_chars);
|
||||
while (*str) {
|
||||
vec_push(code_chars, *str);
|
||||
++str;
|
||||
}
|
||||
vec_push(code_chars, 0);
|
||||
return off;
|
||||
}
|
||||
|
||||
uint32_t code_cachedstring(const char *str)
|
||||
{
|
||||
size_t s = 0;
|
||||
/* We could implement knuth-morris-pratt or something
|
||||
* and also take substrings, but I'm uncomfortable with
|
||||
* pointing to subparts of strings for the sake of clarity...
|
||||
*/
|
||||
while (s < vec_size(code_chars)) {
|
||||
if (!strcmp(str, code_chars + s))
|
||||
return s;
|
||||
while (code_chars[s]) ++s;
|
||||
++s;
|
||||
}
|
||||
return code_genstring(str);
|
||||
}
|
||||
|
||||
qcint code_alloc_field (size_t qcsize)
|
||||
{
|
||||
qcint pos = (qcint)code_entfields;
|
||||
code_entfields += qcsize;
|
||||
return pos;
|
||||
}
|
||||
|
||||
bool code_write(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.forcecrc)
|
||||
code_header.crc16 = opts.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 lnotype = *(unsigned int*)"LNOF";
|
||||
uint32_t version = 1;
|
||||
|
||||
fp = 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 (file_write(&lnotype, sizeof(lnotype), 1, fp) != 1 ||
|
||||
file_write(&version, sizeof(version), 1, fp) != 1 ||
|
||||
file_write(&code_header.defs.length, sizeof(code_header.defs.length), 1, fp) != 1 ||
|
||||
file_write(&code_header.globals.length, sizeof(code_header.globals.length), 1, fp) != 1 ||
|
||||
file_write(&code_header.fields.length, sizeof(code_header.fields.length), 1, fp) != 1 ||
|
||||
file_write(&code_header.statements.length, sizeof(code_header.statements.length), 1, fp) != 1 ||
|
||||
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");
|
||||
}
|
||||
|
||||
file_close(fp);
|
||||
fp = NULL;
|
||||
}
|
||||
|
||||
fp = file_open(filename, "wb");
|
||||
if (!fp)
|
||||
return false;
|
||||
|
||||
if (1 != file_write(&code_header, sizeof(prog_header) , 1 , fp) ||
|
||||
vec_size(code_statements) != file_write(code_statements, sizeof(prog_section_statement), vec_size(code_statements), fp) ||
|
||||
vec_size(code_defs) != file_write(code_defs, sizeof(prog_section_def) , vec_size(code_defs) , fp) ||
|
||||
vec_size(code_fields) != file_write(code_fields, sizeof(prog_section_field) , vec_size(code_fields) , fp) ||
|
||||
vec_size(code_functions) != file_write(code_functions, sizeof(prog_section_function) , vec_size(code_functions) , fp) ||
|
||||
vec_size(code_globals) != file_write(code_globals, sizeof(int32_t) , vec_size(code_globals) , fp) ||
|
||||
vec_size(code_chars) != file_write(code_chars, 1 , vec_size(code_chars) , fp))
|
||||
{
|
||||
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);
|
||||
file_close(fp);
|
||||
return true;
|
||||
}
|
348
code.cpp
348
code.cpp
|
@ -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;
|
||||
}
|
433
conout.c
Normal file
433
conout.c
Normal file
|
@ -0,0 +1,433 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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 _MSC_VER
|
||||
#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(const char *str, FILE *h) {
|
||||
/* state for translate */
|
||||
int acolor;
|
||||
int wcolor;
|
||||
int icolor;
|
||||
|
||||
int state;
|
||||
|
||||
/* 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 {
|
||||
file_putc(*str, h);
|
||||
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 _MSC_VER
|
||||
ln = vfprintf(handle, fmt, va);
|
||||
#else
|
||||
{
|
||||
char data[4096];
|
||||
memset(data, 0, sizeof(data));
|
||||
vsnprintf(data, sizeof(data), fmt, va);
|
||||
ln = (GMQCC_IS_DEFINE(handle)) ? win_fputs(data, handle) : file_puts(data, handle);
|
||||
}
|
||||
#endif
|
||||
return ln;
|
||||
}
|
||||
|
||||
/**********************************************************************
|
||||
* EXPOSED INTERFACE BEGINS
|
||||
*********************************************************************/
|
||||
|
||||
void con_close() {
|
||||
if (!GMQCC_IS_DEFINE(console.handle_err))
|
||||
file_close(console.handle_err);
|
||||
if (!GMQCC_IS_DEFINE(console.handle_out))
|
||||
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 = 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 = file_open(err, "w"))) return 0;
|
||||
|
||||
/* no buffering */
|
||||
setvbuf(console.handle_out, NULL, _IONBF, 0);
|
||||
setvbuf(console.handle_err, NULL, _IONBF, 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;
|
||||
|
||||
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)
|
||||
{
|
||||
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);
|
||||
|
||||
if (OPTS_WERROR(warntype)) {
|
||||
++compile_errors;
|
||||
lvl = LVL_ERROR;
|
||||
}
|
||||
else
|
||||
++compile_warnings;
|
||||
|
||||
con_vprintmsg_c(lvl, ctx.file, ctx.line, ((lvl == LVL_ERROR) ? "error" : "warning"), fmt, ap, warn_name);
|
||||
|
||||
return OPTS_WERROR(warntype);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
226
conout.cpp
226
conout.cpp
|
@ -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;
|
||||
}
|
857
doc/gmqcc.1
857
doc/gmqcc.1
File diff suppressed because it is too large
Load diff
155
doc/qcvm.1
155
doc/qcvm.1
|
@ -1,107 +1,92 @@
|
|||
.\" qcvm mdoc manpage
|
||||
.Dd January 31, 2013
|
||||
.Dt QCVM 1 PRM
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm qcvm
|
||||
.Nd A standalone QuakeC VM binary executor
|
||||
.Sh SYNOPSIS
|
||||
.Nm qcvm
|
||||
.Op Cm options
|
||||
.Op Cm parameters
|
||||
.Ar program-file
|
||||
.Sh DESCRIPTION
|
||||
.Nm qcvm
|
||||
is an executor for QuakeC VM binary files created using a QC
|
||||
.\" Process with groff -man -Tascii file.3
|
||||
.TH QCVM 1 2012-18-12 "" "gmqcc Manual"
|
||||
.SH NAME
|
||||
qcvm \- A standalone QuakeC VM binary executor.
|
||||
.SH SYNOPSIS
|
||||
.B qcvm
|
||||
[\fIOPTIONS\fR] [\fIPARAMS\fR] [\fIfile\fR]
|
||||
.SH DESCRIPTION
|
||||
qcvm is an executor for QuakeC VM binary files created using a QC
|
||||
compiler such as gmqcc(1) or fteqcc. It provides a small set of
|
||||
builtin functions, and by default executes the
|
||||
.Fn main
|
||||
function if there is one. Some options useful for debugging are
|
||||
available as well.
|
||||
.Sh OPTIONS
|
||||
builtin functions, and by default executes the \fImain\fR function if
|
||||
there is one. Some options useful for debugging are available as well.
|
||||
.SH OPTIONS
|
||||
There are 2 types of options. Options for the executor, and parameter
|
||||
options used to add parameters which are passed to the main function
|
||||
on execution.
|
||||
.Bl -tag -width Ds
|
||||
.It Fl h , Fl -help
|
||||
.TP
|
||||
.B "-h, --help"
|
||||
Show a usage message and exit.
|
||||
.It Fl trace
|
||||
.TP
|
||||
.B "-trace"
|
||||
Trace the execution. Each instruction will be printed to stdout before
|
||||
executing it.
|
||||
.It Fl profile
|
||||
.TP
|
||||
.B "-profile"
|
||||
Perform some profiling. This is currently not really implemented, the
|
||||
option is available nonetheless.
|
||||
.It Fl info
|
||||
.TP
|
||||
.B "-info"
|
||||
Print information from the program's header instead of executing.
|
||||
.It Fl disasm
|
||||
.TP
|
||||
.B "-disasm"
|
||||
Disassemble the program by function instead of executing.
|
||||
.It Fl disasm-func Ar function
|
||||
Search for and disassemble the given function.
|
||||
.It Fl printdefs
|
||||
.TP
|
||||
.B "-printdefs"
|
||||
List all entries from the program's defs-section. Effectively
|
||||
listing all the global variables of the program.
|
||||
This option disables execution.
|
||||
.It Fl printfields
|
||||
.TP
|
||||
.B "-printfields"
|
||||
List all entries from the program's fields-section. Listing all
|
||||
entity-fields declared in the program.
|
||||
This option disables execution.
|
||||
.It Fl printfuns
|
||||
.TP
|
||||
.B "-printfuns"
|
||||
List functions and some information about their parameters.
|
||||
This option disables execution. With a verbosity level of 1, builtin
|
||||
numbers are printed. With a verbosity of 2, the function's sizes are
|
||||
printed as well. This takes a little longer since the size is found by
|
||||
searching for a
|
||||
.Ql DONE
|
||||
instruction in the code.
|
||||
.It Fl v
|
||||
Increase verbosity level, can be used multiple times.
|
||||
.It Fl vector Ar 'x y z'
|
||||
Append a vector parameter to be passed to
|
||||
.Fn main Ns .
|
||||
.It Fl float Ar number
|
||||
Append a float parameter to be passed to
|
||||
.Fn main Ns .
|
||||
.It Fl string Ar 'text'
|
||||
Append a string parameter to be passed to
|
||||
.Fn main Ns .
|
||||
.El
|
||||
.Sh BUILTINS
|
||||
This option disables execution.
|
||||
.TP
|
||||
.BI "-vector """ "x y z" """"
|
||||
Append a vector parameter to be passed to \fImain\fR.
|
||||
.TP
|
||||
.BI "-float " number
|
||||
Append a float parameter to be passed to \fImain\fR.
|
||||
.TP
|
||||
.BI "-string """ "text" """"
|
||||
Append a string parameter to be passed to \fImain\fR.
|
||||
.SH BUILTINS
|
||||
The following builtin functions are available:
|
||||
.Bl -ohang
|
||||
.It Li 1) void print(string...) = #1;
|
||||
.Bd -unfilled -offset indent -compact
|
||||
.TP
|
||||
.RI "1) " void " print(" string... ") = " "#1" ;
|
||||
Print the passed strings to stdout. At most 8 strings are allowed.
|
||||
.Ed
|
||||
.It Li 2) string ftos(float) = #2;
|
||||
.D1 Convert a float to a string.
|
||||
.It Li 3) entity spawn() = #3;
|
||||
.D1 Spawn an entity.
|
||||
.It Li 4) void remove(entity) = #4;
|
||||
.D1 Remove an entity.
|
||||
.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)
|
||||
.It Li 7) float vlen(vector) = #7;
|
||||
.D1 Get the length of a vector.
|
||||
.It Li 8) string etos(entity) = #8;
|
||||
.D1 Get the entity ID as string.
|
||||
.It Li 9) float stof(string) = #9;
|
||||
.D1 Convert a string to a float.
|
||||
.It Li 10) string strcat(string, string) = #10;
|
||||
.D1 Concatenate two strings, returning a tempstring.
|
||||
.It Li 11) float strcmp(string, string) = #11;
|
||||
.Li 12) float strncmp(string, string, float) = #11;
|
||||
.D1 Compare two strings. Returns the same as the corresponding C functions.
|
||||
.It Li 12) vector normalize(vector) = #12;
|
||||
.D1 Normalize a vector so its length is 1.
|
||||
.It Li 13) float sqrt(float) = #13;
|
||||
.D1 Get a value's square root.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr gmqcc 1
|
||||
.Sh AUTHOR
|
||||
See <http://graphitemaster.github.com/gmqcc>.
|
||||
.Sh BUGS
|
||||
.TP
|
||||
.RI "2) " string " ftos(" float ") = " "#2" ;
|
||||
Convert a float to a string.
|
||||
.TP
|
||||
.RI "3) " entity " spawn() = " "#3" ;
|
||||
Spawn an entity.
|
||||
.TP
|
||||
.RI "4) " void " remove(" entity ") = " "#4" ;
|
||||
Remove an entity.
|
||||
.TP
|
||||
.RI "5) " string " vtos(" vector ") = " "#5" ;
|
||||
Convert a vector to a string.
|
||||
.TP
|
||||
.RI "6) " void " error(" string... ") = " "#6" ;
|
||||
Print at most 8 strings to stdout and then exit with an error.
|
||||
.TP
|
||||
.RI "7) " float " vlen(" vector ") = " "#7" ;
|
||||
Get the length of a vector.
|
||||
.TP
|
||||
.RI "8) " string " etos(" entity ") = " "#8" ;
|
||||
Get the entity ID as string.
|
||||
.TP
|
||||
.RI "9) " float " stof(" string ") = " "#9" ;
|
||||
Convert a string to a float.
|
||||
.SH BUGS
|
||||
Please report bugs on <http://github.com/graphitemaster/gmqcc/issues>,
|
||||
or see <http://graphitemaster.github.com/gmqcc> on how to contact us.
|
||||
.SH SEE ALSO
|
||||
.IR gmqcc (1)
|
||||
.SH AUTHOR
|
||||
See <http://graphitemaster.github.com/gmqcc>.
|
||||
|
|
1029
exec.cpp → exec.c
1029
exec.cpp → exec.c
File diff suppressed because it is too large
Load diff
214
file.c
Normal file
214
file.c
Normal file
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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
|
||||
/* {{{ */
|
||||
/*
|
||||
* 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");
|
||||
abort();
|
||||
}
|
||||
|
||||
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 *file_open(const char *filename, const char *mode) {
|
||||
FILE *handle = NULL;
|
||||
file_init();
|
||||
|
||||
return ((fopen_s(&handle, filename, mode) != 0) ? NULL : handle;
|
||||
}
|
||||
|
||||
size_t file_read(void *buffer, size_t size, size_t count, FILE *fp) {
|
||||
file_init();
|
||||
return fread_s(buffer, size*count, size, count, fp);
|
||||
}
|
||||
|
||||
int 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 *file_open(const char *filename, const char *mode) {
|
||||
return fopen(filename, mode);
|
||||
}
|
||||
|
||||
size_t file_read(void *buffer, size_t size, size_t count, FILE *fp) {
|
||||
return fread(buffer, size, count, fp);
|
||||
}
|
||||
|
||||
int 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 file_close(FILE *fp) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
fclose (fp);
|
||||
}
|
||||
|
||||
size_t 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 file_error(FILE *fp) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return ferror(fp);
|
||||
}
|
||||
|
||||
int file_getc(FILE *fp) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return fgetc(fp);
|
||||
}
|
||||
|
||||
int file_puts(FILE *fp, const char *str) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return fputs(str, fp);
|
||||
}
|
||||
|
||||
int file_seek(FILE *fp, long int off, int whence) {
|
||||
/* Invokes file_exception on windows if fp is null */
|
||||
return fseek(fp, off, whence);
|
||||
}
|
||||
|
||||
/*
|
||||
* Implements libc getline for systems that don't have it, which is
|
||||
* assmed all. This works the same as getline().
|
||||
*/
|
||||
int 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 = 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);
|
||||
}
|
121
fold.h
121
fold.h
|
@ -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
|
File diff suppressed because it is too large
Load diff
|
@ -1,745 +1,159 @@
|
|||
#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.
|
||||
# Enabling this can potentially reduces code size by overlapping
|
||||
# locals where possible.
|
||||
OVERLAP_LOCALS = false
|
||||
|
||||
DARKPLACES_STRING_TABLE_BUG = true
|
||||
# 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
|
||||
|
||||
#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.
|
||||
# Enabling this allows the use of the FTEQ preprocessor, as well as
|
||||
# additional preprocessing directives such as #error and #warning.
|
||||
FTEPP = true
|
||||
|
||||
ADJUST_VECTOR_FIELDS = 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
|
||||
|
||||
#Enable a partially fteqcc-compatible preprocessor. It supports
|
||||
#all the features used in the Xonotic codebase. If you need more,
|
||||
#write a ticket.
|
||||
# Enabling this allows perl-like evaluation/logic.
|
||||
PERL_LOGIC = true
|
||||
|
||||
FTEPP = 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
|
||||
|
||||
#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.
|
||||
# Enabling this allows function types to be assignable even if their
|
||||
# signatures are invariant of each other.
|
||||
ASSIGN_FUNCTION_TYPES = false
|
||||
|
||||
FTEPP_PREDEFS = 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
|
||||
|
||||
#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
|
||||
|
||||
# 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.
|
||||
TOO_FEW_PARAMETERS = 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
|
||||
|
||||
|
||||
#Warn when the initialization of a local variable turns the vari‐
|
||||
#able into a constant. This is default behaviour unless
|
||||
#-finitialized-nonconstants is used.
|
||||
|
||||
LOCAL_CONSTANTS = false
|
||||
|
||||
|
||||
#There are only 2 known global variables of type void:
|
||||
#‘end_sys_globals’ and ‘end_sys_fields’. Any other void-variable
|
||||
#will warn.
|
||||
|
||||
VOID_VARIABLES = false
|
||||
|
||||
|
||||
#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
|
||||
# Enables warnings about unreachable code.
|
||||
UNREACHABLE_CODE = true
|
||||
|
||||
# Enables preprocessor "#warnings"
|
||||
CPP = 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.
|
||||
|
||||
TAIL_RECURSION = 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.
|
||||
|
||||
OVERLAP_LOCALS = 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
|
||||
# Enables tail-call optimizations.
|
||||
TAIL_CALLS = true
|
||||
|
|
2048
intrin.cpp
2048
intrin.cpp
File diff suppressed because it is too large
Load diff
74
intrin.h
74
intrin.h
|
@ -1,74 +0,0 @@
|
|||
#ifndef GMQCC_INTRIN_HDR
|
||||
#define GMQCC_INTRIN_HDR
|
||||
#include "gmqcc.h"
|
||||
|
||||
struct fold;
|
||||
struct parser_t;
|
||||
|
||||
struct ast_function;
|
||||
struct ast_expression;
|
||||
struct ast_value;
|
||||
|
||||
struct intrin;
|
||||
|
||||
struct intrin_func_t {
|
||||
ast_expression *(intrin::*function)();
|
||||
const char *name;
|
||||
const char *alias;
|
||||
size_t args;
|
||||
};
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
504
ir.h
504
ir.h
|
@ -1,334 +1,336 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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"
|
||||
|
||||
/*
|
||||
* 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;
|
||||
/* ir_value */
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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, 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 GMQCC_WARN 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 eid;
|
||||
bool is_return;
|
||||
size_t run_id;
|
||||
|
||||
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_value* ir_block_create_add(ir_block*, lex_ctx, const char *label, ir_value *l, ir_value *r);
|
||||
ir_value* ir_block_create_sub(ir_block*, lex_ctx, const char *label, ir_value *l, ir_value *r);
|
||||
ir_value* ir_block_create_mul(ir_block*, lex_ctx, const char *label, ir_value *l, ir_value *r);
|
||||
ir_value* ir_block_create_div(ir_block*, lex_ctx, const char *label, ir_value *l, ir_value *r);
|
||||
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;
|
||||
int builtin;
|
||||
|
||||
std::vector<std::unique_ptr<ir_block>> m_blocks;
|
||||
ir_value *value;
|
||||
|
||||
/*
|
||||
* values generated from operations
|
||||
/* 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;
|
||||
|
||||
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;
|
||||
|
||||
/* vararg support: */
|
||||
size_t m_max_varargs = 0;
|
||||
};
|
||||
struct ir_builder_s *owner;
|
||||
} ir_function;
|
||||
|
||||
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 *generateExtparamProto();
|
||||
void generateExtparam();
|
||||
const char **filenames;
|
||||
qcint *filestrings;
|
||||
/* we cache the #IMMEDIATE string here */
|
||||
qcint str_immediate;
|
||||
} ir_builder;
|
||||
|
||||
ir_value *literalFloat(float value, bool add_to_list);
|
||||
ir_builder* ir_builder_new(const char *modulename);
|
||||
void ir_builder_delete(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;
|
||||
bool ir_builder_set_name(ir_builder *self, const char *name);
|
||||
|
||||
ht m_htfunctions;
|
||||
ht m_htglobals;
|
||||
ht m_htfields;
|
||||
ir_function* ir_builder_get_function(ir_builder*, const char *fun);
|
||||
ir_function* ir_builder_create_function(ir_builder*, const char *name, int outtype);
|
||||
|
||||
// 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_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);
|
||||
|
||||
// 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;
|
||||
bool ir_builder_generate(ir_builder *self, const char *filename);
|
||||
|
||||
std::vector<const char*> m_filenames;
|
||||
std::vector<qcint_t> m_filestrings;
|
||||
void ir_builder_dump(ir_builder*, int (*oprintf)(const char*, ...));
|
||||
|
||||
// we cache the #IMMEDIATE string here
|
||||
qcint_t m_str_immediate = 0;
|
||||
|
||||
// there should just be this one nil
|
||||
ir_value *m_nil;
|
||||
ir_value *m_reserved_va_count = nullptr;
|
||||
ir_value *m_coverage_func = nullptr;
|
||||
|
||||
/* 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
|
||||
*/
|
||||
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];
|
||||
/* This code assumes 32 bit floats while generating binary */
|
||||
extern int check_int_and_float_size
|
||||
[ (sizeof(int32_t) == sizeof(qcfloat)) ? 1 : -1 ];
|
||||
|
||||
#endif
|
||||
|
|
File diff suppressed because it is too large
Load diff
343
lexer.h
343
lexer.h
|
@ -1,19 +1,60 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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;
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
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
|
||||
*
|
||||
*/
|
||||
|
@ -35,10 +76,6 @@ enum {
|
|||
TOKEN_ATTRIBUTE_OPEN, /* [[ */
|
||||
TOKEN_ATTRIBUTE_CLOSE, /* ]] */
|
||||
|
||||
TOKEN_VA_ARGS, /* for the ftepp only */
|
||||
TOKEN_VA_ARGS_ARRAY, /* for the ftepp only */
|
||||
TOKEN_VA_COUNT, /* to get the count of vaargs */
|
||||
|
||||
TOKEN_STRINGCONST, /* not the typename but an actual "string" */
|
||||
TOKEN_CHARCONST,
|
||||
TOKEN_VECTORCONST,
|
||||
|
@ -48,11 +85,7 @@ enum {
|
|||
TOKEN_WHITE,
|
||||
TOKEN_EOL,
|
||||
|
||||
/* if we add additional tokens before this, the exposed API
|
||||
* should not be broken anyway, but EOF/ERROR/... should
|
||||
* still be at the bottom
|
||||
*/
|
||||
TOKEN_EOF = 1024,
|
||||
TOKEN_EOF,
|
||||
|
||||
/* We use '< TOKEN_ERROR', so TOKEN_FATAL must come after it and any
|
||||
* other error related tokens as well
|
||||
|
@ -61,13 +94,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 {
|
||||
FILE *file;
|
||||
const char *open_string;
|
||||
size_t open_string_length;
|
||||
size_t open_string_pos;
|
||||
|
@ -75,9 +108,8 @@ struct lex_file {
|
|||
char *name;
|
||||
size_t line;
|
||||
size_t sline; /* line at the start of a token */
|
||||
size_t column;
|
||||
|
||||
int peek[256];
|
||||
char peek[256];
|
||||
size_t peekpos;
|
||||
|
||||
bool eof;
|
||||
|
@ -85,18 +117,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 +148,179 @@ 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, 15, OP_SUFFIX},
|
||||
{ "--", 1, opid3('S','-','-'), ASSOC_LEFT, 15, OP_SUFFIX},
|
||||
{ ".", 2, opid1('.'), ASSOC_LEFT, 15, 0 },
|
||||
{ "(", 0, opid1('('), ASSOC_LEFT, 15, 0 }, /* function call */
|
||||
{ "[", 2, opid1('['), ASSOC_LEFT, 15, 0 }, /* array subscript */
|
||||
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 16, OP_PREFIX, false},
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 16, 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, opid2('-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "++", 1, opid3('+','+','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
{ "--", 1, opid3('-','-','P'), ASSOC_RIGHT, 14, OP_PREFIX },
|
||||
/* { "&", 1, opid2('&','P'), ASSOC_RIGHT, 14, OP_PREFIX }, */
|
||||
|
||||
{ "**", 2, 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, opid1('*'), ASSOC_LEFT, 13, 0 },
|
||||
{ "/", 2, opid1('/'), ASSOC_LEFT, 13, 0 },
|
||||
{ "%", 2, opid1('%'), ASSOC_LEFT, 13, 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},
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0 },
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0 },
|
||||
|
||||
{ "+", 2, opid1('+'), ASSOC_LEFT, 12, 0, true},
|
||||
{ "-", 2, opid1('-'), ASSOC_LEFT, 12, 0, true},
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0 },
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 0 },
|
||||
|
||||
{ "<<", 2, opid2('<','<'), ASSOC_LEFT, 11, 0, true},
|
||||
{ ">>", 2, opid2('>','>'), ASSOC_LEFT, 11, 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, 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, 9, 0 },
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0 },
|
||||
|
||||
{ "==", 2, opid2('=','='), ASSOC_LEFT, 9, 0, true},
|
||||
{ "!=", 2, opid2('!','='), ASSOC_LEFT, 9, 0, true},
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0 },
|
||||
|
||||
{ "&", 2, opid1('&'), ASSOC_LEFT, 8, 0, true},
|
||||
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0 },
|
||||
|
||||
{ "^", 2, opid1('^'), ASSOC_LEFT, 7, 0, true},
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0 },
|
||||
|
||||
{ "|", 2, opid1('|'), ASSOC_LEFT, 6, 0, true},
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0 },
|
||||
|
||||
{ "&&", 2, opid2('&','&'), ASSOC_LEFT, 5, 0, true},
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0 },
|
||||
|
||||
{ "||", 2, opid2('|','|'), ASSOC_LEFT, 4, 0, true},
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0 },
|
||||
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 3, 0, true},
|
||||
{ "=", 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, 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},
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0 },
|
||||
|
||||
{ ":", 0, opid2(':','?'), ASSOC_RIGHT, 1, 0, false},
|
||||
|
||||
{ ",", 2, opid1(','), ASSOC_LEFT, 0, 0, false}
|
||||
{ ",", 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, 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_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},
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0 },
|
||||
|
||||
{ "?", 3, opid2('?',':'), ASSOC_RIGHT, 9, 0, true},
|
||||
{ "=", 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, 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, 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
|
||||
|
|
|
@ -1,20 +1,40 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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"
|
||||
|
||||
/* 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,8 +42,8 @@ static ppitem *ppems = nullptr;
|
|||
|
||||
static const char *app_name;
|
||||
|
||||
static void version(void) {
|
||||
con_out("GMQCC %d.%d.%d Built %s %s\n" GMQCC_DEV_VERSION_STRING,
|
||||
static void version() {
|
||||
con_out("GMQCC %d.%d.%d Built %s %s\n",
|
||||
GMQCC_VERSION_MAJOR,
|
||||
GMQCC_VERSION_MINOR,
|
||||
GMQCC_VERSION_PATCH,
|
||||
|
@ -32,15 +52,15 @@ 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");
|
||||
con_out(" -q, --quiet be less verbose\n");
|
||||
con_out(" -config file use the specified ini file\n");
|
||||
con_out(" -std=standard select one of the following standards\n"
|
||||
" -std=qcc original QuakeC\n"
|
||||
|
@ -61,8 +81,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 +135,18 @@ 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;
|
||||
|
||||
while (!argend && argc > 1) {
|
||||
char *argarg;
|
||||
argitem item;
|
||||
ppitem macro;
|
||||
ppitem macro;
|
||||
|
||||
++argv;
|
||||
--argc;
|
||||
|
@ -136,31 +156,14 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
if (options_long_gcc("std", &argc, &argv, &argarg)) {
|
||||
if (!strcmp(argarg, "gmqcc") || !strcmp(argarg, "default")) {
|
||||
|
||||
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);
|
||||
opts_set(opts.flags, TRANSLATABLE_STRINGS, true);
|
||||
opts_set(opts.flags, INITIALIZED_NONCONSTANTS, true);
|
||||
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;
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, true);
|
||||
opts.standard = COMPILER_GMQCC;
|
||||
|
||||
} else if (!strcmp(argarg, "qcc")) {
|
||||
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
|
||||
opts_set(opts.flags, ASSIGN_FUNCTION_TYPES, true);
|
||||
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCC;
|
||||
opts.standard = COMPILER_QCC;
|
||||
|
||||
} else if (!strcmp(argarg, "fte") || !strcmp(argarg, "fteqcc")) {
|
||||
|
||||
|
@ -168,16 +171,14 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
opts_set(opts.flags, TRANSLATABLE_STRINGS, true);
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
|
||||
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;
|
||||
opts_set(opts.flags, CORRECT_TERNARY, false);
|
||||
opts.standard = COMPILER_FTEQCC;
|
||||
|
||||
} else if (!strcmp(argarg, "qccx")) {
|
||||
|
||||
opts_set(opts.flags, ADJUST_VECTOR_FIELDS, false);
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = COMPILER_QCCX;
|
||||
opts.standard = COMPILER_QCCX;
|
||||
|
||||
} else {
|
||||
con_out("Unknown standard: %s\n", argarg);
|
||||
|
@ -186,25 +187,22 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
continue;
|
||||
}
|
||||
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.forcecrc = true;
|
||||
opts.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;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* show defaults (like pathscale) */
|
||||
if (!strcmp(argv[0]+1, "show-defaults")) {
|
||||
|
@ -230,25 +228,25 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
}
|
||||
|
||||
if (!strcmp(argv[0]+1, "debug")) {
|
||||
OPTS_OPTION_BOOL(OPTION_DEBUG) = true;
|
||||
opts.debug = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "dump")) {
|
||||
OPTS_OPTION_BOOL(OPTION_DUMP) = true;
|
||||
opts.dump = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "dumpfin")) {
|
||||
OPTS_OPTION_BOOL(OPTION_DUMPFIN) = true;
|
||||
opts.dumpfin = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "memchk")) {
|
||||
opts.memchk = true;
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "nocolor")) {
|
||||
con_color(0);
|
||||
continue;
|
||||
}
|
||||
if (!strcmp(argv[0]+1, "coverage")) {
|
||||
OPTS_OPTION_BOOL(OPTION_COVERAGE) = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (argv[0][1]) {
|
||||
/* -h, show usage but exit with 0 */
|
||||
|
@ -262,18 +260,13 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
exit(0);
|
||||
|
||||
case 'E':
|
||||
OPTS_OPTION_BOOL(OPTION_PP_ONLY) = true;
|
||||
opts_set(opts.flags, FTEPP_PREDEFS, true); /* predefs on for -E */
|
||||
opts.pp_only = true;
|
||||
break;
|
||||
|
||||
/* debug turns on -flno */
|
||||
case 'g':
|
||||
opts_setflag("LNO", true);
|
||||
OPTS_OPTION_BOOL(OPTION_G) = true;
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
OPTS_OPTION_BOOL(OPTION_QUIET) = true;
|
||||
opts.g = true;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
|
@ -284,7 +277,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);
|
||||
|
@ -297,7 +290,7 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
case 'f':
|
||||
util_strtocmd(argv[0]+2, argv[0]+2, strlen(argv[0]+2)+1);
|
||||
if (!strcmp(argv[0]+2, "HELP") || *(argv[0]+2) == '?') {
|
||||
con_out("Possible flags:\n\n");
|
||||
con_out("Possible flags:\n");
|
||||
for (itr = 0; itr < COUNT_FLAGS; ++itr) {
|
||||
util_strtononcmd(opts_flag_list[itr].name, buffer, sizeof(buffer));
|
||||
con_out(" -f%s\n", buffer);
|
||||
|
@ -322,37 +315,31 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
for (itr = 0; itr < COUNT_WARNINGS; ++itr) {
|
||||
util_strtononcmd(opts_warn_list[itr].name, buffer, sizeof(buffer));
|
||||
con_out(" -W%s\n", buffer);
|
||||
if (itr == WARN_DEBUG)
|
||||
con_out(" Warnings included by -Wall:\n");
|
||||
}
|
||||
exit(0);
|
||||
}
|
||||
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;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "ERROR") ||
|
||||
!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;
|
||||
}
|
||||
else if (!strncmp(argv[0]+2, "ERROR_", 6)) {
|
||||
|
@ -384,10 +371,9 @@ 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);
|
||||
OPTS_OPTION_U32(OPTION_O) = val;
|
||||
opts_setoptimlevel(val);
|
||||
if (isdigit(argarg[0])) {
|
||||
opts.O = atoi(argarg);
|
||||
opts_setoptimlevel(opts.O);
|
||||
} else {
|
||||
util_strtocmd(argarg, argarg, strlen(argarg)+1);
|
||||
if (!strcmp(argarg, "HELP")) {
|
||||
|
@ -399,10 +385,9 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
exit(0);
|
||||
}
|
||||
else if (!strcmp(argarg, "ALL"))
|
||||
opts_setoptimlevel(OPTS_OPTION_U32(OPTION_O) = 9999);
|
||||
opts_setoptimlevel(opts.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;
|
||||
}
|
||||
|
@ -421,7 +406,7 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
con_out("option -o requires an argument: the output file name\n");
|
||||
return false;
|
||||
}
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
|
||||
opts.output = argarg;
|
||||
opts_output_wasset = true;
|
||||
break;
|
||||
|
||||
|
@ -435,9 +420,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 '-':
|
||||
|
@ -455,18 +437,10 @@ static bool options_parse(int argc, char **argv, bool *has_progs_src) {
|
|||
version();
|
||||
exit(0);
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "quiet")) {
|
||||
OPTS_OPTION_BOOL(OPTION_QUIET) = true;
|
||||
break;
|
||||
}
|
||||
else if (!strcmp(argv[0]+2, "add-info")) {
|
||||
OPTS_OPTION_BOOL(OPTION_ADD_INFO) = true;
|
||||
break;
|
||||
}
|
||||
else {
|
||||
/* All long options with arguments */
|
||||
if (options_long_witharg("output", &argc, &argv, &argarg)) {
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = argarg;
|
||||
opts.output = argarg;
|
||||
opts_output_wasset = true;
|
||||
} else {
|
||||
con_out("Unknown parameter: %s\n", argv[0]);
|
||||
|
@ -493,21 +467,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 = 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 */
|
||||
|
@ -520,38 +494,35 @@ 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;
|
||||
int retval = 0;
|
||||
bool opts_output_free = false;
|
||||
bool operators_free = false;
|
||||
bool progs_src = false;
|
||||
FILE *outfile = 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();
|
||||
}
|
||||
|
||||
if (OPTS_FLAG(TRUE_EMPTY_STRINGS) && OPTS_FLAG(FALSE_EMPTY_STRINGS)) {
|
||||
con_err("-ftrue-empty-strings and -ffalse-empty-strings are mutually exclusive");
|
||||
exit(EXIT_FAILURE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* the standard decides which set of operators to use */
|
||||
if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_GMQCC) {
|
||||
if (opts.standard == COMPILER_GMQCC) {
|
||||
operators = c_operators;
|
||||
operator_count = GMQCC_ARRAY_COUNT(c_operators);
|
||||
} else if (OPTS_OPTION_U32(OPTION_STANDARD) == COMPILER_FTEQCC) {
|
||||
operator_count = c_operator_count;
|
||||
} else if (opts.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) {
|
||||
|
@ -562,7 +533,7 @@ int main(int argc, char **argv) {
|
|||
operators[operator_count-1].id != opid2(':','?'))
|
||||
{
|
||||
con_err("internal error: operator precedence table wasn't updated correctly!\n");
|
||||
exit(EXIT_FAILURE);
|
||||
exit(1);
|
||||
}
|
||||
operators_free = true;
|
||||
newops = (oper_info*)mem_a(sizeof(operators[0]) * operator_count);
|
||||
|
@ -574,22 +545,22 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_DUMP)) {
|
||||
if (opts.dump) {
|
||||
for (itr = 0; itr < COUNT_FLAGS; ++itr)
|
||||
con_out("Flag %s = %i\n", opts_flag_list[itr].name, OPTS_FLAG(itr));
|
||||
for (itr = 0; itr < COUNT_WARNINGS; ++itr)
|
||||
con_out("Warning %s = %i\n", opts_warn_list[itr].name, OPTS_WARN(itr));
|
||||
|
||||
con_out("output = %s\n", OPTS_OPTION_STR(OPTION_OUTPUT));
|
||||
con_out("optimization level = %u\n", OPTS_OPTION_U32(OPTION_O));
|
||||
con_out("standard = %u\n", OPTS_OPTION_U32(OPTION_STANDARD));
|
||||
|
||||
con_out("output = %s\n", opts.output);
|
||||
con_out("optimization level = %d\n", opts.O);
|
||||
con_out("standard = %i\n", opts.standard);
|
||||
}
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (opts.pp_only) {
|
||||
if (opts_output_wasset) {
|
||||
outfile = fopen(OPTS_OPTION_STR(OPTION_OUTPUT), "wb");
|
||||
outfile = file_open(opts.output, "wb");
|
||||
if (!outfile) {
|
||||
con_err("failed to open `%s` for writing\n", OPTS_OPTION_STR(OPTION_OUTPUT));
|
||||
con_err("failed to open `%s` for writing\n", opts.output);
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
|
@ -599,26 +570,31 @@ int main(int argc, char **argv) {
|
|||
}
|
||||
}
|
||||
|
||||
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (!(parser = parser_create())) {
|
||||
if (!opts.pp_only) {
|
||||
if (!parser_init()) {
|
||||
con_err("failed to initialize parser\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
|
||||
if (!(ftepp = ftepp_create())) {
|
||||
if (opts.pp_only || OPTS_FLAG(FTEPP)) {
|
||||
if (!ftepp_init()) {
|
||||
con_err("failed to initialize parser\n");
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
if (OPTS_FLAG(TRUE_EMPTY_STRINGS))
|
||||
type_not_instr[TYPE_STRING] = INSTR_NOT_F;
|
||||
|
||||
util_debug("COM", "starting ...\n");
|
||||
|
||||
/* add macros */
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY) || OPTS_FLAG(FTEPP)) {
|
||||
if (opts.pp_only || OPTS_FLAG(FTEPP)) {
|
||||
for (itr = 0; itr < vec_size(ppems); itr++) {
|
||||
ftepp_add_macro(ftepp, ppems[itr].name, ppems[itr].value);
|
||||
ftepp_add_macro(ppems[itr].name, ppems[itr].value);
|
||||
mem_d(ppems[itr].name);
|
||||
|
||||
/* can be null */
|
||||
|
@ -627,62 +603,56 @@ 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 = 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.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:
|
||||
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"));
|
||||
if (!opts.pp_only) {
|
||||
con_out("Mode: %s\n", (progs_src ? "progs.src" : "manual"));
|
||||
con_out("There are %lu items to compile:\n", (unsigned long)vec_size(items));
|
||||
}
|
||||
|
||||
for (itr = 0; itr < vec_size(items); ++itr) {
|
||||
if (!OPTS_OPTION_BOOL(OPTION_QUIET) &&
|
||||
!OPTS_OPTION_BOOL(OPTION_PP_ONLY))
|
||||
{
|
||||
if (!opts.pp_only) {
|
||||
con_out(" item: %s (%s)\n",
|
||||
items[itr].filename,
|
||||
( (items[itr].type == TYPE_QC ? "qc" :
|
||||
|
@ -691,82 +661,80 @@ int main(int argc, char **argv) {
|
|||
("unknown"))))));
|
||||
}
|
||||
|
||||
if (items[itr].type == TYPE_SRC) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (opts.pp_only) {
|
||||
const char *out;
|
||||
if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
|
||||
if (!ftepp_preprocess_file(items[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
out = ftepp_get(ftepp);
|
||||
out = ftepp_get();
|
||||
if (out)
|
||||
fprintf(outfile, "%s", out);
|
||||
ftepp_flush(ftepp);
|
||||
file_printf(outfile, "%s", out);
|
||||
ftepp_flush();
|
||||
}
|
||||
else {
|
||||
if (OPTS_FLAG(FTEPP)) {
|
||||
const char *data;
|
||||
if (!ftepp_preprocess_file(ftepp, items[itr].filename)) {
|
||||
if (!ftepp_preprocess_file(items[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
data = ftepp_get(ftepp);
|
||||
data = ftepp_get();
|
||||
if (vec_size(data)) {
|
||||
if (!parser_compile_string(parser, items[itr].filename, data, vec_size(data))) {
|
||||
if (!parser_compile_string(items[itr].filename, data, vec_size(data))) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
ftepp_flush(ftepp);
|
||||
ftepp_flush();
|
||||
}
|
||||
else {
|
||||
if (!parser_compile_file(parser, items[itr].filename)) {
|
||||
if (!parser_compile_file(items[itr].filename)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (!OPTS_OPTION_BOOL(OPTION_PP_ONLY)) {
|
||||
if (!parser_finish(parser, OPTS_OPTION_STR(OPTION_OUTPUT))) {
|
||||
ftepp_finish();
|
||||
if (!opts.pp_only) {
|
||||
if (!parser_finish(opts.output)) {
|
||||
retval = 1;
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* stuff */
|
||||
if (!opts.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:
|
||||
if (ftepp)
|
||||
ftepp_finish(ftepp);
|
||||
util_debug("COM", "cleaning ...\n");
|
||||
ftepp_finish();
|
||||
con_close();
|
||||
vec_free(items);
|
||||
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));
|
||||
|
||||
if (!opts.pp_only)
|
||||
parser_cleanup();
|
||||
if (opts_output_free)
|
||||
mem_d((char*)opts.output);
|
||||
if (operators_free)
|
||||
mem_d((void*)operators);
|
||||
|
||||
lex_cleanup();
|
||||
|
||||
if (!retval && compile_errors)
|
||||
retval = 1;
|
||||
util_meminfo();
|
||||
return retval;
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
#!/bin/sh
|
||||
prog=$0
|
||||
|
||||
die() {
|
||||
echo "$@"
|
||||
exit 1
|
||||
}
|
||||
|
||||
want() {
|
||||
test -e "$1" && return
|
||||
echo "$prog: missing $1"
|
||||
echo "$prog: run this script from the top of a gmqcc source tree"
|
||||
exit 1
|
||||
}
|
||||
|
||||
for i in opts.def \
|
||||
doc/gmqcc.1 \
|
||||
gmqcc.ini.example
|
||||
do want "$i"; done
|
||||
|
||||
# y/_ABCDEFGHIJKLMNOPQRSTUVWXYZ/-abcdefghijklmnopqrstuvwxyz/;
|
||||
check_opt() {
|
||||
opt_def_name=$1
|
||||
arg_char=$2
|
||||
|
||||
for i in $(sed -ne \
|
||||
'/^#ifdef GMQCC_TYPE_'${opt_def_name}'$/,/^#endif/{
|
||||
/GMQCC_DEFINE_FLAG/{
|
||||
s/^.*GMQCC_DEFINE_FLAG(\([^,)]*\)[),].*$/\1/;p;
|
||||
}
|
||||
}' opts.def)
|
||||
do
|
||||
opt=$(echo "$i" | tr -- '_A-Z' '-a-z')
|
||||
grep -qF -- ".It Fl "${arg_char}" Ns Cm $opt" \
|
||||
doc/gmqcc.1 || echo "doc/gmqcc.1: missing: -${arg_char}$opt"
|
||||
grep -q -- "[^a-zA-Z_]$i[^a-zA-Z_]" \
|
||||
gmqcc.ini.example || echo "gmqcc.ini.example: missing: $i"
|
||||
done
|
||||
}
|
||||
|
||||
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
|
25
msvc/gmqcc.sln
Normal file
25
msvc/gmqcc.sln
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 11.00
|
||||
# Visual Studio 2010
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "qcvm", "qcvm.vcxproj", "{8DC505A6-6047-4683-BA81-BC4B7A839352}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "gmqcc", "gmqcc.vcxproj", "{0F0B0779-1A2F-43E9-B833-18C443F7229E}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "testsuite", "testsuite.vcxproj", "{3F8F0021-66B8-43ED-906C-1CFE204E5673}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{8DC505A6-6047-4683-BA81-BC4B7A839352}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{8DC505A6-6047-4683-BA81-BC4B7A839352}.Release|Win32.Build.0 = Release|Win32
|
||||
{0F0B0779-1A2F-43E9-B833-18C443F7229E}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{0F0B0779-1A2F-43E9-B833-18C443F7229E}.Release|Win32.Build.0 = Release|Win32
|
||||
{3F8F0021-66B8-43ED-906C-1CFE204E5673}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{3F8F0021-66B8-43ED-906C-1CFE204E5673}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
EndGlobal
|
78
msvc/gmqcc.vcxproj
Normal file
78
msvc/gmqcc.vcxproj
Normal 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="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{0F0B0779-1A2F-43E9-B833-18C443F7229E}</ProjectGuid>
|
||||
<RootNamespace>gmqcc</RootNamespace>
|
||||
<TrackFileAccess>false</TrackFileAccess>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<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)'=='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 Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<EmbedManifest>false</EmbedManifest>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<ProgramDatabaseFile>
|
||||
</ProgramDatabaseFile>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>Del /Q "$(IntDir)\*.tlog"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\ast.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\code.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\conout.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)conout_gmqcc.o</ObjectFileName>
|
||||
</ClCompile>
|
||||
<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="..\util.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)util_gmqcc.o</ObjectFileName>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\ast.h" />
|
||||
<ClInclude Include="..\gmqcc.h" />
|
||||
<ClInclude Include="..\ir.h" />
|
||||
<ClInclude Include="..\lexer.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
67
msvc/qcvm.vcxproj
Normal file
67
msvc/qcvm.vcxproj
Normal file
|
@ -0,0 +1,67 @@
|
|||
<?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="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{8DC505A6-6047-4683-BA81-BC4B7A839352}</ProjectGuid>
|
||||
<RootNamespace>qcvm</RootNamespace>
|
||||
<TrackFileAccess>false</TrackFileAccess>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</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" />
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Full</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<AdditionalIncludeDirectories>.;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
|
||||
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
|
||||
<OmitFramePointers>true</OmitFramePointers>
|
||||
<EnableFiberSafeOptimizations>true</EnableFiberSafeOptimizations>
|
||||
<PreprocessorDefinitions>QCVM_EXECUTOR=1;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>Del /Q "$(IntDir)\*.tlog"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\conout.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)conout_qcvm.o</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\exec.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\util.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)util_qcvm.o</ObjectFileName>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\gmqcc.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
58
msvc/testsuite.vcxproj
Normal file
58
msvc/testsuite.vcxproj
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?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="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{3F8F0021-66B8-43ED-906C-1CFE204E5673}</ProjectGuid>
|
||||
<RootNamespace>testsuite</RootNamespace>
|
||||
<TrackFileAccess>false</TrackFileAccess>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<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)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<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>
|
||||
<PostBuildEvent>
|
||||
<Command>Del /Q "$(IntDir)\*.tlog"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\conout.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)conout_testsuite.o</ObjectFileName>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\test.c" />
|
||||
<ClCompile Include="..\util.c">
|
||||
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(IntDir)util_testsuite.o</ObjectFileName>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\gmqcc.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -1,43 +1,33 @@
|
|||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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";
|
||||
|
||||
|
||||
/* warnings */
|
||||
opts_set(opts.warn, WARN_UNUSED_VARIABLE, true);
|
||||
opts_set(opts.warn, WARN_USED_UNINITIALIZED, true);
|
||||
|
@ -45,7 +35,8 @@ static void opts_setdefault(void) {
|
|||
opts_set(opts.warn, WARN_EXTENSIONS, true);
|
||||
opts_set(opts.warn, WARN_FIELD_REDECLARED, true);
|
||||
opts_set(opts.warn, WARN_MISSING_RETURN_VALUES, true);
|
||||
opts_set(opts.warn, WARN_INVALID_PARAMETER_COUNT, true);
|
||||
opts_set(opts.warn, WARN_TOO_FEW_PARAMETERS, true);
|
||||
opts_set(opts.warn, WARN_LOCAL_SHADOWS, false);
|
||||
opts_set(opts.warn, WARN_LOCAL_CONSTANTS, true);
|
||||
opts_set(opts.warn, WARN_VOID_VARIABLES, true);
|
||||
opts_set(opts.warn, WARN_IMPLICIT_FUNCTION_POINTER, true);
|
||||
|
@ -54,78 +45,46 @@ static void opts_setdefault(void) {
|
|||
opts_set(opts.warn, WARN_EFFECTLESS_STATEMENT, true);
|
||||
opts_set(opts.warn, WARN_END_SYS_FIELDS, true);
|
||||
opts_set(opts.warn, WARN_ASSIGN_FUNCTION_TYPES, true);
|
||||
opts_set(opts.warn, WARN_CPP, true);
|
||||
opts_set(opts.warn, WARN_PREPROCESSOR, true);
|
||||
opts_set(opts.warn, WARN_MULTIFILE_IF, true);
|
||||
opts_set(opts.warn, WARN_DOUBLE_DECLARATION, true);
|
||||
opts_set(opts.warn, WARN_CONST_VAR, true);
|
||||
opts_set(opts.warn, WARN_MULTIBYTE_CHARACTER, true);
|
||||
opts_set(opts.warn, WARN_UNKNOWN_PRAGMAS, true);
|
||||
opts_set(opts.warn, WARN_UNREACHABLE_CODE, true);
|
||||
opts_set(opts.warn, WARN_CPP, true);
|
||||
opts_set(opts.warn, WARN_UNKNOWN_ATTRIBUTE, true);
|
||||
opts_set(opts.warn, WARN_RESERVED_NAMES, true);
|
||||
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);
|
||||
opts_set(opts.flags, FTEPP, false);
|
||||
opts_set(opts.flags, CORRECT_TERNARY, true);
|
||||
opts_set(opts.flags, BAIL_ON_WERROR, true);
|
||||
opts_set(opts.flags, LEGACY_VECTOR_MATHS, true);
|
||||
opts_set(opts.flags, DARKPLACES_STRING_TABLE_BUG, true);
|
||||
|
||||
/* options */
|
||||
OPTS_OPTION_U32(OPTION_STATE_FPS) = 10;
|
||||
}
|
||||
|
||||
void opts_backup_non_Wall() {
|
||||
size_t i;
|
||||
for (i = 0; i <= WARN_DEBUG; ++i)
|
||||
opts_set(opts.warn_backup, i, OPTS_WARN(i));
|
||||
}
|
||||
|
||||
void opts_restore_non_Wall() {
|
||||
size_t i;
|
||||
for (i = 0; i <= WARN_DEBUG; ++i)
|
||||
opts_set(opts.warn, i, OPTS_GENERIC(opts.warn_backup, i));
|
||||
}
|
||||
|
||||
void opts_backup_non_Werror_all() {
|
||||
size_t i;
|
||||
for (i = 0; i <= WARN_DEBUG; ++i)
|
||||
opts_set(opts.werror_backup, i, OPTS_WERROR(i));
|
||||
}
|
||||
|
||||
void opts_restore_non_Werror_all() {
|
||||
size_t i;
|
||||
for (i = 0; i <= WARN_DEBUG; ++i)
|
||||
opts_set(opts.werror, i, OPTS_GENERIC(opts.werror_backup, i));
|
||||
}
|
||||
|
||||
void opts_init(const char *output, int standard, size_t arraysize) {
|
||||
opts_setdefault();
|
||||
|
||||
OPTS_OPTION_STR(OPTION_OUTPUT) = output;
|
||||
OPTS_OPTION_U32(OPTION_STANDARD) = standard;
|
||||
OPTS_OPTION_U32(OPTION_MAX_ARRAY_SIZE) = arraysize;
|
||||
|
||||
opts.output = output;
|
||||
opts.standard = (opts_std_t)standard; /* C++ ... y u no like me? */
|
||||
opts.max_array_size = arraysize;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (!strcmp(name, list[i].name)) {
|
||||
longbit lb = list[i].bit;
|
||||
|
||||
#if 0
|
||||
if (on)
|
||||
flags[lb.idx] |= (1<<(lb.bit));
|
||||
else
|
||||
flags[lb.idx] &= ~(1<<(lb.bit));
|
||||
|
||||
#else
|
||||
if (on)
|
||||
flags[0] |= (1<<lb);
|
||||
else
|
||||
flags[0] &= ~(1<<(lb));
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -145,22 +104,24 @@ bool opts_setoptim (const char *name, bool on) {
|
|||
}
|
||||
|
||||
void opts_set(uint32_t *flags, size_t idx, bool on) {
|
||||
longbit lb;
|
||||
LONGBIT_SET(lb, idx);
|
||||
|
||||
longbit lb = LONGBIT(idx);
|
||||
#if 0
|
||||
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));
|
||||
#else
|
||||
if (on)
|
||||
flags[0] |= (1<<(lb));
|
||||
else
|
||||
flags[0] &= ~(1<<(lb));
|
||||
#endif
|
||||
}
|
||||
|
||||
void opts_setoptimlevel(unsigned int level) {
|
||||
size_t i;
|
||||
for (i = 0; i < COUNT_OPTIMIZATIONS; ++i)
|
||||
opts_set(opts.optimization, i, level >= opts_opt_oflag[i]);
|
||||
|
||||
if (!level)
|
||||
opts.optimizeoff = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -171,14 +132,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 +147,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 +170,7 @@ static size_t opts_ini_parse (
|
|||
char *read_name;
|
||||
char *read_value;
|
||||
|
||||
while (util_getline(&line, &linesize, filehandle) != EOF) {
|
||||
while (file_getline(&line, &linesize, filehandle) != EOF) {
|
||||
parse_beg = line;
|
||||
|
||||
/* handle BOM */
|
||||
|
@ -231,7 +191,7 @@ static size_t opts_ini_parse (
|
|||
/* section found */
|
||||
if (*(parse_end = opts_ini_next(parse_beg + 1, ']')) == ']') {
|
||||
* parse_end = '\0'; /* terminate bro */
|
||||
util_strncpy(section_data, parse_beg + 1, sizeof(section_data));
|
||||
strncpy(section_data, parse_beg + 1, sizeof(section_data));
|
||||
section_data[sizeof(section_data) - 1] = '\0';
|
||||
*oldname_data = '\0';
|
||||
} else if (!error) {
|
||||
|
@ -241,7 +201,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 */
|
||||
|
@ -252,23 +212,11 @@ static size_t opts_ini_parse (
|
|||
opts_ini_rstrip(read_value);
|
||||
|
||||
/* valid name value pair, lets call down to handler */
|
||||
util_strncpy(oldname_data, read_name, sizeof(oldname_data));
|
||||
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 +226,6 @@ static size_t opts_ini_parse (
|
|||
}
|
||||
mem_d(line);
|
||||
return error;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -287,41 +234,21 @@ 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 !!atoi(value);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/*
|
||||
* undef all of these because they may still be defined like in my
|
||||
* case they where.
|
||||
*/
|
||||
*/
|
||||
#undef GMQCC_TYPE_FLAGS
|
||||
#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) \
|
||||
|
@ -340,15 +267,6 @@ static char *opts_ini_load(const char *section, const char *name, const char *va
|
|||
}
|
||||
#include "opts.def"
|
||||
|
||||
/* Werror-individuals */
|
||||
#define GMQCC_TYPE_WARNS
|
||||
#define GMQCC_DEFINE_FLAG(X) \
|
||||
if (!strcmp(section, "errors") && !strcmp(name, #X)) { \
|
||||
opts_set(opts.werror, WARN_##X, opts_ini_bool(value)); \
|
||||
found = true; \
|
||||
}
|
||||
#include "opts.def"
|
||||
|
||||
/* optimizations */
|
||||
#define GMQCC_TYPE_OPTIMIZATIONS
|
||||
#define GMQCC_DEFINE_FLAG(X,Y) \
|
||||
|
@ -360,29 +278,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 +309,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 = file_open((file = "gmqcc.ini"), "r")))
|
||||
/* try cfg */
|
||||
if (!(ini = fopen((file = "gmqcc.cfg"), "r")))
|
||||
if (!(ini = file_open((file = "gmqcc.cfg"), "r")))
|
||||
return;
|
||||
} else if (!(ini = fopen(file, "r")))
|
||||
} else if (!(ini = 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);
|
||||
}
|
||||
file_close(ini);
|
||||
}
|
101
opts.def
101
opts.def
|
@ -1,15 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2012
|
||||
* 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"
|
||||
# define GMQCC_DEFINE_FLAG(x)
|
||||
#endif
|
||||
|
||||
/* codegen flags */
|
||||
#ifdef GMQCC_TYPE_FLAGS
|
||||
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS)
|
||||
GMQCC_DEFINE_FLAG(DARKPLACES_STRING_TABLE_BUG)
|
||||
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)
|
||||
|
@ -22,36 +43,18 @@
|
|||
GMQCC_DEFINE_FLAG(CORRECT_LOGIC)
|
||||
GMQCC_DEFINE_FLAG(TRUE_EMPTY_STRINGS)
|
||||
GMQCC_DEFINE_FLAG(FALSE_EMPTY_STRINGS)
|
||||
GMQCC_DEFINE_FLAG(UTF8)
|
||||
GMQCC_DEFINE_FLAG(BAIL_ON_WERROR)
|
||||
GMQCC_DEFINE_FLAG(LOOP_LABELS)
|
||||
GMQCC_DEFINE_FLAG(UNTYPED_NIL)
|
||||
GMQCC_DEFINE_FLAG(PERMISSIVE)
|
||||
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 */
|
||||
#ifdef GMQCC_TYPE_WARNS
|
||||
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)
|
||||
GMQCC_DEFINE_FLAG(FIELD_REDECLARED)
|
||||
GMQCC_DEFINE_FLAG(MISSING_RETURN_VALUES)
|
||||
GMQCC_DEFINE_FLAG(INVALID_PARAMETER_COUNT)
|
||||
GMQCC_DEFINE_FLAG(TOO_FEW_PARAMETERS)
|
||||
GMQCC_DEFINE_FLAG(LOCAL_SHADOWS)
|
||||
GMQCC_DEFINE_FLAG(LOCAL_CONSTANTS)
|
||||
GMQCC_DEFINE_FLAG(VOID_VARIABLES)
|
||||
|
@ -61,7 +64,7 @@
|
|||
GMQCC_DEFINE_FLAG(EFFECTLESS_STATEMENT)
|
||||
GMQCC_DEFINE_FLAG(END_SYS_FIELDS)
|
||||
GMQCC_DEFINE_FLAG(ASSIGN_FUNCTION_TYPES)
|
||||
GMQCC_DEFINE_FLAG(CPP)
|
||||
GMQCC_DEFINE_FLAG(PREPROCESSOR)
|
||||
GMQCC_DEFINE_FLAG(MULTIFILE_IF)
|
||||
GMQCC_DEFINE_FLAG(DOUBLE_DECLARATION)
|
||||
GMQCC_DEFINE_FLAG(CONST_VAR)
|
||||
|
@ -69,58 +72,18 @@
|
|||
GMQCC_DEFINE_FLAG(TERNARY_PRECEDENCE)
|
||||
GMQCC_DEFINE_FLAG(UNKNOWN_PRAGMAS)
|
||||
GMQCC_DEFINE_FLAG(UNREACHABLE_CODE)
|
||||
GMQCC_DEFINE_FLAG(CPP)
|
||||
GMQCC_DEFINE_FLAG(UNKNOWN_ATTRIBUTE)
|
||||
GMQCC_DEFINE_FLAG(RESERVED_NAMES)
|
||||
GMQCC_DEFINE_FLAG(UNINITIALIZED_CONSTANT)
|
||||
GMQCC_DEFINE_FLAG(DIFFERENT_QUALIFIERS)
|
||||
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
|
||||
GMQCC_DEFINE_FLAG(PEEPHOLE, 1)
|
||||
GMQCC_DEFINE_FLAG(TAIL_RECURSION, 1)
|
||||
GMQCC_DEFINE_FLAG(OVERLAP_LOCALS, 3)
|
||||
GMQCC_DEFINE_FLAG(LOCAL_TEMPS, 3)
|
||||
GMQCC_DEFINE_FLAG(GLOBAL_TEMPS, 3)
|
||||
GMQCC_DEFINE_FLAG(STRIP_CONSTANT_NAMES, 1)
|
||||
GMQCC_DEFINE_FLAG(OVERLAP_STRINGS, 2)
|
||||
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
|
||||
GMQCC_DEFINE_FLAG(O)
|
||||
GMQCC_DEFINE_FLAG(OUTPUT)
|
||||
GMQCC_DEFINE_FLAG(QUIET)
|
||||
GMQCC_DEFINE_FLAG(G)
|
||||
GMQCC_DEFINE_FLAG(STANDARD)
|
||||
GMQCC_DEFINE_FLAG(DEBUG)
|
||||
GMQCC_DEFINE_FLAG(DUMPFIN)
|
||||
GMQCC_DEFINE_FLAG(DUMP)
|
||||
GMQCC_DEFINE_FLAG(FORCECRC)
|
||||
GMQCC_DEFINE_FLAG(FORCED_CRC)
|
||||
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(PEEPHOLE, 1)
|
||||
GMQCC_DEFINE_FLAG(LOCALTEMPS, 1)
|
||||
GMQCC_DEFINE_FLAG(TAIL_RECURSION, 1)
|
||||
GMQCC_DEFINE_FLAG(TAIL_CALLS, 2)
|
||||
#endif
|
||||
|
||||
/* some cleanup so we don't have to */
|
||||
#undef GMQCC_TYPE_FLAGS
|
||||
#undef GMQCC_TYPE_WARNS
|
||||
#undef GMQCC_TYPE_OPTIONS
|
||||
#undef GMQCC_TYPE_OPTIMIZATIONS
|
||||
#undef GMQCC_DEFINE_FLAG
|
||||
|
|
6416
parser.cpp
6416
parser.cpp
File diff suppressed because it is too large
Load diff
84
parser.h
84
parser.h
|
@ -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
250
stat.cpp
|
@ -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);
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
I: accumulate.qc
|
||||
D: test function accumulation
|
||||
T: -execute
|
||||
C: -std=gmqcc -fftepp
|
||||
M: hello accumulation world
|
|
@ -1,34 +0,0 @@
|
|||
float alias_1 = 3.14;
|
||||
void alias_2() {
|
||||
print("alias_2\n");
|
||||
}
|
||||
|
||||
[[alias("alias_2")]] void alias_2_aliased();
|
||||
[[alias("alias_1")]] float alias_1_aliased;
|
||||
|
||||
|
||||
// alias to an alias?
|
||||
vector alias_3;
|
||||
[[alias("alias_3")]] vector alias_3_aliased;
|
||||
|
||||
// expected output
|
||||
// alias_2
|
||||
// 3.14
|
||||
void main() {
|
||||
alias_2_aliased();
|
||||
|
||||
alias_3_aliased= '1 2 3';
|
||||
|
||||
print(
|
||||
ftos(
|
||||
alias_1_aliased
|
||||
),
|
||||
"\n"
|
||||
);
|
||||
|
||||
print(
|
||||
"x ", ftos(alias_3_aliased_x), "\n",
|
||||
"y ", ftos(alias_3_aliased_y), "\n",
|
||||
"z ", ftos(alias_3_aliased_z), "\n"
|
||||
);
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
I: aliases.qc
|
||||
D: test aliases
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
M: alias_2
|
||||
M: 3.14
|
||||
M: x 1
|
||||
M: y 2
|
||||
M: z 3
|
|
@ -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
|
|
@ -1,4 +0,0 @@
|
|||
I: arithexcept.qc
|
||||
D: arithmetic exceptions (divide by zero)
|
||||
T: -fail
|
||||
C: -std=fteqcc -farithmetic-exceptions -DDIVBYZERO
|
|
@ -1,4 +0,0 @@
|
|||
I: arithexcept.qc
|
||||
D: arithmetic exceptions (overflow)
|
||||
T: -fail
|
||||
C: -std=fteqcc -farithmetic-exceptions -DOVERFLOW
|
|
@ -1,4 +0,0 @@
|
|||
I: arithexcept.qc
|
||||
D: arithmetic exceptions (underflow)
|
||||
T: -fail
|
||||
C: -std=fteqcc -farithmetic-exceptions -DUNDERFLOW
|
|
@ -1,3 +1,7 @@
|
|||
void print(...) = #1;
|
||||
string ftos (float) = #2;
|
||||
entity() spawn = #3;
|
||||
|
||||
float glob[7];
|
||||
|
||||
.float above;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -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
|
|
@ -1,37 +0,0 @@
|
|||
void main() {
|
||||
float a; a = 1;
|
||||
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");
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
# used to test the builtins
|
||||
I: bitnot.qc
|
||||
D: test bitwise not operators (fteqcc operators)
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
E: $null
|
||||
M: a: 0
|
||||
M: b: 0
|
||||
M: c: 0
|
||||
M: e: '0 0 0'
|
||||
M: f: '0 0 0'
|
|
@ -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'
|
|
@ -1,3 +1,6 @@
|
|||
void print(...) = #1;
|
||||
string ftos (float) = #2;
|
||||
|
||||
void test(float brkat, float contat) {
|
||||
float i;
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
void(string) print = #1;
|
||||
|
||||
void() main = {
|
||||
print("hello world");
|
||||
}
|
||||
|
|
|
@ -4,4 +4,6 @@ D: test builtin functions
|
|||
T: -execute
|
||||
C: -std=gmqcc
|
||||
E: $null
|
||||
F: builtins failed
|
||||
S: builtins worked
|
||||
M: hello world
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
void(string, ...) print = #1;
|
||||
string(float) ftos = #2;
|
||||
|
||||
float(float x, float y, float z) sum = {
|
||||
return x + y + z;
|
||||
};
|
||||
|
@ -8,5 +11,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");
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -3,4 +3,6 @@ D: test calls
|
|||
T: -execute
|
||||
C: -std=gmqcc
|
||||
E: -float 100 -float 200 -float 300
|
||||
F: calls failed
|
||||
S: calls worked
|
||||
M: 4600
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
void print(...) = #1;
|
||||
string ftos (float) = #2;
|
||||
|
||||
float test_s_not (vector s) { return !s; }
|
||||
float test_s_and (vector s, vector t) { return s && t; }
|
||||
float test_s_or (vector s, vector t) { return s || t; }
|
||||
|
|
|
@ -1,3 +1,6 @@
|
|||
void print(...) = #1;
|
||||
string ftos (float) = #2;
|
||||
|
||||
void test(vector a, vector b) {
|
||||
print(ftos((a && b) + (a && b)), " ");
|
||||
print(ftos((a || b) + (a || b)), " ");
|
||||
|
|
|
@ -1,21 +0,0 @@
|
|||
// builtins for the standalone qcvm included with gmqcc
|
||||
// in exec.c These should be updated to reflect the new
|
||||
// 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;
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -1,68 +0,0 @@
|
|||
enum {
|
||||
// this behaviour is confusing, but I like that
|
||||
// we support it.
|
||||
__ = (__ - 1),
|
||||
A = (__ + 1),
|
||||
|
||||
B,
|
||||
C
|
||||
};
|
||||
|
||||
enum {
|
||||
D = C + B,
|
||||
E = C + C,
|
||||
F = C + D,
|
||||
};
|
||||
|
||||
enum {
|
||||
G = (B + F), H = (C + F),
|
||||
I = (D + F), J = (B + I)
|
||||
};
|
||||
enum {
|
||||
K = A + B - C + D - E + F *
|
||||
G - H + I - J + A - B -
|
||||
J + A,
|
||||
L,
|
||||
M,
|
||||
N
|
||||
};
|
||||
|
||||
enum : flag {
|
||||
F1, /* = 1 << 1 */
|
||||
F2, /* = 1 << 2 */
|
||||
F3 /* = 1 << 3 */
|
||||
};
|
||||
|
||||
/* reversed enumeration */
|
||||
enum : reverse {
|
||||
R1, // 3
|
||||
R2, // 2
|
||||
R3, // 1
|
||||
R4 // 0
|
||||
};
|
||||
|
||||
void main() {
|
||||
print(ftos(A), "\n");
|
||||
print(ftos(B), "\n");
|
||||
print(ftos(C), "\n");
|
||||
print(ftos(D), "\n");
|
||||
print(ftos(E), "\n");
|
||||
print(ftos(F), "\n");
|
||||
print(ftos(G), "\n");
|
||||
print(ftos(H), "\n");
|
||||
print(ftos(I), "\n");
|
||||
print(ftos(J), "\n");
|
||||
print(ftos(K), "\n");
|
||||
print(ftos(L), "\n");
|
||||
print(ftos(M), "\n");
|
||||
print(ftos(N), "\n");
|
||||
|
||||
print(ftos(F1), "\n");
|
||||
print(ftos(F2), "\n");
|
||||
print(ftos(F3), "\n");
|
||||
|
||||
print(ftos(R1), "\n");
|
||||
print(ftos(R2), "\n");
|
||||
print(ftos(R3), "\n");
|
||||
print(ftos(R4), "\n");
|
||||
};
|
|
@ -1,25 +0,0 @@
|
|||
I: enum.qc
|
||||
D: enumerations
|
||||
T: -execute
|
||||
C: -std=fteqcc
|
||||
M: 0
|
||||
M: 1
|
||||
M: 2
|
||||
M: 3
|
||||
M: 4
|
||||
M: 5
|
||||
M: 6
|
||||
M: 7
|
||||
M: 8
|
||||
M: 9
|
||||
M: 10
|
||||
M: 11
|
||||
M: 12
|
||||
M: 13
|
||||
M: 2
|
||||
M: 4
|
||||
M: 8
|
||||
M: 3
|
||||
M: 2
|
||||
M: 1
|
||||
M: 0
|
|
@ -1,3 +1,6 @@
|
|||
void(string, ...) print = #1;
|
||||
string(float) ftos = #2;
|
||||
|
||||
void(float a, float b) main = {
|
||||
if (a == b) print("eq,");
|
||||
if (a != b) print("ne,");
|
||||
|
|
|
@ -3,4 +3,6 @@ D: test equality
|
|||
T: -execute
|
||||
C: -std=gmqcc
|
||||
E: -float 100 -float 200
|
||||
F: equality failed
|
||||
S: equality worked
|
||||
M: ne,lt,le,
|
||||
|
|
|
@ -1,14 +0,0 @@
|
|||
void main() {
|
||||
float hundy = __builtin_pow(10, 2); // 10^2 == 100
|
||||
print(ftos(hundy), "\n"); // prints: 100
|
||||
|
||||
hundy = pow(10, 2);
|
||||
print(ftos(hundy), "\n");
|
||||
|
||||
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
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
# used to test the builtins
|
||||
I: exponentiation.qc
|
||||
D: test exponentiation operator and __builtin_pow
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
E: $null
|
||||
M: 100
|
||||
M: 100
|
||||
M: 100
|
||||
M: 100
|
||||
M: 22026.5
|
|
@ -1,7 +0,0 @@
|
|||
/* empty line required */
|
||||
void print(string, ...) = #__LINE__ - 1;
|
||||
|
||||
|
||||
void main(string input) {
|
||||
print(input, "\n");
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
I: exprforbuiltins.qc
|
||||
D: expressions for builtins
|
||||
T: -execute
|
||||
C: -std=gmqcc -fftepp -fftepp-predefs
|
||||
F: -no-defs
|
||||
E: -string test
|
||||
M: test
|
|
@ -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");
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
I: fieldfuncs.qc
|
||||
D: test fields with functions
|
||||
T: -execute
|
||||
C: -std=fte
|
||||
M: 42
|
||||
M: 42
|
|
@ -1,3 +1,6 @@
|
|||
void(string, string) print = #1;
|
||||
entity() spawn = #3;
|
||||
|
||||
.string a;
|
||||
.string b;
|
||||
..string ps;
|
||||
|
|
|
@ -3,5 +3,7 @@ D: test field paramaters
|
|||
T: -execute
|
||||
C: -std=qcc
|
||||
E: $null
|
||||
F: field paramaters fail
|
||||
S: field paramaters work
|
||||
M: bar
|
||||
M: foo
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
void main() {
|
||||
float j;
|
||||
for (j = 0; j < 2; ++j)
|
||||
print("+");
|
||||
|
||||
for (float i = 0; i < 5; ++i)
|
||||
print("*");
|
||||
|
||||
for (;;) {
|
||||
print("\n");
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
I: forloop.qc
|
||||
D: test for loops
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
M: ++*****
|
|
@ -1,20 +0,0 @@
|
|||
$frame frame1 frame2
|
||||
|
||||
float time;
|
||||
entity self;
|
||||
.float frame;
|
||||
.float nextthink;
|
||||
.void() think;
|
||||
|
||||
// Mixing syntax, = is optional.
|
||||
void frame1_func_mixed_no_assign() [$frame1, frame2_func_mixed_no_assign] {}
|
||||
void frame2_func_mixed_no_assign() [$frame2, frame2_func_mixed_no_assign] {}
|
||||
|
||||
void frame1_func_mixed() =[$frame1, frame2_func_mixed] {}
|
||||
void frame2_func_mixed() =[$frame2, frame2_func_mixed] {}
|
||||
|
||||
void() frame1_func_old =[$frame1, frame2_func_old] {}
|
||||
void() frame2_func_old =[$frame2, frame2_func_old] {}
|
||||
|
||||
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
I: framemacro.qc
|
||||
D: test frame macros
|
||||
T: -compile
|
||||
C: -std=gmqcc
|
|
@ -1,3 +1,5 @@
|
|||
void(string, string) print = #1;
|
||||
|
||||
string() getter = {
|
||||
return "correct";
|
||||
};
|
||||
|
|
|
@ -3,4 +3,6 @@ D: test functions as paramaters
|
|||
T: -execute
|
||||
C: -std=gmqcc
|
||||
E: $null
|
||||
F: functions as paramaters failed
|
||||
S: functions as paramaters passed
|
||||
M: correct
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
// correct execution order:
|
||||
// label_3
|
||||
// label_2
|
||||
// label_4
|
||||
// label_3
|
||||
// label_1
|
||||
// label_5
|
||||
void main() {
|
||||
float x = 1;
|
||||
float y = 2;
|
||||
|
||||
goto label_3;
|
||||
|
||||
:label_1; print("label_1", "\n"); goto label_5;
|
||||
:label_2; print("label_2", "\n"); goto label_4;
|
||||
:label_3; print("label_3", "\n");
|
||||
|
||||
// will goto label_2
|
||||
goto (x == y) ? label_1 : label_2;
|
||||
|
||||
:label_4; print("label_4", "\n");
|
||||
{
|
||||
x = 1;
|
||||
y = 1;
|
||||
|
||||
// will goto label_1
|
||||
// then goes label_5
|
||||
goto label_3;
|
||||
}
|
||||
|
||||
:label_5; print("label_5", "\n");
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
I: goto.qc
|
||||
D: test goto (both normal and computed)
|
||||
T: -execute
|
||||
C: -std=gmqcc
|
||||
M: label_3
|
||||
M: label_2
|
||||
M: label_4
|
||||
M: label_3
|
||||
M: label_1
|
||||
M: label_5
|
|
@ -1,3 +1,5 @@
|
|||
void(string, ...) print = #1;
|
||||
|
||||
void(float c) main = {
|
||||
if (c == 1)
|
||||
print("One\n");
|
||||
|
|
|
@ -3,4 +3,6 @@ D: test if statement
|
|||
T: -execute
|
||||
C: -std=gmqcc
|
||||
E: -float 2
|
||||
F: if statement failed
|
||||
S: if statement passed
|
||||
M: Two
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
void main() {
|
||||
const float a = 1.0 / 3.0;
|
||||
const float b = 0.33333333333;
|
||||
if (a == b) {
|
||||
// Should trigger warning
|
||||
}
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
I: inexact-local.qc
|
||||
D: inexact comparisons
|
||||
T: -fail
|
||||
C: -std=gmqcc -Winexact-compares -Wall -Werror
|
|
@ -1,8 +0,0 @@
|
|||
const float a = 1.0 / 3.0;
|
||||
const float b = 0.33333333333;
|
||||
|
||||
void main() {
|
||||
if (a == b) {
|
||||
// Should trigger warning
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue