Cleaned up some old D files
parent
4ecb63203b
commit
29522f57d3
@ -1,23 +1 @@
|
|||||||
screenshot*.png
|
build
|
||||||
*.o
|
|
||||||
*.a
|
|
||||||
*~
|
|
||||||
data
|
|
||||||
CMakeFiles
|
|
||||||
CMakeCache.txt
|
|
||||||
Makefile
|
|
||||||
cmake*.cmake
|
|
||||||
Ogre.log
|
|
||||||
ogre.cfg
|
|
||||||
build*
|
|
||||||
plugins.cfg
|
|
||||||
openmw.cfg
|
|
||||||
Doxygen
|
|
||||||
.thumbnails
|
|
||||||
resources
|
|
||||||
mwcompiler
|
|
||||||
mwinterpreter
|
|
||||||
clientconsole
|
|
||||||
MyGUI.log
|
|
||||||
mygui_test
|
|
||||||
mygui_test.png
|
|
||||||
|
@ -1,247 +0,0 @@
|
|||||||
OpenMW - the completely unofficial reimplementation of Morrowind
|
|
||||||
================================================================
|
|
||||||
|
|
||||||
OpenMW is an open source reimplementation of the Morrowind game
|
|
||||||
engine. For more information, see README.txt or
|
|
||||||
|
|
||||||
http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Installation from source
|
|
||||||
========================
|
|
||||||
|
|
||||||
|
|
||||||
Supported Unix platforms:
|
|
||||||
-------------------------
|
|
||||||
|
|
||||||
OpenMW has been tested for the most part on 32bit Ubuntu Linux
|
|
||||||
8.04. It should, however, also work on most other 32bit
|
|
||||||
distributions.
|
|
||||||
|
|
||||||
FreeBSD is also known to work with recent versions of GDC (the D
|
|
||||||
compiler), but you might need to rebuild some of the dependencies from
|
|
||||||
source yourself. (If you have done this and can give more specific
|
|
||||||
instructions, please let me know!)
|
|
||||||
|
|
||||||
64bit systems are NOT supported.
|
|
||||||
|
|
||||||
If you manage to build OpenMW on a platform not listed here, or want
|
|
||||||
to try, please let me know!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Dependencies:
|
|
||||||
-------------
|
|
||||||
|
|
||||||
Dependencies needed to build OpenMW:
|
|
||||||
|
|
||||||
OGRE 1.6.2 (3d engine)
|
|
||||||
OIS-1.0.0 (input system)
|
|
||||||
MyGUI 2.2.2 (GUI system for OGRE)
|
|
||||||
Bullet 2.74 (collision and physics library)
|
|
||||||
Monster 0.12 (scripting engine, included)
|
|
||||||
OpenAL (3d sound system)
|
|
||||||
libavcodec,
|
|
||||||
libavformat (For MP3 playback)
|
|
||||||
gcc and g++ (C++ compiler)
|
|
||||||
GNU make (build tool for C++ files)
|
|
||||||
DMD 1.036 (D compiler)
|
|
||||||
or GDC 4.1.3 (alternative D compiler)
|
|
||||||
|
|
||||||
The above versions are the ones I have tested recently, but other
|
|
||||||
versions might work. OGRE, OpenAL and the other libraries have
|
|
||||||
dependencies of their own, so I recommend using an automated package
|
|
||||||
tool to install as many of these as possible. On Ubuntu, for example,
|
|
||||||
try typing:
|
|
||||||
|
|
||||||
sudo apt-get install libogre-dev libavcodec-dev libavformat-dev libois-dev build-essential g++ gdc
|
|
||||||
|
|
||||||
On Arch Linux, get the following packages from AUR:
|
|
||||||
ogre
|
|
||||||
mygui-svn
|
|
||||||
bullet-2.75
|
|
||||||
openal
|
|
||||||
ffmpeg
|
|
||||||
ois
|
|
||||||
dmd
|
|
||||||
libphobos
|
|
||||||
dsss
|
|
||||||
|
|
||||||
(On Arch you also need to change the Bullet libraries to
|
|
||||||
-llBulletDynamics -llBulletCollision -llLinearMath in dsss.conf, or
|
|
||||||
similarily in the Makefile if you prefer building with make.)
|
|
||||||
|
|
||||||
A note about OpenAL: The library found in most Linux distributions
|
|
||||||
(0.0.8 SI) is outdated. Some distributions are now changing to OpenAL
|
|
||||||
Soft, which has a less hardware-dependent implementation. If you
|
|
||||||
experience sound problems (like stuttering music), try switching to
|
|
||||||
OpenAL Soft.
|
|
||||||
|
|
||||||
Note to UBUNTU 8.10 users: There's a problem with Pulseaudio in this
|
|
||||||
Ubuntu version. If you find that sound doesn't work or you get error
|
|
||||||
messages related to sound, try this page:
|
|
||||||
http://idyllictux.wordpress.com/2008/10/29/alsa-instead-of-pulseaudio-for-ubuntu-810-intrepid-a-non-destructive-way/
|
|
||||||
|
|
||||||
libavcodec and libavformat are part of the FFmpeg package, which is
|
|
||||||
part of the 'mplayer' project. Note that some of the codecs might be
|
|
||||||
patent protected in your country or area.
|
|
||||||
|
|
||||||
If you want to install Ogre, Bullet, OpenAL, OIS or FFmpeg manually,
|
|
||||||
try:
|
|
||||||
|
|
||||||
OGRE: http://ogre3d.org/
|
|
||||||
MyGUI: http://www.ogre3d.org/wiki/index.php/MyGUI
|
|
||||||
OpenAL: http://openal.org/
|
|
||||||
Linux source: http://kcat.strangesoft.net/openal.html
|
|
||||||
OIS: http://sourceforge.net/projects/wgois/
|
|
||||||
FFmpeg: http://ffmpeg.mplayerhq.hu/download.html
|
|
||||||
Bullet: http://bulletphysics.com/
|
|
||||||
Monster: Included with OpenMW.
|
|
||||||
|
|
||||||
|
|
||||||
Setting up Bullet
|
|
||||||
-----------------
|
|
||||||
|
|
||||||
Bullet is not yet included in most Linux distributions, and must
|
|
||||||
usually be compiled manually. Consult the documentation included with
|
|
||||||
Bullet for instructions on compiling.
|
|
||||||
|
|
||||||
After compiling, you should have the files libbulletdynamics.a,
|
|
||||||
libbulletcollision.a and libbulletmath.a. (Depending on build method,
|
|
||||||
you may end up with names like libLibBulletDynamics.a instead.) Copy
|
|
||||||
or link these to bullet/ in the openmw directory (and rename them to
|
|
||||||
the libbulletdynamics.a etc if necessary.)
|
|
||||||
|
|
||||||
Example:
|
|
||||||
cd openmw/bullet
|
|
||||||
ln -s ~/software/bullet-2.74/src/BulletCollision/libbulletcollision.a .
|
|
||||||
|
|
||||||
Next, create a link to the "src" directory in openmw/include/bullet/ .
|
|
||||||
|
|
||||||
Example:
|
|
||||||
cd openmw
|
|
||||||
mkdir -p include
|
|
||||||
cd include
|
|
||||||
ln -s ~/software/bullet-2.74/src bullet
|
|
||||||
|
|
||||||
|
|
||||||
Setting up MyGUI
|
|
||||||
----------------
|
|
||||||
|
|
||||||
Like Bullet, MyGUI is still too fresh to be prepackaged with the major
|
|
||||||
Linux distros. To make matters worse, there's not even a recent source
|
|
||||||
package. You'll need to get the source from svn (at
|
|
||||||
https://my-gui.svn.sourceforge.net/svnroot/my-gui/trunk ). Revisions
|
|
||||||
at around 1738 should work, but newer revisions are probably better.
|
|
||||||
|
|
||||||
Once you've compiled the sources, install normally using 'make
|
|
||||||
install' (check the MyGUI docs for any instructions), and the OpenMW
|
|
||||||
makefile should pick up its location automatically.
|
|
||||||
|
|
||||||
|
|
||||||
Choosing a D compiler
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
The above apt-get command installs the GDC compiler. There are
|
|
||||||
currently two choices for the D compiler, DMD and GDC. DMD is the
|
|
||||||
"official" compiler and is updated more often, while GDC is a
|
|
||||||
completely open source front-end for GCC (the GNU compiler.) Both
|
|
||||||
should work equally well with OpenMW.
|
|
||||||
|
|
||||||
If you want to install GDC manually, go to
|
|
||||||
http://sourceforge.net/projects/dgcc
|
|
||||||
|
|
||||||
If you want to use DMD instead, it can be found at:
|
|
||||||
http://digitalmars.com/d/1.0/dmd-linux.html
|
|
||||||
|
|
||||||
Note that the DMD unstable branch (2.000 and up) will NOT currently
|
|
||||||
work with OpenMW. Use the stable branch (1.x) instead.
|
|
||||||
|
|
||||||
Also note that DMD is only available on 32 bit x86 Linux - other
|
|
||||||
operating systems or architectures will have to use GDC. Note however
|
|
||||||
that GDC might be unstable on these platforms as well, especially on
|
|
||||||
64 bit platforms.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Building:
|
|
||||||
---------
|
|
||||||
|
|
||||||
After installing all the dependencies, you can try running "make cpp"
|
|
||||||
first to see if the C++ parts compile. You may need to alter the
|
|
||||||
Makefile if you are using non-standard include paths, etc. If you have
|
|
||||||
DSSS installed (a D build tool), type:
|
|
||||||
|
|
||||||
dsss build
|
|
||||||
|
|
||||||
If you do NOT have DSSS, try using make
|
|
||||||
|
|
||||||
make all
|
|
||||||
|
|
||||||
You might need to edit the Makefile to match your setup. If you are
|
|
||||||
using DMD instead of GDC, try changing the compiler from "gdmd" to
|
|
||||||
"dmd" in the Makefile.
|
|
||||||
|
|
||||||
If all else fails, you can try the build script:
|
|
||||||
|
|
||||||
./build_openmw.sh
|
|
||||||
|
|
||||||
This build method is deprecated and only works with gdc.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
=============
|
|
||||||
|
|
||||||
Before you can run OpenMW, you have to help it find the Morrowind data
|
|
||||||
files. The 'openmw' program needs the files Morrowind.esm and
|
|
||||||
Morrowind.bsa, and the directories Sound/ and Music/ from your
|
|
||||||
"Morrowind\Data Files\" directory. By default it expects to find these
|
|
||||||
in the data/ directory. (This can be changed in openmw.ini)
|
|
||||||
|
|
||||||
I recommend creating a symbolic link to your original Morrowind
|
|
||||||
install. For example, if you have Morrowind installed in:
|
|
||||||
|
|
||||||
c:\Program Files\Bethesda Softworks\Morrowind\
|
|
||||||
|
|
||||||
and your windows c: drive is mounted on /media/hda1, then run the
|
|
||||||
following command:
|
|
||||||
|
|
||||||
ln -s "/media/hda1/Program Files/Bethesda Softworks/Morrowind/Data Files/" data
|
|
||||||
|
|
||||||
Also, if you have OGRE installed in a non-standard directory (ie. NOT
|
|
||||||
in /usr/lib/OGRE), you have to change the PluginFolder in the file
|
|
||||||
plugins.cfg.linux.
|
|
||||||
|
|
||||||
The first time you run openmw you will be asked to set screen
|
|
||||||
resolution and other graphics settings. You can bring this dialogue up
|
|
||||||
at any time with the -oc command line switch. I don't recommend using
|
|
||||||
fullscreen mode yet, since it might mess up your screen and input
|
|
||||||
settings if the program crashes.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Running OpenMW
|
|
||||||
==============
|
|
||||||
|
|
||||||
If Azura is with you and all the stars and planets are aligned in your
|
|
||||||
favor, you should now be able to run OpenMW using the program called
|
|
||||||
'openmw'.
|
|
||||||
|
|
||||||
Write openmw -h to see a list of options.
|
|
||||||
|
|
||||||
Running without parameters should bring you into the cave called Assu,
|
|
||||||
or the last cell loaded. Move around with WASD (or arrow keys), change
|
|
||||||
physics mode (walking, flying, ghost) with 't', exit with 'q' or
|
|
||||||
escape.
|
|
||||||
|
|
||||||
To load another cell, specify the cell name on the command line. Use
|
|
||||||
the 'esmtool' program to get a list of cells. Note that you must use
|
|
||||||
quotation marks "" if the cell name contains spaces or other weird
|
|
||||||
characters.
|
|
||||||
|
|
||||||
Enjoy! ;-)
|
|
@ -1,83 +0,0 @@
|
|||||||
OpenMW - the completely unofficial reimplementation of Morrowind
|
|
||||||
================================================================
|
|
||||||
|
|
||||||
OpenMW is an open source reimplementation of the Morrowind game
|
|
||||||
engine. For more information, see README.txt or
|
|
||||||
|
|
||||||
http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Building from source
|
|
||||||
====================
|
|
||||||
|
|
||||||
|
|
||||||
Supported Windows platforms:
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
Mostly tested on Windows XP, but Vista is also rumored to work. If you
|
|
||||||
manage to compile or run OpenMW on another Windows platform
|
|
||||||
(9x/Me/NT/2000), please let me know.
|
|
||||||
|
|
||||||
At the moment, only command line building is supported.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Dependencies:
|
|
||||||
-------------
|
|
||||||
|
|
||||||
To compile OpenMW you need Mingw with g++ (a C++ compiler) and gdc
|
|
||||||
(the D compiler.) You can find them here:
|
|
||||||
|
|
||||||
Mingw: http://sourceforge.net/projects/mingw/
|
|
||||||
gdc: http://sourceforge.net/projects/gdcwin/
|
|
||||||
|
|
||||||
Note that the "official" D compiler, DMD, will not currently work on
|
|
||||||
Windows, because it is uses an object format incompatible with most
|
|
||||||
C++ compilers.
|
|
||||||
|
|
||||||
All library dependencies are included in the file
|
|
||||||
openmw-dll-pack.zip. Simply download this file and unpack it in the
|
|
||||||
same directory as the OpenMW source code. You should not need to
|
|
||||||
download any other dependencies.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Setting everything up
|
|
||||||
---------------------
|
|
||||||
|
|
||||||
First, install Mingw (get the automatic installer.) Make sure gcc and
|
|
||||||
g++ packages are selected.
|
|
||||||
|
|
||||||
Next install the gdcwin installer (the package named 'gdc') and
|
|
||||||
install it in the same directory as Mingw.
|
|
||||||
|
|
||||||
Open a command line. Set up your PATH to include Mingw and gdc
|
|
||||||
(eg. "set PATH=%PATH%;c:\mingw\bin").
|
|
||||||
|
|
||||||
Next you must make sure the D include files are found by the
|
|
||||||
compiler. Run the following command:
|
|
||||||
set DFLAGS=-Ic:\mingw\include\d\3.4.5\
|
|
||||||
where the path following -I is the path of your Mingw GDC include
|
|
||||||
directory.
|
|
||||||
|
|
||||||
Make sure the commands g++ and gdc work (should output "no input
|
|
||||||
files".)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Building
|
|
||||||
--------
|
|
||||||
|
|
||||||
To build, simply run build_openmw.bat
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Running
|
|
||||||
-------
|
|
||||||
|
|
||||||
For instructions on how to set everything up after compilation, see
|
|
||||||
README-win32.txt.
|
|
||||||
|
|
||||||
Good luck!
|
|
@ -1,115 +0,0 @@
|
|||||||
# Designed for GNU Make
|
|
||||||
|
|
||||||
# Compiler settings
|
|
||||||
CXXFLAGS?= -g
|
|
||||||
DMD=gdmd -version=Posix
|
|
||||||
|
|
||||||
# Some extra flags for niftool and bsatool
|
|
||||||
NIFFLAGS=-debug=warnstd -debug=check -debug=statecheck -debug=strict -debug=verbose
|
|
||||||
|
|
||||||
# Linker flags
|
|
||||||
LFLAGS= -L-lopenal -L-lOgreMain -L-lOIS -L-lmygui -L-luuid -L-lavcodec -L-lavformat bullet/libbulletdynamics.a bullet/libbulletcollision.a bullet/libbulletmath.a
|
|
||||||
|
|
||||||
# Compiler settings for Ogre, OIS and MyGUI
|
|
||||||
# TODO: the -I when we're done
|
|
||||||
CF_OIS=$(shell pkg-config --cflags OIS OGRE MyGUI) -Iterrain/
|
|
||||||
OGCC=$(CXX) $(CXXFLAGS) $(CF_OIS)
|
|
||||||
|
|
||||||
# Compiler settings for ffmpeg.
|
|
||||||
CF_FFMPEG=$(shell pkg-config --cflags libavcodec libavformat)
|
|
||||||
AVGCC=$(CXX) $(CXXFLAGS) $(CF_FFMPEG)
|
|
||||||
|
|
||||||
# Settings for Bullet
|
|
||||||
CF_BULLET=-Iinclude/bullet
|
|
||||||
BGCC=$(CXX) $(CXXFLAGS) $(CF_BULLET)
|
|
||||||
|
|
||||||
# Ogre C++ files, on the form ogre/cpp_X.cpp. Only the first file is
|
|
||||||
# passed to the compiler, the rest are dependencies.
|
|
||||||
ogre_cpp=ogre framelistener interface bsaarchive
|
|
||||||
|
|
||||||
# MyGUI C++ files, gui/cpp_X.cpp. These are currently included
|
|
||||||
# cpp_ogre.o with cpp_ogre.cpp.
|
|
||||||
mygui_cpp=mygui console
|
|
||||||
|
|
||||||
# Ditto for the landscape engine, in terrain/cpp_X.cpp
|
|
||||||
terrain_cpp=baseland terrain mesh
|
|
||||||
|
|
||||||
# FFmpeg files, in the form sound/cpp_X.cpp.
|
|
||||||
avcodec_cpp=avcodec
|
|
||||||
|
|
||||||
# Bullet cpp files
|
|
||||||
bullet_cpp=bullet player scale
|
|
||||||
|
|
||||||
#### No modifications should be required below this line. ####
|
|
||||||
|
|
||||||
ogre_cpp_files=\
|
|
||||||
$(ogre_cpp:%=ogre/cpp_%.cpp) \
|
|
||||||
$(mygui_cpp:%=gui/cpp_%.cpp) \
|
|
||||||
$(terrain_cpp:%=terrain/cpp_%.cpp)
|
|
||||||
avcodec_cpp_files=$(avcodec_cpp:%=sound/cpp_%.cpp)
|
|
||||||
bullet_cpp_files=$(bullet_cpp:%=bullet/cpp_%.cpp)
|
|
||||||
|
|
||||||
# All object files needed by openmw and esmtool
|
|
||||||
src := $(wildcard bsa/*.d) $(wildcard bullet/*.d) $(wildcard core/*.d) \
|
|
||||||
$(wildcard esm/*.d) $(wildcard input/*.d) $(wildcard nif/*.d) $(wildcard ogre/*.d) \
|
|
||||||
$(wildcard scene/*.d) $(wildcard sound/*.d) $(wildcard util/*.d) $(wildcard gui/*.d)
|
|
||||||
src := $(src) $(wildcard mscripts/*.d) $(wildcard terrain/*.d)
|
|
||||||
src := $(src) monster/monster.d \
|
|
||||||
$(wildcard monster/vm/*.d) \
|
|
||||||
$(wildcard monster/compiler/*.d) \
|
|
||||||
$(wildcard monster/util/*.d) \
|
|
||||||
$(wildcard monster/modules/*.d)
|
|
||||||
obj := $(src:%.d=objs/%.o)
|
|
||||||
|
|
||||||
# The NIF object files for niftool and bsatool are put in a separate
|
|
||||||
# directory, since they are built with different flags.
|
|
||||||
src_nif := $(wildcard nif/*.d)
|
|
||||||
src_nif := $(src_nif) $(wildcard util/*.d)
|
|
||||||
src_nif := $(src_nif) core/memory.d
|
|
||||||
src_nif := $(src_nif) $(wildcard monster/util/*.d)
|
|
||||||
obj_nif := $(src_nif:%.d=nifobjs/%.o)
|
|
||||||
|
|
||||||
.PHONY: cpp all clean
|
|
||||||
|
|
||||||
# Build everything. Default when running 'make' directly.
|
|
||||||
all: openmw esmtool niftool bsatool bored
|
|
||||||
|
|
||||||
# Only build C++ sources. Used when building from DSSS.
|
|
||||||
cpp: cpp_ogre.o cpp_avcodec.o cpp_bullet.o
|
|
||||||
|
|
||||||
cpp_ogre.o: $(ogre_cpp_files)
|
|
||||||
$(OGCC) -o $@ -c $<
|
|
||||||
|
|
||||||
cpp_avcodec.o: $(avcodec_cpp_files)
|
|
||||||
$(AVGCC) -o $@ -c $<
|
|
||||||
|
|
||||||
cpp_bullet.o: $(bullet_cpp_files)
|
|
||||||
$(BGCC) -o $@ -c $<
|
|
||||||
|
|
||||||
objs/%.o: %.d
|
|
||||||
dirname $@ | xargs mkdir -p
|
|
||||||
$(DMD) -c $< -of$@
|
|
||||||
|
|
||||||
nifobjs/%.o: %.d
|
|
||||||
dirname $@ | xargs mkdir -p
|
|
||||||
$(DMD) $(NIFFLAGS) -c $< -of$@
|
|
||||||
|
|
||||||
openmw: openmw.d cpp_ogre.o cpp_avcodec.o cpp_bullet.o $(obj)
|
|
||||||
$(DMD) $^ -of$@ $(LFLAGS)
|
|
||||||
|
|
||||||
esmtool: esmtool.d cpp_ogre.o cpp_avcodec.o cpp_bullet.o $(obj)
|
|
||||||
$(DMD) $^ -of$@ $(LFLAGS)
|
|
||||||
|
|
||||||
niftool: niftool.d $(obj_nif)
|
|
||||||
$(DMD) $^ -of$@
|
|
||||||
|
|
||||||
bsatool: bsatool.d $(obj_nif) objs/bsa/bsafile.o
|
|
||||||
$(DMD) $^ -of$@
|
|
||||||
|
|
||||||
bored: bored.d
|
|
||||||
$(DMD) $^
|
|
||||||
|
|
||||||
clean:
|
|
||||||
-rm -f cpp_bullet.o cpp_ogre.o cpp_avcodec.o bored.o bsafile.o bsatool.o esmtool.o niftool.o openmw.o
|
|
||||||
-rm -f openmw esmtool niftool bsatool bored
|
|
||||||
-rm -rf objs/ nifobjs/ dsss_objs/
|
|
@ -1,54 +0,0 @@
|
|||||||
OpenMW - the completely unofficial reimplementation of Morrowind
|
|
||||||
================================================================
|
|
||||||
|
|
||||||
OpenMW is an open source reimplementation of the Morrowind game
|
|
||||||
engine. For more information, see README.txt or
|
|
||||||
|
|
||||||
http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Running OpenMW
|
|
||||||
==============
|
|
||||||
|
|
||||||
OpenMW consists of three separate downloads:
|
|
||||||
|
|
||||||
openmw-0.X_win32.zip - binary package (with EXE file)
|
|
||||||
openmw-0.X.zip - source code
|
|
||||||
openmw-dll-pack.zip - library pack
|
|
||||||
|
|
||||||
|
|
||||||
You only need the binary or the source package, not both. The DLL pack
|
|
||||||
is needed in both cases.
|
|
||||||
|
|
||||||
NOTE: If downloaded the SOURCE release, please read COMPILE-win32.txt
|
|
||||||
before reading the rest of this file.
|
|
||||||
|
|
||||||
|
|
||||||
Configuration
|
|
||||||
-------------------
|
|
||||||
|
|
||||||
OpenMW assumes you have the Morrowind data files in c:\Program
|
|
||||||
Files\Bethesda Softworks\Morrowind\Data Files\ . If this is not where
|
|
||||||
you have installed Morrowind, you should edit the file openmw.ini.
|
|
||||||
|
|
||||||
|
|
||||||
Running
|
|
||||||
-------
|
|
||||||
|
|
||||||
Just run openmw.exe and enjoy! ;-)
|
|
||||||
|
|
||||||
The first time you run OpenMW, you will be asked to set screen
|
|
||||||
resolution and other graphics settings. To be safe, I don't recommend
|
|
||||||
setting fullscreen mode on the first run. You can bring up the
|
|
||||||
dialogue at any time by using the -oc switch.
|
|
||||||
|
|
||||||
Move around with WASD or arrow keys, change physics mode (walking,
|
|
||||||
flying, ghost) with 't', exit with 'q' or escape. You can change key
|
|
||||||
bindings in the openmw.ini file.
|
|
||||||
|
|
||||||
You start in a cell called "Assu". You can change this in openmw.ini or
|
|
||||||
by specifying a cell name on the command line.
|
|
||||||
|
|
||||||
Write openmw -h on the command line to see a complete list of options.
|
|
@ -1,111 +0,0 @@
|
|||||||
OpenMW - the completely unofficial reimplementation of Morrowind
|
|
||||||
================================================================
|
|
||||||
|
|
||||||
Written by Nicolay Korslund
|
|
||||||
Email: korslund@gmail.com
|
|
||||||
WWW: http://openmw.sourceforge.net
|
|
||||||
License: See GPL3.txt
|
|
||||||
Current version: 0.6
|
|
||||||
Date: 2009 mar. 2
|
|
||||||
|
|
||||||
|
|
||||||
QUICK NOTE: You must own and install Morrowind before you can use
|
|
||||||
OpenMW. Let me repeat that: OpenMW will NOT run if you do not have
|
|
||||||
Morrowind installed on your system!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Installation
|
|
||||||
============
|
|
||||||
|
|
||||||
Currently supported platforms are Windows, Linux and FreeBSD. Most
|
|
||||||
testing is done on Ubuntu 8.04 and Windows XP Professional.
|
|
||||||
|
|
||||||
For instructions, see one of the following:
|
|
||||||
|
|
||||||
README-win32.txt - instructions for binary Windows release
|
|
||||||
COMPILE-win32.txt - instructions for building from source on Windows
|
|
||||||
COMPILE-linux.tx - instructions for building from source on Linux / Unix
|
|
||||||
|
|
||||||
Linux 64 does NOT work, because of problems with the D compiler. We
|
|
||||||
hope to sort this out at some point, but right now it's simply not
|
|
||||||
supported.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Programs included in this package:
|
|
||||||
==================================
|
|
||||||
|
|
||||||
openmw - The main program. Run openmw -h for a list of options.
|
|
||||||
|
|
||||||
esmtool - Used to inspect ES files (ESM, ESP, ESS). Run without
|
|
||||||
arguments to get a list of options.
|
|
||||||
|
|
||||||
bsatool - Tool for viewing and extracting files from BSA archives.
|
|
||||||
(Can also be used to test the NIF parser on a BSA.)
|
|
||||||
|
|
||||||
niftool - Decodes one or more NIF files and prints the details.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Changelog:
|
|
||||||
==========
|
|
||||||
|
|
||||||
0.6 (2009 mar. 2) - latest release
|
|
||||||
|
|
||||||
- coded a GUI system using MyGUI
|
|
||||||
- skinned MyGUI to look like Morrowind (work in progress)
|
|
||||||
- integrated the Monster-script engine
|
|
||||||
- rewrote some parts into script code
|
|
||||||
- very early MyGUI <-> Monster binding
|
|
||||||
- fixed Windows sound problems (replaced old openal32.dll)
|
|
||||||
|
|
||||||
|
|
||||||
0.5 (2008 nov. 5)
|
|
||||||
|
|
||||||
- Collision detection with Bullet
|
|
||||||
- Experimental walk & fall character physics
|
|
||||||
- New key bindings:
|
|
||||||
t - toggle physics mode (walking, flying, ghost)
|
|
||||||
n - night-eye: brightens the scene
|
|
||||||
- Fixed incompatability with DMD 1.032 and newer
|
|
||||||
- Various minor changes and updates
|
|
||||||
|
|
||||||
|
|
||||||
0.4 (2008 aug. 30)
|
|
||||||
|
|
||||||
- switched from Audiere to OpenAL (BIG thanks to Chris Robinson)
|
|
||||||
- added complete Makefile (again) as a alternative build tool
|
|
||||||
- more realistic lighting (thanks again to Chris Robinson)
|
|
||||||
- various localization fixes - tested with Russian and French versions
|
|
||||||
- temporary workaround for the Unicode issue: invalid UTF displayed as '?'
|
|
||||||
- added -ns option to disable sound, for debugging
|
|
||||||
- various bug-fixes
|
|
||||||
- cosmetic changes to placate gdc -Wall
|
|
||||||
|
|
||||||
|
|
||||||
0.3 (2008 jul. 10)
|
|
||||||
|
|
||||||
- built and tested on Windows XP
|
|
||||||
- partial support for FreeBSD (exceptions do not work)
|
|
||||||
- temporarily dropped DSSS and Monster as necessary dependencies
|
|
||||||
- renamed main program from 'morro' to 'openmw'
|
|
||||||
- made the config system more robust
|
|
||||||
- added -oc switch for showing Ogre config window on startup
|
|
||||||
- removed some config files, these are auto-generated when
|
|
||||||
missing. Separated plugins.cfg into linux and windows versions.
|
|
||||||
- updated Makefile and sources for increased portability (thanks to
|
|
||||||
Dmitry Marakasov.)
|
|
||||||
- tested against OIS 1.0.0 (Ubuntu repository package)
|
|
||||||
|
|
||||||
|
|
||||||
0.2 (2008 jun. 17)
|
|
||||||
|
|
||||||
- compiles with gdc
|
|
||||||
- switched to DSSS for building D code
|
|
||||||
- includes the program esmtool
|
|
||||||
|
|
||||||
|
|
||||||
0.1 (2008 jun. 03)
|
|
||||||
|
|
||||||
- first release
|
|
@ -1,11 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
rem See COMPILE-win32.txt for instructions.
|
|
||||||
|
|
||||||
echo Compiling C++ files
|
|
||||||
g++ -c sound\cpp_avcodec.cpp -I.\includes\ffmpeg\
|
|
||||||
g++ -c ogre\cpp_ogre.cpp -I.\includes\ogre\ -I.\includes\mygui\
|
|
||||||
g++ -c bullet\cpp_bullet.cpp -I.\includes\bullet\
|
|
||||||
|
|
||||||
echo Compiling main program (openmw.exe)
|
|
||||||
gdc -g openmw.d bsa\*.d core\*.d esm\*.d input\*.d nif\*.d ogre\*.d scene\*.d sound\*.d util\*.d bullet\*.d cpp_ogre.o cpp_avcodec.o cpp_bullet.o libbulletdynamics.a libbulletcollision.a libbulletmath.a mscripts\setup.d monster\monster.d monster\compiler\*.d monster\vm\*.d monster\util\*.d monster\modules\*.d avcodec-51.dll avformat-52.dll avdevice-52.dll avutil-49.dll openal32.dll mygui.a freetype6.dll -lole32 ogremain_d.dll OIS_d.dll -lstdc++ -o openmw.exe
|
|
@ -1,275 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (inifile.d) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module core.inifile;
|
|
||||||
|
|
||||||
import std.stdio;
|
|
||||||
import std.stream;
|
|
||||||
import std.string;
|
|
||||||
import std.path;
|
|
||||||
import std.file;
|
|
||||||
|
|
||||||
import monster.util.string;
|
|
||||||
import monster.util.aa;
|
|
||||||
|
|
||||||
void safeMkdir(char[] npath)
|
|
||||||
{
|
|
||||||
char curDir[];
|
|
||||||
int index = 0;
|
|
||||||
int srch;
|
|
||||||
|
|
||||||
do
|
|
||||||
{
|
|
||||||
while(npath[index..$].begins("/"))
|
|
||||||
index++;
|
|
||||||
|
|
||||||
srch = npath[index..$].find('/');
|
|
||||||
|
|
||||||
if(srch == -1)
|
|
||||||
curDir = npath;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
index += srch;
|
|
||||||
curDir = npath[0..index];
|
|
||||||
}
|
|
||||||
index++;
|
|
||||||
|
|
||||||
if(!exists(curDir) || !isdir(curDir))
|
|
||||||
mkdir(curDir);
|
|
||||||
}
|
|
||||||
while(srch != -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Writes an ini file.
|
|
||||||
struct IniWriter
|
|
||||||
{
|
|
||||||
File ini;
|
|
||||||
bool firstSection = true;
|
|
||||||
|
|
||||||
void openFile(char[] file)
|
|
||||||
{
|
|
||||||
if(ini is null) ini = new File();
|
|
||||||
char[] oldFile = file~".old";
|
|
||||||
|
|
||||||
if(exists(file))
|
|
||||||
copy(file, oldFile);
|
|
||||||
|
|
||||||
// Make sure the output directory exists
|
|
||||||
char[] dr = getDirName(file);
|
|
||||||
if(dr.length)
|
|
||||||
safeMkdir(dr);
|
|
||||||
|
|
||||||
ini.open(file, FileMode.OutNew);
|
|
||||||
}
|
|
||||||
|
|
||||||
void close()
|
|
||||||
{
|
|
||||||
ini.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void section(char[] name)
|
|
||||||
{
|
|
||||||
if(firstSection)
|
|
||||||
firstSection = false;
|
|
||||||
else
|
|
||||||
ini.writefln();
|
|
||||||
|
|
||||||
ini.writefln("[%s]", name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void comment(char[] str)
|
|
||||||
{
|
|
||||||
ini.writefln("; %s", str);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeType(T)(char[] name, T t)
|
|
||||||
{
|
|
||||||
ini.writefln("%s=%s", name, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
alias writeType!(int) writeInt;
|
|
||||||
alias writeType!(char[]) writeString;
|
|
||||||
|
|
||||||
void writeFloat(char[] name, float f)
|
|
||||||
{
|
|
||||||
ini.writefln("%s=%.3s", name, f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void writeBool(char[] name, bool b)
|
|
||||||
{
|
|
||||||
ini.writefln("%s=%s", name, b?"yes":"no");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Keytype that holds section name and variable name
|
|
||||||
struct SecVar
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
char[] section;
|
|
||||||
char[] variable;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
bool opEquals(ref SecVar v)
|
|
||||||
{
|
|
||||||
return section == v.section && variable == v.variable;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint toHash()
|
|
||||||
{
|
|
||||||
const auto ID = typeid(char[]);
|
|
||||||
return ID.getHash(§ion) + ID.getHash(&variable);
|
|
||||||
}
|
|
||||||
|
|
||||||
static SecVar opCall(char[] sec, char[] var)
|
|
||||||
{
|
|
||||||
SecVar sv;
|
|
||||||
sv.section = sec;
|
|
||||||
sv.variable = var;
|
|
||||||
return sv;
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] toString()
|
|
||||||
{
|
|
||||||
return "[" ~ section ~ "]:" ~ variable;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IniReader
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
HashTable!(SecVar, char[]) vars;
|
|
||||||
char[] section;
|
|
||||||
|
|
||||||
// Start a new section
|
|
||||||
void setSection(char[] sec)
|
|
||||||
{
|
|
||||||
section = sec.dup;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert a new value from file
|
|
||||||
void set(char[] variable, char[] value)
|
|
||||||
{
|
|
||||||
vars[SecVar(section, variable.dup)] = value.dup;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
vars.reset();
|
|
||||||
section = null;
|
|
||||||
wasRead = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getInt(char[] sec, char[] var, int def)
|
|
||||||
{
|
|
||||||
char[] value;
|
|
||||||
if(vars.inList(SecVar(sec,var), value))
|
|
||||||
return cast(int)atoi(value);
|
|
||||||
else
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
float getFloat(char[] sec, char[] var, float def)
|
|
||||||
{
|
|
||||||
char[] value;
|
|
||||||
if(vars.inList(SecVar(sec,var), value))
|
|
||||||
return atof(value);
|
|
||||||
else
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] getString(char[] sec, char[] var, char[] def)
|
|
||||||
{
|
|
||||||
char[] value;
|
|
||||||
if(vars.inList(SecVar(sec,var), value))
|
|
||||||
return value;
|
|
||||||
else
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return true if the string matches some case of 'yes', and false
|
|
||||||
// otherwise.
|
|
||||||
bool getBool(char[] sec, char[] var, bool def)
|
|
||||||
{
|
|
||||||
char[] value;
|
|
||||||
if(vars.inList(SecVar(sec,var), value))
|
|
||||||
return icmp(value, "yes") == 0;
|
|
||||||
else
|
|
||||||
return def;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool wasRead = false;
|
|
||||||
|
|
||||||
void readFile(char[] fn)
|
|
||||||
{
|
|
||||||
// Reset this struct
|
|
||||||
reset();
|
|
||||||
|
|
||||||
// If the file doesn't exist, simply exit. This will work fine,
|
|
||||||
// and default values will be used instead.
|
|
||||||
if(!exists(fn)) return;
|
|
||||||
|
|
||||||
wasRead = true;
|
|
||||||
|
|
||||||
// Read buffer. Finite in size but perfectly safe - the readLine
|
|
||||||
// routine allocates more mem if it needs it.
|
|
||||||
char[300] buffer;
|
|
||||||
|
|
||||||
scope File ini = new File(fn);
|
|
||||||
while(!ini.eof)
|
|
||||||
{
|
|
||||||
char[] line = ini.readLine(buffer);
|
|
||||||
|
|
||||||
// Remove leading and trailing whitespace
|
|
||||||
line = strip(line);
|
|
||||||
|
|
||||||
// Ignore comments and blank lines
|
|
||||||
if(line.length == 0 || line.begins(";")) continue;
|
|
||||||
|
|
||||||
// New section?
|
|
||||||
if(line.begins("["))
|
|
||||||
{
|
|
||||||
if(!line.ends("]"))
|
|
||||||
{
|
|
||||||
//writefln("Malformed section: %s", line);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
setSection(line[1..$-1]);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Split line into a key and a value
|
|
||||||
int index = line.find('=');
|
|
||||||
if(index != -1)
|
|
||||||
{
|
|
||||||
char[] value = line[index+1..$];
|
|
||||||
line = line[0..index];
|
|
||||||
set(line, value);
|
|
||||||
}
|
|
||||||
//else writefln("Malformed value: '%s'", line);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (memory.d) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module core.memory;
|
|
||||||
|
|
||||||
import util.regions;
|
|
||||||
|
|
||||||
// Global memory managers used by various loading routines
|
|
||||||
RegionManager
|
|
||||||
esmRegion, // Memory used by ESMs, BSAs and plugins. Cleared only
|
|
||||||
// when all files are reloaded. Lifetime is current
|
|
||||||
// plugin setting session - if we change the set of
|
|
||||||
// plugins, this region is reset and everything is
|
|
||||||
// reloaded.
|
|
||||||
|
|
||||||
gameRegion, // Memory used in current game. Cleared whenever a new
|
|
||||||
// game is loaded. Lifetime is the current game
|
|
||||||
// session, loading a saved game will reset the
|
|
||||||
// region.
|
|
||||||
|
|
||||||
nifRegion; // Used by the NIF loader. Cleared when a NIF file has
|
|
||||||
// been inserted into OGRE and the file data is no
|
|
||||||
// longer needed. In other words this is cleared
|
|
||||||
// immediately after loading each NIF file.
|
|
||||||
|
|
||||||
// AA allocator that uses esmRegion
|
|
||||||
struct ESMRegionAlloc
|
|
||||||
{
|
|
||||||
static const bool autoinit = false;
|
|
||||||
static void* alloc(uint size) { return esmRegion.allocate(size).ptr; }
|
|
||||||
static void free(void* p) { }
|
|
||||||
}
|
|
||||||
|
|
||||||
void initializeMemoryRegions()
|
|
||||||
{
|
|
||||||
// Default block sizes are probably ok
|
|
||||||
esmRegion = new RegionManager("ESM");
|
|
||||||
gameRegion = new RegionManager("GAME");
|
|
||||||
nifRegion = new RegionManager("NIF");
|
|
||||||
}
|
|
@ -1,508 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (resource.d) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module core.resource;
|
|
||||||
|
|
||||||
import std.stdio;
|
|
||||||
import std.string;
|
|
||||||
import std.stream;
|
|
||||||
import std.file;
|
|
||||||
import std.path;
|
|
||||||
|
|
||||||
import monster.util.aa;
|
|
||||||
import monster.util.string;
|
|
||||||
|
|
||||||
import bsa.bsafile;
|
|
||||||
|
|
||||||
import bullet.bindings;
|
|
||||||
|
|
||||||
import core.memory;
|
|
||||||
import core.config;
|
|
||||||
|
|
||||||
import ogre.bindings;
|
|
||||||
import ogre.meshloader;
|
|
||||||
|
|
||||||
import sound.audio;
|
|
||||||
import sound.sfx;
|
|
||||||
|
|
||||||
import nif.nif;
|
|
||||||
|
|
||||||
import core.filefinder;
|
|
||||||
|
|
||||||
// These are handles for various resources. They may refer to a file
|
|
||||||
// in the file system, an entry in a BSA archive, or point to an
|
|
||||||
// already loaded resource. Resource handles that are not implemented
|
|
||||||
// yet are typedefed as ints for the moment.
|
|
||||||
typedef int IconIndex;
|
|
||||||
|
|
||||||
alias SoundResource* SoundIndex;
|
|
||||||
alias TextureResource* TextureIndex;
|
|
||||||
alias MeshResource* MeshIndex;
|
|
||||||
|
|
||||||
ResourceManager resources;
|
|
||||||
|
|
||||||
// Called from ogre/cpp_bsaarchive.cpp. We will probably move these
|
|
||||||
// later.
|
|
||||||
extern(C)
|
|
||||||
{
|
|
||||||
// Does the file exist in the archives?
|
|
||||||
int d_bsaExists(char *filename)
|
|
||||||
{
|
|
||||||
char[] name = toString(filename);
|
|
||||||
|
|
||||||
auto res = resources.lookupTexture(name);
|
|
||||||
if(res.bsaFile != -1)
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open a file. Return the pointer and size.
|
|
||||||
void d_bsaOpenFile(char *filename,
|
|
||||||
void **retPtr, uint *retSize)
|
|
||||||
{
|
|
||||||
char[] name = toString(filename);
|
|
||||||
void[] result;
|
|
||||||
|
|
||||||
//writefln("calling d_bsaOpenFile(%s, %s)", bsaFile, name);
|
|
||||||
|
|
||||||
auto tex = resources.lookupTexture(name);
|
|
||||||
|
|
||||||
if(tex.bsaFile == -1) result = null;
|
|
||||||
else result = resources.archives[tex.bsaFile].findSlice(tex.bsaIndex);
|
|
||||||
*retPtr = result.ptr;
|
|
||||||
*retSize = result.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ResourceManager
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
// Holds lists of resources in the file system.
|
|
||||||
FileFinder
|
|
||||||
meshes,
|
|
||||||
icons,
|
|
||||||
textures,
|
|
||||||
sounds,
|
|
||||||
bookart,
|
|
||||||
bsa, esm, esp;
|
|
||||||
|
|
||||||
// Archives
|
|
||||||
BSAFile archives[];
|
|
||||||
|
|
||||||
// List of resources that have already been looked up (case
|
|
||||||
// insensitive)
|
|
||||||
HashTable!(char[], MeshIndex, ESMRegionAlloc, CITextHash) meshLookup;
|
|
||||||
HashTable!(char[], TextureIndex, ESMRegionAlloc, CITextHash) textureLookup;
|
|
||||||
HashTable!(char[], SoundIndex, ESMRegionAlloc, CITextHash) soundLookup;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Hack. Set to true in esmtool to disable all the lookup*
|
|
||||||
// functions.
|
|
||||||
bool dummy = false;
|
|
||||||
|
|
||||||
void initResources()
|
|
||||||
{
|
|
||||||
bsa = new FileFinder(config.bsaDir, "bsa", Recurse.No);
|
|
||||||
archives.length = bsa.length;
|
|
||||||
foreach(int i, ref BSAFile f; archives)
|
|
||||||
f = new BSAFile(bsa[i+1]);
|
|
||||||
|
|
||||||
sounds = new FileFinder(config.sndDir);
|
|
||||||
|
|
||||||
// Simple playlists, similar to the Morrowind one. Later I imagine
|
|
||||||
// adding an interactive MP3 player with a bit more finesse, but
|
|
||||||
// this will do for now.
|
|
||||||
char[][] music;
|
|
||||||
|
|
||||||
char[][] getDir(char[] dir)
|
|
||||||
{
|
|
||||||
dir = FileFinder.addSlash(dir);
|
|
||||||
char[][] res = ((exists(dir) && isdir(dir)) ? listdir(dir) : null);
|
|
||||||
foreach(ref char[] fn; res)
|
|
||||||
fn = dir ~ fn;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
Music.setPlaylists(getDir(config.musDir),
|
|
||||||
getDir(config.musDir2));
|
|
||||||
|
|
||||||
meshLookup.reset();
|
|
||||||
textureLookup.reset();
|
|
||||||
soundLookup.reset();
|
|
||||||
|
|
||||||
meshBuffer[0..7] = "meshes\\";
|
|
||||||
texBuffer[0..9] = "textures\\";
|
|
||||||
}
|
|
||||||
|
|
||||||
// These three functions are so similar that I should probably split
|
|
||||||
// out big parts of them into one common function.
|
|
||||||
SoundIndex lookupSound(char[] id)
|
|
||||||
{
|
|
||||||
if(dummy) return null;
|
|
||||||
|
|
||||||
assert(id != "", "loadSound called with empty id");
|
|
||||||
|
|
||||||
SoundIndex si;
|
|
||||||
|
|
||||||
if( soundLookup.inList(id, si) ) return si;
|
|
||||||
|
|
||||||
si = esmRegion.newT!(SoundResource);
|
|
||||||
|
|
||||||
// Check if the file exists
|
|
||||||
int index = sounds[id];
|
|
||||||
|
|
||||||
// If so, get the real file name
|
|
||||||
if(index) si.file = sounds[index];
|
|
||||||
// Otherwise, make this an empty resource
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//writefln("Lookup failed to find sound %s", id);
|
|
||||||
si.file = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
si.res.loaded = false;
|
|
||||||
|
|
||||||
// Copy name and insert. We MUST copy here, since indices during
|
|
||||||
// load are put in a temporary buffer, and thus overwritten.
|
|
||||||
si.name = esmRegion.copyz(id);
|
|
||||||
assert(si.name == id);
|
|
||||||
soundLookup[si.name] = si;
|
|
||||||
|
|
||||||
return si;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quick but effective hack
|
|
||||||
char[80] meshBuffer;
|
|
||||||
|
|
||||||
MeshIndex lookupMesh(char[] id)
|
|
||||||
{
|
|
||||||
if(dummy) return null;
|
|
||||||
|
|
||||||
MeshIndex mi;
|
|
||||||
|
|
||||||
// If it is already looked up, return the result
|
|
||||||
if( meshLookup.inList(id, mi) ) return mi;
|
|
||||||
|
|
||||||
mi = esmRegion.newT!(MeshResource);
|
|
||||||
|
|
||||||
// If not, find it. For now we only check the BSA.
|
|
||||||
char[] search;
|
|
||||||
if(id.length < 70)
|
|
||||||
{
|
|
||||||
// Go to great lengths to avoid the concat :) The speed gain
|
|
||||||
// is negligible (~ 1%), but the GC memory usage is HALVED!
|
|
||||||
// This may mean significantly fewer GC collects during a long
|
|
||||||
// run of the program.
|
|
||||||
meshBuffer[7..7+id.length] = id;
|
|
||||||
search = meshBuffer[0..7+id.length];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
search = "meshes\\" ~ id;
|
|
||||||
|
|
||||||
//writefln("lookupMesh(%s): searching for %s", id, search);
|
|
||||||
|
|
||||||
mi.bsaIndex = -1;
|
|
||||||
mi.bsaFile = -1;
|
|
||||||
foreach(int ind, BSAFile bs; archives)
|
|
||||||
{
|
|
||||||
mi.bsaIndex = bs.getIndex(search);
|
|
||||||
if(mi.bsaIndex != -1) // Found something
|
|
||||||
{
|
|
||||||
mi.bsaFile = ind;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mi.bsaIndex == -1)
|
|
||||||
{
|
|
||||||
//writefln("Lookup failed to find mesh %s", search);
|
|
||||||
assert(mi.bsaFile == -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resource is not loaded
|
|
||||||
mi.node = null;
|
|
||||||
|
|
||||||
// Make a copy of the id
|
|
||||||
mi.name = esmRegion.copyz(id);
|
|
||||||
meshLookup[mi.name] = mi;
|
|
||||||
|
|
||||||
return mi;
|
|
||||||
}
|
|
||||||
|
|
||||||
char[80] texBuffer;
|
|
||||||
|
|
||||||
// Checks the BSAs / file system for a given texture name. Tries
|
|
||||||
// various Morrowind-specific hacks, like changing the extension to
|
|
||||||
// .dds and adding a 'textures\' prefix. Does not load the texture.
|
|
||||||
TextureIndex lookupTexture(char[] id)
|
|
||||||
{
|
|
||||||
if(dummy) return null;
|
|
||||||
|
|
||||||
TextureIndex ti;
|
|
||||||
|
|
||||||
// Checked if we have looked it up before
|
|
||||||
if( textureLookup.inList(id, ti) ) return ti;
|
|
||||||
|
|
||||||
// Create a new resource locator
|
|
||||||
ti = esmRegion.newT!(TextureResource);
|
|
||||||
|
|
||||||
ti.name = esmRegion.copyz(id);
|
|
||||||
ti.newName = ti.name;
|
|
||||||
ti.type = ti.name[$-3..$];
|
|
||||||
|
|
||||||
void searchBSAs(char[] search)
|
|
||||||
{
|
|
||||||
// Look it up in the BSA
|
|
||||||
ti.bsaIndex = -1;
|
|
||||||
ti.bsaFile = -1;
|
|
||||||
foreach(int ind, BSAFile bs; archives)
|
|
||||||
{
|
|
||||||
ti.bsaIndex = bs.getIndex(search);
|
|
||||||
if(ti.bsaIndex != -1) // Found something
|
|
||||||
{
|
|
||||||
ti.bsaFile = ind;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void searchWithDDS(char[] search)
|
|
||||||
{
|
|
||||||
searchBSAs(search);
|
|
||||||
|
|
||||||
// If we can't find it, try the same filename but with .dds as
|
|
||||||
// the extension. Bethesda did at some point convert all their
|
|
||||||
// textures to dds to improve loading times. However, they did
|
|
||||||
// not update their esm-files or require them to use the
|
|
||||||
// correct extention (if they had, it would have broken a lot
|
|
||||||
// of user mods). So we must support files that are referenced
|
|
||||||
// as eg .tga but stored as .dds.
|
|
||||||
if(ti.bsaIndex == -1 && ti.type != "dds")
|
|
||||||
{
|
|
||||||
search[$-3..$] = "dds";
|
|
||||||
searchBSAs(search);
|
|
||||||
if(ti.bsaIndex != -1)
|
|
||||||
{
|
|
||||||
// Store the real name in newName.
|
|
||||||
ti.newName = esmRegion.copyz(ti.name);
|
|
||||||
|
|
||||||
// Get a slice of the extension and overwrite it.
|
|
||||||
ti.type = ti.newName[$-3..$];
|
|
||||||
ti.type[] = "dds";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Search for 'texture\name' first
|
|
||||||
char[] tmp;
|
|
||||||
if(id.length < 70)
|
|
||||||
{
|
|
||||||
// Avoid memory allocations if possible.
|
|
||||||
tmp = texBuffer[0..9+id.length];
|
|
||||||
tmp[9..$] = id;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tmp = "textures\\" ~ id;
|
|
||||||
writefln("WARNING: Did an allocation on %s", tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
searchWithDDS(tmp);
|
|
||||||
|
|
||||||
// Not found? Try without the 'texture\'
|
|
||||||
if(ti.bsaIndex == -1)
|
|
||||||
{
|
|
||||||
tmp = tmp[9..$];
|
|
||||||
tmp[] = id; // Reset the name (replace .dds with the original)
|
|
||||||
|
|
||||||
searchWithDDS(tmp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check that extensions match, to be on the safe side
|
|
||||||
assert(ti.type == ti.newName[$-3..$]);
|
|
||||||
|
|
||||||
if(ti.bsaIndex == -1)
|
|
||||||
{
|
|
||||||
//writefln("Lookup failed to find texture %s", tmp);
|
|
||||||
assert(ti.bsaFile == -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
textureLookup[ti.name] = ti;
|
|
||||||
|
|
||||||
return ti;
|
|
||||||
}
|
|
||||||
|
|
||||||
IconIndex lookupIcon(char[] id) { return -3; }
|
|
||||||
|
|
||||||
// Inserts a given mesh into ogre. Currently only reads the BSA
|
|
||||||
// file. Is only called from within MeshResource itself, and should
|
|
||||||
// never be called when the mesh is already loaded.
|
|
||||||
private void loadMesh(MeshIndex mi)
|
|
||||||
in
|
|
||||||
{
|
|
||||||
assert(!mi.isLoaded);
|
|
||||||
assert(!mi.isEmpty);
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
// Get a slice of the mesh
|
|
||||||
void[] s = archives[mi.bsaFile].findSlice(mi.bsaIndex);
|
|
||||||
|
|
||||||
// Load the NIF into memory. No need to call close(), the file is
|
|
||||||
// automatically closed when the data is loaded.
|
|
||||||
nifMesh.open(s, mi.name);
|
|
||||||
|
|
||||||
// Load and insert nif
|
|
||||||
// TODO: Might add BSA name to the handle name, for clarity
|
|
||||||
meshLoader.loadMesh(mi.name, mi.node, mi.shape);
|
|
||||||
|
|
||||||
// TODO: We could clear the BSA memory mapping here to free some
|
|
||||||
// mem
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SoundResource
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
char[] name;
|
|
||||||
char[] file;
|
|
||||||
SoundFile res;
|
|
||||||
|
|
||||||
public:
|
|
||||||
char[] getName() { return name; }
|
|
||||||
|
|
||||||
SoundInstance getInstance()
|
|
||||||
in
|
|
||||||
{
|
|
||||||
assert(!isEmpty());
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
if(!isLoaded())
|
|
||||||
res.load(file);
|
|
||||||
|
|
||||||
return res.getInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isEmpty() { return file == ""; }
|
|
||||||
bool isLoaded() { return res.loaded; }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MeshResource
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
char[] name;
|
|
||||||
int bsaFile;
|
|
||||||
int bsaIndex;
|
|
||||||
|
|
||||||
// Points to the 'template' SceneNode of this mesh. Is null if this
|
|
||||||
// mesh hasn't been inserted yet.
|
|
||||||
NodePtr node;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Bullet collision shape. Can be null.
|
|
||||||
BulletShape shape;
|
|
||||||
|
|
||||||
NodePtr getNode()
|
|
||||||
in
|
|
||||||
{
|
|
||||||
assert(!isEmpty());
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
if(node == null) resources.loadMesh(this);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] getName() { return name; }
|
|
||||||
|
|
||||||
// Returns true if this resource does not exist (ie. file not found)
|
|
||||||
// TODO: This must be modified later for non-BSA files.
|
|
||||||
bool isEmpty()
|
|
||||||
{
|
|
||||||
return bsaIndex == -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if resource is loaded
|
|
||||||
bool isLoaded()
|
|
||||||
{
|
|
||||||
return node != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct TextureResource
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
char[] name;
|
|
||||||
char[] newName; // Converted name, ie. with extension converted to
|
|
||||||
// .dds if necessary
|
|
||||||
int bsaFile; // If set to -1, the file is in the file system
|
|
||||||
int bsaIndex;
|
|
||||||
char[] type; // Texture format, eg "tga" or "dds";
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
char[] getName() { return name; }
|
|
||||||
char[] getNewName() { return newName; }
|
|
||||||
|
|
||||||
// Returns true if this resource does not exist (ie. file not found)
|
|
||||||
bool isEmpty()
|
|
||||||
{
|
|
||||||
return bsaIndex == -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// OLD STUFF
|
|
||||||
/+
|
|
||||||
|
|
||||||
void initResourceManager()
|
|
||||||
{
|
|
||||||
// Find all resource files
|
|
||||||
char[] morroDir = config.morrowindDirectory();
|
|
||||||
bsa = new FileFinder(morroDir, "bsa");
|
|
||||||
|
|
||||||
// Jump to the data files directory
|
|
||||||
morroDir = config.dataFilesDirectory();
|
|
||||||
|
|
||||||
esm = new FileFinder(morroDir, "esm", Recurse.No);
|
|
||||||
esp = new FileFinder(morroDir, "esp", Recurse.No);
|
|
||||||
|
|
||||||
meshes = new FileFinder(morroDir ~ "Meshes");
|
|
||||||
icons = new FileFinder(morroDir ~ "Icons");
|
|
||||||
textures = new FileFinder(morroDir ~ "Textures");
|
|
||||||
sounds = new FileFinder(morroDir ~ "Sound");
|
|
||||||
bookart = new FileFinder(morroDir ~ "BookArt");
|
|
||||||
|
|
||||||
char[][] bsas = config.bsaArchives();
|
|
||||||
archives.length = bsas.length;
|
|
||||||
writef("Loading BSA archives...");
|
|
||||||
|
|
||||||
writefln(" Done\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+/
|
|
File diff suppressed because it is too large
Load Diff
@ -1,165 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (block.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.compiler.block;
|
|
||||||
|
|
||||||
import monster.compiler.tokenizer;
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.compiler.assembler;
|
|
||||||
|
|
||||||
import monster.vm.error;
|
|
||||||
|
|
||||||
// Base class for all kinds of blocks. A 'block' is a token or
|
|
||||||
// collection of tokens that belong together and form a syntactical
|
|
||||||
// unit. A block might for example be a statement, a declaration, a
|
|
||||||
// block of code, or the entire class.
|
|
||||||
abstract class Block
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
static Token next(ref TokenArray toks)
|
|
||||||
{
|
|
||||||
if(toks.length == 0)
|
|
||||||
fail("Unexpected end of file");
|
|
||||||
Token tt = toks[0];
|
|
||||||
toks = toks[1..toks.length];
|
|
||||||
return tt;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Floc getLoc(TokenArray toks)
|
|
||||||
{
|
|
||||||
Floc loc;
|
|
||||||
if(toks.length) loc = toks[0].loc;
|
|
||||||
return loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isNext(ref TokenArray toks, TT type)
|
|
||||||
{
|
|
||||||
Floc ln;
|
|
||||||
return isNext(toks, type, ln);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isNext(ref TokenArray toks, TT type, out Floc loc)
|
|
||||||
{
|
|
||||||
if( toks.length == 0 ) return false;
|
|
||||||
loc = toks[0].loc;
|
|
||||||
if( toks[0].type != type ) return false;
|
|
||||||
next(toks);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool isNext(ref TokenArray toks, TT type, out Token tok)
|
|
||||||
{
|
|
||||||
if( toks.length == 0 ) return false;
|
|
||||||
if( toks[0].type != type ) return false;
|
|
||||||
tok = next(toks);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the next token a separator, ie. a ; or a new line
|
|
||||||
static bool isSep(ref TokenArray toks, TT symbol = TT.Semicolon)
|
|
||||||
{
|
|
||||||
if( toks.length == 0 ) return true;
|
|
||||||
if( toks[0].newline ) return true;
|
|
||||||
return isNext(toks, symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if the next token is on a new line. Does not remove
|
|
||||||
// any tokens.
|
|
||||||
static bool isNewline(ref TokenArray toks)
|
|
||||||
// TT.EMPTY never occurs in the stream, so it's safe to check for
|
|
||||||
// it.
|
|
||||||
{ return isSep(toks, TT.EMPTY); }
|
|
||||||
|
|
||||||
// Require either a line break or a given character (default ;)
|
|
||||||
static void reqSep(ref TokenArray toks, TT symbol = TT.Semicolon)
|
|
||||||
{
|
|
||||||
if(!isSep(toks, symbol))
|
|
||||||
fail("Expected '" ~ tokenList[symbol] ~ "' or newline", toks);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reqNext(ref TokenArray toks, TT type, out Token tok)
|
|
||||||
{
|
|
||||||
if(!isNext(toks, type, tok))
|
|
||||||
fail("Expected " ~ tokenList[type], toks);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reqNext(ref TokenArray toks, TT type, out Floc loc)
|
|
||||||
{
|
|
||||||
Token t;
|
|
||||||
reqNext(toks, type, t);
|
|
||||||
loc = t.loc;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reqNext(ref TokenArray toks, TT type)
|
|
||||||
{
|
|
||||||
Token t;
|
|
||||||
reqNext(toks, type, t);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip any matching set of parens (), [] or {}, and anything inside
|
|
||||||
// it.
|
|
||||||
static void skipParens(ref TokenArray toks, TT type = TT.LeftParen)
|
|
||||||
{
|
|
||||||
TT endType;
|
|
||||||
if(type == TT.LeftParen)
|
|
||||||
endType = TT.RightParen;
|
|
||||||
else if(type == TT.LeftCurl)
|
|
||||||
endType = TT.RightCurl;
|
|
||||||
else if(type == TT.LeftSquare)
|
|
||||||
endType = TT.RightSquare;
|
|
||||||
else assert(0);
|
|
||||||
|
|
||||||
int count = 0;
|
|
||||||
|
|
||||||
while(toks.length != 0)
|
|
||||||
{
|
|
||||||
if(toks[0].type == type)
|
|
||||||
count++;
|
|
||||||
else if(toks[0].type == endType)
|
|
||||||
count--;
|
|
||||||
|
|
||||||
toks = toks[1..$];
|
|
||||||
|
|
||||||
if(count <= 0)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the assembler debug line to the line belonging to this
|
|
||||||
// block.
|
|
||||||
final void setLine() { tasm.setLine(loc.line); }
|
|
||||||
|
|
||||||
public:
|
|
||||||
// File position where this block was defined
|
|
||||||
Floc loc;
|
|
||||||
|
|
||||||
// Parse a list of tokens and attempt to understand how they belong
|
|
||||||
// together. This is the syntactical level.
|
|
||||||
void parse(ref TokenArray toks);
|
|
||||||
|
|
||||||
// This goes through the code, resolves names and types etc,
|
|
||||||
// converts expressions into an intermediate form which can be
|
|
||||||
// compiled to byte code later. This is basically the semantic level.
|
|
||||||
void resolve(Scope sc);
|
|
||||||
}
|
|
@ -1,656 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (bytecode.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.compiler.bytecode;
|
|
||||||
|
|
||||||
// Byte code operands
|
|
||||||
enum BC
|
|
||||||
{
|
|
||||||
Exit = 1, // Exit function.
|
|
||||||
|
|
||||||
Call, // Call function in this object. Takes a
|
|
||||||
// global function index on the stack, int.
|
|
||||||
|
|
||||||
CallFar, // Call function in another object. The object
|
|
||||||
// must be pushed on the stack, followed by
|
|
||||||
// the global function index.
|
|
||||||
|
|
||||||
Return, // Takes a parameter nr (int). Equivalent to:
|
|
||||||
// POPN nr (remove nr values of the stack)
|
|
||||||
// EXIT
|
|
||||||
|
|
||||||
ReturnVal, // Takes parameter nr (int). Equivalent to:
|
|
||||||
// POP value and store it in TMP
|
|
||||||
// POPN nr (remove function parameters)
|
|
||||||
// PUSH TMP (put return value back on stack)
|
|
||||||
// EXIT
|
|
||||||
|
|
||||||
ReturnValN, // Same as ReturnVal, but takes a second
|
|
||||||
// parameter (int). This gives the size of the
|
|
||||||
// return value.
|
|
||||||
|
|
||||||
State, // Set state. Followed by an int giving the
|
|
||||||
// state index, another int giving the label
|
|
||||||
// index and a third int giving the class tree
|
|
||||||
// index. State index -1 means the empty
|
|
||||||
// state, and -1 for the label means no label
|
|
||||||
// is specified (eg state = test; instead of
|
|
||||||
// state = test.label). For state -1 the label
|
|
||||||
// index must also be -1, and the class index
|
|
||||||
// is ignored.
|
|
||||||
|
|
||||||
EnumValue, // Get the 'value' field of the enum variable
|
|
||||||
// on the stack. Takes an enum type index.
|
|
||||||
|
|
||||||
EnumField, // Get the given field of an enum. Takes the
|
|
||||||
// field number and the enum type index, both
|
|
||||||
// ints.
|
|
||||||
|
|
||||||
EnumValToIndex, // Used to look up enum names (string index)
|
|
||||||
EnumNameToIndex, // or value (long) and returns the enum index.
|
|
||||||
|
|
||||||
|
|
||||||
New, // Create a new object. Followed by an int
|
|
||||||
// giving the class index (in the file lookup
|
|
||||||
// table) and one giving the paramter
|
|
||||||
// number. The parameter indices, class
|
|
||||||
// indices and values are popped of the stack,
|
|
||||||
// see NewExpression.evalAsm for details.
|
|
||||||
|
|
||||||
Clone, // Clones an object - create a new object of
|
|
||||||
// the same class, then copy variable values
|
|
||||||
// and state from the old object to the
|
|
||||||
// new. Replaces the object index on the stack
|
|
||||||
// with the new index.
|
|
||||||
|
|
||||||
Jump, // Jump to given position (int)
|
|
||||||
|
|
||||||
JumpZ, // Pop a value, if it is zero then jump to
|
|
||||||
// given position (int)
|
|
||||||
|
|
||||||
JumpNZ, // Jump if non-zero
|
|
||||||
|
|
||||||
PushData, // Push the next four bytes verbatim
|
|
||||||
|
|
||||||
PushLocal, // Push value of a local variable or function
|
|
||||||
// parameter, index given as a signed
|
|
||||||
// int. Gives the index from the current stack
|
|
||||||
// frame. 0 is the first int, 1 is the second
|
|
||||||
// int, -1 is the last int in the function
|
|
||||||
// parameter area, etc.
|
|
||||||
|
|
||||||
PushClassVar, // Push value in object data segment (in this
|
|
||||||
// object). Parameter is an int offset.
|
|
||||||
|
|
||||||
PushParentVar, // Push value in data segment of parent
|
|
||||||
// object. int class tree index, int offset
|
|
||||||
|
|
||||||
PushFarClassVar, // Push value from the data segment in another
|
|
||||||
// object. The object reference is already on
|
|
||||||
// the stack. int class tree index, int offset
|
|
||||||
|
|
||||||
PushFarClassMulti, // Pushes multiple ints from the data
|
|
||||||
// segment. Takes the variable size (int) as
|
|
||||||
// the first parameter, otherwise identical to
|
|
||||||
// PushFarClassVar.
|
|
||||||
|
|
||||||
PushThis, // Push the 'this' object reference
|
|
||||||
|
|
||||||
PushSingleton, // Push the singleton object. Takes the global
|
|
||||||
// class index (int) as parameter.
|
|
||||||
|
|
||||||
// The Push*8 instructions are not implemented yet as they are
|
|
||||||
// just optimizations of existing features. The names are reserved
|
|
||||||
// for future use.
|
|
||||||
|
|
||||||
Push8, // Push the next 8 bytes (two ints)
|
|
||||||
// verbatim.
|
|
||||||
PushLocal8, // Does the same as PushLocal except it also
|
|
||||||
// pushes the next int onto the stack. The
|
|
||||||
// index must be > 0 or <= -2.
|
|
||||||
PushClassVar8, // Same as PushClassVar but pushes two ints
|
|
||||||
PushFarClassVar8,
|
|
||||||
|
|
||||||
|
|
||||||
Pop, // Pop data and forget about it
|
|
||||||
|
|
||||||
PopN, // Takes a byte parameter N, equivalent to
|
|
||||||
// calling Pop N times.
|
|
||||||
|
|
||||||
Dup, // Duplicate the next value on the
|
|
||||||
// stack. Equivalent to: a=pop; push a; push
|
|
||||||
// a;
|
|
||||||
|
|
||||||
Store, // Basic operation for moving data to
|
|
||||||
// memory. Schematically pops a Ptr of the
|
|
||||||
// stack, pops a value, moves the value into
|
|
||||||
// the Ptr.
|
|
||||||
|
|
||||||
Store8, // Same as Store except two ints are popped
|
|
||||||
// from the stack and moved into the data.
|
|
||||||
|
|
||||||
StoreMult, // Takes the size as an int parameter
|
|
||||||
|
|
||||||
IAdd, // Standard addition, operates on the two next
|
|
||||||
// ints in the stack, and stores the result in
|
|
||||||
// the stack.
|
|
||||||
|
|
||||||
ISub, // Subtraction. a-b, where a is pushed first.
|
|
||||||
|
|
||||||
IMul, // Multiplication
|
|
||||||
|
|
||||||
IDiv, // Division. The dividend must be pushed
|
|
||||||
// first, the divisor second.
|
|
||||||
UDiv, // uint
|
|
||||||
|
|
||||||
IDivRem, // Reminder division
|
|
||||||
UDivRem, // uint
|
|
||||||
|
|
||||||
INeg, // Negate the next integer on the stack
|
|
||||||
|
|
||||||
FAdd, // Float arithmetic
|
|
||||||
FSub,
|
|
||||||
FMul,
|
|
||||||
FDiv,
|
|
||||||
FIDiv,
|
|
||||||
FDivRem,
|
|
||||||
FNeg,
|
|
||||||
|
|
||||||
LAdd, // Long arithmetic
|
|
||||||
LSub,
|
|
||||||
LMul,
|
|
||||||
LDiv,
|
|
||||||
ULDiv,
|
|
||||||
LDivRem,
|
|
||||||
ULDivRem,
|
|
||||||
LNeg,
|
|
||||||
|
|
||||||
DAdd, // Double arithmetic
|
|
||||||
DSub,
|
|
||||||
DMul,
|
|
||||||
DDiv,
|
|
||||||
DIDiv,
|
|
||||||
DDivRem,
|
|
||||||
DNeg,
|
|
||||||
|
|
||||||
IsEqual, // Pop two ints, push true (1) if they are
|
|
||||||
// equal or false (0) otherwise
|
|
||||||
IsEqualMulti, // Takes an int parameter giving the value size
|
|
||||||
|
|
||||||
IsCaseEqual, // Same as IsEqual, but uses (unicode) case
|
|
||||||
// insensitive match. Values are assumed to be
|
|
||||||
// dchars.
|
|
||||||
|
|
||||||
CmpArray, // Compare two arrays. Only does exact
|
|
||||||
// byte-for-byte matching.
|
|
||||||
ICmpStr, // Does a case insensitive check of two
|
|
||||||
// strings. Both these pop two array indices
|
|
||||||
// of the stack and push a bool.
|
|
||||||
|
|
||||||
PreInc, // ++, pops an address, increases variable,
|
|
||||||
// and pushes the value
|
|
||||||
|
|
||||||
PreDec, // -- (these might change)
|
|
||||||
|
|
||||||
PostInc, // ++ and -- that push the original value
|
|
||||||
PostDec, // rather than the new one
|
|
||||||
|
|
||||||
PreInc8, PreDec8,
|
|
||||||
PostInc8, PostDec8, // 64 bit versions
|
|
||||||
|
|
||||||
Not, // Inverse the bool on the stack
|
|
||||||
|
|
||||||
ILess, // Pops b, pops a, pushes true if a < b, false
|
|
||||||
// otherwise. Works on signed ints.
|
|
||||||
|
|
||||||
ULess, // unsigned ints
|
|
||||||
LLess, // long
|
|
||||||
ULLess, // ulong
|
|
||||||
FLess, // float
|
|
||||||
DLess, // double
|
|
||||||
|
|
||||||
// All the Cast* instructions pops a variable of the given type of
|
|
||||||
// the stack, and pushes back an equivalent variable of the new
|
|
||||||
// type.
|
|
||||||
|
|
||||||
CastI2L, // int to long (signed)
|
|
||||||
|
|
||||||
CastD2F, // double to float
|
|
||||||
CastF2D, // float to double
|
|
||||||
|
|
||||||
CastI2F, // int to float
|
|
||||||
CastU2F, // uint to float
|
|
||||||
CastL2F, // long to float
|
|
||||||
CastUL2F, // ulong to float
|
|
||||||
|
|
||||||
CastI2D, // int to double
|
|
||||||
CastU2D, // uint to double
|
|
||||||
CastL2D, // long to double
|
|
||||||
CastUL2D, // ulong to double
|
|
||||||
|
|
||||||
CastF2I, // float to int
|
|
||||||
CastF2U, // float to uint
|
|
||||||
CastF2L, // float to long
|
|
||||||
CastF2UL, // float to ulong
|
|
||||||
|
|
||||||
CastD2I, // double to int
|
|
||||||
CastD2U, // double to uint
|
|
||||||
CastD2L, // double to long
|
|
||||||
CastD2UL, // double to ulong
|
|
||||||
|
|
||||||
CastS2C, // string to char - string must have length 1
|
|
||||||
|
|
||||||
CastT2S, // cast any type to string. Takes the type
|
|
||||||
// index (int) as a parameter
|
|
||||||
|
|
||||||
DownCast, // Takes a global class index (int). Checks if
|
|
||||||
// the object on the stack is an instance of
|
|
||||||
// the given class.
|
|
||||||
|
|
||||||
RefFunc, // Pop an array reference (two ints) and push
|
|
||||||
// the corresponding function name as a string
|
|
||||||
|
|
||||||
FetchElem, // Get an element from an array. Pops the
|
|
||||||
// index, then the array reference, then
|
|
||||||
// pushes the value. The element size is
|
|
||||||
// determined from the array.
|
|
||||||
|
|
||||||
GetArrLen, // Get the length of an array. Pops the array,
|
|
||||||
// pushes the length.
|
|
||||||
|
|
||||||
PopToArray, // Takes a raw length n (int) and an element
|
|
||||||
// size (int). Creates an array from the last
|
|
||||||
// n values on the stack and pops them off.
|
|
||||||
// Pushes the new array index. The values are
|
|
||||||
// copied into the new array, and are
|
|
||||||
// independent of the stack.
|
|
||||||
|
|
||||||
NewArray, // Takes one int giving the array nesting
|
|
||||||
// level (rank), one int giving the element
|
|
||||||
// size (s) and s ints giving the initial
|
|
||||||
// value. Pops the lengths (one int per rank
|
|
||||||
// level) from the stack. Pushes a new array
|
|
||||||
// of the given length. The lengths should
|
|
||||||
// pushed in the same order they appear in a
|
|
||||||
// new-expression, ie. new int[1][2] are
|
|
||||||
// pushed as 1 then 2.
|
|
||||||
|
|
||||||
CopyArray, // Pops two array indices from the stack, and
|
|
||||||
// copies the data from one to another. The
|
|
||||||
// destination array is popped first, then the
|
|
||||||
// source. The lengths must match. The arrays
|
|
||||||
// may overlap in memory without unexpected
|
|
||||||
// effects.
|
|
||||||
|
|
||||||
DupArray, // Pops an array index of the stack, creates a
|
|
||||||
// copy of the array, and pushes the index of
|
|
||||||
// the new array.
|
|
||||||
|
|
||||||
MakeConstArray, // Pops an array index, creates a const
|
|
||||||
// reference to the same data, pushes the
|
|
||||||
// index.
|
|
||||||
|
|
||||||
IsConstArray, // Pops the array index, pushes bool
|
|
||||||
// reflecting the const status
|
|
||||||
|
|
||||||
Slice, // Create a slice. Pops the second index, then
|
|
||||||
// the first, then the array index. Pushes a
|
|
||||||
// new array that is a slice of the original.
|
|
||||||
|
|
||||||
FillArray, // Fill an array. Pop an array index, then a
|
|
||||||
// value. Sets all the elements in the array
|
|
||||||
// to the value. Takes an int specifying the
|
|
||||||
// element/value size.
|
|
||||||
|
|
||||||
CatArray, // Concatinate two arrays, on the stack.
|
|
||||||
|
|
||||||
CatLeft, // Concatinate an array with a left
|
|
||||||
// element. The element is pushed first, so
|
|
||||||
// the array index is first off the stack.
|
|
||||||
CatRight, // Concatinate with right element. Array
|
|
||||||
// pushed first, then element. Both these take
|
|
||||||
// an element size (int).
|
|
||||||
|
|
||||||
ReverseArray, // Reverses an array. The index is on the
|
|
||||||
// stack. The array is reversed in place, and
|
|
||||||
// the index is left untouched on the stack.
|
|
||||||
|
|
||||||
CreateArrayIter, // Create array iterator. Expects the stack to
|
|
||||||
// hold an index(int), a value(int) and an
|
|
||||||
// array index, pushed in that order. Replaces
|
|
||||||
// the array index with an iterator index,
|
|
||||||
// sets the rest to reflect the first element
|
|
||||||
// in iteration. Takes two byte parameters
|
|
||||||
// that are either 0 or 1. If the first is
|
|
||||||
// set, then the array is iterated in the
|
|
||||||
// reverse order. If the second is set, then
|
|
||||||
// the value is a reference, ie. changes to it
|
|
||||||
// will be transfered back to the orignal
|
|
||||||
// array. Pushes false if the array is empty,
|
|
||||||
// true otherwise.
|
|
||||||
|
|
||||||
CreateClassIter, // Create a class iterator. Expects the
|
|
||||||
// stack to hold a value (object index =
|
|
||||||
// int). Takes one int parameter, which is the
|
|
||||||
// class index. Pushes false if no objects
|
|
||||||
// exist, true otherwise.
|
|
||||||
|
|
||||||
IterNext, // Iterate to the next element. Leaves the
|
|
||||||
// iteration variables on the stack. Pushes
|
|
||||||
// false if this was the last element, true
|
|
||||||
// otherwise.
|
|
||||||
|
|
||||||
IterBreak, // Break off iteration. Does exactly the same
|
|
||||||
// thing as IterNext would done if it had just
|
|
||||||
// finished the last iteration step, except it
|
|
||||||
// does not push anything onto the stack.
|
|
||||||
|
|
||||||
IterUpdate, // Update the original array or data from
|
|
||||||
// reference variables in this
|
|
||||||
// iteration. Called whenever a 'ref' variable
|
|
||||||
// is changed, to make sure that the changes
|
|
||||||
// take effect. Takes an int parameter that
|
|
||||||
// gives the stack position (in the current
|
|
||||||
// frame) of the iterator index.
|
|
||||||
|
|
||||||
GetStack, // Internal debugging function. Pushes the
|
|
||||||
// current stack position on the stack. Might
|
|
||||||
// be removed later.
|
|
||||||
|
|
||||||
MultiByte, // This instruction consists of a second byte
|
|
||||||
// or an extra int. Reserved for future
|
|
||||||
// use. This is intended for when / if the
|
|
||||||
// number of instructions exceeds
|
|
||||||
// 256. Instructions in this list with numbers
|
|
||||||
// >= 256 will be coded in this way.
|
|
||||||
|
|
||||||
// Instructions appearing after the MultiByte mark might be coded
|
|
||||||
// as multi-byte. They are handled in a more time-consuming
|
|
||||||
// matter. You should only use this space for instructions that
|
|
||||||
// are seldomly executed.
|
|
||||||
|
|
||||||
Error, // Throw an exception. Takes an error code
|
|
||||||
// (byte) defined below. The library user will
|
|
||||||
// later be able to choose whether this halts
|
|
||||||
// execution entirely or just kills the
|
|
||||||
// offending vthread.
|
|
||||||
|
|
||||||
Last
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure all single-byte instructions will in fact fit in a single
|
|
||||||
// byte.
|
|
||||||
static assert(BC.MultiByte < 255);
|
|
||||||
|
|
||||||
// Make us aware when we break the byte barrier
|
|
||||||
static assert(BC.Last < 255);
|
|
||||||
|
|
||||||
enum Err
|
|
||||||
{
|
|
||||||
None, // Should never happen
|
|
||||||
NoReturn, // Function is missing a return statement
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for coded pointers. The first byte in a coded pointer gives
|
|
||||||
// the pointer type, and the remaining 24 bits gives an index whose
|
|
||||||
// meaning is determined by the type. The pointers can be used both
|
|
||||||
// for variables and for functions. All pointers are 3 ints in size on
|
|
||||||
// the stack. The comments below defines what the indices mean - the
|
|
||||||
// ones that are not mentioned are zero.
|
|
||||||
enum PT
|
|
||||||
{
|
|
||||||
Null = 0, // Null pointer. The index must also be zero.
|
|
||||||
|
|
||||||
Stack = 1, // Index is relative to function stack
|
|
||||||
// frame. Used for local variables.
|
|
||||||
|
|
||||||
DataOffs = 2, // This class, this object. Index is data
|
|
||||||
// segment offset.
|
|
||||||
|
|
||||||
DataOffsCls = 4, // Variable is in this object, but in another
|
|
||||||
// class. The class MUST be a parent class of the
|
|
||||||
// current object. A class tree index follows
|
|
||||||
// this pointer on the stack.
|
|
||||||
|
|
||||||
FarDataOffs = 5, // Another class, another object. The index is a
|
|
||||||
// data offset. Pop the class index off the
|
|
||||||
// stack, and then object index.
|
|
||||||
|
|
||||||
ArrayIndex = 30, // Pointer to an array element. The array and
|
|
||||||
// the index are pushed on the stack, the
|
|
||||||
// pointer index is zero.
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] errorString(ubyte er)
|
|
||||||
{
|
|
||||||
if(er < errorToString.length)
|
|
||||||
return errorToString[er];
|
|
||||||
return "Unknown error code";
|
|
||||||
}
|
|
||||||
|
|
||||||
union _CodePtr
|
|
||||||
{
|
|
||||||
// How the pointer is coded
|
|
||||||
align(1) struct
|
|
||||||
{
|
|
||||||
ubyte type;
|
|
||||||
int val24;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The end result is stored in val32
|
|
||||||
align(1) struct
|
|
||||||
{
|
|
||||||
int val32;
|
|
||||||
ubyte remains;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static assert(_CodePtr.sizeof == 5);
|
|
||||||
|
|
||||||
// Encode a "pointer". Pointers are two shorts encoded into an
|
|
||||||
// int. The first byte is the pointer type, the remaining 24 bits
|
|
||||||
// gives the index.
|
|
||||||
int codePtr(PT type, int index)
|
|
||||||
{
|
|
||||||
assert(index >= -(1<<23) && index < (1<<24),
|
|
||||||
"index out of range for 24 bit value");
|
|
||||||
assert(type != 0 || index == 0,
|
|
||||||
"null pointers must have index == 0");
|
|
||||||
assert(type == PT.Stack || index >= 0,
|
|
||||||
"only PT.Stack can have a negative index");
|
|
||||||
|
|
||||||
|
|
||||||
_CodePtr t;
|
|
||||||
t.type = type;
|
|
||||||
t.val24 = index;
|
|
||||||
|
|
||||||
assert((index >= 0 && t.remains == 0) ||
|
|
||||||
(index < 0 && t.remains == 255));
|
|
||||||
|
|
||||||
return t.val32;
|
|
||||||
}
|
|
||||||
|
|
||||||
void decodePtr(int ptr, out PT type, out int index)
|
|
||||||
{
|
|
||||||
_CodePtr t;
|
|
||||||
t.val32 = ptr;
|
|
||||||
|
|
||||||
// Manage negative numbers
|
|
||||||
if(t.val24 >= 0x800000)
|
|
||||||
{
|
|
||||||
t.remains = 255;
|
|
||||||
assert(t.val24 < 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
type = cast(PT) t.type;
|
|
||||||
index = t.val24;
|
|
||||||
|
|
||||||
assert(type != 0 || index == 0,
|
|
||||||
"null pointers must have index == 0");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Getting the name of an enum should be much easier than creating
|
|
||||||
// braindead constructions like this. Although this is still much
|
|
||||||
// better than the C++ equivalent. I'm just happy I did it through a
|
|
||||||
// script instead of typing it all by hand.
|
|
||||||
|
|
||||||
// These kind of braindead constructions will luckily be completely
|
|
||||||
// unnecessary in Monster script, We will not only will have .name
|
|
||||||
// property on enums, but make it easy to assign other values (like
|
|
||||||
// numbers and descriptions) to them as well.
|
|
||||||
char[][] errorToString =
|
|
||||||
[
|
|
||||||
Err.None: "No error!",
|
|
||||||
Err.NoReturn: "Function ended without returning a value"
|
|
||||||
];
|
|
||||||
|
|
||||||
char[][] bcToString =
|
|
||||||
[
|
|
||||||
BC.Exit: "Exit",
|
|
||||||
BC.Call: "Call",
|
|
||||||
BC.CallFar: "CallFar",
|
|
||||||
BC.Return: "Return",
|
|
||||||
BC.ReturnVal: "ReturnVal",
|
|
||||||
BC.ReturnValN: "ReturnValN",
|
|
||||||
BC.State: "State",
|
|
||||||
BC.EnumValue: "EnumValue",
|
|
||||||
BC.EnumField: "EnumField",
|
|
||||||
BC.EnumValToIndex: "EnumValToIndex",
|
|
||||||
BC.EnumNameToIndex: "EnumNameToIndex",
|
|
||||||
BC.New: "New",
|
|
||||||
BC.Jump: "Jump",
|
|
||||||
BC.JumpZ: "JumpZ",
|
|
||||||
BC.JumpNZ: "JumpNZ",
|
|
||||||
BC.PushData: "PushData",
|
|
||||||
BC.PushLocal: "PushLocal",
|
|
||||||
BC.PushClassVar: "PushClassVar",
|
|
||||||
BC.PushParentVar: "PushParentVar",
|
|
||||||
BC.PushFarClassVar: "PushFarClassVar",
|
|
||||||
BC.PushFarClassMulti: "PushFarClassMulti",
|
|
||||||
BC.PushThis: "PushThis",
|
|
||||||
BC.PushSingleton: "PushSingleton",
|
|
||||||
BC.Push8: "Push8",
|
|
||||||
BC.PushLocal8: "PushLocal8",
|
|
||||||
BC.PushClassVar8: "PushClassVar8",
|
|
||||||
BC.PushFarClassVar8: "PushFarClassVar8",
|
|
||||||
BC.Pop: "Pop",
|
|
||||||
BC.PopN: "PopN",
|
|
||||||
BC.Dup: "Dup",
|
|
||||||
BC.Store: "Store",
|
|
||||||
BC.Store8: "Store8",
|
|
||||||
BC.StoreMult: "StoreMult",
|
|
||||||
BC.RefFunc: "RefFunc",
|
|
||||||
BC.FetchElem: "FetchElem",
|
|
||||||
BC.GetArrLen: "GetArrLen",
|
|
||||||
BC.IMul: "IMul",
|
|
||||||
BC.IAdd: "IAdd",
|
|
||||||
BC.ISub: "ISub",
|
|
||||||
BC.IDiv: "IDiv",
|
|
||||||
BC.IDivRem: "IDivRem",
|
|
||||||
BC.UDiv: "UDiv",
|
|
||||||
BC.UDivRem: "UDivRem",
|
|
||||||
BC.INeg: "INeg",
|
|
||||||
BC.LMul: "LMul",
|
|
||||||
BC.LAdd: "LAdd",
|
|
||||||
BC.LSub: "LSub",
|
|
||||||
BC.LDiv: "LDiv",
|
|
||||||
BC.LDivRem: "LDivRem",
|
|
||||||
BC.ULDiv: "ULDiv",
|
|
||||||
BC.ULDivRem: "ULDivRem",
|
|
||||||
BC.LNeg: "LNeg",
|
|
||||||
BC.DMul: "DMul",
|
|
||||||
BC.DAdd: "DAdd",
|
|
||||||
BC.DSub: "DSub",
|
|
||||||
BC.DDiv: "DDiv",
|
|
||||||
BC.DIDiv: "DIDiv",
|
|
||||||
BC.DDivRem: "DDivRem",
|
|
||||||
BC.DNeg: "DNeg",
|
|
||||||
BC.FAdd: "FAdd",
|
|
||||||
BC.FSub: "FSub",
|
|
||||||
BC.FMul: "FMul",
|
|
||||||
BC.FDiv: "FDiv",
|
|
||||||
BC.FIDiv: "FIDiv",
|
|
||||||
BC.FDivRem: "FDivRem",
|
|
||||||
BC.FNeg: "FNeg",
|
|
||||||
BC.IsEqual: "IsEqual",
|
|
||||||
BC.IsEqualMulti: "IsEqualMulti",
|
|
||||||
BC.IsCaseEqual: "IsCaseEqual",
|
|
||||||
BC.CmpArray: "CmpArray",
|
|
||||||
BC.ICmpStr: "ICmpStr",
|
|
||||||
BC.PreInc: "PreInc",
|
|
||||||
BC.PreDec: "PreDec",
|
|
||||||
BC.PostInc: "PostInc",
|
|
||||||
BC.PostDec: "PostDec",
|
|
||||||
BC.PreInc8: "PreInc8",
|
|
||||||
BC.PreDec8: "PreDec8",
|
|
||||||
BC.PostInc8: "PostInc8",
|
|
||||||
BC.PostDec8: "PostDec8",
|
|
||||||
BC.Not: "Not",
|
|
||||||
BC.ILess: "ILess",
|
|
||||||
BC.ULess: "ULess",
|
|
||||||
BC.LLess: "LLess",
|
|
||||||
BC.ULLess: "ULLess",
|
|
||||||
BC.FLess: "FLess",
|
|
||||||
BC.DLess: "DLess",
|
|
||||||
BC.CastI2L: "CastI2L",
|
|
||||||
BC.CastI2F: "CastI2F",
|
|
||||||
BC.CastU2F: "CastU2F",
|
|
||||||
BC.CastL2F: "CastL2F",
|
|
||||||
BC.CastUL2F: "CastUL2F",
|
|
||||||
BC.CastD2F: "CastD2F",
|
|
||||||
BC.CastI2D: "CastI2D",
|
|
||||||
BC.CastU2D: "CastU2D",
|
|
||||||
BC.CastL2D: "CastL2D",
|
|
||||||
BC.CastUL2D: "CastUL2D",
|
|
||||||
BC.CastF2D: "CastF2D",
|
|
||||||
BC.CastF2I: "CastF2I",
|
|
||||||
BC.CastF2U: "CastF2U",
|
|
||||||
BC.CastF2L: "CastF2L",
|
|
||||||
BC.CastF2UL: "CastF2UL",
|
|
||||||
BC.CastD2I: "CastD2I",
|
|
||||||
BC.CastD2U: "CastD2U",
|
|
||||||
BC.CastD2L: "CastD2L",
|
|
||||||
BC.CastD2UL: "CastD2UL",
|
|
||||||
BC.CastS2C: "CastS2C",
|
|
||||||
BC.CastT2S: "CastT2S",
|
|
||||||
BC.DownCast: "DownCast",
|
|
||||||
BC.PopToArray: "PopToArray",
|
|
||||||
BC.NewArray: "NewArray",
|
|
||||||
BC.CopyArray: "CopyArray",
|
|
||||||
BC.DupArray: "DupArray",
|
|
||||||
BC.MakeConstArray: "MakeConstArray",
|
|
||||||
BC.IsConstArray: "IsConstArray",
|
|
||||||
BC.Slice: "Slice",
|
|
||||||
BC.FillArray: "FillArray",
|
|
||||||
BC.CatArray: "CatArray",
|
|
||||||
BC.CatLeft: "CatLeft",
|
|
||||||
BC.CatRight: "CatRight",
|
|
||||||
BC.CreateArrayIter: "CreateArrayIter",
|
|
||||||
BC.IterNext: "IterNext",
|
|
||||||
BC.IterBreak: "IterBreak",
|
|
||||||
BC.IterUpdate: "IterUpdate",
|
|
||||||
BC.CreateClassIter: "CreateClassIter",
|
|
||||||
BC.GetStack: "GetStack",
|
|
||||||
BC.MultiByte: "MultiByte",
|
|
||||||
BC.Error: "Error",
|
|
||||||
];
|
|
@ -1,133 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (enums.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.compiler.enums;
|
|
||||||
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.compiler.types;
|
|
||||||
import monster.compiler.expression;
|
|
||||||
import monster.compiler.statement;
|
|
||||||
import monster.compiler.tokenizer;
|
|
||||||
|
|
||||||
import monster.vm.error;
|
|
||||||
|
|
||||||
// Definition of a field
|
|
||||||
struct FieldDef
|
|
||||||
{
|
|
||||||
Token name;
|
|
||||||
Type type;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Definition of each entry in the enum
|
|
||||||
struct EnumEntry
|
|
||||||
{
|
|
||||||
Token name; // Entry identifier
|
|
||||||
int index; // Enum index value
|
|
||||||
char[] stringValue; // Returned when printing the value
|
|
||||||
|
|
||||||
long value; // Numeric value assigned to the enum. Can be set by the
|
|
||||||
// user, but defaults to 0 for the first entry and to
|
|
||||||
// the previous entry+1 when no value is set.
|
|
||||||
|
|
||||||
Expression exp[]; // Field values (before resolving)
|
|
||||||
int[][] fields; // Field values
|
|
||||||
}
|
|
||||||
|
|
||||||
class EnumDeclaration : TypeDeclaration
|
|
||||||
{
|
|
||||||
static bool canParse(TokenArray toks)
|
|
||||||
{ return toks.isNext(TT.Enum); }
|
|
||||||
|
|
||||||
EnumType type;
|
|
||||||
|
|
||||||
override:
|
|
||||||
void parse(ref TokenArray toks)
|
|
||||||
{
|
|
||||||
type = new EnumType();
|
|
||||||
|
|
||||||
reqNext(toks, TT.Enum);
|
|
||||||
reqNext(toks, TT.Identifier, type.nameTok);
|
|
||||||
|
|
||||||
// Field definitions?
|
|
||||||
while(isNext(toks, TT.Colon))
|
|
||||||
{
|
|
||||||
FieldDef fd;
|
|
||||||
fd.type = Type.identify(toks);
|
|
||||||
reqNext(toks, TT.Identifier, fd.name);
|
|
||||||
type.fields ~= fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
reqNext(toks, TT.LeftCurl);
|
|
||||||
|
|
||||||
type.name = type.nameTok.str;
|
|
||||||
type.loc = type.nameTok.loc;
|
|
||||||
|
|
||||||
// Parse the entries and their fields
|
|
||||||
int lastVal = -1;
|
|
||||||
while(!isNext(toks, TT.RightCurl))
|
|
||||||
{
|
|
||||||
EnumEntry entry;
|
|
||||||
reqNext(toks, TT.Identifier, entry.name);
|
|
||||||
|
|
||||||
// Get the given value, if any
|
|
||||||
if(isNext(toks, TT.Equals))
|
|
||||||
{
|
|
||||||
Token num;
|
|
||||||
reqNext(toks, TT.IntLiteral, num);
|
|
||||||
lastVal = LiteralExpr.parseIntLiteral(num);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// No given value, just increase the last one given
|
|
||||||
lastVal++;
|
|
||||||
|
|
||||||
entry.value = lastVal;
|
|
||||||
|
|
||||||
while(isNext(toks, TT.Colon))
|
|
||||||
entry.exp ~= Expression.identify(toks);
|
|
||||||
|
|
||||||
reqSep(toks, TT.Comma);
|
|
||||||
|
|
||||||
type.entries ~= entry;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(type.entries.length == 0)
|
|
||||||
fail("Enum is empty", type.loc);
|
|
||||||
|
|
||||||
isNext(toks, TT.Semicolon);
|
|
||||||
}
|
|
||||||
|
|
||||||
void insertType(TFVScope last)
|
|
||||||
{
|
|
||||||
// Insert ourselves into the parent scope
|
|
||||||
assert(last !is null);
|
|
||||||
assert(type !is null);
|
|
||||||
last.insertEnum(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
void resolve(Scope last)
|
|
||||||
{
|
|
||||||
// Delegate to the type, since all the variables are defined
|
|
||||||
// there.
|
|
||||||
type.resolve(last);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (linespec.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* This module provides a simple system for converting positions in a
|
|
||||||
code segment (of compiled byte code) into a line number in source
|
|
||||||
code.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.compiler.linespec;
|
|
||||||
|
|
||||||
import monster.vm.error;
|
|
||||||
|
|
||||||
// A line specification. These are usually found in a list, one for
|
|
||||||
// each buffer of code. All instructions after position 'pos' belong
|
|
||||||
// to line 'line', unless a new LineSpec comes after it that covers
|
|
||||||
// that position.
|
|
||||||
struct LineSpec
|
|
||||||
{
|
|
||||||
int pos; // Offset of instruction
|
|
||||||
int line; // Line number for instructions after this offset
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the line belonging to a given position. This does not need to
|
|
||||||
// be fast, it is only used for error messages and the like.
|
|
||||||
int findLine(LineSpec[] list, int pos)
|
|
||||||
{
|
|
||||||
int lastpos = -1;
|
|
||||||
int lastline = -1;
|
|
||||||
|
|
||||||
assert(pos >= 0);
|
|
||||||
|
|
||||||
// The first entry must represent pos = 0
|
|
||||||
if(list.length && list[0].pos != 0)
|
|
||||||
fail("Invalid line list: first offset not zero");
|
|
||||||
|
|
||||||
foreach(ls; list)
|
|
||||||
{
|
|
||||||
if(ls.pos <= lastpos)
|
|
||||||
fail("Invalid line list: decreasing offset");
|
|
||||||
|
|
||||||
// Have we searched past pos?
|
|
||||||
if(ls.pos > pos)
|
|
||||||
// If so, the last entry was the correct one
|
|
||||||
return lastline;
|
|
||||||
|
|
||||||
lastpos = ls.pos;
|
|
||||||
lastline = ls.line;
|
|
||||||
}
|
|
||||||
|
|
||||||
// We never searched past our position, that means the last entry is
|
|
||||||
// the most correct.
|
|
||||||
return lastline;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,377 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (properties.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.compiler.properties;
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.compiler.types;
|
|
||||||
import monster.compiler.assembler;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
|
|
||||||
import std.stdio;
|
|
||||||
|
|
||||||
/* This module contains special scopes for builtin types. These are
|
|
||||||
used to resolve type properties like .length for arrays and .min
|
|
||||||
and .max for ints, etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// TODO: This is nice, but could be nicer. I would like most of these
|
|
||||||
// stored as values rather than functions, and have a general
|
|
||||||
// mechanism for converting values into data (a converted version of
|
|
||||||
// pushInit in Type) used for all types except the ones requiring a
|
|
||||||
// function. I guess I could make versions of insert(s) that takes
|
|
||||||
// floats, ints etc as parameters, and double checks it against the
|
|
||||||
// type. The main reason for storing values is that most of them can
|
|
||||||
// then be optimized away as compile time constants. This is not a
|
|
||||||
// priority.
|
|
||||||
|
|
||||||
class NumericProperties(T) : SimplePropertyScope
|
|
||||||
{
|
|
||||||
this()
|
|
||||||
{
|
|
||||||
super(T.stringof ~ "Properties",
|
|
||||||
GenericProperties.singleton);
|
|
||||||
|
|
||||||
// Static properties of all numeric types
|
|
||||||
static if(T.sizeof == 4)
|
|
||||||
{
|
|
||||||
inserts("min", T.stringof, { tasm.push(T.min); });
|
|
||||||
inserts("max", T.stringof, { tasm.push(T.max); });
|
|
||||||
}
|
|
||||||
else static if(T.sizeof == 8)
|
|
||||||
{
|
|
||||||
inserts("min", T.stringof, { tasm.push8(T.min); });
|
|
||||||
inserts("max", T.stringof, { tasm.push8(T.max); });
|
|
||||||
}
|
|
||||||
else static assert(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class IntProperties : NumericProperties!(int)
|
|
||||||
{ static IntProperties singleton; }
|
|
||||||
|
|
||||||
class UintProperties : NumericProperties!(uint)
|
|
||||||
{ static UintProperties singleton; }
|
|
||||||
|
|
||||||
class LongProperties : NumericProperties!(long)
|
|
||||||
{ static LongProperties singleton; }
|
|
||||||
|
|
||||||
class UlongProperties : NumericProperties!(ulong)
|
|
||||||
{ static UlongProperties singleton; }
|
|
||||||
|
|
||||||
class FloatingProperties(T) : NumericProperties!(T)
|
|
||||||
{
|
|
||||||
this()
|
|
||||||
{
|
|
||||||
char[] tp = T.stringof;
|
|
||||||
|
|
||||||
// Additional static properties of floating point numbers
|
|
||||||
static if(T.sizeof == 4)
|
|
||||||
{
|
|
||||||
inserts("infinity", tp, { tasm.push(T.infinity); });
|
|
||||||
inserts("inf", tp, { tasm.push(T.infinity); });
|
|
||||||
inserts("nan", tp, { tasm.push(T.nan); });
|
|
||||||
inserts("epsilon", tp, { tasm.push(T.epsilon); });
|
|
||||||
}
|
|
||||||
else static if(T.sizeof == 8)
|
|
||||||
{
|
|
||||||
inserts("infinity", tp, { tasm.push8(T.infinity); });
|
|
||||||
inserts("inf", tp, { tasm.push8(T.infinity); });
|
|
||||||
inserts("nan", tp, { tasm.push8(T.nan); });
|
|
||||||
inserts("epsilon", tp, { tasm.push8(T.epsilon); });
|
|
||||||
}
|
|
||||||
else static assert(0);
|
|
||||||
|
|
||||||
inserts("dig", "int", { tasm.push(T.dig); });
|
|
||||||
inserts("max_10_exp", "int", { tasm.push(T.max_10_exp); });
|
|
||||||
inserts("max_exp", "int", { tasm.push(T.max_exp); });
|
|
||||||
inserts("min_10_exp", "int", { tasm.push(T.min_10_exp); });
|
|
||||||
inserts("min_exp", "int", { tasm.push(T.min_exp); });
|
|
||||||
|
|
||||||
// Number of bits in mantissa. D calls it mant_dig, but
|
|
||||||
// mant_bits is more natural. Let us allow both.
|
|
||||||
inserts("mant_bits", "int", { tasm.push(T.mant_dig); });
|
|
||||||
inserts("mant_dig", "int", { tasm.push(T.mant_dig); });
|
|
||||||
|
|
||||||
// Lets add in number of bits in the exponent as well.
|
|
||||||
inserts("exp_bits", "int", { tasm.push(cast(uint)(8*T.sizeof-T.mant_dig)); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class FloatProperties : FloatingProperties!(float)
|
|
||||||
{ static FloatProperties singleton; }
|
|
||||||
|
|
||||||
class DoubleProperties : FloatingProperties!(double)
|
|
||||||
{ static DoubleProperties singleton; }
|
|
||||||
|
|
||||||
// Function pointers / references
|
|
||||||
class FuncRefProperties : SimplePropertyScope
|
|
||||||
{
|
|
||||||
static FuncRefProperties singleton;
|
|
||||||
|
|
||||||
this()
|
|
||||||
{
|
|
||||||
super("FuncRefProperties", GenericProperties.singleton);
|
|
||||||
|
|
||||||
// A function ref is object + function index. Just pop away the
|
|
||||||
// function and the object is left on the stack. Since we cannot
|
|
||||||
// possibly know the type of the object at compile time, we
|
|
||||||
// default to 'Object' as the class type.
|
|
||||||
auto ot = MonsterClass.getObject().objType;
|
|
||||||
assert(ot !is null);
|
|
||||||
insert("obj", ot, { tasm.pop(); });
|
|
||||||
|
|
||||||
// TODO: Replace this with something else later (like a Function
|
|
||||||
// class or similar)
|
|
||||||
insert("func", ArrayType.getString(), { tasm.refFunc(); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles .length, .dup, etc for arrays
|
|
||||||
class ArrayProperties: SimplePropertyScope
|
|
||||||
{
|
|
||||||
static ArrayProperties singleton;
|
|
||||||
|
|
||||||
this()
|
|
||||||
{
|
|
||||||
super("ArrayProperties", GenericProperties.singleton);
|
|
||||||
|
|
||||||
insert("length", "int",
|
|
||||||
{ tasm.getArrayLength(); },
|
|
||||||
{ assert(0, "cannot set length yet"); });
|
|
||||||
insert("dup", "owner", { tasm.dupArray(); });
|
|
||||||
insert("reverse", "owner", { tasm.reverseArray(); });
|
|
||||||
insert("sort", "owner", { assert(0, "sort not implemented"); });
|
|
||||||
insert("const", "owner", { tasm.makeArrayConst(); });
|
|
||||||
insert("isConst", "bool", { tasm.isArrayConst(); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Rename to ObjectProperties
|
|
||||||
class ClassProperties : SimplePropertyScope
|
|
||||||
{
|
|
||||||
static ClassProperties singleton;
|
|
||||||
|
|
||||||
this()
|
|
||||||
{
|
|
||||||
super("ClassProperties",
|
|
||||||
GenericProperties.singleton);
|
|
||||||
|
|
||||||
insert("clone", "owner", { tasm.cloneObj(); });
|
|
||||||
|
|
||||||
// We should move handling of states here. This will mean
|
|
||||||
// removing StateStatement and making states a propert type. We
|
|
||||||
// can't leave statestatement in as a special syntax for setting
|
|
||||||
// types, because the member syntax obj.state = state.label;
|
|
||||||
// would still have to be handled somehow. However, even if this
|
|
||||||
// is more work, it has some additional benefits of allowing
|
|
||||||
// states to be used in other expressions, eg state ==
|
|
||||||
// SomeState. And we should still be able to optimize it into
|
|
||||||
// one instruction.
|
|
||||||
|
|
||||||
// One downside now is that we are currently using static
|
|
||||||
// properties. If we are going to use non-static properties and
|
|
||||||
// allow both member and non-member access, we have to
|
|
||||||
// differentiate between near and far properties too. Think more
|
|
||||||
// about it.
|
|
||||||
//insert("state", "int", { tasm.push(6); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Dynamically handles properties like init and sizeof that are valid
|
|
||||||
// for all types.
|
|
||||||
class GenericProperties : SimplePropertyScope
|
|
||||||
{
|
|
||||||
static GenericProperties singleton;
|
|
||||||
|
|
||||||
this()
|
|
||||||
{
|
|
||||||
super("GenericProperties");
|
|
||||||
|
|
||||||
inserts("init", "owner", {assert(0);});
|
|
||||||
inserts("sizeof", "int", {assert(0);});
|
|
||||||
inserts("bitsof", "int", {assert(0);});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Overwrite the above actions
|
|
||||||
void getValue(char[] name, Type oType)
|
|
||||||
{
|
|
||||||
if(oType.isMeta) oType = oType.getBase();
|
|
||||||
|
|
||||||
if(name == "sizeof") tasm.push(oType.getSize);
|
|
||||||
else if(name == "bitsof") tasm.push(oType.getSize*32);
|
|
||||||
else if(name == "init") oType.pushInit();
|
|
||||||
else assert(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* This is a base class that simplifies definition of property
|
|
||||||
scopes. You can simply call insert and inserts (for static
|
|
||||||
properties) in the constructor. An example:
|
|
||||||
|
|
||||||
inserts("max", "int", { tasm.push(int.max); });
|
|
||||||
|
|
||||||
This inserts the property "max", of type "int", and pushes the
|
|
||||||
value int.max whenever it is invoked. Since it is a static
|
|
||||||
property, the left hand side (eg an int value) is never
|
|
||||||
evaluated. If the type is "" or "owner", then the property type
|
|
||||||
will be the same as the owner type.
|
|
||||||
|
|
||||||
It's likely that parts of this will be simplified or rewritten
|
|
||||||
later. One thing that's missing is compile time properties, for
|
|
||||||
example.
|
|
||||||
*/
|
|
||||||
|
|
||||||
abstract class SimplePropertyScope : PropertyScope
|
|
||||||
{
|
|
||||||
this(char[] n, PropertyScope ps = null) { super(n, ps); }
|
|
||||||
|
|
||||||
private SP[char[]] propList;
|
|
||||||
|
|
||||||
// Convert a typename to a type
|
|
||||||
private Type getType(char[] tp)
|
|
||||||
{
|
|
||||||
if(tp == "" || tp == "owner")
|
|
||||||
return null;
|
|
||||||
|
|
||||||
return BasicType.get(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert properties into the list
|
|
||||||
void insert(char[] name, Type tp, Action push, Action pop = null)
|
|
||||||
{
|
|
||||||
assert(!hasProperty(name));
|
|
||||||
propList[name] = SP(tp, false, push, pop);
|
|
||||||
}
|
|
||||||
|
|
||||||
void insert(char[] name, char[] tp, Action push, Action pop = null)
|
|
||||||
{ insert(name, getType(tp), push, pop); }
|
|
||||||
|
|
||||||
// Insert static properties. TODO: These should take values rather
|
|
||||||
// than code. It should be possible to retireve this value at
|
|
||||||
// compile-time.
|
|
||||||
void inserts(char[] name, Type tp, Action push)
|
|
||||||
{
|
|
||||||
assert(!hasProperty(name));
|
|
||||||
propList[name] = SP(tp, true, push, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
void inserts(char[] name, char[] tp, Action push)
|
|
||||||
{ inserts(name, getType(tp), push); }
|
|
||||||
|
|
||||||
// TODO: These are hacks to work around a silly but irritating DMD
|
|
||||||
// feature. Whenever there's an error somewhere, function literals
|
|
||||||
// like { something; } get resolved as int delegate() instead of
|
|
||||||
// void delegate() for some reason. This gives a ton of error
|
|
||||||
// messages, but these overloads will prevent that.
|
|
||||||
alias int delegate() FakeIt;
|
|
||||||
void insert(char[],char[],FakeIt,FakeIt pop = null) {assert(0);}
|
|
||||||
void insert(char[],Type,FakeIt,FakeIt pop = null) {assert(0);}
|
|
||||||
void inserts(char[],char[],FakeIt) {assert(0);}
|
|
||||||
void inserts(char[],Type,FakeIt) {assert(0);}
|
|
||||||
|
|
||||||
override:
|
|
||||||
|
|
||||||
// Return the stored type. If it is null, return the owner type
|
|
||||||
// instead.
|
|
||||||
Type getType(char[] name, Type oType)
|
|
||||||
{
|
|
||||||
assert(hasProperty(name));
|
|
||||||
Type tp = propList[name].type;
|
|
||||||
|
|
||||||
// No stored type? We have to copy the owner.
|
|
||||||
if(tp is null)
|
|
||||||
{
|
|
||||||
// The owner type might be a meta-type (eg. int.init). Pretend
|
|
||||||
// it is the base type instead.
|
|
||||||
if(oType.isMeta())
|
|
||||||
tp = oType.getBase();
|
|
||||||
else
|
|
||||||
tp = oType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tp;
|
|
||||||
}
|
|
||||||
|
|
||||||
void getValue(char[] name, Type oType)
|
|
||||||
{
|
|
||||||
assert(hasProperty(name));
|
|
||||||
propList[name].push();
|
|
||||||
}
|
|
||||||
|
|
||||||
void setValue(char[] name, Type oType)
|
|
||||||
{
|
|
||||||
assert(hasProperty(name));
|
|
||||||
propList[name].pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasProperty(char[] name)
|
|
||||||
{ return (name in propList) != null; }
|
|
||||||
|
|
||||||
bool isStatic(char[] name, Type oType)
|
|
||||||
{
|
|
||||||
assert(hasProperty(name));
|
|
||||||
return propList[name].isStatic;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isLValue(char[] name, Type oType)
|
|
||||||
{
|
|
||||||
assert(hasProperty(name));
|
|
||||||
return propList[name].isLValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alias void delegate() Action;
|
|
||||||
struct SP
|
|
||||||
{
|
|
||||||
Action push, pop;
|
|
||||||
Type type;
|
|
||||||
bool isStatic;
|
|
||||||
|
|
||||||
static SP opCall(Type tp, bool stat, Action push, Action pop)
|
|
||||||
{
|
|
||||||
SP s;
|
|
||||||
s.push = push;
|
|
||||||
s.pop = pop;
|
|
||||||
s.type = tp;
|
|
||||||
s.isStatic = stat;
|
|
||||||
assert(!stat || pop == null);
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isLValue() { return pop != null; }
|
|
||||||
}
|
|
||||||
|
|
||||||
void initProperties()
|
|
||||||
{
|
|
||||||
GenericProperties.singleton = new GenericProperties;
|
|
||||||
ArrayProperties.singleton = new ArrayProperties;
|
|
||||||
IntProperties.singleton = new IntProperties;
|
|
||||||
UintProperties.singleton = new UintProperties;
|
|
||||||
LongProperties.singleton = new LongProperties;
|
|
||||||
UlongProperties.singleton = new UlongProperties;
|
|
||||||
FloatProperties.singleton = new FloatProperties;
|
|
||||||
DoubleProperties.singleton = new DoubleProperties;
|
|
||||||
ClassProperties.singleton = new ClassProperties;
|
|
||||||
FuncRefProperties.singleton = new FuncRefProperties;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,262 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (states.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.compiler.states;
|
|
||||||
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.compiler.assembler;
|
|
||||||
import monster.compiler.tokenizer;
|
|
||||||
import monster.compiler.linespec;
|
|
||||||
import monster.compiler.statement;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.error;
|
|
||||||
|
|
||||||
import monster.util.aa;
|
|
||||||
|
|
||||||
import std.stdio;
|
|
||||||
|
|
||||||
struct State
|
|
||||||
{
|
|
||||||
MonsterClass owner; // This must be the first entry, since we're
|
|
||||||
// using some pointer trickery with Function and
|
|
||||||
// State.
|
|
||||||
|
|
||||||
LineSpec[] lines; // Line specifications for byte code
|
|
||||||
ubyte[] bcode; // Final compiled code
|
|
||||||
Token name;
|
|
||||||
int index;
|
|
||||||
|
|
||||||
// Labels in this scope.
|
|
||||||
HashTable!(char[], StateLabel*) labels;
|
|
||||||
StateLabel* labelList[];
|
|
||||||
|
|
||||||
// Cache the begin label since it has special meaning and is looked
|
|
||||||
// up often.
|
|
||||||
StateLabel* begin;
|
|
||||||
|
|
||||||
StateScope sc; // Scope for this state
|
|
||||||
|
|
||||||
// State declaration - used to resolve forward references. Should
|
|
||||||
// not be kept around when compilation is finished.
|
|
||||||
StateDeclaration stateDec;
|
|
||||||
|
|
||||||
StateLabel* findLabel(char[] name)
|
|
||||||
{
|
|
||||||
StateLabel *lb;
|
|
||||||
if(labels.inList(name, lb))
|
|
||||||
return lb;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look up a label in this state, or register a forward reference if
|
|
||||||
// the state hasn't been resolved yet.
|
|
||||||
void registerGoto(char[] label, LabelUser lu)
|
|
||||||
{
|
|
||||||
StateLabel *sl;
|
|
||||||
|
|
||||||
assert(lu !is null);
|
|
||||||
|
|
||||||
if( labels.inList(label, sl) )
|
|
||||||
lu.setLabel(sl.ls);
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(stateDec is null)
|
|
||||||
{
|
|
||||||
// The state has been resolved, and the label was not
|
|
||||||
// found. Let lu handle the error message.
|
|
||||||
lu.setLabel(null);
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
with(stateDec)
|
|
||||||
{
|
|
||||||
// The state is not resolved yet, so create a forward
|
|
||||||
// reference to this label.
|
|
||||||
Forward *fw;
|
|
||||||
|
|
||||||
// Get the pointer to the Forward struct in the AA, or a
|
|
||||||
// new one if none existed.
|
|
||||||
forwards.insertEdit(label, fw);
|
|
||||||
|
|
||||||
// Add the reference to the list
|
|
||||||
fw.lus ~= lu;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct StateLabel
|
|
||||||
{
|
|
||||||
Token name;
|
|
||||||
uint offs;
|
|
||||||
uint index; // Index used to represent this label in byte code
|
|
||||||
LabelStatement ls; // TODO: Remove this later?
|
|
||||||
}
|
|
||||||
|
|
||||||
// Simple struct used for representing a label and its state in one
|
|
||||||
// value.
|
|
||||||
struct StateLabelPair
|
|
||||||
{
|
|
||||||
State *state;
|
|
||||||
StateLabel *label;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles declaration of states at the class scope. Uses a code block
|
|
||||||
// for state contents.
|
|
||||||
class StateDeclaration : Statement
|
|
||||||
{
|
|
||||||
State *st;
|
|
||||||
|
|
||||||
CodeBlock code;
|
|
||||||
|
|
||||||
static struct Forward
|
|
||||||
{ LabelUser lus[]; }
|
|
||||||
|
|
||||||
HashTable!(char[], Forward) forwards;
|
|
||||||
|
|
||||||
static bool canParse(TokenArray toks)
|
|
||||||
{
|
|
||||||
return isNext(toks, TT.State);
|
|
||||||
}
|
|
||||||
|
|
||||||
void parse(ref TokenArray toks)
|
|
||||||
{
|
|
||||||
st = new State;
|
|
||||||
st.stateDec = this;
|
|
||||||
|
|
||||||
if(!isNext(toks, TT.State))
|
|
||||||
assert(0, "Internal error in StateDeclaration");
|
|
||||||
|
|
||||||
if(!isNext(toks, TT.Identifier, st.name))
|
|
||||||
fail("Expected state name identifier", toks);
|
|
||||||
|
|
||||||
// Create a code block, and tell it (the parameter) that it is a
|
|
||||||
// state block.
|
|
||||||
code = new CodeBlock(true);
|
|
||||||
code.parse(toks);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve the state. Besides resolving the code we have to resolve
|
|
||||||
// any forward references to labels within the state afterwards.
|
|
||||||
void resolve(Scope last)
|
|
||||||
{
|
|
||||||
assert(st !is null);
|
|
||||||
// Create a state scope. The scope will help enforce special
|
|
||||||
// rules, such as allowing idle functions and disallowing
|
|
||||||
// variable declarations.
|
|
||||||
st.sc = new StateScope(last, st);
|
|
||||||
st.owner = st.sc.getClass();
|
|
||||||
|
|
||||||
// Resolve the interior of the code block
|
|
||||||
assert(code !is null);
|
|
||||||
code.resolve(st.sc);
|
|
||||||
|
|
||||||
// Go through the forward list and resolve everything
|
|
||||||
foreach(char[] label, Forward fd; forwards)
|
|
||||||
{
|
|
||||||
StateLabel *sl;
|
|
||||||
LabelStatement ls;
|
|
||||||
if(st.labels.inList(label, sl))
|
|
||||||
{
|
|
||||||
assert(sl !is null);
|
|
||||||
ls = sl.ls;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
ls = null; // Give a null to setLabel and let it handle
|
|
||||||
// the error message.
|
|
||||||
|
|
||||||
// Loop through the label users
|
|
||||||
foreach(LabelUser lu; fd.lus)
|
|
||||||
lu.setLabel(ls);
|
|
||||||
|
|
||||||
// setLabel should have thrown an error at this point
|
|
||||||
assert(ls !is null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear the forwards list
|
|
||||||
forwards.reset();
|
|
||||||
|
|
||||||
// At this point the State no longer needs to refer to us. Set
|
|
||||||
// the stateDec reference to null. This is also a signal to
|
|
||||||
// State.registerGoto that the state has been resolved, and no
|
|
||||||
// further forward references will be accepted.
|
|
||||||
st.stateDec = null;
|
|
||||||
|
|
||||||
// After the code has been resolved, all labels should now be
|
|
||||||
// registered in the 'labels' list. We must assign a number to
|
|
||||||
// each label for later reference. We also set up the labelList
|
|
||||||
// which can be used to look up the labels directly.
|
|
||||||
int cnt = 0;
|
|
||||||
st.labelList.length = st.labels.length;
|
|
||||||
foreach(char[] name, StateLabel *sl; st.labels)
|
|
||||||
{
|
|
||||||
assert(sl !is null);
|
|
||||||
assert(name == sl.name.str, "label name mismatch");
|
|
||||||
sl.index = cnt++;
|
|
||||||
st.labelList[sl.index] = sl;
|
|
||||||
|
|
||||||
// Cache the 'begin:' label
|
|
||||||
if(name == "begin")
|
|
||||||
{
|
|
||||||
assert(st.begin is null);
|
|
||||||
st.begin = sl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Compile it as a function.
|
|
||||||
void compile()
|
|
||||||
{
|
|
||||||
// No forward references must be inserted after the state has
|
|
||||||
// been resolved.
|
|
||||||
assert(forwards.length == 0);
|
|
||||||
|
|
||||||
tasm.newFunc();
|
|
||||||
code.compile();
|
|
||||||
|
|
||||||
// Table used to fetch the offset for the labels in this state.
|
|
||||||
uint offsets[];
|
|
||||||
|
|
||||||
offsets.length = st.labels.length;
|
|
||||||
|
|
||||||
// Assemble the code and get the offsets
|
|
||||||
st.bcode = tasm.assemble(st.lines, offsets);
|
|
||||||
|
|
||||||
// Store the offsets in the label statements themselves, for
|
|
||||||
// later use
|
|
||||||
int cnt = 0;
|
|
||||||
foreach(StateLabel* ls; st.labels)
|
|
||||||
{
|
|
||||||
ls.offs = offsets[ls.index];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] toString()
|
|
||||||
{
|
|
||||||
return
|
|
||||||
"State declaration: " ~
|
|
||||||
st.name.str ~ "\n" ~
|
|
||||||
code.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (structs.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.compiler.structs;
|
|
||||||
|
|
||||||
import monster.compiler.types;
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.compiler.variables;
|
|
||||||
import monster.compiler.functions;
|
|
||||||
import monster.compiler.tokenizer;
|
|
||||||
import monster.compiler.statement;
|
|
||||||
import monster.vm.error;
|
|
||||||
import std.stdio;
|
|
||||||
|
|
||||||
class StructDeclaration : TypeDeclaration
|
|
||||||
{
|
|
||||||
StructType type;
|
|
||||||
Token name;
|
|
||||||
|
|
||||||
private:
|
|
||||||
FuncDeclaration[] funcdecs;
|
|
||||||
VarDeclStatement[] vardecs;
|
|
||||||
|
|
||||||
// Identify what kind of block the given set of tokens represent,
|
|
||||||
// parse them, and store it in the appropriate list;
|
|
||||||
void store(ref TokenArray toks)
|
|
||||||
{
|
|
||||||
// canParse() is not ment as a complete syntax test, only to be
|
|
||||||
// enough to identify which Block parser to apply.
|
|
||||||
if(FuncDeclaration.canParse(toks))
|
|
||||||
{
|
|
||||||
auto fd = new FuncDeclaration;
|
|
||||||
funcdecs ~= fd;
|
|
||||||
fd.parse(toks);
|
|
||||||
}
|
|
||||||
else if(VarDeclStatement.canParse(toks))
|
|
||||||
{
|
|
||||||
auto vd = new VarDeclStatement;
|
|
||||||
vd.parse(toks);
|
|
||||||
vardecs ~= vd;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fail("Illegal type or declaration", toks);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
static bool canParse(TokenArray toks)
|
|
||||||
{ return toks.isNext(TT.Struct); }
|
|
||||||
|
|
||||||
override:
|
|
||||||
void parse(ref TokenArray toks)
|
|
||||||
{
|
|
||||||
if(!isNext(toks, TT.Struct, loc))
|
|
||||||
fail("Internal error in StructDeclaration");
|
|
||||||
|
|
||||||
if(!isNext(toks, TT.Identifier, name))
|
|
||||||
fail("Expected struct name", toks);
|
|
||||||
|
|
||||||
if(!isNext(toks, TT.LeftCurl))
|
|
||||||
fail("Struct expected {", toks);
|
|
||||||
|
|
||||||
// Parse the rest of the file
|
|
||||||
while(!isNext(toks, TT.RightCurl))
|
|
||||||
store(toks);
|
|
||||||
|
|
||||||
// Allow an optional semicolon
|
|
||||||
isNext(toks, TT.Semicolon);
|
|
||||||
}
|
|
||||||
|
|
||||||
void insertType(TFVScope last)
|
|
||||||
{
|
|
||||||
// Set up the struct type.
|
|
||||||
type = new StructType(this);
|
|
||||||
|
|
||||||
// Create a new scope
|
|
||||||
type.sc = new StructScope(last, type);
|
|
||||||
|
|
||||||
// Insert ourselves into the parent scope
|
|
||||||
assert(last !is null);
|
|
||||||
last.insertStruct(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void resolve(Scope last)
|
|
||||||
{
|
|
||||||
if(type.set)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// Get the total number of variables.
|
|
||||||
uint tot = 0;
|
|
||||||
foreach(dec; vardecs)
|
|
||||||
tot += dec.vars.length;
|
|
||||||
|
|
||||||
// Must have at least one variable per declaration statement
|
|
||||||
assert(tot >= vardecs.length);
|
|
||||||
|
|
||||||
// Get one array containing all the variables
|
|
||||||
Variable*[] vars = new Variable*[tot];
|
|
||||||
int ind = 0;
|
|
||||||
foreach(st; vardecs)
|
|
||||||
foreach(dec; st.vars)
|
|
||||||
vars[ind++] = dec.var;
|
|
||||||
assert(ind == tot);
|
|
||||||
|
|
||||||
// Store the variables in the StructType
|
|
||||||
type.vars = vars;
|
|
||||||
|
|
||||||
// Mark the size as "set" now.
|
|
||||||
type.set = true;
|
|
||||||
|
|
||||||
// Resolve
|
|
||||||
foreach(dec; vardecs)
|
|
||||||
dec.resolve(type.sc);
|
|
||||||
|
|
||||||
// Calculate the struct size
|
|
||||||
type.size = 0;
|
|
||||||
foreach(t; vars)
|
|
||||||
type.size += t.type.getSize();
|
|
||||||
|
|
||||||
// Set up the init value
|
|
||||||
ind = 0;
|
|
||||||
int[] init = new int[type.getSize()];
|
|
||||||
foreach(st; vardecs)
|
|
||||||
foreach(dec; st.vars)
|
|
||||||
{
|
|
||||||
int si = dec.var.type.getSize();
|
|
||||||
init[ind..ind+si] = dec.getCTimeValue();
|
|
||||||
ind += si;
|
|
||||||
}
|
|
||||||
assert(ind == init.length);
|
|
||||||
type.defInit = init;
|
|
||||||
|
|
||||||
// Functions:
|
|
||||||
|
|
||||||
// Disallow anything but normal functions (we can fix static and
|
|
||||||
// native struct functions later.)
|
|
||||||
|
|
||||||
// Struct resolve only resolves header information, it doesn't
|
|
||||||
// resolve function bodies.
|
|
||||||
assert(funcdecs.length == 0, "struct functions not supported yet");
|
|
||||||
/*
|
|
||||||
foreach(dec; funcdecs)
|
|
||||||
type.sc.insertFunc(dec);
|
|
||||||
*/
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,942 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (tokenizer.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.compiler.tokenizer;
|
|
||||||
|
|
||||||
import std.string;
|
|
||||||
import std.stream;
|
|
||||||
import std.stdio;
|
|
||||||
import std.utf;
|
|
||||||
|
|
||||||
import monster.util.string : begins;
|
|
||||||
|
|
||||||
import monster.vm.error;
|
|
||||||
import monster.options;
|
|
||||||
|
|
||||||
alias Token[] TokenArray;
|
|
||||||
|
|
||||||
// Check if a character is alpha-numerical or an underscore
|
|
||||||
bool validIdentChar(char c)
|
|
||||||
{
|
|
||||||
if(validFirstIdentChar(c) || numericalChar(c))
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same as above, except numbers are not allowed as the first
|
|
||||||
// character. Will extend to support full Unicode later.
|
|
||||||
bool validFirstIdentChar(char c)
|
|
||||||
{
|
|
||||||
if((c >= 'a' && c <= 'z') ||
|
|
||||||
(c >= 'A' && c <= 'Z') ||
|
|
||||||
(c == '_') ) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isValidIdent(char[] iname)
|
|
||||||
{
|
|
||||||
if(iname.length == 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if(!validFirstIdentChar(iname[0]))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
foreach(char c; iname)
|
|
||||||
if(!validIdentChar(c)) return false;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool numericalChar(char c)
|
|
||||||
{
|
|
||||||
return c >= '0' && c <= '9';
|
|
||||||
}
|
|
||||||
|
|
||||||
enum TT
|
|
||||||
{
|
|
||||||
// Syntax characters
|
|
||||||
Semicolon, DDDot, DDot,
|
|
||||||
LeftParen, RightParen,
|
|
||||||
LeftCurl, RightCurl,
|
|
||||||
LeftSquare, RightSquare,
|
|
||||||
Dot, Comma, Colon,
|
|
||||||
|
|
||||||
// Array length symbol
|
|
||||||
Dollar,
|
|
||||||
|
|
||||||
// 'at' sign, @
|
|
||||||
Alpha,
|
|
||||||
|
|
||||||
// Conditional expressions
|
|
||||||
IsEqual, NotEqual,
|
|
||||||
IsCaseEqual, IsCaseEqual2,
|
|
||||||
NotCaseEqual, NotCaseEqual2,
|
|
||||||
Less, More,
|
|
||||||
LessEq, MoreEq,
|
|
||||||
And, Or, Not,
|
|
||||||
|
|
||||||
// Assignment operators.
|
|
||||||
Equals, PlusEq, MinusEq, MultEq, DivEq, RemEq, IDivEq,
|
|
||||||
CatEq,
|
|
||||||
|
|
||||||
// Pre- and postfix increment and decrement operators ++ --
|
|
||||||
PlusPlus, MinusMinus,
|
|
||||||
|
|
||||||
// Arithmetic operators
|
|
||||||
Plus, Minus, Mult, Div, Rem, IDiv,
|
|
||||||
Cat,
|
|
||||||
|
|
||||||
// Keywords. Note that we use Class as a separator below, so it
|
|
||||||
// must be first in this list. All operator tokens must occur
|
|
||||||
// before Class, and all keywords must come after Class.
|
|
||||||
Class, Module, Singleton,
|
|
||||||
If, Else,
|
|
||||||
For, Foreach, ForeachRev,
|
|
||||||
Do, While, Until,
|
|
||||||
Continue, Break,
|
|
||||||
Typeof,
|
|
||||||
Return,
|
|
||||||
Switch, Select,
|
|
||||||
State,
|
|
||||||
Struct, Enum,
|
|
||||||
Import, Clone, Override, Final, Function, With,
|
|
||||||
This, New, Static, Const, Out, Ref, Abstract, Idle,
|
|
||||||
Public, Private, Protected, True, False, Native, Null,
|
|
||||||
Goto, Var,
|
|
||||||
|
|
||||||
Last, // Tokens after this do not have a specific string
|
|
||||||
// associated with them.
|
|
||||||
|
|
||||||
StringLiteral, // "something" or 'something'
|
|
||||||
IntLiteral, // Anything that starts with a number, except
|
|
||||||
// floats
|
|
||||||
FloatLiteral, // Any number which contains a period symbol
|
|
||||||
Identifier, // user-named identifier
|
|
||||||
EOF, // end of file
|
|
||||||
EMPTY // empty line (not stored)
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Token
|
|
||||||
{
|
|
||||||
TT type;
|
|
||||||
char[] str;
|
|
||||||
Floc loc;
|
|
||||||
|
|
||||||
// Translated string literal (with resolved escape codes.)
|
|
||||||
dchar[] str32;
|
|
||||||
|
|
||||||
// True if this token was the first on its line.
|
|
||||||
bool newline;
|
|
||||||
|
|
||||||
char[] toString() { return str; }
|
|
||||||
|
|
||||||
static Token opCall(char[] name, Floc loc)
|
|
||||||
{ return Token(TT.Identifier, name, loc); }
|
|
||||||
static Token opCall(TT tt, char[] name, Floc loc)
|
|
||||||
{
|
|
||||||
Token t;
|
|
||||||
t.type = tt;
|
|
||||||
t.str = name;
|
|
||||||
t.loc = loc;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used to look up keywords.
|
|
||||||
TT keywordLookup[char[]];
|
|
||||||
bool lookupSetup = false;
|
|
||||||
|
|
||||||
void initTokenizer()
|
|
||||||
{
|
|
||||||
assert(!lookupSetup);
|
|
||||||
|
|
||||||
// Insert the keywords into the lookup table
|
|
||||||
for(TT t = TT.Class; t < TT.Last; t++)
|
|
||||||
{
|
|
||||||
char[] tok = tokenList[t];
|
|
||||||
assert(tok != "");
|
|
||||||
assert((tok in keywordLookup) == null);
|
|
||||||
keywordLookup[tok] = t;
|
|
||||||
}
|
|
||||||
|
|
||||||
lookupSetup = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index table of all the tokens
|
|
||||||
const char[][] tokenList =
|
|
||||||
[
|
|
||||||
TT.Semicolon : ";",
|
|
||||||
TT.DDDot : "...",
|
|
||||||
TT.DDot : "..",
|
|
||||||
TT.LeftParen : "(",
|
|
||||||
TT.RightParen : ")",
|
|
||||||
TT.LeftCurl : "{",
|
|
||||||
TT.RightCurl : "}",
|
|
||||||
TT.LeftSquare : "[",
|
|
||||||
TT.RightSquare : "]",
|
|
||||||
TT.Dot : ".",
|
|
||||||
TT.Comma : ",",
|
|
||||||
TT.Colon : ":",
|
|
||||||
|
|
||||||
TT.Dollar : "$",
|
|
||||||
|
|
||||||
TT.Alpha : "@",
|
|
||||||
|
|
||||||
TT.IsEqual : "==",
|
|
||||||
TT.NotEqual : "!=",
|
|
||||||
|
|
||||||
TT.IsCaseEqual : "=i=",
|
|
||||||
TT.IsCaseEqual2 : "=I=",
|
|
||||||
TT.NotCaseEqual : "!=i=",
|
|
||||||
TT.NotCaseEqual2 : "!=I=",
|
|
||||||
|
|
||||||
TT.Less : "<",
|
|
||||||
TT.More : ">",
|
|
||||||
TT.LessEq : "<=",
|
|
||||||
TT.MoreEq : ">=",
|
|
||||||
TT.And : "&&",
|
|
||||||
TT.Or : "||",
|
|
||||||
TT.Not : "!",
|
|
||||||
|
|
||||||
TT.Equals : "=",
|
|
||||||
TT.PlusEq : "+=",
|
|
||||||
TT.MinusEq : "-=",
|
|
||||||
TT.MultEq : "*=",
|
|
||||||
TT.DivEq : "/=",
|
|
||||||
TT.RemEq : "%=",
|
|
||||||
TT.IDivEq : "\\=",
|
|
||||||
TT.CatEq : "~=",
|
|
||||||
|
|
||||||
TT.PlusPlus : "++",
|
|
||||||
TT.MinusMinus : "--",
|
|
||||||
TT.Cat : "~",
|
|
||||||
|
|
||||||
TT.Plus : "+",
|
|
||||||
TT.Minus : "-",
|
|
||||||
TT.Mult : "*",
|
|
||||||
TT.Div : "/",
|
|
||||||
TT.Rem : "%",
|
|
||||||
TT.IDiv : "\\",
|
|
||||||
|
|
||||||
TT.Class : "class",
|
|
||||||
TT.Module : "module",
|
|
||||||
TT.Return : "return",
|
|
||||||
TT.For : "for",
|
|
||||||
TT.This : "this",
|
|
||||||
TT.New : "new",
|
|
||||||
TT.If : "if",
|
|
||||||
TT.Else : "else",
|
|
||||||
TT.Foreach : "foreach",
|
|
||||||
TT.ForeachRev : "foreach_reverse",
|
|
||||||
TT.Do : "do",
|
|
||||||
TT.While : "while",
|
|
||||||
TT.Until : "until",
|
|
||||||
TT.Continue : "continue",
|
|
||||||
TT.Break : "break",
|
|
||||||
TT.Switch : "switch",
|
|
||||||
TT.Select : "select",
|
|
||||||
TT.State : "state",
|
|
||||||
TT.Struct : "struct",
|
|
||||||
TT.Enum : "enum",
|
|
||||||
TT.Import : "import",
|
|
||||||
TT.Typeof : "typeof",
|
|
||||||
TT.Singleton : "singleton",
|
|
||||||
TT.Clone : "clone",
|
|
||||||
TT.Static : "static",
|
|
||||||
TT.Const : "const",
|
|
||||||
TT.Abstract : "abstract",
|
|
||||||
TT.Override : "override",
|
|
||||||
TT.Final : "final",
|
|
||||||
TT.Function : "function",
|
|
||||||
TT.With : "with",
|
|
||||||
TT.Idle : "idle",
|
|
||||||
TT.Out : "out",
|
|
||||||
TT.Ref : "ref",
|
|
||||||
TT.Public : "public",
|
|
||||||
TT.Private : "private",
|
|
||||||
TT.Protected : "protected",
|
|
||||||
TT.True : "true",
|
|
||||||
TT.False : "false",
|
|
||||||
TT.Native : "native",
|
|
||||||
TT.Null : "null",
|
|
||||||
TT.Goto : "goto",
|
|
||||||
TT.Var : "var",
|
|
||||||
|
|
||||||
// These are only used in error messages
|
|
||||||
TT.StringLiteral : "string literal",
|
|
||||||
TT.IntLiteral : "integer literal",
|
|
||||||
TT.FloatLiteral : "floating point literal",
|
|
||||||
TT.Identifier : "identifier",
|
|
||||||
TT.EOF : "end of file",
|
|
||||||
TT.EMPTY : "empty line - you should never see this"
|
|
||||||
];
|
|
||||||
|
|
||||||
class Tokenizer
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
// Line buffer. Don't worry, this is perfectly safe. It is used by
|
|
||||||
// Stream.readLine, which uses the buffer if it fits and creates a
|
|
||||||
// new one if it doesn't. It is only here to optimize memory usage
|
|
||||||
// (avoid creating a new buffer for each line), and lines longer
|
|
||||||
// than 300 characters will work without problems.
|
|
||||||
char[300] buffer;
|
|
||||||
char[] line; // The rest of the current line
|
|
||||||
Stream inf;
|
|
||||||
int lineNum=-1;
|
|
||||||
char[] fname;
|
|
||||||
bool newline;
|
|
||||||
|
|
||||||
// Make a token of given type with given string, and remove it from
|
|
||||||
// the input line.
|
|
||||||
Token retToken(TT type, char[] str)
|
|
||||||
{
|
|
||||||
Token t;
|
|
||||||
t.type = type;
|
|
||||||
t.str = str;
|
|
||||||
t.newline = newline;
|
|
||||||
t.loc.fname = fname;
|
|
||||||
t.loc.line = lineNum;
|
|
||||||
|
|
||||||
// Special case for =I= and !=I=. Treat them the same as =i= and
|
|
||||||
// !=i=.
|
|
||||||
if(type == TT.IsCaseEqual2) t.type = TT.IsCaseEqual;
|
|
||||||
if(type == TT.NotCaseEqual2) t.type = TT.NotCaseEqual;
|
|
||||||
|
|
||||||
// Treat } as a separator
|
|
||||||
if(type == TT.RightCurl) t.newline = true;
|
|
||||||
|
|
||||||
// Remove the string from 'line', along with any following witespace
|
|
||||||
remWord(str);
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removes 'str' from the beginning of 'line', or from
|
|
||||||
// line[leadIn..$] if leadIn != 0.
|
|
||||||
void remWord(char[] str, int leadIn = 0)
|
|
||||||
{
|
|
||||||
assert(line.length >= leadIn);
|
|
||||||
line = line[leadIn..$];
|
|
||||||
|
|
||||||
assert(line.begins(str));
|
|
||||||
line = line[str.length..$].stripl();
|
|
||||||
}
|
|
||||||
|
|
||||||
Token eofToken()
|
|
||||||
{
|
|
||||||
Token t;
|
|
||||||
t.str = "<end of file>";
|
|
||||||
t.type = TT.EOF;
|
|
||||||
t.newline = true;
|
|
||||||
t.loc.line = lineNum;
|
|
||||||
t.loc.fname = fname;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
Token empty;
|
|
||||||
|
|
||||||
public:
|
|
||||||
final:
|
|
||||||
// Used when reading tokens from a file or a stream
|
|
||||||
this(char[] fname, Stream inf, int bom)
|
|
||||||
{
|
|
||||||
assert(inf !is null);
|
|
||||||
|
|
||||||
// The BOM (byte order mark) defines the byte order (little
|
|
||||||
// endian or big endian) and the encoding (utf8, utf16 or
|
|
||||||
// utf32).
|
|
||||||
switch(bom)
|
|
||||||
{
|
|
||||||
case -1:
|
|
||||||
// Files without a BOM are interpreted as UTF8
|
|
||||||
case BOM.UTF8:
|
|
||||||
// UTF8 is the default
|
|
||||||
break;
|
|
||||||
|
|
||||||
case BOM.UTF16LE:
|
|
||||||
case BOM.UTF16BE:
|
|
||||||
case BOM.UTF32LE:
|
|
||||||
case BOM.UTF32BE:
|
|
||||||
fail("UTF16 and UTF32 files are not supported yet");
|
|
||||||
default:
|
|
||||||
fail("Unknown BOM value!");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.inf = inf;
|
|
||||||
this.fname = fname;
|
|
||||||
|
|
||||||
this();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is used for single-line mode, such as in a console.
|
|
||||||
this()
|
|
||||||
{
|
|
||||||
empty.type = TT.EMPTY;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setLine(char[] ln)
|
|
||||||
{
|
|
||||||
assert(inf is null, "setLine only supported in line mode");
|
|
||||||
line = ln;
|
|
||||||
}
|
|
||||||
|
|
||||||
~this() { if(inf !is null) delete inf; }
|
|
||||||
|
|
||||||
void fail(char[] msg)
|
|
||||||
{
|
|
||||||
if(inf !is null)
|
|
||||||
// File mode
|
|
||||||
throw new MonsterException(format("%s:%s: %s", fname, lineNum, msg));
|
|
||||||
else
|
|
||||||
// Line mode
|
|
||||||
throw new MonsterException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Various parsing modes
|
|
||||||
enum
|
|
||||||
{
|
|
||||||
Normal, // Normal mode
|
|
||||||
Block, // Block comment
|
|
||||||
Nest // Nested block comment
|
|
||||||
}
|
|
||||||
int mode = Normal;
|
|
||||||
int nests = 0; // Nest level
|
|
||||||
|
|
||||||
// Get the next token from the line, if any
|
|
||||||
Token getNextFromLine()
|
|
||||||
{
|
|
||||||
assert(lookupSetup,
|
|
||||||
"Internal error: The tokenizer lookup table has not been set up!");
|
|
||||||
|
|
||||||
restart:
|
|
||||||
|
|
||||||
if(mode == Block)
|
|
||||||
{
|
|
||||||
int index = line.find("*/");
|
|
||||||
|
|
||||||
// If we find a '*/', the comment is done
|
|
||||||
if(index != -1)
|
|
||||||
{
|
|
||||||
mode = Normal;
|
|
||||||
|
|
||||||
// Cut the comment from the input
|
|
||||||
remWord("*/", index);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Comment was not terminated on this line, try the next
|
|
||||||
line = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(mode == Nest)
|
|
||||||
{
|
|
||||||
// Check for nested /+ and +/ in here, but go to restart if
|
|
||||||
// none is found (meaning the comment continues on the next
|
|
||||||
// line), or reset mode and go to restart if nest level ever
|
|
||||||
// gets to 0.
|
|
||||||
|
|
||||||
while(line.length >= 2)
|
|
||||||
{
|
|
||||||
int incInd = -1;
|
|
||||||
int decInd = -1;
|
|
||||||
// Find the first matching '/+' or '+/
|
|
||||||
foreach(int i, char c; line[0..$-1])
|
|
||||||
{
|
|
||||||
if(c == '/' && line[i+1] == '+')
|
|
||||||
{
|
|
||||||
incInd = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if(c == '+' && line[i+1] == '/')
|
|
||||||
{
|
|
||||||
decInd = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add a nest level when '/+' is found
|
|
||||||
if(incInd != -1)
|
|
||||||
{
|
|
||||||
remWord("/+", incInd);
|
|
||||||
nests++;
|
|
||||||
continue; // Search more in this line
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove a nest level when '+/' is found
|
|
||||||
else if(decInd != -1)
|
|
||||||
{
|
|
||||||
// Remove the +/ from input
|
|
||||||
remWord("+/", decInd);
|
|
||||||
|
|
||||||
nests--; // Remove a level
|
|
||||||
assert(nests >= 0);
|
|
||||||
|
|
||||||
// Are we done? If so, return to normal mode.
|
|
||||||
if(nests == 0)
|
|
||||||
{
|
|
||||||
mode = Normal;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nothing found on this line, try the next
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're still in nested comment mode, ignore the rest of
|
|
||||||
// the line
|
|
||||||
if(mode == Nest)
|
|
||||||
line = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Comment - ignore the rest of the line
|
|
||||||
if(line.begins("//"))
|
|
||||||
line = null;
|
|
||||||
|
|
||||||
// If the line is empty at this point, there's nothing more to
|
|
||||||
// be done
|
|
||||||
if(line == "")
|
|
||||||
return empty;
|
|
||||||
|
|
||||||
// Block comment
|
|
||||||
if(line.begins("/*"))
|
|
||||||
{
|
|
||||||
mode = Block;
|
|
||||||
line = line[2..$];
|
|
||||||
goto restart;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nested comment
|
|
||||||
if(line.begins("/+"))
|
|
||||||
{
|
|
||||||
mode = Nest;
|
|
||||||
line = line[2..$];
|
|
||||||
nests++;
|
|
||||||
goto restart;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(line.begins("*/")) fail("Unexpected end of block comment");
|
|
||||||
if(line.begins("+/")) fail("Unexpected end of nested comment");
|
|
||||||
|
|
||||||
// String literals (multi-line literals not implemented yet)
|
|
||||||
if(line.begins("\"") || // Standard string: "abc"
|
|
||||||
line.begins("r\"") || // Wysiwig string: r"c:\dir"
|
|
||||||
line.begins("\\\"") || // ditto: \"c:\dir"
|
|
||||||
line.begins("'") ||
|
|
||||||
line.begins("r'") || // Equivalent ' versions
|
|
||||||
line.begins("\\'"))
|
|
||||||
{
|
|
||||||
bool found = false;
|
|
||||||
bool wysiwig = false;
|
|
||||||
|
|
||||||
// Quote character that terminates this string.
|
|
||||||
char quote;
|
|
||||||
|
|
||||||
char[] slice = line;
|
|
||||||
|
|
||||||
// Removes the first num chars from the line
|
|
||||||
void skip(int num)
|
|
||||||
{
|
|
||||||
assert(num <= line.length);
|
|
||||||
slice = slice[num..$];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Parse the first quotation
|
|
||||||
if(slice[0] == '"' || slice[0] == '\'')
|
|
||||||
{
|
|
||||||
quote = slice[0];
|
|
||||||
skip(1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Check for wysiwig strings
|
|
||||||
if(slice[0] == '\\' || slice[0] == 'r')
|
|
||||||
wysiwig = true;
|
|
||||||
else assert(0);
|
|
||||||
|
|
||||||
quote = slice[1];
|
|
||||||
skip(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(quote == '"' || quote == '\'');
|
|
||||||
|
|
||||||
// This will store the result
|
|
||||||
dchar[] result;
|
|
||||||
|
|
||||||
// Stores a single character in the result string, and
|
|
||||||
// removes a given number of input characters.
|
|
||||||
void store(dchar ch, int slen)
|
|
||||||
{
|
|
||||||
result ~= ch;
|
|
||||||
skip(slen);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a given code into 'ch', if it is found.
|
|
||||||
void convert(char[] code, dchar ch)
|
|
||||||
{
|
|
||||||
if(slice.begins(code))
|
|
||||||
store(ch, code.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert given escape character to 'res'
|
|
||||||
void escape(char ch, dchar res)
|
|
||||||
{
|
|
||||||
if(slice.length >= 2 &&
|
|
||||||
slice[0] == '\\' &&
|
|
||||||
slice[1] == ch)
|
|
||||||
store(res, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interpret string
|
|
||||||
while(slice.length)
|
|
||||||
{
|
|
||||||
int startLen = slice.length;
|
|
||||||
|
|
||||||
// Convert "" to " (or '' to ' in single-quote strings)
|
|
||||||
convert(""~quote~quote, quote);
|
|
||||||
|
|
||||||
// Interpret backslash escape codes if we're not in
|
|
||||||
// wysiwig mode
|
|
||||||
if(!wysiwig)
|
|
||||||
{
|
|
||||||
escape('"', '"'); // \" == literal "
|
|
||||||
escape('\'', '\''); // \' == literal '
|
|
||||||
escape('\\', '\\'); // \\ == literal \
|
|
||||||
|
|
||||||
escape('a', 7); // \a == bell
|
|
||||||
escape('b', 8); // \b == backspace
|
|
||||||
escape('f', 12); // \f == feed form
|
|
||||||
escape('n', '\n'); // \n == newline
|
|
||||||
escape('r', '\r'); // \r == carriage return
|
|
||||||
escape('t', '\t'); // \t == tab
|
|
||||||
escape('v', '\v'); // \v == vertical tab
|
|
||||||
escape('e', 27); // \e == ANSI escape
|
|
||||||
|
|
||||||
// Check for numerical escapes
|
|
||||||
|
|
||||||
// If either of these aren't met, this isn't a valid
|
|
||||||
// escape code.
|
|
||||||
if(slice.length < 2 ||
|
|
||||||
slice[0] != '\\')
|
|
||||||
goto nocode;
|
|
||||||
|
|
||||||
// Checks and converts the digits in slice[] into a
|
|
||||||
// character.
|
|
||||||
void convertNumber(int skp, int maxLen, int base,
|
|
||||||
char[] pattern, char[] name)
|
|
||||||
{
|
|
||||||
assert(base <= 16);
|
|
||||||
|
|
||||||
// Skip backslash and other leading characters
|
|
||||||
skip(skp);
|
|
||||||
|
|
||||||
int len; // Number of digits found
|
|
||||||
uint result = 0;
|
|
||||||
|
|
||||||
for(len=0; len<maxLen; len++)
|
|
||||||
{
|
|
||||||
if(slice.length <= len) break;
|
|
||||||
|
|
||||||
char digit = slice[len];
|
|
||||||
|
|
||||||
// Does the digit qualify?
|
|
||||||
if(!inPattern(digit, pattern))
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Multiply up the existing number to
|
|
||||||
// make room for the digit.
|
|
||||||
result *= base;
|
|
||||||
|
|
||||||
// Convert single digit to a number
|
|
||||||
if(digit >= '0' && digit <= '9')
|
|
||||||
digit -= '0';
|
|
||||||
else if(digit >= 'a' && digit <= 'z')
|
|
||||||
digit -= 'a' - 10;
|
|
||||||
else if(digit >= 'A' && digit <= 'Z')
|
|
||||||
digit -= 'A' - 10;
|
|
||||||
assert(digit >= 0 && digit < base);
|
|
||||||
|
|
||||||
// Add inn the digit
|
|
||||||
result += digit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(len > 0)
|
|
||||||
{
|
|
||||||
// We got something. Convert it and store
|
|
||||||
// it.
|
|
||||||
store(result, len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
fail("Invalid " ~ name ~ " escape code");
|
|
||||||
}
|
|
||||||
|
|
||||||
const Dec = "0-9";
|
|
||||||
const Oct = "0-7";
|
|
||||||
const Hex = "0-9a-fA-F";
|
|
||||||
|
|
||||||
// Octal escapes: \0N, \0NN or \0NNN where N are
|
|
||||||
// octal digits (0-7). Also accepts \o instead of
|
|
||||||
// \0.
|
|
||||||
if(slice[1] == '0' || slice[1] == 'o')
|
|
||||||
convertNumber(2, 3, 8, Oct, "octal");
|
|
||||||
|
|
||||||
// Decimal escapes: \N \NN and \NNN, where N are
|
|
||||||
// digits and the first digit is not zero.
|
|
||||||
else if(inPattern(slice[1], Dec))
|
|
||||||
convertNumber(1, 3, 10, Dec, "decimal");
|
|
||||||
|
|
||||||
// Hex escape codes: \xXX where X are hex digits
|
|
||||||
else if(slice[1] == 'x')
|
|
||||||
convertNumber(2, 2, 16, Hex, "hex");
|
|
||||||
|
|
||||||
// Unicode escape codes:
|
|
||||||
// \uXXXX
|
|
||||||
else if(slice[1] == 'u')
|
|
||||||
convertNumber(2, 4, 16, Hex, "Unicode hex");
|
|
||||||
|
|
||||||
// \UXXXXXXXX
|
|
||||||
else if(slice[1] == 'U')
|
|
||||||
convertNumber(2, 8, 16, Hex, "Unicode hex");
|
|
||||||
|
|
||||||
}
|
|
||||||
nocode:
|
|
||||||
|
|
||||||
// If something was converted this round, start again
|
|
||||||
// from the top.
|
|
||||||
if(startLen != slice.length)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
assert(slice.length > 0);
|
|
||||||
|
|
||||||
// Nothing was done. Are we at the end of the string?
|
|
||||||
if(slice[0] == quote)
|
|
||||||
{
|
|
||||||
skip(1);
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unhandled escape code?
|
|
||||||
if(slice[0] == '\\' && !wysiwig)
|
|
||||||
{
|
|
||||||
if(slice.length == 0)
|
|
||||||
// Just a single \ at the end of the line
|
|
||||||
fail("Multiline string literals not implemented");
|
|
||||||
else
|
|
||||||
fail("Unhandled escape code: \\" ~ slice[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nope. It's just a normal character. Decode it from
|
|
||||||
// UTF8.
|
|
||||||
size_t clen = 0;
|
|
||||||
dchar cres;
|
|
||||||
cres = decode(slice,clen);
|
|
||||||
store(cres, clen);
|
|
||||||
}
|
|
||||||
if(!found) fail("Unterminated string literal '" ~line~ "'");
|
|
||||||
|
|
||||||
// Set 'slice' to contain the original string
|
|
||||||
slice = line[0..(line.length-slice.length)];
|
|
||||||
|
|
||||||
// Set up the token
|
|
||||||
auto t = retToken(TT.StringLiteral, slice.dup);
|
|
||||||
t.str32 = result;
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Numerical literals - if it starts with a number, we accept
|
|
||||||
// it, until it is interupted by an unacceptable character. We
|
|
||||||
// also accept numbers on the form .NUM. We do not try to parse
|
|
||||||
// the number here.
|
|
||||||
if(numericalChar(line[0]) ||
|
|
||||||
// Cover the .num case
|
|
||||||
( line.length >= 2 && line[0] == '.' &&
|
|
||||||
numericalChar(line[1]) ))
|
|
||||||
{
|
|
||||||
// Treat the rest as we would an identifier - the actual
|
|
||||||
// interpretation will be done later. We allow non-numerical
|
|
||||||
// tokens in the literal, such as 0x0a or 1_000_000. We must
|
|
||||||
// also explicitly allow '.' dots.
|
|
||||||
int len = 1;
|
|
||||||
bool lastDot = false; // Was the last char a '.'?
|
|
||||||
int dots; // Number of dots
|
|
||||||
foreach(char ch; line[1..$])
|
|
||||||
{
|
|
||||||
if(ch == '.')
|
|
||||||
{
|
|
||||||
// We accept "." but not "..", as this might be an
|
|
||||||
// operator.
|
|
||||||
if(lastDot)
|
|
||||||
{
|
|
||||||
// Remove the last dot and exit.
|
|
||||||
len--;
|
|
||||||
dots--;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lastDot = true;
|
|
||||||
dots++;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if(!validIdentChar(ch)) break;
|
|
||||||
lastDot = false;
|
|
||||||
//lastPer = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// This was a valid character, count it
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
if(dots != 0)
|
|
||||||
return retToken(TT.FloatLiteral, line[0..len].dup);
|
|
||||||
else
|
|
||||||
return retToken(TT.IntLiteral, line[0..len].dup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for identifiers
|
|
||||||
if(validFirstIdentChar(line[0]))
|
|
||||||
{
|
|
||||||
// It's an identifier or name, find the length
|
|
||||||
int len = 1;
|
|
||||||
foreach(char ch; line[1..$])
|
|
||||||
{
|
|
||||||
if(!validIdentChar(ch)) break;
|
|
||||||
len++;
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] id = line[0..len];
|
|
||||||
|
|
||||||
// We only allow certain identifiers to begin with __, as
|
|
||||||
// these are reserved for internal use.
|
|
||||||
if(id.begins("__"))
|
|
||||||
if(id != "__STACK__")
|
|
||||||
fail("Identifier " ~ id ~ " is not allowed to begin with __");
|
|
||||||
|
|
||||||
// Check if this is a keyword
|
|
||||||
if(id in keywordLookup)
|
|
||||||
{
|
|
||||||
TT t = keywordLookup[id];
|
|
||||||
assert(t >= TT.Class && t < TT.Last,
|
|
||||||
"Found " ~ id ~ " as a keyword, but with wrong type!");
|
|
||||||
return retToken(t, tokenList[t]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not a keyword? Then it's an identifier
|
|
||||||
return retToken(TT.Identifier, id.dup);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for operators and syntax characters. We browse through
|
|
||||||
// the entire list, and select the longest match that fits (so
|
|
||||||
// we don't risk matching "+" to "+=", for example.)
|
|
||||||
TT match;
|
|
||||||
int mlen = 0;
|
|
||||||
foreach(int i, char[] tok; tokenList[0..TT.Class])
|
|
||||||
{
|
|
||||||
// Skip =i= and family, if monster.options tells us to
|
|
||||||
static if(!ciStringOps)
|
|
||||||
{
|
|
||||||
if(i == TT.IsCaseEqual || i == TT.IsCaseEqual2 ||
|
|
||||||
i == TT.NotCaseEqual || i == TT.NotCaseEqual2)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(line.begins(tok) && tok.length >= mlen)
|
|
||||||
{
|
|
||||||
assert(tok.length > mlen, "Two matching tokens of the same length");
|
|
||||||
mlen = tok.length;
|
|
||||||
match = cast(TT) i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(mlen) return retToken(match, tokenList[match]);
|
|
||||||
|
|
||||||
// Invalid token
|
|
||||||
fail("Invalid token " ~ line);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the next token from a stream
|
|
||||||
Token getNext()
|
|
||||||
{
|
|
||||||
assert(inf !is null, "getNext() found a null stream");
|
|
||||||
|
|
||||||
if(lineNum == -1) lineNum = 0;
|
|
||||||
|
|
||||||
restart:
|
|
||||||
newline = false;
|
|
||||||
// Get the next line, if the current is empty
|
|
||||||
while(line.length == 0)
|
|
||||||
{
|
|
||||||
// No more information, we're done
|
|
||||||
if(inf.eof())
|
|
||||||
{
|
|
||||||
if(mode == Block) fail("Unterminated block comment");
|
|
||||||
if(mode == Nest) fail("Unterminated nested comment");
|
|
||||||
return eofToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read a line and remove leading and trailing whitespace
|
|
||||||
line = inf.readLine(buffer).strip();
|
|
||||||
lineNum++;
|
|
||||||
newline = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(line.length > 0);
|
|
||||||
|
|
||||||
static if(skipHashes)
|
|
||||||
{
|
|
||||||
// Skip the line if it begins with #.
|
|
||||||
if(/*lineNum == 1 && */line.begins("#"))
|
|
||||||
{
|
|
||||||
line = null;
|
|
||||||
goto restart;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Token tt = getNextFromLine();
|
|
||||||
|
|
||||||
// Skip empty lines, don't return them into the token list.
|
|
||||||
if(tt.type == TT.EMPTY)
|
|
||||||
goto restart;
|
|
||||||
|
|
||||||
return tt;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read the entire file into an array of tokens. This includes the EOF
|
|
||||||
// token at the end.
|
|
||||||
TokenArray tokenizeStream(char[] fname, Stream stream, int bom)
|
|
||||||
{
|
|
||||||
TokenArray tokenArray;
|
|
||||||
|
|
||||||
Tokenizer tok = new Tokenizer(fname, stream, bom);
|
|
||||||
Token tt;
|
|
||||||
do
|
|
||||||
{
|
|
||||||
tt = tok.getNext();
|
|
||||||
tokenArray ~= tt;
|
|
||||||
}
|
|
||||||
while(tt.type != TT.EOF)
|
|
||||||
delete tok;
|
|
||||||
|
|
||||||
return tokenArray;
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (all.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.modules.all;
|
|
||||||
|
|
||||||
import monster.modules.io;
|
|
||||||
import monster.modules.math;
|
|
||||||
import monster.modules.timer;
|
|
||||||
import monster.modules.frames;
|
|
||||||
import monster.modules.random;
|
|
||||||
import monster.modules.threads;
|
|
||||||
|
|
||||||
import monster.options;
|
|
||||||
|
|
||||||
bool has(char[] str, char[] sub)
|
|
||||||
{
|
|
||||||
if(sub.length == 0) return false;
|
|
||||||
|
|
||||||
int diff = str.length;
|
|
||||||
int sln = sub.length;
|
|
||||||
|
|
||||||
diff -= sln;
|
|
||||||
for(int i=0; i<=diff; i++)
|
|
||||||
if(str[i..i+sln] == sub[])
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initAllModules()
|
|
||||||
{
|
|
||||||
static if(moduleList.has("io")) initIOModule();
|
|
||||||
static if(moduleList.has("timer")) initTimerModule();
|
|
||||||
static if(moduleList.has("frames")) initFramesModule();
|
|
||||||
static if(moduleList.has("thread")) initThreadModule();
|
|
||||||
static if(moduleList.has("random")) initRandomModule();
|
|
||||||
static if(moduleList.has("math")) initMathModule();
|
|
||||||
}
|
|
@ -1,553 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (console.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.modules.console;
|
|
||||||
|
|
||||||
// This file implements an optimized way of running Monster
|
|
||||||
// interactively, what is sometimes called "line mode" or "interactive
|
|
||||||
// mode". It is ideally suited for making ingame consoles.
|
|
||||||
|
|
||||||
// The main idea is to retain all reusable data structures and
|
|
||||||
// minimize the number of heap allocations at runtime. The current
|
|
||||||
// implemention is not perfected in that regard, but later
|
|
||||||
// implementations will be.
|
|
||||||
|
|
||||||
// All input into the console is given through the input() function,
|
|
||||||
// and all output is sent to the output() callback function. You can
|
|
||||||
// also poll for output manually using output();
|
|
||||||
|
|
||||||
import monster.util.growarray;
|
|
||||||
import monster.compiler.tokenizer;
|
|
||||||
import monster.compiler.statement;
|
|
||||||
import monster.compiler.variables;
|
|
||||||
import monster.compiler.functions;
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.compiler.bytecode;
|
|
||||||
import monster.compiler.assembler;
|
|
||||||
import monster.compiler.types;
|
|
||||||
import monster.compiler.expression;
|
|
||||||
import std.stdio;
|
|
||||||
import std.string;
|
|
||||||
import monster.monster;
|
|
||||||
|
|
||||||
// Console results
|
|
||||||
enum CR
|
|
||||||
{
|
|
||||||
Ok = 1, // Command was executed
|
|
||||||
Error = 2, // An error occurred
|
|
||||||
More = 3, // An unterminated multi-line statement was entered, need
|
|
||||||
// more input
|
|
||||||
Empty = 4, // The line was empty (nothing was executed)
|
|
||||||
}
|
|
||||||
|
|
||||||
class Console
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
Tokenizer tn;
|
|
||||||
|
|
||||||
GrowArray!(Token) tokArr;
|
|
||||||
GrowArray!(char) outBuf;
|
|
||||||
|
|
||||||
Function fn;
|
|
||||||
|
|
||||||
FuncScope sc;
|
|
||||||
|
|
||||||
MonsterObject *obj;
|
|
||||||
|
|
||||||
Variable* varList[];
|
|
||||||
uint varSize;
|
|
||||||
|
|
||||||
// The thread that we run console commands in. It's put in the
|
|
||||||
// background when not in use.
|
|
||||||
Thread *trd;
|
|
||||||
|
|
||||||
// The thread that was running when we started (if any)
|
|
||||||
Thread *store;
|
|
||||||
|
|
||||||
int paren, curl, square;
|
|
||||||
|
|
||||||
void delegate(char[] str) output_cb;
|
|
||||||
bool hasCallback;
|
|
||||||
|
|
||||||
char[] norm_prompt = ">>> ";
|
|
||||||
int tab = 4;
|
|
||||||
char[] ml_prompt = "... ";
|
|
||||||
char[] cmt_prompt = "(comment) ";
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool allowVar = true;
|
|
||||||
|
|
||||||
this(MonsterObject *ob = null)
|
|
||||||
{
|
|
||||||
tn = new Tokenizer();
|
|
||||||
|
|
||||||
// Set the context object
|
|
||||||
obj = ob;
|
|
||||||
if(obj is null)
|
|
||||||
obj = Function.getIntMO();
|
|
||||||
|
|
||||||
// Next set up the function and the scope
|
|
||||||
fn.name.str = "__console";
|
|
||||||
fn.owner = obj.cls;
|
|
||||||
sc = new FuncScope(obj.cls.sc, &fn);
|
|
||||||
|
|
||||||
// Get a new thread
|
|
||||||
trd = Thread.getPaused();
|
|
||||||
}
|
|
||||||
|
|
||||||
void put(char[] str, bool newLine=false)
|
|
||||||
{
|
|
||||||
if(hasCallback)
|
|
||||||
{
|
|
||||||
output_cb(str);
|
|
||||||
if(newLine)
|
|
||||||
output_cb("\n");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outBuf ~= str;
|
|
||||||
if(newLine)
|
|
||||||
outBuf ~= '\n';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void putln(char[] str) { put(str, true); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
Statement[] parse(TokenArray toks, Scope sc)
|
|
||||||
{
|
|
||||||
Statement b;
|
|
||||||
Statement[] res;
|
|
||||||
|
|
||||||
repeat:
|
|
||||||
|
|
||||||
b = null;
|
|
||||||
|
|
||||||
if(CodeBlock.canParse(toks)) b = new CodeBlock;
|
|
||||||
else if(IfStatement.canParse(toks)) b = new IfStatement;
|
|
||||||
else if(DoWhileStatement.canParse(toks)) b = new DoWhileStatement;
|
|
||||||
else if(WhileStatement.canParse(toks)) b = new WhileStatement;
|
|
||||||
else if(ForStatement.canParse(toks)) b = new ForStatement;
|
|
||||||
else if(ForeachStatement.canParse(toks)) b = new ForeachStatement;
|
|
||||||
else if(ImportStatement.canParse(toks)) b = new ImportStatement(true);
|
|
||||||
|
|
||||||
if(b !is null)
|
|
||||||
{
|
|
||||||
// Parse and resolve
|
|
||||||
b.parse(toks);
|
|
||||||
b.resolve(sc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If this is not one of the above, default to a console
|
|
||||||
// statement.
|
|
||||||
auto es = new ConsoleStatement;
|
|
||||||
b = es;
|
|
||||||
|
|
||||||
// Parse and resolve in one operation.
|
|
||||||
es.parseResolve(toks, sc, allowVar);
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(b !is null);
|
|
||||||
res ~= b;
|
|
||||||
|
|
||||||
// Are there more tokens waiting for us?
|
|
||||||
if(toks.length > 0)
|
|
||||||
goto repeat;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sumParen()
|
|
||||||
{
|
|
||||||
// Clean up if we had an unmatched end bracket somewhere
|
|
||||||
if(paren < 0) paren = 0;
|
|
||||||
if(curl < 0) curl = 0;
|
|
||||||
if(square < 0) square = 0;
|
|
||||||
|
|
||||||
return paren + curl + square;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isComment()
|
|
||||||
{
|
|
||||||
return tn.mode != Tokenizer.Normal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resets the console to a usable state. Does not delete variables.
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
paren = 0;
|
|
||||||
curl = 0;
|
|
||||||
square = 0;
|
|
||||||
tn.mode = Tokenizer.Normal;
|
|
||||||
|
|
||||||
if(cthread is trd)
|
|
||||||
{
|
|
||||||
// Reset the function stack.
|
|
||||||
trd.fstack.killAll();
|
|
||||||
|
|
||||||
assert(trd.fstack.isEmpty);
|
|
||||||
assert(!trd.fstack.hasNatives);
|
|
||||||
|
|
||||||
// Our variables should still be on the stack though.
|
|
||||||
if(stack.getPos > varSize)
|
|
||||||
stack.popInts(stack.getPos - varSize);
|
|
||||||
else
|
|
||||||
assert(stack.getPos == varSize);
|
|
||||||
|
|
||||||
// Make sure the thread is still in the 'paused' mode
|
|
||||||
trd.moveTo(&scheduler.paused);
|
|
||||||
|
|
||||||
// Background the thread - this will also capture the stack
|
|
||||||
trd.background();
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(trd !is cthread);
|
|
||||||
|
|
||||||
// Restore the previous thread (if any)
|
|
||||||
if(store !is null)
|
|
||||||
store.foreground();
|
|
||||||
|
|
||||||
store = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push the input into the compiler and run it
|
|
||||||
CR runInput(char[] str)
|
|
||||||
{
|
|
||||||
// Set up the tokenizer
|
|
||||||
tn.setLine(str);
|
|
||||||
|
|
||||||
// Reset the token buffer, unless we're in a multiline command
|
|
||||||
if(paren == 0 && curl == 0 && square == 0)
|
|
||||||
tokArr.length = 0;
|
|
||||||
|
|
||||||
// Phase I, tokenize
|
|
||||||
Token t = tn.getNextFromLine();
|
|
||||||
// Mark the first token as a newline / separator
|
|
||||||
t.newline = true;
|
|
||||||
while(t.type != TT.EMPTY)
|
|
||||||
{
|
|
||||||
if(t.type == TT.LeftParen)
|
|
||||||
paren++;
|
|
||||||
else if(t.type == TT.LeftCurl)
|
|
||||||
curl++;
|
|
||||||
else if(t.type == TT.LeftSquare)
|
|
||||||
square++;
|
|
||||||
else if(t.type == TT.RightParen)
|
|
||||||
paren--;
|
|
||||||
else if(t.type == TT.RightCurl)
|
|
||||||
curl--;
|
|
||||||
else if(t.type == TT.RightSquare)
|
|
||||||
square--;
|
|
||||||
|
|
||||||
tokArr ~= t;
|
|
||||||
t = tn.getNextFromLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(paren < 0 || curl < 0 || square < 0)
|
|
||||||
fail("Unmatched end bracket(s)");
|
|
||||||
|
|
||||||
// Wait for more input inside a bracket
|
|
||||||
if(sumParen() > 0)
|
|
||||||
return CR.More;
|
|
||||||
|
|
||||||
// Ditto for block comments
|
|
||||||
if(isComment())
|
|
||||||
return CR.More;
|
|
||||||
|
|
||||||
// Ignore empty token lists
|
|
||||||
if(tokArr.length == 0)
|
|
||||||
return CR.Empty;
|
|
||||||
|
|
||||||
// Phase II & III, parse and resolve
|
|
||||||
TokenArray toks = tokArr.arrayCopy();
|
|
||||||
Statement[] sts = parse(toks, sc);
|
|
||||||
delete toks;
|
|
||||||
assert(sts.length >= 1);
|
|
||||||
|
|
||||||
// First, background the current thread (if any) and bring up
|
|
||||||
// our own. This is necessary in order to keep the stack
|
|
||||||
// variables we make.
|
|
||||||
store = cthread;
|
|
||||||
if(store !is null)
|
|
||||||
store.background();
|
|
||||||
assert(trd !is null);
|
|
||||||
trd.foreground();
|
|
||||||
|
|
||||||
// We have to push ourselves on the function stack, or
|
|
||||||
// Function.call() will see that it's empty and kill the thread
|
|
||||||
// upon exit
|
|
||||||
trd.fstack.pushExt("Console");
|
|
||||||
|
|
||||||
// The rest must be performed separately for each statement on
|
|
||||||
// the line.
|
|
||||||
foreach(st; sts)
|
|
||||||
{
|
|
||||||
// Phase IV, compile
|
|
||||||
tasm.newFunc();
|
|
||||||
|
|
||||||
// Expression to print, if any
|
|
||||||
Expression printExp = null;
|
|
||||||
|
|
||||||
// Is it a special console statement?
|
|
||||||
auto cs = cast(ConsoleStatement)st;
|
|
||||||
if(cs !is null)
|
|
||||||
{
|
|
||||||
// Get the client expression, if any.
|
|
||||||
ExprStatement es = cast(ExprStatement)cs.client;
|
|
||||||
if(es !is null)
|
|
||||||
{
|
|
||||||
// It's a normal expression
|
|
||||||
if(es.right is null)
|
|
||||||
printExp = es.left;
|
|
||||||
}
|
|
||||||
// Not an expression, maybe a function?
|
|
||||||
else if(cs.func !is null)
|
|
||||||
{
|
|
||||||
// Yup, store it
|
|
||||||
printExp = cs.func;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ok, we got an expression. But is the type usable?
|
|
||||||
if(printExp !is null)
|
|
||||||
{
|
|
||||||
auto tp = printExp.type;
|
|
||||||
auto strt = ArrayType.getString();
|
|
||||||
assert(tp !is null);
|
|
||||||
assert(strt !is null);
|
|
||||||
|
|
||||||
if(tp == strt || tp.canCastTo(strt))
|
|
||||||
{
|
|
||||||
// Yup, it is! Cast the expression to string.
|
|
||||||
strt.typeCast(printExp, "console output");
|
|
||||||
printExp.eval();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// Type isn't usable, so set printExp to null to flag
|
|
||||||
// this.
|
|
||||||
printExp = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(printExp is null)
|
|
||||||
// No expression is being used, so compile the statement
|
|
||||||
// normally.
|
|
||||||
st.compile();
|
|
||||||
|
|
||||||
// Gather all the statements into one function and get the
|
|
||||||
// bytecode.
|
|
||||||
fn.bcode = tasm.assemble(fn.lines);
|
|
||||||
fn.bcode ~= cast(ubyte)BC.Exit; // Non-optimal hack
|
|
||||||
|
|
||||||
// Phase V, call the function
|
|
||||||
fn.call(obj);
|
|
||||||
|
|
||||||
// Finally, get the expression result, if any, and print it.
|
|
||||||
if(printExp !is null)
|
|
||||||
putln(stack.popString8());
|
|
||||||
|
|
||||||
// In the case of new a variable declaration, we have to
|
|
||||||
// make sure they are accessible to any subsequent calls to
|
|
||||||
// the function. Since the stack frame gets set at each
|
|
||||||
// call, we have to access the variables outside the
|
|
||||||
// function frame, ie. the same way we treat function
|
|
||||||
// parameters. We do this by giving the variables negative
|
|
||||||
// indices.
|
|
||||||
if(cs !is null)
|
|
||||||
{
|
|
||||||
auto vs = cast(VarDeclStatement)cs.client;
|
|
||||||
if(vs !is null)
|
|
||||||
{
|
|
||||||
// Add the new vars to the list
|
|
||||||
foreach(v; vs.vars)
|
|
||||||
{
|
|
||||||
varList ~= v.var;
|
|
||||||
|
|
||||||
// Add the size as well
|
|
||||||
varSize += v.var.type.getSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recalculate all the indices backwards from zero
|
|
||||||
int place = 0;
|
|
||||||
foreach_reverse(v; varList)
|
|
||||||
{
|
|
||||||
place -= v.type.getSize;
|
|
||||||
v.number = place;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the scope stack counters
|
|
||||||
sc.reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
trd.fstack.pop();
|
|
||||||
|
|
||||||
// Reset the console to a usable state
|
|
||||||
reset();
|
|
||||||
|
|
||||||
return CR.Ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
void prompt()
|
|
||||||
{
|
|
||||||
int sum = sumParen();
|
|
||||||
bool isBracket = (sum != 0);
|
|
||||||
|
|
||||||
sum = sum * tab + norm_prompt.length;
|
|
||||||
|
|
||||||
if(isComment)
|
|
||||||
{
|
|
||||||
sum -= cmt_prompt.length;
|
|
||||||
while(sum-->0)
|
|
||||||
put(" ");
|
|
||||||
put(cmt_prompt);
|
|
||||||
}
|
|
||||||
else if(isBracket)
|
|
||||||
{
|
|
||||||
sum -= ml_prompt.length;
|
|
||||||
while(sum-->0)
|
|
||||||
put(" ");
|
|
||||||
put(ml_prompt);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
put(norm_prompt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addImports(char[][] str...)
|
|
||||||
{
|
|
||||||
assert(sc !is null);
|
|
||||||
sc.registerImport(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the accumulated output since the last call. Includes
|
|
||||||
// newlines. Will only work if you have not set an output callback
|
|
||||||
// function.
|
|
||||||
char[] output()
|
|
||||||
{
|
|
||||||
assert(!hasCallback);
|
|
||||||
char[] res = outBuf.arrayCopy();
|
|
||||||
outBuf.length = 0;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the command prompt (default is "> ")
|
|
||||||
void setPrompt(char[] prmt)
|
|
||||||
{ norm_prompt = prmt; }
|
|
||||||
|
|
||||||
// Sets the multi-line prompt (default is "... ")
|
|
||||||
void setMLPrompt(char[] prmt)
|
|
||||||
{ ml_prompt = prmt; }
|
|
||||||
|
|
||||||
// Set tab size (default 4)
|
|
||||||
void setTabSize(int i)
|
|
||||||
{ tab = i; }
|
|
||||||
|
|
||||||
// Get input. Will sometimes expect multi-line input, for example if
|
|
||||||
// case a line contains an open brace or an unterminated block
|
|
||||||
// comment. In that case the console will produce another prompt
|
|
||||||
// (when you call prompt()), and also return true.
|
|
||||||
CR input(char[] str)
|
|
||||||
{
|
|
||||||
str = str.strip();
|
|
||||||
if(str == "") return CR.Empty;
|
|
||||||
|
|
||||||
try return runInput(str);
|
|
||||||
catch(MonsterException e)
|
|
||||||
{
|
|
||||||
putln(e.toString);
|
|
||||||
reset();
|
|
||||||
return CR.Error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Statement that handles variable declarations, function calls and
|
|
||||||
// expression statements in consoles. Since these are gramatically
|
|
||||||
// similar, we need the type to determine which it is.
|
|
||||||
class ConsoleStatement : Statement
|
|
||||||
{
|
|
||||||
// Used for variables and expression statements
|
|
||||||
Statement client;
|
|
||||||
|
|
||||||
// Used for function calls
|
|
||||||
FunctionCallExpr func;
|
|
||||||
|
|
||||||
void parseResolve(ref TokenArray toks, Scope sc, bool allowVar)
|
|
||||||
{
|
|
||||||
assert(toks.length != 0);
|
|
||||||
|
|
||||||
// Get the first expression
|
|
||||||
auto first = Expression.identify(toks);
|
|
||||||
|
|
||||||
// And the type right away
|
|
||||||
first.resolve(sc);
|
|
||||||
auto type = first.type;
|
|
||||||
assert(type !is null);
|
|
||||||
|
|
||||||
// Type? If so, it's a variable declaration
|
|
||||||
if((type.isMeta || type.isVar) && allowVar)
|
|
||||||
{
|
|
||||||
if(type.isMeta) type = type.getBase();
|
|
||||||
|
|
||||||
client = new VarDeclStatement(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function?
|
|
||||||
else if(type.isIntFunc)
|
|
||||||
func = new FunctionCallExpr(first, toks, true);
|
|
||||||
|
|
||||||
// It's an expression statement
|
|
||||||
else
|
|
||||||
client = new ExprStatement(first);
|
|
||||||
|
|
||||||
if(client !is null)
|
|
||||||
{
|
|
||||||
client.parse(toks);
|
|
||||||
client.resolve(sc);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(func !is null);
|
|
||||||
func.resolve(sc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override:
|
|
||||||
void parse(ref TokenArray) { assert(0); }
|
|
||||||
void resolve(Scope sc) { assert(0); }
|
|
||||||
|
|
||||||
void compile()
|
|
||||||
{
|
|
||||||
if(client !is null)
|
|
||||||
{
|
|
||||||
client.compile();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(func !is null);
|
|
||||||
func.evalPop();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (frames.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Provides some simple numbers and functions regarding the rendering
|
|
||||||
// frames of the application. It's up to the user to some degree to
|
|
||||||
// provide this information, though. We rely on vm.frame to be called
|
|
||||||
// each frame.
|
|
||||||
module monster.modules.frames;
|
|
||||||
|
|
||||||
import monster.monster;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.idlefunction;
|
|
||||||
import monster.vm.thread;
|
|
||||||
|
|
||||||
const char[] moduleDef =
|
|
||||||
"module frames;
|
|
||||||
|
|
||||||
float time; // Time since last frame
|
|
||||||
float totalTime; // Time since rendering started
|
|
||||||
|
|
||||||
ulong counter; // Number of frames since program startup
|
|
||||||
|
|
||||||
// Sleep a given number of frames
|
|
||||||
idle fsleep(int frameNum);
|
|
||||||
"; //"
|
|
||||||
|
|
||||||
// Keep local copies of these, since we don't want Monster code to
|
|
||||||
// overwrite them (we'll be able to explicitly forbid this later.)
|
|
||||||
ulong frames = 0;
|
|
||||||
float totTime = 0;
|
|
||||||
|
|
||||||
ulong *counter_ptr;
|
|
||||||
float *time_ptr;
|
|
||||||
float *totalTime_ptr;
|
|
||||||
|
|
||||||
// Add the given time and number of frames to the counters
|
|
||||||
void updateFrames(float time, int frmCount = 1)
|
|
||||||
{
|
|
||||||
// Add up to the totals
|
|
||||||
frames += frmCount;
|
|
||||||
totTime += time;
|
|
||||||
|
|
||||||
// Set the Monster variables
|
|
||||||
*counter_ptr = frames;
|
|
||||||
*time_ptr = time;
|
|
||||||
*totalTime_ptr = totTime;
|
|
||||||
|
|
||||||
// TODO: A similar priority queue like we're planning for timer
|
|
||||||
// would also be applicable here. However I'm guessing frameSleep()
|
|
||||||
// will be used a lot less than sleep() though, so this is really
|
|
||||||
// not high up on the priority list.
|
|
||||||
}
|
|
||||||
|
|
||||||
// Idle function that sleeps a given number of frames before
|
|
||||||
// returning.
|
|
||||||
class IdleFrameSleep : IdleFunction
|
|
||||||
{
|
|
||||||
override:
|
|
||||||
IS initiate(Thread* cn)
|
|
||||||
{
|
|
||||||
// Calculate the return frame
|
|
||||||
cn.idleData.l = frames + stack.popInt;
|
|
||||||
|
|
||||||
// Schedule us
|
|
||||||
return IS.Poll;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasFinished(Thread* cn)
|
|
||||||
{
|
|
||||||
// Are we at (or past) the correct frame?
|
|
||||||
return frames >= cn.idleData.l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void initFramesModule()
|
|
||||||
{
|
|
||||||
static MonsterClass mc;
|
|
||||||
if(mc !is null) return;
|
|
||||||
|
|
||||||
mc = vm.loadString(moduleDef, "frames");
|
|
||||||
|
|
||||||
// Bind the idle
|
|
||||||
mc.bind("fsleep", new IdleFrameSleep);
|
|
||||||
|
|
||||||
// Get pointers to the variables so we can write to them easily.
|
|
||||||
auto mo = mc.getSing();
|
|
||||||
counter_ptr = mo.getUlongPtr("counter");
|
|
||||||
time_ptr = mo.getFloatPtr("time");
|
|
||||||
totalTime_ptr = mo.getFloatPtr("totalTime");
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
module frames;
|
|
||||||
|
|
||||||
float time; // Time since last frame
|
|
||||||
float totalTime; // Time since rendering started
|
|
||||||
|
|
||||||
ulong counter; // Number of frames since program startup
|
|
||||||
|
|
||||||
// Sleep a given number of frames
|
|
||||||
idle fsleep(int frameNum);
|
|
@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (io.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// This module provides simple output functions for Monster. The 'i'
|
|
||||||
// (input) part of 'io' isn't really there yet.
|
|
||||||
module monster.modules.io;
|
|
||||||
|
|
||||||
import monster.monster;
|
|
||||||
|
|
||||||
const char[] moduleDef =
|
|
||||||
"module io;
|
|
||||||
|
|
||||||
native write(char[][] args...);
|
|
||||||
native writeln(char[][] args...);
|
|
||||||
native writes(char[][] args...);
|
|
||||||
native writelns(char[][] args...);
|
|
||||||
native print(char[][] args...);
|
|
||||||
"; //"
|
|
||||||
|
|
||||||
// Use tango library functions directly, since flushing the
|
|
||||||
// minibos-versions will give weird effects when mixing with other
|
|
||||||
// tango output.
|
|
||||||
version(Tango)
|
|
||||||
{
|
|
||||||
import tango.io.Stdout;
|
|
||||||
void doWrite(bool space)
|
|
||||||
{
|
|
||||||
AIndex[] args = stack.popAArray();
|
|
||||||
|
|
||||||
char[] form = "{}";
|
|
||||||
if(space) form = "{} ";
|
|
||||||
|
|
||||||
foreach(AIndex ind; args)
|
|
||||||
Stdout.format(form, arrays.getRef(ind).carr);
|
|
||||||
|
|
||||||
Stdout.flush();
|
|
||||||
}
|
|
||||||
void writefln() { Stdout.newline; }
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // Phobos
|
|
||||||
import std.stdio;
|
|
||||||
void doWrite(bool space)
|
|
||||||
{
|
|
||||||
AIndex[] args = stack.popAArray();
|
|
||||||
|
|
||||||
char[] form = "%s";
|
|
||||||
if(space) form = "%s ";
|
|
||||||
|
|
||||||
foreach(AIndex ind; args)
|
|
||||||
writef(form, arrays.getRef(ind).carr);
|
|
||||||
|
|
||||||
fflush(stdout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void initIOModule()
|
|
||||||
{
|
|
||||||
static MonsterClass mc;
|
|
||||||
if(mc !is null) return;
|
|
||||||
|
|
||||||
mc = vm.loadString(moduleDef, "io");
|
|
||||||
|
|
||||||
mc.bind("write", { doWrite(false); });
|
|
||||||
mc.bind("writeln", { doWrite(false); writefln(); });
|
|
||||||
mc.bind("writes", { doWrite(true); });
|
|
||||||
mc.bind("writelns", { doWrite(true); writefln(); });
|
|
||||||
|
|
||||||
// Print is just another name for writelns
|
|
||||||
mc.bind("print", { doWrite(true); writefln(); });
|
|
||||||
}
|
|
@ -1,17 +0,0 @@
|
|||||||
/*
|
|
||||||
NOTE: This file is not used - it is here just for reference. The
|
|
||||||
real module is defined internally in io.d.
|
|
||||||
*/
|
|
||||||
|
|
||||||
module io;
|
|
||||||
|
|
||||||
// Write to console, with or without a newline
|
|
||||||
native write(char[][] args...);
|
|
||||||
native writeln(char[][] args...);
|
|
||||||
|
|
||||||
// Automatically inserts spaces between arguments
|
|
||||||
native writes(char[][] args...);
|
|
||||||
native writelns(char[][] args...);
|
|
||||||
|
|
||||||
// Identical to writelns
|
|
||||||
native print(char[][] args...);
|
|
@ -1,166 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (math.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
// Simple math functions
|
|
||||||
module monster.modules.math;
|
|
||||||
|
|
||||||
import monster.monster;
|
|
||||||
import std.math;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
|
|
||||||
const char[] moduleDef =
|
|
||||||
"module math;
|
|
||||||
|
|
||||||
double E = 2.7182818284590452354;
|
|
||||||
double PI = 3.141592653589793238;
|
|
||||||
double DEGTORAD = 3.141592653590/180;
|
|
||||||
double RADTODEG = 180/3.141592653590;
|
|
||||||
|
|
||||||
native double sin(double x);
|
|
||||||
native double cos(double x);
|
|
||||||
native double tan(double x);
|
|
||||||
native double asin(double x);
|
|
||||||
native double acos(double x);
|
|
||||||
native double atan(double x);
|
|
||||||
native double atan2(double y, double x); // = atan(y/x)
|
|
||||||
native double sinh(double x);
|
|
||||||
native double cosh(double x);
|
|
||||||
native double tanh(double x);
|
|
||||||
native double asinh(double x);
|
|
||||||
native double acosh(double x);
|
|
||||||
native double atanh(double x);
|
|
||||||
|
|
||||||
native double sqrt(double x);
|
|
||||||
native double exp(double x); // e^x
|
|
||||||
native double exp2(double x); // 2^x
|
|
||||||
native double log(double x); // base e
|
|
||||||
native double log10(double x);// base 10
|
|
||||||
native double log2(double x); // base 2
|
|
||||||
|
|
||||||
native double pow(double x, double y); // x^y
|
|
||||||
native double ipow(double x, int n); // x^n (faster than pow)
|
|
||||||
|
|
||||||
native int abs(int x);
|
|
||||||
native double fabs(double x);
|
|
||||||
native double ceil(double x);
|
|
||||||
native double floor(double x);
|
|
||||||
native double round(double x); // rounds to nearest integer
|
|
||||||
native double trunc(double x);
|
|
||||||
|
|
||||||
native double hypot(double x, double y); // = sqrt(x*x+y*y)
|
|
||||||
native double cbrt(double x); // cube root
|
|
||||||
|
|
||||||
// Calculates polynomial a0 + x*a1 + x^2*a2 + x^3*a3 + ...
|
|
||||||
native double poly(double x, float[] A);
|
|
||||||
"; //"
|
|
||||||
|
|
||||||
version(Tango)
|
|
||||||
{
|
|
||||||
double fabs(double x) { return abs(x); }
|
|
||||||
}
|
|
||||||
|
|
||||||
void initMathModule()
|
|
||||||
{
|
|
||||||
static MonsterClass mc;
|
|
||||||
if(mc !is null) return;
|
|
||||||
|
|
||||||
mc = vm.loadString(moduleDef, "math");
|
|
||||||
|
|
||||||
mc.bind("sin", { stack.pushDouble(sin(stack.popDouble)); });
|
|
||||||
mc.bind("cos", { stack.pushDouble(cos(stack.popDouble)); });
|
|
||||||
mc.bind("tan", { stack.pushDouble(tan(stack.popDouble)); });
|
|
||||||
mc.bind("asin", { stack.pushDouble(asin(stack.popDouble)); });
|
|
||||||
mc.bind("acos", { stack.pushDouble(acos(stack.popDouble)); });
|
|
||||||
mc.bind("atan", { stack.pushDouble(atan(stack.popDouble)); });
|
|
||||||
|
|
||||||
mc.bind("sinh", { stack.pushDouble(sinh(stack.popDouble)); });
|
|
||||||
mc.bind("cosh", { stack.pushDouble(cosh(stack.popDouble)); });
|
|
||||||
mc.bind("tanh", { stack.pushDouble(tanh(stack.popDouble)); });
|
|
||||||
mc.bind("asinh", { stack.pushDouble(asinh(stack.popDouble)); });
|
|
||||||
mc.bind("acosh", { stack.pushDouble(acosh(stack.popDouble)); });
|
|
||||||
mc.bind("atanh", { stack.pushDouble(atanh(stack.popDouble)); });
|
|
||||||
|
|
||||||
mc.bind("atan2",
|
|
||||||
{
|
|
||||||
// Remember to pop the variables in the reverse order
|
|
||||||
auto x = stack.popDouble;
|
|
||||||
stack.pushDouble(atan2(stack.popDouble, x));
|
|
||||||
});
|
|
||||||
|
|
||||||
mc.bind("sqrt", { stack.pushDouble(sqrt(stack.popDouble)); });
|
|
||||||
mc.bind("exp", { stack.pushDouble(exp(stack.popDouble)); });
|
|
||||||
mc.bind("exp2", { stack.pushDouble(exp2(stack.popDouble)); });
|
|
||||||
mc.bind("log", { stack.pushDouble(log(stack.popDouble)); });
|
|
||||||
mc.bind("log2", { stack.pushDouble(log2(stack.popDouble)); });
|
|
||||||
mc.bind("log10", { stack.pushDouble(log10(stack.popDouble)); });
|
|
||||||
|
|
||||||
mc.bind("pow",
|
|
||||||
{
|
|
||||||
auto x = stack.popDouble;
|
|
||||||
stack.pushDouble(pow(stack.popDouble, x));
|
|
||||||
});
|
|
||||||
mc.bind("ipow",
|
|
||||||
{
|
|
||||||
auto x = stack.popInt;
|
|
||||||
stack.pushDouble(pow(cast(real)stack.popDouble, x));
|
|
||||||
});
|
|
||||||
|
|
||||||
mc.bind("abs", { stack.pushDouble(abs(stack.popDouble)); });
|
|
||||||
mc.bind("fabs", { stack.pushDouble(fabs(stack.popDouble)); });
|
|
||||||
mc.bind("ceil", { stack.pushDouble(ceil(stack.popDouble)); });
|
|
||||||
mc.bind("floor", { stack.pushDouble(floor(stack.popDouble)); });
|
|
||||||
mc.bind("round", { stack.pushDouble(round(stack.popDouble)); });
|
|
||||||
mc.bind("trunc", { stack.pushDouble(trunc(stack.popDouble)); });
|
|
||||||
|
|
||||||
// Order doesn't matter here
|
|
||||||
mc.bind("hypot", { stack.pushDouble(hypot(stack.popDouble,
|
|
||||||
stack.popDouble)); });
|
|
||||||
|
|
||||||
mc.bind("cbrt", { stack.pushDouble(cbrt(stack.popDouble)); });
|
|
||||||
|
|
||||||
mc.bind("poly", &npoly);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Implement this ourselves, since phobos doesn't use the types we
|
|
||||||
// want
|
|
||||||
double poly(double x, float A[])
|
|
||||||
{
|
|
||||||
// Use 'real' internally for higher precision
|
|
||||||
real r = A[$-1];
|
|
||||||
foreach_reverse(c; A[0..$-1])
|
|
||||||
{
|
|
||||||
r *= x;
|
|
||||||
r += c;
|
|
||||||
}
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
// double poly(double x, float[] A);
|
|
||||||
void npoly()
|
|
||||||
{
|
|
||||||
auto arf = stack.popArray();
|
|
||||||
assert(arf.elemSize == 1);
|
|
||||||
stack.pushDouble(poly(stack.popDouble, arf.farr));
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
module math;
|
|
||||||
|
|
||||||
double E = 2.7182818284590452354;
|
|
||||||
double PI = 3.141592653589793238;
|
|
||||||
double DEGTORAD = 3.141592653590/180;
|
|
||||||
double RADTODEG = 180/3.141592653590;
|
|
||||||
|
|
||||||
native double sin(double x);
|
|
||||||
native double cos(double x);
|
|
||||||
native double tan(double x);
|
|
||||||
native double asin(double x);
|
|
||||||
native double acos(double x);
|
|
||||||
native double atan(double x);
|
|
||||||
native double atan2(double y, double x); // = atan(y/x)
|
|
||||||
native double sinh(double x);
|
|
||||||
native double cosh(double x);
|
|
||||||
native double tanh(double x);
|
|
||||||
native double asinh(double x);
|
|
||||||
native double acosh(double x);
|
|
||||||
native double atanh(double x);
|
|
||||||
|
|
||||||
native double sqrt(double x);
|
|
||||||
native double exp(double x); // e^x
|
|
||||||
native double exp2(double x); // 2^x
|
|
||||||
native double log(double x); // base e
|
|
||||||
native double log10(double x);// base 10
|
|
||||||
native double log2(double x); // base 2
|
|
||||||
|
|
||||||
native double pow(double x, double y); // x^y
|
|
||||||
native double ipow(double x, int n); // x^n (faster than pow)
|
|
||||||
|
|
||||||
native int abs(int x);
|
|
||||||
native double fabs(double x);
|
|
||||||
native double ceil(double x);
|
|
||||||
native double floor(double x);
|
|
||||||
native double round(double x); // rounds to nearest integer
|
|
||||||
native double trunc(double x);
|
|
||||||
|
|
||||||
native double hypot(double x, double y); // = sqrt(x*x+y*y)
|
|
||||||
native double cbrt(double x); // cube root
|
|
||||||
|
|
||||||
// Calculates polynomial a0 + x*a1 + x^2*a2 + x^3*a3 + ...
|
|
||||||
native double poly(double x, float[] A);
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (random.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This module provides simple random number generation. Since this is
|
|
||||||
// intended for game development, speed and simplicity is favored over
|
|
||||||
// flexibility and random number quality.
|
|
||||||
module monster.modules.random;
|
|
||||||
|
|
||||||
import monster.monster;
|
|
||||||
import std.random;
|
|
||||||
|
|
||||||
const char[] moduleDef =
|
|
||||||
"module random;
|
|
||||||
|
|
||||||
native uint rand(); // Return a number between 0 and uint.max, inclusive
|
|
||||||
native float frand(); // Return a number between 0 and 1, inclusive
|
|
||||||
|
|
||||||
// Return a random number between a and b, inclusive. Allows negative
|
|
||||||
// numbers, and works with a>b, a<b and a==b
|
|
||||||
native int randInt(int a, int b);
|
|
||||||
"; //"
|
|
||||||
|
|
||||||
const float _frandFactor = 1.0/uint.max;
|
|
||||||
|
|
||||||
// Return a random integer between a and b, inclusive.
|
|
||||||
int randInt(int a, int b)
|
|
||||||
out(result)
|
|
||||||
{
|
|
||||||
// Result must be in range m <= result <= M, where m=min(a,b) and M=max(a,b)
|
|
||||||
if(b >= a) assert( (a <= result) && (result <= b) );
|
|
||||||
else if(a > b) assert( (b <= result) && (result <= a) );
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
if(a>b) return cast(int)(rand() % (a-b+1)) + b;
|
|
||||||
else if(b>a) return cast(int)(rand() % (b-a+1)) + a;
|
|
||||||
else return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
void initRandomModule()
|
|
||||||
{
|
|
||||||
static MonsterClass mc;
|
|
||||||
if(mc !is null) return;
|
|
||||||
|
|
||||||
mc = vm.loadString(moduleDef, "random");
|
|
||||||
|
|
||||||
mc.bind("rand", { stack.pushInt(rand()); });
|
|
||||||
mc.bind("frand", { stack.pushFloat(rand()*_frandFactor); });
|
|
||||||
mc.bind("randInt", { stack.pushInt(randInt(stack.popInt,
|
|
||||||
stack.popInt)); });
|
|
||||||
}
|
|
@ -1,8 +0,0 @@
|
|||||||
module random;
|
|
||||||
|
|
||||||
native uint rand(); // Return a number between 0 and uint.max, inclusive
|
|
||||||
native float frand(); // Return a number between 0 and 1, inclusive
|
|
||||||
|
|
||||||
// Return a random number between a and b, inclusive. Allows negative
|
|
||||||
// numbers, and works with a>b, a<b and a==b
|
|
||||||
native int randInt(int a, int b);
|
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
NOTE: This file is not used - it is here just for reference. The
|
|
||||||
real module is defined internally in io.d.
|
|
||||||
*/
|
|
||||||
|
|
||||||
singleton thread;
|
|
||||||
|
|
||||||
// Used to kill or pause our own or other threads.
|
|
||||||
idle kill();
|
|
||||||
idle pause();
|
|
||||||
|
|
||||||
// Get status information about a thread
|
|
||||||
native bool isScheduled();
|
|
||||||
native bool isPaused();
|
|
||||||
native bool isIdle();
|
|
||||||
native bool isDead();
|
|
||||||
bool isAlive() { return !isDead(); }
|
|
||||||
|
|
||||||
// Create a new (paused) thread for a given function
|
|
||||||
native thread create(function() f);
|
|
||||||
|
|
||||||
// Schedule a (paused) thread to run the next frame
|
|
||||||
native restart();
|
|
||||||
|
|
||||||
// Call a (paused) thread directly - returns when the thread exits or
|
|
||||||
// calls an idle function.
|
|
||||||
idle call();
|
|
||||||
|
|
||||||
// Wait for a thread to finish. Will not return until the thread is
|
|
||||||
// dead.
|
|
||||||
idle wait();
|
|
||||||
|
|
||||||
// Start a function as a thread in the background
|
|
||||||
thread start(function() f)
|
|
||||||
{
|
|
||||||
var t = create(f);
|
|
||||||
t.restart();
|
|
||||||
return t;
|
|
||||||
}
|
|
@ -1,279 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (threads.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This module provides an interface to the virtual threading API in
|
|
||||||
// Monster.
|
|
||||||
|
|
||||||
module monster.modules.threads;
|
|
||||||
|
|
||||||
import monster.monster;
|
|
||||||
import monster.vm.mobject;
|
|
||||||
import monster.vm.idlefunction;
|
|
||||||
import monster.vm.thread;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.compiler.functions;
|
|
||||||
import std.stdio;
|
|
||||||
|
|
||||||
const char[] moduleDef =
|
|
||||||
"singleton thread;
|
|
||||||
|
|
||||||
// Used to kill or pause our own or other threads.
|
|
||||||
idle kill();
|
|
||||||
idle pause();
|
|
||||||
|
|
||||||
// Get status information about a thread
|
|
||||||
native bool isScheduled();
|
|
||||||
native bool isPaused();
|
|
||||||
native bool isIdle();
|
|
||||||
native bool isDead();
|
|
||||||
bool isAlive() { return !isDead(); }
|
|
||||||
|
|
||||||
// Create a new (paused) thread for a given function
|
|
||||||
native thread create(function() f);
|
|
||||||
|
|
||||||
// Schedule a (paused) thread to run the next frame
|
|
||||||
native restart();
|
|
||||||
|
|
||||||
// Call a (paused) thread directly - returns when the thread exits or
|
|
||||||
// calls an idle function.
|
|
||||||
idle call();
|
|
||||||
|
|
||||||
// Wait for a thread to finish. Will not return until the thread is
|
|
||||||
// dead.
|
|
||||||
idle wait();
|
|
||||||
|
|
||||||
// Start a function as a thread in the background
|
|
||||||
thread start(function() f)
|
|
||||||
{
|
|
||||||
var t = create(f);
|
|
||||||
t.restart();
|
|
||||||
return t;
|
|
||||||
}"; //"
|
|
||||||
|
|
||||||
/*
|
|
||||||
The char[] name stuff above will of course be replaced with real
|
|
||||||
function pointers once those are done. When closures are done we
|
|
||||||
will also add:
|
|
||||||
|
|
||||||
function() wrap(function() f)
|
|
||||||
{
|
|
||||||
var t = create(f);
|
|
||||||
return { t.call(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
MonsterObject *trdSing;
|
|
||||||
|
|
||||||
class Kill : IdleFunction
|
|
||||||
{
|
|
||||||
IS initiate(Thread *t)
|
|
||||||
{
|
|
||||||
auto mo = params.obj;
|
|
||||||
|
|
||||||
if(mo !is trdSing)
|
|
||||||
{
|
|
||||||
// Check if this is another thread
|
|
||||||
auto trd = getOwner(mo);
|
|
||||||
if(trd !is t)
|
|
||||||
{
|
|
||||||
// It is. Kill it explicitly and return.
|
|
||||||
trd.kill();
|
|
||||||
return IS.Return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it's our own thread, tell the scheduler to kill it from
|
|
||||||
// the inside.
|
|
||||||
return IS.Kill;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Pause : IdleFunction
|
|
||||||
{
|
|
||||||
IS initiate(Thread *t)
|
|
||||||
{
|
|
||||||
auto mo = params.obj;
|
|
||||||
|
|
||||||
// Can only run on the singleton object
|
|
||||||
if(mo !is trdSing)
|
|
||||||
fail("Can only pause our own thread");
|
|
||||||
|
|
||||||
// Move the thread to the 'paused' list
|
|
||||||
t.moveTo(&scheduler.paused);
|
|
||||||
|
|
||||||
return IS.Manual;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread *getOwner() { return getOwner(params.obj); }
|
|
||||||
Thread *getOwner(MonsterObject *mo)
|
|
||||||
{
|
|
||||||
assert(mo !is null);
|
|
||||||
if(mo is trdSing)
|
|
||||||
fail("Cannot run this function on the global singleton thread.");
|
|
||||||
auto trd = cast(Thread*) mo.getExtra(_threadClass).vptr;
|
|
||||||
assert(trd !is null);
|
|
||||||
assert(!trd.isRunning || !trd.isPaused,
|
|
||||||
"thread cannot be running and paused at the same time");
|
|
||||||
return trd;
|
|
||||||
}
|
|
||||||
|
|
||||||
MonsterObject *createObj(Thread *trd)
|
|
||||||
{
|
|
||||||
assert(trd !is null);
|
|
||||||
auto mo = _threadClass.createObject();
|
|
||||||
mo.getExtra(_threadClass).vptr = trd;
|
|
||||||
return mo;
|
|
||||||
}
|
|
||||||
|
|
||||||
void create()
|
|
||||||
{
|
|
||||||
// Can only run on the singleton object
|
|
||||||
if(params.obj !is trdSing)
|
|
||||||
fail("Can only use create() on the global thread object.");
|
|
||||||
|
|
||||||
auto fn = stack.popFuncRef();
|
|
||||||
auto trd = fn.getObject().thread(fn.getFunction());
|
|
||||||
|
|
||||||
stack.pushObject(createObj(trd));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call is used to restore a thread that was previously paused. It
|
|
||||||
// will enter the thread immediately, like a normal function call, but
|
|
||||||
// it will still run in its own thread. If you only wish to schedule
|
|
||||||
// it for later, use restart instead.
|
|
||||||
class Call : IdleFunction
|
|
||||||
{
|
|
||||||
override:
|
|
||||||
IS initiate(Thread *t)
|
|
||||||
{
|
|
||||||
if(params.obj is trdSing)
|
|
||||||
fail("Cannot use call() on our own thread.");
|
|
||||||
|
|
||||||
// Get the thread we're resuming
|
|
||||||
auto trd = getOwner();
|
|
||||||
|
|
||||||
if(trd is t)
|
|
||||||
fail("Cannot use call() on our own thread.");
|
|
||||||
|
|
||||||
if(trd.isDead)
|
|
||||||
fail("Cannot call a dead thread.");
|
|
||||||
|
|
||||||
if(!trd.isPaused)
|
|
||||||
fail("Can only use call() on paused threads");
|
|
||||||
|
|
||||||
// Background the current thread. Move it to the pause list
|
|
||||||
// first, so background doesn't inadvertently delete it.
|
|
||||||
t.moveTo(&scheduler.paused);
|
|
||||||
t.background();
|
|
||||||
assert(!t.isDead);
|
|
||||||
|
|
||||||
// Reenter the thread
|
|
||||||
trd.reenter();
|
|
||||||
assert(cthread is null);
|
|
||||||
|
|
||||||
// Put the old thread in the forground again
|
|
||||||
t.foreground();
|
|
||||||
|
|
||||||
// Make the thread transient again
|
|
||||||
t.moveTo(&scheduler.transient);
|
|
||||||
|
|
||||||
// Return to sender
|
|
||||||
return IS.Return;
|
|
||||||
}
|
|
||||||
|
|
||||||
void abort(Thread *t)
|
|
||||||
{
|
|
||||||
fail("Cannot abort thread while it is calling another thread");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class Wait : IdleFunction
|
|
||||||
{
|
|
||||||
override:
|
|
||||||
IS initiate(Thread *t)
|
|
||||||
{
|
|
||||||
if(params.obj is trdSing)
|
|
||||||
fail("Cannot use wait on our own thread.");
|
|
||||||
|
|
||||||
// Get the thread we're resuming
|
|
||||||
auto trd = getOwner();
|
|
||||||
|
|
||||||
if(trd is t)
|
|
||||||
fail("Cannot use wait on our own thread.");
|
|
||||||
|
|
||||||
// Return immediately if the thread is dead
|
|
||||||
if(trd.isDead)
|
|
||||||
return IS.Return;
|
|
||||||
|
|
||||||
t.idleData.vptr = trd;
|
|
||||||
|
|
||||||
return IS.Poll;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasFinished(Thread *t)
|
|
||||||
{
|
|
||||||
return (cast(Thread*)t.idleData.vptr).isDead;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void restart()
|
|
||||||
{ getOwner().restart(); }
|
|
||||||
|
|
||||||
void isDead()
|
|
||||||
{ stack.pushBool(getOwner().isDead); }
|
|
||||||
|
|
||||||
void isIdle()
|
|
||||||
{ stack.pushBool(getOwner().fstack.isIdle); }
|
|
||||||
|
|
||||||
void isPaused()
|
|
||||||
{ stack.pushBool(getOwner().isPaused); }
|
|
||||||
|
|
||||||
void isScheduled()
|
|
||||||
{ stack.pushBool(getOwner().isScheduled); }
|
|
||||||
|
|
||||||
MonsterClass _threadClass;
|
|
||||||
|
|
||||||
void initThreadModule()
|
|
||||||
{
|
|
||||||
if(_threadClass !is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_threadClass = vm.loadString(moduleDef, "thread");
|
|
||||||
trdSing = _threadClass.getSing();
|
|
||||||
|
|
||||||
_threadClass.bind("kill", new Kill);
|
|
||||||
_threadClass.bind("call", new Call);
|
|
||||||
_threadClass.bind("pause", new Pause);
|
|
||||||
_threadClass.bind("wait", new Wait);
|
|
||||||
|
|
||||||
_threadClass.bind("create", &create);
|
|
||||||
_threadClass.bind("restart", &restart);
|
|
||||||
|
|
||||||
_threadClass.bind("isDead", &isDead);
|
|
||||||
_threadClass.bind("isIdle", &isIdle);
|
|
||||||
_threadClass.bind("isPaused", &isPaused);
|
|
||||||
_threadClass.bind("isScheduled", &isScheduled);
|
|
||||||
}
|
|
@ -1,190 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (timer.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This module contains (or will contain) various routines for
|
|
||||||
// timing. It is also home of the ubiquitous "sleep" idle function.
|
|
||||||
|
|
||||||
module monster.modules.timer;
|
|
||||||
|
|
||||||
import std.stdio;
|
|
||||||
|
|
||||||
// For some utterly idiotic reason, DMD's public imports will suddenly
|
|
||||||
// stop working from time to time.
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.mobject;
|
|
||||||
import monster.vm.stack;
|
|
||||||
import monster.vm.thread;
|
|
||||||
import monster.vm.idlefunction;
|
|
||||||
|
|
||||||
import monster.monster;
|
|
||||||
import monster.options;
|
|
||||||
|
|
||||||
const char[] moduleDef =
|
|
||||||
"singleton timer;
|
|
||||||
idle sleep(float secs);
|
|
||||||
"; //"
|
|
||||||
|
|
||||||
static if(timer_useClock)
|
|
||||||
{
|
|
||||||
// Sleep a given amount of time. This implementation uses the system
|
|
||||||
// clock.
|
|
||||||
import std.date;
|
|
||||||
class IdleSleep_SystemClock : IdleFunction
|
|
||||||
{
|
|
||||||
override:
|
|
||||||
IS initiate(Thread* cn)
|
|
||||||
{
|
|
||||||
// Get the parameter
|
|
||||||
float secs = stack.popFloat;
|
|
||||||
|
|
||||||
// Get current time
|
|
||||||
cn.idleData.l = getUTCtime();
|
|
||||||
|
|
||||||
// Calculate when we should return
|
|
||||||
cn.idleData.l += secs*TicksPerSecond;
|
|
||||||
|
|
||||||
// Schedule us
|
|
||||||
return IS.Poll;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasFinished(Thread* cn)
|
|
||||||
{
|
|
||||||
// Is it time?
|
|
||||||
return getUTCtime() >= cn.idleData.l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
} else { // If timer_useClock is NOT set:
|
|
||||||
|
|
||||||
// This implementation uses a user-driven timer instead of the system
|
|
||||||
// clock. It's more efficient, but requires the user to update the
|
|
||||||
// given timer manually each frame. The default sleep (timer.sleep) is
|
|
||||||
// bound to the default timer, but it's possible to create multiple
|
|
||||||
// independent timers.
|
|
||||||
class IdleSleep_Timer : IdleFunction
|
|
||||||
{
|
|
||||||
override:
|
|
||||||
IS initiate(Thread* cn)
|
|
||||||
{
|
|
||||||
// The timer is stored in the object's 'extra' pointer
|
|
||||||
auto t = cast(SleepManager)cn.extraData.obj;
|
|
||||||
assert(t !is null);
|
|
||||||
|
|
||||||
// Calculate the return time
|
|
||||||
cn.idleData.l = t.current + cast(long)(t.tickSize*stack.popFloat);
|
|
||||||
|
|
||||||
// Schedule us
|
|
||||||
return IS.Poll;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasFinished(Thread* cn)
|
|
||||||
{
|
|
||||||
// Get the timer
|
|
||||||
auto t = cast(SleepManager)cn.extraData.obj;
|
|
||||||
assert(t !is null);
|
|
||||||
|
|
||||||
// Is it time?
|
|
||||||
return t.current >= cn.idleData.l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A manually updated timer. This can be improved quite a lot: Most
|
|
||||||
// sleep operations (depending on application of course) will skip
|
|
||||||
// many frames before they return. For example, for sleep(0.5) at 100
|
|
||||||
// fps, hasFinished will return false approximately 50 times before
|
|
||||||
// returning true. For bigger sleep values and a large number of
|
|
||||||
// objects, the impact of this is significant. A good solution would
|
|
||||||
// be to pool scheduled objects together and only perform one check on
|
|
||||||
// the entire pool. If the pool is due, all the nodes within it are
|
|
||||||
// inserted into the scheduler for detailed checking. We could have a
|
|
||||||
// series of such pools, ordered by expiration time, so that we only
|
|
||||||
// ever need to check the first pool in the list. The optimal pool
|
|
||||||
// interval, number of pools etc depends on the application and the
|
|
||||||
// fps - but it should be possible to find some reasonable defaults. A
|
|
||||||
// more generalized priority queue implementation is also possible.
|
|
||||||
class SleepManager
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
// Instance of the timer class that is associated with this timer
|
|
||||||
MonsterObject *tobj;
|
|
||||||
|
|
||||||
// Current tick count
|
|
||||||
long current;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Specify a Monster object to associate with this timer. Use 'null'
|
|
||||||
// if you don't need an object.
|
|
||||||
this(MonsterObject *obj)
|
|
||||||
{
|
|
||||||
if(obj is null) return;
|
|
||||||
|
|
||||||
tobj = obj;
|
|
||||||
tobj.getExtra(_timerClass).obj = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// By default, create a new object
|
|
||||||
this()
|
|
||||||
{ this(_timerClass.createObject); }
|
|
||||||
|
|
||||||
// Number of 'ticks' per second
|
|
||||||
static const long tickSize = 1000;
|
|
||||||
|
|
||||||
// Reset the timer to zero
|
|
||||||
void reset() { current = 0; }
|
|
||||||
|
|
||||||
// Return the total number of elapsed seconds since start (or last
|
|
||||||
// reset)
|
|
||||||
double read() { return current/cast(double)tickSize; }
|
|
||||||
|
|
||||||
// Add time to the timer.
|
|
||||||
void add(double d) { current += cast(long)(tickSize*d); }
|
|
||||||
void addl(long l) { current += l; }
|
|
||||||
|
|
||||||
MonsterObject *getObj() { return tobj; }
|
|
||||||
}
|
|
||||||
|
|
||||||
SleepManager idleTime;
|
|
||||||
}
|
|
||||||
|
|
||||||
MonsterClass _timerClass;
|
|
||||||
|
|
||||||
void initTimerModule()
|
|
||||||
{
|
|
||||||
if(_timerClass !is null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_timerClass = vm.loadString(moduleDef, "timer");
|
|
||||||
|
|
||||||
static if(timer_useClock)
|
|
||||||
{
|
|
||||||
_timerClass.bind("sleep", new IdleSleep_SystemClock);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(idleTime is null);
|
|
||||||
idleTime = new SleepManager(_timerClass.getSing());
|
|
||||||
_timerClass.bind("sleep", new IdleSleep_Timer);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
/*
|
|
||||||
NOTE: This file is not used - it is here just for reference. The
|
|
||||||
real module is defined internally in timer.d.
|
|
||||||
*/
|
|
||||||
|
|
||||||
singleton timer;
|
|
||||||
|
|
||||||
// Sleep the given number of seconds
|
|
||||||
idle sleep(float secs);
|
|
@ -1,183 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (vfs.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.modules.vfs;
|
|
||||||
|
|
||||||
import std.file;
|
|
||||||
import std.stream;
|
|
||||||
import std.string;
|
|
||||||
import monster.util.string;
|
|
||||||
import monster.vm.error;
|
|
||||||
|
|
||||||
abstract class VFS
|
|
||||||
{
|
|
||||||
// Abstract functions. These must be implemented in child classes.
|
|
||||||
|
|
||||||
// Return true if a file exists. Should not return true for
|
|
||||||
// directories.
|
|
||||||
abstract bool has(char[] file);
|
|
||||||
abstract bool hasDir(char[] dir);
|
|
||||||
|
|
||||||
// Open the given file and return it as a stream.
|
|
||||||
abstract Stream open(char[] file);
|
|
||||||
|
|
||||||
// Check for invalid file names. This makes sure the caller cannot
|
|
||||||
// read files outside the designated subdirectory.
|
|
||||||
final static void checkForEscape(char[] file)
|
|
||||||
{
|
|
||||||
if(file.begins("/") || file.begins("\\"))
|
|
||||||
fail("Filename " ~ file ~ " cannot begin with a path separator");
|
|
||||||
if(file.find(":") != -1)
|
|
||||||
fail("Filename " ~ file ~ " cannot contain colons");
|
|
||||||
if(file.find("..") != -1)
|
|
||||||
fail("Filename " ~ file ~ " cannot contain '..'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A VFS that contains a list of other VFS objects
|
|
||||||
class ListVFS : VFS
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
VFS[] list;
|
|
||||||
|
|
||||||
public:
|
|
||||||
this(VFS v[] ...)
|
|
||||||
{ list = v; }
|
|
||||||
|
|
||||||
void add(VFS v[] ...)
|
|
||||||
{ list ~= v; }
|
|
||||||
|
|
||||||
void addFirst(VFS v[] ...)
|
|
||||||
{ list = v ~ list; }
|
|
||||||
|
|
||||||
bool has(char[] file)
|
|
||||||
{
|
|
||||||
foreach(l; list)
|
|
||||||
if(l.has(file)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasDir(char[] file)
|
|
||||||
{
|
|
||||||
foreach(l; list)
|
|
||||||
if(l.hasDir(file)) return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream open(char[] file)
|
|
||||||
{
|
|
||||||
foreach(l; list)
|
|
||||||
if(l.has(file)) return l.open(file);
|
|
||||||
fail("No member VFS contains file " ~ file);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// A VFS that reads files from a given path in the OS file
|
|
||||||
// system. Disallows filenames that escape the given path,
|
|
||||||
// ie. filenames such as:
|
|
||||||
//
|
|
||||||
// /etc/passwd
|
|
||||||
// dir/../../file
|
|
||||||
// c:\somefile
|
|
||||||
class FileVFS : VFS
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
char[] sysPath;
|
|
||||||
char[] buffer;
|
|
||||||
|
|
||||||
char[] getPath(char[] file)
|
|
||||||
{
|
|
||||||
// Make sure the buffer is large enough
|
|
||||||
if(buffer.length < file.length+sysPath.length)
|
|
||||||
buffer.length = file.length + sysPath.length + 50;
|
|
||||||
|
|
||||||
checkForEscape(file);
|
|
||||||
|
|
||||||
// Copy the file name over
|
|
||||||
buffer[sysPath.length .. sysPath.length+file.length]
|
|
||||||
= file[];
|
|
||||||
|
|
||||||
// Convert the path characters
|
|
||||||
convPath();
|
|
||||||
|
|
||||||
// Return the result
|
|
||||||
return buffer[0..sysPath.length+file.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert path separators
|
|
||||||
void convPath()
|
|
||||||
{
|
|
||||||
foreach(ref c; buffer)
|
|
||||||
if(c == from)
|
|
||||||
c = to;
|
|
||||||
}
|
|
||||||
|
|
||||||
version(Windows)
|
|
||||||
{
|
|
||||||
const char from = '/';
|
|
||||||
const char to = '\\';
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
const char from = '\\';
|
|
||||||
const char to = '/';
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
this(char[] path = "")
|
|
||||||
{
|
|
||||||
// Set up the initial buffer
|
|
||||||
buffer.length = path.length + 50;
|
|
||||||
|
|
||||||
if(path.length)
|
|
||||||
{
|
|
||||||
// Slice the beginning of it and copy the path over
|
|
||||||
sysPath = buffer[0..path.length];
|
|
||||||
sysPath[] = path[];
|
|
||||||
}
|
|
||||||
|
|
||||||
convPath();
|
|
||||||
|
|
||||||
// Make sure the last char in the path is a path separator
|
|
||||||
if(!path.ends([to]))
|
|
||||||
{
|
|
||||||
sysPath = buffer[0..path.length+1];
|
|
||||||
sysPath[$-1] = to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has(char[] file)
|
|
||||||
{
|
|
||||||
char[] pt = getPath(file);
|
|
||||||
return exists(pt) && isfile(pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasDir(char[] file)
|
|
||||||
{
|
|
||||||
char[] pt = getPath(file);
|
|
||||||
return exists(pt) && isdir(pt);
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream open(char[] file)
|
|
||||||
{ return new BufferedFile(getPath(file)); }
|
|
||||||
}
|
|
@ -1,42 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (monster.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.monster;
|
|
||||||
|
|
||||||
public
|
|
||||||
{
|
|
||||||
// These should contain all you need for normal usage.
|
|
||||||
import monster.vm.mobject;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.stack;
|
|
||||||
import monster.vm.vm;
|
|
||||||
import monster.vm.thread;
|
|
||||||
import monster.vm.idlefunction;
|
|
||||||
import monster.vm.arrays;
|
|
||||||
import monster.vm.params;
|
|
||||||
import monster.vm.error;
|
|
||||||
}
|
|
||||||
|
|
||||||
version(LittleEndian) {}
|
|
||||||
else static assert(0, "This library does not yet support big endian systems.");
|
|
@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (options.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.options;
|
|
||||||
|
|
||||||
/*
|
|
||||||
The purpose of this file is to set compile time options for the
|
|
||||||
Monster library - including compiler, VM and modules. This allows
|
|
||||||
the user to customize the language in various ways to fit each
|
|
||||||
project individually.
|
|
||||||
|
|
||||||
For changes to take effect, you must recompile and reinstall the
|
|
||||||
library.
|
|
||||||
|
|
||||||
If you have suggestions for additional options and ways to customize
|
|
||||||
the language, let us know!
|
|
||||||
*/
|
|
||||||
|
|
||||||
static:
|
|
||||||
const:
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Language options
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// Set to false to make the entire language case insensitive. Affects
|
|
||||||
// all identifier and keyword matching. (Not implemented yet!)
|
|
||||||
bool caseSensitive = true;
|
|
||||||
|
|
||||||
// Include the case-insensitive string (and character) operators =i=,
|
|
||||||
// =I=, !=i= and !=I=.
|
|
||||||
bool ciStringOps = true;
|
|
||||||
|
|
||||||
// Skip lines beginning with a hash character '#'
|
|
||||||
bool skipHashes = true;
|
|
||||||
|
|
||||||
// Do we allow implicit downcasting of classes? Downcasting means
|
|
||||||
// casting from a parent class to a child class. The actual object
|
|
||||||
// type is checked at runtime. In any case you can always downcast
|
|
||||||
// explicitly, using ClassName(obj).
|
|
||||||
bool implicitDowncast = true;
|
|
||||||
|
|
||||||
// Allow implicit conversion from float to int (and similar
|
|
||||||
// conversions). If false, you must use explicit casting,
|
|
||||||
// ie. int(value)
|
|
||||||
bool implicitTruncate = false;
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
VM options
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// Whether to add the current working directory to the VFS at
|
|
||||||
// startup. If false, you must add your own directories (using
|
|
||||||
// vm.addPath) or your own VFS implementations, otherwise the library
|
|
||||||
// will not be able to find any script files.
|
|
||||||
bool vmAddCWD = false;
|
|
||||||
|
|
||||||
// Maximum stack size. Prevents stack overflow through infinite
|
|
||||||
// recursion and other bugs.
|
|
||||||
int maxStack = 100;
|
|
||||||
|
|
||||||
// Maximum function stack size
|
|
||||||
int maxFStack = 100;
|
|
||||||
|
|
||||||
// Whether we should limit the number of instructions that execute()
|
|
||||||
// can run at once. Enabling this will prevent infinite loops.
|
|
||||||
bool enableExecLimit = true;
|
|
||||||
|
|
||||||
// Maximum number of instructions to allow in each call to execute()
|
|
||||||
// (if enableExecLimit is true)
|
|
||||||
long execLimit = 10000000;
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Debugging options
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// If true, set the log output to standard out. If false, logging is
|
|
||||||
// disabled by default and must be activated (through monster.vm.dbg)
|
|
||||||
// at runtime.
|
|
||||||
bool defaultLogToStdout = false;
|
|
||||||
|
|
||||||
// If true, all function stack operations (pushes and pops) are logged
|
|
||||||
// with dbg.log(), and all logged messages are indented according to
|
|
||||||
// the current fstack level.
|
|
||||||
bool logFStack = true;
|
|
||||||
|
|
||||||
// If true, log when threads are put into the background/forground.
|
|
||||||
bool logThreads = true;
|
|
||||||
|
|
||||||
// The following options control more low-level debugging. Most of
|
|
||||||
// these will enable tracing of various internal function calls and
|
|
||||||
// parameters directly to stdout.
|
|
||||||
|
|
||||||
// Trace the thread system
|
|
||||||
bool traceThreads = false;
|
|
||||||
|
|
||||||
// Trace scope lookups
|
|
||||||
bool traceLookups = false;
|
|
||||||
|
|
||||||
// Trace resolve() calls
|
|
||||||
bool traceResolve = false;
|
|
||||||
|
|
||||||
// Print completed output from the assembler
|
|
||||||
bool printAsmOutput = false;
|
|
||||||
|
|
||||||
// Trace VM opcode execution
|
|
||||||
bool traceVMOps = false;
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Optimization options
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// Enable assembler optimizations
|
|
||||||
bool optimizeAsm = true;
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Modules
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// Load modules at startup? If false, you can still load modules
|
|
||||||
// manually using monster.modules.all.initAllModules().
|
|
||||||
bool loadModules = true;
|
|
||||||
|
|
||||||
// List of modules to load when initAllModules is called (and at
|
|
||||||
// startup if loadModules is true.)
|
|
||||||
char[] moduleList = "io math timer frames random thread";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Timer module
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// When true, idle function sleep() uses the system clock. When false,
|
|
||||||
// the time is only updated manually when the user calls vm.frame().
|
|
||||||
// The system clock is fine for small applications, but the manual
|
|
||||||
// method is much more optimized. It is highly recommended to use
|
|
||||||
// vm.frame() manually for games and other projects that use a
|
|
||||||
// rendering loop.
|
|
||||||
bool timer_useClock = false;
|
|
@ -1,192 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (options.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.options;
|
|
||||||
|
|
||||||
/*
|
|
||||||
The purpose of this file is to set compile time options for the
|
|
||||||
Monster library - including compiler, VM and modules. This allows
|
|
||||||
the user to customize the language in various ways to fit each
|
|
||||||
project individually.
|
|
||||||
|
|
||||||
For changes to take effect, you must recompile and reinstall the
|
|
||||||
library.
|
|
||||||
|
|
||||||
If you have suggestions for additional options and ways to customize
|
|
||||||
the language, let us know!
|
|
||||||
*/
|
|
||||||
|
|
||||||
static:
|
|
||||||
const:
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Language options
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// Set to false to make the entire language case insensitive. Affects
|
|
||||||
// all identifier and keyword matching. (Not implemented yet!)
|
|
||||||
bool caseSensitive = true;
|
|
||||||
|
|
||||||
// Include the case-insensitive string (and character) operators =i=,
|
|
||||||
// =I=, !=i= and !=I=.
|
|
||||||
bool ciStringOps = true;
|
|
||||||
|
|
||||||
// Skip lines beginning with a hash character '#'
|
|
||||||
bool skipHashes = true;
|
|
||||||
|
|
||||||
// Do we allow implicit downcasting of classes? Downcasting means
|
|
||||||
// casting from a parent class to a child class. The actual object
|
|
||||||
// type is checked at runtime. In any case you can always downcast
|
|
||||||
// explicitly, using ClassName(obj).
|
|
||||||
bool implicitDowncast = true;
|
|
||||||
|
|
||||||
// Allow implicit conversion from float to int (and similar
|
|
||||||
// conversions). If false, you must use explicit casting,
|
|
||||||
// ie. int(value)
|
|
||||||
bool implicitTruncate = false;
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
VM options
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// Whether to add the current working directory to the VFS at
|
|
||||||
// startup. If false, you must add your own directories (using
|
|
||||||
// vm.addPath) or your own VFS implementations, otherwise the library
|
|
||||||
// will not be able to find any script files.
|
|
||||||
bool vmAddCWD = false;
|
|
||||||
|
|
||||||
// Maximum stack size. Prevents stack overflow through infinite
|
|
||||||
// recursion and other bugs.
|
|
||||||
int maxStack = 100;
|
|
||||||
|
|
||||||
// Maximum function stack size
|
|
||||||
int maxFStack = 100;
|
|
||||||
|
|
||||||
// Whether we should limit the number of instructions that execute()
|
|
||||||
// can run at once. Enabling this will prevent infinite loops.
|
|
||||||
bool enableExecLimit = true;
|
|
||||||
|
|
||||||
// Maximum number of instructions to allow in each call to execute()
|
|
||||||
// (if enableExecLimit is true)
|
|
||||||
long execLimit = 10000000;
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Debugging options
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// If true, set the log output to standard out. If false, logging is
|
|
||||||
// disabled by default and must be activated (through monster.vm.dbg)
|
|
||||||
// at runtime.
|
|
||||||
bool defaultLogToStdout = false;
|
|
||||||
|
|
||||||
// If true, all function stack operations (pushes and pops) are logged
|
|
||||||
// with dbg.log(), and all logged messages are indented according to
|
|
||||||
// the current fstack level.
|
|
||||||
bool logFStack = true;
|
|
||||||
|
|
||||||
// If true, log when threads are put into the background/forground.
|
|
||||||
bool logThreads = true;
|
|
||||||
|
|
||||||
// The following options control more low-level debugging. Most of
|
|
||||||
// these will enable tracing of various internal function calls and
|
|
||||||
// parameters directly to stdout.
|
|
||||||
|
|
||||||
// Trace the thread system
|
|
||||||
bool traceThreads = false;
|
|
||||||
|
|
||||||
// Trace scope lookups
|
|
||||||
bool traceLookups = false;
|
|
||||||
|
|
||||||
// Trace resolve() calls
|
|
||||||
bool traceResolve = false;
|
|
||||||
|
|
||||||
// Print completed output from the assembler
|
|
||||||
bool printAsmOutput = false;
|
|
||||||
|
|
||||||
// Trace VM opcode execution
|
|
||||||
bool traceVMOps = false;
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Optimization options
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// Enable assembler optimizations
|
|
||||||
bool optimizeAsm = true;
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Modules
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// Load modules at startup? If false, you can still load modules
|
|
||||||
// manually using monster.modules.all.initAllModules().
|
|
||||||
bool loadModules = true;
|
|
||||||
|
|
||||||
// List of modules to load when initAllModules is called (and at
|
|
||||||
// startup if loadModules is true.)
|
|
||||||
char[] moduleList = "io math timer frames random thread";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*********************************************************
|
|
||||||
|
|
||||||
|
|
||||||
Timer module
|
|
||||||
|
|
||||||
|
|
||||||
*********************************************************/
|
|
||||||
|
|
||||||
// When true, idle function sleep() uses the system clock. When false,
|
|
||||||
// the time is only updated manually when the user calls vm.frame().
|
|
||||||
// The system clock is fine for small applications, but the manual
|
|
||||||
// method is much more optimized. It is highly recommended to use
|
|
||||||
// vm.frame() manually for games and other projects that use a
|
|
||||||
// rendering loop.
|
|
||||||
bool timer_useClock = false;
|
|
@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
svn export https://monster-script.svn.sourceforge.net/svnroot/monster-script/trunk/monster/ . --force
|
|
||||||
rm -r minibos vm/c_api.d
|
|
||||||
|
|
||||||
for a in $(find -iname \*.d); do
|
|
||||||
cat "$a" | sed s/monster.minibos./std./g > "$a"_new
|
|
||||||
mv "$a"_new "$a"
|
|
||||||
done
|
|
||||||
|
|
||||||
svn st
|
|
||||||
|
|
||||||
diff options.openmw options.d || $EDITOR options.d
|
|
||||||
mv options.openmw options.openmw_last
|
|
||||||
cp options.d options.openmw
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (flags.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.util.flags;
|
|
||||||
|
|
||||||
// A generic bitwise flag manager.
|
|
||||||
struct Flags(T)
|
|
||||||
{
|
|
||||||
T flags;
|
|
||||||
|
|
||||||
void set(T t)
|
|
||||||
{ flags |= t; }
|
|
||||||
|
|
||||||
void unset(T t)
|
|
||||||
{ flags ^= flags & t; }
|
|
||||||
|
|
||||||
void set(T t, bool value)
|
|
||||||
{
|
|
||||||
if(value) set(t);
|
|
||||||
else unset(t);
|
|
||||||
assert(has(t) == value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does it have all of the bits in the parameter set?
|
|
||||||
bool has(T t)
|
|
||||||
{ return (flags & t) == t; }
|
|
||||||
|
|
||||||
// Does it have any of the bits in the parameter set?
|
|
||||||
bool hasAny(T t)
|
|
||||||
{ return (flags & t) != 0; }
|
|
||||||
|
|
||||||
// For single-bit parameters, has() and hasAny() are identical.
|
|
||||||
}
|
|
||||||
|
|
||||||
unittest
|
|
||||||
{
|
|
||||||
Flags!(int) fl;
|
|
||||||
|
|
||||||
assert(fl.flags == 0);
|
|
||||||
|
|
||||||
// Try setting some flags
|
|
||||||
assert(!fl.has(2));
|
|
||||||
fl.set(2);
|
|
||||||
assert(fl.has(2));
|
|
||||||
|
|
||||||
assert(!fl.has(4));
|
|
||||||
fl.set(4, true);
|
|
||||||
assert(fl.has(4));
|
|
||||||
|
|
||||||
assert(fl.flags == 6);
|
|
||||||
|
|
||||||
// Make sure setting them again won't change anything
|
|
||||||
fl.set(2);
|
|
||||||
assert(fl.flags == 6);
|
|
||||||
|
|
||||||
fl.set(4);
|
|
||||||
assert(fl.flags == 6);
|
|
||||||
|
|
||||||
fl.set(2, true);
|
|
||||||
assert(fl.flags == 6);
|
|
||||||
|
|
||||||
fl.set(4, true);
|
|
||||||
assert(fl.flags == 6);
|
|
||||||
|
|
||||||
// Test the has() and hasAny() functions
|
|
||||||
with(fl)
|
|
||||||
{
|
|
||||||
assert( !has(1) ); // 1 is not set
|
|
||||||
assert( has(2) ); // 2 is set
|
|
||||||
assert( !has(3) ); // 2+1 is NOT set, because 1 is missing
|
|
||||||
assert( has(4) ); // 4 is set
|
|
||||||
assert( !has(5) ); // 4+1 is NOT set, 1 is missing
|
|
||||||
assert( has(6) ); // 2+4 is set because both are present
|
|
||||||
assert( !has(7) ); // 1+2+4 not set, 1 missing
|
|
||||||
|
|
||||||
assert( !hasAny(1) );// 1 is not set
|
|
||||||
assert( hasAny(2) ); // 2 is set
|
|
||||||
assert( hasAny(3) ); // 2 is set and part of 3=1+2
|
|
||||||
assert( hasAny(4) ); // 4 is set
|
|
||||||
assert( hasAny(5) ); // 4 is and is part of 5=1+4
|
|
||||||
assert( hasAny(6) ); // 2+4 is set
|
|
||||||
assert( hasAny(7) ); // 2+4 are part of 7
|
|
||||||
assert( !hasAny(8) );
|
|
||||||
assert( !hasAny(9) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a mixed flag
|
|
||||||
fl.set(3);
|
|
||||||
assert(fl.has(3));
|
|
||||||
assert(fl.has(7));
|
|
||||||
assert(fl.flags == 7);
|
|
||||||
|
|
||||||
// Unset a flag
|
|
||||||
fl.unset(2);
|
|
||||||
assert(fl.flags == 5);
|
|
||||||
|
|
||||||
// And again
|
|
||||||
fl.unset(2);
|
|
||||||
assert(fl.flags == 5);
|
|
||||||
|
|
||||||
// Set and unset it with set()
|
|
||||||
fl.set(2,true);
|
|
||||||
assert(fl.flags == 7);
|
|
||||||
fl.set(2,false);
|
|
||||||
assert(fl.flags == 5);
|
|
||||||
|
|
||||||
// Now try an enum
|
|
||||||
enum MF
|
|
||||||
{
|
|
||||||
A = 1,
|
|
||||||
B = 2,
|
|
||||||
AB = 3,
|
|
||||||
C = 4
|
|
||||||
}
|
|
||||||
|
|
||||||
Flags!(MF) mf;
|
|
||||||
mf.set(MF.A);
|
|
||||||
assert(mf.has(MF.A));
|
|
||||||
assert(!mf.has(MF.AB));
|
|
||||||
mf.set(MF.B, true);
|
|
||||||
assert(mf.has(MF.B));
|
|
||||||
assert(mf.has(MF.AB));
|
|
||||||
mf.unset(MF.B);
|
|
||||||
assert(mf.has(MF.A));
|
|
||||||
assert(!mf.has(MF.B));
|
|
||||||
assert(!mf.has(MF.AB));
|
|
||||||
}
|
|
@ -1,401 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (freelist.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.util.freelist;
|
|
||||||
|
|
||||||
import monster.util.list;
|
|
||||||
import monster.util.growarray;
|
|
||||||
import std.c.stdlib : malloc, free;
|
|
||||||
|
|
||||||
// A freelist for buffers of a given size. The 'size' template
|
|
||||||
// parameter gives the total requested size of the entire struct,
|
|
||||||
// including overhead.
|
|
||||||
struct BufferList(int size)
|
|
||||||
{
|
|
||||||
// Calculate the 'overhead' size of the structs
|
|
||||||
alias void* vp;
|
|
||||||
static const junk = 2*vp.sizeof + int.sizeof;
|
|
||||||
|
|
||||||
static union BuffData(int size)
|
|
||||||
{
|
|
||||||
static assert(size >= 2, "size must be at least 2");
|
|
||||||
|
|
||||||
int[size/int.sizeof] ints;
|
|
||||||
ubyte[size] bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the data sizes
|
|
||||||
static const bytes = size - junk;
|
|
||||||
static const ints = bytes / int.sizeof;
|
|
||||||
|
|
||||||
static assert(bytes > 0, "size is too small");
|
|
||||||
|
|
||||||
alias BuffData!(bytes) Value;
|
|
||||||
alias Value* ValuePtr;
|
|
||||||
static assert(Value.sizeof == bytes);
|
|
||||||
|
|
||||||
alias LinkedList!(Value, NoAlloc) List;
|
|
||||||
alias List.Node LNode;
|
|
||||||
|
|
||||||
static struct BufferNode(int size)
|
|
||||||
{
|
|
||||||
LNode data;
|
|
||||||
int index;
|
|
||||||
}
|
|
||||||
|
|
||||||
alias BufferNode!(bytes) Node;
|
|
||||||
alias Node* NodePtr;
|
|
||||||
static assert(Node.sizeof == size);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// This is the array that does all the actual allocations. It is
|
|
||||||
// used for quickly looking up indices. GrowArrays are designed to
|
|
||||||
// grow dynamically without reallocation, while still being easily
|
|
||||||
// indexed like a normal array.
|
|
||||||
static GrowArray!(Node) array;
|
|
||||||
|
|
||||||
// The freelist. This is shared between all template instances of
|
|
||||||
// the same size.
|
|
||||||
static List freeList;
|
|
||||||
|
|
||||||
// The nodes belonging to THIS list instance
|
|
||||||
List nodes;
|
|
||||||
|
|
||||||
// Get a new node (move from freelist to node list)
|
|
||||||
ValuePtr getNew()
|
|
||||||
{
|
|
||||||
// Is the freelist empty?
|
|
||||||
if(freeList.length == 0)
|
|
||||||
{
|
|
||||||
// Create a bunch of nodes and shove them into the freelist.
|
|
||||||
const makeSize = 50;
|
|
||||||
|
|
||||||
// Grow the growarray
|
|
||||||
uint len = array.length;
|
|
||||||
array.length = len + makeSize;
|
|
||||||
|
|
||||||
// Loop through the new nodes, number them, and insert them
|
|
||||||
// into freeList
|
|
||||||
for(int i=0; i < makeSize; i++)
|
|
||||||
{
|
|
||||||
NodePtr fn = array.getPtr(i+len);
|
|
||||||
fn.index = i + len;
|
|
||||||
freeList.insertNode(&fn.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the first element from the freelist into the node list.
|
|
||||||
auto node = freeList.getHead;
|
|
||||||
freeList.removeNode(node);
|
|
||||||
nodes.insertNodeFirst(node);
|
|
||||||
|
|
||||||
// Return the value pointer. Since the value is always at the
|
|
||||||
// begining of the Node struct, this is the same
|
|
||||||
// pointer.
|
|
||||||
return &node.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move a node back to the freelist ("delete" it)
|
|
||||||
void remove(ValuePtr node)
|
|
||||||
{
|
|
||||||
nodes.removeNode(node);
|
|
||||||
freeList.insertNodeFirst(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Get the node corresponding to an index
|
|
||||||
static ValuePtr getNode(int index)
|
|
||||||
{
|
|
||||||
return &array.getPtr(index).data.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the index from a node
|
|
||||||
static int getIndex(ValuePtr node)
|
|
||||||
{
|
|
||||||
return ( cast(Node*)node ).index;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint length() { return nodes.length; }
|
|
||||||
static uint totLength() { return array.length; }
|
|
||||||
|
|
||||||
// Move the given node to another list
|
|
||||||
ValuePtr moveTo(ref BufferList fl, ValuePtr node)
|
|
||||||
{
|
|
||||||
nodes.removeNode(node);
|
|
||||||
fl.nodes.insertNodeFirst(node);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first element in the list
|
|
||||||
ValuePtr getHead() { return &nodes.getHead().value; }
|
|
||||||
|
|
||||||
// Loop through the structs in this list
|
|
||||||
int opApply(int delegate(ref Value) dg)
|
|
||||||
{
|
|
||||||
return nodes.opApply(dg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int[] getInt(int isize)
|
|
||||||
{
|
|
||||||
assert(isize <= ints);
|
|
||||||
|
|
||||||
return getNew().ints[0..isize];
|
|
||||||
}
|
|
||||||
|
|
||||||
void freeInt(int[] buf)
|
|
||||||
{
|
|
||||||
assert(buf.length <= ints);
|
|
||||||
remove(cast(ValuePtr)buf.ptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
void* get() { return getNew(); }
|
|
||||||
void free(void* p) { remove(cast(ValuePtr)p); }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Buffers
|
|
||||||
{
|
|
||||||
static:
|
|
||||||
BufferList!(64) b64;
|
|
||||||
BufferList!(128) b128;
|
|
||||||
BufferList!(256) b256;
|
|
||||||
BufferList!(768) b768;
|
|
||||||
|
|
||||||
/*
|
|
||||||
static this()
|
|
||||||
{
|
|
||||||
writefln("64: ints=%s bytes=%s", b64.ints, b64.bytes);
|
|
||||||
writefln("128: ints=%s bytes=%s", b128.ints, b128.bytes);
|
|
||||||
writefln("256: ints=%s bytes=%s", b256.ints, b256.bytes);
|
|
||||||
writefln("768: ints=%s bytes=%s", b768.ints, b768.bytes);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
int[] getInt(uint size)
|
|
||||||
{
|
|
||||||
if(size <= b64.ints) return b64.getInt(size);
|
|
||||||
else if(size <= b128.ints) return b128.getInt(size);
|
|
||||||
else if(size <= b256.ints) return b256.getInt(size);
|
|
||||||
else if(size <= b768.ints) return b768.getInt(size);
|
|
||||||
// Too large for our lists - just use malloc
|
|
||||||
else
|
|
||||||
{
|
|
||||||
//writefln("WARNING: using malloc for %s ints (%s bytes)", size, size*int.sizeof);
|
|
||||||
return ( cast(int*)malloc(size*int.sizeof) )[0..size];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(int[] buf)
|
|
||||||
{
|
|
||||||
uint size = buf.length;
|
|
||||||
if(size <= b64.ints) b64.freeInt(buf);
|
|
||||||
else if(size <= b128.ints) b128.freeInt(buf);
|
|
||||||
else if(size <= b256.ints) b256.freeInt(buf);
|
|
||||||
else if(size <= b768.ints) b768.freeInt(buf);
|
|
||||||
else .free(buf.ptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* THIS DOESN'T WORK - because DMD is still stubborn with those
|
|
||||||
template forwarding issues. Instead we'll just reuse the old
|
|
||||||
freelist implementation below.
|
|
||||||
|
|
||||||
// A list that uses a freelist for allocation. It is built on top of
|
|
||||||
// BufferList.
|
|
||||||
struct FreeList(T)
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
// For small sizes, pool together with existing lists.
|
|
||||||
static if(T.sizeof <= 64) static const size = 64;
|
|
||||||
else static if(T.sizeof <= 128) static const size = 128;
|
|
||||||
else static if(T.sizeof <= 256) static const size = 256;
|
|
||||||
// Just use the actual size, rounded up to the nearest 16
|
|
||||||
else static if(T.sizeof % 16 == 0)
|
|
||||||
const size = T.sizeof;
|
|
||||||
else
|
|
||||||
const size = T.sizeof + 16 - (T.sizeof%16);
|
|
||||||
|
|
||||||
alias BufferList!(size) BuffList;
|
|
||||||
BuffList buffer;
|
|
||||||
|
|
||||||
alias BuffList.Value Value;
|
|
||||||
alias BuffList.ValuePtr ValuePtr;
|
|
||||||
|
|
||||||
static assert(T.sizeof <= BuffList.bytes);
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Get a new node (move from freelist to node list)
|
|
||||||
T* getNew()
|
|
||||||
{ return cast(T*) buffer.get(); }
|
|
||||||
|
|
||||||
// Move a node back to the freelist ("delete" it)
|
|
||||||
void remove(T* node)
|
|
||||||
{ buffer.free(node); }
|
|
||||||
|
|
||||||
// Get the node corresponding to an index
|
|
||||||
static T* getNode(int index)
|
|
||||||
{ return cast(T*) buffer.getNode(index); }
|
|
||||||
|
|
||||||
// Get the index from a node
|
|
||||||
static int getIndex(T *node)
|
|
||||||
{ return buffer.getIndex(cast(ValuePtr)node); }
|
|
||||||
|
|
||||||
uint length() { return buffer.length(); }
|
|
||||||
static uint totLength() { return buffer.totLength(); }
|
|
||||||
|
|
||||||
// Move the given node to another list
|
|
||||||
T* moveTo(ref FreeList fl, T* node)
|
|
||||||
{
|
|
||||||
auto vp = cast(ValuePtr) node;
|
|
||||||
return cast(T*) buffer.moveTo(fl.buffer, vp);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first element in the list
|
|
||||||
T* getHead() { return cast(T*) buffer.getHead(); }
|
|
||||||
|
|
||||||
// Loop through the structs in this list
|
|
||||||
int opApply(int delegate(ref T) dg)
|
|
||||||
{
|
|
||||||
auto dgc = cast(int delegate(ref Value)) dg;
|
|
||||||
return nodes.opApply(dgc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This had to be moved outside FreeList to work around some
|
|
||||||
// irritating DMD template problems. (Can you say Aaargh!)
|
|
||||||
struct __FreeNode(T)
|
|
||||||
{
|
|
||||||
_lstNode!(T) data;
|
|
||||||
int index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A list that uses a freelist for allocation. Based on
|
|
||||||
// LinkedList. Very basic, only functions that are actually in use in
|
|
||||||
// my own code are implemented.
|
|
||||||
struct FreeList(T)
|
|
||||||
{
|
|
||||||
alias LinkedList!(T, NoAlloc) TList;
|
|
||||||
alias TList.Node TNode;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
alias __FreeNode!(T) _FreeNode;
|
|
||||||
|
|
||||||
// This is the array that does all the actual allocations. It is
|
|
||||||
// used for quickly looking up indices.
|
|
||||||
static GrowArray!(_FreeNode) array;
|
|
||||||
|
|
||||||
// The freelist. This is shared between all template instances of
|
|
||||||
// the same type, as far as I know. DMD might have some strange
|
|
||||||
// behavior that I am not aware of, but the worst case is that we
|
|
||||||
// end up with multiple freelists, which is not the end of the world
|
|
||||||
// (although slightly inefficient.)
|
|
||||||
static TList freeList;
|
|
||||||
|
|
||||||
// The nodes belonging to THIS list
|
|
||||||
TList nodes;
|
|
||||||
|
|
||||||
public:
|
|
||||||
// Get a new node (move from freelist to node list)
|
|
||||||
T* getNew()
|
|
||||||
{
|
|
||||||
// Is the freelist empty?
|
|
||||||
if(freeList.length == 0)
|
|
||||||
{
|
|
||||||
// Create a bunch of nodes and shove them into the freelist.
|
|
||||||
const makeSize = 100;
|
|
||||||
|
|
||||||
// Grow the growarray
|
|
||||||
uint len = array.length;
|
|
||||||
array.length = len + makeSize;
|
|
||||||
|
|
||||||
// Loop through the new nodes, number them, and insert them
|
|
||||||
// into freeList
|
|
||||||
for(int i=0; i < makeSize; i++)
|
|
||||||
{
|
|
||||||
_FreeNode *fn = array.getPtr(i+len);
|
|
||||||
fn.index = i + len;
|
|
||||||
freeList.insertNode(&fn.data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move the first element from the freelist into the node list.
|
|
||||||
auto node = freeList.getHead;
|
|
||||||
freeList.removeNode(node);
|
|
||||||
nodes.insertNodeFirst(node);
|
|
||||||
|
|
||||||
// Return the value pointer. Since the value is always at the
|
|
||||||
// begining of the Node struct, this is the same
|
|
||||||
// pointer. LinkedList lets us choose if we want to use T* or
|
|
||||||
// Node*.
|
|
||||||
return &node.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the node corresponding to an index
|
|
||||||
static T* getNode(int index)
|
|
||||||
{
|
|
||||||
return &array.getPtr(index).data.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the index from a node
|
|
||||||
static int getIndex(T *node)
|
|
||||||
{
|
|
||||||
return ( cast(_FreeNode*)node ).index;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move a node back to the freelist ("delete" it)
|
|
||||||
void remove(T* node)
|
|
||||||
{
|
|
||||||
nodes.removeNode(node);
|
|
||||||
freeList.insertNodeFirst(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint length() { return nodes.length; }
|
|
||||||
static uint totLength() { return array.length; }
|
|
||||||
|
|
||||||
// Move the given node to another list
|
|
||||||
T* moveTo(ref FreeList fl, T* node)
|
|
||||||
{
|
|
||||||
nodes.removeNode(node);
|
|
||||||
fl.nodes.insertNodeFirst(node);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the first element in the list
|
|
||||||
T* getHead() { return &nodes.getHead().value; }
|
|
||||||
|
|
||||||
// Get the next element in the list
|
|
||||||
T* getNext(T* nd)
|
|
||||||
{
|
|
||||||
auto node = cast(TNode*)nd;
|
|
||||||
return cast(T*) node.getNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through the structs in this list
|
|
||||||
int opApply(int delegate(ref T) dg)
|
|
||||||
{ return nodes.opApply(dg); }
|
|
||||||
}
|
|
@ -1,312 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (growarray.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.util.growarray;
|
|
||||||
|
|
||||||
// Array that grows without reallocations. It does this by
|
|
||||||
// block-allocation and by stringing several blocks together to work
|
|
||||||
// like one single array. Elements are not guaranteed to be stored
|
|
||||||
// continuously in memory.
|
|
||||||
struct GrowArray(T)
|
|
||||||
{
|
|
||||||
const defSize = 128;
|
|
||||||
|
|
||||||
private:
|
|
||||||
uint listSize = defSize; // Size of new lists
|
|
||||||
uint elements; // Current number of elements
|
|
||||||
uint elemsAlloc; // Elements allocated
|
|
||||||
|
|
||||||
uint begin; // At what element to start counting. Used for
|
|
||||||
// slices.
|
|
||||||
|
|
||||||
T[][] listList;
|
|
||||||
|
|
||||||
// Make sure there is room for at least 'size' elements in total.
|
|
||||||
void alloc(uint size)
|
|
||||||
{
|
|
||||||
// Do nothing if the list is large enough
|
|
||||||
if(size <= elemsAlloc) return;
|
|
||||||
|
|
||||||
// If this is a slice, we must always reallocate when
|
|
||||||
// growing. Implement that later.
|
|
||||||
if(begin) assert(0, "Cannot grow a slice");
|
|
||||||
|
|
||||||
// Number of lists we need
|
|
||||||
uint lists = ((size-1) / listSize) + 1;
|
|
||||||
|
|
||||||
// The number of needed elements should never decrease
|
|
||||||
assert((listSize*lists) >= elemsAlloc);
|
|
||||||
|
|
||||||
// Number of elements we need allocated
|
|
||||||
elemsAlloc = listSize * lists;
|
|
||||||
|
|
||||||
// Make sure the list of lists is large enough
|
|
||||||
if(listList.length < lists)
|
|
||||||
listList.length = lists+30;
|
|
||||||
|
|
||||||
// Allocate the lists we need
|
|
||||||
for(int i=0; i<lists; i++)
|
|
||||||
listList[i].length = listSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
uint length() { return elements - begin; }
|
|
||||||
void length(uint newLen)
|
|
||||||
{
|
|
||||||
newLen += begin;
|
|
||||||
|
|
||||||
alloc(newLen);
|
|
||||||
|
|
||||||
elements = newLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize - set the current size
|
|
||||||
void initialize(uint size = 0, uint listSize = defSize)
|
|
||||||
{
|
|
||||||
assert(listList.length == 0);
|
|
||||||
this.listSize = listSize;
|
|
||||||
length(size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static GrowArray opCall(uint size = 0, uint listSize = defSize)
|
|
||||||
{
|
|
||||||
GrowArray a;
|
|
||||||
a.initialize(size, listSize);
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opCatAssign(T t)
|
|
||||||
{
|
|
||||||
length = length + 1;
|
|
||||||
opIndexAssign(t, length-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
void opCatAssign(T[] list)
|
|
||||||
{
|
|
||||||
uint len = length;
|
|
||||||
length = len + list.length;
|
|
||||||
foreach(int i, ref T t; list)
|
|
||||||
opIndexAssign(t, len+i);
|
|
||||||
}
|
|
||||||
|
|
||||||
T opIndex(int index)
|
|
||||||
{
|
|
||||||
index += begin;
|
|
||||||
assert(index >= begin && index < elements,
|
|
||||||
"GrowArray index out of bounds");
|
|
||||||
|
|
||||||
return listList[index/listSize][index%listSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
T opIndexAssign(T value, int index)
|
|
||||||
{
|
|
||||||
index += begin;
|
|
||||||
assert(index >= begin && index < elements,
|
|
||||||
"GrowArray index out of bounds");
|
|
||||||
|
|
||||||
return (listList[index/listSize][index%listSize] = value);
|
|
||||||
}
|
|
||||||
|
|
||||||
T* getPtr(int index)
|
|
||||||
{
|
|
||||||
index += begin;
|
|
||||||
assert(index >= begin && index < elements,
|
|
||||||
"GrowArray index out of bounds");
|
|
||||||
|
|
||||||
return &listList[index/listSize][index%listSize];
|
|
||||||
}
|
|
||||||
|
|
||||||
GrowArray opSlice(int start, int stop)
|
|
||||||
{
|
|
||||||
assert(start<=stop, "Illegal GrowArray slice");
|
|
||||||
GrowArray ga = *this;
|
|
||||||
ga.begin = begin+start;
|
|
||||||
ga.length = stop-start;
|
|
||||||
return ga;
|
|
||||||
}
|
|
||||||
|
|
||||||
GrowArray opSlice()
|
|
||||||
{
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a contiguous array copy containg all the elements.
|
|
||||||
T[] arrayCopy()
|
|
||||||
{
|
|
||||||
T[] res = new T[length()];
|
|
||||||
|
|
||||||
// Non-optimized!
|
|
||||||
foreach(i, ref r; res)
|
|
||||||
r = opIndex(i);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int opApply(int delegate(ref int, ref T) dg)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
int len = length;
|
|
||||||
int pos = begin%listSize;
|
|
||||||
int list = begin/listSize;
|
|
||||||
for(int i; i<len; i++)
|
|
||||||
{
|
|
||||||
res = dg(i, listList[list][pos++]);
|
|
||||||
if(res) break;
|
|
||||||
|
|
||||||
if(pos == listSize)
|
|
||||||
{
|
|
||||||
list++;
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
int opApply(int delegate(ref T) dg)
|
|
||||||
{
|
|
||||||
int res;
|
|
||||||
int len = length;
|
|
||||||
int pos = begin%listSize;
|
|
||||||
int list = begin/listSize;
|
|
||||||
for(int i; i<len; i++)
|
|
||||||
{
|
|
||||||
res = dg(listList[list][pos++]);
|
|
||||||
if(res) break;
|
|
||||||
|
|
||||||
if(pos == listSize)
|
|
||||||
{
|
|
||||||
list++;
|
|
||||||
pos = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unittest
|
|
||||||
{
|
|
||||||
// Test default initialization
|
|
||||||
GrowArray!(int) arr;
|
|
||||||
assert(arr.begin == 0);
|
|
||||||
assert(arr.length == 0);
|
|
||||||
assert(arr.elements == 0);
|
|
||||||
assert(arr.elemsAlloc == 0);
|
|
||||||
assert(arr.listList.length == 0);
|
|
||||||
assert(arr.listSize == 128);
|
|
||||||
|
|
||||||
// Setting length
|
|
||||||
arr.length = 3;
|
|
||||||
assert(arr.length == 3);
|
|
||||||
assert(arr.elements == 3);
|
|
||||||
assert(arr.elemsAlloc == arr.listSize);
|
|
||||||
assert(arr.listList.length >= 1);
|
|
||||||
|
|
||||||
// Setting and reading elements
|
|
||||||
arr[0] = 1;
|
|
||||||
arr[1] = 2;
|
|
||||||
arr[2] = 3;
|
|
||||||
assert(arr[0] == 1);
|
|
||||||
assert(arr[1] == 2);
|
|
||||||
assert(arr[2] == 3);
|
|
||||||
assert(arr.listList[0][0] == 1);
|
|
||||||
assert(arr.listList[0][1] == 2);
|
|
||||||
assert(arr.listList[0][2] == 3);
|
|
||||||
|
|
||||||
// Test opCatAssign
|
|
||||||
arr ~= 4;
|
|
||||||
assert(arr.length == 4);
|
|
||||||
assert(arr[3] == 4);
|
|
||||||
|
|
||||||
// Foreach
|
|
||||||
int tmp = 0;
|
|
||||||
foreach(int i, int v; arr)
|
|
||||||
{
|
|
||||||
assert(v==i+1);
|
|
||||||
tmp++;
|
|
||||||
}
|
|
||||||
assert(tmp == 4);
|
|
||||||
|
|
||||||
tmp = 1;
|
|
||||||
foreach(int v; arr)
|
|
||||||
assert(v == tmp++);
|
|
||||||
assert(tmp == 5);
|
|
||||||
|
|
||||||
// Slicing the entire array
|
|
||||||
arr = arr[0..4];
|
|
||||||
assert(arr.length == 4);
|
|
||||||
assert(arr[3] == 4);
|
|
||||||
|
|
||||||
// Slicing part of the array
|
|
||||||
auto arrS = arr[1..3];
|
|
||||||
assert(arrS.length == 2);
|
|
||||||
assert(arrS[0] == 2);
|
|
||||||
assert(arrS[1] == 3);
|
|
||||||
arrS[0] = 10;
|
|
||||||
assert(arr[1] == 10);
|
|
||||||
|
|
||||||
// Slicing the slice
|
|
||||||
arrS = arrS[1..2];
|
|
||||||
assert(arrS.length == 1);
|
|
||||||
assert(arrS[0] == 3);
|
|
||||||
|
|
||||||
// Empty slice
|
|
||||||
arrS = arr[3..3];
|
|
||||||
assert(arrS.length == 0);
|
|
||||||
|
|
||||||
// Custom list size, and more than one list
|
|
||||||
auto arr2 = GrowArray!(byte)(3,2);
|
|
||||||
assert(arr2.length == 3);
|
|
||||||
assert(arr2.elements == 3);
|
|
||||||
assert(arr2.listSize == 2);
|
|
||||||
assert(arr2.elemsAlloc == 4);
|
|
||||||
assert(arr2.listList.length >= 2);
|
|
||||||
assert(arr2.listList[0].length == 2);
|
|
||||||
|
|
||||||
assert(arr2[0] == 0);
|
|
||||||
assert(arr2[1] == 0);
|
|
||||||
assert(arr2[2] == 0);
|
|
||||||
|
|
||||||
arr2[1]=2;
|
|
||||||
arr2[2]=4;
|
|
||||||
|
|
||||||
foreach(int i, byte v; arr2)
|
|
||||||
assert(v == 2*i);
|
|
||||||
|
|
||||||
// Check that boundry checking works (in non-release mode.)
|
|
||||||
bool err = false;
|
|
||||||
try{arr2[3];}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
err = true;
|
|
||||||
}
|
|
||||||
assert(err == true);
|
|
||||||
|
|
||||||
err = false;
|
|
||||||
try{arr2[3] = 0;}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
err = true;
|
|
||||||
}
|
|
||||||
assert(err == true);
|
|
||||||
}
|
|
@ -1,641 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (list.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.util.list;
|
|
||||||
|
|
||||||
// Set this to enable some more extensive list checks. These will loop
|
|
||||||
// through the entire list on every insert and remove, so they are
|
|
||||||
// very slow for large lists. But they are very handy bug catchers
|
|
||||||
// when doing a little dirty list hacking.
|
|
||||||
// debug=slowcheck;
|
|
||||||
|
|
||||||
private import std.c.stdlib;
|
|
||||||
private import std.string;
|
|
||||||
|
|
||||||
typedef void GCAlloc;
|
|
||||||
|
|
||||||
alias malloc cmalloc;
|
|
||||||
alias free cfree;
|
|
||||||
|
|
||||||
class LinkedListException : Exception
|
|
||||||
{
|
|
||||||
this(char[] msg)
|
|
||||||
{
|
|
||||||
super(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Internal structure used by List
|
|
||||||
*/
|
|
||||||
|
|
||||||
align(1)
|
|
||||||
struct _lstNode(Value)
|
|
||||||
{
|
|
||||||
// It is essential that the value is first in the struct. This
|
|
||||||
// allows us to interchange pointers to the value with pointer to
|
|
||||||
// the Node. This is done for convenience - allowing us to use the
|
|
||||||
// value directly instead of using somePtr.value, This also
|
|
||||||
// sidesteps the fact that DMD isn't very good with template forward
|
|
||||||
// references, something that creates a lot of problems if we use
|
|
||||||
// LinkedList.Iterator for everything (trust me on this.)
|
|
||||||
Value value;
|
|
||||||
|
|
||||||
_lstNode* getNext() { return next; }
|
|
||||||
_lstNode* getPrev() { return prev; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
_lstNode* next; // Next node
|
|
||||||
_lstNode* prev; // Previous node
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a doubly linked list. It's not terribly advanced at the
|
|
||||||
* moment, but I don't need any more functionality right now.
|
|
||||||
*
|
|
||||||
* Alloc must have the following members:
|
|
||||||
* void* alloc(uint size)
|
|
||||||
* void free(void*)
|
|
||||||
* bool autoinit; // True if alloc automatically sets memory to zero.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Example allocator using malloc and free:
|
|
||||||
struct Malloc
|
|
||||||
{
|
|
||||||
static const bool autoinit = false; // malloc does not initialize memory
|
|
||||||
static const bool usefree = true; // We must call free() to release memory
|
|
||||||
static void* alloc(uint size) { return cmalloc(size); }
|
|
||||||
static void free(void* p) { cfree(p); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// A null allocator. Use if you only intend to move nodes into and out
|
|
||||||
// of the list, not to allocate them. Useful for a freelist, for
|
|
||||||
// example.
|
|
||||||
struct NoAlloc
|
|
||||||
{
|
|
||||||
static const bool autoinit = false;
|
|
||||||
static const bool usefree = true;
|
|
||||||
static void *alloc(uint size) { assert(0, "NoAlloc.alloc not allowed"); }
|
|
||||||
static void free(void *p) { assert(0, "NoAlloc.free not allowed"); }
|
|
||||||
}
|
|
||||||
|
|
||||||
struct LinkedList(Value, alias Alloc = GCAlloc)
|
|
||||||
{
|
|
||||||
alias _lstNode!(Value) Node;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
Node *head; // This is the head of the linked list (first element)
|
|
||||||
Node *tail; // New nodes are inserted here
|
|
||||||
uint totalNum; // Number of elements
|
|
||||||
|
|
||||||
// Determine if the allocator automatically initializes memory
|
|
||||||
static if(is(Alloc == GCAlloc))
|
|
||||||
static const bool autoinit = true;
|
|
||||||
else static if(Alloc.autoinit)
|
|
||||||
static const bool autoinit = true;
|
|
||||||
else
|
|
||||||
static const bool autoinit = false;
|
|
||||||
|
|
||||||
// Determine if we have to manually free memory
|
|
||||||
static if(is(Alloc == GCAlloc))
|
|
||||||
static const bool usefree = false;
|
|
||||||
else static if(Alloc.usefree)
|
|
||||||
static const bool usefree = true;
|
|
||||||
else
|
|
||||||
static const bool usefree = false;
|
|
||||||
|
|
||||||
// Throw an exception
|
|
||||||
void fail(char[] msg)
|
|
||||||
{
|
|
||||||
msg = format("LinkedList!(%s) exception: %s", typeid(Value).toString, msg);
|
|
||||||
throw new LinkedListException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Whenever you find a bug that creates an invalid state, put it in
|
|
||||||
// here so we can safeguard against regressions
|
|
||||||
|
|
||||||
invariant()
|
|
||||||
{
|
|
||||||
if(head != null || tail != null || totalNum != 0)
|
|
||||||
{
|
|
||||||
assert(head != null);
|
|
||||||
assert(tail != null);
|
|
||||||
assert(totalNum != 0);
|
|
||||||
|
|
||||||
assert(head.prev == null);
|
|
||||||
assert(tail.next == null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alias Node* Iterator;
|
|
||||||
|
|
||||||
// Simply reset all pointers and variables, losing any nodes
|
|
||||||
// present.
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
head = tail = null;
|
|
||||||
totalNum = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go through the list and delete all nodes
|
|
||||||
void deleteAll()
|
|
||||||
{
|
|
||||||
// If there is no need to free objects, then deleteAll() is
|
|
||||||
// equivalent to reset().
|
|
||||||
static if(usefree)
|
|
||||||
{
|
|
||||||
// Loop through the list and delete everything
|
|
||||||
Node *p = head;
|
|
||||||
while(p != null)
|
|
||||||
{
|
|
||||||
Node *next = p.next;
|
|
||||||
Alloc.free(p);
|
|
||||||
p = next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator getHead() { return head; }
|
|
||||||
Iterator getTail() { return tail; }
|
|
||||||
|
|
||||||
// Check if the given iterator is part of the list
|
|
||||||
bool hasIterator(Node *v)
|
|
||||||
{
|
|
||||||
Node* p = head;
|
|
||||||
|
|
||||||
while(p != null)
|
|
||||||
{
|
|
||||||
if(p == v)
|
|
||||||
{
|
|
||||||
assert(length >= 1);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
p = p.next;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert a value at the end of the list.
|
|
||||||
alias insert insertLast;
|
|
||||||
Iterator insert(Value v)
|
|
||||||
{
|
|
||||||
Node *p = createNode();
|
|
||||||
p.value = v;
|
|
||||||
return insertNode(p);
|
|
||||||
}
|
|
||||||
// Also allow ~= syntax for this
|
|
||||||
Iterator opCatAssign(Value v) { return insert(v); }
|
|
||||||
|
|
||||||
// Insert an existing node at the end of the list. The insertNode*()
|
|
||||||
// variants along with removeNode() are useful for removing and
|
|
||||||
// reinserting nodes without allocating more memory. This can for
|
|
||||||
// example be used for free lists and similar constructions. In
|
|
||||||
// other words, you can use these to move elements from one list to
|
|
||||||
// another.
|
|
||||||
alias insertNode insertNodeLast;
|
|
||||||
Iterator insertNode(Node *p)
|
|
||||||
in
|
|
||||||
{
|
|
||||||
assert(!hasIterator(p), "inserNode: Node is already in the list");
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
if(tail)
|
|
||||||
{
|
|
||||||
// Insert node at the end of the list
|
|
||||||
assert(head != null);
|
|
||||||
tail.next = p;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This is the first element to be inserted
|
|
||||||
assert(head == null);
|
|
||||||
head = p;
|
|
||||||
}
|
|
||||||
p.prev = tail;
|
|
||||||
tail = p;
|
|
||||||
p.next = null;
|
|
||||||
|
|
||||||
totalNum++;
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
// The Value* variants of the node functions work the same way as
|
|
||||||
// their Iterator (Node*) versions. The pointers are the same, they
|
|
||||||
// just need to be recast.
|
|
||||||
Value* insertNode(Value *p)
|
|
||||||
{ return &insertNode( cast(Node*)p ).value; }
|
|
||||||
|
|
||||||
// Beginning of the list
|
|
||||||
Iterator insertFirst(Value v)
|
|
||||||
{
|
|
||||||
Node *p = createNode();
|
|
||||||
p.value = v;
|
|
||||||
return insertNodeFirst(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterator insertNodeFirst(Node *p)
|
|
||||||
in
|
|
||||||
{
|
|
||||||
debug(slowcheck)
|
|
||||||
assert(!hasIterator(p), "inserNodeFirst: Node is already in the list");
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
if(head)
|
|
||||||
{
|
|
||||||
// Insert node at the beginning of the list
|
|
||||||
assert(tail != null);
|
|
||||||
head.prev = p;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// This is the first element to be inserted
|
|
||||||
assert(tail == null);
|
|
||||||
tail = p;
|
|
||||||
}
|
|
||||||
p.next = head;
|
|
||||||
head = p;
|
|
||||||
p.prev = null;
|
|
||||||
|
|
||||||
totalNum++;
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
Value* insertNodeFirst(Value *p)
|
|
||||||
{ return &insertNodeFirst( cast(Node*)p ).value; }
|
|
||||||
|
|
||||||
// Insert after a given element
|
|
||||||
Iterator insertAfter(Iterator i, Value v)
|
|
||||||
{
|
|
||||||
Node *p = createNode();
|
|
||||||
p.value = v;
|
|
||||||
return insertNodeAfter(i, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert p after i
|
|
||||||
Iterator insertNodeAfter(Iterator i, Node *p)
|
|
||||||
in
|
|
||||||
{
|
|
||||||
//debug(slowcheck)
|
|
||||||
{
|
|
||||||
assert(!hasIterator(p), "inserNodeAfter: Node is already in the list");
|
|
||||||
assert(hasIterator(i), "insertNodeAfter(): element i not part of the list");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
// If i is the last element, then insertNodeLast already does a
|
|
||||||
// stellar job of inserting
|
|
||||||
if(i == tail)
|
|
||||||
return insertNodeLast(p);
|
|
||||||
|
|
||||||
// Make p point to the right elements
|
|
||||||
p.next = i.next;
|
|
||||||
p.prev = i;
|
|
||||||
|
|
||||||
// Random consistency check
|
|
||||||
assert(i == i.next.prev);
|
|
||||||
|
|
||||||
// Make the right elements point to p
|
|
||||||
i.next = p;
|
|
||||||
assert(p.next != null);
|
|
||||||
p.next.prev = p;
|
|
||||||
|
|
||||||
totalNum++;
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
// Insert p after i
|
|
||||||
Value* insertNodeAfter(Value* p, Value* i)
|
|
||||||
{ return &insertNodeAfter( cast(Node*)p, cast(Node*)i ).value; }
|
|
||||||
|
|
||||||
|
|
||||||
// Insert value v before i
|
|
||||||
Iterator insertBefore(Iterator i, Value v)
|
|
||||||
{
|
|
||||||
Node *p = createNode();
|
|
||||||
p.value = v;
|
|
||||||
return insertNodeBefore(i, p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Insert p before i
|
|
||||||
Iterator insertNodeBefore(Iterator i, Node *p)
|
|
||||||
in
|
|
||||||
{
|
|
||||||
//debug(slowcheck)
|
|
||||||
{
|
|
||||||
assert(!hasIterator(p), "inserNodeBefore: Node is already in the list");
|
|
||||||
assert(hasIterator(i), "insertBefore(): element not part of the list");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
// If i is the first, just insert at the beginning
|
|
||||||
if(i==head) return insertNodeFirst(p);
|
|
||||||
|
|
||||||
// If I mess it up, an assertion failure is easier to debug than
|
|
||||||
// a segfault.
|
|
||||||
assert(i.prev != null);
|
|
||||||
|
|
||||||
// Reuse insertAfter instead of reinventing the wheel
|
|
||||||
return insertNodeAfter(i.prev, p);
|
|
||||||
}
|
|
||||||
// Insert p before i
|
|
||||||
Value* insertNodeBefore(Value* p, Value* i)
|
|
||||||
{ return &insertNodeBefore( cast(Node*)p, cast(Node*)i ).value; }
|
|
||||||
|
|
||||||
|
|
||||||
// Swap position of element a and b
|
|
||||||
void swap(Iterator a, Iterator b)
|
|
||||||
in
|
|
||||||
{
|
|
||||||
//debug(slowcheck)
|
|
||||||
assert(hasIterator(a) && hasIterator(b),
|
|
||||||
"swap(a,b): both elements must be in the list");
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
Iterator tmp;
|
|
||||||
|
|
||||||
// Handle special cases first
|
|
||||||
|
|
||||||
// The same element? Do nothing.
|
|
||||||
if(a==b) return;
|
|
||||||
|
|
||||||
// Are they next to each other?
|
|
||||||
if(b.next == a)
|
|
||||||
{
|
|
||||||
// Swap it so we have a before b, then handle it below.
|
|
||||||
assert(a.prev == b);
|
|
||||||
tmp = a;
|
|
||||||
a = b;
|
|
||||||
b = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Point a.prev to b
|
|
||||||
if(a.prev) a.prev.next = b;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(head == a);
|
|
||||||
head = b;
|
|
||||||
}
|
|
||||||
// Point to b.next a
|
|
||||||
if(b.next) b.next.prev = a;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(tail == b);
|
|
||||||
tail = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// From this point on, if a is next to b it must be handled as a
|
|
||||||
// special case. We have already swapped them above so that a is
|
|
||||||
// before b.
|
|
||||||
if(a.next == b)
|
|
||||||
{
|
|
||||||
assert(b.prev == a);
|
|
||||||
|
|
||||||
// Assign outer pointers
|
|
||||||
b.prev = a.prev;
|
|
||||||
a.next = b.next;
|
|
||||||
|
|
||||||
// Assign inner pointers
|
|
||||||
a.prev = b;
|
|
||||||
b.next = a;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If a is NOT next to b, continue the pointer orgy.
|
|
||||||
|
|
||||||
// Point a.next to b
|
|
||||||
if(a.next) a.next.prev = b;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(tail == a);
|
|
||||||
tail = b;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(b.prev) b.prev.next = a;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(head == b);
|
|
||||||
head = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finally, swap a and b's internal pointers
|
|
||||||
tmp = a.next;
|
|
||||||
a.next = b.next;
|
|
||||||
b.next = tmp;
|
|
||||||
|
|
||||||
tmp = a.prev;
|
|
||||||
a.prev = b.prev;
|
|
||||||
b.prev = tmp;
|
|
||||||
}
|
|
||||||
void swap(Value* a, Value* b)
|
|
||||||
{ swap( cast(Node*)a, cast(Node*)b ); }
|
|
||||||
|
|
||||||
// Remove a node from the list and delete it
|
|
||||||
void remove(Iterator p)
|
|
||||||
{
|
|
||||||
removeNode(p);
|
|
||||||
deleteNode(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Just remove the node from the list, do not delete it.
|
|
||||||
void removeNode(Iterator p)
|
|
||||||
in
|
|
||||||
{
|
|
||||||
//debug(slowcheck)
|
|
||||||
assert(hasIterator(p), "remove(): element not part of the list");
|
|
||||||
}
|
|
||||||
body
|
|
||||||
{
|
|
||||||
// Remove from the list
|
|
||||||
if(p.next)
|
|
||||||
{
|
|
||||||
p.next.prev = p.prev;
|
|
||||||
|
|
||||||
// Make sure we are NOT tail
|
|
||||||
assert(tail != p);
|
|
||||||
}
|
|
||||||
else // We're the tail
|
|
||||||
{
|
|
||||||
assert(tail == p);
|
|
||||||
tail = p.prev;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(p.prev)
|
|
||||||
{
|
|
||||||
p.prev.next = p.next;
|
|
||||||
|
|
||||||
// We are NOT the head, since we have a previous element
|
|
||||||
assert(head != p);
|
|
||||||
}
|
|
||||||
else // We're head
|
|
||||||
{
|
|
||||||
assert(head == p);
|
|
||||||
head = p.next;
|
|
||||||
}
|
|
||||||
|
|
||||||
totalNum--;
|
|
||||||
}
|
|
||||||
void removeNode(Value *v)
|
|
||||||
{ removeNode( cast(Iterator)v ); }
|
|
||||||
|
|
||||||
// Free a node
|
|
||||||
static private void deleteNode(Node *p)
|
|
||||||
{
|
|
||||||
// For the GC, just release the
|
|
||||||
// pointer into the wild.
|
|
||||||
static if(usefree) Alloc.free(p);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new node and return it's pointer. TODO: Make this
|
|
||||||
// static, and increase totalNum in the insert methods instead.
|
|
||||||
static private Node* createNode()
|
|
||||||
{
|
|
||||||
static if(is(Alloc == GCAlloc))
|
|
||||||
Node *p = new Node;
|
|
||||||
else
|
|
||||||
Node *p = cast(Node*)Alloc.alloc(Node.sizeof);
|
|
||||||
|
|
||||||
// Initialize next pointers
|
|
||||||
static if(!autoinit)
|
|
||||||
{
|
|
||||||
p.next = null;
|
|
||||||
p.prev = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return p;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through the nodes in the order they were inserted
|
|
||||||
int opApply(int delegate(ref Value v) del)
|
|
||||||
{
|
|
||||||
Node *p = head;
|
|
||||||
uint safeGuard = 0;
|
|
||||||
while(p != null)
|
|
||||||
{
|
|
||||||
assert(safeGuard++ < totalNum);
|
|
||||||
int i = del(p.value);
|
|
||||||
if(i) return i;
|
|
||||||
p = p.next;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Loop through the nodes in the order they were inserted
|
|
||||||
int opApply(int delegate(ref int ind, ref Value v) del)
|
|
||||||
{
|
|
||||||
Node *p = head;
|
|
||||||
int ind = 0;
|
|
||||||
while(p != null)
|
|
||||||
{
|
|
||||||
assert(ind < totalNum);
|
|
||||||
int i = del(ind, p.value);
|
|
||||||
ind++;
|
|
||||||
if(i) return i;
|
|
||||||
p = p.next;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Number of elements
|
|
||||||
uint length() { return totalNum; }
|
|
||||||
|
|
||||||
char[] toString()
|
|
||||||
{
|
|
||||||
char[] res = "[";
|
|
||||||
foreach(int i, Value v; *this)
|
|
||||||
{
|
|
||||||
if(i < totalNum-1) res ~= format(" %s,", v);
|
|
||||||
else res ~= format(" %s ]", v);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alias LinkedList!(void*, NoAlloc) PointerList;
|
|
||||||
alias PointerList.Node vpNode;
|
|
||||||
alias PointerList.Iterator vpIter;
|
|
||||||
|
|
||||||
/* This test is NOT very complete */
|
|
||||||
unittest
|
|
||||||
{
|
|
||||||
LinkedList!(float) ll;
|
|
||||||
|
|
||||||
assert(ll.length == 0);
|
|
||||||
ll.Iterator it = ll.insert(10.4);
|
|
||||||
assert(ll.length == 1);
|
|
||||||
ll.insert(23);
|
|
||||||
it = ll.insert(6.3);
|
|
||||||
ll.insert(-1000);
|
|
||||||
|
|
||||||
assert(ll.length == 4);
|
|
||||||
|
|
||||||
//foreach(float f; ll) writefln(f);
|
|
||||||
|
|
||||||
ll.remove(it);
|
|
||||||
|
|
||||||
assert(ll.length == 3);
|
|
||||||
|
|
||||||
ll.reset();
|
|
||||||
|
|
||||||
assert(ll.length == 0);
|
|
||||||
|
|
||||||
//foreach(int i, float f; ll) writefln(i, " ", f);
|
|
||||||
}
|
|
||||||
//import std.stdio;
|
|
||||||
|
|
||||||
// Array allocator. TODO: Put this and Malloc in their own place,
|
|
||||||
// extend list to be the same quality as aa.d and make a system out of
|
|
||||||
// it. Make some better unit tests.
|
|
||||||
struct ArrAlloc
|
|
||||||
{
|
|
||||||
ubyte[] data;
|
|
||||||
uint pos;
|
|
||||||
|
|
||||||
void reset() { pos = 0; }
|
|
||||||
|
|
||||||
const bool autoinit = false;
|
|
||||||
const bool usefree = false;
|
|
||||||
|
|
||||||
void* alloc(uint size)
|
|
||||||
{
|
|
||||||
if(pos+size > data.length)
|
|
||||||
data.length = pos+size+30;
|
|
||||||
|
|
||||||
void * ptr = &data[pos];
|
|
||||||
|
|
||||||
pos += size;
|
|
||||||
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void free(void* p) { }
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2004, 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (string.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.util.string;
|
|
||||||
|
|
||||||
import std.utf;
|
|
||||||
import std.string;
|
|
||||||
|
|
||||||
// These functions check whether a string begins or ends with a
|
|
||||||
// certain substring.
|
|
||||||
bool begins(char[] str, char[] start)
|
|
||||||
{
|
|
||||||
if(str.length < start.length ||
|
|
||||||
str[0..start.length] != start) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ends(char[] str, char[] end)
|
|
||||||
{
|
|
||||||
if(str.length < end.length ||
|
|
||||||
str[$-end.length..$] != end) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Case insensitive versions of begins and ends
|
|
||||||
bool iBegins(char[] str, char[] start)
|
|
||||||
{
|
|
||||||
if(str.length < start.length ||
|
|
||||||
icmp(str[0..start.length], start) != 0) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool iEnds(char[] str, char[] end)
|
|
||||||
{
|
|
||||||
if(str.length < end.length ||
|
|
||||||
icmp(str[$-end.length..$], end) != 0) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] nextWord(ref char[] str, char delim = ' ')
|
|
||||||
{
|
|
||||||
int i = find(str, delim);
|
|
||||||
char[] result;
|
|
||||||
|
|
||||||
// No 'delim' found, return the entire string and set remainder to
|
|
||||||
// null.
|
|
||||||
if(i == -1)
|
|
||||||
{
|
|
||||||
result = str;
|
|
||||||
str = null;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// A separator was found. Return everything upto 'delim' (index i),
|
|
||||||
// put the remainder of the string (not including the char at [i])
|
|
||||||
// in str.
|
|
||||||
result = str[0..i];
|
|
||||||
str = str[i+1..$];
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Strip trailing zeros
|
|
||||||
char[] stripz(char [] s)
|
|
||||||
{
|
|
||||||
foreach(int i, char c; s)
|
|
||||||
if( c == 0 )
|
|
||||||
return s[0..i];
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a long integer into a string using nice comma
|
|
||||||
// formatting. delim is the delimiter character, size is the number of
|
|
||||||
// digits in each group. See the unittest for examples.
|
|
||||||
char[] comma(long i, char delim=',', int size = 3)
|
|
||||||
{
|
|
||||||
char[] str = toString(i);
|
|
||||||
char[] res;
|
|
||||||
|
|
||||||
if(i<0) str=str[1..$];
|
|
||||||
|
|
||||||
str.reverse;
|
|
||||||
foreach(int j, char c; str)
|
|
||||||
{
|
|
||||||
if(j!=0 && j%size == 0)
|
|
||||||
res = delim ~ res;
|
|
||||||
res = c ~ res;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(i<0) res = "-" ~ res;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
@ -1,369 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (arrays.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.arrays;
|
|
||||||
|
|
||||||
import monster.vm.stack;
|
|
||||||
import monster.util.freelist;
|
|
||||||
import monster.util.flags;
|
|
||||||
import monster.vm.error;
|
|
||||||
import monster.vm.mobject;
|
|
||||||
|
|
||||||
import std.string;
|
|
||||||
import std.uni;
|
|
||||||
import std.stdio;
|
|
||||||
import std.utf;
|
|
||||||
|
|
||||||
// An index to an array. Array indices may be 0, unlike object indices
|
|
||||||
// which span from 1 and upwards, and has 0 as the illegal 'null'
|
|
||||||
// reference. A null array will always refer to an empty array,
|
|
||||||
// because we insert an empty array at the first slot in the index
|
|
||||||
// list.
|
|
||||||
typedef int AIndex;
|
|
||||||
|
|
||||||
// Not all of these are used yet.
|
|
||||||
enum AFlags : int
|
|
||||||
{
|
|
||||||
None = 0x00,
|
|
||||||
Alive = 0x01, // This reference is not deleted
|
|
||||||
Const = 0x02, // Constant data
|
|
||||||
CanCollect = 0x04, // Can be colleted by the GC
|
|
||||||
RefCounted = 0x08, // Is reference counted
|
|
||||||
Marked = 0x10, // Was marked in the last GC sweep
|
|
||||||
Null = 0x20, // Is the null array
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ArrayRef
|
|
||||||
{
|
|
||||||
union
|
|
||||||
{
|
|
||||||
int[] iarr;
|
|
||||||
float[] farr;
|
|
||||||
dchar[] carr;
|
|
||||||
AIndex[] aarr;
|
|
||||||
}
|
|
||||||
Flags!(AFlags) flags;
|
|
||||||
|
|
||||||
uint elemSize; // Size of each element (in ints)
|
|
||||||
|
|
||||||
AIndex getIndex()
|
|
||||||
{
|
|
||||||
return cast(AIndex)( Arrays.ArrayList.getIndex(this) );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Array length, in terms of its element size
|
|
||||||
uint length()
|
|
||||||
{
|
|
||||||
if(isNull) return 0;
|
|
||||||
assert(elemSize != 0, "elemSize not set");
|
|
||||||
assert(iarr.length % elemSize == 0, "array length not divisible by element size");
|
|
||||||
return iarr.length / elemSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isAlive() { return flags.has(AFlags.Alive); }
|
|
||||||
bool isConst() { return flags.has(AFlags.Const); }
|
|
||||||
bool isNull() { return flags.has(AFlags.Null); }
|
|
||||||
}
|
|
||||||
|
|
||||||
Arrays arrays;
|
|
||||||
|
|
||||||
struct Arrays
|
|
||||||
{
|
|
||||||
alias FreeList!(ArrayRef) ArrayList;
|
|
||||||
|
|
||||||
private:
|
|
||||||
ArrayList arrList;
|
|
||||||
|
|
||||||
// Get a new array reference
|
|
||||||
ArrayRef *createArray()
|
|
||||||
{
|
|
||||||
ArrayRef *ar = arrList.getNew();
|
|
||||||
|
|
||||||
assert(!ar.isAlive);
|
|
||||||
|
|
||||||
// Set the "alive" flag
|
|
||||||
ar.flags.set(AFlags.Alive);
|
|
||||||
|
|
||||||
assert(!ar.isNull);
|
|
||||||
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put a reference back into the freelist
|
|
||||||
void destroyArray(ArrayRef *ar)
|
|
||||||
{
|
|
||||||
assert(ar.isAlive);
|
|
||||||
assert(!ar.isNull);
|
|
||||||
assert(!ar.isConst);
|
|
||||||
ar.flags.unset(AFlags.Alive);
|
|
||||||
arrList.remove(ar);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
// Set up this struct
|
|
||||||
void initialize()
|
|
||||||
{
|
|
||||||
// Make sure index zero is valid and is an empty array. Set
|
|
||||||
// more flags later.
|
|
||||||
auto ar = createArray();
|
|
||||||
ar.iarr = null;
|
|
||||||
ar.flags.set(AFlags.Null);
|
|
||||||
|
|
||||||
assert(ar.getIndex == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the reference to the empty array
|
|
||||||
ArrayRef *getZero()
|
|
||||||
{
|
|
||||||
return getRef(cast(AIndex)0);
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayRef *createT(T)(T[] data)
|
|
||||||
{
|
|
||||||
static if(T.sizeof == 4) return create(cast(int[])data, 1);
|
|
||||||
else static if(T.sizeof == 8) return create(cast(int[])data, 2);
|
|
||||||
else static assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
alias createT!(int) create;
|
|
||||||
alias createT!(uint) create;
|
|
||||||
alias createT!(long) create;
|
|
||||||
alias createT!(ulong) create;
|
|
||||||
alias createT!(float) create;
|
|
||||||
alias createT!(double) create;
|
|
||||||
alias createT!(dchar) create;
|
|
||||||
alias createT!(AIndex) create;
|
|
||||||
alias createT!(MIndex) create;
|
|
||||||
|
|
||||||
ArrayRef *create(char[] arg)
|
|
||||||
{ return create(toUTF32(arg)); }
|
|
||||||
|
|
||||||
// Generic element size
|
|
||||||
ArrayRef *create(int[] data, int size)
|
|
||||||
{
|
|
||||||
assert(size > 0);
|
|
||||||
|
|
||||||
if(data.length == 0) return getZero();
|
|
||||||
|
|
||||||
ArrayRef *ar = createArray();
|
|
||||||
ar.iarr = data;
|
|
||||||
ar.elemSize = size;
|
|
||||||
|
|
||||||
if(data.length % size != 0)
|
|
||||||
fail("Array length not divisible by element size");
|
|
||||||
|
|
||||||
return ar;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayRef *createConst(int[] data, int elem)
|
|
||||||
{
|
|
||||||
ArrayRef *arf = create(data, elem);
|
|
||||||
arf.flags.set(AFlags.Const);
|
|
||||||
return arf;
|
|
||||||
}
|
|
||||||
|
|
||||||
ArrayRef *getRef(AIndex index)
|
|
||||||
{
|
|
||||||
if(index < 0 || index >= getTotalArrays())
|
|
||||||
fail("Invalid array reference: " ~ toString(cast(int)index));
|
|
||||||
|
|
||||||
ArrayRef *arr = ArrayList.getNode(index);
|
|
||||||
|
|
||||||
if(!arr.isAlive)
|
|
||||||
fail("Dead array reference: " ~ toString(cast(int)index));
|
|
||||||
|
|
||||||
assert(arr.getIndex() == index);
|
|
||||||
|
|
||||||
if(index == 0) assert(arr.iarr.length == 0);
|
|
||||||
|
|
||||||
return arr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the number of array references in use
|
|
||||||
int getArrays()
|
|
||||||
{
|
|
||||||
return arrList.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the total number of Array references ever allocated for the
|
|
||||||
// free list.
|
|
||||||
int getTotalArrays()
|
|
||||||
{
|
|
||||||
return ArrayList.totLength();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a multi-dimensional array of rank 'rank' and innermost data
|
|
||||||
// initialized to 'initval'. The array lengths are popped of the
|
|
||||||
// script stack.
|
|
||||||
void createMultiDimArray(int rank, int init[])
|
|
||||||
{
|
|
||||||
if(rank <= 0 || rank >= 30)
|
|
||||||
fail("Invalid array nesting number " ~ toString(rank));
|
|
||||||
|
|
||||||
assert(init.length > 0);
|
|
||||||
|
|
||||||
int[30] lenbuf;
|
|
||||||
int[] lens = lenbuf[0..rank];
|
|
||||||
int[] data; // All the elements + overhead data
|
|
||||||
ulong totElem = 1; // Total number of elements. Set to 1 and
|
|
||||||
// multiplied with the length later.
|
|
||||||
ulong totSize = 0; // Total size of data to allocate
|
|
||||||
|
|
||||||
int[] currSlice; // Current slice of the data, used by getNext.
|
|
||||||
|
|
||||||
// Get the next 'count' ints of data, in the form of a newly created
|
|
||||||
// ArrayRef.
|
|
||||||
ArrayRef *getNext(int count, int elemSize=1)
|
|
||||||
{
|
|
||||||
assert(count <= currSlice.length);
|
|
||||||
|
|
||||||
int[] res = currSlice[0..count];
|
|
||||||
|
|
||||||
currSlice = currSlice[count..$];
|
|
||||||
|
|
||||||
return arrays.create(res, elemSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the lengths, and calculate how much data we need. The first
|
|
||||||
// length is the outermost wrapper, the last is the number of
|
|
||||||
// actual elements in the innermost array wrapper.
|
|
||||||
foreach(int i, ref int len; lens)
|
|
||||||
{
|
|
||||||
len = stack.popInt();
|
|
||||||
|
|
||||||
// Do some sanity check on the length. The upper bound set here
|
|
||||||
// is pretty arbitrary, we might enlarge it later.
|
|
||||||
if(len <= 0 || len > 0x100000)
|
|
||||||
fail("Invalid array length " ~ toString(len));
|
|
||||||
|
|
||||||
// We could allow 0-length arrays here, but there's not much
|
|
||||||
// point really.
|
|
||||||
|
|
||||||
// Calculate in the element size in the last element
|
|
||||||
if(i == lens.length-1) len *= init.length;
|
|
||||||
|
|
||||||
// The total data is the cumulative value of totElem through all
|
|
||||||
// iterations. For example, if we have a k*m*n array, we must
|
|
||||||
// have k outer arrays, indexing a total of k*m subarrays,
|
|
||||||
// indexing a total of k*m*n elements. The total data size,
|
|
||||||
// assuming element sizes have been figured in, is
|
|
||||||
// k + k*m + k*m*n.
|
|
||||||
totElem *= len;
|
|
||||||
totSize += totElem;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate all the elements + overhead (data for the lookup arrays)
|
|
||||||
if(totSize)
|
|
||||||
{
|
|
||||||
assert(totElem >= 0 && totElem <= totSize);
|
|
||||||
|
|
||||||
// Let's slap a 10 meg sanity check on the total data size
|
|
||||||
if(totSize > 10*1024*1024)
|
|
||||||
fail("Total array size is too large: " ~ toString(totSize));
|
|
||||||
|
|
||||||
data.length = totSize;
|
|
||||||
|
|
||||||
// Set currSlice to point to the entire data
|
|
||||||
currSlice = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up inner arrays recursively. This can be optimized heavily
|
|
||||||
// later (removing recursion, moving if-tests out of loops, avoiding
|
|
||||||
// double initialization, and so on.)
|
|
||||||
void setupArray(int lenIndex, ArrayRef *arr)
|
|
||||||
{
|
|
||||||
// Length of arrays at this level
|
|
||||||
int len = lens[lenIndex];
|
|
||||||
|
|
||||||
// Loop through the previous level and create the arrays of this level
|
|
||||||
foreach(ref AIndex ind; arr.aarr)
|
|
||||||
{
|
|
||||||
ArrayRef *narr;
|
|
||||||
if(lenIndex == rank-1)
|
|
||||||
// Remember to set the element size on the inner level
|
|
||||||
narr = getNext(len, init.length);
|
|
||||||
else
|
|
||||||
narr = getNext(len);
|
|
||||||
|
|
||||||
// Store the index or this array in the previous level
|
|
||||||
ind = narr.getIndex();
|
|
||||||
|
|
||||||
// Is this the innermost level?
|
|
||||||
if(lenIndex == rank-1)
|
|
||||||
{
|
|
||||||
// If so, this is an array of elements. Initialize them.
|
|
||||||
if(init.length == 1) narr.iarr[] = init[0];
|
|
||||||
else if(init.length == 2) (cast(long[])narr.iarr)[] = *(cast(long*)init.ptr);
|
|
||||||
else
|
|
||||||
for(int i=0; i<lens[0]; i+=init.length)
|
|
||||||
arr.iarr[i..i+init.length] = init[];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
// If not, set up the indices in this array
|
|
||||||
setupArray(lenIndex+1, narr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(rank > 1)
|
|
||||||
{
|
|
||||||
// Create outer array and push it
|
|
||||||
ArrayRef *arr = getNext(lens[0]);
|
|
||||||
stack.pushArray(arr);
|
|
||||||
|
|
||||||
// Recursively set up the sub-arrays
|
|
||||||
setupArray(1, arr);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Create outer array and push it. Element size has already been
|
|
||||||
// multiplied into the length.
|
|
||||||
ArrayRef *arr = getNext(lens[0], init.length);
|
|
||||||
stack.pushArray(arr);
|
|
||||||
|
|
||||||
// There is only one array level, so this IS the inner
|
|
||||||
// array. Initialize the elements. Optimize for element sizes 1
|
|
||||||
// and 2
|
|
||||||
if(init.length == 1) arr.iarr[] = init[0];
|
|
||||||
else if(init.length == 2) (cast(long[])arr.iarr)[] = *(cast(long*)init.ptr);
|
|
||||||
else
|
|
||||||
for(int i=0; i<lens[0]; i+=init.length)
|
|
||||||
arr.iarr[i..i+init.length] = init[];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Make sure we used all the data!
|
|
||||||
assert(currSlice.length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// There's no phobos function that does unicode case insensitive
|
|
||||||
// string comparison, so let's make one ourselves. This can probably
|
|
||||||
// be optimized. toUniLower is in prinicple an expensive operation,
|
|
||||||
// but not so much if we assume most characters are ascii.
|
|
||||||
bool isUniCaseEqual(dchar[] a, dchar[] b)
|
|
||||||
{
|
|
||||||
if(a.length != b.length) return false;
|
|
||||||
foreach(int i, dchar ch; a)
|
|
||||||
if(ch != b[i] && toUniLower(ch) != toUniLower(b[i]))
|
|
||||||
return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (codestream.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.codestream;
|
|
||||||
|
|
||||||
import std.string;
|
|
||||||
import std.stdio;
|
|
||||||
import monster.vm.error;
|
|
||||||
|
|
||||||
// CodeStream is a simple utility structure for reading data
|
|
||||||
// sequentially. It holds a piece of byte compiled code, and keeps
|
|
||||||
// track of the position within the code.
|
|
||||||
struct CodeStream
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
ubyte[] data;
|
|
||||||
int len;
|
|
||||||
ubyte *pos;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
void setData(ubyte[] data)
|
|
||||||
{
|
|
||||||
this.data = data;
|
|
||||||
len = data.length;
|
|
||||||
pos = data.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when the end of the stream was unexpectedly encountered
|
|
||||||
void eos(char[] func)
|
|
||||||
{
|
|
||||||
char[] res = format("Premature end of input: %s() missing %s byte(s)",
|
|
||||||
func, -len);
|
|
||||||
fail(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Jump to given position
|
|
||||||
void jump(int newPos)
|
|
||||||
{
|
|
||||||
if(newPos<0 || newPos>=data.length)
|
|
||||||
fail("Jump out of range");
|
|
||||||
len = data.length - newPos;
|
|
||||||
pos = &data[newPos];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current position
|
|
||||||
int getPos()
|
|
||||||
{
|
|
||||||
return pos-data.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ubyte get()
|
|
||||||
{
|
|
||||||
if(len--) return *(pos++);
|
|
||||||
eos("get");
|
|
||||||
}
|
|
||||||
|
|
||||||
int getInt()
|
|
||||||
{
|
|
||||||
len -= 4;
|
|
||||||
if(len < 0) eos("getInt");
|
|
||||||
int i = *(cast(int*)pos);
|
|
||||||
pos+=4;
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a slice of the 'size' next ints
|
|
||||||
int[] getIntArray(uint size)
|
|
||||||
{
|
|
||||||
size *=4; // Convert size to bytes
|
|
||||||
len -= size;
|
|
||||||
if(len < 0) eos("getArray");
|
|
||||||
int[] res = cast(int[])pos[0..size];
|
|
||||||
pos += size;
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,162 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (dbg.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.dbg;
|
|
||||||
|
|
||||||
import monster.vm.thread;
|
|
||||||
import std.stream;
|
|
||||||
import std.string;
|
|
||||||
import std.cstream;
|
|
||||||
import monster.vm.fstack;
|
|
||||||
import monster.options;
|
|
||||||
|
|
||||||
/*
|
|
||||||
This file is used for runtime debugging, stack tracing, etc.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
Create a trace for your function that automatically pops itself of
|
|
||||||
the fstack when it goes out of scope. Usage:
|
|
||||||
|
|
||||||
auto scope _t = new MTrace("funcName");
|
|
||||||
|
|
||||||
This is one of the few places where C++ is actually easier to use...
|
|
||||||
|
|
||||||
An alternative way to do the same thing:
|
|
||||||
|
|
||||||
dbg.trace("funcName");
|
|
||||||
scope(exit) dbg.untrace();
|
|
||||||
*/
|
|
||||||
|
|
||||||
scope class MTrace
|
|
||||||
{
|
|
||||||
this(char[] str) { dbg.trace(str); }
|
|
||||||
~this() { dbg.untrace(); }
|
|
||||||
}
|
|
||||||
|
|
||||||
Dbg dbg;
|
|
||||||
|
|
||||||
struct Dbg
|
|
||||||
{
|
|
||||||
Stream dbgOut = null;
|
|
||||||
|
|
||||||
// Add indentation for each function stack level (only works if
|
|
||||||
// logFStack is true in options.d)
|
|
||||||
char[] logLevelString = "| ";
|
|
||||||
|
|
||||||
// Called at startup
|
|
||||||
void init()
|
|
||||||
{
|
|
||||||
static if(defaultLogToStdout)
|
|
||||||
enableStdout();
|
|
||||||
}
|
|
||||||
|
|
||||||
void enableStdout()
|
|
||||||
{
|
|
||||||
dbgOut = dout;
|
|
||||||
}
|
|
||||||
|
|
||||||
void log(char[] msg, Thread *tr = null)
|
|
||||||
{
|
|
||||||
if(dbgOut !is null)
|
|
||||||
{
|
|
||||||
int logLevel = getFStackLevel();
|
|
||||||
int extLevel = getExtLevel();
|
|
||||||
int trdIndex = getThreadIndex(tr);
|
|
||||||
|
|
||||||
char[] str = format("(trd=%s,ext=%s,lev=%s)",
|
|
||||||
trdIndex,
|
|
||||||
extLevel,
|
|
||||||
logLevel);
|
|
||||||
str = format("%-24s", str);
|
|
||||||
dbgOut.writeString(str);
|
|
||||||
|
|
||||||
// If we're logging function stack activity, put in some fancy
|
|
||||||
// indentation as well.
|
|
||||||
static if(logFStack)
|
|
||||||
{
|
|
||||||
for(int i;i<logLevel+extLevel;i++)
|
|
||||||
dbgOut.writeString(logLevelString);
|
|
||||||
}
|
|
||||||
|
|
||||||
dbgOut.writeLine(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stack tracing functions. These might be used internally in the
|
|
||||||
// engine, so they should not be disabled.
|
|
||||||
|
|
||||||
// Add an external function to the stack
|
|
||||||
void trace(char[] name)
|
|
||||||
{
|
|
||||||
getFStack().pushExt(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop the last function pushed by trace()
|
|
||||||
void untrace()
|
|
||||||
{
|
|
||||||
auto fs = getFStack();
|
|
||||||
assert(fs.cur !is null && fs.cur.isExternal,
|
|
||||||
"vm.untrace() used on a non-external function stack entry");
|
|
||||||
fs.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the current function stack printout
|
|
||||||
char[] getTrace()
|
|
||||||
{ return getFStack().toString(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
int getExtLevel()
|
|
||||||
{
|
|
||||||
return externals.list.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getFStackLevel()
|
|
||||||
{
|
|
||||||
if(cthread !is null)
|
|
||||||
return cthread.fstack.list.length;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getThreadIndex(Thread *tr = null)
|
|
||||||
{
|
|
||||||
if(tr !is null)
|
|
||||||
return tr.getIndex();
|
|
||||||
else if(cthread !is null)
|
|
||||||
return cthread.getIndex();
|
|
||||||
else return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the active function stack, or the externals stack if no
|
|
||||||
// thread is active.
|
|
||||||
FunctionStack *getFStack()
|
|
||||||
{
|
|
||||||
if(cthread !is null)
|
|
||||||
{
|
|
||||||
assert(!cthread.fstack.isEmpty);
|
|
||||||
return &cthread.fstack;
|
|
||||||
}
|
|
||||||
return &externals;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (error.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.error;
|
|
||||||
|
|
||||||
import monster.compiler.tokenizer;
|
|
||||||
version(Tango) import tango.core.Exception;
|
|
||||||
import std.string;
|
|
||||||
|
|
||||||
class MonsterException : Exception
|
|
||||||
{
|
|
||||||
this(char[] msg) { super(/*"MonsterException: " ~*/ msg); }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Source file location
|
|
||||||
struct Floc
|
|
||||||
{
|
|
||||||
int line = -1;
|
|
||||||
char[] fname;
|
|
||||||
|
|
||||||
char[] toString() { return format("%s:%s", fname, line); }
|
|
||||||
}
|
|
||||||
|
|
||||||
void fail(char[] msg, Floc loc)
|
|
||||||
{
|
|
||||||
fail(msg, loc.fname, loc.line);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fail(char[] msg, char[] fname, int line)
|
|
||||||
{
|
|
||||||
if(line != -1)
|
|
||||||
fail(format("%s:%s: %s", fname, line, msg));
|
|
||||||
else
|
|
||||||
fail(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fail(char[] msg)
|
|
||||||
{
|
|
||||||
throw new MonsterException(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
void fail(char[] msg, TokenArray toks)
|
|
||||||
{
|
|
||||||
if(toks.length)
|
|
||||||
fail(msg ~ ", found " ~ toks[0].str, toks[0].loc);
|
|
||||||
else
|
|
||||||
fail(msg ~ ", found end of file");
|
|
||||||
}
|
|
@ -1,389 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (fstack.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.fstack;
|
|
||||||
|
|
||||||
import monster.vm.codestream;
|
|
||||||
import monster.vm.mobject;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.stack;
|
|
||||||
import monster.vm.error;
|
|
||||||
import monster.vm.thread;
|
|
||||||
import monster.vm.dbg;
|
|
||||||
import monster.compiler.states;
|
|
||||||
import monster.compiler.functions;
|
|
||||||
import monster.compiler.linespec;
|
|
||||||
|
|
||||||
import monster.options;
|
|
||||||
import monster.util.freelist;
|
|
||||||
|
|
||||||
import std.stdio;
|
|
||||||
import std.string;
|
|
||||||
|
|
||||||
// "friendly" parameter and stack handling.
|
|
||||||
enum SPType
|
|
||||||
{
|
|
||||||
Function, // A function (script or native)
|
|
||||||
Idle, // Idle function
|
|
||||||
State, // State code
|
|
||||||
External, // An external function represented on the function stack
|
|
||||||
}
|
|
||||||
|
|
||||||
// One entry in the function stack
|
|
||||||
struct StackPoint
|
|
||||||
{
|
|
||||||
CodeStream code; // The byte code handler
|
|
||||||
|
|
||||||
union
|
|
||||||
{
|
|
||||||
Function *func; // What function we are in (if any)
|
|
||||||
State *state; // What state the function belongs to (if any)
|
|
||||||
char[] extName; // Name of external function
|
|
||||||
}
|
|
||||||
|
|
||||||
SPType ftype;
|
|
||||||
|
|
||||||
MonsterObject *obj; // "this"-pointer for the function
|
|
||||||
|
|
||||||
// Get the class owning the function
|
|
||||||
MonsterClass getCls()
|
|
||||||
{
|
|
||||||
assert(isFunc || isState);
|
|
||||||
assert(func !is null);
|
|
||||||
return func.owner;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isStatic()
|
|
||||||
{ return isFunc() && func.isStatic; }
|
|
||||||
|
|
||||||
bool isFunc()
|
|
||||||
{ return (ftype == SPType.Function) || (ftype == SPType.Idle); }
|
|
||||||
|
|
||||||
bool isState()
|
|
||||||
{ return ftype == SPType.State; }
|
|
||||||
|
|
||||||
bool isIdle()
|
|
||||||
{ return ftype == SPType.Idle; }
|
|
||||||
|
|
||||||
bool isNative()
|
|
||||||
{ return isFunc && func.isNative; }
|
|
||||||
|
|
||||||
bool isNormal()
|
|
||||||
{ return isState || (isFunc && func.isNormal); }
|
|
||||||
|
|
||||||
bool isExternal()
|
|
||||||
{ return ftype == SPType.External; }
|
|
||||||
|
|
||||||
// Get the current source position (file name and line
|
|
||||||
// number). Mostly used for error messages.
|
|
||||||
Floc getFloc()
|
|
||||||
{
|
|
||||||
assert(isFunc || isState);
|
|
||||||
|
|
||||||
Floc fl;
|
|
||||||
fl.fname = getCls().name.loc.fname;
|
|
||||||
|
|
||||||
// Subtract one to make sure we get the last instruction executed,
|
|
||||||
// not the next.
|
|
||||||
int pos = code.getPos() - 1;
|
|
||||||
if(pos < 0) pos = 0;
|
|
||||||
|
|
||||||
if(isFunc)
|
|
||||||
fl.line = findLine(func.lines, pos);
|
|
||||||
else
|
|
||||||
fl.line = findLine(state.lines, pos);
|
|
||||||
|
|
||||||
return fl;
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] toString()
|
|
||||||
{
|
|
||||||
if(isExternal)
|
|
||||||
return "external " ~ extName;
|
|
||||||
|
|
||||||
assert(func !is null);
|
|
||||||
|
|
||||||
char[] type, cls, name;
|
|
||||||
cls = getCls().name.str;
|
|
||||||
if(isState)
|
|
||||||
{
|
|
||||||
type = "state";
|
|
||||||
name = state.name.str;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
assert(isFunc);
|
|
||||||
name = func.name.str;
|
|
||||||
|
|
||||||
if(isIdle) type = "idle";
|
|
||||||
else if(isNormal) type = "script";
|
|
||||||
else if(isNative) type = "native";
|
|
||||||
else assert(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function location and name
|
|
||||||
return format("%s %s.%s", type, cls, name);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alias FreeList!(StackPoint) StackList;
|
|
||||||
alias StackList.TNode *StackNode;
|
|
||||||
|
|
||||||
// External functions that are pushed when there is no active
|
|
||||||
// thread. These will not prevent any future thread from being put in
|
|
||||||
// the background.
|
|
||||||
FunctionStack externals;
|
|
||||||
|
|
||||||
struct FunctionStack
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
|
|
||||||
// Number of native functions on the stack
|
|
||||||
int natives;
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
StackList list;
|
|
||||||
|
|
||||||
// The current entry
|
|
||||||
StackPoint *cur = null;
|
|
||||||
|
|
||||||
// Consistancy checks
|
|
||||||
invariant()
|
|
||||||
{
|
|
||||||
if(cur !is null)
|
|
||||||
{
|
|
||||||
assert(list.length > 0);
|
|
||||||
if(cur.ftype == SPType.State)
|
|
||||||
assert(list.length == 1);
|
|
||||||
}
|
|
||||||
else assert(list.length == 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the thread associated with this function stack. Depends on
|
|
||||||
// the fact that we are the first member of the Thread, so our
|
|
||||||
// pointers are the same. If this changes, you MUST change this
|
|
||||||
// function as well.
|
|
||||||
Thread *getThread()
|
|
||||||
{
|
|
||||||
return cast(Thread*)this;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Used for debug logging
|
|
||||||
void log(char[] msg)
|
|
||||||
{
|
|
||||||
static if(logFStack)
|
|
||||||
{
|
|
||||||
dbg.log(msg, getThread());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void killAll()
|
|
||||||
{
|
|
||||||
natives = 0;
|
|
||||||
while(list.length)
|
|
||||||
{
|
|
||||||
assert(cur !is null);
|
|
||||||
list.remove(cur);
|
|
||||||
cur = list.getHead();
|
|
||||||
}
|
|
||||||
assert(cur is null);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool hasNatives() { return natives != 0; }
|
|
||||||
|
|
||||||
// Check if the function calling us is a normal function
|
|
||||||
bool isNormal()
|
|
||||||
{
|
|
||||||
return cur !is null && cur.isNormal;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is the function stack empty?
|
|
||||||
bool isEmpty() { return cur is null; }
|
|
||||||
|
|
||||||
bool isIdle() { return cur !is null && cur.ftype == SPType.Idle; }
|
|
||||||
|
|
||||||
// Are we currently running state code?
|
|
||||||
bool isStateCode() { return list.length == 1 && cur.ftype == SPType.State; }
|
|
||||||
|
|
||||||
// Sets up the next stack point and assigns the given object
|
|
||||||
private void push(MonsterObject *obj)
|
|
||||||
{
|
|
||||||
if(list.length >= maxFStack)
|
|
||||||
fail("Function stack overflow - infinite recursion?");
|
|
||||||
|
|
||||||
assert(cur is null || !cur.isIdle,
|
|
||||||
"Cannot call other script functions from an idle function");
|
|
||||||
|
|
||||||
// Puts a new node at the beginning of the list
|
|
||||||
cur = list.getNew();
|
|
||||||
cur.obj = obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the stack point up as a function. Allows obj to be null.
|
|
||||||
void push(Function *func, MonsterObject *obj)
|
|
||||||
{
|
|
||||||
push(obj);
|
|
||||||
|
|
||||||
assert(func !is null);
|
|
||||||
cur.ftype = SPType.Function;
|
|
||||||
cur.func = func;
|
|
||||||
|
|
||||||
assert(func.owner !is null);
|
|
||||||
assert(obj is null || func.owner.parentOf(obj.cls));
|
|
||||||
|
|
||||||
// Point the code stream to the byte code, if any.
|
|
||||||
if(func.isNormal)
|
|
||||||
cur.code.setData(func.bcode);
|
|
||||||
else if(func.isNative)
|
|
||||||
natives++;
|
|
||||||
|
|
||||||
assert(!func.isIdle, "don't use fstack.push() on idle functions");
|
|
||||||
|
|
||||||
static if(logFStack)
|
|
||||||
{
|
|
||||||
log("+++ " ~ cur.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set the stack point up as a state
|
|
||||||
void push(State *st, MonsterObject *obj)
|
|
||||||
{
|
|
||||||
assert(st !is null);
|
|
||||||
assert(isEmpty,
|
|
||||||
"state code can only run at the bottom of the function stack");
|
|
||||||
|
|
||||||
push(obj);
|
|
||||||
cur.ftype = SPType.State;
|
|
||||||
cur.state = st;
|
|
||||||
|
|
||||||
assert(obj !is null);
|
|
||||||
assert(st.owner !is null);
|
|
||||||
assert(st.owner.parentOf(obj.cls));
|
|
||||||
|
|
||||||
// Set up the byte code
|
|
||||||
cur.code.setData(st.bcode);
|
|
||||||
|
|
||||||
static if(logFStack)
|
|
||||||
{
|
|
||||||
log("+++ " ~ cur.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push an external (non-scripted) function on the function
|
|
||||||
// stack.
|
|
||||||
void pushExt(char[] name)
|
|
||||||
{
|
|
||||||
push(null);
|
|
||||||
natives++;
|
|
||||||
cur.ftype = SPType.External;
|
|
||||||
cur.extName = name;
|
|
||||||
|
|
||||||
static if(logFStack)
|
|
||||||
{
|
|
||||||
log("+++ " ~ cur.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void pushIdle(Function *func, MonsterObject *obj)
|
|
||||||
{
|
|
||||||
push(obj);
|
|
||||||
assert(func !is null);
|
|
||||||
cur.func = func;
|
|
||||||
cur.ftype = SPType.Idle;
|
|
||||||
|
|
||||||
assert(func.owner !is null);
|
|
||||||
assert(obj is null || func.owner.parentOf(obj.cls));
|
|
||||||
assert(func.isIdle, func.name.str ~ "() is not an idle function");
|
|
||||||
|
|
||||||
static if(logFStack)
|
|
||||||
{
|
|
||||||
log("+++ " ~ cur.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pops one entry of the stack. Checks that the stack level has been
|
|
||||||
// returned to the correct position.
|
|
||||||
void pop()
|
|
||||||
{
|
|
||||||
if(isEmpty)
|
|
||||||
fail("Function stack underflow");
|
|
||||||
|
|
||||||
assert(list.length >= 1);
|
|
||||||
|
|
||||||
if(cur.isNative || cur.isExternal)
|
|
||||||
natives--;
|
|
||||||
assert(natives >= 0);
|
|
||||||
|
|
||||||
static if(logFStack)
|
|
||||||
{
|
|
||||||
log(" -- " ~ cur.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the topmost node from the list, and set cur.
|
|
||||||
assert(cur == list.getHead());
|
|
||||||
list.remove(cur);
|
|
||||||
cur = list.getHead();
|
|
||||||
|
|
||||||
assert(list.length != 0 || cur is null);
|
|
||||||
|
|
||||||
static if(logFStack)
|
|
||||||
{
|
|
||||||
log("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get a stack trace (pretty basic at the moment)
|
|
||||||
char[] toString()
|
|
||||||
{
|
|
||||||
char[] res;
|
|
||||||
|
|
||||||
int i;
|
|
||||||
foreach(ref c; list)
|
|
||||||
{
|
|
||||||
char[] msg;
|
|
||||||
if(i == 0)
|
|
||||||
msg = " (<---- current function)";
|
|
||||||
else if(i == list.length-1)
|
|
||||||
msg = " (<---- first Monster function)";
|
|
||||||
res ~= c.toString ~ msg ~ '\n';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we're not the externals list, add that one too
|
|
||||||
i = 0;
|
|
||||||
if(this !is &externals)
|
|
||||||
{
|
|
||||||
foreach(ref c; externals.list)
|
|
||||||
{
|
|
||||||
char[] msg;
|
|
||||||
if(i == externals.list.length-1)
|
|
||||||
msg = " (<---- first external function)";
|
|
||||||
res ~= c.toString ~ msg ~ '\n';
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Trace:\n" ~ res;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (gc.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This file will eventually contain the garbage collector.
|
|
||||||
module monster.vm.gc;
|
|
@ -1,81 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (idlefunction.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.idlefunction;
|
|
||||||
|
|
||||||
import monster.vm.thread;
|
|
||||||
|
|
||||||
// Idle scheduling actions - returned from initiate()
|
|
||||||
enum IS
|
|
||||||
{
|
|
||||||
Poll, // Poll the hasFinished function regularily
|
|
||||||
Return, // Return to the thread immediately - reenter() is called
|
|
||||||
// first
|
|
||||||
Kill, // Kill the thread
|
|
||||||
Manual, // Handle the scheduling ourselves. We have to schedule
|
|
||||||
// or move the thread to another ThreadList before the
|
|
||||||
// end of initiate()
|
|
||||||
}
|
|
||||||
|
|
||||||
// A callback class for idle functions. A child object of this class
|
|
||||||
// is what you "bind" to idle functions (rather than just a delegate,
|
|
||||||
// like for native functions.) Note that instances are not bound to
|
|
||||||
// specific script objects or threads; one idle function instance may
|
|
||||||
// be called for many objects / threads simultaneously. Any data
|
|
||||||
// specific to this call (such as parameters) must be stored
|
|
||||||
// elsewhere, usually within the Thread.
|
|
||||||
abstract class IdleFunction
|
|
||||||
{
|
|
||||||
// This is called immediately after the idle function is "called"
|
|
||||||
// from M script. It has to handle function parameters (remove them
|
|
||||||
// from the stack), but otherwise does not have to do
|
|
||||||
// anything. Return true if the scheduler should put this idle
|
|
||||||
// function into the condition list, which is usually a good
|
|
||||||
// idea. For functions which never "return", and for event driven
|
|
||||||
// idle functions (which handle their own scheduling), you should
|
|
||||||
// return false.
|
|
||||||
abstract IS initiate(Thread*);
|
|
||||||
|
|
||||||
// This is called whenever the idle function is about to "return" to
|
|
||||||
// state code. It has to push the return value, if any, but
|
|
||||||
// otherwise it can be empty. Note that if the idle function is
|
|
||||||
// aborted (eg. the state is changed), this function is never
|
|
||||||
// called, and abort() is called instead.
|
|
||||||
void reentry(Thread*) {}
|
|
||||||
|
|
||||||
// Called whenever an idle function is aborted, for example by a
|
|
||||||
// state change. No action is usually required.
|
|
||||||
void abort(Thread*) {}
|
|
||||||
|
|
||||||
// The condition that determines if this function has finished. This
|
|
||||||
// is the main method by which the scheduler determines when to
|
|
||||||
// reenter M state code. For example, for an idle function
|
|
||||||
// waitSoundFinish(), this would return false if the sound is still
|
|
||||||
// playing, and true if the sound has finished. If you want a purely
|
|
||||||
// event-driven idle function (rather than polling each frame), you
|
|
||||||
// should return false in initiate and instead reschedule the object
|
|
||||||
// manually when the event occurs. (A nice interface for this has
|
|
||||||
// not been created yet, though.)
|
|
||||||
bool hasFinished(Thread*) { assert(0, "empty hasFinished()"); }
|
|
||||||
}
|
|
@ -1,150 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (init.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// This module makes sure that the library is initialized in all
|
|
||||||
// cases.
|
|
||||||
module monster.vm.init;
|
|
||||||
|
|
||||||
import monster.compiler.tokenizer;
|
|
||||||
import monster.compiler.properties;
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.vm.thread;
|
|
||||||
import monster.vm.stack;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.arrays;
|
|
||||||
import monster.vm.vm;
|
|
||||||
import monster.vm.dbg;
|
|
||||||
|
|
||||||
import monster.modules.all;
|
|
||||||
import monster.options;
|
|
||||||
|
|
||||||
version(Tango)
|
|
||||||
{}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
// D runtime stuff
|
|
||||||
version(Posix)
|
|
||||||
{
|
|
||||||
extern (C) void _STI_monitor_staticctor();
|
|
||||||
//extern (C) void _STD_monitor_staticdtor();
|
|
||||||
extern (C) void _STI_critical_init();
|
|
||||||
//extern (C) void _STD_critical_term();
|
|
||||||
}
|
|
||||||
version(Win32)
|
|
||||||
{
|
|
||||||
extern (C) void _minit();
|
|
||||||
}
|
|
||||||
extern (C) void gc_init();
|
|
||||||
//extern (C) void gc_term();
|
|
||||||
extern (C) void _moduleCtor();
|
|
||||||
//extern (C) void _moduleDtor();
|
|
||||||
extern (C) void _moduleUnitTests();
|
|
||||||
|
|
||||||
//extern (C) bool no_catch_exceptions;
|
|
||||||
|
|
||||||
} // end version(Tango) .. else
|
|
||||||
|
|
||||||
|
|
||||||
bool initHasRun = false;
|
|
||||||
|
|
||||||
bool stHasRun = false;
|
|
||||||
|
|
||||||
static this()
|
|
||||||
{
|
|
||||||
assert(!stHasRun);
|
|
||||||
stHasRun = true;
|
|
||||||
|
|
||||||
// While we're here, run the initializer right away if it hasn't run
|
|
||||||
// already.
|
|
||||||
if(!initHasRun)
|
|
||||||
doMonsterInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
void doMonsterInit()
|
|
||||||
{
|
|
||||||
// Prevent recursion
|
|
||||||
assert(!initHasRun, "doMonsterInit should never run more than once");
|
|
||||||
initHasRun = true;
|
|
||||||
|
|
||||||
// First check if D has been initialized.
|
|
||||||
if(!stHasRun)
|
|
||||||
{
|
|
||||||
// Nope. This is normal though if we're running as a C++
|
|
||||||
// library. We have to init the D runtime manually.
|
|
||||||
|
|
||||||
// But this is not supported in Tango at the moment.
|
|
||||||
version(Tango)
|
|
||||||
{
|
|
||||||
assert(0, "tango-compiled C++ library not supported yet");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
version (Posix)
|
|
||||||
{
|
|
||||||
_STI_monitor_staticctor();
|
|
||||||
_STI_critical_init();
|
|
||||||
}
|
|
||||||
|
|
||||||
gc_init();
|
|
||||||
|
|
||||||
version (Win32)
|
|
||||||
{
|
|
||||||
_minit();
|
|
||||||
}
|
|
||||||
|
|
||||||
_moduleCtor();
|
|
||||||
_moduleUnitTests();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(stHasRun, "D library initializion failed");
|
|
||||||
|
|
||||||
// Next, initialize the Monster library
|
|
||||||
|
|
||||||
// Initialize the debugger structure
|
|
||||||
dbg.init();
|
|
||||||
|
|
||||||
// Initialize tokenizer
|
|
||||||
initTokenizer();
|
|
||||||
|
|
||||||
// initScope depends on doVMInit setting vm.vfs
|
|
||||||
vm.doVMInit();
|
|
||||||
initScope();
|
|
||||||
|
|
||||||
// The rest of the VM
|
|
||||||
scheduler.init();
|
|
||||||
stack.init();
|
|
||||||
arrays.initialize();
|
|
||||||
|
|
||||||
// Compiles the 'Object' class
|
|
||||||
MonsterClass.initialize();
|
|
||||||
|
|
||||||
// Depends on 'Object'
|
|
||||||
initProperties();
|
|
||||||
|
|
||||||
// Load modules
|
|
||||||
static if(loadModules)
|
|
||||||
initAllModules();
|
|
||||||
}
|
|
@ -1,281 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (iterators.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.iterators;
|
|
||||||
|
|
||||||
import monster.util.freelist;
|
|
||||||
import monster.vm.error;
|
|
||||||
import monster.vm.arrays;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.mobject;
|
|
||||||
import monster.util.flags;
|
|
||||||
|
|
||||||
import std.string;
|
|
||||||
import std.stdio;
|
|
||||||
|
|
||||||
// An iterator index.
|
|
||||||
typedef int IIndex;
|
|
||||||
|
|
||||||
// Flags for iterator structs
|
|
||||||
enum IFlags
|
|
||||||
{
|
|
||||||
None = 0x00,
|
|
||||||
Alive = 0x01, // This reference is not deleted
|
|
||||||
}
|
|
||||||
|
|
||||||
struct IteratorRef
|
|
||||||
{
|
|
||||||
Flags!(IFlags) flags;
|
|
||||||
|
|
||||||
ArrayRef *array;
|
|
||||||
int index; // TODO: Might not be necessary to keep a local copy of this
|
|
||||||
int indexMul; // Index multiplied with element size
|
|
||||||
int elemSize;
|
|
||||||
int *sindex; // Index on the stack
|
|
||||||
int[] sval; // Value on the stack
|
|
||||||
|
|
||||||
bool isReverse, isRef;
|
|
||||||
bool isClass;
|
|
||||||
|
|
||||||
MonsterObject *mo;
|
|
||||||
MonsterClass mc;
|
|
||||||
|
|
||||||
// Array iterators
|
|
||||||
bool firstArray(bool irev, bool iref, int *stk)
|
|
||||||
{
|
|
||||||
isRef = iref;
|
|
||||||
isReverse = irev;
|
|
||||||
isClass = false;
|
|
||||||
|
|
||||||
// Replace the array index on the stack
|
|
||||||
AIndex ai = cast(AIndex)*stk;
|
|
||||||
*stk = cast(int) getIndex();
|
|
||||||
|
|
||||||
// Fetch the array
|
|
||||||
array = arrays.getRef(ai);
|
|
||||||
|
|
||||||
// Cannot use reference values on const arrays
|
|
||||||
if(array.isConst && isRef)
|
|
||||||
// TODO: Try to give line and file in all messages
|
|
||||||
fail("Cannot use 'ref' values with constant arrays");
|
|
||||||
|
|
||||||
// Skip the loop if it's empty
|
|
||||||
if(array.iarr.length == 0) return false;
|
|
||||||
|
|
||||||
assert(array.elemSize > 0);
|
|
||||||
elemSize = array.elemSize;
|
|
||||||
|
|
||||||
// Point to the stack index and value
|
|
||||||
stk -= elemSize;
|
|
||||||
sval = stk[0..elemSize];
|
|
||||||
stk--;
|
|
||||||
sindex = stk;
|
|
||||||
|
|
||||||
// Set up the first element
|
|
||||||
if(isReverse) index = array.length-1;
|
|
||||||
else index = 0;
|
|
||||||
|
|
||||||
indexMul = index * elemSize;
|
|
||||||
|
|
||||||
*sindex = index;
|
|
||||||
sval[] = array.iarr[indexMul..indexMul+elemSize];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Class iterators
|
|
||||||
bool firstClass(MonsterClass mc, int[] stk)
|
|
||||||
{
|
|
||||||
assert(stk.length == 2);
|
|
||||||
isClass = true;
|
|
||||||
|
|
||||||
// Set the iterator index on the stack
|
|
||||||
stk[1] = cast(int) getIndex();
|
|
||||||
|
|
||||||
mo = mc.getFirst();
|
|
||||||
this.mc = mc;
|
|
||||||
|
|
||||||
// Are there any objects?
|
|
||||||
if(mo == null) return false;
|
|
||||||
|
|
||||||
sindex = &stk[0];
|
|
||||||
*sindex = cast(int)mo.getIndex();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void storeRef()
|
|
||||||
{
|
|
||||||
assert(!isClass);
|
|
||||||
|
|
||||||
if(isRef)
|
|
||||||
array.iarr[indexMul..indexMul+elemSize] = sval[];
|
|
||||||
else
|
|
||||||
fail("Array iterator update called on non-ref parameter");
|
|
||||||
}
|
|
||||||
|
|
||||||
bool next()
|
|
||||||
{
|
|
||||||
// Handle class iterations seperately
|
|
||||||
if(isClass)
|
|
||||||
{
|
|
||||||
mo = mc.getNext(mo);
|
|
||||||
if(mo == null) return false;
|
|
||||||
|
|
||||||
*sindex = cast(int)mo.getIndex();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isReverse)
|
|
||||||
{
|
|
||||||
index--;
|
|
||||||
indexMul -= elemSize;
|
|
||||||
if(index == -1) return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
index++;
|
|
||||||
indexMul += elemSize;
|
|
||||||
if(index*elemSize == array.iarr.length) return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(indexMul < array.iarr.length);
|
|
||||||
assert(index >= 0 && index < array.length);
|
|
||||||
assert(indexMul == index*elemSize);
|
|
||||||
|
|
||||||
*sindex = index;
|
|
||||||
sval[] = array.iarr[indexMul..indexMul+elemSize];
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
IIndex getIndex()
|
|
||||||
{
|
|
||||||
return cast(IIndex)( Iterators.IterList.getIndex(this) );
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isAlive() { return flags.has(IFlags.Alive); }
|
|
||||||
}
|
|
||||||
|
|
||||||
Iterators iterators;
|
|
||||||
|
|
||||||
struct Iterators
|
|
||||||
{
|
|
||||||
alias FreeList!(IteratorRef) IterList;
|
|
||||||
|
|
||||||
private:
|
|
||||||
IterList iterList;
|
|
||||||
|
|
||||||
// Get a new iterator reference
|
|
||||||
IteratorRef *createIterator()
|
|
||||||
{
|
|
||||||
IteratorRef *it = iterList.getNew();
|
|
||||||
|
|
||||||
assert(!it.isAlive);
|
|
||||||
|
|
||||||
// Set the "alive" flag
|
|
||||||
it.flags.set(IFlags.Alive);
|
|
||||||
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Put a reference back into the freelist
|
|
||||||
void destroyIterator(IteratorRef *it)
|
|
||||||
{
|
|
||||||
assert(it.isAlive);
|
|
||||||
it.flags.unset(IFlags.Alive);
|
|
||||||
iterList.remove(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
bool firstArray(bool irev, bool iref, int *stk)
|
|
||||||
{
|
|
||||||
IteratorRef *it = createIterator();
|
|
||||||
bool res = it.firstArray(irev,iref,stk);
|
|
||||||
|
|
||||||
// Kill the iterator reference if we are done iterating
|
|
||||||
if(!res) destroyIterator(it);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool firstClass(MonsterClass mc, int[] stk)
|
|
||||||
{
|
|
||||||
IteratorRef *it = createIterator();
|
|
||||||
bool res = it.firstClass(mc,stk);
|
|
||||||
|
|
||||||
// Kill the iterator reference if we are done iterating
|
|
||||||
if(!res) destroyIterator(it);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool next(IIndex ind)
|
|
||||||
{
|
|
||||||
IteratorRef *it = getRef(ind);
|
|
||||||
bool res = it.next();
|
|
||||||
|
|
||||||
// Kill the iterator reference if this was the last iteration
|
|
||||||
if(!res) destroyIterator(it);
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
void stop(IIndex ind)
|
|
||||||
{
|
|
||||||
IteratorRef *it = getRef(ind);
|
|
||||||
destroyIterator(it);
|
|
||||||
}
|
|
||||||
|
|
||||||
void update(IIndex ind)
|
|
||||||
{
|
|
||||||
IteratorRef *it = getRef(ind);
|
|
||||||
it.storeRef();
|
|
||||||
}
|
|
||||||
|
|
||||||
IteratorRef *getRef(IIndex index)
|
|
||||||
{
|
|
||||||
if(index < 0 || index >= getTotalIterators())
|
|
||||||
fail("Invalid iterator reference: " ~ toString(cast(int)index));
|
|
||||||
|
|
||||||
IteratorRef *itr = IterList.getNode(index);
|
|
||||||
|
|
||||||
if(!itr.isAlive)
|
|
||||||
fail("Dead iterator reference: " ~ toString(cast(int)index));
|
|
||||||
|
|
||||||
assert(itr.getIndex() == index);
|
|
||||||
|
|
||||||
return itr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the number of iterator references in use
|
|
||||||
int getIterators()
|
|
||||||
{
|
|
||||||
return iterList.length();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the total number of Iterator references ever allocated for the
|
|
||||||
// free list.
|
|
||||||
int getTotalIterators()
|
|
||||||
{
|
|
||||||
return IterList.totLength();
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,484 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (mobject.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.mobject;
|
|
||||||
|
|
||||||
import monster.vm.thread;
|
|
||||||
import monster.vm.error;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.arrays;
|
|
||||||
import monster.vm.stack;
|
|
||||||
|
|
||||||
import monster.util.freelist;
|
|
||||||
import monster.util.list;
|
|
||||||
|
|
||||||
import monster.compiler.states;
|
|
||||||
import monster.compiler.variables;
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.compiler.functions;
|
|
||||||
|
|
||||||
import std.string;
|
|
||||||
import std.stdio;
|
|
||||||
import std.utf;
|
|
||||||
|
|
||||||
// An index to a monster object.
|
|
||||||
typedef int MIndex;
|
|
||||||
|
|
||||||
union SharedType
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
uint ui;
|
|
||||||
long l;
|
|
||||||
ulong ul;
|
|
||||||
float f;
|
|
||||||
double d;
|
|
||||||
|
|
||||||
void *vptr;
|
|
||||||
Object obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct ExtraData
|
|
||||||
{
|
|
||||||
SharedType extra;
|
|
||||||
vpNode node;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MonsterObject
|
|
||||||
{
|
|
||||||
/*******************************************************
|
|
||||||
* *
|
|
||||||
* Public variables *
|
|
||||||
* *
|
|
||||||
*******************************************************/
|
|
||||||
|
|
||||||
MonsterClass cls;
|
|
||||||
|
|
||||||
// Thread used for running state code. May be null if no code is
|
|
||||||
// running or scheduled.
|
|
||||||
Thread *sthread;
|
|
||||||
|
|
||||||
// The following variables are "tree-indexed". This means that
|
|
||||||
// they're arrays, with one element for each class in the
|
|
||||||
// inheritance hierarchy. The corresponding class tree can be found
|
|
||||||
// in cls.tree.
|
|
||||||
|
|
||||||
// Object data segment.
|
|
||||||
int[][] data;
|
|
||||||
|
|
||||||
/*******************************************************
|
|
||||||
* *
|
|
||||||
* Private variables *
|
|
||||||
* *
|
|
||||||
*******************************************************/
|
|
||||||
|
|
||||||
//private:
|
|
||||||
State *state; // Current state, null is the empty state.
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
/*******************************************************
|
|
||||||
* *
|
|
||||||
* Functions for object handling *
|
|
||||||
* *
|
|
||||||
*******************************************************/
|
|
||||||
|
|
||||||
// Get the index of this object
|
|
||||||
MIndex getIndex()
|
|
||||||
{
|
|
||||||
return cast(MIndex)( ObjectList.getIndex(this)+1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete this object. Do not use the object after calling this
|
|
||||||
// function.
|
|
||||||
void deleteSelf()
|
|
||||||
{
|
|
||||||
cls.deleteObject(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a clone of this object.
|
|
||||||
MonsterObject *clone()
|
|
||||||
{ return cls.createClone(this); }
|
|
||||||
|
|
||||||
/*******************************************************
|
|
||||||
* *
|
|
||||||
* Member variable getters / setters *
|
|
||||||
* *
|
|
||||||
*******************************************************/
|
|
||||||
|
|
||||||
// The last two ints of the data segment can be used to store extra
|
|
||||||
// data associated with the object. A typical example is the pointer
|
|
||||||
// to a D/C++ struct or class counterpart to the Monster class.
|
|
||||||
static const exSize = ExtraData.sizeof / int.sizeof;
|
|
||||||
static assert(exSize*4 == ExtraData.sizeof);
|
|
||||||
SharedType *getExtra(int index)
|
|
||||||
{
|
|
||||||
return & (cast(ExtraData*)&data[index][$-exSize]).extra;
|
|
||||||
}
|
|
||||||
SharedType *getExtra(MonsterClass mc)
|
|
||||||
{ return getExtra(cls.upcast(mc)); }
|
|
||||||
|
|
||||||
// This is the work horse for all the set/get functions.
|
|
||||||
T* getPtr(T)(char[] name)
|
|
||||||
{
|
|
||||||
// Find the variable
|
|
||||||
Variable *vb = cls.findVariable(name);
|
|
||||||
assert(vb !is null);
|
|
||||||
|
|
||||||
// Check the type
|
|
||||||
if(!vb.type.isDType(typeid(T)))
|
|
||||||
{
|
|
||||||
char[] request;
|
|
||||||
static if(is(T == dchar)) request = "char"; else
|
|
||||||
static if(is(T == AIndex)) request = "array"; else
|
|
||||||
static if(is(T == MIndex)) request = "object"; else
|
|
||||||
request = typeid(T).toString();
|
|
||||||
|
|
||||||
fail(format("Requested variable %s is not the right type (wanted %s, found %s)",
|
|
||||||
name, request, vb.type.toString()));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cast the object to the right kind
|
|
||||||
assert(vb.sc.isClass(), "variable must be a class variable");
|
|
||||||
MonsterClass mc = vb.sc.getClass();
|
|
||||||
assert(mc !is null);
|
|
||||||
|
|
||||||
// Return the pointer
|
|
||||||
return cast(T*) getDataInt(mc.treeIndex, vb.number);
|
|
||||||
}
|
|
||||||
T getType(T)(char[] name)
|
|
||||||
{ return *getPtr!(T)(name); }
|
|
||||||
void setType(T)(char[] name, T t)
|
|
||||||
{ *getPtr!(T)(name) = t; }
|
|
||||||
|
|
||||||
alias getPtr!(int) getIntPtr;
|
|
||||||
alias getPtr!(uint) getUintPtr;
|
|
||||||
alias getPtr!(long) getLongPtr;
|
|
||||||
alias getPtr!(ulong) getUlongPtr;
|
|
||||||
alias getPtr!(bool) getBoolPtr;
|
|
||||||
alias getPtr!(float) getFloatPtr;
|
|
||||||
alias getPtr!(double) getDoublePtr;
|
|
||||||
alias getPtr!(dchar) getCharPtr;
|
|
||||||
alias getPtr!(AIndex) getAIndexPtr;
|
|
||||||
alias getPtr!(MIndex) getMIndexPtr;
|
|
||||||
|
|
||||||
alias getType!(int) getInt;
|
|
||||||
alias getType!(uint) getUint;
|
|
||||||
alias getType!(long) getLong;
|
|
||||||
alias getType!(ulong) getUlong;
|
|
||||||
alias getType!(bool) getBool;
|
|
||||||
alias getType!(float) getFloat;
|
|
||||||
alias getType!(double) getDouble;
|
|
||||||
alias getType!(dchar) getChar;
|
|
||||||
alias getType!(AIndex) getAIndex;
|
|
||||||
alias getType!(MIndex) getMIndex;
|
|
||||||
|
|
||||||
alias setType!(int) setInt;
|
|
||||||
alias setType!(uint) setUint;
|
|
||||||
alias setType!(long) setLong;
|
|
||||||
alias setType!(ulong) setUlong;
|
|
||||||
alias setType!(bool) setBool;
|
|
||||||
alias setType!(float) setFloat;
|
|
||||||
alias setType!(double) setDouble;
|
|
||||||
alias setType!(dchar) setChar;
|
|
||||||
alias setType!(AIndex) setAIndex;
|
|
||||||
alias setType!(MIndex) setMIndex;
|
|
||||||
|
|
||||||
MonsterObject *getObject(char[] name)
|
|
||||||
{ return getMObject(getMIndex(name)); }
|
|
||||||
void setObject(char[] name, MonsterObject *obj)
|
|
||||||
{ setMIndex(name, obj.getIndex()); }
|
|
||||||
|
|
||||||
// Array stuff
|
|
||||||
ArrayRef* getArray(char[] name)
|
|
||||||
{ return arrays.getRef(getAIndex(name)); }
|
|
||||||
void setArray(char[] name, ArrayRef *r)
|
|
||||||
{ setAIndex(name,r.getIndex()); }
|
|
||||||
|
|
||||||
char[] getString8(char[] name)
|
|
||||||
{ return toUTF8(getArray(name).carr); }
|
|
||||||
void setString8(char[] name, char[] str)
|
|
||||||
{ setArray(name, arrays.create(toUTF32(str))); }
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************
|
|
||||||
* *
|
|
||||||
* Lower level member data functions *
|
|
||||||
* *
|
|
||||||
*******************************************************/
|
|
||||||
|
|
||||||
// Get an int from the data segment
|
|
||||||
int *getDataInt(int treeIndex, int pos)
|
|
||||||
{
|
|
||||||
assert(treeIndex >= 0 && treeIndex < data.length,
|
|
||||||
"tree index out of range: " ~ .toString(treeIndex));
|
|
||||||
assert(pos >= 0 && pos<data[treeIndex].length,
|
|
||||||
"data pointer out of range: " ~ .toString(pos));
|
|
||||||
return &data[treeIndex][pos];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get an array from the data segment
|
|
||||||
int[] getDataArray(int treeIndex, int pos, int len)
|
|
||||||
{
|
|
||||||
assert(len > 0);
|
|
||||||
assert(treeIndex >= 0 && treeIndex < data.length,
|
|
||||||
"tree index out of range: " ~ .toString(treeIndex));
|
|
||||||
assert(pos >= 0 && (pos+len)<=data[treeIndex].length,
|
|
||||||
"data pointer out of range: pos=" ~ .toString(pos) ~
|
|
||||||
", len=" ~.toString(len));
|
|
||||||
return data[treeIndex][pos..pos+len];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/*******************************************************
|
|
||||||
* *
|
|
||||||
* Calling functions and setting states *
|
|
||||||
* *
|
|
||||||
*******************************************************/
|
|
||||||
|
|
||||||
// Call a named function directly. The function is executed
|
|
||||||
// immediately, and call() returns when the function is
|
|
||||||
// finished. The function is called virtually, so any child class
|
|
||||||
// function that overrides it will take precedence. This is the 'low
|
|
||||||
// level' way to call functions, meaning that you have to handle
|
|
||||||
// parameters and return values on the stack manually. Use the
|
|
||||||
// template functions below for a more high level interface.
|
|
||||||
void call(char[] name)
|
|
||||||
{
|
|
||||||
cls.findFunction(name).call(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
template callT(T)
|
|
||||||
{
|
|
||||||
T callT(A ...)(char[] name, A a)
|
|
||||||
{
|
|
||||||
return cls.findFunction(name).callT!(T)(this, a);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alias callT!(void) callVoid;
|
|
||||||
|
|
||||||
alias callT!(int) callInt;
|
|
||||||
alias callT!(uint) callUint;
|
|
||||||
alias callT!(float) callFloat;
|
|
||||||
alias callT!(double) callDouble;
|
|
||||||
alias callT!(long) callLong;
|
|
||||||
alias callT!(ulong) callUlong;
|
|
||||||
alias callT!(dchar) callChar;
|
|
||||||
alias callT!(MIndex) callMIndex;
|
|
||||||
alias callT!(AIndex) callAIndex;
|
|
||||||
|
|
||||||
// Create a paused thread that's set up to call the given
|
|
||||||
// function. It must be started with Thread.call() or
|
|
||||||
// Thread.restart().
|
|
||||||
Thread *thread(char[] name)
|
|
||||||
{ return thread(cls.findFunction(name)); }
|
|
||||||
Thread *thread(Function *fn)
|
|
||||||
{
|
|
||||||
assert(fn !is null);
|
|
||||||
if(fn.paramSize > 0)
|
|
||||||
fail("thread(): function " ~ fn.name.str ~ " cannot have parameters");
|
|
||||||
|
|
||||||
fn = fn.findVirtual(this);
|
|
||||||
|
|
||||||
Thread *trd = Thread.getNew();
|
|
||||||
|
|
||||||
// Schedule the function to run the next frame
|
|
||||||
trd.pushFunc(fn, this);
|
|
||||||
assert(trd.isPaused);
|
|
||||||
assert(trd.fstack.cur !is null);
|
|
||||||
|
|
||||||
return trd;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a thread containing the function and schedule it to start
|
|
||||||
// the next frame
|
|
||||||
Thread *start(char[] name)
|
|
||||||
{ return start(cls.findFunction(name)); }
|
|
||||||
Thread *start(Function *fn)
|
|
||||||
{
|
|
||||||
assert(fn !is null);
|
|
||||||
auto trd = thread(fn);
|
|
||||||
trd.restart();
|
|
||||||
return trd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Set state. Invoked by the statement "state = statename;". This
|
|
||||||
function can be called in several situations, with various
|
|
||||||
results:
|
|
||||||
|
|
||||||
+ setState called with current state, no label
|
|
||||||
-> no action is performed
|
|
||||||
|
|
||||||
+ setState called with another state
|
|
||||||
+ setState called with current state + a label
|
|
||||||
-> state is changed normally
|
|
||||||
|
|
||||||
If a state change takes place directly in state code, the code is
|
|
||||||
aborted immediately. If it takes place in a function called from
|
|
||||||
state code, then code flow is allowed to return normally back to
|
|
||||||
the state code level, but is aborted immediately once it reaches
|
|
||||||
state code.
|
|
||||||
|
|
||||||
State changes outside state code will always unschedule any
|
|
||||||
previously scheduled code (such as idle functions, or previous
|
|
||||||
calls to setState.)
|
|
||||||
*/
|
|
||||||
void setState(State *st, StateLabel *label)
|
|
||||||
{
|
|
||||||
// Does the state actually change?
|
|
||||||
if(st !is state)
|
|
||||||
{
|
|
||||||
// Set the state
|
|
||||||
state = st;
|
|
||||||
|
|
||||||
// We must handle state functions and other magic here.
|
|
||||||
}
|
|
||||||
// If no label is specified and we are already in this state, then
|
|
||||||
// don't do anything.
|
|
||||||
else if(label is null) return;
|
|
||||||
|
|
||||||
// Do we already have a thread?
|
|
||||||
if(sthread !is null)
|
|
||||||
{
|
|
||||||
// Check if the thread has gone and died on us while we were
|
|
||||||
// away.
|
|
||||||
if(sthread.isDead)
|
|
||||||
sthread = null;
|
|
||||||
else
|
|
||||||
// Still alive. Stop any execution of the thread
|
|
||||||
sthread.stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we are jumping to anything but the empty state, we will have
|
|
||||||
// to schedule some code.
|
|
||||||
if(st !is null)
|
|
||||||
{
|
|
||||||
// Check that this state is valid
|
|
||||||
assert(st.owner.parentOf(cls), "state '" ~ st.name.str ~
|
|
||||||
"' is not part of class " ~ cls.getName());
|
|
||||||
|
|
||||||
if(label is null)
|
|
||||||
// Use the 'begin:' label, if any. It will be null there's
|
|
||||||
// no begin label.
|
|
||||||
label = st.begin;
|
|
||||||
|
|
||||||
if(label !is null)
|
|
||||||
{
|
|
||||||
// Make sure there's a thread to run in
|
|
||||||
if(sthread is null)
|
|
||||||
sthread = Thread.getNew();
|
|
||||||
|
|
||||||
// Schedule the thread to start at the given state and
|
|
||||||
// label
|
|
||||||
sthread.scheduleState(this, label.offs);
|
|
||||||
assert(sthread.isScheduled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If nothing is scheduled, kill the thread
|
|
||||||
if(sthread !is null && !sthread.isScheduled)
|
|
||||||
{
|
|
||||||
assert(sthread.isTransient);
|
|
||||||
sthread.kill();
|
|
||||||
|
|
||||||
// Zero out any pointers to the thread.
|
|
||||||
if(sthread is cthread)
|
|
||||||
cthread = null;
|
|
||||||
sthread = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(sthread is null || sthread.isScheduled);
|
|
||||||
}
|
|
||||||
|
|
||||||
void clearState() { setState(cast(State*)null, null); }
|
|
||||||
|
|
||||||
// Index version of setState - called from bytecode
|
|
||||||
void setState(int st, int label, int clsInd)
|
|
||||||
{
|
|
||||||
if(st == -1)
|
|
||||||
{
|
|
||||||
assert(label == -1);
|
|
||||||
clearState();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto cls = cls.upcast(clsInd);
|
|
||||||
|
|
||||||
// TODO: This does not support virtual states yet
|
|
||||||
auto pair = cls.findState(st, label);
|
|
||||||
|
|
||||||
assert(pair.state.index == st);
|
|
||||||
assert(pair.state.owner is cls);
|
|
||||||
|
|
||||||
setState(pair.state, pair.label);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Named version of the above function. An empty string sets the
|
|
||||||
// state to -1 (the empty state.) If no label is given (or given as
|
|
||||||
// ""), this is equivalent to the script command state=name; If a
|
|
||||||
// label is given, it is equivalent to state = name.label;
|
|
||||||
void setState(char[] name, char[] label = "")
|
|
||||||
{
|
|
||||||
if(label == "")
|
|
||||||
{
|
|
||||||
if(name == "") clearState();
|
|
||||||
else setState(cls.findState(name), null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(name != "", "The empty state cannot contain the label " ~ label);
|
|
||||||
|
|
||||||
auto stl = cls.findState(name, label);
|
|
||||||
setState(stl.state, stl.label);
|
|
||||||
}
|
|
||||||
|
|
||||||
char[] toString()
|
|
||||||
{
|
|
||||||
return cls.toString ~ "#" ~ .toString(cast(int)getIndex());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
alias FreeList!(MonsterObject) ObjectList;
|
|
||||||
|
|
||||||
// The freelist used for allocation of objects. This contains all
|
|
||||||
// allocated and in-use objects.
|
|
||||||
ObjectList allObjects;
|
|
||||||
|
|
||||||
// Convert an index to an object pointer
|
|
||||||
MonsterObject *getMObject(MIndex index)
|
|
||||||
{
|
|
||||||
if(index == 0)
|
|
||||||
fail("Null object reference encountered");
|
|
||||||
|
|
||||||
if(index < 0 || index > ObjectList.totLength())
|
|
||||||
fail("Invalid object reference");
|
|
||||||
|
|
||||||
MonsterObject *obj = ObjectList.getNode(index-1);
|
|
||||||
|
|
||||||
if(obj.cls is null)
|
|
||||||
fail("Dead object reference (index " ~ toString(cast(int)index) ~ ")");
|
|
||||||
|
|
||||||
assert(obj.getIndex() == index);
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (params.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.params;
|
|
||||||
|
|
||||||
import monster.vm.mobject;
|
|
||||||
import monster.vm.thread;
|
|
||||||
|
|
||||||
/* This module offers a "friendly" interface for dealing with
|
|
||||||
parameters and return values on the stack. It is meant to be an
|
|
||||||
alternative to manipulating the stack directly when writing native
|
|
||||||
functions.
|
|
||||||
|
|
||||||
NOT FINISHED!
|
|
||||||
*/
|
|
||||||
|
|
||||||
Params params;
|
|
||||||
|
|
||||||
struct Params
|
|
||||||
{
|
|
||||||
static:
|
|
||||||
|
|
||||||
// Get the current object (the 'this' reference for the current
|
|
||||||
// function)
|
|
||||||
MonsterObject *obj()
|
|
||||||
{
|
|
||||||
assert(cthread !is null);
|
|
||||||
assert(cthread.fstack.cur !is null);
|
|
||||||
assert(cthread.fstack.cur.obj !is null);
|
|
||||||
return cthread.fstack.cur.obj;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,454 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (stack.d) is part of the Monster script language
|
|
||||||
package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.stack;
|
|
||||||
|
|
||||||
import std.string;
|
|
||||||
import std.stdio;
|
|
||||||
import std.utf;
|
|
||||||
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
import monster.compiler.functions;
|
|
||||||
import monster.options;
|
|
||||||
|
|
||||||
import monster.vm.mobject;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.arrays;
|
|
||||||
import monster.vm.error;
|
|
||||||
|
|
||||||
// Stack. There's only one global instance, but threads will make
|
|
||||||
// copies when they need it.
|
|
||||||
CodeStack stack;
|
|
||||||
|
|
||||||
struct FunctionRef
|
|
||||||
{
|
|
||||||
MIndex obj;
|
|
||||||
int fIndex;
|
|
||||||
|
|
||||||
MonsterObject *getObject()
|
|
||||||
{ return getMObject(obj); }
|
|
||||||
|
|
||||||
Function *getFunctionNonVirtual()
|
|
||||||
{ return functionList[fIndex]; }
|
|
||||||
|
|
||||||
Function *getFunction()
|
|
||||||
{
|
|
||||||
auto f = getFunctionNonVirtual();
|
|
||||||
return f.findVirtual(getObject());
|
|
||||||
}
|
|
||||||
|
|
||||||
void set(Function* fn, MonsterObject *mo)
|
|
||||||
{
|
|
||||||
assert(fn !is null);
|
|
||||||
assert(mo !is null);
|
|
||||||
assert(mo.cls.childOf(fn.owner));
|
|
||||||
fIndex = fn.getGIndex();
|
|
||||||
obj = mo.getIndex();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
static assert(FunctionRef.sizeof == 8);
|
|
||||||
|
|
||||||
// A simple stack. All data are in chunks of 4 bytes
|
|
||||||
struct CodeStack
|
|
||||||
{
|
|
||||||
private:
|
|
||||||
int[] data;
|
|
||||||
|
|
||||||
int left, total;
|
|
||||||
int *pos; // Current position
|
|
||||||
|
|
||||||
public:
|
|
||||||
void init()
|
|
||||||
{
|
|
||||||
data.length = maxStack;
|
|
||||||
left = maxStack;
|
|
||||||
total = maxStack;
|
|
||||||
pos = data.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the current position index.
|
|
||||||
int getPos()
|
|
||||||
{
|
|
||||||
return total-left;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reset the stack level to zero.
|
|
||||||
void reset()
|
|
||||||
{
|
|
||||||
left = total;
|
|
||||||
pos = data.ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pushInt(int i)
|
|
||||||
{
|
|
||||||
left--;
|
|
||||||
if(left<0) overflow("pushInt");
|
|
||||||
*pos = i;
|
|
||||||
pos++;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pushLong(long i)
|
|
||||||
{
|
|
||||||
left -= 2;
|
|
||||||
if(left<0) overflow("pushLong");
|
|
||||||
*(cast(long*)pos) = i;
|
|
||||||
pos+=2;
|
|
||||||
}
|
|
||||||
|
|
||||||
int popInt()
|
|
||||||
{
|
|
||||||
left++;
|
|
||||||
if(left>total) overflow("popInt");
|
|
||||||
pos--;
|
|
||||||
return *pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
long popLong()
|
|
||||||
{
|
|
||||||
left+=2;
|
|
||||||
if(left>total) overflow("popLong");
|
|
||||||
pos-=2;
|
|
||||||
return *(cast(long*)pos);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the pointer to an int at the given position backwards from
|
|
||||||
// the current stack pointer. 0 means the first int, ie. the one we
|
|
||||||
// would get if we called popInt. 1 is the next, etc
|
|
||||||
int *getInt(int ptr)
|
|
||||||
{
|
|
||||||
ptr++;
|
|
||||||
if(ptr < 1 || ptr > (total-left) )
|
|
||||||
fail("CodeStack.getInt() pointer out of range");
|
|
||||||
return pos-ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the array _beginning_ at ptr
|
|
||||||
int[] getInts(int ptr, int len)
|
|
||||||
{
|
|
||||||
assert(len > 0 && ptr >= len-1);
|
|
||||||
if(left+len-ptr>total) overflow("getInts");
|
|
||||||
return getInt(ptr)[0..len];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pops the next len ints off the stack and returns them as an
|
|
||||||
// array. The array is ordered as the values were pushed, not as
|
|
||||||
// they would have been popped (ie. this function is like popping
|
|
||||||
// one big value of the stack.) The array is a direct slice of the
|
|
||||||
// stack, so don't store it or use it after pushing other values.
|
|
||||||
int[] popInts(int len)
|
|
||||||
{
|
|
||||||
assert(len > 0);
|
|
||||||
int[] r = getInts(len-1, len);
|
|
||||||
pop(len);
|
|
||||||
assert(r.length == len);
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
void pushInts(int[] arr)
|
|
||||||
{
|
|
||||||
left -= arr.length;
|
|
||||||
if(left<0) overflow("pushInts");
|
|
||||||
pos[0..arr.length] = arr[];
|
|
||||||
pos+=arr.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pushing and poping objects of the stack - will actually push/pop
|
|
||||||
// their index.
|
|
||||||
void pushObject(MonsterObject *mo)
|
|
||||||
{ pushInt(mo.getIndex); }
|
|
||||||
|
|
||||||
MonsterObject *popObject()
|
|
||||||
{ return getMObject(cast(MIndex)popInt()); }
|
|
||||||
|
|
||||||
MonsterObject *peekObject()
|
|
||||||
{ return getMObject(cast(MIndex)peekInt()); }
|
|
||||||
|
|
||||||
// Push arrays of objects. TODO: These do memory allocation, and I'm
|
|
||||||
// not sure that belongs here. I will look into it later.
|
|
||||||
void pushObjects(MonsterObject *objs[])
|
|
||||||
{
|
|
||||||
int[] indices;
|
|
||||||
indices.length = objs.length;
|
|
||||||
|
|
||||||
foreach(i, mo; objs)
|
|
||||||
indices[i] = mo.getIndex();
|
|
||||||
|
|
||||||
pushIArray(indices);
|
|
||||||
}
|
|
||||||
|
|
||||||
MonsterObject*[] popObjects()
|
|
||||||
{
|
|
||||||
MIndex[] indices = cast(MIndex[]) popIArray();
|
|
||||||
MonsterObject* objs[];
|
|
||||||
|
|
||||||
objs.length = indices.length;
|
|
||||||
foreach(i, ind; indices)
|
|
||||||
objs[i] = getMObject(ind);
|
|
||||||
|
|
||||||
return objs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push and pop array references.
|
|
||||||
void pushArray(ArrayRef *ar)
|
|
||||||
{ pushInt(ar.getIndex); }
|
|
||||||
ArrayRef *popArray()
|
|
||||||
{ return arrays.getRef(cast(AIndex)popInt()); }
|
|
||||||
ArrayRef *getArray(int i)
|
|
||||||
{ return arrays.getRef(cast(AIndex)*getInt(i)); }
|
|
||||||
ArrayRef *peekArray()
|
|
||||||
{ return getArray(0); }
|
|
||||||
|
|
||||||
// More easy versions. Note that pushArray() will create a new array
|
|
||||||
// reference each time it is called! Only use it if this is what you
|
|
||||||
// want.
|
|
||||||
void pushCArray(dchar[] str) { pushArray(arrays.create(str)); }
|
|
||||||
void pushIArray(int[] str) { pushArray(arrays.create(str)); }
|
|
||||||
void pushUArray(uint[] str) { pushArray(arrays.create(str)); }
|
|
||||||
void pushLArray(long[] str){ pushArray(arrays.create(str)); }
|
|
||||||
void pushULArray(ulong[] str) { pushArray(arrays.create(str)); }
|
|
||||||
void pushFArray(float[] str) { pushArray(arrays.create(str)); }
|
|
||||||
void pushDArray(double[] str) { pushArray(arrays.create(str)); }
|
|
||||||
void pushAArray(AIndex[] str) { pushArray(arrays.create(str)); }
|
|
||||||
void pushMArray(AIndex[] str) { pushArray(arrays.create(str)); }
|
|
||||||
|
|
||||||
alias pushCArray pushArray, pushString;
|
|
||||||
alias pushIArray pushArray;
|
|
||||||
alias pushFArray pushArray;
|
|
||||||
alias pushAArray pushArray;
|
|
||||||
alias pushString8 pushArray, pushString;
|
|
||||||
|
|
||||||
dchar[] popCArray() { return popArray().carr; }
|
|
||||||
int[] popIArray() { return popArray().iarr; }
|
|
||||||
float[] popFArray() { return popArray().farr; }
|
|
||||||
AIndex[] popAArray() { return popArray().aarr; }
|
|
||||||
alias popCArray popString;
|
|
||||||
|
|
||||||
void pushString8(char[] str)
|
|
||||||
{ pushArray(toUTF32(str)); }
|
|
||||||
char[] popString8()
|
|
||||||
{ return toUTF8(popString()); }
|
|
||||||
char[] peekString8()
|
|
||||||
{ return toUTF8(peekArray().carr); }
|
|
||||||
|
|
||||||
// For multibyte arrays
|
|
||||||
void pushArray(int[] str, int size)
|
|
||||||
{ pushArray(arrays.create(str, size)); }
|
|
||||||
|
|
||||||
// Various convenient conversion templates. These will be inlined,
|
|
||||||
// so don't worry :) The *4() functions are for types that are 4
|
|
||||||
// bytes long.
|
|
||||||
void push4(T)(T var)
|
|
||||||
{
|
|
||||||
static assert(T.sizeof == 4);
|
|
||||||
pushInt(*(cast(int*)&var));
|
|
||||||
}
|
|
||||||
T pop4(T)()
|
|
||||||
{
|
|
||||||
static assert(T.sizeof == 4);
|
|
||||||
int i = popInt();
|
|
||||||
return *(cast(T*)&i);
|
|
||||||
}
|
|
||||||
// Gets a pointer to a given stack value. Counts from the head - 0
|
|
||||||
// is the first int, 1 is the second, etc. Note that it counts in
|
|
||||||
// ints (four bytes) no matter what the type T is - this is by
|
|
||||||
// design.
|
|
||||||
T* get4(T)(int ptr) { return cast(T*)getInt(ptr); }
|
|
||||||
|
|
||||||
// Returns the first value on the stack without poping it
|
|
||||||
T peek4(T)() { return *(cast(T*)getInt(0)); }
|
|
||||||
|
|
||||||
// 64 bit version
|
|
||||||
void push8(T)(T var)
|
|
||||||
{
|
|
||||||
static assert(T.sizeof == 8);
|
|
||||||
pushLong(*(cast(long*)&var));
|
|
||||||
}
|
|
||||||
T pop8(T)()
|
|
||||||
{
|
|
||||||
static assert(T.sizeof == 8);
|
|
||||||
long l = popLong();
|
|
||||||
return *(cast(T*)&l);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bools are 1 byte in D
|
|
||||||
void pushBool(bool b)
|
|
||||||
{
|
|
||||||
if(b) pushInt(1);
|
|
||||||
else pushInt(0);
|
|
||||||
}
|
|
||||||
bool popBool() { return popInt() != 0; }
|
|
||||||
alias get4!(bool) getBool;
|
|
||||||
|
|
||||||
// Template conversions
|
|
||||||
|
|
||||||
alias push4!(MIndex) pushMIndex;
|
|
||||||
alias pop4!(MIndex) popMIndex;
|
|
||||||
alias get4!(MIndex) getMIndex;
|
|
||||||
alias peek4!(MIndex) peekMIndex;
|
|
||||||
|
|
||||||
alias push4!(AIndex) pushAIndex;
|
|
||||||
alias pop4!(AIndex) popAIndex;
|
|
||||||
alias get4!(AIndex) getAIndex;
|
|
||||||
alias peek4!(AIndex) peekAIndex;
|
|
||||||
|
|
||||||
alias peek4!(int) peekInt;
|
|
||||||
|
|
||||||
alias push4!(uint) pushUint;
|
|
||||||
alias pop4!(uint) popUint;
|
|
||||||
alias get4!(uint) getUint;
|
|
||||||
alias peek4!(uint) peekUint;
|
|
||||||
|
|
||||||
alias get4!(long) getLong;
|
|
||||||
alias peek4!(long) peekLong;
|
|
||||||
|
|
||||||
alias push8!(ulong) pushUlong;
|
|
||||||
alias pop8!(ulong) popUlong;
|
|
||||||
alias get4!(ulong) getUlong;
|
|
||||||
alias peek4!(ulong) peekUlong;
|
|
||||||
|
|
||||||
alias push4!(float) pushFloat;
|
|
||||||
alias pop4!(float) popFloat;
|
|
||||||
alias get4!(float) getFloat;
|
|
||||||
alias peek4!(float) peekFloat;
|
|
||||||
|
|
||||||
alias push8!(double) pushDouble;
|
|
||||||
alias pop8!(double) popDouble;
|
|
||||||
alias get4!(double) getDouble;
|
|
||||||
alias peek4!(double) peekDouble;
|
|
||||||
|
|
||||||
alias push4!(dchar) pushChar;
|
|
||||||
alias pop4!(dchar) popChar;
|
|
||||||
alias get4!(dchar) getChar;
|
|
||||||
alias peek4!(dchar) peekDchar;
|
|
||||||
|
|
||||||
alias push8!(FunctionRef) pushFuncRef;
|
|
||||||
alias pop8!(FunctionRef) popFuncRef;
|
|
||||||
alias get4!(FunctionRef) getFuncRef;
|
|
||||||
alias peek4!(FunctionRef) peekFuncRef;
|
|
||||||
|
|
||||||
void pushFuncRef(Function *fn, MonsterObject *obj)
|
|
||||||
{
|
|
||||||
FunctionRef f;
|
|
||||||
f.set(fn,obj);
|
|
||||||
pushFuncRef(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
void pushFail(T)(T t)
|
|
||||||
{
|
|
||||||
static assert(0, "pushType not yet implemented for " ~ T.stringof);
|
|
||||||
}
|
|
||||||
|
|
||||||
T popFail(T)()
|
|
||||||
{
|
|
||||||
static assert(0, "popType not yet implemented for " ~ T.stringof);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Generic push template
|
|
||||||
template pushType(T)
|
|
||||||
{
|
|
||||||
static if(is(T == MIndex) || is(T == AIndex) ||
|
|
||||||
is(T == int) || is(T == uint) || is(T == float))
|
|
||||||
alias push4!(T) pushType;
|
|
||||||
|
|
||||||
else static if(is(T == long) || is(T == ulong) ||
|
|
||||||
is(T == double) || is(T == dchar))
|
|
||||||
alias push8!(T) pushType;
|
|
||||||
|
|
||||||
else
|
|
||||||
alias pushFail!(T) pushType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ditto for pop
|
|
||||||
template popType(T)
|
|
||||||
{
|
|
||||||
static if(is(T == MIndex) || is(T == AIndex) ||
|
|
||||||
is(T == int) || is(T == uint) || is(T == float))
|
|
||||||
alias pop4!(T) popType;
|
|
||||||
|
|
||||||
else static if(is(T == long) || is(T == ulong) ||
|
|
||||||
is(T == double) || is(T == dchar))
|
|
||||||
alias pop8!(T) popType;
|
|
||||||
|
|
||||||
else
|
|
||||||
alias popFail!(T) popType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop off and ignore a given amount of values
|
|
||||||
void pop(int num)
|
|
||||||
{
|
|
||||||
left += num;
|
|
||||||
if(left>total) overflow("pop1");
|
|
||||||
pos -= num;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pop off and ignore given values, but remember the top
|
|
||||||
// values. Equivalent to popping of (and storing) 'keep' ints, then
|
|
||||||
// poping away 'num' ints, and finally pushing the kept ints
|
|
||||||
// back. The final stack imprint is -num.
|
|
||||||
void pop(uint num, uint keep)
|
|
||||||
{
|
|
||||||
assert(keep>0);
|
|
||||||
assert(num>0);
|
|
||||||
|
|
||||||
left += num;
|
|
||||||
|
|
||||||
// We move the stack pointer back num values, but we access as far
|
|
||||||
// back as num+keep values, so we need to check that we are still
|
|
||||||
// within the stack.
|
|
||||||
if((left+keep)>total) overflow("pop2");
|
|
||||||
|
|
||||||
int *from = pos-keep; // Where to get the 'keep' values from
|
|
||||||
int *to = from-num; // Where they end up
|
|
||||||
pos -= num; // Where the final stack pointer should be
|
|
||||||
|
|
||||||
assert(to < from);
|
|
||||||
|
|
||||||
// Copy the values
|
|
||||||
for(; keep>0; keep--)
|
|
||||||
*(to++) = *(from++);
|
|
||||||
}
|
|
||||||
|
|
||||||
void debugPrint()
|
|
||||||
{
|
|
||||||
writefln("Stack:");
|
|
||||||
foreach(int i, int val; data[0..total-left])
|
|
||||||
writefln("%s: %s", i, val);
|
|
||||||
writefln();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void overflow(char[] func)
|
|
||||||
{
|
|
||||||
char[] res;
|
|
||||||
if(left<0)
|
|
||||||
res = format("Stack overflow by %s ints in CodeStack.%s()",
|
|
||||||
-left, func);
|
|
||||||
else if(left>total)
|
|
||||||
res = format("Stack underflow by %s ints in CodeStack.%s()",
|
|
||||||
left-total, func);
|
|
||||||
else res = format("Internal error in CodeStack.%s(), left=%s, total=%s",
|
|
||||||
func, left, total);
|
|
||||||
fail(res);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,334 +0,0 @@
|
|||||||
/*
|
|
||||||
Monster - an advanced game scripting language
|
|
||||||
Copyright (C) 2007-2009 Nicolay Korslund
|
|
||||||
Email: <korslund@gmail.com>
|
|
||||||
WWW: http://monster.snaptoad.com/
|
|
||||||
|
|
||||||
This file (vm.d) is part of the Monster script language package.
|
|
||||||
|
|
||||||
Monster is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
module monster.vm.vm;
|
|
||||||
|
|
||||||
import monster.vm.error;
|
|
||||||
import monster.vm.thread;
|
|
||||||
import monster.vm.mclass;
|
|
||||||
import monster.vm.mobject;
|
|
||||||
import monster.vm.init;
|
|
||||||
|
|
||||||
import monster.compiler.tokenizer;
|
|
||||||
import monster.compiler.linespec;
|
|
||||||
import monster.compiler.functions;
|
|
||||||
import monster.compiler.assembler;
|
|
||||||
import monster.compiler.scopes;
|
|
||||||
|
|
||||||
import monster.modules.timer;
|
|
||||||
import monster.modules.frames;
|
|
||||||
import monster.modules.vfs;
|
|
||||||
import monster.options;
|
|
||||||
|
|
||||||
import std.stream;
|
|
||||||
import std.string;
|
|
||||||
import std.stdio;
|
|
||||||
import std.utf;
|
|
||||||
import std.format;
|
|
||||||
import monster.util.string;
|
|
||||||
|
|
||||||
VM vm;
|
|
||||||
|
|
||||||
struct VM
|
|
||||||
{
|
|
||||||
// Run a script file in the context of the object obj. If no object
|
|
||||||
// is given, an instance of an empty class is used.
|
|
||||||
Thread *run(char[] file, MonsterObject *obj = null)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
Thread *trd;
|
|
||||||
auto func = new Function;
|
|
||||||
if(obj !is null)
|
|
||||||
{
|
|
||||||
*func = Function(file, obj.cls);
|
|
||||||
trd = func.call(obj);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*func = Function(file);
|
|
||||||
trd = func.call();
|
|
||||||
}
|
|
||||||
return trd;
|
|
||||||
}
|
|
||||||
|
|
||||||
void frame(float time = 0)
|
|
||||||
{
|
|
||||||
static if(!timer_useClock)
|
|
||||||
{
|
|
||||||
if(time != 0)
|
|
||||||
idleTime.add(time);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFrames(time);
|
|
||||||
|
|
||||||
scheduler.doFrame();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load a class based on class name, file name, or both.
|
|
||||||
MonsterClass load(char[] nam1, char[] nam2 = "")
|
|
||||||
{ return doLoad(nam1, nam2, true, true); }
|
|
||||||
|
|
||||||
// Case insensitive with regards to the given class name
|
|
||||||
MonsterClass loadCI(char[] nam1, char[] nam2 = "")
|
|
||||||
{ return doLoad(nam1, nam2, false, true); }
|
|
||||||
|
|
||||||
// Does not fail if the class is not found, just returns null. It
|
|
||||||
// will still fail if the class exists and contains errors.
|
|
||||||
MonsterClass loadNoFail(char[] nam1, char[] nam2 = "")
|
|
||||||
{ return doLoad(nam1, nam2, true, false); }
|
|
||||||
|
|
||||||
// Load a class from a stream. The filename parameter is only used
|
|
||||||
// for error messages.
|
|
||||||
MonsterClass load(Stream s, char name[] = "", int bom=-1)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
assert(s !is null, "Cannot load from null stream");
|
|
||||||
auto mc = new MonsterClass(global);
|
|
||||||
mc.parse(s, name, bom);
|
|
||||||
return mc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load a class from a token array. The filename parameter is only
|
|
||||||
// used for error messages.
|
|
||||||
MonsterClass load(ref TokenArray toks, char[] name = "")
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
auto mc = new MonsterClass(global);
|
|
||||||
mc.parse(toks, name);
|
|
||||||
return mc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load a class from a string containing the script. The filename
|
|
||||||
// parameter is only used for error messages.
|
|
||||||
MonsterClass loadString(char[] str, char[] name="")
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
assert(str != "", "Cannot load empty string");
|
|
||||||
auto ms = new MemoryStream(str);
|
|
||||||
if(name == "") name = "(string)";
|
|
||||||
|
|
||||||
return load(ms, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
void addPath(char[] path)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
addVFS(new FileVFS(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
void addVFS(VFS fs) { init(); vfs.add(fs); }
|
|
||||||
void addVFSFirst(VFS fs) { init(); vfs.addFirst(fs); }
|
|
||||||
|
|
||||||
void init()
|
|
||||||
{
|
|
||||||
if(!initHasRun)
|
|
||||||
doMonsterInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is called from init(), you don't have to call it yourself.
|
|
||||||
void doVMInit()
|
|
||||||
{
|
|
||||||
assert(vfs is null);
|
|
||||||
vfs = new ListVFS;
|
|
||||||
|
|
||||||
static if(vmAddCWD) addPath("./");
|
|
||||||
}
|
|
||||||
|
|
||||||
ListVFS vfs;
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
// Load file based on file name, class name, or both. The order of
|
|
||||||
// the strings doesn't matter, and name2 can be empty. useCase
|
|
||||||
// determines if we require a case sensitive match between the given
|
|
||||||
// class name and the loaded name. If doThrow is true, we throw an
|
|
||||||
// error if the class was not found, otherwise we just return null.
|
|
||||||
MonsterClass doLoad(char[] name1, char[] name2, bool useCase, bool doThrow)
|
|
||||||
{
|
|
||||||
init();
|
|
||||||
|
|
||||||
char[] fname, cname;
|
|
||||||
MonsterClass mc;
|
|
||||||
|
|
||||||
PackageScope pack = global;
|
|
||||||
assert(pack !is null);
|
|
||||||
|
|
||||||
if(name1 == "")
|
|
||||||
fail("Cannot give empty first parameter to load()");
|
|
||||||
|
|
||||||
if(name1.iEnds(".mn"))
|
|
||||||
{
|
|
||||||
fname = name1;
|
|
||||||
cname = name2;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fname = name2;
|
|
||||||
cname = name1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(cname.iEnds(".mn"))
|
|
||||||
fail("load() recieved two filenames: " ~ fname ~ " and " ~ cname);
|
|
||||||
|
|
||||||
// The filename must either be empty, or end with .mn
|
|
||||||
if(fname != "" && !fname.iEnds(".mn"))
|
|
||||||
fail("Neither " ~ name1 ~ " nor " ~ name2 ~
|
|
||||||
" is a valid script filename.");
|
|
||||||
|
|
||||||
// Remember if cname was originally set
|
|
||||||
bool cNameSet = (cname != "");
|
|
||||||
|
|
||||||
// Was a filename given?
|
|
||||||
if(fname != "")
|
|
||||||
{
|
|
||||||
// Derive the class and package names from the given file name.
|
|
||||||
VFS.checkForEscape(fname);
|
|
||||||
|
|
||||||
char[] file = fname;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
// Find a path separator
|
|
||||||
int ind = file.find('/');
|
|
||||||
if(ind == -1)
|
|
||||||
ind = file.find('\\');
|
|
||||||
|
|
||||||
if(ind == -1) break;
|
|
||||||
|
|
||||||
// The file is in a directory. Add it as a package.
|
|
||||||
char[] packname = file[0..ind];
|
|
||||||
file = file[ind+1..$];
|
|
||||||
|
|
||||||
// Empty directory name (eg. dir//file.mn or
|
|
||||||
// dir/./file.mn) should not be added as packages.
|
|
||||||
if(packname != "" && packname != ".")
|
|
||||||
pack = pack.insertPackage(packname);
|
|
||||||
|
|
||||||
// Did we end with a path separator?
|
|
||||||
if(file == "")
|
|
||||||
fail("File name " ~ fname ~ " is a directory");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 'file' now contains the base filename, without the
|
|
||||||
// directory
|
|
||||||
assert(file.iEnds(".mn"));
|
|
||||||
|
|
||||||
// Pick away the extension
|
|
||||||
file = file[0..$-3];
|
|
||||||
|
|
||||||
if(!cNameSet)
|
|
||||||
// No class name given, set it to the derived name
|
|
||||||
cname = file;
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Both names were given, make sure they match
|
|
||||||
if(cname.find('.') != -1)
|
|
||||||
fail(format("Don't use a package specifier in the class name when the file name is also given (class %s, file %s)",
|
|
||||||
cname, fname));
|
|
||||||
|
|
||||||
if(icmp(file,cname) != 0)
|
|
||||||
fail(format("Class name %s does not match file name %s",
|
|
||||||
cname, fname));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Pick out the package part of the class name.
|
|
||||||
char[] pname = cname;
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
int ind = find(pname, '.');
|
|
||||||
if(ind != -1)
|
|
||||||
{
|
|
||||||
// Found a package name separator. Insert the package.
|
|
||||||
pack = pack.insertPackage(pname[0..ind]);
|
|
||||||
pname = pname[ind+1..$];
|
|
||||||
|
|
||||||
if(pname == "")
|
|
||||||
fail("Class name cannot end with a period: " ~ cname);
|
|
||||||
}
|
|
||||||
else break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cname = pname;
|
|
||||||
|
|
||||||
// Derive the file name from the given class name.
|
|
||||||
fname = pack.getPath(tolower(cname)) ~ ".mn";
|
|
||||||
}
|
|
||||||
|
|
||||||
assert(cname != "" && !cname.iEnds(".mn"));
|
|
||||||
assert(fname.iEnds(".mn"));
|
|
||||||
|
|
||||||
if(!isValidIdent(cname))
|
|
||||||
fail(format("Invalid class name %s (file %s)", cname, fname));
|
|
||||||
|
|
||||||
// At this point, check if the class already exists.
|
|
||||||
if(pack.ciInList(cname, mc))
|
|
||||||
{
|
|
||||||
// Match!
|
|
||||||
assert(mc !is null);
|
|
||||||
|
|
||||||
// If the class name was given, we must have an exact match.
|
|
||||||
if(cNameSet && (cname != mc.name.str))
|
|
||||||
fail(format("Searched for %s but could only find case insensitive match %s",
|
|
||||||
cname, mc.name.str));
|
|
||||||
|
|
||||||
// All is good, return the class.
|
|
||||||
return mc;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No existing class. Search for the script file.
|
|
||||||
if(!vfs.has(fname))
|
|
||||||
{
|
|
||||||
if(doThrow)
|
|
||||||
fail("Cannot find script file " ~ fname);
|
|
||||||
else return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a temporary file stream and load it
|
|
||||||
auto bf = vfs.open(fname);
|
|
||||||
auto ef = new EndianStream(bf);
|
|
||||||
int bom = ef.readBOM();
|
|
||||||
mc = new MonsterClass(pack);
|
|
||||||
mc.parse(ef, fname, bom);
|
|
||||||
delete bf;
|
|
||||||
|
|
||||||
// After the class is loaded, we can check its real name.
|
|
||||||
|
|
||||||
// If the name matches, we're done.
|
|
||||||
if(cname == mc.name.str) return mc;
|
|
||||||
|
|
||||||
// Allow a case insensitive match if useCase is false or the name
|
|
||||||
// was not given.
|
|
||||||
if((!useCase || !cNameSet) && (icmp(cname, mc.name.str) == 0)) return mc;
|
|
||||||
|
|
||||||
// Oops, name mismatch
|
|
||||||
fail(format("%s: Expected class name %s does not match loaded name %s",
|
|
||||||
fname, cname, mc.name.str));
|
|
||||||
assert(0);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (config.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
singleton Config;
|
|
||||||
|
|
||||||
// Only some config options have been moved into Monster. Key bindings
|
|
||||||
// and other low-level settings are still handled in D.
|
|
||||||
|
|
||||||
float musicVolume;
|
|
||||||
float sfxVolume;
|
|
||||||
float mainVolume;
|
|
||||||
bool useMusic;
|
|
||||||
|
|
||||||
float mouseSensX;
|
|
||||||
float mouseSensY;
|
|
||||||
bool flipMouseY;
|
|
||||||
|
|
||||||
import sound;
|
|
||||||
|
|
||||||
// TODO: This could be replaced by some sort of hook placed on
|
|
||||||
// mainVolume. Writing to the variable would automatically update
|
|
||||||
// everything.
|
|
||||||
setMainVolume(float f)
|
|
||||||
{
|
|
||||||
mainVolume = f;
|
|
||||||
Music.updateVolume();
|
|
||||||
}
|
|
||||||
|
|
||||||
setMusicVolume(float f)
|
|
||||||
{
|
|
||||||
musicVolume = f;
|
|
||||||
Music.updateVolume();
|
|
||||||
}
|
|
||||||
|
|
||||||
setSfxVolume(float f)
|
|
||||||
{
|
|
||||||
sfxVolume = f;
|
|
||||||
// TODO: Update something here
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the "real" music volume
|
|
||||||
float calcMusicVolume() { return mainVolume * musicVolume; }
|
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (console.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Console
|
|
||||||
|
|
||||||
|
|
||||||
// This class contains all the functions available to the ingame
|
|
||||||
// console.
|
|
||||||
|
|
||||||
import game
|
|
||||||
|
|
||||||
// Change player physics mode
|
|
||||||
native walk();
|
|
||||||
native fly();
|
|
||||||
native ghost();
|
|
||||||
|
|
||||||
// Clear the console
|
|
||||||
native clear();
|
|
||||||
|
|
||||||
// Set the console log font
|
|
||||||
native setfont(char[] name);
|
|
||||||
|
|
||||||
// Exit the game
|
|
||||||
native exit();
|
|
||||||
quit() { exit(); }
|
|
||||||
|
|
||||||
// Toggle wireframe mode
|
|
||||||
native wireframe();
|
|
||||||
twf() { wireframe() }
|
|
||||||
|
|
@ -1,35 +0,0 @@
|
|||||||
// Small script that prints the FPS to screen with regular intervals.
|
|
||||||
import frames;
|
|
||||||
|
|
||||||
// Set up the text widget
|
|
||||||
Widget txt = gui.text("StaticText",
|
|
||||||
gui.getWidth()-90, 10, 120, 30,
|
|
||||||
"Statistic");
|
|
||||||
txt.setNeedMouseFocus(false);
|
|
||||||
txt.setTextColor(1,1,1);
|
|
||||||
txt.setCaption("hello!");
|
|
||||||
|
|
||||||
// Sleep until rendering begins. This just prevents the first printed
|
|
||||||
// value from being 'nan'
|
|
||||||
fsleep(0);
|
|
||||||
|
|
||||||
// counter and totalTime (in the 'frames' module) are updated
|
|
||||||
// automatically by the system.
|
|
||||||
ulong lastFrame = counter;
|
|
||||||
float lastTime = totalTime;
|
|
||||||
|
|
||||||
float delay = 1.5;
|
|
||||||
|
|
||||||
while(true)
|
|
||||||
{
|
|
||||||
sleep(delay);
|
|
||||||
|
|
||||||
// Calculate differences since last frame
|
|
||||||
ulong fdiff = counter-lastFrame;
|
|
||||||
float tdiff = totalTime-lastTime;
|
|
||||||
|
|
||||||
txt.setCaption("fps: ", fdiff/tdiff);
|
|
||||||
|
|
||||||
lastFrame = counter;
|
|
||||||
lastTime = totalTime;
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (activator.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Activator : GameObject;
|
|
||||||
|
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (actor.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Actors are a common base class for creatures, NPCs and the player.
|
|
||||||
class Actor : GameObject;
|
|
||||||
|
|
||||||
// The actor's level
|
|
||||||
int level;
|
|
||||||
|
|
||||||
// Stats. These are the base values, before any positive or negative
|
|
||||||
// effects are applied.
|
|
||||||
int
|
|
||||||
baseStrength, baseIntelligence, baseWillpower, baseAgility,
|
|
||||||
baseSpeed, baseEndurance, basePersonality, baseLuck;
|
|
||||||
|
|
||||||
// Maximum health values, before any effects are applied.
|
|
||||||
int baseMaxHealth, baseMaxMana, baseMaxFatigue;
|
|
||||||
|
|
||||||
// Amount of gold this actor is carrying
|
|
||||||
int gold;
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (apparatus.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Covers all alchemy apparatus - mortars, retorts, etc
|
|
||||||
class Apparatus : InventoryItem;
|
|
||||||
|
|
||||||
enum AppaType : char[] altName
|
|
||||||
{
|
|
||||||
MortarPestle = 0 : "Mortar and Pestle",
|
|
||||||
Albemic = 1 : "Albemic"
|
|
||||||
Calcinator = 2 : "Calcinator"
|
|
||||||
}
|
|
||||||
|
|
||||||
float quality;
|
|
||||||
int type;
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (armor.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Covers all weapons and projectile weapons you can carry (like
|
|
||||||
// arrows and throwable items.)
|
|
||||||
class Armor : Repairable;
|
|
||||||
|
|
||||||
int type, armor;
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (book.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Book : EnchantItem;
|
|
||||||
|
|
||||||
bool isScroll;
|
|
||||||
|
|
||||||
int skillID; // Skill that is enhanced by reading this book, if any
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (clothing.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// All items that can be repaired (weapons and armor)
|
|
||||||
class Clothing : EnchantItem;
|
|
||||||
|
|
||||||
int type;
|
|
||||||
|
|
||||||
enum Type
|
|
||||||
{
|
|
||||||
Pants = 0,
|
|
||||||
Shoes = 1,
|
|
||||||
Shirt = 2,
|
|
||||||
Belt = 3,
|
|
||||||
Robe = 4,
|
|
||||||
RGlove = 5,
|
|
||||||
LGlove = 6,
|
|
||||||
Skirt = 7,
|
|
||||||
Ring = 8,
|
|
||||||
Amulet = 9
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (container.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Container : LockedObject;
|
|
||||||
|
|
||||||
float weight; // Not sure, might be max total weight allowed?
|
|
@ -1,36 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (creature.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Creature : Actor;
|
|
||||||
|
|
||||||
// Soul gem value
|
|
||||||
int soul;
|
|
||||||
|
|
||||||
// Not sure how to use these
|
|
||||||
int combat, magic, stealth;
|
|
||||||
|
|
||||||
// Attack values for various types of attack
|
|
||||||
int
|
|
||||||
attackMin1, attackMax1,
|
|
||||||
attackMin2, attackMax2,
|
|
||||||
attackMin3, attackMax3;
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (door.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Door : LockedObject;
|
|
||||||
|
|
||||||
// Does this door transport you to another cell?
|
|
||||||
bool teleport;
|
|
||||||
|
|
||||||
// Door destination
|
|
||||||
float destx, desty, destz;
|
|
||||||
float destr1, destr2, destr3;
|
|
||||||
char[] destCell;
|
|
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (enchantitem.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Items that can be enchanted
|
|
||||||
class EnchantItem : InventoryItem;
|
|
||||||
|
|
||||||
int enchant;
|
|
@ -1,79 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (gameobject.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// An object that exists inside a cell. All cell objects must have a
|
|
||||||
// position in space.
|
|
||||||
class GameObject;
|
|
||||||
|
|
||||||
// Is this object placed in a cell? isPlaced is true if the object is
|
|
||||||
// displayed inside a cell with a mesh and given 3D coordinates, and
|
|
||||||
// false otherwise (eg. if it is part of the player's inventory or in
|
|
||||||
// a container.)
|
|
||||||
|
|
||||||
// TODO: This will change to an actual cell reference later on, and be
|
|
||||||
// null if the object is not placed.
|
|
||||||
bool isPlaced;
|
|
||||||
|
|
||||||
// Position and rotation in space (only valid if isPlaced is true.)
|
|
||||||
float x, y, z;
|
|
||||||
float r1, r2, r3;
|
|
||||||
|
|
||||||
float scale = 1.0;
|
|
||||||
|
|
||||||
char[] name, id;
|
|
||||||
|
|
||||||
// Various variables that are currently unused. Most of the strings
|
|
||||||
// will be replaced by object references at some point.
|
|
||||||
|
|
||||||
// Owner of an object / activator
|
|
||||||
char[] owner;
|
|
||||||
|
|
||||||
// A global variable? Don't know what it's used for.
|
|
||||||
char[] glob;
|
|
||||||
|
|
||||||
// Reference to a soul trapped creature?
|
|
||||||
char[] soulID;
|
|
||||||
|
|
||||||
// Faction owner? Rank?
|
|
||||||
char[] cnam;
|
|
||||||
int indx;
|
|
||||||
|
|
||||||
// Magic value / health / uses of an item?
|
|
||||||
float xchg;
|
|
||||||
|
|
||||||
// These depend on the item in question
|
|
||||||
int intv, nam9;
|
|
||||||
int fltv;
|
|
||||||
int unam;
|
|
||||||
|
|
||||||
// TODO: Scripts
|
|
||||||
|
|
||||||
// Cute hack for the console
|
|
||||||
char[] pos()
|
|
||||||
{
|
|
||||||
// TODO: Make this simpler
|
|
||||||
char[] xx = x;
|
|
||||||
char[] yy = y;
|
|
||||||
char[] zz = z;
|
|
||||||
return "X:" ~ xx ~ " Y:" ~ yy ~ " Z:" ~ zz;
|
|
||||||
}
|
|
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (ingredient.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Alchemy ingredients
|
|
||||||
class Ingredient : InventoryItem;
|
|
||||||
|
|
||||||
// more to come here...
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (inventoryitem.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class InventoryItem : GameObject;
|
|
||||||
|
|
||||||
float weight;
|
|
||||||
int value;
|
|
||||||
|
|
||||||
// Reference to current container. Player / NPC inventories are also
|
|
||||||
// containers. Not used yet.
|
|
||||||
Container holder;
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (light.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Light : InventoryItem;
|
|
||||||
|
|
||||||
// Time left in seconds (for carried lights)
|
|
||||||
float lifetime;
|
|
||||||
|
|
||||||
int radius;
|
|
||||||
uint flags;
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (lockedobject.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Objects that can have a lock level and a trap
|
|
||||||
class LockedObject : GameObject;
|
|
||||||
|
|
||||||
// ID of key and trap type.
|
|
||||||
char[] key, trap;
|
|
||||||
|
|
||||||
int lockLevel;
|
|
@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (lockpick.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Lockpick : Tool;
|
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (misc.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Misc inventory items, like bottles, pots, pillows, keys etc. They
|
|
||||||
// are mostly useless (except keys), but may be bought and sold.
|
|
||||||
class Misc : InventoryItem;
|
|
||||||
|
|
||||||
// Not quite sure what the significance of this is. It is set to
|
|
||||||
// non-zero for some keys, but not for all.
|
|
||||||
int isKey;
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (npc.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class NPC : Person
|
|
||||||
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (person.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Person : Actor;
|
|
||||||
|
|
||||||
int disposition, reputation, rank;
|
|
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (player.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
singleton player : Person
|
|
||||||
|
|
||||||
name="Player Name"
|
|
||||||
level = 5 // Just an example value
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (potion.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
class Potion : InventoryItem;
|
|
||||||
|
|
||||||
int autoCalc;
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (probe.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Probes - tools used for disarming traps
|
|
||||||
class Probe : Tool;
|
|
@ -1,27 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (repairable.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// All items that can be repaired (weapons and armor)
|
|
||||||
class Repairable : EnchantItem;
|
|
||||||
|
|
||||||
int health;
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (repairitem.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Hammers etc. used to repair weapons and armor
|
|
||||||
class RepairItem : Tool;
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (static.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Static meshes - walls, buildings, rocks, etc.
|
|
||||||
class Static : GameObject;
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (tool.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Common base class for probes, lock picks and repair hammers.
|
|
||||||
class Tool : InventoryItem;
|
|
||||||
|
|
||||||
float quality;
|
|
||||||
int uses;
|
|
@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
OpenMW - The completely unofficial reimplementation of Morrowind
|
|
||||||
Copyright (C) 2008 Nicolay Korslund
|
|
||||||
Email: < korslund@gmail.com >
|
|
||||||
WWW: http://openmw.snaptoad.com/
|
|
||||||
|
|
||||||
This file (weapon.mn) is part of the OpenMW package.
|
|
||||||
|
|
||||||
OpenMW is distributed as free software: you can redistribute it
|
|
||||||
and/or modify it under the terms of the GNU General Public License
|
|
||||||
version 3, as published by the Free Software Foundation.
|
|
||||||
|
|
||||||
This program is distributed in the hope that it will be useful, but
|
|
||||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU General Public License
|
|
||||||
version 3 along with this program. If not, see
|
|
||||||
http://www.gnu.org/licenses/ .
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Covers all weapons and carry-able projectiles.
|
|
||||||
class Weapon : Repairable;
|
|
||||||
|
|
||||||
float speed, reach;
|
|
||||||
|
|
||||||
// Not set yet.
|
|
||||||
bool magical, silver;
|
|
||||||
bool twoHanded;
|
|
File diff suppressed because it is too large
Load Diff
@ -1,5 +0,0 @@
|
|||||||
/*
|
|
||||||
Widget hud;
|
|
||||||
hud = gui.loadLayout("openmw_hud_layout.xml");
|
|
||||||
hud.setCoord(10,10,500,500);
|
|
||||||
*/
|
|
@ -1,11 +0,0 @@
|
|||||||
// GUI module. Only covers some very basic functions.
|
|
||||||
module gui;
|
|
||||||
|
|
||||||
// Later, we'll have singleton gui : Widget; with most of the
|
|
||||||
// functions defined in Widget.
|
|
||||||
|
|
||||||
native Widget loadLayout(char[] file);
|
|
||||||
native Widget text(char[] skin, int x, int y, int w, int h,
|
|
||||||
char[] layer);
|
|
||||||
native int getWidth();
|
|
||||||
native int getHeight();
|
|
@ -1,10 +0,0 @@
|
|||||||
class Widget;
|
|
||||||
|
|
||||||
native setNeedMouseFocus(bool b);
|
|
||||||
native setTextColor(float r, float g, float b);
|
|
||||||
native setCoord(int x, int y, int w, int h);
|
|
||||||
|
|
||||||
native setCaption(char[][] str...);
|
|
||||||
|
|
||||||
// Find a named child widget
|
|
||||||
native Widget get(char[] name);
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue