diff --git a/.gitignore b/.gitignore index c38aabac4..378eac25d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1 @@ -screenshot*.png -*.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 +build diff --git a/old_d_version/COMPILE-linux.txt b/old_d_version/COMPILE-linux.txt deleted file mode 100644 index 56dad8fa1..000000000 --- a/old_d_version/COMPILE-linux.txt +++ /dev/null @@ -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! ;-) diff --git a/old_d_version/COMPILE-win32.txt b/old_d_version/COMPILE-win32.txt deleted file mode 100644 index bf06913d1..000000000 --- a/old_d_version/COMPILE-win32.txt +++ /dev/null @@ -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! diff --git a/old_d_version/Makefile b/old_d_version/Makefile deleted file mode 100644 index 201b6f004..000000000 --- a/old_d_version/Makefile +++ /dev/null @@ -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/ diff --git a/old_d_version/README-win32.txt b/old_d_version/README-win32.txt deleted file mode 100644 index 10a9f0270..000000000 --- a/old_d_version/README-win32.txt +++ /dev/null @@ -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. diff --git a/old_d_version/README.txt b/old_d_version/README.txt deleted file mode 100644 index e556eb012..000000000 --- a/old_d_version/README.txt +++ /dev/null @@ -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 diff --git a/old_d_version/build_openmw.bat b/old_d_version/build_openmw.bat deleted file mode 100755 index d2a62a8de..000000000 --- a/old_d_version/build_openmw.bat +++ /dev/null @@ -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 diff --git a/old_d_version/core/inifile.d b/old_d_version/core/inifile.d deleted file mode 100644 index fa3c7434f..000000000 --- a/old_d_version/core/inifile.d +++ /dev/null @@ -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); - } - } -} diff --git a/old_d_version/core/memory.d b/old_d_version/core/memory.d deleted file mode 100644 index 3bb1f414d..000000000 --- a/old_d_version/core/memory.d +++ /dev/null @@ -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"); -} diff --git a/old_d_version/core/resource.d b/old_d_version/core/resource.d deleted file mode 100644 index 4747c16d3..000000000 --- a/old_d_version/core/resource.d +++ /dev/null @@ -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"); - } -} -+/ diff --git a/old_d_version/monster/compiler/assembler.d b/old_d_version/monster/compiler/assembler.d deleted file mode 100644 index 690b9e45e..000000000 --- a/old_d_version/monster/compiler/assembler.d +++ /dev/null @@ -1,1686 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (assembler.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.assembler; - -import std.string; -import std.stdio; - -import monster.util.list; - -import monster.compiler.bytecode; -import monster.compiler.linespec; -import monster.compiler.types; -import monster.compiler.tokenizer; - -import monster.vm.error; -import monster.options; - -// Element type -enum ET - { - None, // Undefined - OpCode, // Instruction, variable number of bytes - Data1, // One byte of data - Data4, // One int of data - Data8, // Two ints of data - Ptr, // Data pointer - Label, // Jump label - JumpPtr, // Appears after a jump instruction, referes - // to a label. - JumpIndex, // Refers to a label by index, not by pointer - StackPtr, // Reference to a stack value - is replaced - // with a normal Ptr during compilation - StackMark, // A marked stack position - MarkRef, // Reference to a previously marked stack position - } - -// Represents one element in the code list. -struct CodeElement -{ - ET type; - union - { - ubyte bytes[8]; - ubyte bdata; - int idata; - long ldata; - } - - CodeElement *label; // Destination for jumps - - // Cumulative offset into the final code buffer - int offset = -1; - - // Source code line corresponding to this instruction - int line = -1; - - // Label number. This is different from the label / jump index, - // which is a low-level reference. One label number is given to each - // named label in state code, and these are used to set up state - // offsets correctly. - int lnumber = -1; - - // Represents the stack imprint of this element. May be positive, - // zero or negative. - int imprint; - - // Current stack position BEFORE this instruction is executed. This - // is the cumulative imprints of all the elements before us. - int stackPos = -1; - - // Counts the number of jumps to this label. Labels with zero - // ingoing jumps are deleted. - int jumps = 0; - - // Set the offset to val, return the offset of the next element (val - // + size of this) - uint setOffset(uint val) - { - offset = val; - return val + getSize(); - } - - bool isCmd(BC ops[]...) - { - if(type != ET.OpCode) return false; - - foreach(op; ops) - if(idata == op) return true; - - return false; - } - - bool isStateLabel() - { return type == ET.Label && lnumber != -1; } - - // Set the stack position, returns the next stack position - int setStack(int stack) - { - if(stackPos != -1) - assert(stack == stackPos, "Stack mismatch detected"); - - stackPos = stack; - return stack + imprint; - } - - // Insert ourselves into the code block at the right offset. - void insertCode(ubyte[] code) - { - // Zero-size elements don't have any code - if(getSize() == 0) return; - - // Jumps are handled in a special way - assert(type != ET.JumpIndex); - if(type == ET.JumpPtr) - { - // Convert it back to a number, but this time the label offset - type = ET.JumpIndex; - idata = label.offset; - } - - // Write the data - int s = getSize(); - code[offset..offset+s] = bytes[0..s]; - } - - // A principal element is one that may own other elements behind it, - // and it is not owned by any other element. - bool isPrincipal() - { - return - type == ET.Label || - type == ET.OpCode || - type == ET.StackMark; - } - - void print(LineSpec[] lines) - { - int index; - PT ptype; - - // These start a new line - if(isPrincipal()) - { - int fline = findLine(lines, offset); - if(fline != line) - writef("\n%s(%s) ", line, fline); - else - writef("\n%s ", line); - writef("stk=%s(%s): ", stackPos, imprint); - } - - switch(type) - { - case ET.Label: - writef("label %s @ %s", idata, offset); - if(jumps == 0) writef(" (inactive)"); - break; - - case ET.OpCode: - writef("%s", bcToString[idata]); - break; - - case ET.Data1: - writef(", byte ", idata); - break; - - case ET.JumpPtr: - writef(" -> label %s @ %s", label.idata, label.offset); - break; - - case ET.Ptr: - decodePtr(idata, ptype, index); - writef(", ptr(%s, %s)", ptype, index); - break; - - case ET.Data4: - writef(", int %s", idata); - break; - - case ET.Data8: - case ET.None: - default: - assert(0); - } - } - - void setMark(CodeElement *mrk) - { - assert(type == ET.MarkRef); - assert(mrk !is null); - assert(mrk.type == ET.StackMark); - - label = mrk; - mrk.jumps++; - } - - void setLabel(CodeElement *lab) - { - // Check that we only get called on jumps - assert(type == ET.JumpIndex); - - // And check that the label actually matches this jump - assert(lab !is null); - assert(lab.type == ET.Label); - assert(lab.idata == idata); - - label = lab; - lab.jumps++; - type = ET.JumpPtr; - } - - // Get the size of this element in the generated code. It does not - // determine how much data is stored by this struct, or which - // variable above it uses, only how big the result is in the - // finished code buffer. - int getSize() - { - switch(type) - { - case ET.Label: - return 0; - - case ET.OpCode: - assert(idata > 0); - // Instructions are either one or two bytes. Single byte - // instructions have values <= 255, everything else is coded - // with two bytes. - if(idata < 256) return 1; - assert(0, "not used yet"); - return 2; - - case ET.Data1: - return 1; - - case ET.JumpPtr: - case ET.JumpIndex: - case ET.Ptr: - case ET.StackPtr: - case ET.Data4: - return 4; - - case ET.Data8: - return 8; - - case ET.None: - default: - assert(0); - } - } -} - -Assembler tasm; - -// A structure that takes a series of pseudo-assembler commands as -// input and stores them in a list of byte code instructions and data. -// We can perform various optimizations on this command list (not -// implemented yet) before storing it to file. -struct Assembler -{ - private: -LinkedList!(CodeElement) codeList; - - int toInt(float f) { return *(cast(int*)&f); } - - // Add a new code element to the list - void add(ET type, long data) - { - if(type == ET.OpCode && data > 255) - { - // Instruction values over 255 are stored as two bytes - data -= 256; - assert(data < 255); - - data <<= 8; // Move the instruction into the next byte - data += BC.MultiByte; // Set the multi-byte indicator - } - - CodeElement ce; - ce.ldata = data; - ce.type = type; - ce.line = line; - codeList ~= ce; - } - - // Low level opcode functions - - // Add an opcode, with optional stack imprint - void cmd(BC code, int stk=0) - { - add(ET.OpCode, code); - if(stk != 0) setStack(stk); - } - - void addb(int i) - { - assert(i >= 0 && i < 256); - add(ET.Data1, i); - } - void addib(int i) - { - assert(i >= -128 && i < 127); - add(ET.Data1, i); - } - void addi(int i) { add(ET.Data4, i); } - void addl(long l) { add(ET.Data8, l); } - void label_add(int i) { add(ET.Label, i); } - void jump_add(int i) { add(ET.JumpIndex, i); } - - // Current index in the table of jump points - int jumpIndex; - - // Highest inserted label number - int maxNumber; - - // Current line number - int line; - - // Add a command that varies depending on a type size. If size is > - // 1, it is added as an integer parameter to the instruction. - void cmdmult(BC codeOne, BC codeMult, int size, int stk=0) - { - assert(size >= 1); - if(size == 1) cmd(codeOne, stk); - else - { - cmd(codeMult, stk); - addi(size); - } - } - - // Like cmdmult, but size can only be 1 or 2, and the size isn't - // added. - void cmd2(BC codeOne, BC codeTwo, int size, int stk=0) - { - assert(size == 1 || size == 2); - if(size == 1) cmd(codeOne, stk); - else cmd(codeTwo, stk); - } - - // Set the stack imprint of the last element in the code list - void setStack(int imp) - { - assert(codeList.length > 0); - - with(codeList.getTail().value) - { - // Only allowed for opcodes - assert(type == ET.OpCode); - imprint = imp; - } - } - - public: - - // Start a new function - void newFunc() - { - codeList.reset(); - jumpIndex = 0; - maxNumber = -1; - line = -1; - } - - // Set the current line. A line table is stored in the file, so - // specific instructions can be translated to a line in the source - // file. Mainly used for debugging. - void setLine(int ln) - { - // Never overwrite a valid line with -1 - if(ln == -1) return; - - line = ln; - } - - int getLine() { return line; } - - // Higher level opcode commands - - // Exit a function. params is the number of function parameters - // and/or local variables to pop, ret is the return value size - void exit(int params, int locals, int ret) - { - // Calculate the stack imprint. Notice that we omit the params, - // since they are irrelevant for this function (outside the - // current 'frame', and we want to end up at stack level zero. - int imprint = -locals -ret; - - // Count the parameters and locals as one from here on. - params += locals; - - if(params) - { - if(ret == 1) - { - // Function has parameters and a return value of size 1 - cmd(BC.ReturnVal, imprint); - addi(params); - } - else if(ret > 1) - { - // Return value larger than 1 - cmd(BC.ReturnValN, imprint); - addi(params); - addi(ret); - } - else - { - // Parameters, but no return value - cmd(BC.Return, imprint); - addi(params); - } - return; - } - - // No parameters / local variables. The return value (if any) is - // already on the top of the stack, so we can just exit quietly - // and pass it along. - cmd(BC.Exit, imprint); - } - - void error(Err code) - { - cmd(BC.Error); - addb(code); - - assert(code != 0); - } - - void call(bool isFar, int imprint) - { - // The stack imprint from the instruction itself must be added to - // the imprint from the function call (which removes parameters - // but pushes a return value onto the stack.) - if(isFar) cmd(BC.CallFar, -2 + imprint); - else cmd(BC.Call, -1 + imprint); - } - - void newObj(int clsIndex, int params, int imprint) - { - // The instruction removes all the pushed parameters, and pushes - // an object index. - cmd(BC.New, 1-imprint); - addi(clsIndex); - addi(params); - } - - void cloneObj() { cmd(BC.Clone); } - - // Copy the topmost int on the stack - void dup() { cmd(BC.Dup, 1); } - - /* All jump and label commands come in two versions, one for - jump-before-label, and one for label-before-jump. Eg.: - - Label-before-jump: - - int l1 = tasm.label(); // Jump back here - ... - tasm.jumpz(l1); - - Jump-before-label: - - int l2 = tasm.jump(); - ... - tasm.label(l2); // Jump forwards to here - - - The following other cases are also currently possible: - - - Orphaned (backward) label: - tasm.label() with no corresponding jump - - - Multiple backward jumps: - a=tasm.label(); tasm.jump(a); tasm.jump(a); ... - - Other combinations are NOT allowed! However, these are enough to - implement more complex jump patterns like break/continue. The - more advanced combinations are handled in LabelStatement and - GotoStatement in statement.d - */ - - // Generate the jump opcode, and set the stack adjustment - // accordingly - private void jumpOp(BC code) - { - cmd(code); - - // JumpZ and JumpNZ pop a value from the stack - if(code != BC.Jump) - setStack(-1); - } - - // Generic jump command to jump to a labed that is not yet - // defined. Returns a jump index. - private int genjump(BC code) - { - jumpOp(code); - - // Add and return the current jump index. Then increase it for the - // next jump. - jump_add(jumpIndex); - return jumpIndex++; - } - - // Generic version to jump to an existing label index - private void genjump(BC code, int label) - { - jumpOp(code); - jump_add(label); - } - - // Jump if zero - int jumpz() { return genjump(BC.JumpZ); } - void jumpz(int i) { genjump(BC.JumpZ, i); } - - // Jump if non-zero - int jumpnz() { return genjump(BC.JumpNZ); } - void jumpnz(int i) { genjump(BC.JumpNZ, i); } - - // Just jump dammit! - int jump() { return genjump(BC.Jump); } - void jump(int i) { genjump(BC.Jump, i); } - - // Used for label before jump - int label() - { - label_add(jumpIndex); - return jumpIndex++; - } - - // Label after jump - void label(int i) { label_add(i); } - - // Assign a lnumber to the last label element (used by the functions - // below) - private void setLNumber(int number) - { - with(codeList.getTail().value) - { - // Some sanity checks - assert(type == ET.Label); - assert(lnumber == -1); - - // Set the label number - lnumber = number; - } - - // Remember the max number given - if(number > maxNumber) maxNumber = number; - } - - // Assign a number to a label (used for named labels, to get their - // offset) - int labelNum(int number) - { - int l = label(); - - if(number != -1) - setLNumber(number); - - return l; - } - - void labelNum(int i, int number) - { - label(i); - - if(number != -1) - setLNumber(number); - } - - // Push the current stack position - void getStack() { cmd(BC.GetStack, 1); } - - // Variable and stack management - void push(int i) - { - cmd(BC.PushData, 1); - addi(i); - } - - void push(float f) { push(*(cast(int*)&f)); } - void push(uint u) { push(*(cast(int*)&u)); } - - union Share - { - double d; - long l; - ulong u; - int ints[2]; - } - - void push8(long l) - { - Share s; - s.l = l; - pushArray(s.ints); - } - - void push8(ulong u) - { - Share s; - s.u = u; - pushArray(s.ints); - } - - void push8(double d) - { - Share s; - s.d = d; - pushArray(s.ints); - } - - void pushArray(int[] data ...) - { - // TODO: We should use a push8 instructions or similar for larger - // data types, but that's optimization. - foreach(int i; data) - push(i); - } - - // Push a value from the stack - void pushLocal(int index, int siz) - { - assert(siz >= 1); - assert(index >= 0 || index <= -siz); - - // Local variables are counted forwards from the current stack - // frame pointer. The frame pointer is set whenever a new function - // starts, that is AFTER the parameters have been pushed. Thus we - // count parameters backwards from the stack pointer, and - // variables forwards. - - // For multi-int variables, just push each int as a separate - // variable. We can optimize this later. - while(siz-- > 0) - { - cmd(BC.PushLocal, 1); - addi(index++); - } - } - - // Push a value from a class variable. The second parameter is the - // number of ints to push. - void pushClass(int index, int siz) - { - assert(siz >= 1); - assert(index >= 0); - - while(siz-- > 0) - { - cmd(BC.PushClassVar, 1); - addi(index++); - } - } - - void pushParentVar(int index, int classIndex, int siz) - { - assert(index >= 0); - assert(classIndex >= 0); - assert(siz >= 1); - - while(siz-- > 0) - { - cmd(BC.PushParentVar, 1); - addi(classIndex); - addi(index++); - } - } - - // Push a variable from another object - void pushFarClass(int index, int classIndex, int siz) - { - assert(index >= 0); - assert(classIndex >= 0); - assert(siz >= 1); - - // The FAR versions of variables, assumes that the object - // reference has already been pushed. - cmdmult(BC.PushFarClassVar, BC.PushFarClassMulti, siz, siz-1); - addi(classIndex); - addi(index++); - } - - // Push 'this', the current object reference - void pushThis() { cmd(BC.PushThis, 1); } - - // Push the singleton object of the given class index - void pushSingleton(int classIndex) - { - assert(classIndex >= 0); - cmd(BC.PushSingleton, 1); - addi(classIndex); - } - - // Push a coded pointer, consisting of the type pt and the given - // index. - private void pushPtr(PT pt, int index) - { - cmd(BC.PushData, 1); - // Add the coded pointer as the pushed data - add(ET.Ptr, codePtr(pt, index)); - } - - // Push addresses instead of values - - // Pointer to data segment of this object - void pushClassAddr(int i) - { - pushArray(0,0); - pushPtr(PT.DataOffs, i); - } - - // Imported local variable - void pushParentVarAddr(int i, int classIndex) - { - assert(classIndex >= 0); - - push(0); - push(classIndex); - pushPtr(PT.DataOffsCls, i); - } - - // Data segment of another object, but this class (use offset.) The - // object index is already on the stack - void pushFarClassAddr(int i, int classIndex) - { - assert(classIndex >= 0); - push(classIndex); - pushPtr(PT.FarDataOffs, i); - } - - // Local stack variable - void pushLocalAddr(int i) - { - pushArray(0,0); - cmd(BC.PushData, 1); - add(ET.StackPtr, i); - } - - CodeElement *markStack() - { - add(ET.StackMark, 0); - return &codeList.getTail().value; - } - - void pushMark(CodeElement *mark) - { - assert(mark !is null); - cmd(BC.PushLocal, 1); - add(ET.MarkRef, 1); - codeList.getTail().value.setMark(mark); - } - - // Get the function part of a function reference (currently just a - // string containing the name) - void refFunc() { cmd(BC.RefFunc, -1); } - - // Push the address of an array element. The index and array are - // already on the stack. - void elementAddr() { pushPtr(PT.ArrayIndex,0); } - - // Convert an array index to a const reference - void makeArrayConst() { cmd(BC.MakeConstArray); } - - // Check if an array is const - void isArrayConst() { cmd(BC.IsConstArray); } - - void makeSlice() { cmd(BC.Slice, -2); } - - // Pops the last 'len' elements off the stack and creates an array - // from them. The element size (in ints) is given in the second - // parameter. - void popToArray(int len, int elemSize) - { - cmd(BC.PopToArray, 1-len*elemSize); - addi(len*elemSize); - addi(elemSize); - } - - // Create a new array, initialized with the given value and with the - // given dimension number. The lengths are popped of the stack. - void newArray(int ini[], int rank) - { - cmd(BC.NewArray, 1-rank); - addi(rank); - addi(ini.length); - foreach(i; ini) addi(i); - } - - // Copies two array references from the stack, copies the data in - // the last into the first. The (raw) lengths must match. - void copyArray() { cmd(BC.CopyArray, -2); } - - // Fills an array with a specific value. - void fillArray(int size) - { - cmd(BC.FillArray, -size-1); - addi(size); - } - - // Creates a copy of the array whose index is on the stack - void dupArray() { cmd(BC.DupArray); } - - // Just remove a value from the stack (will later be optimized away - // in most cases.) - void pop(int i=1) - { - if(i == 0) return; - if(i == 1) cmd(BC.Pop, -1); - else - { - cmd(BC.PopN, -i); - addb(i); - } - } - - void mov(int s) - { - // A Store* removes a pointer (3 ints) plus the value itself (s - // ints) - if(s < 3) cmd2(BC.Store, BC.Store8, s, -s-3); - else cmdmult(BC.Store, BC.StoreMult, -s-3); - } - - // Set object state to the given index - void setState(int st, int label, int cls) - { - cmd(BC.State); - addi(st); - addi(label); - addi(cls); - } - - // Get the given field of an enum. If field is -1, get the "value" - // field. - void getEnumValue(Type type, int field=-1) - { - assert(type.isEnum, "given type is not an enum"); - - auto et = cast(EnumType)type; - assert(et !is null); - - if(field == -1) - cmd(BC.EnumValue, 1); - else - { - assert(field >= 0 && field <= et.fields.length); - - // Get the number of bytes we're pushing onto the stack - int size = et.fields[field].type.getSize(); - - cmd(BC.EnumField, size-1); - addi(field); - } - addi(type.tIndex); - } - - void enumValToIndex(int tIndex) - { - assert(Type.typeList[tIndex].isEnum, - "given type index is not an enum"); - cmd(BC.EnumValToIndex, -1); - addi(tIndex); - } - - void enumNameToIndex(int tIndex) - { - assert(Type.typeList[tIndex].isEnum, - "given type index is not an enum"); - cmd(BC.EnumNameToIndex); - addi(tIndex); - } - - // Fetch an element from an array - void fetchElement(int s) { cmd(BC.FetchElem, -2+s); } - - // Array concatination. The first concatinates two arrays, the - // others adds a single element to the array from either side. - void catArray() { cmd(BC.CatArray, -1); } - void catArrayLeft(int s) { cmd(BC.CatLeft, -s); addi(s); } - void catArrayRight(int s) { cmd(BC.CatRight, -s); addi(s); } - - // Get the length of an array. Converts the array index to an int - // (holding the length) on the stack. - void getArrayLength() { cmd(BC.GetArrLen); } - - // Reverse an array in place - void reverseArray() { cmd(BC.ReverseArray); } - - // Create an array iterator - void createArrayIterator(bool isRev, bool isRef) - { - cmd(BC.CreateArrayIter, 1); - addb(isRev); - addb(isRef); - } - - // Create a class iterator - void createClassIterator(int classNum) - { - cmd(BC.CreateClassIter, 2); - addi(classNum); - } - - // Do the next iteration - void iterateNext() { cmd(BC.IterNext, 1); } - - // Break off iteration - void iterateBreak() { cmd(BC.IterBreak); } - - void iterateUpdate(int pos) - { - assert(pos >= 0); - cmd(BC.IterUpdate); - addi(pos); - } - - // Value modifiers - void add(Type t) - { - if(t.isInt || t.isUint) cmd(BC.IAdd); - else if(t.isLong || t.isUlong) cmd(BC.LAdd); - else if(t.isFloat) cmd(BC.FAdd); - else if(t.isDouble) cmd(BC.DAdd); - else assert(0); - setStack(-t.getSize()); - } - - void sub(Type t) - { - if(t.isInt || t.isUint) cmd(BC.ISub); - else if(t.isLong || t.isUlong) cmd(BC.LSub); - else if(t.isFloat) cmd(BC.FSub); - else if(t.isDouble) cmd(BC.DSub); - else assert(0); - setStack(-t.getSize()); - } - - void mul(Type t) - { - if(t.isInt || t.isUint) cmd(BC.IMul); - else if(t.isLong || t.isUlong) cmd(BC.LMul); - else if(t.isFloat) cmd(BC.FMul); - else if(t.isDouble) cmd(BC.DMul); - else assert(0); - setStack(-t.getSize()); - } - - void div(Type t) - { - if(t.isInt) cmd(BC.IDiv); - else if(t.isUint) cmd(BC.UDiv); - else if(t.isLong) cmd(BC.LDiv); - else if(t.isUlong) cmd(BC.ULDiv); - else if(t.isFloat) cmd(BC.FDiv); - else if(t.isDouble) cmd(BC.DDiv); - else assert(0); - setStack(-t.getSize()); - } - - void idiv(Type t) - { - if(t.isIntegral) div(t); - else if(t.isFloat) cmd(BC.FIDiv); - else if(t.isDouble) cmd(BC.DIDiv); - else assert(0); - setStack(-t.getSize()); - } - - void divrem(Type t) - { - if(t.isInt) cmd(BC.IDivRem); - else if(t.isUint) cmd(BC.UDivRem); - else if(t.isLong) cmd(BC.LDivRem); - else if(t.isUlong) cmd(BC.ULDivRem); - else if(t.isFloat) cmd(BC.FDivRem); - else if(t.isDouble) cmd(BC.DDivRem); - else assert(0); - setStack(-t.getSize()); - } - - void neg(Type t) - { - if(t.isInt) cmd(BC.INeg); - else if(t.isLong) cmd(BC.LNeg); - else if(t.isFloat) cmd(BC.FNeg); - else if(t.isDouble) cmd(BC.DNeg); - else assert(0); - } - - void incDec(TT op, bool postfix, int s) - { - // Each instruction pops a ptr (3 ints) and pushes a result (s - // ints) - if(op == TT.PlusPlus) - if(postfix) cmd2(BC.PostInc, BC.PostInc8, s, s-3); - else cmd2(BC.PreInc, BC.PreInc8, s, s-3); - else if(op == TT.MinusMinus) - if(postfix) cmd2(BC.PostDec, BC.PostDec8, s, s-3); - else cmd2(BC.PreDec, BC.PreDec8, s, s-3); - else - assert(0, "Illegal op type"); - } - - // Type casting - void castStrToChar() { cmd(BC.CastS2C); } - - void castIntToLong(bool fromSign) - { - if(fromSign) cmd(BC.CastI2L, 1); - - // Converting to unsigned long is as simple as setting zero as the - // upper int. We don't need a separate instruction to do this. - else push(0); - } - - void castLongToInt() { pop(); } - - void castFloatToInt(Type fr, Type to) - { - assert(fr.isFloating); - assert(to.isIntegral); - if(fr.isFloat) - { - if(to.isInt) cmd(BC.CastF2I); - else if(to.isUint) cmd(BC.CastF2U); - else if(to.isLong) cmd(BC.CastF2L); - else if(to.isUlong) cmd(BC.CastF2UL); - else assert(0); - } - else - { - assert(fr.isDouble); - if(to.isInt) cmd(BC.CastD2I); - else if(to.isUint) cmd(BC.CastD2U); - else if(to.isLong) cmd(BC.CastD2L); - else if(to.isUlong) cmd(BC.CastD2UL); - else assert(0); - } - setStack(to.getSize() - fr.getSize()); - } - - void castIntToFloat(Type fr, Type to) - { - assert(fr.isIntegral); - assert(to.isFloating); - if(to.isFloat) - { - if(fr.isInt) cmd(BC.CastI2F); - else if(fr.isUint) cmd(BC.CastU2F); - else if(fr.isLong) cmd(BC.CastL2F); - else if(fr.isUlong) cmd(BC.CastUL2F); - else assert(0); - } - else - { - assert(to.isDouble); - if(fr.isInt) cmd(BC.CastI2D); - else if(fr.isUint) cmd(BC.CastU2D); - else if(fr.isLong) cmd(BC.CastL2D); - else if(fr.isUlong) cmd(BC.CastUL2D); - else assert(0); - } - setStack(to.getSize() - fr.getSize()); - } - - void castFloatToFloat(int fs, int ts) - { - if(fs == 1 && ts == 2) cmd(BC.CastF2D); - else if(fs == 2 && ts == 1) cmd(BC.CastD2F); - else assert(0); - setStack(ts-fs); - } - - // Cast a generic type to string - void castToString(Type from) - { - cmd(BC.CastT2S, 1-from.getSize()); - addi(from.tIndex); - } - - // Cast an object to a given class. Takes a global class index. The - // object on the stack isn't changed in any way, but the VM checks - // if a cast is valid. - void downCast(int cindex) - { - cmd(BC.DownCast); - addi(cindex); - } - - // Boolean operators - void isEqual(int s) - { - cmdmult(BC.IsEqual, BC.IsEqualMulti, s, 1-2*s); - } - - void isCaseEqual() { cmd(BC.IsCaseEqual, -1); } - - void less(Type t) - { - if(t.isUint || t.isChar) cmd(BC.ULess); - else if(t.isInt) cmd(BC.ILess); - else if(t.isLong) cmd(BC.LLess); - else if(t.isFloat) cmd(BC.FLess); - else if(t.isUlong) cmd(BC.ULLess); - else if(t.isDouble) cmd(BC.DLess); - setStack(1-2*t.getSize()); - } - - void not() { cmd(BC.Not); } - - void cmpArray() { cmd(BC.CmpArray, -1); } - void icmpString() { cmd(BC.ICmpStr, -1); } - - // Convert the current code list to byte code, and return it. The - // lines parameter is filled with an array of line specifiers that - // can be used to translate instruction position to a source code - // line number. The offsets parameter, when not null, must be of - // length maxNumber+1. It will be filled with the offsets of - // numbered labels. - ubyte[] assemble(ref LineSpec lines[], uint offsets[] = null) - in - { - if(offsets.length != 0) - assert(offsets.length == 1+maxNumber, - "offset table must match number of named labels"); - else - assert(maxNumber == -1, - "assemble() called with named labels but nowhere to store offsets"); - } - body - { - lines = null; - - // List of jumps, used for converting jump numbers to specific - // labels. - CodeElement* jumpList[]; - CodeElement* labelList[]; - - jumpList.length = jumpIndex; - labelList.length = jumpIndex; - - // Go through the code list and resolve jumps and labels - foreach(ref CodeElement ce; codeList) - { - assert(ce.type != ET.JumpPtr); - - if(ce.type == ET.JumpIndex) - { - // Has the label been looked up? - if(labelList[ce.idata] != null) - // Set the jump pointer to replace the index - ce.setLabel(labelList[ce.idata]); - else - { - // Store the jump until the label is found. There can - // only be one jump stored in each jumplist element. - assert(jumpList[ce.idata] == null); - jumpList[ce.idata] = &ce; - } - continue; - } - - if(ce.type == ET.Label) - { - // Is there a jump that refers to this label? - if(jumpList[ce.idata] != null) - { - // Make the jump point to this label. - jumpList[ce.idata].setLabel(&ce); - jumpList[ce.idata] = null; - } - else - // Set the label up for later referal - labelList[ce.idata] = &ce; - } - } - - // Run optimizations on the instruction list - static if(optimizeAsm) - optimize(); - - // Next, go through the list and compute stack positions. We have - // to loop the entire block since we're not always able to fix the - // entire code in one go. - bool changed, dead; - do - { - changed = false; - dead = false; - - int stackPos = 0; - Node node = codeList.getHead(); - while(node !is null) - { - // Do we currently have a stack position to set? - if(stackPos != -1) - { - // Remember if we change anything - if(node.value.stackPos == -1) - changed = true; - - // Set the stack. If we're overwriting an existing - // value, setStack will check that it's correct. - stackPos = node.value.setStack(stackPos); - - assert(stackPos >= 0, "Assembler detected stack underflow"); - } - else - { - // Nope, we don't have any information to give - - if(node.value.stackPos == -1) - // Neither has the code. That means we have dead - // code (unset stack values) - dead = true; - else - // This code point has a stack value - this means - // that it's a label that has been 'marked' - // previously by a jump command. Let's copy its - // value. - { - assert(node.value.type == ET.Label); - assert(node.value.imprint == 0); - stackPos = node.value.stackPos; - } - } - - // Did we have or get a stack value this round? - if(stackPos != -1) - { - // The stack position should be exactly zero when we enter - // and exit the function. That includes state labels. - if(isCmd(node, BC.Return, BC.ReturnVal, BC.ReturnValN, - BC.Exit) || node.value.isStateLabel()) - assert(stackPos == 0); - - // For jumps, mark the label as well - if(isCmd(node, BC.Jump, BC.JumpZ, BC.JumpNZ)) - { - auto ptr = node.getNext(); - assert(ptr.value.type == ET.JumpPtr); - auto ce = ptr.value.label; - assert(ce !is null); - ce.setStack(stackPos); - } - } - - // After exits, returns and jumps, we don't know what the - // stack value is. - if(isCmd(node, BC.Jump, BC.Return, BC.ReturnVal, BC.ReturnValN, - BC.Exit)) - stackPos = -1; - - node = getNext(node); - } - } - while(changed && dead); - - static if(optimizeAsm) - // Optimization should have removed all dead code - assert(!dead); - - // Replace all PushLocal indices with relative indices. - { - Node node = codeList.getHead(); - - while(node !is null) - { - auto next = node.getNext(); - if(next is null) - break; - - int sp = node.value.stackPos; - - if(isCmd(node, BC.PushLocal, BC.IterUpdate)) - { - with(next.value) - { - if(sp != -1) - { - // Normal index reference - if(type == ET.Data4) - { - assert(idata < sp); - idata = sp-idata-1; - } - // Marked stack value - else if(type == ET.MarkRef) - { - assert(label !is null); - assert(label.jumps > 0); - label.jumps--; - - // Get the marked stack position - int sp2 = label.stackPos; - assert(sp2 != -1); - - // Convert to relative index and store it as - // a normal pushlocal - assert(sp2 < sp); - idata = sp-sp2-1; - type = ET.Data4; - } - else assert(0); - } - } - } - - else if(isCmd(node, BC.PushData) && - next.value.type == ET.StackPtr) - { - // Convert to a normal pointer - next.value.type = ET.Ptr; - - // Find the relative stack position - int rel = next.value.idata; - assert(rel < sp); - rel = sp-rel-1; - next.value.idata = codePtr(PT.Stack,rel); - } - - node = next; - } - } - - // Kill stack marks - { - Node node = codeList.getHead(); - while(node !is null) - { - auto next = node.getNext(); - if(node.value.type == ET.StackMark) - codeList.remove(node); - node = next; - } - } - - // TODO: Do additional PushLocal-optimization here. We only need - // specific optimizations that deal with pushlocal, such as - // replacing pushlocal 0 with dup, or with replacing pushlocal + - // getarrlen (a normal combination for $) with a specialized - // instruction. - - // Also TODO: Move all the optimization code to asmopt.d, or this - // file will get too messy. - - // Finishing up. Go through the list and set the offsets and the - // line specifications. - uint offset = 0; - LineSpec ls; - ls.line = -2; - ls.pos = -1; - foreach(ref CodeElement ce; codeList) - { - // Only store line specs for opcodes - if(ce.type == ET.OpCode && - ls.line != ce.line && offset != ls.pos) - { - // New line! - ls.pos = offset; - ls.line = ce.line; - lines ~= ls; - } - - offset = ce.setOffset(offset); - } - - // Output the current code list - static if(printAsmOutput) - { - writef("\nCompiled function:"); - foreach(CodeElement ce; codeList) - ce.print(lines); - writefln(); - } - - // Store the offsets of named labels in the 'offsets' table - if(offsets.length > 0) - { - foreach(CodeElement ce; codeList) - if(ce.lnumber != -1) - offsets[ce.lnumber] = ce.offset; - } - - // Set up the code list, fill it, and return it. - ubyte[] code; - code.length = offset; - foreach(ref CodeElement ce; codeList) - ce.insertCode(code); - - return code; - } - - // Some handy definitions and functions - alias codeList.Iterator Node; - - // True if the given node is an opcode or a label - bool isPrincipal(Node n) - { - assert(n !is null); - return - n.value.isPrincipal(); - } - - // Skip until the next principal node - Node getNext(Node node) - { - assert(node !is null); - - do node = node.getNext(); - while(node !is null && !isPrincipal(node)) - - return node; - } - - bool isCmd(Node node, BC ops[]...) - { - assert(node !is null); - return node.value.isCmd(ops); - } - - /************************************************* - - Optimization - - *************************************************/ - - // All assembler-level optimization goes into this function - void optimize() - { - // Whether anything changed this round. If we can run the entire - // function with out changing anything, then we're done. - bool changed; - - // Deletes an opcode or label, and all it's data nodes. Returns - // the next node. - Node deleteNode(Node node) - { - changed = true; - - assert(node !is null); - assert(isPrincipal(node)); - - // Remove this node and all following non-principal ones. - do - { - auto tmp = node; - node = node.getNext(); - - // If this is a jump or a stack reference, make sure we - // notify the label - if(tmp.value.type == ET.JumpPtr || - tmp.value.type == ET.MarkRef) - { - auto lb = tmp.value.label; - assert(lb !is null); - assert(lb.jumps > 0); - lb.jumps--; - } - - codeList.remove(tmp); - } - while(node !is null && !isPrincipal(node)); - - return node; - } - - // Pushed value, in cases of static pushes - int pushValue; - // Is this a static push? (Ie. is the data available in the code - // itself?) - bool isStaticPush(Node node) - { - if(isCmd(node, BC.PushData)) - { - node = node.getNext(); - assert(node.value.type == ET.Data4 || - node.value.type == ET.Ptr); - pushValue = node.value.idata; - return true; - } - return false; - } - - // Is this a push, of any kind? TODO: This should cover ANY - // operation that has an imprint of +1 and no side effects. For - // operations with imprint 0 and no side effects followed by a - // pop, we can work backwards from the pop and remove the - // operations. Essentially, we need more meta information about - // the instructions (such as whether they have side effects) so - // that we can fully automate this and not keep instruction-lists - // here. - bool isPush(Node node) - { - return isCmd(node, - BC.PushData, BC.PushLocal, BC.PushClassVar, - BC.PushParentVar, BC.PushThis, BC.Dup); - } - - bool isValidLabel(Node node) - { - assert(node !is null); - - // Valid labels have jumps to them, or are state labels. - if(node.value.type == ET.Label && - (node.value.jumps > 0 || - node.value.lnumber != -1)) - return true; - return false; - } - - // TODO: We need to run several of these multiple times. For - // example, when jumps get deleted, old labels can become - // inactive, making ever more code dead or prone for other - // optimizations. The brute force approach is to just run the - // entire function until it has an entire pass without changing - // anything. This approach will probably work quite well, too. We - // can refine it later, and see what other optimizers (GCC, LLVM, - // Lua, Java, DMD, and so on) do. - - rerun: - changed = false; - - - /************************************************* - - Removing dead code - - *************************************************/ - - // Remove dead code - non-label code that occurs after jumps, - // exits and returns - { - auto node = codeList.getHead(); - bool killSpree = false; - while(node !is null) - { - // Active labels turn the killing spree off - if(isValidLabel(node)) - killSpree = false; - - // Everything else is obliterated if we're on the - // spree. - if(killSpree) - node = deleteNode(node); - - // If we're not on a spree, any non-conditional jump, exit - // or return should start one up. - else - { - if(isCmd(node, - BC.Jump, BC.Exit, BC.Return, - BC.ReturnVal, BC.ReturnValN)) - killSpree = true; - - // In any case, if we didn't delete the node, skip to - // the next one - node = getNext(node); - } - } - } - - /************************************************* - - Remove pointless jumps - - *************************************************/ - - // Jumps where there's nothing between the jump op and the - // label. These are common after dead code removal. Can probably - // be squeezed into a more general peephole loop later. - { - auto node = codeList.getHead(); - while(node !is null) - { - Node next = node.getNext(); - - if(node.value.type == ET.JumpPtr && - node.value.label == &next.value) - { - // Step back one to get the jump opcode - node = node.getPrev(); - assert(node !is null && node.value.type == ET.OpCode); - - // TODO: We only handle pure jumps at the moment, but - // later we should replace the others with a pop. - if(node.value.idata == BC.Jump) - next = deleteNode(node); - } - - node = next; - } - } - - /************************************************* - - Peephole loop - - *************************************************/ - - // These are just some examples I toyed around with. Later we will - // probably generalize this a whole lot, eg. with a replace() set - // of functions or something similar. - { - auto node = codeList.getHead(); - while(node !is null) - { - Node next = getNext(node); - - // Kill inactive labels, except for state labels. - if(node.value.type == ET.Label && - node.value.jumps == 0 && - node.value.lnumber == -1) - deleteNode(node); - - // All the following depend on pairs of instructions. - else if(next !is null) - { - // Is this a push (of any type?) - if(isPush(node)) - { - // Push and pop? Delete both - if(isCmd(next, BC.Pop)) - { - next = deleteNode(node); - next = deleteNode(next); - } - } - - // Is this a static push? - else if(isStaticPush(node)) - { - // Conditional jump that always fails? - if((isCmd(next, BC.JumpZ) && pushValue != 0) || - (isCmd(next, BC.JumpNZ) && pushValue == 0)) - { - // Delete both the push and the jump - next = deleteNode(node); - next = deleteNode(next); - } - - // Conditional jump that always follows through? - else if((isCmd(next, BC.JumpZ) && pushValue == 0) || - (isCmd(next, BC.JumpNZ) && pushValue != 0)) - { - // Delete the push, replace the jump with an - // unconditional one - next = deleteNode(node); - next.value.idata = BC.Jump; - } - } - } - - node = next; - } - } - - // If the code changed, run the optimizations again - if(changed) - goto rerun; - } -} diff --git a/old_d_version/monster/compiler/block.d b/old_d_version/monster/compiler/block.d deleted file mode 100644 index 03703cf85..000000000 --- a/old_d_version/monster/compiler/block.d +++ /dev/null @@ -1,165 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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); -} diff --git a/old_d_version/monster/compiler/bytecode.d b/old_d_version/monster/compiler/bytecode.d deleted file mode 100644 index 2d1f9095a..000000000 --- a/old_d_version/monster/compiler/bytecode.d +++ /dev/null @@ -1,656 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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", - ]; diff --git a/old_d_version/monster/compiler/enums.d b/old_d_version/monster/compiler/enums.d deleted file mode 100644 index 8dd4b344a..000000000 --- a/old_d_version/monster/compiler/enums.d +++ /dev/null @@ -1,133 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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); - } -} diff --git a/old_d_version/monster/compiler/expression.d b/old_d_version/monster/compiler/expression.d deleted file mode 100644 index d33e50f42..000000000 --- a/old_d_version/monster/compiler/expression.d +++ /dev/null @@ -1,1393 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (expression.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.expression; - -import monster.compiler.tokenizer; -import monster.compiler.scopes; -import monster.compiler.operators; -import monster.compiler.types; -import monster.compiler.assembler; -import monster.compiler.block; -import monster.compiler.statement; -import monster.compiler.variables; -import monster.compiler.functions; - -import monster.vm.error; -import monster.vm.mclass; -import monster.vm.arrays; -import monster.util.list; - -import std.string; -import std.stdio; -import std.utf : decode, toUTF32; - -alias Expression[] ExprArray; - -// An expression is basically anything that can return a value, -// including a 'void'. -abstract class Expression : Block -{ - // Identify a part of an expression. The sub expressions include all - // expressions except binary operators, and it is used in identify() - // to parse the parts between such operators. All lvalues can be - // parsed with this function. - static Expression identifySub(ref TokenArray toks, bool isMember = false) - /* - out(res) - { - writefln("identifySub returned ", res); - } - body - //*/ - { - Expression b; - Floc ln; - - // Only identifiers (variables, properties, etc) are allowed as - // members. - if(MemberExpr.canParse(toks)) b = new MemberExpr; - else if(isMember) fail(toks[0].str ~ " can not be a member", toks[0].loc); - - // The rest are not allowed for members (eg. con.(a+b) is not - // allowed) - else if(NewExpression.canParse(toks)) b = new NewExpression; - else if(TypeofExpression.canParse(toks)) b = new TypeofExpression; - else if(LiteralExpr.canParse(toks)) b = new LiteralExpr; - else if(ArrayLiteralExpr.canParse(toks)) b = new ArrayLiteralExpr; - // Sub-expression (expr) - else if(isNext(toks, TT.LeftParen)) - { - b = readParens(toks); - goto noParse; - } - // Unary operators, op expr - else if(UnaryOperator.canParse(toks)) b = new UnaryOperator; - - else if(isNext(toks, TT.Semicolon, ln)) - fail("Use {} for empty statements, not ;", ln); - - else - { - if(toks.length != 0) - fail("Cannot interpret expression " ~ toks[0].str, toks[0].loc); - else - fail("Missing expression"); - } - - b.parse(toks); - - noParse: - - // Check for . expressions and resolve the members as such. If - // the sub-expression b is followed by a . then the following - // tokens must be parsed as a new sub-expression that is a - // member of b. The problem is to call identifySub recursively - // so that expressions are ordered correctly, - // ie. first.second.third becomes (((first).second).third). The - // solution (relying on isMember) is a bit quirky but it - // works. Dots were originally handled as normal binary - // operators, but that gave wrong syntax in some corner - // cases. There is also the problem that operators such as ++ - // and [] should apply to the subexpression as a whole, not the - // member, so we put those in here. - if(!isMember) - { - while(1) - { - if(isNext(toks, TT.Dot, ln)) - b = new DotOperator(b, identifySub(toks, true), ln); - - // After any sub-expression there might be a [] or [expr] to - // indicate array operation. We check for that here, and - // loop since the result is a subexpression which might - // itself contain more []s after it. We allow expressions - // like 20[5] syntactically, since these will be weeded out - // at the semantic level anyway. - else if(isNext(toks,TT.LeftSquare)) - { - // Empty []? - if(isNext(toks, TT.RightSquare, ln)) - b = new ArrayOperator(b, ln); - else // [expr] or [expr..expr] - { - Expression exp = identify(toks); - - if(isNext(toks, TT.DDot)) - // b[exp .. exp2] - b = new ArrayOperator(b, ln, exp, identify(toks)); - else - // b[exp] - b = new ArrayOperator(b, ln, exp); - - if(!isNext(toks, TT.RightSquare)) - fail("Array expected closing ]", toks); - } - } - // Any expression followed by a left parenthesis is - // interpreted as a function call. We do not allow it to - // be on the next line however, as this could be - // gramatically ambiguous. - else if(!isNewline(toks) && isNext(toks,TT.LeftParen)) - b = new FunctionCallExpr(b, toks); - - else break; - } - // Finally, check for a single ++ or -- following an expression. - if(isNext(toks, TT.PlusPlus)) b = new UnaryOperator(b, true); - else if(isNext(toks, TT.MinusMinus)) b = new UnaryOperator(b, false); - } - - return b; - } - - private: - // This reads the contents of a parenthesis pair (...). It really - // just calls identify() again and removes the paren at the - // end. However, this ensures that operator precedence takes place - // locally inside the paren pair, and that the result is seen as one - // unit to the outside expression. And this is of course what - // parentheses are for. - static Expression readParens(ref TokenArray toks) - { - // We assume the opening parenthesis has been removed - // already. Let's get to parsin'! - Expression res = identify(toks); - - if(!isNext(toks, TT.RightParen)) - fail("Expected ) after expression", toks); - - return res; - } - - // Used in parsing expressions - struct ExpOp - { - Expression exp; - TT nextOp; // Operator to the right of the expression - Floc loc; - } - - // Operators handled below. - static bool getNextOp(ref TokenArray toks, ref Token t) - { - if(toks.length == 0) return false; - TT tt = toks[0].type; - if(tt == TT.Plus || tt == TT.Minus || tt == TT.Mult || - tt == TT.Div || tt == TT.Rem || tt == TT.IDiv || - - tt == TT.Cat || - - tt == TT.IsEqual || tt == TT.Less || tt == TT.More || - tt == TT.LessEq || tt == TT.MoreEq || tt == TT.NotEqual || - tt == TT.And || tt == TT.Or || - - tt == TT.IsCaseEqual || tt == TT.NotCaseEqual) - { - t = next(toks); - return true; - } - return false; - } - - public: - - // This represents the type of this expression. It must be created - // and set up (resolved) in the child classes. - Type type; - - // Parse an entire expression - static Expression identify(ref TokenArray toks) - /* - out(res) - { - writefln("identify returned ", res); - } - body - //*/ - { - // Create a linked list to store the expressions and operators. - LinkedList!(ExpOp) exprList; - - ExpOp eo; - Token tt; - - do - { - // Parse the next sub-expression - eo.exp = identifySub(toks); - - // Check if this expression has an operator behind it. If - // not, it is the last expression. - if(getNextOp(toks, tt)) - { - eo.nextOp = tt.type; - eo.loc = tt.loc; - } - else - eo.nextOp = TT.EOF; - - // Insert the (expr+op) pair. - exprList.insert(eo); - } - while(eo.nextOp != TT.EOF); - - // Replaces a pair (expr1+op1,expr2,op2) with (newExpr,op2), - // where newExpr is created from (expr1 op1 expr2). - // eg. converts a+b*c to a + (b*c). It takes the right - // expression as parameter, and removes the left, so that it can - // be called while iterating the list. - void replace(exprList.Iterator right, bool assign = false, bool boolop=false) - { - auto left = right.getPrev; - assert(left != null); - - // Create the compound expression. Replace the right node, - // since it already has the correct operator. - assert(!assign); - if(boolop) - right.value.exp = new BooleanOperator(left.value.exp, - right.value.exp, - left.value.nextOp, - left.value.loc); - else - right.value.exp = new BinaryOperator(left.value.exp, - right.value.exp, - left.value.nextOp, - left.value.loc); - - // Kill the left node. - exprList.remove(left); - } - - static bool has(TT list[], TT type) - { - foreach(tt; list) if(tt == type) return true; - return false; - } - - // Find all expression pairs bound together with one of the - // given operator types - void find(TT types[] ...) - { - auto it = exprList.getHead(); - while(it !is null) - { - // Is it the right operator? - if(types.has(it.value.nextOp)) - { - // Replace it and continue to the next element - it = it.getNext(); - replace(it); - } - else - it = it.getNext(); - } - } - - // Boolean operators - void findBool(TT types[] ...) - { - auto it = exprList.getHead(); - while(it !is null) - { - // Is it the right operator? - if(types.has(it.value.nextOp)) - { - // Replace it and continue to the next element - it = it.getNext(); - replace(it, false, true); - } - else - it = it.getNext(); - } - } - - // Now sort through the operators according to precedence. This - // is the precedence I use, it should be ok. (Check it against - // something else) - - // / % - // * - // + - - // ~ - // == != < > <= >= =i= =I= !=i= !=I= - // || && - // = += -= *= /= %= ~= - - // Dot operators are now handled in identifySub - //find(TT.Dot); - - find(TT.Div, TT.Rem, TT.IDiv); - - find(TT.Mult); - - find(TT.Plus, TT.Minus); - - find(TT.Cat); - - findBool(TT.IsEqual, TT.NotEqual, TT.Less, TT.More, TT.LessEq, TT.MoreEq, - TT.IsCaseEqual, TT.NotCaseEqual); - - findBool(TT.Or, TT.And); - - assert(exprList.length == 1, "Cannot reduce expression list to one element"); - return exprList.getHead().value.exp; - } - - // Get a sensible line number for this expression. Used in error - // messages. - Floc getLoc() { return loc; } - - // Used for error messages - char[] typeString() { return type.toString(); } - - // Can this expression be assigned to? (most types can not) - bool isLValue() { return false; } - - // Evaluate this using run-time instructions. This is only used when - // evalCTime can not be used (ie. when isCTime is false.) - void evalAsm() { fail(format("expression ", this, " not implemented")); } - - // This is the equivalent of 'compile' for statements. Create - // compiled code that evaluates the expression. The result should be - // the value of the expression pushed onto the stack. Uses compile - // time information when available. - final void eval() - { - if(isCTime()) - { - int[] data = evalCTime(); - assert(data.length == type.getSize); - tasm.pushArray(data); - } - else - evalAsm(); - } - - // Evaluate and pop the value afterwards. Might be optimized later. - final void evalPop() - { - eval(); - - // Internal types never come from expressions that have any - // effect. Leaving them dangling without a reciever can cause - // stack bugs in some cases (since the owner and other internal - // data is never popped), so we forbid them. - if(!type.isLegal) - fail("Expression " ~ toString() ~ " has no effect", loc); - - setLine(); - if(!type.isVoid) - tasm.pop(type.getSize()); - } - - // Evaluate this expression as a destination (ie. push a pointer - // instead of a value). Only valid for LValues. - void evalDest() { assert(0, "evalDest() called for non-lvalue " ~ this.toString); } - - // Pop values of the stack and store it in this expression. Only - // valid for lvalues. This is now used in place of evalDest in most - // places (although it may use evalDest internally.) - void store() - { assert(0, "store not implemented for " ~ toString()); } - - // Handles ++ and -- - void incDec(TT op, bool post) - { - assert(isLValue); - assert(type.isInt || type.isUint || - type.isLong || type.isUlong); - evalDest(); - tasm.incDec(op, post, type.getSize()); - } - - // Can this expression be evaluated at compile time? - bool isCTime() { return false; } - - // Return the compile-time value of this expression - int[] evalCTime() { assert(0); return null; } -} - -// Handles typeof(exp), returns an empty expression with the meta-type -// of exp. -class TypeofExpression : Expression -{ - TypeofType tt; - - static bool canParse(TokenArray toks) - { return TypeofType.canParse(toks); } - - void parse(ref TokenArray toks) - { - // Let TypeofType handle the details - tt = new TypeofType; - tt.parse(toks); - } - - void resolve(Scope sc) - { - tt.resolve(sc); - type = tt.getBase().getMeta(); - } - - // We can always determine the type at compile time - bool isCTime() { return true; } - - int[] evalCTime() { return null; } - - char[] toString() - { - return "typeof(" ~ tt.exp.toString ~ ")"; - } -} - -// new-expressions, ie. (new Sometype[]). -class NewExpression : Expression -{ - // The array of expressions inside brackets. Examples: - // new int[5][10] - contains the expressions 5 and 10 - // new int[][][2] - contains the expressions null, null and 2 - ExprArray exArr; - - // Base type of our array. Examples: - // new int[10] - base is int - // new int[10][10] - base is int - // new int[][10] - base is int[] - Type baseType; - - CIndex clsInd; - - NamedParam[] params; - Variable* varlist[]; - - static bool canParse(TokenArray toks) - { return isNext(toks, TT.New); } - - void parse(ref TokenArray toks) - { - reqNext(toks, TT.New, loc); - type = Type.identify(toks, true, exArr); - - // Parameters? - ExprArray exps; - if(isNext(toks, TT.LeftParen)) - FunctionCallExpr.getParams(toks, exps, params); - - if(exps.length != 0) - fail("'new' can only take named parameters", loc); - } - - char[] toString() - { - char[] res = "new " ~ typeString ~ ""; - if(params.length) - { - res ~= "("; - foreach(i, v; params) - { - res ~= v.name.str; - res ~= "="; - res ~= v.value.toString; - if(i < params.length-1) - res ~= ", "; - } - res ~= ")"; - } - return res; - } - - void resolve(Scope sc) - { - type.resolve(sc); - MonsterClass mc; - - if(type.isReplacer) - type = type.getBase(); - - if(type.isObject) - { - // We need to find the index associated with this class, and - // pass it to the assembler. - mc = (cast(ObjectType)type).getClass(); - assert(mc !is null); - clsInd = mc.getIndex(); - - // Don't create instances of modules! - if(mc.isModule) - fail("Cannot create instances of modules", loc); - } - else if(type.isArray) - { - assert(exArr.length == type.arrays); - - // Used for array size specifiers - Type intType = BasicType.getInt; - - // The remaining expressions must fill toward the right. For - // example, [1][2][3] is allowed, as is [][][1][2], but not - // [1][][2]. - bool found=false; - int firstIndex = -1; // Index of the first non-empty expression - foreach(int i, ref Expression e; exArr) - if(e !is null) - { - if(!found) - { - // The first non-empty expression! Store the - // index. - found = true; - firstIndex = i; - } - - // Resolve the expressions while we're at it. - e.resolve(sc); - - // Check the type - intType.typeCast(e, "array index"); - } - else - { - if(found) // Cannot have an empty expression after a - // non-empty one. - fail("Invalid array specifier in 'new' expression", loc); - } - - if(firstIndex == -1) - fail("Right-most bracket in 'new' expression must contain an expression", - loc); - - // This is already true from the above check, we're - // defensive here. - assert(exArr[$-1] !is null); - - // Only store non-empty expressions, since those are the - // only ones we will act on. - exArr = exArr[firstIndex..$]; - - // Find the base type of our allocation. This is used to - // find the initialization value and size. - baseType = type; - for(int i=exArr.length; i>0; i--) - baseType = baseType.getBase(); - } - else - fail("Cannot use 'new' with type " ~ type.toString, loc); - - // Resolve the parameters - if(params.length != 0) - { - if(!type.isObject) - fail("New can take parameters to class types, not " ~ type.toString, loc); - - assert(mc !is null); - - varlist.length = params.length; - foreach(int i, ref v; params) - { - // Lookup the variable in the class - auto look = mc.sc.lookup(v.name); - if(!look.isVar) - fail(v.name.str ~ " is not a variable in class " ~ type.toString, loc); - - // Store the looked up variable - varlist[i] = look.var; - - // Next resolve the expression - v.value.resolve(sc); - - // Finally, convert the type - look.type.typeCast(v.value, v.name.str); - } - } - } - - void evalAsm() - { - setLine(); - - if(type.isObject) - { - // Total stack size of parameters - int imprint = 0; - - // Push the variable pointers and values on the stack - foreach(i, v; params) - { - auto lvar = varlist[i]; - - assert(v.value !is null); - assert(lvar !is null); - assert(lvar.sc !is null); - - // Push the expression and some meta info - v.value.eval(); - tasm.push(v.value.type.getSize); // Type size - tasm.push(lvar.number); // Var number - tasm.push(lvar.sc.getClass().getTreeIndex()); // Class index - - // Tally up the casualties - imprint += v.value.type.getSize() + 3; - } - - // Create a new object. This is done through a special byte - // code instruction. - tasm.newObj(clsInd, params.length, imprint); - } - else if(type.isArray) - { - assert(params.length == 0); - - int initVal[] = baseType.defaultInit(); - - int rank = exArr.length; // Array nesting level - - // Check that the numbers add up - assert(type.arrays == baseType.arrays + rank); - - // Push the lengths on the stack - foreach(ex; exArr) - ex.eval(); - - setLine(); - - // Create an array with the given initialization and - // dimensions. The init value can be any length (number of - // ints), and elements are assumed to be the same length. - tasm.newArray(initVal, rank); - } - else assert(0, "not implemented yet"); - } -} - -// Array literals [expr,expr,...]. Does not cover string literals, -// those are covered by LiteralExpr below. -class ArrayLiteralExpr : Expression -{ - ExprArray params; - - MonsterClass cls; // Needed to insert static arrays - AIndex arrind; // Do not remove! Used for semi-permanent slices. - - static bool canParse(TokenArray toks) - { return isNext(toks, TT.LeftSquare); } - - void parse(ref TokenArray toks) - { - reqNext(toks, TT.LeftSquare, loc); - - Floc loc2; - if(isNext(toks, TT.RightSquare, loc2)) - fail("Array literal cannot be empty. Use 'null' instead.", loc2); - - // Read the first expression - params ~= Expression.identify(toks); - - // Check for more expressions - while(isNext(toks, TT.Comma)) - params ~= Expression.identify(toks); - - reqNext(toks, TT.RightSquare); - } - - char[] toString() - { - char[] res = " ["; - foreach(expr; params[0..params.length-1]) - res ~= expr.toString ~ ", "; - return res ~ params[params.length-1].toString ~ "] "; - } - - bool isCTime() - { - foreach(p; params) - if(!p.isCTime) return false; - return true; - } - - void resolve(Scope sc) - { - foreach(expr; params) - expr.resolve(sc); - - assert(params.length != 0); - - // Set the type - Type base = params[0].type; - type = ArrayType.get(base); - - foreach(ref par; params) - { - // Check that all elements are of a usable type, and convert - // if necessary. - base.typeCast(par, "array element"); - } - - cls = sc.getClass(); - - /* - if(isCTime) - cls.reserveStatic(params.length * base.getSize); - */ - } - - int[] evalCTime() - { - assert(isCTime()); - - // Element size and number - int elem = type.getBase().getSize(); - int num = params.length; - - int data[] = new int[num*elem]; - - // Set up the data - foreach(i, par; params) - data[i*elem..(i+1)*elem] = par.evalCTime(); - - // Create the array and get the index - arrind = arrays.createConst(data, elem).getIndex; - - // Create an array from the index and return it as the compile - // time value - return (cast(int*)&arrind)[0..1]; - } - - void evalAsm() - { - assert(!isCTime()); - - int s = params[0].type.getSize; - assert(s >= 1); - - // Push all the elements on the stack - foreach(par; params) - { - assert(par.type.getSize == s); - par.eval(); - } - - setLine(); - - // And simply pop them back into an array - tasm.popToArray(params.length, s); - } -} - -// Expression representing a literal or other special single-token -// values. Supported tokens are StringLiteral, Int/FloatLiteral, True, -// False, Null, Dollar and This. Array literals are handled by -// ArrayLiteralExpr. -class LiteralExpr : Expression -{ - Token value; - - // Values (used depending on type) - int ival; - dchar dval; // Characters are handled internally as dchars - float fval; - - // The array operator expression for $ symbols (ie. a[b] or a[b..c]) - ArrayOperator arrOp; - - // This is set if the array that $ refers to is ctime. - bool arrayCTime; - - // String, with decoded escape characters etc and converted to - // utf32. We need the full dchar string here, since we might have to - // handle compile-time expressions like "hello"[2..4], which should - // return the right characters. - dchar[] strVal; - AIndex arrind; // Placed here because we return a permanent slice of it - - // Used for inserting string data into the data segment. - MonsterClass cls; - - static bool canParse(TokenArray toks) - { - return - isNext(toks, TT.StringLiteral) || - isNext(toks, TT.IntLiteral) || - isNext(toks, TT.FloatLiteral) || - isNext(toks, TT.True) || - isNext(toks, TT.False) || - isNext(toks, TT.Null) || - isNext(toks, TT.Dollar) || - isNext(toks, TT.This); - } - - void parse(ref TokenArray toks) - { - value = next(toks); - loc = value.loc; - } - - char[] toString() - { - return value.str; - } - - bool isRes = false; - - // Convert a token to an integer. TODO: Will do base conversions etc - // later on. - static long parseIntLiteral(Token t) - { - assert(t.type == TT.IntLiteral); - return atoi(t.str); - } - - void resolve(Scope sc) - { - isRes = true; - - // Find the class and store it for later - cls = sc.getClass(); - - // The 'this' name refers to the current object, and is the same - // type as the current class. - if(value.type == TT.This) - { - // Get the type from the current class - type = sc.getClass().objType; - return; - } - - // Numeric literals - if(value.type == TT.IntLiteral) - { - type = BasicType.getInt; - ival = parseIntLiteral(value); - return; - } - - // Floats - if(value.type == TT.FloatLiteral) - { - type = BasicType.getFloat; - fval = atof(value.str); - return; - } - - // The $ token. Only allowed in array indices. - if(value.type == TT.Dollar) - { - if(!sc.isArray) - fail("Array length $ not allowed here", loc); - - type = BasicType.getInt; - arrOp = sc.getArray(); - - // If the array itself is compile time, then so is the - // length. - assert(arrOp.name !is null); - assert(arrOp.name.type.isArray()); - arrayCTime = arrOp.name.isCTime(); - return; - } - - // The token 'null' - if(value.type == TT.Null) - { - // 'null' has a special type that is converted when needed - type = new NullType; - return; - } - - // Bool literal - if(value.type == TT.True || value.type == TT.False) - { - type = BasicType.getBool; - if(value.type == TT.True) - ival = 1; - else - ival = 0; - return; - } - - // Strings - if(value.type == TT.StringLiteral) - { - type = ArrayType.getString; - - strVal = value.str32; - - return; - } - - fail("Unhandled literal type " ~ value.str, loc); - } - - // We currently support a few kinds of constants - bool isCTime() - { - // If the array value itself is ctime, then $ is too. - if(value.type == TT.Dollar) - return arrayCTime; - - return - type.isInt() || type.isBool() || type.isFloat || type.isChar || - value.type == TT.Null || type.isString; - } - - int[] evalCTime() - { - if(value.type == TT.Dollar) - { - // Get the array - assert(arrOp.name.isCTime()); - int res[] = arrOp.name.evalCTime().dup; - assert(res.length == 1); - auto arf = arrays.getRef(cast(AIndex)res[0]); - - // Return the length - res[0] = arf.length(); - return res; - } - - // Return a slice of the value - if(type.isInt || type.isBool) return (&ival)[0..1]; - if(type.isChar) return (cast(int*)&dval)[0..1]; - if(type.isFloat) return (cast(int*)&fval)[0..1]; - - if(type.isString) - { - // Create array index - arrind = arrays.createConst(cast(int[])strVal, 1).getIndex; - // Create an array from it - return (cast(int*)&arrind)[0..1]; - } - - // Let the type cast from NullType create the data - if(value.type == TT.Null) return null; - - assert(0, "Compile time evaluation of " ~ toString ~ " not implemented yet"); - } - - void evalAsm() - { - assert(!isCTime()); - assert(type !is null, "not resolved"); - setLine(); - - if(value.type == TT.Dollar) - { - // The array index should already be on the stack. Get it. - auto sMark = arrOp.stackMark; - assert(sMark !is null); - tasm.pushMark(sMark); - - // Convert it to the length - setLine(); - tasm.getArrayLength(); - } - else if(value.type == TT.This) tasm.pushThis(); - else fail("Literal type '" ~ value.str ~ "' not supported yet", loc); - } -} - -// Expression that handles conversion from one type to another. This -// is used for implisit casts (eg. when using ints and floats -// together) and in the future it will also parse explisit casts. It -// acts as a wrapper that inserts the correct conversion code after an -// expression is compiled. The actual conversion code is done in the -// type itself. Compile time conversion of constants will also be done -// later. I am not sure how we will handle casts between objects yet - -// then need to be checked at runtime in the vm in any case. -class CastExpression : Expression -{ - // The original expression. The 'type' property in the base class - // holds the new type. - Expression orig; - - this(Expression exp, Type newType) - { - orig = exp; - type = newType; - - assert(type !is null); - assert(orig.type.canCastTo(type) || orig.type.canCastToExplicit(type)); - } - - // These would only be used if typecasts got their own unique - // syntax, eg. "cast(Type) expression" like in D. - void parse(ref TokenArray) { assert(0, "Cannot parse casts"); } - void resolve(Scope sc) { assert(0, "Cannot resolve casts"); } - - bool isCTime() - { - return orig.isCTime() && orig.type.canCastCTime(type); - } - - int[] evalCTime() - { - assert(isCTime()); - - // Let the type do the conversion - int[] res = type.typeCastCTime(orig, ""); - - return res; - } - - void evalAsm() - { - orig.eval(); - - // The type does the low-level stuff - orig.type.evalCastTo(type, orig.loc); - } - - char[] toString() - { - return "cast(" ~ type.toString ~ ")(" ~ orig.toString ~ ")"; - } -} - -// Used as the surrogate owner expression for imported -// members. Example: -// import x; -// y = 3; // refers to x.y -// y is resolved as x.y, where the owner (x) is an import holder. -abstract class ImportHolder : Expression -{ - // All lookups in this import is done through this function. - abstract ScopeLookup lookup(Token name, bool autoLoad=false); - - // Get a short name (for error messages) - abstract char[] getName(); - - // Override these to avoid duplicate imports - bool isClass(MonsterClass mc) { return false; } - bool isPack(PackageScope sc) { return false; } - - override: - - // Override these - char[] toString() {assert(0);} - void evalAsm() {} - - // These aren't needed - final: - void parse(ref TokenArray) { assert(0); } - void resolve(Scope sc) {} - void evalDest() { assert(0); } -} - -// Import holder for packages -class PackageImpHolder : ImportHolder -{ - PackageScope sc; - - this(PackageScope psc) - { - sc = psc; - assert(sc !is null); - - type = psc.type; - assert(type !is null); - } - - override: - - ScopeLookup lookup(Token name, bool autoLoad=false) - { - return sc.lookup(name, autoLoad); - } - - char[] getName() { return sc.toString(); } - - char[] toString() { return "imported package " ~ sc.toString(); } - - bool isPack(PackageScope s) { return s is sc; } -} - -// Import holder for classes -class ClassImpHolder : ImportHolder -{ - MonsterClass mc; - - this(MonsterClass pmc) - { - mc = pmc; - - // The imported class must be resolved at this point - mc.requireScope(); - - // Importing singletons and modules is like importing - // the object itself - if(mc.isSingleton) - type = mc.objType; - else - type = mc.classType; - - assert(type !is null); - } - - override: - - char[] getName() { return mc.name.str; } - - ScopeLookup lookup(Token name, bool autoLoad=false) - { - assert(mc !is null); - mc.requireScope(); - return mc.sc.lookup(name, autoLoad); - } - - - char[] toString() { return "imported class " ~ mc.name.str; } - - void evalAsm() - { - assert(mc !is null); - mc.requireScope(); - if(mc.isSingleton) - { - assert(type.isObject); - tasm.pushSingleton(mc.getIndex()); - } - } - - bool isClass(MonsterClass m) { return m is mc; } -} - -// An expression that works as a statement. This also handles all -// assignments, ie. expr1 = expr2. Supported operators are -// =, +=, *= /=, %=, ~= -class ExprStatement : Statement -{ - Expression left; - - // For assignment - TT opType; - Expression right; - Type type; - - bool catElem; // For concatinations (~=), true when the right hand - // side is a single element rather than an array. - - // Require a terminating semicolon or line break - bool term = true; - - // True if 'left' was set in the constructor - bool leftSet = false; - - this(Expression lft=null) - { - left = lft; - if(left !is null) - leftSet = true; - } - - void parse(ref TokenArray toks) - { - if(left is null) - { - if(toks.length == 0) - fail("Expected statement, found end of stream."); - left = Expression.identify(toks); - } - - loc = left.loc; - - Token tok; - - if(toks.isNext(TT.Equals, tok) || - toks.isNext(TT.PlusEq, tok) || - toks.isNext(TT.MinusEq, tok) || - toks.isNext(TT.MultEq, tok) || - toks.isNext(TT.DivEq, tok) || - toks.isNext(TT.RemEq, tok) || - toks.isNext(TT.IDivEq, tok) || - toks.isNext(TT.CatEq, tok)) - { - opType = tok.type; - - right = Expression.identify(toks); - } - - if(term) reqSep(toks); - } - - char[] toString() - { - char[] res = left.toString(); - if(right !is null) - { - res ~= tokenList[opType]; - res ~= right.toString(); - } - return res; - } - - void resolve(Scope sc) - { - if(!leftSet) - left.resolve(sc); - - loc = left.loc; - type = left.type; - - // If this isn't an assignment, we're done. - if(right is null) - return; - - right.resolve(sc); - - // Check that we are allowed assignment - if(!left.isLValue) - fail("Cannot assign to expression '" ~ left.toString ~ "'", loc); - - // Operators other than = and ~= are only allowed for numerical - // types. - if(opType != TT.Equals && opType != TT.CatEq && !type.isNumerical) - fail("Assignment " ~tokenList[opType] ~ - " not allowed for non-numerical type " ~ left.toString(), loc); - - // Is the left hand expression a sliced array? - auto arr = cast(ArrayOperator) left; - if(arr !is null && arr.isSlice) - { - assert(type.isArray); - - if(opType == TT.CatEq) - fail("Cannot use ~= on array slice " ~ left.toString, loc); - - // For array slices on the right hand side, the left hand - // type must macth exactly, without implisit casting. For - // example, the following is not allowed, even though 3 can - // implicitly be cast to char[]: - // char[] a; - // a[] = 3; // Error - - // We are, on the other hand, allowed to assign a single - // value to a slice, eg: - // int[] i = new int[5]; - // i[] = 3; // Set all elements to 3. - // In this case we ARE allowed to typecast, though. - - if(right.type == left.type) return; - - Type base = type.getBase(); - - base.typeCast(right, left.toString); - - // Inform arr.store() that we're filling in a single element - arr.isFill = true; - - return; - } - - // Handle concatination ~= - if(opType == TT.CatEq) - { - if(!left.type.isArray) - fail("Opertaor ~= can only be used on arrays, not " ~ left.toString ~ - " of type " ~ left.typeString, left.loc); - - // Array with array - if(left.type == right.type) catElem = false; - // Array with element - else if(right.type.canCastOrEqual(left.type.getBase())) - { - left.type.getBase().typeCast(right, ""); - catElem = true; - } - else - fail("Cannot use operator ~= on types " ~ left.typeString ~ - " and " ~ right.typeString, left.loc); - return; - } - - // Cast the right side to the left type, if possible. - type.typeCast(right, left.toString); - - assert(left.type == right.type); - } - - void compile() - { - if(right is null) - { - // Simple expression, no assignment. - left.evalPop(); - return; - } - - int s = right.type.getSize; - - // += -= etc are implemented without adding new - // instructions. This might change later. The "downside" to this - // approach is that the left side is evaluated two times, which - // might not matter much for lvalues anyway, but it does give - // more M-code vs native code, and thus more overhead. I'm not - // sure if a function call can ever be an lvalue or if lvalues - // can otherwise have side effects in the future. If that - // becomes the case, then this implementation will have to - // change. Right now, the expression i += 3 will be exactly - // equivalent to i = i + 3. - - if(opType != TT.Equals) - { - // Get the values of both sides - left.eval(); - right.eval(); - - setLine(); - - // Concatination - if(opType == TT.CatEq) - { - assert(type.isArray); - - if(catElem) - // Append one element onto the array - tasm.catArrayRight(s); - else - // Concatinate two arrays. - tasm.catArray(); - } - - // Perform the arithmetic operation. This puts the result of - // the addition on the stack. The evalDest() and mov() below - // will store it in the right place. - else if(type.isNumerical) - { - if(opType == TT.PlusEq) tasm.add(type); - else if(opType == TT.MinusEq) tasm.sub(type); - else if(opType == TT.MultEq) tasm.mul(type); - else if(opType == TT.DivEq) tasm.div(type); - else if(opType == TT.IDivEq) tasm.idiv(type); - else if(opType == TT.RemEq) tasm.divrem(type); - else fail("Unhandled assignment operator", loc); - } - else assert(0, "Type not handled"); - } - else right.eval(); - - // Store the value on the stack into the left expression. - setLine(); - left.store(); - } -} diff --git a/old_d_version/monster/compiler/functions.d b/old_d_version/monster/compiler/functions.d deleted file mode 100644 index 2a923b123..000000000 --- a/old_d_version/monster/compiler/functions.d +++ /dev/null @@ -1,1464 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (functions.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.functions; - -enum FuncType - { - Normal, // Normal function, defined in script code - Native, // Unassigned native function - NativeDFunc, // Native D function - NativeDDel, // Native D delegate - NativeCFunc, // Native C function - Abstract, // Abstract, does not have a body - Idle, // Idle function, can only be called in state code - } - -import monster.compiler.types; -import monster.compiler.operators; -import monster.compiler.assembler; -import monster.compiler.bytecode; -import monster.compiler.scopes; -import monster.compiler.expression; -import monster.compiler.variables; -import monster.compiler.tokenizer; -import monster.compiler.linespec; -import monster.compiler.statement; - -import monster.vm.mobject; -import monster.vm.idlefunction; -import monster.vm.mclass; -import monster.vm.error; -import monster.vm.thread; -import monster.vm.stack; -import monster.vm.vm; - -import monster.util.growarray; - -import std.stdio; -import std.stream; -import std.string; - -version(Tango) import tango.core.Traits; -else import std.traits; - -// One problem with these split compiler / vm classes is that we -// likely end up with data (or at least pointers) we don't need, and a -// messy interface. The problem with splitting is that we duplicate -// code and definitions. One solution is to let the VM class be a -// separate class (should be in vm/), but containing all we need in -// the VM (like the code, list of parameters, etc.) The point of this -// class (which we can rename FunctionCompiler, and leave in this -// file) is to create, build and nurture the Function it creates. The -// Function can be a struct, really, but I'll look into that. Flipping -// function structs off a region and pointing to them is easy and -// efficient, but creating classes isn't much worse. It depends if we -// need to inherit from them, really. - -// Used for native functions -alias void delegate() dg_callback; -typedef void function() fn_callback; -typedef extern(C) void function() c_callback; - -struct Function -{ - MonsterClass owner; // Must be the first entry - - LineSpec[] lines; // Line specifications for byte code - union - { - ubyte[] bcode; // Final compiled code (normal functions) - dg_callback natFunc_dg; // Various types of native functions - fn_callback natFunc_fn; - c_callback natFunc_c; - IdleFunction idleFunc; // Idle function callback - } - Token name; - - Type type; // Return type - FuncType ftype; // Function type - Variable* params[]; // List of parameters - int[][] defaults; // Default parameter values (if specified, null otherwise) - int index; // Unique function identifier within its class - - // Register this function in the global function list. This can only - // be done once, but it's required before functions can be - // referenced by function pointers in script code. Don't call this - // if you don't know what you're doing. - void register() - { - assert(!hasGIndex(), "Don't call register() more than once."); - - gIndex = functionList.length(); - functionList ~= this; - } - - int getGIndex() - { - assert(hasGIndex(), "This function doesn't have a global index"); - return gIndex; - } - - bool hasGIndex() { return gIndex != -1; } - - static Function *fromIndex(int index) - { - if(index < 0) - fail("Null function reference encountered"); - - if(index > functionList.length) - fail("Invalid function index encountered"); - - return functionList[index]; - } - - int paramSize; - - // Is this function final? (can not be overridden in child classes) - bool isFinal; - - // If true, this function can be executed without an object - bool isStatic; - - // What function we override (if any) - Function *overrides; - - // Find the virtual replacement for this function in the context of - // the object mo. - Function *findVirtual(MonsterObject *mo) - { - assert(mo !is null); - return findVirtual(mo.cls); - } - - // Find virtual replacement in the context of class cls. - Function *findVirtual(MonsterClass cls) - { - assert(cls.childOf(owner)); - return cls.findVirtualFunc(owner.getTreeIndex(), index); - } - - bool isNormal() { return ftype == FuncType.Normal; } - bool isNative() - { - return - ftype == FuncType.Native || ftype == FuncType.NativeDFunc || - ftype == FuncType.NativeDDel || ftype == FuncType.NativeCFunc; - } - bool isAbstract() { return ftype == FuncType.Abstract; } - bool isIdle() { return ftype == FuncType.Idle; } - - // True if the last parameter is a vararg parameter, meaning that - // this is a function that takes a variable number of arguments. - bool isVararg() { return params.length && params[$-1].isVararg; } - - // Bind the given function template parameter to this Function. The - // function is assumed to have compatible parameter and return - // types, and the values are pushed and popped of the Monster stack - // automatically. - void bindT(alias func)() - { - assert(isNative, "cannot bind to non-native function " ~ name.str); - - version(Tango) - { - alias ParameterTypleOf!(func) P; - alias ReturnTypeOf!(func) R; - } - else - { - alias ParameterTypeTuple!(func) P; - alias ReturnType!(func) R; - } - - assert(P.length == params.length, format("function %s has %s parameters, but binding function has %s", name.str, params.length, P.length)); - - // Check parameter types - foreach(int i, p; P) - assert(params[i].type.isDType(typeid(p)), format( - "binding %s: type mismatch in parameter %s, %s != %s", - name.str, i, params[i].type, typeid(p))); - - // Check the return type - static if(is(R == void)) - { - assert(type.isVoid, format("binding %s: expected to return type %s", - name.str, type)); - } - else - { - assert(!type.isVoid, format( - "binding %s: function does not have return value %s", - name.str, typeid(R))); - - assert(type.isDType(typeid(R)), format( - "binding %s: mismatch in return type, %s != %s", - name.str, type, typeid(R))); - } - - // This is the actual function that is bound, and called each time - // the native function is invoked. - void delegate() dg = - { - P parr; - - foreach_reverse(int i, PT; P) - { - parr[i] = stack.popType!(PT)(); - } - - static if(is(R == void)) - { - func(parr); - } - else - { - R r = func(parr); - stack.pushType!(R)(r); - } - }; - - // Store the function - ftype = FuncType.NativeDDel; - natFunc_dg = dg; - } - - template callT(T) - { - T callT(A...)(MonsterObject *mo, A a) - { - // Check parameter types - foreach(int i, p; A) - assert(params[i].type.isDType(typeid(p)), format( - "calling %s: type mismatch in parameter %s, %s != %s", - name.str, i, params[i].type, typeid(p))); - - // Check the return type - static if(is(T == void)) - { - assert(type.isVoid, format("calling %s: expected to return type %s", - name.str, type)); - } - else - { - assert(!type.isVoid, format( - "calling %s: function does not have return value %s", - name.str, typeid(T))); - - assert(type.isDType(typeid(T)), format( - "calling %s: mismatch in return type, %s != %s", - name.str, type, typeid(T))); - } - - // Push all the values - foreach(i, AT; A) - stack.pushType!(AT)(a[i]); - - call(mo); - - static if(!is(T == void)) - // Get the return value - return stack.popType!(T)(); - } - } - - // This is used to call the given function from native code. Note - // that this is used internally for native functions, but not for - // any other type. Idle functions can NOT be called directly from - // native code. Returns the thread, which is never null but might be - // dead. - Thread *call(MonsterObject *obj) - { - // Make sure there's a thread to use - if(cthread is null) - { - // Get a new thread and put it in the foreground - auto tr = Thread.getNew(); - tr.foreground(); - assert(tr is cthread); - } - - assert(cthread !is null); - assert(!cthread.isDead); - - bool wasEmpty = cthread.fstack.isEmpty; - - // Push the function on the stack - cthread.fstack.push(this, obj); - - switch(ftype) - { - case FuncType.NativeDDel: - natFunc_dg(); - goto pop; - case FuncType.NativeDFunc: - natFunc_fn(); - goto pop; - case FuncType.NativeCFunc: - natFunc_c(); - pop: - // Remove ourselves from the function stack - cthread.fstack.pop(); - break; - case FuncType.Normal: - cthread.execute(); - assert(!cthread.shouldExit, - "shouldExit should only be set for state threads"); - // Execute will pop itself if necessary - break; - case FuncType.Native: - fail("Called unimplemented native function " ~ toString); - case FuncType.Idle: - fail("Cannot call idle function " ~ toString ~ " from native code"); - case FuncType.Abstract: - fail("Called unimplemented abstract function " ~ toString); - default: - assert(0, "unknown FuncType for " ~ toString); - } - - // If we started at the bottom of the function stack, put the - // thread in the background now. This will automatically delete - // the thread if it's not used. - assert(cthread !is null); - auto ct = cthread; - if(wasEmpty) - { - if(cthread.fstack.isEmpty && stack.getPos != 0) - { - assert(cthread.isTransient); - - // We have to do some trickery to retain the stack in - // cases where the function exits completely. - cthread = null; // This will prevent kill() from clearing - // the stack. - ct.kill(); - } - else - cthread.background(); - } - return ct; - } - - // Call without an object. TODO: Only allowed for functions compiled - // without a class using compile() below, but in the future it will - // be allowed for static function. - Thread* call() - { - assert(owner is int_mc); - assert(owner !is null); - assert(int_mo !is null); - return call(int_mo); - } - - // This allows you to compile a function file by writing fn = - // Function("filename"). - static Function opCall(char[] file, MonsterClass mc = null) - { - Function fn; - fn.compile(file, mc); - return fn; - } - - // Compile the function script 'file' in the context of the class - // 'mc'. If no class is given, use an empty internal class. - void compile(char[] file, MonsterClass mc = null) - { - vm.init(); - - // Check if the file exists - if(!vm.vfs.has(file)) - fail("File not found: " ~ file); - - // Create the stream and pass it on - auto bf = vm.vfs.open(file); - compile(file, bf, mc); - delete bf; - } - - void compile(char[] file, Stream str, MonsterClass mc = null) - { - vm.init(); - - // Get the BOM and tokenize the stream - auto ef = new EndianStream(str); - int bom = ef.readBOM(); - TokenArray tokens = tokenizeStream(file, ef, bom); - compile(file, tokens, mc); - //delete ef; - } - - void compile(char[] file, ref TokenArray tokens, MonsterClass mc = null) - { - vm.init(); - - assert(name.str == "", - "Function " ~ name.str ~ " has already been set up"); - - // Check if this is a class or a module file first - if(MonsterClass.canParse(tokens)) - fail("Cannot run " ~ file ~ " - it is a class or module."); - - // Set mc to the empty class if no class is given - if(mc is null) - mc = getIntMC(); - - auto fd = new FuncDeclaration; - // Parse and comile the function - fd.parseFile(tokens, this); - name.str = file; - name.loc.fname = file; - fd.resolve(mc.sc); - fd.resolveBody(); - fd.compile(); - assert(fd.fn == this); - delete fd; - } - - static MonsterClass getIntMC() - { - if(int_mc is null) - { - assert(int_mo is null); - int_mc = vm.loadString(int_class); - int_mo = int_mc.createObject; - } - assert(int_mo !is null); - return int_mc; - } - - static MonsterObject *getIntMO() - { - getIntMC(); - return int_mo; - } - - // Returns the function name, on the form Class.func() - char[] toString() - { return owner.name.str ~ "." ~ name.str ~ "()"; } - - private: - - // Global unique function index - int gIndex = -1; - - // Empty class / object used internally - static const char[] int_class = "class _ScriptFile_;"; - static MonsterClass int_mc; - static MonsterObject *int_mo; -} - -// A specialized function declaration that handles class constructors -class Constructor : FuncDeclaration -{ - static bool canParse(TokenArray toks) - { return toks.isNext(TT.New); } - - void parse(ref TokenArray toks) - { - // Create a Function struct. - fn = new Function; - - // Default function type is normal - fn.ftype = FuncType.Normal; - - // No return value - fn.type = BasicType.getVoid; - - // Parse - toks.reqNext(TT.New, fn.name); - loc = fn.name.loc; - code = new CodeBlock; - code.parse(toks); - } - - char[] toString() - { - char[] res = "Constructor:\n"; - assert(code !is null); - res ~= code.toString(); - return res; - } - - // Resolve the constructor - void resolve(Scope last) - { - assert(fn.type !is null); - - // Create a local scope for this function - sc = new FuncScope(last, fn); - - // Set the owner class - auto cls = sc.getClass(); - fn.owner = cls; - - // Make sure we're assigned to the class - assert(cls.scptConst is this); - - // Resolve the function body - assert(code !is null); - code.resolve(sc); - } -} - -// Global list of functions. -GrowArray!(Function*) functionList; - -// Responsible for parsing, analysing and compiling functions. -class FuncDeclaration : Statement -{ - CodeBlock code; - VarDeclaration[] paramList; - FuncScope sc; // Scope used internally in the function body - - // The persistant function definition. This data will be passed to - // the VM when the compiler is done working. - Function *fn; - - // Is the 'override' keyword present - bool isOverride; - - // Is this a stand-alone script file (not really needed) - bool isFile; - - // Parse keywords allowed to be used on functions. This (and its - // borthers elsewhere) is definitely ripe for some pruning / - // refactoring. - private void parseKeywords(ref TokenArray toks) - { - Floc loc; - - // Get the old state - bool isNative = fn.isNative; - bool isAbstract = fn.isAbstract; - bool isIdle = fn.isIdle; - - while(1) - { - if(isNext(toks, TT.Native, loc)) - { - if(isNative) - fail("Multiple token 'native' in function declaration", - loc); - isNative = true; - continue; - } - if(isNext(toks, TT.Abstract, loc)) - { - if(isAbstract) - fail("Multiple token 'abstract' in function declaration", - loc); - isAbstract = true; - continue; - } - if(isNext(toks, TT.Idle, loc)) - { - if(isIdle) - fail("Multiple token 'idle' in function declaration", - loc); - isIdle = true; - continue; - } - if(isNext(toks, TT.Override, loc)) - { - if(isOverride) - fail("Multiple token 'override' in function declaration", - loc); - isOverride = true; - continue; - } - if(isNext(toks, TT.Final, loc)) - { - if(fn.isFinal) - fail("Multiple token 'final' in function declaration", - loc); - fn.isFinal = true; - continue; - } - break; - } - - // Check that only one of the type keywords are used - if( (isAbstract && isNative) || - (isAbstract && isIdle) || - (isNative && isIdle) ) - fail("Only one of the keywords native, idle, abstract can be used on one function", loc); - - // Set the new state - if(isNative) fn.ftype = FuncType.Native; - else if(isAbstract) fn.ftype = FuncType.Abstract; - else if(isIdle) fn.ftype = FuncType.Idle; - else assert(fn.isNormal); - } - - void parseFile(ref TokenArray toks, Function *fnc) - { - isFile = true; - fn = fnc; - fn.ftype = FuncType.Normal; - - // Is there an explicit function declaration? - if(isNext(toks, TT.Function)) - { - TokenArray temp = toks; - - reqNext(temp, TT.Identifier); - - // Is this a function without type? - if(isFuncDec(toks)) - // If so, set the type to void - fn.type = BasicType.getVoid; - else - // Otherwise, parse it - fn.type = Type.identify(toks); - - // In any case, parse the rest of the declaration - parseParams(toks); - - reqSep(toks); - } - else - { - // No type, no parameters - fn.type = BasicType.getVoid; - fn.name.str = "script-file"; - } - - code = new CodeBlock(false, true); - code.parse(toks); - } - - void parse(ref TokenArray toks) - { - // Create a Function struct. - fn = new Function; - - // Register a global index for all class functions - fn.register(); - assert(fn.getGIndex() != -1); - - // Default function type is normal - fn.ftype = FuncType.Normal; - - // Parse keyword list - parseKeywords(toks); - - // Is this a function without type? - if(isFuncDec(toks)) - // If so, set the type to void - fn.type = BasicType.getVoid; - else - // Otherwise, parse it - fn.type = Type.identify(toks); - - // Parse any other keywords - parseKeywords(toks); - - parseParams(toks); - - if(fn.isAbstract || fn.isNative || fn.isIdle) - { - reqSep(toks); - } - else - { - code = new CodeBlock; - code.parse(toks); - } - } - - // Parse function name and parameters - void parseParams(ref TokenArray toks) - { - fn.name = next(toks); - loc = fn.name.loc; - if(fn.name.type != TT.Identifier) - fail("Token '" ~ fn.name.str ~ "' cannot be used as a function name", - loc); - - if(!isNext(toks, TT.LeftParen)) - fail("Function expected parameter list", toks); - - // Parameters? - if(!isNext(toks, TT.RightParen)) - { - auto vd = new VarDeclaration(); - vd.parse(toks); - paramList ~= vd; - - // Other parameters - while(isNext(toks, TT.Comma)) - { - vd = new VarDeclaration(); - vd.parse(toks); - paramList ~= vd; - } - - // Vararg-parameter? - if(isNext(toks, TT.DDDot)) - paramList[$-1].var.isVararg = true; - - if(!isNext(toks, TT.RightParen)) - fail("Expected end of parameter list", toks); - } - } - - // Can the given tokens be parsed as the main function declaration? - static bool isFuncDec(TokenArray toks) - { - return isNext(toks, TT.Identifier) && isNext(toks, TT.LeftParen); - } - - static bool canParse(TokenArray toks) - { - // Is the next token an allowed keyword? - bool isKeyword(ref TokenArray toks) - { - return - isNext(toks, TT.Native) || - isNext(toks, TT.Abstract) || - isNext(toks, TT.Override) || - isNext(toks, TT.Final) || - isNext(toks, TT.Idle); - } - - // Remove keywords - while(isKeyword(toks)) {} - - // We allow the declaration to have no type (which implies type - // void) - if(isFuncDec(toks)) return true; - - // The next token(s) must be the type - if(!Type.canParseRem(toks)) return false; - - // There might be more keywords - while(isKeyword(toks)) {} - - // Finally we must have the function declaration at the end - return isFuncDec(toks); - } - - char[] toString() - { - char[] res = "Function declaration: "; - assert(fn.type !is null); - - res ~= fn.type.toString(); - - res ~= " " ~ fn.name.str ~ "("; - if(paramList.length) - { - if(paramList.length > 1) - foreach(par; paramList[0..paramList.length-1]) - res ~= par.toString ~ ", "; - - res ~= paramList[$-1].toString; - } - - res ~= ")\n"; - if(code !is null) res ~= code.toString(); - return res; - } - - // Resolve the function definition (return type and parameter - // types). The rest is handed by resolveBody() - void resolve(Scope last) - { - assert(fn.type !is null); - - fn.type.resolve(last); - - if(fn.type.isVar) - fail("var not allowed as function return type", fn.type.loc); - - if(fn.type.isReplacer) - fn.type = fn.type.getBase(); - - // Create a local scope for this function - sc = new FuncScope(last, fn); - - // Calculate total size of parameters. This value is also used - // in compile() and by external classes, so we store it. - fn.paramSize = 0; - foreach(vd; paramList) - { - // Resolve the variable first, to make sure we get the right - // size - vd.resolveParam(sc); - assert(!vd.var.type.isReplacer); - - fn.paramSize += vd.var.type.getSize(); - } - - // Set the owner class - fn.owner = sc.getClass(); - - // Parameters are given negative numbers according to their - // position backwards from the stack pointer, the last being - // -1. - int pos = -fn.paramSize; - - // Set up the function variable list - // TODO: Do fancy memory management - fn.params.length = paramList.length; - - // Set up function parameter numbers and insert them into the - // list. - foreach(i, dec; paramList) - { - assert(pos < 0); - dec.setNumber(pos); - pos += dec.var.type.getSize(); - fn.params[i] = dec.var; - } - assert(pos == 0); - - // Vararg functions must have the last parameter as an array. - if(fn.isVararg) - { - assert(paramList.length > 0); - auto dc = paramList[$-1]; - if(!dc.var.type.isArray) - fail("Vararg argument must be an array type, not " ~ - dc.var.type.toString, dc.var.name.loc); - } - - assert(pos == 0, "Variable positions didn't add up"); - - // Do we override a function? - if(fn.overrides) - { - Function *o = fn.overrides; - assert(fn.owner !is null); - assert(o.owner !is null, - "overrided function must be resolved before us"); - - if(fn.owner is o.owner) - fail(format("Function %s is already declared on line %s", - fn.name.str, o.name.loc.line), fn.name.loc); - - // Check that the function we're overriding isn't final - if(o.isFinal) - fail("Cannot override final function " ~ o.toString, fn.name.loc); - - // Check that signatures match - if(o.type != fn.type) - fail(format("Cannot override %s with different return type (%s -> %s)", - fn.name.str, o.type, fn.type), fn.name.loc); - - bool parFail = false; - if(o.params.length != fn.params.length) parFail = true; - else - foreach(i,p; fn.params) - if(p.type != o.params[i].type || - p.isVararg != o.params[i].isVararg) - { - parFail = true; - break; - } - - if(parFail) - fail(format("Cannot override %s, parameter types do not match", - o.toString), fn.name.loc); - - if(o.isIdle && !fn.isIdle) - fail("Cannot override idle function " ~ o.name.str ~ - " with a non-idle function", fn.name.loc); - if(!o.isIdle && fn.isIdle) - fail("Cannot override normal function " ~ o.name.str ~ - " with an idle function", fn.name.loc); - } - else - { - // No overriding. Make sure the 'override' flag isn't set. - if(isOverride) - fail("function " ~ fn.name.str ~ - " doesn't override anything", fn.name.loc); - } - - // Get the values of parameters which have default values - // assigned - fn.defaults.length = paramList.length; - foreach(i, dec; paramList) - { - if(dec.init !is null) - { - if(fn.isVararg) - fail("Vararg functions cannot have default parameter values", fn.name.loc); - - // Get the value and store it. Fails if the expression - // is not computable at compile time. - fn.defaults[i] = dec.getCTimeValue(); - assert(fn.defaults[i].length > 0); - } - } - - } - - // Resolve the interior of the function - void resolveBody() - { - // Validate all types (make sure there are no dangling forward - // references) - fn.type.validate(fn.name.loc); - foreach(p; fn.params) - p.type.validate(fn.name.loc); - - // Resolve the function body - if(code !is null) - code.resolve(sc); - } - - void compile() - { - if(fn.isAbstract || fn.isNative || fn.isIdle) - { - // No body to compile - return; - } - - tasm.newFunc(); - code.compile(); - - tasm.setLine(code.endLine.line); - - if(fn.type.isVoid) - // Remove parameters from the stack at the end of the function - tasm.exit(fn.paramSize,0,0); - else - // Functions with return types must have a return statement, - // so we should never reach the end of the function. Fail if - // we do. - tasm.error(Err.NoReturn); - - // Assemble the finished function - fn.bcode = tasm.assemble(fn.lines); - } -} - -struct NamedParam -{ - Token name; - Expression value; -} - -// Expression representing a function call -class FunctionCallExpr : Expression -{ - Expression fname; // Function name or pointer value - ExprArray params; // Normal (non-named) parameters - NamedParam[] named; // Named parameters - - ExprArray coverage; // Expressions sorted in the same order as the - // function parameter list. Null expressions - // means we must use the default value. Never - // used for vararg functions. - - // These are used to get information about the function and - // parameters. If this is a function reference call, fd is null and - // only frt is set. - Function* fd; - FuncRefType frt; - - bool isCast; // If true, this is an explicit typecast, not a - // function call. - - bool isVararg; - - bool fResolved; // True if fname is already resolved - - // Read a function parameter list (a,b,v1=c,v2=d,...). The function - // expects that you have already removed the initial left paren '(' - // token. - static void getParams(ref TokenArray toks, - out ExprArray parms, - out NamedParam[] named) - { - parms = null; - named = null; - - // No parameters? - if(isNext(toks, TT.RightParen)) return; - - // Read the comma-separated list of parameters - do - { - if(toks.length < 2) - fail("Unexpected end of stream"); - - // Named paramter? - if(toks[1].type == TT.Equals) - { - NamedParam np; - - reqNext(toks, TT.Identifier, np.name); - reqNext(toks, TT.Equals); - np.value = Expression.identify(toks); - - named ~= np; - } - else - { - // Normal parameter - if(named.length) - fail("Cannot use non-named parameters after a named one", - toks[0].loc); - - parms ~= Expression.identify(toks); - } - } - while(isNext(toks, TT.Comma)) - - if(!isNext(toks, TT.RightParen)) - fail("Parameter list expected ')'", toks); - } - - // Special version of getParams that allow 'console-mode' grammar. - static void getParamsConsole(ref TokenArray toks, - out ExprArray parms, - out NamedParam[] named) - { - parms = null; - named = null; - - // The param list is terminated by a semicolon or a newline - while(!isSep(toks)) - { - if(toks.length == 0) - fail("Unexpected end of stream"); - - // Named paramter? - if(toks.length >= 3 && toks[1].type == TT.Equals) - { - NamedParam np; - - reqNext(toks, TT.Identifier, np.name); - reqNext(toks, TT.Equals); - np.value = Expression.identify(toks); - - named ~= np; - } - else - { - // Normal parameter - if(named.length) - fail("Cannot use non-named parameters after a named one", - toks[0].loc); - - parms ~= Expression.identify(toks); - } - - // Allow optional commas between parameters - isNext(toks, TT.Comma); - } - } - - this(Expression func, ref TokenArray toks, bool console=false) - { - assert(func !is null); - fname = func; - loc = fname.loc; - - // Parse the parameter list. The 'console' parameter determines - // whether we're using normal grammar rules: - // func(param1,param2) - // or console rules - // func param1 param2 - if(console) - getParamsConsole(toks, params, named); - else - getParams(toks, params, named); - fResolved = console; - } - - void parse(ref TokenArray toks) { assert(0); } - - char[] toString() - { - char[] result = fname.toString ~ "("; - foreach(b; params) - result ~= b.toString ~", "; - foreach(b; named) - result ~= b.name.str ~ "=" ~ b.value.toString ~ ", "; - return result ~ ")"; - } - - char[] name() { assert(fname !is null); return fname.toString(); } - - void resolve(Scope sc) - { - // Resolve the function lookup first - if(!fResolved) - fname.resolve(sc); - assert(fname.type !is null); - - // Is the 'function' really a type name? - if(fname.type.isMeta) - { - // If so, it's a type cast! Get the type we're casting to. - type = fname.type.getBase(); - assert(type !is null); - - // Only one (non-named) parameter is allowed - if(params.length != 1 || named.length != 0) - fail("Invalid parameter list to type cast", loc); - - isCast = true; - - // Resolve the expression we're converting - params[0].resolve(sc); - - // Cast it - type.typeCastExplicit(params[0]); - - return; - } - - // TODO: Do typecasting here. That will take care of polysemous - // types later as well. - if(!fname.type.isIntFunc && !fname.type.isFuncRef) - fail(format("Expression '%s' of type %s is not a function", - fname, fname.typeString), loc); - - if(fname.type.isFuncRef) - { - // Set frt to the reference type - frt = cast(FuncRefType)fname.type; - assert(frt !is null); - fd = null; - } - else if(fname.type.isIntFunc) - { - // Get the function from the type - auto ft = cast(IntFuncType)fname.type; - assert(ft !is null); - fd = ft.func; - - // Create a temporary reference type - frt = new FuncRefType(ft); - } - - isVararg = frt.isVararg; - type = frt.retType; - assert(type !is null); - - if(fd is null && named.length != 0) - fail("Cannot use named parameters when calling function references", - loc); - - // Get the best parameter name possible from the available - // information - char[] getParName(int i) - { - char[] dst = "parameter "; - if(fd !is null) - dst ~= fd.params[i].name.str; - else - dst ~= .toString(i); - return dst; - } - - if(isVararg) - { - if(named.length) - fail("Cannot give named parameters to vararg functions", loc); - - // The vararg parameter can match a variable number of - // arguments, including zero. - if(params.length < frt.params.length-1) - fail(format("%s() expected at least %s parameters, got %s", - name, frt.params.length-1, params.length), - loc); - - // Check parameter types except for the vararg parameter - foreach(int i, par; frt.params[0..$-1]) - { - params[i].resolve(sc); - - // The info about each parameter depends on whether this - // is a direct function call or a func reference call. - par.typeCast(params[i], getParName(i)); - } - - // Loop through remaining arguments - int start = frt.params.length-1; - - assert(frt.params[start].isArray); - Type base = frt.params[start].getBase(); - - foreach(int i, ref par; params[start..$]) - { - par.resolve(sc); - - // If the first and only vararg parameter is of the - // array type itself, then we are sending an actual - // array. Treat it like a normal parameter. - if(i == 0 && start == params.length-1 && - par.type == frt.params[start]) - { - isVararg = false; - coverage = params; - break; - } - - // Otherwise, cast the type to the array base type. - base.typeCast(par, "vararg array base type"); - } - return; - } - - // Non-vararg case. Non-vararg functions must cover at least all - // the non-optional function parameters. - - int parNum = frt.params.length; - - // When calling a function reference, we can't use named - // parameters, and we don't know the function's default - // values. Because of this, all parameters must be present. - if(fd is null && params.length != parNum) - fail(format("Function reference %s (of type '%s') expected %s arguments, got %s", - name, frt, parNum, params.length), fname.loc); - - // Sanity check on the parameter number for normal function - // calls - if(params.length > parNum) - fail(format("Too many parameters to function %s(): expected %s, got %s", - name, parNum, params.length), fname.loc); - - // Make the coverage list of all the parameters. - coverage = new Expression[parNum]; - - // Mark all the parameters which are present. Start with the - // sequential (non-named) paramteres. - foreach(i,p; params) - { - assert(coverage[i] is null); - assert(p !is null); - coverage[i] = p; - } - - if(fd !is null) - { - // This part (named and optional parameters) does not apply - // when calling function references - - assert(fd !is null); - - // Add named parameters to the list - foreach(p; named) - { - // Look up the named parameter - int index = -1; - foreach(i, fp; fd.params) - if(fp.name.str == p.name.str) - { - index = i; - break; - } - if(index == -1) - fail(format("Function %s() has no parameter named %s", - name, p.name.str), - p.name.loc); - - assert(index= frt.params.length-1) - continue; - - // Convert 'const' parameters to actual constant references - if(frt.isConst[i]) - { - assert(frt.params[i].isArray); - tasm.makeArrayConst(); - } - } - - // Compute the length of the vararg array. - int len = params.length - frt.params.length + 1; - - // If it contains no elements, push a null array reference - // (0 is always null). - if(len == 0) tasm.push(0); - else - { - // Convert the pushed values to an array index - tasm.popToArray(len, params[$-1].type.getSize()); - - // Convert the vararg array to 'const' if needed - if(frt.isConst[$-1]) - { - assert(frt.params[$-1].isArray); - tasm.makeArrayConst(); - } - } - return; - } - - // Non-vararg case - assert(!isVararg); - assert(coverage.length == frt.params.length); - foreach(i, ex; coverage) - { - if(ex !is null) - { - assert(ex.type !is null); - assert(frt.params[i] !is null); - assert(ex.type == frt.params[i]); - - ex.eval(); - } - else - { - // resolve() should already have caught this - assert(fd !is null, - "cannot use default parameters with reference calls"); - - // No param specified, use default value - assert(fd.defaults[i].length == - fd.params[i].type.getSize); - assert(fd.params[i].type.getSize > 0); - tasm.pushArray(fd.defaults[i]); - } - - // Convert 'const' parameters to actual constant references - if(frt.isConst[i]) - { - assert(frt.params[i].isArray); - tasm.makeArrayConst(); - } - } - } - - void evalAsm() - { - if(isCast) - { - // This is a type cast, not a function call. - - // Just evaluate the expression. CastExpression takes care - // of everything automatically. - assert(params.length == 1); - assert(params[0] !is null); - params[0].eval(); - - return; - } - assert(frt !is null); - - // Push parameters first - evalParams(); - - // Then push the function expression or reference. - fname.eval(); - setLine(); - - bool isFar; - - // Total stack imprint of the function call - int imprint; - - if(fd !is null) - { - // Direct function call - assert(fname.type.isIntFunc); - auto ft = cast(IntFuncType)fname.type; - assert(ft !is null); - - assert(fd.owner !is null); - - // Calculate the stack imprint - imprint = fd.type.getSize() - fd.paramSize; - - // Push the function index. For far function calls, we have - // in effect pushed a function reference (object+function) - tasm.push(fd.getGIndex()); - - isFar = ft.isMember; - } - else - { - // Function reference call - assert(fname.type.isFuncRef); - isFar = true; - - // Let the type calculate the stack imprint - auto frt = cast(FuncRefType)fname.type; - assert(frt !is null); - - imprint = frt.getImprint(); - } - - tasm.call(isFar, imprint); - } -} diff --git a/old_d_version/monster/compiler/linespec.d b/old_d_version/monster/compiler/linespec.d deleted file mode 100644 index 9d664cd8b..000000000 --- a/old_d_version/monster/compiler/linespec.d +++ /dev/null @@ -1,74 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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; -} diff --git a/old_d_version/monster/compiler/operators.d b/old_d_version/monster/compiler/operators.d deleted file mode 100644 index 1eb2a3284..000000000 --- a/old_d_version/monster/compiler/operators.d +++ /dev/null @@ -1,1150 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (operators.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.operators; - -import monster.compiler.expression; -import monster.compiler.assembler; -import monster.compiler.tokenizer; -import monster.compiler.scopes; -import monster.compiler.types; -import monster.compiler.functions; -import monster.compiler.enums; -import monster.compiler.variables; -import monster.vm.error; -import monster.vm.arrays; - -import std.stdio; -import std.string; -import std.utf; - -// Handles - ! ++ -- @ -class UnaryOperator : Expression -{ - TT opType; - Expression exp; - bool postfix; - - // Original function type (for @) - IntFuncType ift; - - this() - { - postfix = false; - } - - // Used for postfix operators exp++ and exp-- - this(Expression ex, bool isAdd) - { - if(isAdd) opType = TT.PlusPlus; - else opType = TT.MinusMinus; - postfix = true; - exp = ex; - } - - static bool canParse(TokenArray toks) - { - return - isNext(toks, TT.Minus) || - isNext(toks, TT.Not) || - isNext(toks, TT.Alpha) || - isNext(toks, TT.PlusPlus) || - isNext(toks, TT.MinusMinus); - } - - void parse(ref TokenArray toks) - { - opType = next(toks).type; - exp = Expression.identifySub(toks); - } - - char[] toString() - { - if(postfix) return "(" ~ exp.toString ~ ")" ~ tokenList[opType]; - return tokenList[opType] ~ "(" ~ exp.toString ~ ")"; - } - - void resolve(Scope sc) - { - exp.resolve(sc); - - // Copy everything from the sub expression - type = exp.type; - loc = exp.getLoc; - - if(opType == TT.Alpha) - { - if(!type.isIntFunc) - fail("Operator @ cannot be used on non-function " ~ - exp.toString() ~ " (of type " ~ exp.typeString() ~ - ")", loc); - - ift = cast(IntFuncType)type; - assert(ift !is null); - - // Make sure this function has an index - if(!ift.func.hasGIndex()) - fail("Cannot create reference to function " ~ ift.func.toString(), - loc); - - // Replace the internal function type with a function - // reference. - type = new FuncRefType(ift); - - // If the function is not a member, make sure it's a class - // function in our current class (or one of our parents.) - assert(ift.isMember || (sc.getClass().childOf(ift.func.owner)), - "oops, found the wrong owner class"); - } - - if(opType == TT.PlusPlus || opType == TT.MinusMinus) - { - if(!type.isIntegral) - fail("Operator " ~ tokenList[opType] ~ " not allowed for " ~ - exp.toString() ~ " of type " ~ exp.typeString(), loc); - - if(!exp.isLValue) - fail("Operator " ~ tokenList[opType] ~ - " only allowed for lvalues, not " ~ exp.toString(), - loc); - } - - if(opType == TT.Minus) - { - // Type check - if(!type.isNumerical) - fail("Unary minus only allowed for numerical types, not " - ~ exp.typeString, loc); - - // Convert unsigned types to signed when using unary minus - if(type.isUint) type = BasicType.getInt(); - if(type.isUlong) type = BasicType.getLong(); - } - - if(opType == TT.Not && !type.isBool) - fail("Boolean ! operator only allowed for bools, not " - ~ exp.typeString, loc); - } - - bool isCTime() - { - if(opType == TT.PlusPlus || opType == TT.MinusMinus || - opType == TT.Alpha) - return false; - - return exp.isCTime(); - } - - int[] evalCTime() - { - int[] res = exp.evalCTime(); - - assert(res.length == type.getSize()); - - if(opType == TT.Minus) - { - if(type.isInt || type.isUint) - res[0] *= -1; - else if(type.isLong || type.isUlong) - { - long *lp = cast(long*)res.ptr; - *lp *= -1; - } - else if(type.isFloat) - { - float *fp = cast(float*)res.ptr; - *fp *= -1; - } - else if(type.isDouble) - { - double *fp = cast(double*)res.ptr; - *fp *= -1; - } - else assert(0); - } - else fail("Cannot evaluate " ~ toString ~ " at compile time yet"); - - return res; - } - - void evalAsm() - { - if(opType == TT.PlusPlus || opType == TT.MinusMinus) - { - setLine(); - exp.incDec(opType, postfix); - return; - } - - exp.eval(); - setLine(); - - if(opType == TT.Alpha) - { - assert(ift !is null); - - // If the function is a member, the object will already have - // been pushed on the stack. If not, push this. - if(!ift.isMember) - tasm.pushThis(); - - // Push the function index - tasm.push(ift.func.getGIndex()); - } - - else if(opType == TT.Minus) - { - if(type.isInt || type.isLong || type.isFloat || type.isDouble) - tasm.neg(type); - // TODO: This is perhaps a little too strict, unsigned types - // are not included - else fail("unary minus not implemented for type " ~ typeString, loc); - } - else if(type.isBool && opType == TT.Not) tasm.not(); - else fail("unary operator " ~ toString() ~ " not implemented yet", loc); - } -} - -// OperatorExprs are compounds of expressions bound together by an -// operator. Eg. a+b is considered to be the single expression -// (a+b). Due to precedence condsiderations, these are not parsed in -// the normal way but rather put together from a list in -// Expression.identify(). Therefore, we do not need the parse() -// function. -abstract class OperatorExpr : Expression -{ - final void parse(ref TokenArray) { assert(0); } -} - -// Any sub-expression with array operators suffixed, eg a[], -// getList()[14], and array[2..$]; -class ArrayOperator : OperatorExpr -{ - bool isSlice; // Set if this is a slice - bool isFill; // Set during assignment if we're filling the array - // with one single value - - int isEnum; // Used for enum types - - // A stack mark giving the position of the array index on the stack. - CodeElement *stackMark; - - // name[], name[index], name[index..index2] - Expression name, index, index2; - - this(Expression name, Floc loc, - Expression index = null, - Expression index2 = null) - { - this.name = name; - this.index = index; - this.index2 = index2; - this.loc = loc; - } - - char[] toString() - { - char[] res = "(" ~ name.toString ~ "["; - if(index !is null) - res ~= index.toString; - if(index2 !is null) - res ~= " .. " ~ index2.toString; - return res ~= "])"; - } - - // We can ALWAYS assign to elements of an array. Const arrays are - // handled at runtime only at the moment. - bool isLValue() { return true; } - - void resolve(Scope sc) - { - name.resolve(sc); - - // Copy the type of the name expression - type = name.type; - - // May be used on enums as well - if(type.isEnum || - (type.isMeta && type.getBase.isEnum)) - { - if(type.isMeta) - type = type.getBase(); - - assert(type.isEnum); - - if(index is null) - fail("Missing lookup value", loc); - - if(index2 !is null) - fail("Slices are not allowed for enums", loc); - - index.resolve(sc); - - // The indices must be ints - Type lng = BasicType.getLong(); - if(index.type.canCastTo(lng)) - { - lng.typeCast(index, ""); - isEnum = 1; - } - else - { - if(!index.type.isString) - fail("Enum lookup value must be a number or a string, not type " - ~index.typeString, loc); - isEnum = 2; - } - - return; - } - - // Check that we are indeed an array. - if(!type.isArray) - fail("Expression '" ~ name.toString ~ "' of type '" ~ name.typeString - ~ "' is not an array", loc); - - // If we have one and only one index expression i[4], reduce the - // array level by one, but not if we have two. - if(index !is null) - { - // The first index is present. Our total expression is - // either name[index] or name[index..index2] - - // Create an inner scope where $ is a valid character. - ArrayScope isc = new ArrayScope(sc, this); - - index.resolve(isc); - - // The indices must be ints - Type tpint = BasicType.getInt(); - tpint.typeCast(index, "array index"); - - if(index2 !is null) - { - // slice, name[index..index2] - index2.resolve(isc); - tpint.typeCast(index2, "array index"); - isSlice = true; - } - else - // element access, name[index]. Change the type to the - // element type. - type = type.getBase(); - } - // Complete slice, a[] - else isSlice = true; - } - - bool isCTime() - { - if(isEnum) return index.isCTime; - - if(isDest) return false; - - // a[b] and a[b..c]; are compile time if a, b and c is. - if(index !is null && !index.isCTime) return false; - if(index2 !is null && !index2.isCTime) return false; - return name.isCTime(); - } - - AIndex arrind; - - int[] evalCTime() - { - if(isEnum) - { - assert(index !is null && index2 is null); - auto et = cast(EnumType)type; - assert(et !is null); - EnumEntry *ptr; - - if(isEnum == 1) - { - long val = *(cast(long*)index.evalCTime().ptr); - ptr = et.lookup(val); - if(ptr is null) - fail("No matching value " ~ .toString(val) ~ " in enum", loc); - } - else - { - assert(isEnum == 2); - AIndex ai = cast(AIndex)index.evalCTime()[0]; - char[] str = toUTF8(arrays.getRef(ai).carr); - ptr = et.lookup(str); - if(ptr is null) - fail("No matching value " ~ str ~ " in enum", loc); - } - return (&(ptr.index))[0..1]; - } - - // Get the array index - int[] arr = name.evalCTime(); - assert(arr.length == 1); - - // Full slice, a[]. No extra action is needed. - if(index is null) return arr; - - ArrayRef *arf = arrays.getRef(cast(AIndex)arr[0]); - - int elem = arf.elemSize; - assert(elem == name.type.getBase().getSize()); - - // Get the first index - arr = index.evalCTime(); - assert(arr.length == 1); - int ind = arr[0]; - - if(index2 is null) - { - // Normal indexing. - arr = arf.iarr[ind*elem..(ind+1)*elem]; - assert(arr.length == elem); - return arr; - } - - // Slice a[b..c]. Get the second index. - arr = index2.evalCTime(); - assert(arr.length == 1); - int ind2 = arr[0]; - - // Create a new array ref as a constant slice of the original - // data, and return it - arf = arrays.createConst(arf.iarr[ind*elem..ind2*elem], elem); - arrind = arf.getIndex(); - return (cast(int*)&arrind)[0..1]; - } - - // Since very little separates eval() and evalDest(), use a shared - // function. - bool isDest = false; - - void evalAsm() - { - if(isEnum) - { - assert(index !is null && index2 is null); - assert(type.isEnum); - assert((isEnum == 1 && index.type.isLong) || - (isEnum == 2 && index.type.isString)); - - // All we need is the index value - index.eval(); - - if(isEnum == 1) - tasm.enumValToIndex(type.tIndex); - else - tasm.enumNameToIndex(type.tIndex); - return; - } - - // Get a stack mark. This may be used by $ to get the array - // index later. - stackMark = tasm.markStack(); - - // Push the array index - name.eval(); - - setLine(); - - if(index is null) - { - // Full slice, a[]. No extra action is needed. - assert(index2 is null); - assert(isSlice); - } - - else if(index2 is null) - { - // Normal indexing, eg array[2] - - // Push the index, and then go fetch the element or push a - // pointer that directs us to the array. - index.eval(); - - setLine(); - if(isDest) tasm.elementAddr(); - else tasm.fetchElement(type.getSize()); - - assert(!isSlice); - } - else - { - // Slice, eg. array[1..4] - - // Push the indices - index.eval(); - index2.eval(); - - setLine(); - tasm.makeSlice(); - - assert(isSlice); - } - } - - void evalDest() - { - assert(isLValue); - isDest = true; - eval(); - } - - void store() - { - evalDest(); - - if(isSlice) - { - // Special case for left hand slices, of the type a[] or - // a[i..j]. In this case we use special commands to fill or - // copy the entire array data. - - if(isFill) - tasm.fillArray(type.getBase().getSize()); - else tasm.copyArray(); - } - else - tasm.mov(type.getSize()); - } -} - -// Handles the DOT operator, eg. someObj.someFunc(); -class DotOperator : OperatorExpr -{ - // owner.member - Expression owner; - MemberExpr member; - - this(Expression own, Expression memb, Floc loc) - { - owner = own; - - assert(own !is null, "owner cannot be null"); - assert(memb !is null); - - member = cast(MemberExpr)memb; - assert(member !is null); - - this.loc = loc; - } - - char[] toString() - { - return "(" ~ owner.toString ~ "." ~ member.toString ~ ")"; - } - - bool isLValue() - { - // For dots, the right hand side must be an lvalue. The left - // side can be anything, it just specifies where the right side - // is looked up. - return member.isLValue; - } - - void resolve(Scope sc) - { - // In order to find the correct scope for the right side, we - // need to resolve and find the type of the left side first. - owner.resolve(sc); - - Type ot = owner.type; - assert(ot !is null, "owner " ~ owner.toString ~ " has no type!"); - - ot.getMemberScope(); - - if(ot.getMemberScope() is null) - fail(owner.toString() ~ " of type " ~ owner.typeString() - ~ " cannot have members", loc); - - // Resolve the member in the context of the left scope. - member.resolveMember(sc, ot); - - type = member.type; - - // Make sure we only call static members when the owner is a - // type or a package. - if((ot.isMeta || ot.isPackage) && !member.isStatic) - fail("Can only access static members for " ~ owner.toString, - loc); - } - - // TODO: An evalMemberCTime() function could be used here - - void evalAsm() - { - evalCommon(); - member.eval(); - } - - void evalDest() - { - evalCommon(); - member.evalDest(); - } - - void store() - { - evalCommon(); - member.store(); - } - - void incDec(TT o, bool b) - { - evalCommon(); - member.incDec(o,b); - } - - void evalCommon() - { - // Ask the rhs if it needs the lhs. It says no if the rhs is - // staic or evaluatable at compile time, eg a type name, part of - // an enum, a static function or a static property. So if you - // use int i; i.max, you could get int.max without evaluating i. - if(!member.isStatic) - // We push the owner up front and let the member take care of - // it. - owner.eval(); - } -} - -// Boolean operators: ==, !=, <, >, <=, >=, &&, ||, =i=, =I=, !=i=, !=I= -class BooleanOperator : BinaryOperator -{ - bool leftIs(bool t) - { - if(!left.isCTime) return false; - int[] val = left.evalCTime(); - assert(val.length == 1); - if(val[0]) return t; - else return !t; - } - bool rightIs(bool t) - { - if(!right.isCTime) return false; - int[] val = right.evalCTime(); - assert(val.length == 1); - if(val[0]) return t; - else return !t; - } - - bool ctimeval; - - bool isMeta = false; - - public: - - this(Expression left, Expression right, TT opType, Floc loc) - { super(left, right, opType, loc); } - - override: - - void resolve(Scope sc) - { - left.resolve(sc); - right.resolve(sc); - - type = BasicType.getBool; - - // Check if one or both types are meta-types first - if(left.type.isMeta || right.type.isMeta) - { - isMeta = true; - - if(opType != TT.IsEqual && opType != TT.NotEqual) - fail("Cannot use operator " ~ tokenList[opType] ~ " on types", loc); - - assert(isCTime()); - - // This means we have one of the following cases: - // i == int - // int == int - - // In these cases, we compare the types only, and ignore the - // values of any expressions (they are not computed.) - - // Get base types - Type lb = left.type; - Type rb = right.type; - if(lb.isMeta) lb = lb.getBase(); - if(rb.isMeta) rb = rb.getBase(); - - // Compare the base types and store the result - ctimeval = (lb == rb) != 0; - - if(opType == TT.NotEqual) - ctimeval = !ctimeval; - else - assert(opType == TT.IsEqual); - - return; - } - - // Cast to a common type - Type.castCommon(left, right); - - // At this point the types must match - assert(left.type == right.type); - - // TODO: We might allow < and > for strings at some point. - if(opType == TT.Less || opType == TT.More || opType == TT.LessEq || - opType == TT.MoreEq) - { - if(!left.type.isNumerical() && !left.type.isChar()) - fail("Cannot use operator " ~ tokenList[opType] ~ " for type " - ~ left.typeString(), loc); - } - else if(opType == TT.And || opType == TT.Or) - { - if(!left.type.isBool()) - fail("Operator " ~ tokenList[opType] ~ " cannot be used on type " ~ - left.typeString(), loc); - } - else - { - assert(opType == TT.IsEqual || opType == TT.NotEqual || - opType == TT.IsCaseEqual || opType == TT.NotCaseEqual); - - // Nested arrays are handled recursively and element per - // element. This is not implemented yet. - if(left.type.arrays > 1) - fail("Boolean operators do not yet support nested arrays", loc); - - if(opType == TT.IsCaseEqual || opType == TT.NotCaseEqual) - if(!left.type.isChar() && !left.type.isString) - fail("Cannot use case insensitive operator " ~ tokenList[opType] ~ - " on type " ~ left.typeString(), loc); - } - } - - // Pretty much all of these can be evaluated at compile time when - // both sides are ctime. In the cases of && and ||, it's enough that - // one side is ctime to optimize the code, and sometimes cases make - // the expression compile time. The optimization cases must be - // handled be handled in evalAsm, since they are not applicable to - // static initialization. - bool isCTime() - { - // If both types are meta-types, then we might be ctime. - if(isMeta) - return (opType == TT.IsEqual || opType == TT.NotEqual); - - // Only compute && and || for now, add the rest later. - if(!(opType == TT.And || opType == TT.Or)) return false; - - if(left.isCTime && right.isCTime) return true; - - // Handle shortened cases - if(opType == TT.Or) - // If the left side is compile time computable and is true, - // the entire value must be true. The same is true for the - // right side, but the user expects the left-most expression - // to be evaluated at runtime, so we must do that. - if(leftIs(true)) return true; - - if(opType == TT.And) - // If the first side is false, we are a compile time value. - if(leftIs(false)) return true; - - return false; - } - - int[] evalCTime() - { - if(opType == TT.And) - ctimeval = !(leftIs(false) || rightIs(false)); - else if(opType == TT.Or) - ctimeval = (leftIs(true) || rightIs(true)); - else - // For meta types, ctimeval is already set - assert(isMeta); - - return (cast(int*)&ctimeval)[0..1]; - } - - void evalAsm() - { - bool negate = false; - int label; - Type tp = left.type; - int s = tp.getSize(); - - // To reduce the number of operations we implement, we treat < - // and > the same, but just reverse the order of expressions, so - // eg. a > b gets treated the same as b < a. - - if(opType == TT.More || opType == TT.LessEq) - { - right.eval(); - left.eval(); - } - else if(opType == TT.And || opType == TT.Or) - { - assert(tp.isBool); - - // TODO: We do compile time shortening here, if possible, - // and eliminate unneccesary code. - - // && and || are 'shortened', so even in the runtime case we - // only evaluate the first operand here. - left.eval(); - } - else - { - left.eval(); - right.eval(); - } - - setLine(); - - // Treat array types separately - if(tp.arrays != 0) - { - if(tp.arrays != 1) - fail("Cannot compare nested arrays yet", loc); - assert(s == 1); - - switch(opType) - { - case TT.NotEqual: - negate = true; - case TT.IsEqual: - tasm.cmpArray(); - break; - - case TT.NotCaseEqual: - negate = true; - case TT.IsCaseEqual: - assert(tp.isString, "Should not get here on non-string types"); - tasm.icmpString(); - break; - } - if(negate) tasm.not(); - return; - } - - // Non-array types - switch(opType) - { - case TT.NotEqual: - negate = true; - case TT.IsEqual: - tasm.isEqual(s); - break; - - case TT.NotCaseEqual: - negate = true; - case TT.IsCaseEqual: - assert(s == 1 && tp.isChar); - tasm.isCaseEqual(); - break; - - case TT.LessEq: - case TT.MoreEq: - negate=true; - case TT.Less: - case TT.More: - tasm.less(tp); - break; - - case TT.And: - // Skip the second operand if the first is false. The - // implementation here might be optimized later. - tasm.dup(); - label = tasm.jumpz(); - tasm.pop(); - right.eval(); // The second value now determines the final - // value of the expression. - tasm.label(label); - break; - - case TT.Or: - tasm.dup(); - label = tasm.jumpnz(); - tasm.pop(); - right.eval(); - tasm.label(label); - break; - - default: - fail("boolean operator "~tokenList[opType]~" not implemented", loc); - } - - // These not's can in many cases be optimized away at a later - // stage by peephole optimization. For example, a not and a - // jumpz is converted to a jumpnz. We might also create a - // isNotEqual instruction later on if that will significantly - // improve performance. - if(negate) tasm.not(); - } -} - -extern(C) double floor(double d); - -// All other binary operators. -class BinaryOperator : OperatorExpr -{ - Expression left, right; - TT opType; - - // Used with ~, is one side an element (instead of an array)? - enum CatElem { None, Left, Right } - CatElem cat; - - this(Expression left, Expression right, TT opType, Floc loc) - { - this.left = left; - this.right = right; - this.opType = opType; - this.loc = loc; - } - - char[] toString() - { - char[] opName = tokenList[opType]; - return "(" ~ left.toString ~ opName ~ right.toString ~ ")"; - } - - void resolve(Scope sc) - { - left.resolve(sc); - right.resolve(sc); - cat = CatElem.None; - - // Special case for concatination - if(opType == TT.Cat) - { - // Array with array - if(left.type.isArray && left.type == right.type) - { - type = left.type; - cat = CatElem.None; - return; - } - - // Element with array? - if(right.type.isArray && left.type.canCastOrEqual(right.type.getBase())) - { - right.type.getBase().typeCast(left, ""); - type = right.type; - cat = CatElem.Left; - return; - } - - // Array with element? - if(left.type.isArray && right.type.canCastOrEqual(left.type.getBase())) - { - left.type.getBase().typeCast(right, ""); - type = left.type; - cat = CatElem.Right; - return; - } - - fail("Cannot use operator ~ on types " ~ left.typeString ~ - " and " ~ right.typeString, left.loc); - } - - // Assume arithmetic operator for now (Later, check the complete - // list in tokenizer and Expression.identify and treat every - // case correctly.) - - // Cast to a common type - Type.castCommon(left, right); - - type = right.type; - - if(!type.isNumerical) - fail("Operator " ~tokenList[opType] ~ " not allowed for types " ~ - left.typeString() ~ " and " ~ right.typeString(), loc); - - // At this point the types must match - assert(left.type == right.type); - } - - bool isCTime() - { - return - left.isCTime() && right.isCTime() && - opType != TT.Cat; - - // We do not handle concatination here, because this operation - // is expected to create a new array each time it is executed, - // and the array is expected to be dynamic. We could still - // optimize this using compile time values, but it's not a - // priority. - } - - int[2] ctimeval; - - int[] evalCTime() - { - int ls[] = left.evalCTime(); - int rs[] = right.evalCTime(); - assert(ls.length == left.type.getSize()); - assert(rs.length == right.type.getSize()); - - assert(type.isNumerical); - - if(type.isInt || type.isUint) - { - int v1 = ls[0]; - int v2 = rs[0]; - int res; - switch(opType) - { - case TT.Plus: res = v1+v2; break; - case TT.Minus: res = v1-v2; break; - case TT.Mult: res = v1*v2; break; - case TT.Div: - case TT.IDiv: - if(type.isInt) - res = v1/v2; - else - res = (cast(uint)v1)/(cast(uint)v2); - break; - case TT.Rem: - if(type.isInt) - res = v1%v2; - else - res = (cast(uint)v1)%(cast(uint)v2); - break; - } - ctimeval[0] = res; - } - else if(type.isLong || type.isUlong) - { - long v1 = *(cast(long*)ls.ptr); - long v2 = *(cast(long*)rs.ptr); - long res; - switch(opType) - { - case TT.Plus: res = v1+v2; break; - case TT.Minus: res = v1-v2; break; - case TT.Mult: res = v1*v2; break; - case TT.Div: - case TT.IDiv: - if(type.isLong) - res = v1/v2; - else - res = (cast(ulong)v1)/(cast(ulong)v2); - break; - case TT.Rem: - if(type.isLong) - res = v1%v2; - else - res = (cast(ulong)v1)%(cast(ulong)v2); - break; - } - *(cast(long*)ctimeval.ptr) = res; - } - else if(type.isFloat) - { - float v1 = *(cast(float*)ls.ptr); - float v2 = *(cast(float*)rs.ptr); - float res; - switch(opType) - { - case TT.Plus: res = v1+v2; break; - case TT.Minus: res = v1-v2; break; - case TT.Mult: res = v1*v2; break; - case TT.Div: res = v1/v2; break; - case TT.IDiv: res = floor(v1/v2); break; - case TT.Rem: - res = v1-v2*floor(v1/v2); - break; - } - *(cast(float*)ctimeval.ptr) = res; - } - else if(type.isDouble) - { - double v1 = *(cast(double*)ls.ptr); - double v2 = *(cast(double*)rs.ptr); - double res; - switch(opType) - { - case TT.Plus: res = v1+v2; break; - case TT.Minus: res = v1-v2; break; - case TT.Mult: res = v1*v2; break; - case TT.Div: res = v1/v2; break; - case TT.IDiv: res = floor(v1/v2); break; - case TT.Rem: - res = v1-v2*floor(v1/v2); - break; - } - *(cast(double*)ctimeval.ptr) = res; - } - - // Return the final value - assert(type.getSize <= 2); - return ctimeval[0..type.getSize()]; - } - - void evalAsm() - { - left.eval(); - right.eval(); - - setLine(); - - // Concatination - if(opType == TT.Cat) - { - switch(cat) - { - case CatElem.None: - // Concatinating two arrays. - tasm.catArray(); - break; - case CatElem.Left: - tasm.catArrayLeft(left.type.getSize); - break; - case CatElem.Right: - tasm.catArrayRight(right.type.getSize); - break; - default: - assert(0, "Illegal value of 'cat'"); - } - return; - } - - // All other operators - - // If any type conversion is necessary to perform the operator, - // it has already been taken care of in resolve(). - assert(left.type == right.type, "Type mismatch: " ~ - left.type.toString ~ " != " ~ right.type.toString); - - if(type.isNumerical) - { - if(opType == TT.Plus) tasm.add(type); - else if(opType == TT.Minus) tasm.sub(type); - else if(opType == TT.Mult) tasm.mul(type); - else if(opType == TT.Div) tasm.div(type); - else if(opType == TT.IDiv) tasm.idiv(type); - else if(opType == TT.Rem) tasm.divrem(type); - else fail("operator "~tokenList[opType]~ - " not implemented yet"); - } - else assert(0, "Binary operators not implemented for this type yet: " ~ typeString); - } -} diff --git a/old_d_version/monster/compiler/properties.d b/old_d_version/monster/compiler/properties.d deleted file mode 100644 index 8b63f81c4..000000000 --- a/old_d_version/monster/compiler/properties.d +++ /dev/null @@ -1,377 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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; -} diff --git a/old_d_version/monster/compiler/scopes.d b/old_d_version/monster/compiler/scopes.d deleted file mode 100644 index cccf876af..000000000 --- a/old_d_version/monster/compiler/scopes.d +++ /dev/null @@ -1,1422 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (scopes.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.scopes; - -import std.stdio; -import std.string; - -import monster.util.aa; -import monster.options; - -import monster.compiler.statement; -import monster.compiler.expression; -import monster.compiler.tokenizer; -import monster.compiler.types; -import monster.compiler.assembler; -import monster.compiler.properties; -import monster.compiler.functions; -import monster.compiler.states; -import monster.compiler.structs; -import monster.compiler.enums; -import monster.compiler.operators; -import monster.compiler.variables; - -import monster.vm.mclass; -import monster.vm.error; -import monster.vm.vm; - -// The global scope -RootScope global; - -void initScope() -{ - global = new RootScope; -} - -// List all identifier types -enum LType - { - None, - Class, - Type, - Variable, - Function, - State, - StateLabel, - LoopLabel, - Property, - Import, - Package, - } - -const char[][] LTypeName = - [ - LType.None: "", - LType.Class: "class", - LType.Type: "type", - LType.Variable: "variable", - LType.Function: "function", - LType.State: "state", - LType.StateLabel: "state label", - LType.LoopLabel: "loop label", - LType.Property: "property", - LType.Package: "package", - ]; - -struct ScopeLookup -{ - Token name; - LType ltype; - - Scope sc; - Type type; - union - { - MonsterClass mc; - Variable* var; - Function* func; - State* state; - StateLabel *slabel; - ImportHolder imphold; - - void *ptr; - Object ob; - } - - bool isClass() { return ltype == LType.Class; } - bool isType() { return ltype == LType.Type; } - bool isVar() { return ltype == LType.Variable; } - bool isFunc() { return ltype == LType.Function; } - bool isState() { return ltype == LType.State; } - bool isNone() { return ltype == LType.None; } - bool isImport() { return ltype == LType.Import; } - bool isPackage() { return ltype == LType.Package; } - bool isProperty() - { - bool ret = (ltype == LType.Property); - assert( ret == (cast(PropertyScope)sc !is null) ); - return ret; - } - - // For properties only - Type getPropType(Type owner) - { - assert(type is null); - assert(owner !is null); - assert(name.str != ""); - - type = owner; - return ps().getType(name.str, owner); - } - private PropertyScope ps() - { - assert(isProperty); - assert(type !is null); - return cast(PropertyScope)sc; - } - bool isPropLValue() { return ps().isLValue(name.str, type); } - bool isPropStatic() { return ps().isStatic(name.str, type); } - void getPropValue() { ps().getValue(name.str, type); } - void setPropValue() { ps().setValue(name.str, type); } - - static ScopeLookup opCall(Token nm, LType lt, Type tp, Scope sc, Object ob) - { - auto sl = ScopeLookup(nm, lt, tp, sc); - sl.ob = ob; - return sl; - } - - static ScopeLookup opCall(Token nm, LType lt, Type tp, Scope sc, void*p = null) - { - assert(nm.str != ""); - - ScopeLookup sl; - sl.name = nm; - sl.ltype = lt; - sl.type = tp; - sl.sc = sc; - sl.ptr = p; - return sl; - } -} - - -// TODO: Write here which of these should be kept around at runtime, -// and which of them can be discarded after compilation. - -abstract class Scope -{ - protected: - // The parent scope. For function scopes, this is the scope of the - // class it belongs to. For code blocks, loops etc, it is the scope - // of the code block outside this one. For classes, this points to - // the scope of the parent class, if any, or to the package or - // global scope. - Scope parent; - - // Made protected since it is so easy to confuse with isStateCode(), - // and we never actually need it anywhere outside this file. - bool isState() { return false; } - - private: - // Name of this scope. All scopes must have a name, but for some - // types it is set automatically (like a code block scope.) It is - // mostly used for debugging. - char[] scopeName; - - ImportHolder importList[]; - - public: - - this(Scope last, char[] name) - { - scopeName = name; - parent = last; - - assert(last !is this, "scope cannot be it's own parent"); - assert(name != ""); - - // Copy the import list from our parent - if(!isRoot) - importList = parent.importList.dup; - } - - // Verify that an identifier is not declared in this scope. If the - // identifier is found, give a duplicate identifier compiler - // error. Recurses through parent scopes. - final void clearId(Token name) - { - static if(traceLookups) - writefln("ClearId %s in %s (line %s)", name, this, __LINE__); - - // Lookup checks all parent scopes so we only have to call it - // once. - auto sl = lookup(name, false); - - if(!sl.isNone) - { - if(sl.isProperty) - fail(name.str ~ " is a property and cannot be redeclared", - name.loc); - - char[] oldLoc = name.loc.toString; - - // There might be a case insensitive match. In that case we - // should print the original name as well. - if(sl.name.str != name.str) - oldLoc ~= " as " ~ sl.name.str; - - fail(format("%s is already declared as a %s (at %s)", - name.str, LTypeName[sl.ltype], oldLoc), - name.loc); - } - assert(sl.name.str == name.str); - } - - // Is this the root scope? - final bool isRoot() - { - if(parent !is null) return false; - assert(allowRoot(), toString() ~ " cannot be a root scope"); - return true; - } - - // Is THIS scope of this particular kind? - bool isFunc() { return false; } - bool isCode() { return false; } - bool isLoop() { return false; } - bool isClass() { return false; } - bool isArray() { return false; } - bool isStruct() { return false; } - bool isPackage() { return false; } - bool isProperty() { return false; } - - // Is this scope allowed to be a root scope (without parent?) - bool allowRoot() { return false; } - - // Get the function definition belonging to this scope. - Function *getFunction() - { - assert(!isRoot(), "getFunction called on a root scope"); - return parent.getFunction(); - } - - // Get the class - MonsterClass getClass() - { - assert(!isRoot(), "getClass called on a root scope"); - return parent.getClass(); - } - - State* getState() - { - assert(!isRoot(), "getState called on a root scope"); - return parent.getState(); - } - - ArrayOperator getArray() - { - assert(!isRoot(), "getArray called on wrong scope type"); - return parent.getArray(); - } - - int getLoopStack() - { - assert(!isRoot(), "getLoopStack called on wrong scope type"); - return parent.getLoopStack(); - } - - // Get the break or continue label for the given named loop, or the - // innermost loop if name is empty or omitted. Returns null if the - // label was not found. Can only be called within loops. - LabelStatement getBreak(char[] name = "") { return null; } - LabelStatement getContinue(char[] name = "") { return null; } - - // Lookup a string - final ScopeLookup lookupName(char[] name) - { return lookup(Token(name, Floc.init)); } - - // Look up a token in this scope. If autoLoad is true, load classes - // automatically if a file exists. - ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - if(isRoot()) return ScopeLookup(name, LType.None, null, null); - else return parent.lookup(name, autoLoad); - } - - // Look up a token in this scope. If the token is not found, try the - // lookup again and check the file system for classes. - ScopeLookup lookupClass(Token name) - { - auto sl = lookup(name); - if(sl.isNone) - sl = lookup(name, true); - return sl; - } - - // Look up an identifier, and check imported scopes as well. - ScopeLookup lookupImport(Token name, bool second=false) - { - static if(traceLookups) - writefln("LookupImport %s in %s (line %s)", name, this, __LINE__); - auto l = lookup(name, second); - if(!l.isNone) return l; - - // Nuttin' was found, try the imports - bool found = false; - auto old = l; - foreach(ind, imp; importList) - { - static if(traceLookups) - { - writefln(" LI: Import is: %s, index %s of %s", - imp, ind, importList.length-1); - } - l = imp.lookup(name, second); - - // Only accept types, classes, variables and functions - if(l.isType || l.isClass || l.isVar || l.isFunc) - { - // Duplicate matches aren't allowed - if(found && l.sc !is old.sc) - fail(format( - "%s matches both %s.%s (at %s) and %s.%s (at %s)", name.str, - old.imphold.getName(), old.name.str, old.name.loc, - imp.getName(), l.name.str, l.name.loc), - name.loc); - - // First match - found = true; - old = l; - old.imphold = imp; - } - } - - if(!found) - { - // If we haven't tried searching the file system for - // classes, try that. - if(!second) - return lookupImport(name, true); - - return ScopeLookup(name, LType.None, null, null); - } - - // Tell the caller that this is an import. We override the - // lookup struct, but it doesn't matter since the lookup will - // have to be performed again anyway. - old.ltype = LType.Import; - assert(old.imphold !is null); - return old; - } - - // More user-friendly version for API-defined import of classes. - // Eg. global.registerImport(myclass) -> makes myclass available in - // ALL classes. - void registerImport(MonsterClass mc) - { - // Check if this class is already implemented - foreach(imp; importList) - if(imp.isClass(mc)) return; - - static if(traceLookups) - writefln("Importing class %s into %s", mc, this); - importList ~= new ClassImpHolder(mc); - } - - // Ditto for packages - void registerImport(PackageScope psc) - { - // Never import the global scope - it's already implied as a - // parent scope of most other scopes - //if(psc is global) return; - - foreach(imp; importList) - if(imp.isPack(psc)) return; - - static if(traceLookups) - writefln("Importing package %s into %s", psc, this); - - importList ~= new PackageImpHolder(psc); - } - - // Even more user-friendly version for classes. Takes a list of - // class names. - void registerImport(char[][] cls ...) - { - foreach(c; cls) - registerImport(vm.load(c)); - } - - // Used for summing up stack level. Redeclared in StackScope. - int getTotLocals() { return 0; } - int getLocals() { assert(0); } - - // This must be overridden in all scopes that allow variable - // declarations. - int addNewVar(int varSize) { assert(0); } - - final: - - bool isStateCode() - { return isInState() && !isInFunc(); } - - // Is this scope OR one of the parent scopes of the given kind? - bool isInFunc() - { - if(isFunc()) return true; - if(isRoot()) return false; - return parent.isInFunc(); - } - bool isInState() - { - if(isState()) return true; - if(isRoot()) return false; - return parent.isInState(); - } - bool isInLoop() - { - if(isLoop()) return true; - if(isRoot()) return false; - return parent.isInLoop(); - } - bool isInClass() - { - if(isClass()) return true; - if(isRoot()) return false; - return parent.isInClass(); - } - bool isInPackage() - { - if(isPackage()) return true; - if(isRoot()) return false; - return parent.isInPackage(); - } - - char[] toString() - { - if(parent is null) return scopeName; - else return parent.toString() ~ "." ~ scopeName; - } -} - -final class StateScope : Scope -{ - private: - State* st; - - public: - - this(Scope last, State* s) - { - st = s; - super(last, st.name.str); - } - - // Insert a label, check for name collisions. - void insertLabel(StateLabel *lb) - { - // Check for name collisions - clearId(lb.name); - - st.labels[lb.name.str] = lb; - } - - override: - ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - assert(name.str != ""); - - // Check against state labels - StateLabel *lb; - if(st.labels.inList(name.str, lb)) - return ScopeLookup(lb.name, LType.StateLabel, null, this, lb); - - return super.lookup(name, autoLoad); - } - - State* getState() { return st; } - - bool isState() { return true; } -} - -final class RootScope : PackageScope -{ - private: - // Lookup by integer index. The indices are global, that is why they - // are stored in the root scope rather than in the individual - // packages. - HashTable!(CIndex, MonsterClass) indexList; - - // Forward references. Refers to unloaded and non-existing - // classes. TODO: This mechanism probably won't work very well with - // multiple packages. It should be possible to have forward - // references to multiple classes with the same name, as long as - // they are in the correct packages. Think it over some more. - HashTable!(char[], CIndex) forwards; - - // Unique global index to give the next class. - CIndex next = 1; - - public: - this() { super(null, "global", ""); } - bool allowRoot() { return true; } - - // Get a class by index - MonsterClass getClass(CIndex ind) - { - MonsterClass mc; - if(!indexList.inList(ind, mc)) - fail("Invalid class index encountered"); - mc.requireScope(); - return mc; - } - - // Gets the index of a class, or inserts a forward reference to it - // if cannot be found. Subsequent calls for the same name will - // insert the same index, and when/if the class is actually loaded - // it will get the same index. - CIndex getForwardIndex(char[] name) - { - // Called when an existing forward does not exist - void inserter(ref CIndex v) - { v = next++; } - - // Return the index in forwards, or create a new one if none - // exists. - return forwards.get(name, &inserter); - } - - // Returns true if a given class has been inserted into the scope. - bool isLoaded(CIndex ci) - { - return indexList.inList(ci); - } - - override ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - // Basic types are looked up here - if(BasicType.isBasic(name.str)) - return ScopeLookup(name, LType.Type, BasicType.get(name.str), this); - - // Return ourselves as the package named 'global' - if(name.str == "global") - return ScopeLookup(name, LType.Package, type, this); - - // No parents to check - assert(isRoot()); - return super.lookup(name, autoLoad); - } -} - -// A package scope is a scope that can contain classes. -class PackageScope : Scope -{ - private: - // List of classes in this package. This is case insensitive, so we - // can look up file names too. - HashTable!(char[], MonsterClass, GCAlloc, CITextHash) classes; - - // List of sub-packages - HashTable!(char[], PackageScope, GCAlloc, CITextHash) children; - - char[] path; - - public: - // The type is used for expression lookups, eg. packname.Class(obj); - PackageType type; - - this(Scope last, char[] name, char[] dir) - { - super(last, name); - - auto ps = cast(PackageScope) last; - assert(last is null || ps !is null, - "Packages can only have other packages as parent scopes"); - - path = dir; - assert(!path.begins("/")); - - assert(vm.vfs !is null); - if(dir != "" && !vm.vfs.hasDir(dir)) - fail("Cannot create package "~toString~": directory " - ~dir~" not found"); - - type = new PackageType(this); - } - - bool isPackage() { return true; } - - char[] getPath(char[] file) - { - if(path == "") return file; - - file = path ~ "/" ~ file; - assert(file[0] != '/'); - return file; - } - - // Insert a new sub-package, or find an existing one. All package - // names are case insensitive. Returns the package scope. Fails if - // there are naming conflicts with other identifiers. - PackageScope insertPackage(char[] name) - { - auto sl = lookupName(name); - - if(!sl.isPackage) - fail(format("Package name %s is already declared (at %s) as a %s", - sl.name.str, sl.name.loc, LTypeName[sl.ltype])); - if(sl.isNone) - fail("Cannot find package " ~ name); - - auto ps = cast(PackageScope)sl.sc; - assert(ps !is null); - return ps; - } - - // Used internally from lookup() - private PackageScope makeSubPackage(char[] name) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - if(!isValidIdent(name)) - fail("Cannot create package " ~ name ~ ": not a valid identifier"); - - assert(!children.inList(name)); - - PackageScope ps; - ps = new PackageScope(this, name, getPath(name)); - children[name] = ps; - - return ps; - } - - // Insert a new class into the scope. The class is given a unique - // global index. If the class was previously forward referenced, the - // forward is replaced and the previously assigned forward index is - // used. A class can only be inserted once. - void insertClass(MonsterClass cls) - { - assert(cls !is null); - assert(cls.name.str != ""); - - // Are we already in the list? - MonsterClass c2; - if(ciInList(cls.name.str, c2)) - { - // That's not allowed. Determine what error message to give. - - if(c2.name.str == cls.name.str) - // Exact name collision - fail("Cannot load class " ~ cls.name.str ~ - " because it is already loaded."); - - // Case insensitive match - fail("Cannot load class " ~ cls.name.str ~ " because " - ~ c2.name.str ~ - " already exists (class names cannot differ only by case.)"); - } - - // Check that no other identifier with this name exists - clearId(cls.name); - - // We're clear. Find an index to use. If the class was forward - // referenced, then an index has already been assigned. - CIndex ci; - - if(global.forwards.inList(cls.name.str, ci)) - { - if(this !is global) - fail("Class " ~ cls.name.str ~ " was forward referenced and can only be added to the 'global' package, not to " ~ toString); - - // ci is set, remove the entry from the forwards hashmap - assert(ci != 0); - global.forwards.remove(cls.name.str); - } - else - // Get a new index - ci = global.next++; - - assert(!global.indexList.inList(ci)); - - // Assign the index and insert class into both lists - cls.gIndex = ci; - classes[cls.name.str] = cls; - global.indexList[ci] = cls; - - assert(global.indexList.inList(ci)); - } - - // Case insensitive lookups. Used for comparing with file names, - // before the actual class is loaded. - bool ciInList(char[] name) - { return classes.inList(name); } - bool ciInList(char[] name, out MonsterClass cb) - { return classes.inList(name, cb); } - - // Case sensitive versions. If a class is found that does not match - // in case, it is an error. - bool csInList(char[] name, out MonsterClass cb) - { - bool result = ciInList(name, cb) && cb.name.str == name; - - // The caller depends on the output to be null if the search - // fails. - if(!result) cb = null; - - return result; - } - - override ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - // Look up packages - PackageScope psc; - if(!children.inList(name.str, psc)) - // Check the file system if there is a matching directory - if(vm.vfs.hasDir(getPath(name.str))) - psc = makeSubPackage(name.str); - - if(psc !is null) - return ScopeLookup(name, LType.Package, psc.type, psc); - - // Look up classes - MonsterClass mc; - if(!csInList(name.str, mc) && autoLoad) - { - // Load the class from file, if it exists - mc = vm.loadNoFail(name.str, - tolower(getPath(name.str))~".mn"); - } - - if(mc !is null) - return ScopeLookup(mc.name, LType.Class, null, this, mc); - - // Unlike other scopes, a package scope doesn't check through - // all its parent packages. That would mean that a name search - // for a package or a class could 'leak' out of a directory and - // find the wrong match. Instead we jump directly to the root - // scope. - - // If we're the root scope, super.lookup will return an empty - // result. - if(isRoot()) return super.lookup(name, autoLoad); - - // Otherwise, jump directly to root, do not collect 200 dollars. - assert(global !is this); - return global.lookup(name, autoLoad); - } -} - -// A scope that can contain variables. -abstract class VarScope : Scope -{ - private: - HashTable!(char[], Variable*) variables; - - public: - - this(Scope last, char[] name) - { super(last, name); } - - // Insert a variable, checks if it already exists. - void insertVar(Variable* dec) - { - assert(!isStateCode, "called insertVar in state code"); - - // Check for name collisions. - clearId(dec.name); - - variables[dec.name.str] = dec; - } - - override: - - ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - assert(name.str != ""); - - Variable *vd; - if(variables.inList(name.str, vd)) - return ScopeLookup(vd.name, LType.Variable, vd.type, this, vd); - - return super.lookup(name, autoLoad); - } -} - -// A scope that can contain functions and variables -class FVScope : VarScope -{ - protected: - HashTable!(char[], Function*) functions; - - public: - this(Scope last, char[] name) - { super(last, name); } - - // Insert a function. - void insertFunc(Function* fd) - { - if(isClass) - { - // Are we overriding a function? - auto look = lookup(fd.name); - if(look.isFunc) - // We're overriding. Let fd know, and let it handle the - // details when it resolves. - fd.overrides = look.func; - else - // No matching function. Check that the name is available. - clearId(fd.name); - } - else - // Non-class functions can never override anything - clearId(fd.name); - - fd.index = functions.length; - - // Store the function definition - functions[fd.name.str] = fd; - } - - override: - ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - assert(name.str != ""); - - Function* fd; - - if(functions.inList(name.str, fd)) - return ScopeLookup(fd.name, LType.Function, fd.type, this, fd); - - // Let VarScope handle variables - return super.lookup(name, autoLoad); - } -} - -// Can contain types, functions and variables. 'Types' means structs, -// enums and other user-generated sub-types (may also include classes -// later.) - -// The TFV, FV and Var scopes might (probably will) be merged at some -// point. I'm just keeping them separate for clearity while the scope -// structure is being developed. -class TFVScope : FVScope -{ - private: - HashTable!(char[], StructType) structs; - HashTable!(char[], EnumType) enums; - - public: - this(Scope last, char[] name) - { super(last, name); } - - void insertStruct(StructDeclaration sd) - { - clearId(sd.name); - structs[sd.name.str] = sd.type; - } - - void insertEnum(EnumType sd) - { - clearId(sd.nameTok); - enums[sd.name] = sd; - } - - override: - ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - assert(name.str != ""); - - StructType sd; - EnumType ed; - Type tp; - - if(structs.inList(name.str, sd)) tp = sd; - else if(enums.inList(name.str, ed)) tp = ed; - - if(tp !is null) - return ScopeLookup(Token(tp.name, tp.loc), LType.Type, tp, this); - - // Pass it on to the parent - return super.lookup(name, autoLoad); - } -} - -// Lookup scope for enums. For simplicity we use the property system -// to handle enum members. -final class EnumScope : SimplePropertyScope -{ - this(EnumType _et) - { - super("EnumScope", GenericProperties.singleton); - et = _et; - - insert("value", "long", &enumVal); - inserts("length", "int", &enumLen); - inserts("first", et, &enumFirst); - inserts("last", et, &enumLast); - inserts("min", "long", &enumMin); - inserts("max", "long", &enumMax); - } - - void enumMin() - { tasm.push8(et.minVal); } - - void enumMax() - { tasm.push8(et.maxVal); } - - void enumFirst() - { tasm.push(et.entries[0].index); } - - void enumLast() - { tasm.push(et.entries[$-1].index); } - - void enumLen() - { tasm.push(cast(uint)et.entries.length); } - - void enumVal() - { tasm.getEnumValue(et); } - - EnumType et; - - override: - - // Intercept all the enum entry names when used as properties - Type getType(char[] name, Type oType) - { - // Is it one of the enum values? - auto ee = et.lookup(name); - if(ee !is null) - { - assert(et is oType || - et.getMeta() is oType); - return et; - } - - // Maybe one of the fields? - int fe = et.findField(name); - if(fe != -1) - return et.fields[fe].type; - - return super.getType(name, oType); - } - - void getValue(char[] name, Type oType) - { - auto ee = et.lookup(name); - if(ee !is null) - { - assert(et is oType || - et.getMeta() is oType); - tasm.push(ee.index); - return; - } - - int fe = et.findField(name); - if(fe != -1) - { - // We're getting a field from a variable. Eg. - // en.errorString. The enum index is on the stack, and - // getEnumValue supplies the enum type and the field - // index. The VM can find the correct field value from that. - tasm.getEnumValue(et, fe); - return; - } - - super.getValue(name, oType); - } - - bool hasProperty(char[] name) - { - auto ee = et.lookup(name); - if(ee !is null) return true; - if(et.findField(name) != -1) - return true; - return super.hasProperty(name); - } - - bool isStatic(char[] name, Type oType) - { - auto ee = et.lookup(name); - if(ee !is null) - { - assert(et is oType || - et.getMeta() is oType); - return true; - } - - // The fields are always non-static, they depend on the actual - // enum value supplied. - if(et.findField(name) != -1) - return false; - - return super.isStatic(name, oType); - } - - bool isLValue(char[] name, Type oType) - { - // We can't override anything in an enum - auto ee = et.lookup(name); - if(ee !is null) - { - assert(et is oType || - et.getMeta() is oType); - return false; - } - if(et.findField(name) != -1) - return false; - return super.isLValue(name, oType); - } -} - -// Scope for the interior of structs -final class StructScope : VarScope -{ - int offset; - - StructType str; - - this(Scope last, StructType t) - { - str = t; - assert(str !is null); - super(last, t.name); - } - - bool isStruct() { return true; } - - int addNewVar(int varSize) - { return (offset+=varSize) - varSize; } - - // Define it only here since we don't need it anywhere else. - Scope getParent() { return parent; } -} - -// A class scope. In addition to variables, and functions, classes can -// contain states and they keep track of the data segment size. -final class ClassScope : TFVScope -{ - private: - // The class information for this class. - MonsterClass cls; - - HashTable!(char[], State*) states; - int dataSize; // Data segment size for this class - - public: - - this(Scope last, MonsterClass cl) - { - cls = cl; - super(last, cls.name.str); - - // Make sure the class has a valid package - assert(cl.pack !is null); - - // Import the package into this scope - registerImport(cl.pack); - } - - bool isClass() { return true; } - MonsterClass getClass() { return cls; } - - // Add a variable to the data segment, returns the offset. - int addNewVar(int varSize) - { - int tmp = dataSize; - - dataSize += varSize; - - return tmp; - } - - override ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - assert(name.str != ""); - - State* sd; - - if(states.inList(name.str, sd)) - return ScopeLookup(sd.name, LType.State, null, this, sd); - - // Check the property list - auto sl = ClassProperties.singleton.lookup(name); - if(sl.isProperty) - return sl; - - // Let the parent handle everything else - return super.lookup(name, autoLoad); - } - - // Get total data segment size - int getDataSize() { return dataSize; } - - // Insert a state - void insertState(State* st) - { - clearId(st.name); - - st.index = states.length; - states[st.name.str] = st; - } -} - -// A scope that keeps track of the stack -abstract class StackScope : VarScope -{ - private: - int locals; // The number of local variables declared in this - // scope. These must be pop'ed when the scope exits. - int sumLocals;// Current position of the stack relative to the stack - // pointer. This is set to zero at the start of the - // function, and increased for each local variable - // that is added. The difference between sumLocals and - // locals is that sumLocals counts ALL local variables - // declared in the current function (above the current - // point), while locals only counts THIS scope. - - int expStack; // Expression stack. Only eeps track of intra- - // expression stack values, and must end up at zero - // after each statement. - - public: - this(Scope last, char[] name) - { - super(last, name); - - // Local variable position is inherited - assert(!isRoot()); - sumLocals = parent.getTotLocals; - } - - // Allocate a local variable on the stack, and return the offset. - // The parameter gives the size of the requested variable in ints (4 - // bytes.) - int addNewVar(int varSize) - { - assert(expStack == 0); - - locals += varSize; - - int tmp = sumLocals; - sumLocals += varSize; - - return tmp; - } - - // Reset the stack counters. Used from the console. - void reset() - { - locals = 0; - sumLocals = 0; - } - - /* - void push(int i) { expStack += i; } - void push(Type t) { push(t.getSize); } - void pop(int i) { expStack -= i; } - void pop(Type t) { pop(t.getSize); } - */ - - // Get the number of local variables in the current scope. In - // reality it gives the number of ints. A variable 8 bytes long will - // count as two variables. - int getLocals() { return locals; } - - // Get the total number of local variables for this function. Used - // in return statements and by other jumps that might span several - // blocks (break and continue.) - int getTotLocals() { return sumLocals; } - - // Get intra-expression stack (not used yet) - int getExpStack() { return expStack; } - - // Get total stack position, including expression stack values. This - // is used by the array lenght symbol $ to find the array index. - int getPos() { return getTotLocals() + getExpStack(); } -} - -// Scope used for the inside of functions -class FuncScope : StackScope -{ - // Function definition, for function scopes - private: - Function *fnc; - - public: - this(Scope last, Function *fd) - { - super(last, fd.name.str); - fnc = fd; - } - - override: - - bool isFunc() { return true; } - Function *getFunction() { return fnc; } -} - -class CodeScope : StackScope -{ - this(Scope last, CodeBlock cb) - { - char[] name = "codeblock"; - - assert(cb !is null); - if(cb.isState) - name = "stateblock"; - - super(last, name); - } - - this(Scope last, char[] name) { super(last, name) ;} - - bool isCode() { return true; } - - LabelStatement getBreak(char[] name = "") { return parent.getBreak(name); } - LabelStatement getContinue(char[] name = "") { return parent.getContinue(name); } -} - -// A special scope used inside arrays that allow the $ token to be -// used as a shorthand for the array length. -class ArrayScope : StackScope -{ - private ArrayOperator arrOp; - - this(Scope last, ArrayOperator op) - { - super(last, "arrayscope"); - arrOp = op; - } - - bool isArray() { return true; } - ArrayOperator getArray() { return arrOp; } -} - -// Base class for scopes that have properties. The instances of this -// scope are defined in properties.d -abstract class PropertyScope : Scope -{ - this(char[] n, PropertyScope last = null) { super(last, n); } - - // Override these in base classes. - - Type getType(char[] name, Type oType); - void getValue(char[] name, Type oType); - bool hasProperty(char[] name); - bool isStatic(char[] name, Type oType); - - // Most properties are read-only, but override these for exceptions. - bool isLValue(char[] name, Type oType) { return false; } - void setValue(char[] name, Type oType) { assert(0); } - - override: - final: - bool isProperty() { return true; } - bool allowRoot() { return true; } - - ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - // Does this scope contain the property? - if(hasProperty(name.str)) - return ScopeLookup(name, LType.Property, null, this); - - // Check the parent scope - return super.lookup(name, autoLoad); - } -} - -// Scope inside of loops. Handles break and continue, loop labels, and -// some stack stuff. -class LoopScope : CodeScope -{ - private: - LabelStatement breakLabel, continueLabel; - Token loopName; // Loop label name. - int loopStack = -1; // Stack position of the array index. Only used in - // foreach loops. - bool isForeach; // Redundant variable used for integrity checking only - - // Set the label and set up a nice name - this(Scope last, char[] name, Token label) - { - // Include the label name in the scope name - if(label.str != "") - name ~= ":" ~ label.str; - - super(last, name); - - if(label.str != "") - { - // This loop has a label, so set it up - clearId(label); - loopName = label; - } - } - - public: - - this(Scope last, ForStatement fs) - { this(last, "for-loop", fs.labelName); } - this(Scope last, ForeachStatement fs) - { - this(last, "foreach-loop", fs.labelName); - isForeach = true; - } - this(Scope last, DoWhileStatement fs) - { - this(last, "do-while-loop", fs.labelName); - stackPoint(); - } - this(Scope last, WhileStatement fs) - { - this(last, "while-loop", fs.labelName); - stackPoint(); - } - - bool isLoop() { return true; } - - // Called when the stack is set up to the level of the loop - // interior, after loop variables and such are declared. This - // function should only be called for for-loops and foreach-loops, - // and will make sure that loop variables are not popped by break - // and continue statements. - void stackPoint() - { - assert(breakLabel is null && continueLabel is null && loopStack == -1, - "do not call stackPoint multiple times"); - - if(isForeach) loopStack = getTotLocals() - 1; - breakLabel = new LabelStatement(getTotLocals()); - continueLabel = new LabelStatement(getTotLocals()); - } - - override ScopeLookup lookup(Token name, bool autoLoad=false) - { - static if(traceLookups) - writefln("Lookup %s in %s (line %s)", name, this, __LINE__); - - assert(name.str != ""); - - // Check for loop labels - if(loopName.str == name.str) - return ScopeLookup(loopName, LType.LoopLabel, null, this); - - return super.lookup(name, autoLoad); - } - - // Get the break or continue label for the given named loop, or the - // innermost loop if name is empty or omitted. TODO: Might fold - // these into lookup as well. For non-named labels we could use - // __closest_loop__ or something like that internally. - LabelStatement getBreak(char[] name = "") - { - if(name == "" || name == loopName.str) - return breakLabel; - - return parent.getBreak(name); - } - - LabelStatement getContinue(char[] name = "") - { - if(name == "" || name == loopName.str) - return continueLabel; - - return parent.getContinue(name); - } - - int getLoopStack() - { - assert(loopStack != -1 && isForeach, - "getLoopStack called for non-foreach scope"); - return loopStack; - } -} diff --git a/old_d_version/monster/compiler/statement.d b/old_d_version/monster/compiler/statement.d deleted file mode 100644 index 8f71c7cf5..000000000 --- a/old_d_version/monster/compiler/statement.d +++ /dev/null @@ -1,1607 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (statement.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.statement; - -import std.string; -import std.stdio; - -import monster.compiler.tokenizer; -import monster.compiler.expression; -import monster.compiler.scopes; -import monster.compiler.types; -import monster.compiler.block; -import monster.compiler.variables; -import monster.compiler.states; -import monster.compiler.operators; -import monster.compiler.functions; -import monster.compiler.assembler; - -import monster.vm.error; -import monster.vm.mclass; -import monster.vm.vm; - -import monster.options; - -alias Statement[] StateArray; - -abstract class Statement : Block -{ - // Generate byte code from this statement - void compile(); -} - -/* Handles: - import type1, type2, ...; // module scope - import exp1, exp2, ...; // local scope (not implemented yet) - - Also handles: - with(exp1, etc) statement; - which is equivalent to: - { - import exp1, etc; - statement; - } -*/ -class ImportStatement : Statement -{ - Type typeList[]; - - MonsterClass mc; - - bool isLocal; - Statement stm; - - this(bool local = false) { isLocal = local; } - - // TODO: Things like this should be completely unnecessary - we'll - // fix a simpler and more concise parser-system later. - static bool canParse(TokenArray toks) - { return - isNext(toks, TT.Import) || - isNext(toks, TT.With); - } - - void parse(ref TokenArray toks) - { - void getList() - { - typeList = [Type.identify(toks)]; - while(isNext(toks, TT.Comma)) - typeList ~= Type.identify(toks); - } - - if(isNext(toks, TT.Import, loc)) - { - // form: import ImportList; - getList(); - reqSep(toks); - } - else if(isNext(toks, TT.With, loc)) - { - // form: with(ImportList) statement - if(!isLocal) - fail("with(...) not allowed in class scopes", loc); - - reqNext(toks, TT.LeftParen); - getList(); - reqNext(toks, TT.RightParen); - stm = CodeBlock.identify(toks, false); - } - else assert(0); - } - - void resolve(Scope sc) - { - // If a statement is present (ie. if this is a with-statement), - // wrap the imports in a code scope so they become temporary. - if(stm !is null) - sc = new CodeScope(sc, "with-scope"); - - // Add the imports to the scope - foreach(type; typeList) - { - type.resolve(sc); - - if(type.isReplacer) - type = type.getBase(); - - if(type.isObject) - { - auto t = cast(ObjectType)type; - assert(t !is null); - mc = t.getClass(type.loc); - - sc.registerImport(mc); - } - else if(type.isPackage) - { - auto t = cast(PackageType)type; - assert(t !is null); - auto psc = t.sc; - - sc.registerImport(psc); - } - else - fail("Can only import from classes and packages", type.loc); - } - - // Resolve the statement if present - if(stm !is null) - stm.resolve(sc); - } - - void compile() - { - if(stm !is null) - stm.compile(); - } -} - -// Destination for a goto statement, on the form 'label:'. This -// statement is actually a bit complicated, since it must handle jumps -// occuring both above and below the label. Seeing as the assembler -// can only associate one jump with each label for jumps occuring -// before the label, we must insert multiple labels into the assembler -// if there are multiple such jumps / gotos. Therefore we must keep a -// list of jumps occuring before the label itself has been -// compiled. - -// LabelStatements are also used for internal labels. They are -// inserted by loops to handle break and continue, since these have -// similar forward reference problems. -class LabelStatement : Statement -{ - // Jump indices accumulated before compile() is called - int[] preIndex; - - // This array is empty when the label is inserted with - // insertLabel. The label might be registered into the labels list - // before this happens, though, if a goto statement looks up a label - // that is not yet registered. In that case, the list below will - // contain all the goto statements that inserted this label. - GotoStatement gotos[]; - - // Label index used after compile() is called - int postIndex; - - // Has compiled() been called yet? - bool isCompiled = false; - - // Permanent data for state labels. - StateLabel *lb; - - // Stack level where this label is defined. Should be zero for state - // labels, but might be nonzero when LabelStatements are used - // internally in loops. - int stacklevel; - - // Normal creation, we are parsed. - this() - { - lb = new StateLabel; - lb.ls = this; - } - - // Forward reference. A goto statement requested a label by this - // name, but none has been found yet. - this(GotoStatement gs) - { - this(); - - lb.name = gs.labelName; - gotos ~= gs; - } - - // Used for internal levels (like continue and break labels). The - // parameter gives the stack level (within the function) at the - // label location, corresponds to sc.getTotLocals(). Internal labels - // are neither parsed nor resolved, just compiled. - this(int stack) - { - this(); - - stacklevel = stack; - lb.index = -1; - } - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.Identifier) && isNext(toks, TT.Colon); - } - - void parse(ref TokenArray toks) - { - // canParse has already checked the token types, all we need to - // do is to fetch the name. - reqNext(toks, TT.Identifier, lb.name); - reqNext(toks, TT.Colon); - - loc = lb.name.loc; - } - - void resolve(Scope sc) - { - if(!sc.isStateCode) - fail("Labels are only allowed in state code", lb.name.loc); - if(sc.isInLoop) - fail("Labels are not allowed in loops", lb.name.loc); - - // Do the magic. Just tell the scope that we exist and let it - // handle the rest. - auto scp = sc.getState().sc; - assert(scp !is null); - scp.insertLabel(lb); - - stacklevel = sc.getTotLocals(); - assert(stacklevel == 0); - } - - // Compile the label code. Note that when LabelStatement are used - // internally (such as in for loops to handle break and continue), - // then parse() and resolve() are never called, only compile(). - void compile() - { - // Since labels are also used internally, there's a chance that - // we end up calling this function several times by mistake. - assert(!isCompiled, "LabelStatement.compile() was called twice."); - - setLine(); - - // We must insert one label for each of the jumps that occured - // before us - foreach(int ind; preIndex) - tasm.labelNum(ind, lb.index); - preIndex = null; - - // Now get a label index for all the jumps that occur after - // us. We only need one. - postIndex = tasm.labelNum(lb.index); - - // Make sure jump() knows what it is doing. - isCompiled = true; - } - - // This is called from GotoStatement.compile() and tells us to - // insert a jump that jumps to this label. The parameter gives the - // stack level at the jump point. - void jump(int stack) - { - // Before jumping, we have set the stack to the correct level - // for where the label is. - stack -= stacklevel; - assert(stack >= 0, "Negative stack correction on jump"); - if(stack) tasm.pop(stack); - - if(isCompiled) - // We have already compiled this label, so we have an index to - // jump to. - tasm.jump(postIndex); - else - // We must get a jump index that we can use later. - preIndex ~= tasm.jump(); - } -} - -// Used by GotoStatement and StateStatement, since both refer to a -// labels. -interface LabelUser -{ - void setLabel(LabelStatement ls); -} - -// goto label; Only allowed in state code (for now) -class GotoStatement : Statement, LabelUser -{ - // Set in resolve(). This is the label we are jumping to. - LabelStatement label; - - Token labelName; - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.Goto); - } - - // Set the label - void setLabel(LabelStatement ls) - { - label = ls; - if(label is null) - fail("Cannot find label '" ~ labelName.str ~ "'", labelName.loc); - } - - void parse(ref TokenArray toks) - { - reqNext(toks, TT.Goto, loc); - - // Read the label name - if(!isNext(toks, TT.Identifier, labelName)) - fail("goto expected label identifier", toks); - - reqSep(toks); - } - - void resolve(Scope sc) - { - if(!sc.isStateCode) - fail("Goto statements are only allowed in state code", labelName.loc); - - // Tell the state scope that we are trying to goto. The system - // will will set our label variable for us, either straight away - // (if the label is known) or later (if the label is a forward - // reference.) - sc.getState().registerGoto(labelName.str, this); - } - - void compile() - { - assert(label !is null); - - // We let LabelStatement do all the dirty work. - label.jump(0); - } -} - -// Handles continue and break, with or without a label. -class ContinueBreakStatement : Statement -{ - Token labelName; - - // Label we are jumping to. It is obtained from the scope. The label - // we are pointing to will be a break or continue label created - // internally in the loop coresponding to labelName. If labelName is - // empty then the scope will use the innermost loop. - LabelStatement label; - - // Is this a break or a continue? - bool isBreak; - - // Used for error messages. Contains either "break" or "continue". - char[] errName; - - // Stack level (sc.getTotLocals) at the jump point. - int stacklevel; - - static bool canParse(TokenArray toks) - { return isNext(toks, TT.Continue) || isNext(toks, TT.Break); } - - void parse(ref TokenArray toks) - { - if(isNext(toks, TT.Continue, loc)) isBreak = false; - else if(isNext(toks, TT.Break, loc)) isBreak = true; - else assert(0, "Internal error"); - - if(isBreak) errName = "break"; - else errName = "continue"; - - if(!isSep(toks)) - { - if(!isNext(toks, TT.Identifier, labelName)) - fail(errName ~ " expected ; or label", toks); - - reqSep(toks); - } - } - - void resolve(Scope sc) - { - if(!sc.isInLoop()) - fail("Cannot use " ~ errName ~ " outside a loop", loc); - - // Get the correct label to jump to - if(isBreak) label = sc.getBreak(labelName.str); - else label = sc.getContinue(labelName.str); - - // And the stack level at the jump point - stacklevel = sc.getTotLocals(); - - if(label is null) - { - assert(labelName.str != "", "Internal error"); - fail("Loop label '" ~ labelName.str ~ "' not found", labelName.loc); - } - } - - void compile() - { - // Our nice LabelStatement implementation does everything for - // us. - assert(label !is null); - label.jump(stacklevel); - } -} - -// do-while loop (might also support do-until loops in the future) -// do statement while (expression) -// do : label statement while (expression) -class DoWhileStatement : Statement -{ - Expression condition; - Statement block; - - Token labelName; - - LoopScope sc; - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.Do); - } - - void parse(ref TokenArray toks) - { - if(!isNext(toks, TT.Do, loc)) - assert(0, "Internal error"); - - // Is there a loop label? - if(isNext(toks, TT.Colon)) - if(!isNext(toks, TT.Identifier, labelName)) - fail("do statement expected label identifier", toks); - - // Read the statement ('false' to disallow variable - // declarations.) - block = CodeBlock.identify(toks, false); - - if(!isNext(toks, TT.While)) - fail("do statement expected while(...)", toks); - - if(!isNext(toks, TT.LeftParen)) - fail("while statement expected '('", toks); - - // Parse the conditional expression - condition = Expression.identify(toks); - - if(!isNext(toks, TT.RightParen)) - fail("while statement expected ')'", toks); - - // Allow an optional semicolon after the while() - isNext(toks, TT.Semicolon); - } - - void resolve(Scope last) - { - sc = new LoopScope(last, this); - - // Resolve all parts - condition.resolve(sc); - - if(!condition.type.isBool) - fail("while condition " ~ condition.toString ~ " must be a bool, not " - ~condition.type.toString, condition.loc); - - block.resolve(sc); - } - - void compile() - { - LabelStatement - cont = sc.getContinue(), - brk = sc.getBreak(); - - setLine(); - - int label = tasm.label(); - - // Execute the block - block.compile(); - - // Continue label goes here, right after the code block - cont.compile(); - - // Push the conditionel expression - condition.eval(); - - setLine(); - - // Jump if the value is non-zero. - tasm.jumpnz(label); - - // Break label go here, after the entire loop - brk.compile(); - } -} - -// while loop: -// while (expression) statement -// while (expression) : label statement -class WhileStatement : Statement -{ - Expression condition; - Statement block; - - Token labelName; - - LoopScope sc; - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.While); - } - - void parse(ref TokenArray toks) - { - if(!isNext(toks, TT.While, loc)) - assert(0); - - if(!isNext(toks, TT.LeftParen)) - fail("while statement expected '('", toks); - - // Parse the conditional expression - condition = Expression.identify(toks); - - if(!isNext(toks, TT.RightParen)) - fail("while statement expected ')'", toks); - - // Is there a loop label? - if(isNext(toks, TT.Colon)) - if(!isNext(toks, TT.Identifier, labelName)) - fail("while statement expected label identifier", toks); - - // Read the statement ('false' to disallow variable - // declarations.) - block = CodeBlock.identify(toks, false); - } - - void resolve(Scope sco) - { - sc = new LoopScope(sco, this); - - // Resolve all parts - condition.resolve(sc); - - if(!condition.type.isBool) - fail("while condition " ~ condition.toString ~ " must be a bool, not " - ~condition.typeString, condition.loc); - - block.resolve(sc); - } - - void compile() - { - /* To avoid the extra jump back and forth at the end, the coding - here is similar to - if(condition) - { - do - block - while(condition); - } - */ - - LabelStatement - cont = sc.getContinue(), - brk = sc.getBreak(); - - // Evaluate the condition for the first iteration - condition.eval(); - - setLine(); - - // Skip the entire loop if the condition is not met - int outer = tasm.jumpz(); - - // Jump here to repeat the loop - int inner = tasm.label(); - - // Execute the block - block.compile(); - - // Continue label - cont.compile(); - - // Push the conditionel expression - condition.eval(); - - setLine(); - - // Repeat the loop if the value is non-zero. - tasm.jumpnz(inner); - - tasm.label(outer); - - // Break label - brk.compile; - } -} - -/* foreach loop: - foreach(indexDeclaration; arrayExpr) statement - foreach(indexDeclaration; arrayExpr) : label statement - - arrayExpr - any expression that evaluates to an array. - - indexDeclaration - either: - - type var - - or - - int index, type var - - where 'type' is the base type of arrayExpr. Either or both types - may be omitted since they can always be infered by the - compiler. For example: - - foreach(i,v; "hello") // i is type int, v is type char - - Using 'ref' in the declaration of the value variable means that - changes to it will affect the original array. - - Use foreach_reverse instead of foreach to iterate the array in the - opposite direction. - */ -class ForeachStatement : Statement -{ - Expression arrayExp; - VarDeclaration index, value; - Statement block; - - LoopScope sc; - Token labelName; - bool isReverse; // Traverse in reverse order - bool isRef; // Set if the value variable is a reference (alters - // original content) - - // Set if we are traversing the objects of a class - Token className; - bool isClass = false; - MonsterClass clsInfo; - - char[] toString() - { - char[] res = "foreach"; - if(isReverse) res ~= "_reverse"; - res ~= "("; - - if(index !is null) res ~= index.toString ~ ", "; - res ~= value.toString ~ "; " ~ arrayExp.toString ~ ")"; - - if(labelName.str != "") res ~= " : " ~ labelName.str; - - res ~= "\n" ~ block.toString; - - return res; - } - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.Foreach) || isNext(toks, TT.ForeachRev); - } - - void parse(ref TokenArray toks) - { - if(isNext(toks, TT.Foreach, loc)) isReverse = false; - else if(isNext(toks, TT.ForeachRev, loc)) isReverse = true; - else assert(0); - - if(!isNext(toks, TT.LeftParen)) - fail("foreach statement expected '('", toks); - - // Read the first variable declaration (TODO: allow - // 'ref'). Assume it is the value. - value = new VarDeclaration(); - value.allowRef = true; - value.allowNoType = true; - value.parse(toks); - - if(value.init !is null) - fail("Variable initializer is not allowed in foreach", loc); - - if(isNext(toks, TT.Comma)) // Is there another one? - { - // The previous variable was the index, not the value - index = value; - - // Sanity check the index variable. TODO: At some point it - // might be possible to mark it as constant. - if(index.var.isRef) - fail("Index cannot be a reference variable", loc); - - value = new VarDeclaration(); - value.allowRef = true; - value.allowNoType = true; - value.parse(toks); - if(value.init !is null) - fail("Variable initializer is not allowed in foreach", loc); - } - - isRef = value.var.isRef; - - if(!isNext(toks, TT.Semicolon)) - fail("foreach expected ;", toks); - - // Is the expression a class? - if(isNext(toks, TT.Class)) - { - // Get the class name - if(!isNext(toks, TT.Identifier, className)) - fail("foreach expected class name after 'class'"); - isClass = true; - } - else - { - // Get the array - arrayExp = Expression.identify(toks); - } - - if(!isNext(toks, TT.RightParen)) - fail("foreach statement expected ')'", toks); - - // Is there a loop label? - if(isNext(toks, TT.Colon)) - if(!isNext(toks, TT.Identifier, labelName)) - fail("foreach statement expected label identifier", toks); - - // Read the statement (the 'false' parameter is to disallow - // variable declarations.) - block = CodeBlock.identify(toks, false); - } - - void resolve(Scope sco) - { - if(sco.isStateCode) - fail("Foreach loops are currently not allowed in state code."); - - sc = new LoopScope(sco, this); - - if(isClass) - { - // Class loops - clsInfo = vm.load(className.str); - assert(clsInfo !is null); - clsInfo.requireScope(); - - if(index !is null) - fail("Index not allowed in class iteration"); - - // Set the value variable type if it is missing - if(value.var.type is null) - value.var.type = clsInfo.objType; - - // Resolve and allocate stack for the value variable - value.resolve(sc); - - // Check that the type is correct - if(value.var.type.toString != className.str) - fail("Loop variable must be of type " ~ className.str ~ ", not " - ~ value.var.type.toString, value.var.name.loc); - - // Reference variables are not allowed - if(value.var.isRef) - fail("Reference variable not allowed in class iteration."); - - // Reverse is not allowed - if(isReverse) - fail("Cannot traverse class instances in reverse order."); - } - else - { - // This is for array loops - - // Resolve the index, if present - if(index !is null) - { - if(index.var.type is null) - index.var.type = BasicType.getInt; - - index.resolve(sc); - - if(!index.var.type.isInt) - fail("foreach index type must be an int, not " ~ - index.var.type.toString, index.var.name.loc); - } - // If not, allocate a stack value for it anyway. - else sc.addNewVar(1); - - // Check that the array is in fact an array - arrayExp.resolve(sc); - if(!arrayExp.type.isArray) - fail("foreach expected an array, not " ~ arrayExp.toString, arrayExp.loc); - - if(value.var.type is null) - value.var.type = arrayExp.type.getBase(); - - // This also allocates a stack value - value.resolve(sc); - - if(value.var.type != arrayExp.type.getBase()) - fail("foreach iteration variable must be of type " ~ - arrayExp.type.getBase().toString() ~ ", not " ~ - value.var.type.toString(), value.var.name.loc); - } - - // Tell the scope that the iterator index is on the stack, like - // a local variable. This is not the same as the index variable - // above - the iterator index is an internal variable used by - // the system for storing the iterator state. - sc.addNewVar(1); - - // This defines the stack level of the loop internals. Break and - // continue labels are set up so that they return to this level - // when invoked. - sc.stackPoint(); - - // Our stack now looks like this (last pushed at the top): - - // For arrays: - // Array Index / iterator index - // Value variable - // Index variable - - // For classes: - // Iterator index - // Object index variable - - // It is important that we have called sc.addNewLocalVar() for - // each of these, to let the scope know about our stack - // usage. Otherwise local variables declared inside the loop - // body will not work correctly. This is either done explicitly - // or through VarDecl.resolve(). - - block.resolve(sc); - } - - void compile() - { - // Get loop labels. These are set up after the loop variables - // and the array index was declared, and their stack levels - // reflect that. They must only be compiled while the variables - // are still on the stack. - LabelStatement - cont = sc.getContinue(), - brk = sc.getBreak(); - - setLine; - - // First push the index, in case of array iteration - if(!isClass) - { - // Push the variables on the stack - if(index !is null) index.compile(); - else tasm.push(0); // Create an unused int on the stack - } - - // Then push the value - value.compile(); - - // Create iteration reference. - if(isClass) - { - // Classes. Create a class iterator from the given class - // number. - tasm.createClassIterator(clsInfo.getIndex()); - } - else - { - // Arrays. First, evaluate the array expression to get the - // array index on the stack. - arrayExp.eval(); - - // This will replace the array index with an "iterator" - // index. The VM will handle the iteration data from there, - // it is "aware" of the index and value on the stack as - // well. - tasm.createArrayIterator(isReverse, isRef); - } - - setLine(); - - // The rest is the same for all iterators. - - // Skip the loop the array is empty - int outer = tasm.jumpz(); - - // Jump here to repeat the loop - int inner = tasm.label(); - - // Execute the block - block.compile(); - - // Continue statements bring us here - cont.compile(); - - setLine; - - // Go to next iteration. Leaves the iteration index and - // variables on the stack. Pushes true if we should continue, or - // false otherwise. The iterator reference is destroyed when the - // last iteration is done. - tasm.iterateNext(); - - // Repeat the loop if the value is non-zero. - tasm.jumpnz(inner); - int outer2 = tasm.jump(); - - // Break statements get us here. We have to finish the last - // iteration step (copy ref values and delete the - // iterator). This code is NOT guaranteed to be run. If we - // break/continue to an outer loop, or return from the function, - // this will not be run and the iterator never destroyed. This - // is not a big problem though, since the iterator list is - // cleared by the garbage collector. - brk.compile(); - - setLine; - - tasm.iterateBreak(); - - tasm.label(outer); - tasm.label(outer2); - - // Undo the local scope - tasm.pop(sc.getLocals); - } -} - - -/* for loop: - for(initExpr; condExpr; iterExpr) statement - for(initExpr; condExpr; iterExpr) : label statement - - initExpr is either an expression (of any type), a variable - declaration (scoped only for the interior of the loop), or empty - - condExpr is an expression of type bool, or empty - - iterExpr is an expression of any type, or empty - - An empty condExpr is treated as true. The label, if specified, may - be used with break and continue statements inside the loop. -*/ -class ForStatement : Statement -{ - ExprStatement init, iter; - Expression condition; - VarDeclStatement varDec; - - Token labelName; - - Statement block; - - LoopScope sc; // Scope for this loop - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.For); - } - - void parse(ref TokenArray toks) - { - reqNext(toks, TT.For, loc); - reqNext(toks, TT.LeftParen); - - // Check if the init as a variable declaration, if so then parse - // it as a statement (since that takes care of multiple - // variables as well.) - if(VarDeclStatement.canParse(toks)) - { - varDec = new VarDeclStatement(true); - varDec.parse(toks); // This also kills the trailing ; - } - // Is it an empty init statement? - else if(!isNext(toks, TT.Semicolon)) - { - // If not, then assume it's an expression - // statement. (Expression statements also handle - // assignments.) - init = new ExprStatement; - init.term = false; - init.parse(toks); - if(!isNext(toks, TT.Semicolon)) - fail("initialization expression " ~ init.toString() ~ - " must be followed by a ;", toks); - } - - // Phew! Now read the conditional expression, if there is one - if(!isNext(toks, TT.Semicolon)) - { - condition = Expression.identify(toks); - if(!isNext(toks, TT.Semicolon)) - fail("conditional expression " ~ condition.toString() ~ - " must be followed by a ;", toks); - } - - // Finally the last expression statement - if(!isNext(toks, TT.RightParen)) - { - iter = new ExprStatement; - iter.term = false; - iter.parse(toks); - reqNext(toks, TT.RightParen); - } - - // Is there a loop label? - if(isNext(toks, TT.Colon)) - if(!isNext(toks, TT.Identifier, labelName)) - fail("for statement expected label identifier", toks); - - // Read the statement (the 'false' parameter is to disallow - // variable declarations.) - block = CodeBlock.identify(toks, false); - } - - void resolve(Scope sco) - { - sc = new LoopScope(sco, this); - - // Resolve all parts - if(init !is null) - { - init.resolve(sc); - assert(varDec is null); - } - if(varDec !is null) - { - varDec.resolve(sc); - assert(init is null); - } - - // This affects break and continue, making sure they set the - // stack properly when jumping. - sc.stackPoint(); - - if(condition !is null) - { - condition.resolve(sc); - if(!condition.type.isBool) - fail("for condition " ~ condition.toString ~ " must be a bool, not " - ~condition.type.toString, condition.loc); - } - if(iter !is null) iter.resolve(sc); - - block.resolve(sc); - } - - void compile() - { - // Compiled in a similar way as a while loop, with an outer - // check for the initial state of the condition that is separate - // from the check after each iteration. - - // Get loop labels. Remember that these are set up after the - // loop variable was declared (if any), and their stack levels - // reflect that. They must only be compiled while the variable - // is still on the stack. - LabelStatement - cont = sc.getContinue(), - brk = sc.getBreak(); - - // Push any local variables on the stack, or do initialization. - if(varDec !is null) varDec.compile(); - else if(init !is null) - init.compile(); - - int outer; - - // Evaluate the condition for the first iteration - if(condition !is null) - { - condition.eval(); - // Skip the entire loop if the condition is not met - outer = tasm.jumpz(); - } - else - // A missing condition is always met, do nothing - outer = -1; - - // Jump here to repeat the loop - int inner = tasm.label(); - - - // Execute the block - block.compile(); - - // Continue statements bring us here - cont.compile(); - - // Do the iteration step, if any - if(iter !is null) iter.compile(); - - // Push the conditionel expression, or assume it's true if - // missing. - if(condition !is null) - { - condition.eval(); - // Repeat the loop if the value is non-zero. - tasm.jumpnz(inner); - } - else tasm.jump(inner); - - if(outer != -1) tasm.label(outer); - - // Break statements get us here - brk.compile(); - - // Undo the local scope - tasm.pop(sc.getLocals); - } -} - -// Handles state = statename; Might be removed if states become a real -// type. Supports state = null; as well, to set the null state. You -// may specify a label to jump to, as state = statename.label; -class StateStatement : Statement, LabelUser -{ - Token stateName, labelName; - - State* stt; - LabelStatement label; - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.State); - } - - void parse(ref TokenArray toks) - { - if(!isNext(toks, TT.State, loc)) - assert(0, "Internal error in StateStatement"); - - if(!isNext(toks, TT.Equals)) - fail("state statement expected =", toks); - - if(!isNext(toks, TT.Identifier, stateName) && - !isNext(toks, TT.Null) ) - fail("state name identifier or null expected", toks); - - // Check if a label is specified - if(stateName.type == TT.Identifier) - if(isNext(toks, TT.Dot)) - if(!isNext(toks, TT.Identifier, labelName)) - fail("state label expected after .", toks); - - reqSep(toks); - } - - // Set the label to jump to. This is called from the state - // declaration. The argument is null if the label was never - // resolved. - void setLabel(LabelStatement ls) - { - if(ls is null) - fail("Undefined label '" ~ labelName.str ~ "'", labelName.loc); - label = ls; - } - - void resolve(Scope sc) - { - // Check the state name. - if(stateName.type == TT.Identifier) - { - auto sl = sc.lookup(stateName); - if(!sl.isState) - fail("Undefined state " ~ stateName.str, stateName.loc); - - stt = sl.state; - - // If a label is specified, tell the state that we are - // asking for a label. The function will set label for us, - // either now or when the label is resolved. The label will - // in any case always be set before compile() is called. - if(labelName.str != "") - stt.registerGoto(labelName.str, this); - } - else - { - assert(labelName.str == ""); - stt = null; // Signifies the empty state - label = null; // And no label - } - } - - void compile() - { - setLine(); - - // If there is a label, make sure it has been resolved. - assert(labelName.str == "" || label !is null); - - if(stt is null) - { - tasm.setState(-1, -1, 0); - assert(label is null); - return; - } - - int cindex = stt.owner.getTreeIndex(); - - if(label is null) - tasm.setState(stt.index, -1, cindex); - else - tasm.setState(stt.index, label.lb.index, cindex); - } -} - -// Handles if(expr) {} and if(expr) {} else {}. Expr must be of type -// bool. (We will not support variable declarations in the expression, -// since the strict bool requirement would make it useless anyway.) -class IfStatement : Statement -{ - Expression condition; - Statement block, elseBlock; - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.If); - } - - void parse(ref TokenArray toks) - { - if(!isNext(toks, TT.If, loc)) - assert(0, "Internal error in IfStatement"); - - if(!isNext(toks, TT.LeftParen)) - fail("if statement expected '('", toks); - - // Parse conditional expression. - condition = Expression.identify(toks); - - if(!isNext(toks, TT.RightParen)) - fail("if statement expected ')'", toks); - - // Parse the first statement, but disallow variable - // declarations. (if(b) int i; is not allowed, but if(b) {int - // i;} works ok) - block = CodeBlock.identify(toks, false); - - // We are either done now, or there is an 'else' following the - // first statement. - if(isNext(toks, TT.Else)) - elseBlock = CodeBlock.identify(toks, false); - } - - void resolve(Scope sc) - { - // Resolve all parts - condition.resolve(sc); - - if(!condition.type.isBool && - !condition.type.isArray && - !condition.type.isObject) - fail("if condition " ~ condition.toString ~ " must be a bool, not " - ~condition.type.toString, condition.loc); - - block.resolve(sc); - if(elseBlock !is null) elseBlock.resolve(sc); - } - - void compile() - { - bool hasElse = elseBlock !is null; - - // Push the conditionel expression - condition.eval(); - - // For arrays, get the length - if(condition.type.isArray) - tasm.getArrayLength(); - - // TODO: For objects, the null reference will automatically - // evaluate to false. However, we should make a "isValid" - // instruction, both to check if the object is dead (deleted) - // and to guard against any future changes to the object index - // type. - assert(condition.type.getSize == 1); - - // Jump if the value is zero. Get a label reference. - int label = tasm.jumpz(); - - // Execute the first block - block.compile(); - - if(hasElse) - { - // If the condition was true, we must skip the else block - int l2 = tasm.jump(); - // Otherwise, we jump here to execute the else block - tasm.label(label); - elseBlock.compile(); - tasm.label(l2); - } - else tasm.label(label); - } -} - -// Return statement - on the form return; or return expr; -class ReturnStatement : Statement -{ - Expression exp; - Function *fn; - - // Number of local variables to unwind from the stack, and the - // number of parameters. (Counts number of ints / stack values, not - // actual number of variables.) - int locals; - int params; - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.Return); - } - - void parse(ref TokenArray toks) - { - if(!isNext(toks, TT.Return, loc)) - assert(0, "Internal error in ReturnStatement"); - - if(!isSep(toks)) - { - exp = Expression.identify(toks); - - reqSep(toks); - } - } - - char[] toString() - { - if(exp is null) return "ReturnStatement;"; - return "ReturnStatement: " ~ exp.toString; - } - - void resolve(Scope sc) - { - /* - // Not allowed in state code. - if(sc.isStateCode) - fail("return not allowed in state code", loc); - */ - // Return in state code is handled as a special case - if(sc.isStateCode) - { - assert(!sc.isInFunc); - if(exp !is null) - fail("Cannot return an expression in state code", loc); - return; - } - - // Store the number of local variables we have to pop of the - // stack - locals = sc.getTotLocals; - - fn = sc.getFunction(); - assert(fn !is null, "return called outside a function scope"); - - // Get the size of all parameters - params = fn.paramSize; - - // Next, we must check that the returned expression, if any, is - // of the right type. - if(exp is null) - { - if(!fn.type.isVoid) - fail(format("Function expected a return value of type '%s'", - fn.type.toString()), loc); - return; - } - - if(fn.type.isVoid) - fail("Function does not have a return type", loc); - - exp.resolve(sc); - - fn.type.typeCast(exp, "return value"); - } - - void compile() - { - setLine(); - if(exp !is null && fn !is null) - { - assert(!fn.type.isVoid); - exp.eval(); - // Return an expression - tasm.exit(params, locals, exp.type.getSize); - } - else - // Return without an expression - tasm.exit(params, locals, 0); - } -} - -// A block of executable statements between a pair of curly braces {} -class CodeBlock : Statement -{ - StateArray contents; - Scope sc; - - Floc endLine; // Last line, used in error messages - - bool isState; // True if this block belongs to a state - bool isFile; // True if this is a stand-alone file - - private: - - char[] stateName; // Name of the state, used to set up the - // scope. Really a hack but it works. - - // Parses the given tokens as a statement. The second parameter - // specifies if the statement is allowed to be a variable - // declaration (for example, "if (expr) int i;" is not allowed.) - static Statement identify(ref TokenArray toks, bool allowVar = true) - { - Statement b = null; - - if(VarDeclStatement.canParse(toks)) - { - if(!allowVar) fail("Variable declaration not allowed here", toks[0].loc); - b = new VarDeclStatement; - } - else if(CodeBlock.canParse(toks)) b = new CodeBlock; - else if(ReturnStatement.canParse(toks)) b = new ReturnStatement; - 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(StateStatement.canParse(toks)) b = new StateStatement; - else if(LabelStatement.canParse(toks)) b = new LabelStatement; - else if(GotoStatement.canParse(toks)) b = new GotoStatement; - else if(ContinueBreakStatement.canParse(toks)) b = new ContinueBreakStatement; - else if(ForeachStatement.canParse(toks)) b = new ForeachStatement; - else if(ImportStatement.canParse(toks)) b = new ImportStatement(true); - // switch / select - // case - // assert ? - - // If this is not one of the above, default to an expression - // statement. - else b = new ExprStatement; - - b.parse(toks); - return b; - } - - public: - - this(bool isState = false, bool isFile = false) - { - this.isState = isState; - this.isFile = isFile; - - assert(!isFile || !isState); - } - - static bool canParse(TokenArray toks) - { - return isNext(toks, TT.LeftCurl); - } - - void parse(ref TokenArray toks) - { - Token strt; - if(!isFile) - { - if(toks.length == 0) - fail("Code block expected, got end of file"); - - strt = toks[0]; - if(strt.type != TT.LeftCurl) - fail("Code block expected a {", toks); - - loc = strt.loc; - - toks = toks[1..toks.length]; - } - - // Are we parsing stuff that comes before the code? Only - // applicable to state blocks. - bool beforeCode = isState; - - bool theEnd() - { - if(isFile) - return isNext(toks, TT.EOF, endLine); - else - return isNext(toks, TT.RightCurl, endLine); - } - - while(!theEnd()) - { - if(toks.length == 0) - { - if(!isFile) - fail(format("Unterminated code block (starting at line %s)", strt.loc)); - else break; - } - - if(beforeCode) - { - // The first label marks the begining of the code block - if(LabelStatement.canParse(toks)) - { - // Let identify insert the label - contents ~= identify(toks); - - // We are now parsing code - beforeCode = false; - - continue; - } - // TODO: Handle state functions here - else - fail("State code must begin with a label", toks); - } - - contents ~= identify(toks); - } - } - - char[] toString() - { - char[] res = "Codeblock: "; - for(int i=0; i - 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(); - } -} diff --git a/old_d_version/monster/compiler/structs.d b/old_d_version/monster/compiler/structs.d deleted file mode 100644 index 3ad6c447e..000000000 --- a/old_d_version/monster/compiler/structs.d +++ /dev/null @@ -1,167 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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); - */ - } -} diff --git a/old_d_version/monster/compiler/tokenizer.d b/old_d_version/monster/compiler/tokenizer.d deleted file mode 100644 index d220c9633..000000000 --- a/old_d_version/monster/compiler/tokenizer.d +++ /dev/null @@ -1,942 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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 = ""; - 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= '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; -} diff --git a/old_d_version/monster/compiler/types.d b/old_d_version/monster/compiler/types.d deleted file mode 100644 index e6eb83323..000000000 --- a/old_d_version/monster/compiler/types.d +++ /dev/null @@ -1,2023 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (types.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.types; - -import monster.compiler.tokenizer; -import monster.compiler.enums; -import monster.compiler.scopes; -import monster.compiler.expression; -import monster.compiler.assembler; -import monster.compiler.properties; -import monster.compiler.block; -import monster.compiler.functions; -import monster.compiler.variables; -import monster.compiler.states; -import monster.compiler.structs; -import monster.vm.arrays; -import monster.vm.mclass; -import monster.vm.mobject; -import monster.vm.error; - -import monster.options; - -import std.stdio; -import std.utf; -import std.string; - -// A class that represents a type. The Type class is abstract, and the -// different types are actually handled by various subclasses of Type -// (see below.) The static function identify() is used to figure out -// exactly which subclass to use. -abstract class Type : Block -{ - // Can the given tokens possibly be parsed as a type? This is not - // meant as an extensive test to differentiate between types and - // non-types, it is more like a short-cut to get the type tokens out - // of the way. It should only be called from places where you - // require a type (and only in canParse(), since it removes the - // tokens.) - static bool canParseRem(ref TokenArray toks) - { - // This function is a bit messy unfortunately. We should improve - // the process so we can clean it up. Preferably we should - // rewrite the entire parser. - - // We allow typeof(expression) as a type - if(isNext(toks, TT.Typeof)) - { - skipParens(toks); - return true; - } - - // The 'var' keyword can be used as a type - if(isNext(toks, TT.Var)) return true; - - if(FuncRefType.canParseRem(toks)) - return true; - - // Allow typename - if(!isNext(toks, TT.Identifier)) return false; - - // Allow a.b.c - while(isNext(toks, TT.Dot)) - if(!isNext(toks, TT.Identifier)) return false; - - // Allow a[][] - while(isNext(toks,TT.LeftSquare)) - if(!isNext(toks,TT.RightSquare)) - return false; - - return true; - } - - // Parse a type specifieer and return the correct class to handle it - // (fully parsed). Currently valid type formats are - // - // identifier - either a basic type (int, bool, etc) or a class name - // identifier[] - array - // identifier[][]... - arrays can be multi-dimensional - // - // static buffers, ie. int[10] are not allowed as types. The only - // place a type is allowed to take array expressions is after a new, - // eg. int[] i = new int[10], in that case you should set the second - // parameter to true. The expression array is stored in exps. - static Type identify(ref TokenArray toks, bool takeExpr, ref ExprArray exps) - { - assert(toks.length != 0); - - // Model this after Exp.idSub or Code.identify - Type t = null; - - // Find what kind of type this is and create an instance of the - // corresponding class. TODO: Lots of redundant objects created - // here. - if(BasicType.canParse(toks)) t = new BasicType(); - else if(UserType.canParse(toks)) t = new UserType(); - else if(GenericType.canParse(toks)) t = new GenericType(); - else if(TypeofType.canParse(toks)) t = new TypeofType(); - else if(FuncRefType.canParse(toks)) t = new FuncRefType(); - else fail("Cannot parse " ~ toks[0].str ~ " as a type", toks[0].loc); - - // Parse the actual tokens with our new and shiny object. - t.parse(toks); - - // Add arrays here afterwards by wrapping the previous type in - // an ArrayType. - exps = VarDeclaration.getArray(toks, takeExpr); - - if(t.isVar && exps.length) - fail("Cannot have arrays of var (yet)", t.loc); - - // Despite looking strange, this code is correct. - foreach(e; exps) - t = new ArrayType(t); - - return t; - } - - // Short version of the above, when expressions are not allowed. - static Type identify(ref TokenArray toks) - { - ExprArray exp; - return identify(toks, false, exp); - } - - // Lookup table of all types - static Type[int] typeList; - int tIndex; // Index of this type - - // The complete type name including specifiers, eg. "int[]". - char[] name; - - MetaType meta; - final MetaType getMeta() - { - if(meta is null) - meta = new MetaType(this); - return meta; - } - - this() - { - tIndex = typeList.length; - typeList[tIndex] = this; - name = "UNNAMED TYPE"; - } - - // Used for easy checking - bool isInt() { return false; } - bool isUint() { return false; } - bool isLong() { return false; } - bool isUlong() { return false; } - bool isChar() { return false; } - bool isBool() { return false; } - bool isFloat() { return false; } - bool isDouble() { return false; } - bool isVoid() { return false; } - - bool isVar() { return false; } - - bool isString() { return false; } - - bool isArray() { return arrays() != 0; } - bool isObject() { return false; } - bool isPackage() { return false; } - bool isStruct() { return false; } - bool isEnum() { return false; } - bool isIntFunc() { return false; } - bool isFuncRef() { return false; } - - bool isReplacer() { return false; } - - bool isIntegral() { return isInt || isUint || isLong || isUlong; } - bool isFloating() { return isFloat || isDouble; } - - // Numerical types allow arithmetic operators - bool isNumerical() { return isIntegral() || isFloating(); } - - // Is this a meta-type? A meta-type is the type of expressions like - // 'int' and 'ClassName' - they themselves describe a type. - // Meta-types always have a base type accessible through getBase(), - // and a member scope similar to variables of the type itself. - bool isMeta() { return false; } - - // Is this a legal type for variables? If this is false, you cannot - // create variables of this type, neither directly or indirectly - // (through automatic type inference.) - bool isLegal() { return true; } - - // Get base type (used for arrays and meta-types) - Type getBase() { assert(0, "Type " ~ toString() ~ " has no base type"); } - - // Return the array dimension of this type. Eg. int[][] has two - // dimensions, int has zero. - int arrays() { return 0; } - - // Return the number of ints needed to store one variable of this - // type. - int getSize(); - - // Get the scope for resolving members of this type. Examples: - // myObject.func() -> getMemberScope called for the type of myObject, - // returns the class scope, where func is resolved. - // array.length -> getMemberScope called for ArrayType, returns a - // special scope that contains the 'length' property - // Returning null means that this type does not have any members. - Scope getMemberScope() - // By default we return a minimal property scope (containing - // .sizeof etc) - { return GenericProperties.singleton; } - - // Validate that this type actually exists. This is used to make - // sure that all forward references are resolved. - void validate(Floc loc) {} - - // Check if this type is equivalent to the given D type - final bool isDType(TypeInfo ti) - { - char[] name = ti.toString; - name = name[name.rfind('.')+1..$]; - - switch(name) - { - case "int": return isInt(); - case "uint": return isUint(); - case "long": return isLong(); - case "ulong": return isUlong(); - case "float": return isFloat(); - case "double": return isDouble(); - case "bool": return isBool(); - case "dchar": return isChar(); - case "AIndex": return isArray(); - case "MIndex": return isObject(); - default: - assert(0, "illegal type in isDType(): " ~ name); - } - } - - // Used by defaultInit as a shortcut for converting a variable to an - // int array. - static int[] makeData(T)(T val) - { - int data[]; - if(T.sizeof == 4) data.length = 1; - else if(T.sizeof == 8) data.length = 2; - else assert(0, "Unsupported type size"); - - *(cast(T*)data.ptr) = val; - - return data; - } - - // Get the default initializer for this type. The assembler deals - // with data in terms of ints (4 byte chunks), so we return the data - // as an int[]. The default initializer should be an illegal value - // when possible (null pointers, nan, etc) to catch mistakenly - // uninitialized variables as quickly as possible. This will usually - // be the same init value as in D. - int[] defaultInit(); - - // Generate assembler code that pushes the default initializer on - // the stack. TODO: This should become a general function in the - // assembler to push any int[]. Pass it a type so it can check the - // size automatically as well. - final void pushInit() - { - int[] def = defaultInit(); - assert(def.length == getSize, "default initializer is not the correct size"); - setLine(); - - tasm.pushArray(def); - } - - // Compare types - final int opEquals(Type t) - { - if(toString != t.toString) return 0; - - // TODO: In our current system, if the name and array dimension - // match, we must be the same type. In the future we might have - // to add more tests here however. For example, two structs of - // the same name might be defined in different classes. The best - // solution is perhaps to always make sure the type name is - // unique. - - return 1; - } - - final char[] toString() { return name; } - - // Cast the expression orig to this type. Uses canCastTo to - // determine if a cast is possible. The 'to' parameter is used in - // error messages to describe the destination. - final void typeCast(ref Expression orig, char[] to) - { - if(orig.type == this) return; - - // Replace the expression with a CastExpression. This acts as a - // wrapper that puts in the conversion code after expression is - // evaluated. - if(orig.type.canCastTo(this)) - orig = new CastExpression(orig, this); - else - fail(format("Cannot implicitly cast %s of type %s to %s of type %s", - orig.toString, orig.typeString, - to, this), orig.loc); - } - - // Do explicit type casting. This allows for a wider range of type - // conversions. - final void typeCastExplicit(ref Expression orig) - { - if(orig.type == this) return; - - if(orig.type.canCastToExplicit(this) || orig.type.canCastTo(this)) - orig = new CastExpression(orig, this); - else - fail(format("Cannot cast %s of type %s to type %s", - orig.toString, orig.typeString, - this), orig.loc); - } - - // Do compile-time type casting. Gets orig.evalCTime() and returns - // the converted result. - final int[] typeCastCTime(Expression orig, char[] to) - { - int[] res = orig.evalCTime(); - - assert(res.length == orig.type.getSize); - - if(orig.type.canCastCTime(this)) - res = orig.type.doCastCTime(res, this, orig.loc); - else - fail(format("Cannot cast %s of type %s to %s of type %s (at compile time)", - orig.toString, orig.typeString, - to, this), orig.loc); - - assert(res.length == getSize); - - return res; - } - - // Can this type be cast to the parameter type? This function is not - // required to handle cases where the types are the same. - bool canCastTo(Type to) - { - return false; // By default we can't cast anything - } - - // Can the type be explicitly cast? This function does not have to - // handle cases where canCastTo is true. You only need to use it in - // cases where explicit casting is allowed but implicit is not, such - // as casting from float to int. - bool canCastToExplicit(Type to) - { - return false; - } - - // Returns true if the cast can be performed at compile time. This - // is usually true if we can cast at runtime. - bool canCastCTime(Type to) - { return canCastTo(to) || canCastToExplicit(to); } - - // Returns true if the types are equal or if canCastTo returns true. - final bool canCastOrEqual(Type to) - { - return to == this || canCastTo(to); - } - - // Do the cast in the assembler. Must handle both implicit and - // explicit casts. - void evalCastTo(Type to, Floc lc) - { - assert(0, "evalCastTo not implemented for type " ~ toString); - } - - // Do the cast in the compiler. Must handle both implicit and - // explicit casts. - int[] doCastCTime(int[] data, Type to, Floc lc) - { - assert(0, "doCastCTime not implemented for type " ~ toString); - } - - // Cast variable of this type to string - char[] valToString(int[] data) - { - assert(0, "valToString not implemented for " ~ toString); - } - - final AIndex valToStringIndex(int[] data) - { - assert(data.length == getSize); - return monster.vm.arrays.arrays.create(valToString(data)).getIndex; - } - - void parse(ref TokenArray toks) {assert(0, name);} - void resolve(Scope sc) {assert(0, name);} - - /* Cast two expressions to their common type, if any. Fail if not - possible. Examples of possible outcomes: - - int, int -> does nothing - float, int -> converts the second paramter to float - int, float -> converts the first to float - bool, int -> throws an error - - For various integer types, special rules apply. These are (for - the time being): - - ulong, any -> ulong - long, any 32bit -> long - int, uint -> uint - - Similar types (eg. uint, uint) will never convert types. - */ - static void castCommon(ref Expression e1, ref Expression e2) - { - Type t1 = e1.type; - Type t2 = e2.type; - - if(t1 == t2) return; - - Type common; - - // Apply integral promotion rules first. TODO: Apply polysemous - // rules later. - if(t1.isIntegral && t2.isIntegral) - { - // ulong dominates all other types - if(t1.isUlong) common = t1; - else if(t2.isUlong) common = t2; - - // long dominates over 32-bit values - else if(t1.isLong) common = t1; - else if(t2.isLong) common = t2; - - // unsigned dominates over signed - else if(t1.isUint) common = t1; - else if(t2.isUint) common = t2; - else - { - assert(t1.isInt && t2.isInt, "unknown integral type encountered"); - assert(0, "should never get here"); - } - } - else - { - // Find the common type - if(t1.canCastTo(t2)) common = t2; else - if(t2.canCastTo(t1)) common = t1; else - fail(format("Cannot implicitly cast %s of type %s to %s of type %s, or vice versa.", e1, t1, e2, t2), e1.loc); - } - - // Wrap the expressions in CastExpression blocks if necessary. - common.typeCast(e1, ""); - common.typeCast(e2, ""); - } -} - -// Internal types are types that are used internally by the compiler, -// eg. for type conversion or for special syntax. They can not be used -// for variables or values, neither directly nor indirectly. -abstract class InternalType : Type -{ - override: - Scope getMemberScope() { return null; } - final: - bool isLegal() { return false; } - int[] defaultInit() {assert(0, name);} - int getSize() { return 0; } -} - -// Handles package names, in expressions like Package.ClassName. It is -// only used for these expressions and never for anything else. -class PackageType : InternalType -{ - PackageScope sc; - - this(PackageScope _sc) - { - sc = _sc; - name = sc.toString() ~ " (package)"; - } - - override: - Scope getMemberScope() - { - assert(sc !is null); - return sc; - } - - bool isPackage() { return true; } -} - -// Handles the 'null' literal. This type is only used for -// conversions. You cannot declare variables of the nulltype. -class NullType : InternalType -{ - this() { name = "null-type"; } - - bool canCastTo(Type to) - { - return to.isArray || to.isObject || to.isEnum || to.isFuncRef; - } - - void evalCastTo(Type to, Floc lc) - { - // Always evaluable at compile time - there is no runtime value - // to convert. - tasm.pushArray(doCastCTime(null, to, lc)); - } - - int[] doCastCTime(int[] data, Type to, Floc lc) - { - // For all the types we currently support, 'null' is always - // equal to the type's default initializer. - assert(data.length == 0); - return to.defaultInit(); - } -} - -// Helper template for BasicType -abstract class TypeHolderBase -{ - char[] getString(int[] data); -} -class TypeHolder(T) : TypeHolderBase -{ - T toValue(int[] data) - { - static if(!is(T==bool)) - assert(data.length*4 == T.sizeof); - return *(cast(T*)data.ptr); - } - - char[] getString(int [] data) - { - static if(is(T == dchar)) - return toUTF8([toValue(data)]); - else - return .toString(toValue(data)); - } -} - -// Handles all the built-in types. These are: int, uint, long, ulong, -// float, double, bool, char and the "void" type, which is represented -// by an empty string. The void type is only allowed in special cases -// (currently only in function return types), and is not parsed -// normally through identify()/parse(). Instead it is created directly -// with the constructor. -class BasicType : Type -{ - this() {} - - // Create the given type directly without parsing. Use an empty - // string "" for the void type. This is the only way a void type can - // be created. - this(char[] tn) - { - name = tn; - if(name == "") name = "(none)"; - - if(!isBasic(name)) - fail("BasicType does not support type " ~ tn); - - // Cache the class to save some overhead - store[tn] = this; - } - - TypeHolderBase tph; - TypeHolderBase getHolder() - { - if(tph is null) - { - if(isInt) tph = new TypeHolder!(int); - if(isUint) tph = new TypeHolder!(uint); - if(isLong) tph = new TypeHolder!(long); - if(isUlong) tph = new TypeHolder!(ulong); - if(isFloat) tph = new TypeHolder!(float); - if(isDouble) tph = new TypeHolder!(double); - if(isChar) tph = new TypeHolder!(dchar); - if(isBool) tph = new TypeHolder!(bool); - } - assert(!isVoid); - - return tph; - } - - private static BasicType[char[]] store; - - // Get a basic type of the given name. This will not allocate a new - // instance if another instance already exists. - static BasicType get(char[] tn) - { - if(tn in store) return store[tn]; - - // Automatically adds itself to the store - return new BasicType(tn); - } - - // Shortcuts - static BasicType getVoid() { return get(""); } - static BasicType getInt() { return get("int"); } - static BasicType getUint() { return get("uint"); } - static BasicType getLong() { return get("long"); } - static BasicType getUlong() { return get("ulong"); } - static BasicType getFloat() { return get("float"); } - static BasicType getDouble() { return get("double"); } - static BasicType getChar() { return get("char"); } - static BasicType getBool() { return get("bool"); } - - static bool isBasic(char[] tn) - { - return (tn == "(none)" || tn == "int" || tn == "float" || tn == "char" || - tn == "bool" || tn == "uint" || tn == "long" || - tn == "ulong" || tn == "double"); - } - - static bool canParse(TokenArray toks) - { - Token t; - if(!isNext(toks, TT.Identifier, t)) return false; - return isBasic(t.str); - } - - override: - void parse(ref TokenArray toks) - { - Token t; - - reqNext(toks, TT.Identifier, t); - assert(isBasic(t.str)); - - // Get the name and the line from the token - name = t.str; - loc = t.loc; - - if(name == "") name = "(none)"; - } - - bool isInt() { return name == "int"; } - bool isUint() { return name == "uint"; } - bool isLong() { return name == "long"; } - bool isUlong() { return name == "ulong"; } - bool isChar() { return name == "char"; } - bool isBool() { return name == "bool"; } - bool isFloat() { return name == "float"; } - bool isDouble() { return name == "double"; } - bool isVoid() { return name == "(none)"; } - - Scope getMemberScope() - { - if(isInt) return IntProperties.singleton; - if(isUint) return UintProperties.singleton; - if(isLong) return LongProperties.singleton; - if(isUlong) return UlongProperties.singleton; - if(isFloat) return FloatProperties.singleton; - if(isDouble) return DoubleProperties.singleton; - - if(isChar || isBool) - return GenericProperties.singleton; - - assert(isVoid); - return null; - } - - // List the implisit conversions that are possible - bool canCastTo(Type to) - { - // If options.d allow it, we can implicitly convert back from - // floats to integral types. - static if(implicitTruncate) - { - if(isFloating && to.isIntegral) - return true; - } - - // We can convert between all integral types - if(to.isIntegral) return isIntegral; - - // All numerical types can be converted to floating point - if(to.isFloating) return isNumerical; - - // These types can be converted to strings. - if(to.isString) - return isNumerical || isChar || isBool; - - return false; - } - - bool canCastToExplicit(Type to) - { - if(isFloating && to.isIntegral) - return true; - - return false; - } - - void evalCastTo(Type to, Floc lc) - { - assert(this != to); - assert(!isVoid); - - int fromSize = getSize(); - int toSize = to.getSize(); - bool fromSign = isInt || isLong || isFloat || isBool; - - if(isFloating && to.isIntegral) - tasm.castFloatToInt(this, to); - else if(to.isInt || to.isUint) - { - assert(isIntegral); - if(isLong || isUlong) - tasm.castLongToInt(); - } - else if(to.isLong || to.isUlong) - { - if(isInt || isUint) tasm.castIntToLong(fromSign); - else assert(isUlong || isLong); - } - else if(to.isFloat || to.isDouble) - { - if(isIntegral) - tasm.castIntToFloat(this, to); - else if(isFloating) - tasm.castFloatToFloat(fromSize, toSize); - else assert(0); - } - else if(to.isString) - { - assert(!isVoid); - - // Create an array from one element on the stack - if(isChar) tasm.popToArray(1, 1); - else - tasm.castToString(this); - } - else - fail("Conversion " ~ toString ~ " to " ~ to.toString ~ - " not implemented.", lc); - } - - char[] valToString(int[] data) - { - assert(data.length == getSize); - assert(!isVoid); - - return getHolder().getString(data); - } - - int[] doCastCTime(int[] data, Type to, Floc lc) - { - assert(this != to); - assert(!isVoid); - assert(!to.isVoid); - - if(to.isString) - return [valToStringIndex(data)]; - - int fromSize = getSize(); - int toSize = to.getSize(); - - assert(data.length == fromSize); - bool fromSign = isInt || isLong || isFloat || isBool; - - // Set up a new array to hold the result - int[] toData = new int[toSize]; - - if(isFloating && to.isIntegral) - { - int *iptr = cast(int*)toData.ptr; - uint *uptr = cast(uint*)toData.ptr; - long *lptr = cast(long*)toData.ptr; - ulong *ulptr = cast(ulong*)toData.ptr; - - if(isFloat) - { - float f = *(cast(float*)data.ptr); - if(to.isInt) *iptr = cast(int) f; - else if(to.isUint) *uptr = cast(uint) f; - else if(to.isLong) *lptr = cast(long) f; - else if(to.isUlong) *ulptr = cast(ulong) f; - else assert(0); - } - else if(isDouble) - { - double f = *(cast(double*)data.ptr); - if(to.isInt) *iptr = cast(int) f; - else if(to.isUint) *uptr = cast(uint) f; - else if(to.isLong) *lptr = cast(long) f; - else if(to.isUlong) *ulptr = cast(ulong) f; - else assert(0); - } - else assert(0); - } - else if(to.isInt || to.isUint) - { - assert(isIntegral); - // Just pick out the least significant int - toData[] = data[0..1]; - } - else if(to.isLong || to.isUlong) - { - if(isInt || isUint) - { - toData[0] = data[0]; - if(fromSign && to.isLong && data[0] < 0) toData[1] = -1; - else toData[1] = 0; - } - else assert(isUlong || isLong); - } - else if(to.isFloat) - { - assert(isNumerical); - - float *fptr = cast(float*)toData.ptr; - - if(isInt) *fptr = data[0]; - else if(isUint) *fptr = cast(uint)data[0]; - else if(isLong) *fptr = *(cast(long*)data.ptr); - else if(isUlong) *fptr = *(cast(ulong*)data.ptr); - else if(isDouble) *fptr = *(cast(double*)data.ptr); - else assert(0); - } - else if(to.isDouble) - { - assert(isNumerical); - - assert(toData.length == 2); - double *fptr = cast(double*)toData.ptr; - - if(isInt) *fptr = data[0]; - else if(isUint) *fptr = cast(uint)data[0]; - else if(isLong) *fptr = *(cast(long*)data.ptr); - else if(isUlong) *fptr = *(cast(ulong*)data.ptr); - else if(isFloat) *fptr = *(cast(float*)data.ptr); - else assert(0); - } - else - fail("Compile time conversion " ~ toString ~ " to " ~ to.toString ~ - " not implemented.", lc); - - assert(toData.length == toSize); - assert(toData.ptr !is data.ptr); - return toData; - } - - int getSize() - { - if( isInt || isUint || isFloat || isChar || isBool ) - return 1; - - if( isLong || isUlong || isDouble ) - return 2; - - if( isVoid ) - return 0; - - assert(0, "getSize() does not handle type '" ~ name ~ "'"); - } - - void resolve(Scope sc) - { - // Check that our given name is indeed valid - assert(isBasic(name)); - } - - int[] defaultInit() - { - int data[]; - - // Ints default to 0, bools to false - if(isInt || isUint || isBool) data = makeData!(int)(0); - // Ditto for double-size ints - else if(isLong || isUlong) data = makeData!(long)(0); - // Chars default to an illegal utf-32 value - else if(isChar) data = makeData!(int)(0x0000FFFF); - // Floats default to NaN - else if(isFloat) data = makeData!(float)(float.nan); - else if(isDouble) data = makeData!(double)(double.nan); - else - assert(0, "Type '" ~ name ~ "' has no default initializer"); - - assert(data.length == getSize, "size mismatch in defaultInit"); - - return data; - } -} - -// Represents a normal class name. -class ObjectType : Type -{ - final: - private: - // Class that we represent (set by resolve()). Variables of this - // type may point to objects of this class, or to objects of - // subclasses. We only use an index, since classes might be forward - // referenced. - CIndex clsIndex; - - public: - - this(Token t) - { - // Get the name and the line from the token - name = t.str; - loc = t.loc; - } - - this(MonsterClass mc) - { - assert(mc !is null); - name = mc.name.str; - loc = mc.name.loc; - clsIndex = mc.gIndex; - } - - MonsterClass getClass(Floc loc = Floc.init) - { - assert(clsIndex != 0); - - if(!global.isLoaded(clsIndex)) - fail("Cannot use " ~ name ~ - ": class not found or forward reference", loc); - - return global.getClass(clsIndex); - } - - override: - void validate(Floc loc) - { - // getClass does most of the error checking we need - auto mc = getClass(loc); - - // check that we're not using a module as a type - if(mc.isModule) - fail("Cannot use module " ~ name ~ " as a type", loc); - } - - char[] valToString(int[] data) - { - // Use the object's toString function - assert(data.length == 1); - if(data[0] == 0) return "(null object)"; - auto mo = getMObject(cast(MIndex)data[0]); - return mo.toString(); - } - - int getSize() { return 1; } - bool isObject() { return true; } - int[] defaultInit() { return makeData!(int)(0); } - - bool canCastCTime(Type to) { return false; } - - // Used internally below, to get the class from another object type. - private MonsterClass getOther(Type other) - { - assert(other !is this); - assert(other.isObject); - auto ot = cast(ObjectType)other; - assert(ot !is null); - auto cls = ot.getClass(); - assert(cls !is null); - assert(cls !is getClass()); - return cls; - } - - bool canCastTo(Type type) - { - assert(clsIndex != 0); - - if(type.isString) return true; - - if(type.isObject) - { - MonsterClass us = getClass(); - MonsterClass other = getOther(type); - - // Allow implicit downcasting if options.d says so. - static if(implicitDowncast) - { - if(other.childOf(us)) - return true; - } - - // We can always upcast implicitly - return other.parentOf(us); - } - - return false; - } - - bool canCastToExplicit(Type type) - { - // We can explicitly cast to a child class, and let the VM - // handle any errors. - if(type.isObject) - { - auto us = getClass(); - auto other = getOther(type); - - return other.childOf(us); - } - } - - void evalCastTo(Type to, Floc lc) - { - assert(clsIndex != 0); - assert(canCastTo(to) || canCastToExplicit(to)); - - if(to.isObject) - { - auto us = getClass(); - auto other = getOther(to); - - // Upcasting doesn't require any action - if(other.parentOf(us)) {} - - // Downcasting (from parent to child) requires that VM - // checks the runtime type of the object. - else if(other.childOf(us)) - // Use the global class index - tasm.downCast(other.getIndex()); - - // We should never get here - else assert(0); - - return; - } - - assert(to.isString); - tasm.castToString(this); - } - - // Members of objects are resolved in the class scope. - Scope getMemberScope() - { - assert(getClass !is null); - assert(getClass.sc !is null); - return getClass().sc; - } - - // This is called when the type is defined, and it can forward - // reference classes that do not exist yet. The classes must exist - // by the time getClass() is called though, usually when the class - // body (not just the header) is being resolved. - void resolve(Scope sc) - { - // Insert the class into the global scope as a forward - // reference. Note that this means the class may not be part of - // any other package than 'global' when it is loaded later. - if(clsIndex == 0) - clsIndex = global.getForwardIndex(name); - - assert(clsIndex != 0); - } -} - -class ArrayType : Type -{ - // What type is this an array of? - Type base; - - static ArrayType str; - - static ArrayType getString() - { - if(str is null) - str = new ArrayType(BasicType.getChar); - return str; - } - - static ArrayType get(Type base) - { - // TODO: We can cache stuff here later - return new ArrayType(base); - } - - this(Type btype) - { - base = btype; - name = base.name ~ "[]"; - loc = base.loc; - } - - ArrayRef *getArray(int[] data) - { - assert(data.length == 1); - return monster.vm.arrays.arrays.getRef(cast(AIndex)data[0]); - } - - override: - void validate(Floc loc) { assert(base !is null); base.validate(loc); } - int arrays() { return base.arrays() + 1; } - int getSize() { return 1; } - int[] defaultInit() { return makeData!(int)(0); } - Type getBase() { return base; } - - Scope getMemberScope() - { - return ArrayProperties.singleton; - } - - // All arrays can be cast to string - bool canCastTo(Type to) - { - // Strings can be cast to char, but the conversion is only valid - // if the string is one char long. - if(isString) return to.isChar; - - // Conversion to string is always allowed, and overrides other - // array conversions. TODO: This will change later when we get - // the generic 'var' type. Then implicit casting to string will - // be unnecessary. - if(to.isString) - return true; - - if(to.isArray) - return getBase().canCastTo(to.getBase()); - - return false; - } - - void evalCastTo(Type to, Floc lc) - { - if(isString) - { - assert(to.isChar); - tasm.castStrToChar(); - return; - } - - if(to.isString) - { - tasm.castToString(this); - return; - } - - if(to.isArray && getBase().canCastTo(to.getBase())) - fail("Casting arrays at runtime not implemented yet", lc); - - assert(0); - } - - int[] doCastCTime(int[] data, Type to, Floc lc) - { - if(isString) - { - assert(to.isChar); - - // When casting from string to char, we pick out the first - // (and only) character from the string. - - auto arf = getArray(data); - - // FIXME: These error messages don't produce a file/line - // number. Using our own loc doesn't work. I think we need - // to pass it on to doCastCTime. - - if(arf.carr.length == 0) - fail("Cannot cast empty string to 'char'", lc); - - assert(arf.elemSize == 1); - - if(arf.carr.length > 1) - fail("Cannot cast string " ~ valToString(data) ~ - " to 'char': too long", lc); - - return [arf.iarr[0]]; - } - - if(to.isArray && getBase().canCastTo(to.getBase())) - { - auto arf = getArray(data); - assert(arf.iarr.length > 0, "shouldn't need to cast an empty array"); - - // The types - Type oldt = getBase(); - Type newt = to.getBase(); - - assert(oldt.canCastCTime(newt), - "oops, cannot cast at compile time"); - - // Set up the source and destination array data - int len = arf.length(); - int olds = oldt.getSize(); - int news = newt.getSize(); - - int[] src = arf.iarr; - int[] dst = new int[len * news]; - - assert(src.length == len * olds); - - for(int i=0; i 0) - name ~= ","; - name ~= v.toString(); - - if(isVararg && i == params.length-1) - name ~= "..."; - } - - name ~= ")"; - } - - static bool canParseRem(ref TokenArray toks) - { - if(!isNext(toks, TT.Function)) - return false; - - // Return type? - if(toks.length && toks[0].type != TT.LeftParen) - Type.canParseRem(toks); - - skipParens(toks); - return true; - } - - // Same as the above but it doesn't change the array (no 'ref' - // parameter) - static bool canParse(TokenArray toks) - { return canParseRem(toks); } - - // Calculate the stack imprint - int getImprint() - { - assert(retType !is null); - int res = retType.getSize(); - - foreach(p; params) - res -= p.getSize(); - - return res; - } - - override: - void parse(ref TokenArray toks) - { - reqNext(toks, TT.Function); - - if(!isNext(toks, TT.LeftParen)) - { - retType = Type.identify(toks); - reqNext(toks, TT.LeftParen); - } - else - retType = BasicType.getVoid(); - - if(!isNext(toks, TT.RightParen)) - { - do - { - params ~= Type.identify(toks); - } - while(isNext(toks, TT.Comma)); - reqNext(toks, TT.RightParen); - } - } - - void resolve(Scope sc) - { - retType.resolve(sc); - foreach(p; params) - p.resolve(sc); - - createName(); - } - - void validate(Floc f) - { - retType.validate(f); - foreach(p; params) - p.validate(f); - } - - int getSize() { return 2; } - bool isFuncRef() { return true; } - int[] defaultInit() { return [0, -1]; } - Scope getMemberScope() { return FuncRefProperties.singleton; } -} - -// Type used for internal references to functions. This is NOT the -// same as the type used for function references ("pointers") - see -// FuncRefType for that. -class IntFuncType : InternalType -{ - Function *func; - bool isMember; - - this(Function *fn, bool isMemb) - { - func = fn; - assert(fn !is null); - isMember = isMemb; - - name="Function"; - } - - override: - bool isIntFunc() { return true; } -} - -class EnumType : Type -{ - // Enum entries - EnumEntry[] entries; - EnumScope sc; - char[] initString = " (not set)"; - - // Lookup tables - EnumEntry* nameAA[char[]]; - EnumEntry* valueAA[long]; - - // Fields - FieldDef fields[]; - - long - minVal = long.max, - maxVal = long.min; - - Token nameTok; - - EnumEntry *lookup(long val) - { - auto p = val in valueAA; - if(p is null) - return null; - return *p; - } - - EnumEntry *lookup(char[] str) - { - auto p = str in nameAA; - if(p is null) return null; - return *p; - } - - int findField(char[] str) - { - foreach(i, fd; fields) - if(fd.name.str == str) - return i; - return -1; - } - - override: - - bool isEnum() { return true; } - - int[] defaultInit() { return [0]; } - int getSize() { return 1; } - - void resolve(Scope last) - { - assert(sc is null, "resolve() called more than once"); - - initString = name ~ initString; - - foreach(i, ref ent; entries) - { - // Make sure there are no naming conflicts. - last.clearId(ent.name); - - // Assign an internal value to each entry - ent.index = i+1; - - // Set the printed value to be "Enum.Name" - ent.stringValue = name ~ "." ~ ent.name.str; - - // Create an AA for values, and one for the names. This is also - // where we check for duplicates in both. - if(ent.name.str in nameAA) - fail("Duplicate entry '" ~ ent.name.str ~ "' in enum", ent.name.loc); - if(ent.value in valueAA) - fail("Duplicate value " ~ .toString(ent.value) ~ " in enum", ent.name.loc); - nameAA[ent.name.str] = &ent; - valueAA[ent.value] = &ent; - - if(ent.value > maxVal) maxVal = ent.value; - if(ent.value < minVal) minVal = ent.value; - } - - // Create the scope - sc = new EnumScope(this); - - // Check the fields - foreach(ref fd; fields) - { - last.clearId(fd.name); - if(fd.name.str in nameAA) - fail("Field name cannot match value name " ~ fd.name.str, fd.name.loc); - - fd.type.resolve(last); - if(fd.type.isReplacer) - fd.type = fd.type.getBase(); - - fd.type.validate(fd.name.loc); - } - - // Resolve and check field expressions. - foreach(ref ent; entries) - { - // Check number of expressions - if(ent.exp.length > fields.length) - fail(format("Too many fields in enum line (expected %s, found %s)", - fields.length, ent.exp.length), - ent.name.loc); - - ent.fields.length = fields.length; - - foreach(i, ref fe; ent.exp) - { - assert(fe !is null); - fe.resolve(last); - - // Check the types - fields[i].type.typeCast(fe, format("field %s (%s)", - i+1, fields[i].name.str)); - - // And that they are all compile time expressions - if(!fe.isCTime) - fail("Cannot evaluate " ~ fe.toString ~ " at compile time", fe.loc); - } - - // Finally, get the values - foreach(i, ref int[] data; ent.fields) - { - if(i < ent.exp.length) - data = ent.exp[i].evalCTime(); - else - // Use the init value if no field is value is given - data = fields[i].type.defaultInit(); - - assert(data.length == fields[i].type.getSize); - } - - // Clear the expression array since we don't need it anymore - ent.exp = null; - } - } - - bool canCastTo(Type to) - { - // Can always cast to string - if(to.isString) return true; - - // The value is a long, so we can always cast to types that long - // can be cast to. - if(BasicType.getLong().canCastOrEqual(to)) return true; - - // Check each field from left to right. If the field can be cast - // to the given type, then it's ok. - foreach(f; fields) - if(f.type.canCastOrEqual(to)) - return true; - - return false; - } - - void evalCastTo(Type to, Floc lc) - { - // Convert the enum name to a string - if(to.isString) - { - tasm.castToString(this); - return; - } - - auto lng = BasicType.getLong(); - if(lng.canCastOrEqual(to)) - { - // Get the value - tasm.getEnumValue(this); - // Cast it if necessary - if(to != lng) - lng.evalCastTo(to, lc); - return; - } - - // Check the fields - foreach(i, f; fields) - if(f.type.canCastOrEqual(to)) - { - // Get the field value from the enum - tasm.getEnumValue(this, i); - - // If the type doesn't match exactly, convert it. - if(f.type != to) - f.type.evalCastTo(to, lc); - - return; - } - - assert(0); - } - - int[] doCastCTime(int[] data, Type to, Floc lc) - { - if(to.isString) - return [valToStringIndex(data)]; - - // This code won't run yet, because the enum fields are - // properties and we haven't implemented ctime property reading - // yet. Leave this assert in here so that we remember to test it - // later. TODO. - assert(0, "finished, but not tested"); - - // Get the enum index - assert(data.length == 1); - int v = data[0]; - - // Check that we were not given a zero index - if(v-- == 0) - fail("Cannot get value of fields from an empty Enum variable.", lc); - - // Get the entry - assert(v >= 0 && v < entries.length); - auto ent = &entries[v]; - - auto lng = BasicType.getLong(); - if(lng.canCastOrEqual(to)) - { - // Get the value - int[] val = (cast(int*)&ent.value)[0..2]; - // Cast it if necessary - if(to != lng) - val = lng.doCastCTime(val, to, lc); - - return val; - } - - // Check the fields - foreach(i, f; fields) - if(f.type.canCastOrEqual(to)) - { - // Get the field value from the enum - int[] val = ent.fields[i]; - - // If the type doesn't match exactly, convert it. - if(f.type != to) - val = f.type.doCastCTime(val, to, lc); - - return val; - } - - assert(0); - } - - // TODO: In this case, we could override valToStringIndex as well, - // and return a cached, constant string index to further optimize - // memory usage. - char[] valToString(int[] data) - { - assert(data.length == 1); - int v = data[0]; - assert(v >= 0 && v <= entries.length); - - // v == 0 means that no value is set - return a default string - // value - if(v == 0) - return initString; - - else return entries[v-1].stringValue; - } - - Scope getMemberScope() - { return sc; } -} - -class StructType : Type -{ - private: - StructDeclaration sd; - - void setup() - { - if(!set) - sd.resolve(sc.getParent); - else - { - // We might get here if this function is being called - // recursively from sd.resolve. This only happens when the - // struct contains itself somehow. In that case, size will - // not have been set yet. - if(size == -1) - fail("Struct " ~ name ~ " indirectly contains itself", loc); - } - - assert(set); - assert(size != -1); - } - - public: - - int size = -1; - StructScope sc; // Scope of the struct interior - int[] defInit; - bool set; // Have vars and defInit been set? - - // Member variables - Variable*[] vars; - - this(StructDeclaration sdp) - { - sd = sdp; - name = sd.name.str; - loc = sd.name.loc; - } - - override: - // Calls validate for all member types - void validate(Floc loc) - { - foreach(v; vars) - v.type.validate(loc); - } - - // Resolves member - void resolve(Scope sc) {} - - bool isStruct() { return true; } - - int getSize() - { - setup(); - assert(size != -1); - return size; - } - - int[] defaultInit() - { - setup(); - assert(defInit.length == getSize); - return defInit; - } - - // Scope getMemberScope() { return sc; } -} - -// A 'delayed lookup' type - can replace itself after resolve is -// called. -abstract class ReplacerType : InternalType -{ - protected: - Type realType; - - public: - override: - Type getBase() { assert(realType !is null); return realType; } - bool isReplacer() { return true; } -} - -// Type names that consist of an identifier - classes, structs, enums, -// etc. We can't know what type it is until we resolve it. -class UserType : ReplacerType -{ - private: - Token ids[]; - - public: - - static bool canParse(TokenArray toks) - { - if(!isNext(toks, TT.Identifier)) return false; - return true; - } - - void parse(ref TokenArray toks) - { - Token id; - reqNext(toks, TT.Identifier, id); - - // Get the name and the line from the first token - name = id.str~"(replacer)"; - loc = id.loc; - - ids ~= id; - - // Parse any following identifiers, separated by dots. - while(isNext(toks,TT.Dot)) - { - reqNext(toks, TT.Identifier, id); - ids ~= id; - } - } - - void resolve(Scope sc) - { - assert(ids.length >= 1); - - // The top-most identifier is looked up in imported scopes - auto first = ids[0]; - auto sl = sc.lookupImport(first); - if(sl.isImport) - sl = sl.imphold.lookup(first); - - // The scope in which to resolve the class lookup. - Scope lastScope = sc; - - // Loop through the list and look up each member. - foreach(int ind, idt; ids) - { - if(ind == 0) continue; - - if(sl.isClass) - { - // Look up the next identifier in the class scope - assert(sl.mc !is null); - sl.mc.requireScope(); - assert(sl.mc.sc !is null); - sl = sl.mc.sc.lookupClass(idt); - } - else if(sl.isPackage) - { - lastScope = sl.sc; - assert(sl.sc.isPackage); - sl = sl.sc.lookupClass(idt); - } - // Was anything found at all? - else if(!sl.isNone) - fail(sl.name.str ~ " (which is a " ~ LTypeName[sl.ltype] - ~ ") does not have a type member called " ~ idt.str, - idt.loc); - else - fail("Unknown type " ~ sl.name.str, sl.name.loc); - } - - // sl should now contain the lookup result of the last - // identifier in the list. - - // Is it a type? - if(sl.isType) - // Great! - realType = sl.type; - - // If not, maybe a class? - else if(sl.isClass) - { - // Splendid - sl.mc.requireScope(); - realType = sl.mc.objType; - assert(realType !is null); - } - - // Allow packages used as type names in some situations - else if(sl.isPackage) - { - auto psc = cast(PackageScope)sl.sc; - assert(psc !is null); - realType = psc.type; - } - - // Was anything found at all? - else if(!sl.isNone) - // Ouch, something was found that's not a type or class. - fail("Cannot use " ~ sl.name.str ~ " (which is a " ~ - LTypeName[sl.ltype] ~ ") as a type!", sl.name.loc); - - if(realType is null) - { - // Nothing was found. Assume it's a forward reference to a - // class. These are handled later on. - realType = new ObjectType(sl.name); - assert(lastScope !is null); - realType.resolve(lastScope); - } - - assert(realType !is this); - assert(!realType.isReplacer); - } -} - -class TypeofType : ReplacerType -{ - static bool canParse(TokenArray toks) - { return isNext(toks, TT.Typeof); } - - Expression exp; - - void parse(ref TokenArray toks) - { - reqNext(toks, TT.Typeof, loc); - reqNext(toks, TT.LeftParen); - exp = Expression.identify(toks); - reqNext(toks, TT.RightParen); - } - - void resolve(Scope sc) - { - // Resolve the expression in the context of the scope - exp.resolve(sc); - if(exp.type.isMeta) - fail("Cannot use typeof on a meta type", exp.loc); - - realType = exp.type; - } -} - -// The 'var' type - generic type. Currently just works like 'auto' in -// D. -class GenericType : InternalType -{ - static GenericType sing; - - this() { name = "var"; } - - static bool canParse(TokenArray toks) - { return isNext(toks, TT.Var); } - - static GenericType getSingleton() - { - if(sing is null) - sing = new GenericType; - - return sing; - } - - override: - bool isVar() { return true; } - - void parse(ref TokenArray toks) - { - reqNext(toks, TT.Var, loc); - } - - void resolve(Scope sc) {} -} - -// I never meta type I didn't like! -class MetaType : InternalType -{ - protected: - Type base; - AIndex ai; - - public: - - static Type getBasic(char[] basic) - { - return BasicType.get(basic).getMeta(); - } - - this(Type b) - { - base = b; - name = "(meta)"~base.toString; - ai = 0; - } - - bool isMeta() { return true; } - - bool canCastTo(Type type) - { - return type.isString; - } - - char[] valToString(int[] data) - { - assert(data.length == 0); - return base.name; - } - - int[] cache; - - int[] doCastCTime(int[] data, Type to, Floc lc) - { - assert(to.isString); - - if(cache.length == 0) - cache = [valToStringIndex(data)]; - - return cache; - } - - void evalCastTo(Type to, Floc lc) - { - assert(to.isString); - - // Create an array index and store it for reuse later. - if(ai == 0) - { - auto arf = monster.vm.arrays.arrays.create(base.toString); - arf.flags.set(AFlags.Const); - ai = arf.getIndex(); - } - assert(ai != 0); - - tasm.push(cast(uint)ai); - } - - // Return the scope belonging to the base type. This makes int.max - // work just like i.max. Differentiation between static and - // non-static members is handled in the expression resolves. - Scope getMemberScope() { return base.getMemberScope(); } - Type getBase() { return base; } -} diff --git a/old_d_version/monster/compiler/variables.d b/old_d_version/monster/compiler/variables.d deleted file mode 100644 index 6af8c0917..000000000 --- a/old_d_version/monster/compiler/variables.d +++ /dev/null @@ -1,1132 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (variables.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.variables; - -import monster.compiler.types; -import monster.compiler.tokenizer; -import monster.compiler.expression; -import monster.compiler.scopes; -import monster.compiler.statement; -import monster.compiler.states; -import monster.compiler.block; -import monster.compiler.operators; -import monster.compiler.assembler; - -import std.string; -import std.stdio; - -import monster.vm.mclass; -import monster.vm.error; -import monster.vm.vm; - -import monster.options; - -enum VarType - { - Class, - Param, - Local, - } - -struct Variable -{ - Type type; - VarType vtype; - Token name; - - VarScope sc; // Scope that owns this variable - - int number; // Index used in bytecode to reference this variable. It - // corresponds to the stack offset for local variables / - // parameters, and the data segment offset internally - // for the given class for class variables. - - bool isRef; // Is this a reference variable? - bool isConst; // Used for function parameters - bool isVararg; // A vararg function parameter -} - -// Variable declaration. Handles local and class variables, function -// parameters and loop variables. -class VarDeclaration : Block -{ - Variable *var; - - // Initializer expression, if any. Eg: - // int i = 3; => init = 3. - Expression init; - - bool allowRef; // Allows reference variable. - bool allowNoType; // Allow no type to be specified (used in foreach) - bool allowConst; // Allows const. - - this() {} - - // Used when the type is already given, and we only need to read the - // name and what follows. This is used for multiple declarations, - // ie. int i=1, j=2; and will possibly also be used in other places - // later. - this(Type type) - { - assert(var is null); - var = new Variable; - - var.type = type; - } - - // Parse keywords allowed on variables - private void parseKeywords(ref TokenArray toks) - { - Floc loc; - while(1) - { - if(isNext(toks, TT.Ref, loc)) - { - if(var.isRef) - fail("Multiple token 'ref' in variable declaration", - loc); - if(!allowRef) - fail("You cannot use 'ref' variables here", loc); - var.isRef = true; - continue; - } - if(isNext(toks, TT.Const, loc)) - { - if(var.isConst) - fail("Multiple token 'const' in variable declaration", - loc); - var.isConst = true; - continue; - } - break; - } - } - - // Parse a series of array specifiers, ie. - // [] - // [][]... - // [expr1][expr2].... - // If takeExpr = false then the last form is not allowed - static ExprArray getArray(ref TokenArray toks, bool takeExpr = false) - { - // Arrays? - ExprArray arrayArgs; - while(isNext(toks,TT.LeftSquare)) - { - Expression expr = null; - - // Is there an expression inside the brackets? - if(!isNext(toks, TT.RightSquare)) - { - Floc loc = getLoc(toks); - - expr = Expression.identify(toks); - - if(!takeExpr) - fail("Array expression [" ~ expr.toString ~ - "] not allowed here", loc); - - if(!isNext(toks, TT.RightSquare)) - fail("Expected matching ]", toks); - } - - // Insert the expression (or a null if the brackets were - // empty) - arrayArgs ~= expr; - } - return arrayArgs; - } - - // Get the total number of array dimensions. Eg. int[] j[][]; has a - // total of three dimensions. This is now handled entirely by the - // Type class so this function is here for backwards compatability. - int arrays() - { - return var.type.arrays; - } - - // This is slightly messy. But what I'm trying to do IS slightly - // messy. - static bool hasType(TokenArray toks) - { - // Remove the type, if any - if(!Type.canParseRem(toks)) return false; - - // Skip any keywords - while(1) - { - if(isNext(toks, TT.Ref)) continue; - break; - } - - // There must be a variable identifier at the end - return isNext(toks, TT.Identifier); - } - - override void parse(ref TokenArray toks) - { - if(var is null) - { - var = new Variable; - - // Keywords may come before or after the type - parseKeywords(toks); - - // Parse the type, if any. - if(!allowNoType || hasType(toks)) - { - var.type = Type.identify(toks); - - parseKeywords(toks); - } - } - // The type was already set externally. - else - { - assert(var.type !is null); - - // allowNoType is not used in these cases - assert(allowNoType == false); - } - - if(!isNext(toks, TT.Identifier, var.name)) - fail("Variable name must be identifier", toks); - - loc = var.name.loc; - - // Look for arrays after the variable name. - ExprArray arrayArgs = getArray(toks); - - /* We must append these arrays to the type, in the reverse - order. Eg. - - int[1][2] i[4][3]; - - is the same as - - int[1][2][3][4] i; - - (also the same as int i[4][3][2][1];) - - Since we don't take expressions here yet the order is - irrelevant, but let's set it up right. - */ - foreach_reverse(e; arrayArgs) - { - assert(e is null); - var.type = new ArrayType(var.type); - } - - // Does the variable have an initializer? - if(isNext(toks, TT.Equals)) - init = Expression.identify(toks); - } - - char[] toString() - { - char[] res = var.type.toString() ~ " " ~ var.name.str; - if(init !is null) - res ~= " = " ~ init.toString; - return res; - } - - bool isParam = false; - - // Special version only called from FuncDeclaration.resolve() - void resolveParam(Scope sc) - { - isParam = true; - resolve(sc); - } - - // Sets var.number. Only for function parameters. - void setNumber(int num) - { - assert(num<0, "VarDec.setNumber was given a positive num: " ~ .toString(num)); - assert(isParam); - var.number = num; - } - - // Calls resolve() for all sub-expressions - override void resolve(Scope sc) - { - var.type.resolve(sc); - - if(var.type.isReplacer) - var.type = var.type.getBase(); - - // Allow 'const' for function array parameters - if(isParam && var.type.isArray()) - allowConst = true; - - // Handle initial value, if present - if(init !is null) - { - init.resolve(sc); - - // For now, var is disallowed for function parameters (will - // be enabled again when dynamic types are implemented.) - if(var.type.isVar && isParam) - fail("var type not allowed in parameters", var.type.loc); - - // If 'var' is present, just copy the type of the init value - if(var.type.isVar) - var.type = init.type; - else - { - // Convert type, if necessary. - var.type.typeCast(init, var.name.str); - } - assert(init.type == var.type); - } - - // If it's a struct, check that the variable is not part of - // itself. - if(var.type.isStruct) - { - auto st = cast(StructType)var.type; - if(st.sc is sc) - { - // We are inside ourselves - assert(sc.isStruct); - - fail("Struct variables cannot be declared inside the struct itself!", - loc); - } - } - - // We can't have var at this point - if(var.type.isVar) - fail("Cannot implicitly determine type", loc); - - // Nor can we have void types - if(var.type.isVoid) - fail("Cannot declare variables with no type", loc); - - // Illegal types are illegal - if(!var.type.isLegal) - { - char[] msg = "Cannot create variables of type '" ~ - var.type.toString ~ "'"; - - if(var.type.isIntFunc) - msg ~= "\nPerhaps you meant @func ? Use @ to create function references."; - - fail(msg, loc); - } - - if(!allowConst && var.isConst) - fail("'const' is not allowed here", loc); - - // Store the scope in the var struct for later referral. - var.sc = cast(VarScope)sc; - assert(var.sc !is null, "variables can only be declared in VarScopes"); - - if(!isParam) - // If we are not a function parameter, we must get - // var.number from the scope. - var.number = sc.addNewVar(var.type.getSize()); - else - assert(sc.isFunc()); - - // Insert ourselves into the scope. - var.sc.insertVar(var); - } - - int[] getCTimeValue() - out(res) - { - assert(res.length == var.type.getSize, "Size mismatch"); - } - body - { - // Does this variable have an initializer? - if(init !is null) - { - // And can it be evaluated at compile time? - if(!init.isCTime) - fail("Expression " ~ init.toString ~ - " is not computable at compile time", init.loc); - - return init.evalCTime(); - } - else - // Use the default initializer. - return var.type.defaultInit(); - } - - // Executed for local variables upon declaration. Push the variable - // on the stack. - void compile() - { - // Validate the type - var.type.validate(loc); - - setLine(); - - if(init !is null) - // Push the initializer - init.eval(); - else - // Default initializer - var.type.pushInit(); - } -} - -// Declaration that overrides a default variable value in a parent -// class. Only used at the class scope. Takes the form -// varname=expression; Is also used to set the default state. -class ClassVarSet : Block -{ - Token name; - Expression value; - Variable *var; - - StateStatement stateSet; - - // The class owning the variable. NOT the same as the class we're - // defined in. - MonsterClass cls; - - static bool canParse(TokenArray toks) - { - if(isNext(toks, TT.Identifier) && - isNext(toks, TT.Equals)) - return true; - if(isNext(toks, TT.State) && - isNext(toks, TT.Equals)) - return true; - - return false; - } - - bool isState() - { return stateSet !is null; } - - void parse(ref TokenArray toks) - { - if(isNext(toks, TT.Identifier, name)) - { - // Setting normal variable - reqNext(toks, TT.Equals); - value = Expression.identify(toks); - loc = name.loc; - } - else - { - // Setting the state - use a StateStatement to handle - // everything. - assert(toks.length >= 1 && toks[0].type == TT.State); - stateSet = new StateStatement; - stateSet.parse(toks); - loc = stateSet.loc; - } - reqSep(toks); - } - - void resolve(Scope sc) - { - // Get the class we're defined in - assert(sc.isClass); - auto lc = sc.getClass(); - assert(lc !is null); - - // Are we setting the state? - if(stateSet !is null) - { - assert(value is null); - - // Make stateSet find the state and label pointers - stateSet.resolve(sc); - - // Tell the class about us - StateLabel *lb = null; - if(stateSet.label !is null) - lb = stateSet.label.lb; - lc.setDefaultState(stateSet.stt, lb); - return; - } - - // We're setting a normal variable - assert(stateSet is null); - - // Find the variable - assert(lc.parents.length <= 1); - var = null; - - // TODO: If we do mi later, create a parentLookup which handles - // standard error cases for us. - if(lc.parents.length == 1) - { - auto ln = lc.parents[0].sc.lookup(name); - if(ln.isVar) - { - var = ln.var; - cls = ln.sc.getClass(); - } - } - - if(var is null) - { - // No var found. It's possible that there's a match in our - // own class though - that's still an error, but should give - // a slightly different error message - auto ln = lc.sc.lookup(name); - if(ln.isVar) - fail("Cannot override variable " ~ name.str ~ - " because it was defined in the same class", name.loc); - - fail("No variable named " ~ name.str ~ - " in parent classes of " ~ lc.name.str, name.loc); - } - - assert(var.vtype == VarType.Class); - assert(cls !is null); - assert(cls.parentOf(lc)); - value.resolve(sc); - - // Convert the type - var.type.typeCast(value, var.name.str); - - if(!value.isCTime) - fail("Expression " ~ value.toString ~ " cannot be computed at compile time", value.loc); - } - - // Apply this data change to the given data segment - void apply(int[] data) - { - assert(!isState); - - assert(var !is null); - assert(cls.dataSize == data.length); - assert(var.number >= 0); - assert(var.number+var.type.getSize <= data.length); - - // Copy the data - int start = var.number; - int end = var.number + var.type.getSize; - data[start..end] = value.evalCTime(); - } - - char[] toString() - { - char[] res; - if(cls !is null) - res = cls.getName() ~ "."; - if(isState) - { - res ~= "state=" ~ stateSet.stateName.str; - if(stateSet.labelName.str != "") - res ~= "." ~ stateSet.labelName.str; - } - else - res ~= name.str ~ "=" ~ value.toString; - return res; - } -} - -// Represents a reference to an identifier member, such as a variable, -// function or property. Can also refer to type names. Simply stores -// the token representing the identifier. Special names (currently -// only __STACK__) are also handled. -class MemberExpr : Expression -{ - Token name; - - ScopeLookup look; - - // Used to simulate a member for imported variables - DotOperator dotImport; - bool recurse = true; - - enum VType - { - None, // Should never be set - LocalVar, // Local variable - ThisVar, // Variable in this object and this class - ParentVar, // Variable in another class but this object - FarOtherVar, // Another class, another object - Property, // Property (like .length of arrays) - Special, // Special name (like __STACK__) - Function, // Function - Type, // Typename - Package, // Package - } - - VType vtype; - - CIndex singCls = -1; // Singleton class index - - static bool canParse(TokenArray toks) - { - return - isNext(toks, TT.Identifier) || - isNext(toks, TT.Singleton) || - isNext(toks, TT.State) || - isNext(toks, TT.Clone) || - isNext(toks, TT.Var) || - isNext(toks, TT.Const); - } - - // Does this variable name refer to a type name rather than an - // actual variable? TODO: Could be swapped for "requireStatic" or - // similar in expression instead. - bool isType() { return type.isMeta(); } - - bool isProperty() - out(res) - { - if(res) - { - assert(vtype == VType.Property); - assert(!isSpecial); - } - else assert(vtype != VType.Property); - } - body - { - return look.isProperty(); - } - - bool isSpecial() { return vtype == VType.Special; } - bool isPackage() { return vtype == VType.Package; } - - // Is this a static member - bool isStatic() - { - // Properties can be static - if(isProperty) - return look.isPropStatic; - - // Type names are always static. - if(isType) - return true; - - // Singletons are also static members (of packages), even if the - // type is not a meta type - if(singCls != -1) - return true; - - // Ditto for packages - if(isPackage) - return true; - - // Currently no other static variables - return false; - } - - void resolveMember(Scope sc, Type ownerType) - { - assert(ownerType !is null); - assert(sc !is null); - - Scope leftScope; - - leftScope = ownerType.getMemberScope(); - - // Look up the name in the scope belonging to the owner - assert(leftScope !is null); - look = leftScope.lookupClass(name); - - type = look.type; - - // Check for member properties - if(look.isProperty) - { - // TODO: Need to account for ownerType here somehow - - // rewrite the property system - vtype = VType.Property; - type = look.getPropType(ownerType); - return; - } - - // The rest is common - resolveCommon(ownerType, leftScope); - } - - // Common parts for members and non-members - void resolveCommon(Type ownerType, Scope sc) - { - bool isMember = (ownerType !is null); - - // Package name? - if(look.isPackage) - { - vtype = VType.Package; - return; - } - - // Variable? - if(look.isVar) - { - assert(look.sc !is null); - assert(look.sc is look.var.sc); - - if(isMember) - { - // We are a class member variable. - vtype = VType.FarOtherVar; - assert(look.sc.isClass); - } - // This/parent class variable? - else if(look.sc.isClass) - { - // Check if it's in THIS class, which is a common - // case. If so, we can use a simplified instruction that - // doesn't have to look up the class. - if(look.sc.getClass is sc.getClass) - vtype = VType.ThisVar; - else - { - // It's another class. For non-members this can only - // mean a parent class. - vtype = VType.ParentVar; - } - } - else - // Local stack variable - vtype = VType.LocalVar; - } - - // Function? - else if(look.isFunc) - { - // The Function* is stored in the lookup variable - type = new IntFuncType(look.func, isMember); - vtype = VType.Function; - - // TODO: Make the scope set the type for us. In fact, the - // type should contain the param/return type information - // rather than the Function itself, the function should just - // refer to it. This would make checking type compatibility - // easy. - } - // Class name? - else if(look.isClass) - { - if(isMember && !ownerType.isPackage) - fail(format("%s cannot have class member %s", - ownerType, name), loc); - - assert(look.mc !is null); - look.mc.requireScope(); - - type = look.mc.classType; - vtype = VType.Type; - - // Singletons are treated differently - the class name can - // be used to access the singleton object - if(look.mc.isSingleton) - { - type = look.mc.objType; - singCls = look.mc.getIndex(); - } - } - // Type name? - else if(look.isType) - { - assert(look.isType); - assert(look.type !is null); - type = look.type.getMeta(); - vtype = VType.Type; - } - else - { - // Nothing useful was found. - if(isMember) - fail(name.str ~ " is not a member of " ~ ownerType.toString, - loc); - else - fail("Unknown identifier "~name.str, name.loc); - } - } - - override: - char[] toString() { return name.str; } - - // Ask the variable if we can write to it. - bool isLValue() - { - // Specials are read only - if(isSpecial) - return false; - - // Properties may or may not be changable - if(isProperty) - return look.isPropLValue; - - // Normal variables are always lvalues. - return true; - } - - bool isCTime() { return isType; } - - void parse(ref TokenArray toks) - { - name = next(toks); - loc = name.loc; - } - - void resolve(Scope sc) - out - { - // Some sanity checks on the result - if(look.isVar) - { - assert(look.var.sc !is null); - assert(!isProperty); - } - assert(type !is null); - assert(vtype != VType.None); - } - body - { - static if(traceResolve) - writefln("Resolving member expression %s", this); - - // Look for reserved names first. - if(name.str == "__STACK__") - { - vtype = VType.Special; - type = BasicType.getInt; - return; - } - - if(name.type == TT.Const || name.type == TT.Clone) - fail("Cannot use " ~ name.str ~ " as a variable", name.loc); - - if(name.type == TT.Var) - { - type = GenericType.getSingleton(); - vtype = VType.Special; - return; - } - - // Look ourselves up in the local scope, and include imported - // scopes. - look = sc.lookupImport(name); - - if(look.isImport) - { - // We're imported from another scope. This means we're - // essentially a member variable. Let DotOperator handle - // this. - dotImport = new DotOperator(look.imphold, this, loc); - dotImport.resolve(sc); - assert(dotImport.type is type); - return; - } - - // These are special cases that work both as properties - // (object.state) and as non-member variables (state=...) inside - // class functions / state code. Since we already handle them - // nicely as properties, treat them as properties even if - // they're not members. - if(name.type == TT.Singleton || name.type == TT.State) - { - if(!sc.isInClass) - fail(name.str ~ " can only be used in classes", name.loc); - - assert(look.isProperty, name.str ~ " expression not implemented yet"); - - vtype = VType.Property; - assert(0); // FIXME: This can't be right!? Was ownerType. - type = look.getPropType(null); - return; - } - - type = look.type; - - resolveCommon(null, sc); - } - - int[] evalCTime() - { - assert(isCTime); - assert(isType); - assert(type.getSize == 0); - return null; - } - - void evalAsm() - { - if(type.isVar) - fail("Cannot use 'var' as an expression", loc); - - // Hairy. But does the trick for now. - if(dotImport !is null && recurse) - { - recurse = false; - dotImport.evalAsm(); - return; - } - - if(isType) return; - - // If we're a function name, don't push anything. Everything is - // handled through the type system. - if(type.isIntFunc) return; - - setLine(); - - // Special name - if(isSpecial) - { - if(name.str == "__STACK__") - tasm.getStack(); - else assert(0, "Unknown special name " ~ name.str); - return; - } - - // Property - if(isProperty) - { - look.getPropValue(); - return; - } - - // Class singleton name - if(singCls != -1) - { - assert(type.isObject); - - // Convert the class index into a object index at runtime - tasm.pushSingleton(singCls); - return; - } - - // Normal variable - - int s = type.getSize; - auto var = look.var; - - if(vtype == VType.LocalVar) - // This is a variable local to this function. The number gives - // the stack position. - tasm.pushLocal(var.number, s); - - else if(vtype == VType.ThisVar) - // The var.number gives the offset into the data segment in - // this class - tasm.pushClass(var.number, s); - - else if(vtype == VType.ParentVar) - // Variable in a parent but this object - tasm.pushParentVar(var.number, var.sc.getClass().getTreeIndex(), s); - - else if(vtype == VType.FarOtherVar) - // Push the value from a "FAR pointer". The class index should - // already have been pushed on the stack by DotOperator, we - // only push the index. - tasm.pushFarClass(var.number, var.sc.getClass().getTreeIndex(), s); - - else assert(0, "called evalAsm on " ~ toString()); - } - - // Push the address of the variable rather than its value - void evalDest() - { - if(dotImport !is null && recurse) - { - recurse = false; - dotImport.evalDest(); - return; - } - - assert(!isType, "types can never be written to"); - assert(isLValue()); - assert(!isProperty); - - setLine(); - - // No size information is needed for addresses. - auto var = look.var; - - if(vtype == VType.LocalVar) - tasm.pushLocalAddr(var.number); - else if(vtype == VType.ThisVar) - tasm.pushClassAddr(var.number); - else if(vtype == VType.ParentVar) - tasm.pushParentVarAddr(var.number, var.sc.getClass().getTreeIndex()); - else if(vtype == VType.FarOtherVar) - tasm.pushFarClassAddr(var.number, var.sc.getClass().getTreeIndex()); - - else assert(0); - } - - void store() - { - if(dotImport !is null && recurse) - { - recurse = false; - dotImport.store(); - return; - } - - assert(isLValue); - - if(isProperty) - look.setPropValue(); - else - { - assert(look.isVar); - auto var = look.var; - - // Get the destination and move the data - evalDest(); - tasm.mov(type.getSize()); - - assert(var.sc !is null); - if(var.isRef) - // TODO: This assumes all ref variables are foreach values, - // which will probably not be true in the future. - tasm.iterateUpdate(var.sc.getLoopStack()); - } - } - - void incDec(TT op, bool post) - { - if(dotImport !is null && recurse) - { - recurse = false; - dotImport.incDec(op, post); - return; - } - - if(!isProperty) - { - assert(look.isVar); - auto var = look.var; - - super.incDec(op, post); - - assert(var.sc !is null); - if(var.isRef) - tasm.iterateUpdate(var.sc.getLoopStack()); - } - else fail("Cannot use ++ and -- on properties yet", loc); - } -} - -// A variable declaration that works as a statement. Supports multiple -// variable declarations, ie. int i, j; but they must be the same -// type, so int i, j[]; is not allowed. -class VarDeclStatement : Statement -{ - VarDeclaration[] vars; - bool reqSemi; - Type preKnownType = null; - - // Pass 'true' to the constructor to require a semi-colon (used eg - // in for loops) - this(bool rs=false) - { - reqSemi = rs; - } - - // Used from the console, when the type is already known - this(Type type) - { - preKnownType = type; - } - - static bool canParse(TokenArray toks) - { - if(Type.canParseRem(toks) && - isNext(toks, TT.Identifier)) - return true; - return false; - } - - void parse(ref TokenArray toks) - { - VarDeclaration varDec; - - if(preKnownType !is null) - varDec = new VarDeclaration(preKnownType); - else - varDec = new VarDeclaration; - - varDec.parse(toks); - vars ~= varDec; - loc = varDec.var.name.loc; - - // Are there more? - while(isNext(toks, TT.Comma)) - { - // Read a variable, but with the same type as the last - varDec = new VarDeclaration(varDec.var.type); - varDec.parse(toks); - vars ~= varDec; - } - - if(reqSemi) - reqNext(toks, TT.Semicolon); - else - reqSep(toks); - } - - char[] toString() - { - char[] res = "Variable declaration: "; - foreach(vd; vars) res ~= vd.toString ~" "; - return res; - } - - void resolve(Scope sc) - { - if(sc.isStateCode()) - fail("Variable declarations not allowed in state code", loc); - - // Add variables to the scope. - foreach(vd; vars) - vd.resolve(sc); - - // Check that all the vars are in fact the same type - assert(vars.length >= 1); - Type t = vars[0].var.type; - foreach(vd; vars[1..$]) - { - if(t != vd.var.type) - fail("Multiple declarations must have the same type", loc); - } - } - - // Validate types - void validate() - { - assert(vars.length >= 1); - vars[0].var.type.validate(loc); - } - - // Insert local variable(s) on the stack. - void compile() - { - // Compile the variable declarations, they will push the right - // values to the stack. - foreach(vd; vars) - vd.compile(); - } -} diff --git a/old_d_version/monster/modules/all.d b/old_d_version/monster/modules/all.d deleted file mode 100644 index ab3892e96..000000000 --- a/old_d_version/monster/modules/all.d +++ /dev/null @@ -1,58 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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(); -} diff --git a/old_d_version/monster/modules/console.d b/old_d_version/monster/modules/console.d deleted file mode 100644 index 8818a2b2a..000000000 --- a/old_d_version/monster/modules/console.d +++ /dev/null @@ -1,553 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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(); - } -} diff --git a/old_d_version/monster/modules/frames.d b/old_d_version/monster/modules/frames.d deleted file mode 100644 index 1295de80c..000000000 --- a/old_d_version/monster/modules/frames.d +++ /dev/null @@ -1,112 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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"); -} diff --git a/old_d_version/monster/modules/frames.mn b/old_d_version/monster/modules/frames.mn deleted file mode 100644 index 5fc5889a8..000000000 --- a/old_d_version/monster/modules/frames.mn +++ /dev/null @@ -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); diff --git a/old_d_version/monster/modules/io.d b/old_d_version/monster/modules/io.d deleted file mode 100644 index 94b591764..000000000 --- a/old_d_version/monster/modules/io.d +++ /dev/null @@ -1,92 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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(); }); -} diff --git a/old_d_version/monster/modules/io.mn b/old_d_version/monster/modules/io.mn deleted file mode 100644 index 0bba29192..000000000 --- a/old_d_version/monster/modules/io.mn +++ /dev/null @@ -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...); diff --git a/old_d_version/monster/modules/math.d b/old_d_version/monster/modules/math.d deleted file mode 100644 index b7ac33831..000000000 --- a/old_d_version/monster/modules/math.d +++ /dev/null @@ -1,166 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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)); -} diff --git a/old_d_version/monster/modules/math.mn b/old_d_version/monster/modules/math.mn deleted file mode 100644 index eeb71923a..000000000 --- a/old_d_version/monster/modules/math.mn +++ /dev/null @@ -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); diff --git a/old_d_version/monster/modules/random.d b/old_d_version/monster/modules/random.d deleted file mode 100644 index a99f0ee22..000000000 --- a/old_d_version/monster/modules/random.d +++ /dev/null @@ -1,71 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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= 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)); }); -} diff --git a/old_d_version/monster/modules/random.mn b/old_d_version/monster/modules/random.mn deleted file mode 100644 index 273df93a8..000000000 --- a/old_d_version/monster/modules/random.mn +++ /dev/null @@ -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 - 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); -} diff --git a/old_d_version/monster/modules/timer.d b/old_d_version/monster/modules/timer.d deleted file mode 100644 index 5eabd7729..000000000 --- a/old_d_version/monster/modules/timer.d +++ /dev/null @@ -1,190 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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); - } -} diff --git a/old_d_version/monster/modules/timer.mn b/old_d_version/monster/modules/timer.mn deleted file mode 100644 index 2afb9b62c..000000000 --- a/old_d_version/monster/modules/timer.mn +++ /dev/null @@ -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); diff --git a/old_d_version/monster/modules/vfs.d b/old_d_version/monster/modules/vfs.d deleted file mode 100644 index 1d7dfa7fb..000000000 --- a/old_d_version/monster/modules/vfs.d +++ /dev/null @@ -1,183 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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)); } -} diff --git a/old_d_version/monster/monster.d b/old_d_version/monster/monster.d deleted file mode 100644 index 2f5b38a73..000000000 --- a/old_d_version/monster/monster.d +++ /dev/null @@ -1,42 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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."); diff --git a/old_d_version/monster/options.d b/old_d_version/monster/options.d deleted file mode 100644 index 6602b2067..000000000 --- a/old_d_version/monster/options.d +++ /dev/null @@ -1,192 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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; diff --git a/old_d_version/monster/options.openmw b/old_d_version/monster/options.openmw deleted file mode 100644 index 6602b2067..000000000 --- a/old_d_version/monster/options.openmw +++ /dev/null @@ -1,192 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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; diff --git a/old_d_version/monster/update.sh b/old_d_version/monster/update.sh deleted file mode 100755 index 7254162fc..000000000 --- a/old_d_version/monster/update.sh +++ /dev/null @@ -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 - diff --git a/old_d_version/monster/util/aa.d b/old_d_version/monster/util/aa.d deleted file mode 100644 index adefce0c0..000000000 --- a/old_d_version/monster/util/aa.d +++ /dev/null @@ -1,1033 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (aa.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.aa; - -private import std.string; -private import std.c.stdlib; - -alias malloc cmalloc; -alias free cfree; - -typedef void GCAlloc; -typedef void DefHash; - -class HashTableException : Exception -{ - this(char[] msg) - { - super(msg); - } -} - -/* - * Internal structure used by HashTable - */ - -struct _aaNode(Key, Value) -{ - uint hash; // Hash of this value - Key key; - Value value; - - _aaNode* next; // Next node in current bucket - _aaNode* prev; // Previous node - - _aaNode* nextCont; // Next node in the entire container - _aaNode* prevCont; // Previous node -} - -/* - * This hash table is actually a doubly linked list with added lookup - * capabilities. I could rewrite this to use LinkedList. - * - * The key type is assumed to be inexpensive to copy. For large key - * types use pointers and a custom hasher. Large value types will work - * fine, but you should probably use inList, getPtr and insertEdit for - * most lookup operations, since these return pointers instead of by - * value. - * - * 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 void* alloc(uint size) { return std.c.stdlib.malloc(size); } - * static void free(void* p) { std.c.stdlib.free(p); } - * } - * - * - * Hash must contain: - * uint hash(Key k) - * int isEqual(Key a, Key b) - * - * Example integer hasher that ignores sign (ie. treats n and -n as - * the same number): - * - * struct IgnoreSign - * { - * static uint hash(int i) { return i<0 ? -i : i; } - * static int isEqual(int i, int j) { return (i==j) || (i==-j); } - * } - * - * The keyFormat is used when constructing error messages. If set to - * true, it is assumed that format("%s", key) will work. If set to - * false, no attempt is made at converting the key to a string. - * - * Notice that the Hash functions are type specific, while the Alloc - * ones are not. - */ - -struct HashTable(Key, Value, Alloc = GCAlloc, Hash = DefHash, - bool keyFormat = true) -{ - private: - alias _aaNode!(Key, Value) Node; - - Node *head; // Nodes form a linked list, this is the head. - Node *tail; // New nodes are inserted here - - Node* array[]; // The hash table array - - uint bitmask; // The array size is a power of two, we perform a - // bitwise AND between the hash and this bitmask to - // look up in the array. - - uint totalNum;// Number of nodes currently in container - - // What size to choose if the user doesn't specify one. - static const int defaultSize = 500; - - // Max allowed array size. 25 bits corresponds to about 34 million - // entries. - static const int maxBit = 25; - static const uint maxSize = 1 << maxBit; - - // Minimum allowed size. 6 bits corresponds to 64 entries. - static const int minBit = 6; - - // Assumed maximum bucket size, used as a safeguard against infinite - // loops. - static const int bucketMax = 100000; - - // 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; - - // Throw an exception - void fail(char[] msg) - { - msg = format("HashTable!(%s,%s) exception: %s", - typeid(Key).toString, typeid(Value).toString, msg); - throw new HashTableException(msg); - } - - void fail(char[] msg, Key k) - { - static if(keyFormat) fail(format(msg, k)); - // Ignore key if we cannot display it - else fail(format(msg, "(unshowable)")); - } - - public: - - void reset() - { - // This stopped working in DMD 1.032 and later versions. - //*this = typeof(*this).init; - // But this seems to do the trick - *this = (HashTable!(Key, Value, Alloc, Hash, keyFormat)).init; - } - - // Returns number of buckets of each size. Mostly used for testing - // efficiency of hash functions. - int[] getStats() - { - int[] res; - - foreach(Node *n; array) - { - // Get bucket size - uint size = 0; - while(n != null) - { - size++; - n = n.next; - } - - if(size >= res.length) res.length = size+1; - - // Add this bucket size to the stat - res[size]++; - } - return res; - } - //*/ - - // Loop through all values and check that the stored hash values are - // correct. Mostly used for debugging. - // - // For example, a common problem is to use a string as a key, and - // then change the string afterwards. The HashTable does not store - // the string contents (only the reference char[]), so if you change - // the string contents later then there will be a mismatch between - // the key and the hashed value. This function will detect the - // problem. - void validate() - { - Node *p = head; - uint safeGuard = 0, hsh; - while(p != null) - { - assert(safeGuard++ < totalNum); - - static if(is(Hash == DefHash)) hsh = typeid(Key).getHash(&p.key); - else hsh = Hash.hash(p.key); - - if(hsh != p.hash) - fail("Validation failed");// on key %s", p.key); - - p = p.nextCont; - } - } - - // Look up a value. If found return true and set v to value, - // otherwise return false. - bool inList(Key k, ref Value v) - { - Node *p = lookupKey(k); - if(p) v = p.value; - return p != null; - } - - // Look up a value. If found return true, otherwise false. - bool inList(Key k) { return lookupKey(k) != null; } - - // Look up a value. If found return pointer, otherwise null. - Value* lookup(Key k) - { - Node *p = lookupKey(k); - if(p) return &p.value; - return null; - } - - // Get a value, throw an exception if it does not exist - Value get(Key k) - { - Node *p = lookupKey(k); - if(!p) fail("Cannot get key '%s', not found", k); - return p.value; - } - Value opIndex(Key k) { return get(k); } - - // Get a pointer to a value, throw an exception if it does not exist - Value* getPtr(Key k) - { - Node *p = lookupKey(k); - if(!p) fail("Cannot get key '%s', not found", k); - return &p.value; - } - - // Get a value, insert a new if it doesn't exist. This is handy for - // situations where the inserted values might or might not exist, - // and you don't want to overwrite the existing value. - Value get(Key k, Value def) - { - Node *p; - uint hash; - if(!lookupKey(k,hash,p)) - { - // Insert a node - p = insertNode(hash,p); - p.key = k; - p.value = def; - } - - return p.value; - } - Value opIndex(Key k, Value v) { return get(k, v); } - - // Get a value. If it doesn't exist, insert a new one and call - // apply(). Used in the same situation as get(Key, Value) but when - // setting up the new value is expensive or otherwise to be avoided - // when not needed. Handy for classes or when you wish to do - // in-place editing. See also insertEdit. - Value get(Key k, void delegate(ref Value v) apply) - { - Node *p; - uint hash; - if(!lookupKey(k,hash,p)) - { - // Insert a node and call apply - p = insertNode(hash,p); - p.key = k; - apply(p.value); - } - - return p.value; - } - - // Gets the stored key associated with the given key. This seemingly - // useless function is handy for custom hashers that collapse - // several values into one, for example the case insensitive string - // hasher. getKey will return the key as it was originally inserted - // into the list. In the case of reference types (such as arrays) it - // can also be used for referencing the original data. - Key getKey(Key k) - { - Node *p = lookupKey(k); - if(!p) fail("Cannot get key '%s', not found", k); - return p.key; - } - - // Insert a new value, replace it if it already exists. Returns v. - Value insert(Key k, Value v) - { - Node *p; - uint hash; - if(lookupKey(k, hash, p)) - // Key already existed, overwrite value. - p.value = v; - else - { - // Insert a new node - p = insertNode(hash, p); - p.key = k; - p.value = v; - } - - return v; - } - Value opIndexAssign(Value v, Key k) { return insert(k, v); } - - // Get a pointer to value of given key, or insert a new. Useful for - // large value types when you want to avoid copying the value around - // needlessly. Also useful if you want to do in-place - // editing. Returns true if a value was inserted. - bool insertEdit(Key k, out Value *ptr) - { - Node *p; - uint hash; - if(!lookupKey(k,hash,p)) - { - // Insert it - p = insertNode(hash,p); - p.key = k; - ptr = &p.value; - return true; - } - ptr = &p.value; - return false; - } - - // TODO: No unittests for *ifReplace and replace yet - - // Insert a value. Return true if it replaced an existing value, - // otherwise false. - bool ifReplace(Key k, Value v) - { - Node *p; - uint hash; - if(!lookupKey(k,hash,p)) - { - // Insert node - p = insertNode(hash,p); - p.key = k; - p.value = v; - return false; // We did not replace anything, return false - } - // A node was already found - p.value = v; - return true; // We replaced a value - } - - // Replace an existing value with v, return the old value. Fail if - // no value existed. - Value replace(Key k, Value v) - { - Node *p = lookupKey(k); - if(p) - { - Value old = p.value; - p.value = v; - return old; - } - fail("Failed to replace key '%s', not found", k); - assert(0); - } - - // Remove key, return value. If key does not exist, throw an - // exception. - Value remove(Key k) - { - Value v; - if(!ifRemove(k, v)) - fail("Cannot remove key '%s', key not found", k); - - return v; - } - - // Remove a key if it exists. If it was removed, return true, - // otherwise false. - bool ifRemove(Key k) - { - Value v; - return ifRemove(k, v); - } - - // Remove a key if it exists. If it existed, returns true and places - // removed value in v. If not, return false; - bool ifRemove(Key k, ref Value v) - { - Node *p = lookupKey(k); - if(!p) return false; - - v = p.value; - removeNode(p); - return true; - } - - // Loop through the nodes in the order they were inserted - int opApply(int delegate(ref Key k, ref Value v) del) - { - Node *p = head; - uint safeGuard = 0; - while(p != null) - { - assert(safeGuard++ < totalNum); - int i = del(p.key, p.value); - if(i) return i; - p = p.nextCont; - } - return 0; - } - - // Same as above, but do not take Key as a parameter. - 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.nextCont; - } - return 0; - } - - // Number of elements - uint length() { return totalNum; } - - // Table size - uint tabSize() { return array.length; } - - // Rehash the array, ie. resize the array and reinsert the - // members. The parameter gives the requested number of elements. If - // it is zero (or omitted), current number of elements is used - // instead. This function never shrinks the array, use rehashShrink - // for that. - void rehash(uint size = 0) - { - if(size == 0) size = totalNum; - - // Do we need a rehash? - if(size > array.length) - // Let's do it. - rehashShrink(size); - } - - // Same as rehash, but allows the array to shrink - void rehashShrink(uint size = 0) - { - if(size == 0) size = totalNum; - - killArray(); - allocArray(size); - - Node *p = head; - uint safeGuard = 0; - while(p != null) - { - assert(safeGuard++ < totalNum); - rehashNode(p); - p = p.nextCont; - } - } - - // Place a node in the table. Assumes p.hash to be set. Does no - // equality check and does not touch the main linked list. - private void rehashNode(Node *p) - { - Node *n = array[p.hash & bitmask]; - int safeGuard = 0; - - p.next = null; - //p.top = p.bottom = null; - - if(n == null) - { - // We're all alone in this cold empty bucket - array[p.hash & bitmask] = p; - p.prev = null; - return; - } - - // Bucket is occupied - - // Find the last element - while(n.next != null) - { - n = n.next; - assert(safeGuard++ < bucketMax); - } - - // Place p at the end of the list - p.prev = n; - assert(n.next == null); - n.next = p; - } - - private void removeNode(Node *p) - { - // Remove from bucket - if(p.next) p.next.prev = p.prev; - if(p.prev) p.prev.next = p.next; - else // We're at the start of the bucket, remove from hash table - array[p.hash & bitmask] = p.next; - - // Remove from main list - if(p.nextCont) p.nextCont.prevCont = p.prevCont; - else // We're the tail - { - assert(tail == p); - tail = p.prevCont; - } - if(p.prevCont) p.prevCont.nextCont = p.nextCont; - else // We're head - { - assert(head == p); - head = p.nextCont; - } - - // Finally, delete the node. For the GC, just release the - // pointer into the wild. - static if(!is(Alloc == GCAlloc)) Alloc.free(p); - - totalNum--; - } - - // Allocate a new node and inserts it into the main list. Places it - // in the hash array as a successor of the given parent node. If - // parent is null, insert directly into the array using the given - // hash. Returns the newly inserted node. - private Node *insertNode(uint hash, Node *parent) - { - // First, make the new node. - static if(is(Alloc == GCAlloc)) - Node *p = new Node; - else - Node *p = cast(Node*)Alloc.alloc(Node.sizeof); - - p.hash = hash; - - // Is the bucket already occupied? - if(parent) - { - parent.next = p; - p.prev = parent; - } - else - { - // Nope, start our very own bucket. - assert(array[hash & bitmask] == null); - array[hash & bitmask] = p; - static if(!autoinit) p.prev = null; - } - - // Initialize pointers - static if(!autoinit) - { - p.next = null; - p.nextCont = null; - } - - // Insert node into the main linked list - if(tail) - { - assert(head != null); - tail.nextCont = p; - p.prevCont = tail; - } - else - { - // This is the first element to be inserted - assert(head == null); - head = p; - static if(!autoinit) p.prevCont = null; - } - - tail = p; - - totalNum++; - - // I thiiiink it's time to rehash - if(totalNum > 5*array.length) - rehash(); - - return p; - } - - /* Looks up k in the hash table. Returns Node pointer if found, null - * otherwise. This is identical in function to the other lookupKey - * below, except that it returns less information and uses - * marginally fewer instructions. - */ - private Node* lookupKey(Key k) - { - // Check if the array is initialized. - if(!array.length) return null; - - static if(is(Hash == DefHash)) - // Use TypeInfo.getHash to hash the key - uint hsh = typeid(Key).getHash(&k); - else - // Use provided hash function - uint hsh = Hash.hash(k); - - // Look up in the table - Node* p = array[hsh & bitmask]; - - // Search the bucket - int safeGuard = 0; - while(p) - { - assert(safeGuard++ < bucketMax); - - // First check if the hashes match - if(hsh == p.hash) - { - // They did, check if this is the correct one - static if(is(Hash == DefHash)) - { - // Use type specific compare - // This doesn't call opEquals. Fixed. - //if(!typeid(Key).compare(&k, &p.key)) return p; - if(k == p.key) return p; - } - else - { - // Use supplied compare - if(Hash.isEqual(k, p.key)) return p; - } - } - - // Next element - p = p.next; - } - - // End of the list, no node found - return null; - } - - /* Looks up k in the hash table. This is used internally in - * conjuntion with insertNode(). It has three outcomes: - * - * 1 - Key was found. Returns true. Ptr points to corresponding - * Node. - * - * 2 - Key was not found, but bucket is not empty. Returns - * false. Ptr points to last node in bucket list. - * - * 3 - Key was not found, bucket is empty. Returns false, ptr is - * null. - * - * Hash is in any case set to the correct hashed value of k. - */ - private bool lookupKey(Key k, ref uint hash, ref Node* ptr) - { - // Make sure the array is initialized. We do this here instead - // of in insertNode, because this function is always called - // first anyway. - if(!array.length) allocArray(defaultSize); - - static if(is(Hash == DefHash)) - // Use TypeInfo.getHash to hash the key - uint hsh = typeid(Key).getHash(&k); - else - // Use provided hash function - uint hsh = Hash.hash(k); - - // Return the value - hash = hsh; - - // Look up in the table - Node* p = array[hsh & bitmask]; - - // Bucket is empty - if(p == null) - { - ptr = null; - return false; - } - - // Search the bucket - int safeGuard = 0; - while(true) - { - assert(safeGuard++ < bucketMax); - - // First check if the hashes match - if(hsh == p.hash) - { - // They did, check if this is the One True Node - static if(is(Hash == DefHash)) - { - // Use type specific compare - //if(!typeid(Key).compare(&k, &p.key)) - if(k == p.key) - { - ptr = p; - return true; - } - } - else - { - // Use supplied compare - if(Hash.isEqual(k, p.key)) - { - ptr = p; - return true; - } - } - } - - // Break of at the end of the list - if(p.next == null) break; - - p = p.next; - } - - // Node was not found - ptr = p; - return false; - } - - // Deallocates the hash table. Does not touch the nodes. - private void killArray() - { - if(array == null) return; - - static if(!is(Alloc == GCAlloc)) - Alloc.free(array.ptr); - - // With the GC we just leave it dangeling in the wind. - array = null; - } - - // Allocates a new hash array, the old pointer is overwritten. The - // parameter gives the requested number of elements, the actual - // size will most likely be larger, since we must use a power of 2. - private void allocArray(uint number) - out - { - assert( array.length == bitmask+1 ); - assert( array.length != 0 ); - } - body - { - if(number > maxSize) number = maxSize; - - for(uint highBit = minBit; highBit < maxBit+1; highBit++) - { - uint len = 1 << highBit; - if(number < len) - { - static if(is(Alloc == GCAlloc)) array = new Node*[len]; - else - { - Node** p = cast(Node**)Alloc.alloc(len*(Node*).sizeof); - array = p[0..len]; - - // Initialize memory if we have to - static if(!Alloc.autoinit) - array[] = null; - } - bitmask = len-1; // Set all bits below highBit. - return; - } - } - assert(0); - } -} - -// Allocator using Malloc -struct Malloc -{ - static const bool autoinit = false; // malloc does not initialize memory - static void* alloc(uint size) { return cmalloc(size); } - static void free(void* p) { cfree(p); } -} - -// Simple but fast hash function for strings. Speed-wise on par with -// using the default hasher DefHash. Both are faster than the DMD -// built-in AAs. Use this as a base if you need to make special -// variations, such as the CITextHash hasher below. -struct SimpleTextHash -{ - static int isEqual(char[] a, char[] b) - { return !typeid(char[]).compare(&a, &b); } - - static uint hash(char[] s) - { - uint hash; - foreach (char c; s) hash = (hash * 37) + c; - return hash; - } -} - -// Case insensitive hash function, almost as fast as the above. -struct CITextHash -{ - static const char conv = 'a'-'A'; - - static int isEqual(char[] a, char[] b) - { return !icmp(a,b); } - - static uint hash(char[] s) - { - uint hash; - foreach (char c; s) - { - if(c <= 'Z' && c >= 'A') c += conv; - hash = (hash * 37) + c; - } - return hash; - } -} - -unittest -{ - // Test some basic template instants - alias HashTable!(int, int, Malloc) II; - alias HashTable!(char[], int, GCAlloc, SimpleTextHash) CI; - alias HashTable!(II,CI) COMPLEX; - - CI ci; - - int i = ci.tabSize(); - ci.rehash(); - assert(ci.tabSize == i); // A rehash should not do anything for so - // few values. - ci.rehashShrink(); - assert(ci.tabSize != i); // But it does if we allow it to shring - - // Lookup before the list is created - assert(!ci.inList("abcd", i)); - - // Assign some values and test - ci["Hei"] = 10; - ci["Hopp,"] = -34; - assert(ci.insert("Kall2", 5) == 5); - assert((ci["Kall2"] = 20) == 20); // Overwrite value - assert(ci.length == 3); - - // Test inList - assert(ci.inList("Hei")); - assert(ci.inList("Hopp,",i)); - assert(i == -34); - assert(!ci.inList("hei")); - - // Test default values - assert(ci["Kall2"] == 20); - assert(ci["Kall2", 123] == 20); - assert(ci["aa", 13] == 13); - assert(ci["aa", 31] == 13); - assert(ci["aa"] == 13); - - // Get a pointer - int *ip; - int *ip2; - assert(ci.insertEdit("bb", ip) == true); - *ip = 3; - assert(ci.insertEdit("bb", ip2) == false); - assert(ip == ip2); - *ip2 = 4; - assert(ci["bb"] == 4); - - // opApply - assert(ci.length == 5); - const char[][] str = ["Hei", "Hopp,", "Kall2", "aa", "bb"]; - const int[] ia = [10, -34, 20, 13, 4]; - i = 0; - foreach(char[] key, int val; ci) - { - assert(key == str[i]); - assert(val == ia[i]); - i++; - } - - ci.rehash(1000); - assert(ci.tabSize > 1000); - - // Remove elements - assert(!ci.ifRemove("arne")); // Remove something that never was there - - assert(ci.ifRemove("Hei", i)); // Remove from head - assert(i == 10); - assert(!ci.ifRemove("Hei")); - - assert(ci.remove("bb") == 4); // Remove from the tail - assert(!ci.ifRemove("bb")); - - ci.remove("Kall2"); // Remove from the middle - assert(!ci.inList("Kall2")); - - assert(ci.length == 2); - i = 1; - foreach(char[] key, int val; ci) - { - assert(key == str[i]); - assert(val == ia[i]); - i+=2; - } - - // Test for exceptions - i = 0; - try ci["hei"]; - catch(HashTableException e) - { - i = -1; - } - assert(i == -1); - - try ci.remove("bb"); - catch(HashTableException e) - { - i = -5; - } - assert(i == -5); - ci.validate(); - - // Insert more elements than the array length, to force collisions - char[] s = (cast(char*)&i)[0..4]; - for(i = 1; i - 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)); -} diff --git a/old_d_version/monster/util/freelist.d b/old_d_version/monster/util/freelist.d deleted file mode 100644 index 0037cb402..000000000 --- a/old_d_version/monster/util/freelist.d +++ /dev/null @@ -1,401 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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); } -} diff --git a/old_d_version/monster/util/growarray.d b/old_d_version/monster/util/growarray.d deleted file mode 100644 index 91a6abab2..000000000 --- a/old_d_version/monster/util/growarray.d +++ /dev/null @@ -1,312 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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= 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= 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); -} diff --git a/old_d_version/monster/util/list.d b/old_d_version/monster/util/list.d deleted file mode 100644 index 1388b1689..000000000 --- a/old_d_version/monster/util/list.d +++ /dev/null @@ -1,641 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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) { } -} diff --git a/old_d_version/monster/util/string.d b/old_d_version/monster/util/string.d deleted file mode 100644 index 624bdf821..000000000 --- a/old_d_version/monster/util/string.d +++ /dev/null @@ -1,114 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2004, 2007-2009 Nicolay Korslund - Email: - 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; -} diff --git a/old_d_version/monster/vm/arrays.d b/old_d_version/monster/vm/arrays.d deleted file mode 100644 index 00d578c4f..000000000 --- a/old_d_version/monster/vm/arrays.d +++ /dev/null @@ -1,369 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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 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 - 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; - } -} diff --git a/old_d_version/monster/vm/dbg.d b/old_d_version/monster/vm/dbg.d deleted file mode 100644 index bb9f88e15..000000000 --- a/old_d_version/monster/vm/dbg.d +++ /dev/null @@ -1,162 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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 - 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"); -} diff --git a/old_d_version/monster/vm/fstack.d b/old_d_version/monster/vm/fstack.d deleted file mode 100644 index a86f07774..000000000 --- a/old_d_version/monster/vm/fstack.d +++ /dev/null @@ -1,389 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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; - } -} diff --git a/old_d_version/monster/vm/gc.d b/old_d_version/monster/vm/gc.d deleted file mode 100644 index f640da76f..000000000 --- a/old_d_version/monster/vm/gc.d +++ /dev/null @@ -1,26 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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; diff --git a/old_d_version/monster/vm/idlefunction.d b/old_d_version/monster/vm/idlefunction.d deleted file mode 100644 index fce8a6bb7..000000000 --- a/old_d_version/monster/vm/idlefunction.d +++ /dev/null @@ -1,81 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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()"); } -} diff --git a/old_d_version/monster/vm/init.d b/old_d_version/monster/vm/init.d deleted file mode 100644 index 8da4719c9..000000000 --- a/old_d_version/monster/vm/init.d +++ /dev/null @@ -1,150 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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(); -} diff --git a/old_d_version/monster/vm/iterators.d b/old_d_version/monster/vm/iterators.d deleted file mode 100644 index 0bea13bfa..000000000 --- a/old_d_version/monster/vm/iterators.d +++ /dev/null @@ -1,281 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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(); - } -} diff --git a/old_d_version/monster/vm/mclass.d b/old_d_version/monster/vm/mclass.d deleted file mode 100644 index cef1d5ebc..000000000 --- a/old_d_version/monster/vm/mclass.d +++ /dev/null @@ -1,1418 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - WWW: http://monster.snaptoad.com/ - - This file (mclass.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.mclass; - -import monster.compiler.functions; -import monster.compiler.types; -import monster.compiler.scopes; -import monster.compiler.tokenizer; -import monster.compiler.statement; -import monster.compiler.variables; -import monster.compiler.states; -import monster.compiler.structs; -import monster.compiler.block; -import monster.compiler.enums; - -import monster.vm.codestream; -import monster.vm.idlefunction; -import monster.vm.arrays; -import monster.vm.error; -import monster.vm.vm; -import monster.vm.stack; -import monster.vm.thread; -import monster.vm.mobject; - -import monster.util.flags; -import monster.util.string; -import monster.util.list; -import monster.util.freelist; - -import std.string; -import std.stdio; -import std.stream; - -typedef void *MClass; // Pointer to C++ equivalent of MonsterClass. - -typedef int CIndex; - -enum CFlags - { - None = 0x00, // Initial value - - Parsed = 0x01, // Class has been parsed - Scoped = 0x02, // Class has been inserted into the scope - Resolved = 0x04, // Class body has been resolved - Compiled = 0x08, // Class body has been compiled - InScope = 0x10, // We are currently inside the createScope - // function - Module = 0x20, // This is a module, not a class - Singleton = 0x40, // This is a singleton. Also set for modules. - Abstract = 0x80, // No objects can be created from this class - } - -// The class that handles 'classes' in Monster. -final class MonsterClass -{ - /*********************************************** - * * - * Static functions * - * * - ***********************************************/ - - static bool canParse(TokenArray tokens) - { - return - Block.isNext(tokens, TT.Class) || - Block.isNext(tokens, TT.Singleton) || - Block.isNext(tokens, TT.Abstract) || - Block.isNext(tokens, TT.Module); - } - - static uint getTotalObjects() { return allObjects.length; } - - // Sets up the Object class, which is the parent of all other - // classes. - static void initialize() - { - assert(baseObject is null, - "MonsterClass.initialize() run more than once"); - assert(global !is null); - - // The Object class is empty - baseObject = vm.loadString("abstract class Object;", "Object"); - assert(baseObject !is null); - - // Set up the class. createScope() etc will make a special case - // for us when it detects that it is running on baseObject. - baseObject.requireCompile(); - } - - private static MonsterClass baseObject; - - // Returns the class called 'Object' - static MonsterClass getObject() - { - return baseObject; - } - - final: - - /******************************************************* - * * - * Variables * - * * - *******************************************************/ - - // Index within the parent tree. This might become a list at some - // point. - int treeIndex; - - Token name; // Class name and location - - CIndex gIndex; // Global index of this class - - ClassScope sc; - PackageScope pack; - - ObjectType objType; // Type for objects of this class - Type classType; // Type for class references to this class - - // Pointer to the C++ wrapper class, if any. Could be used for other - // wrapper languages at well, but only one at a time. - MClass cppClassPtr; - - private: - // List of objects of this class. Includes objects of all subclasses - // as well. - PointerList objects; - - Flags!(CFlags) flags; - - public: - - // Create a class belonging to the given package scope. Do not call - // this yourself, use vm.load* to load classes. - this(PackageScope psc = null) - { - assert(psc !is null, - "Don't create MonsterClasses directly, use vm.load()"); - - pack = psc; - } - - /******************************************************* - * * - * Management of member functions * - * * - *******************************************************/ - - // Bind a delegate to the name of a native function. TODO: Add - // optional signature check here at some point? - void bind(char[] name, dg_callback nf) - { bind_locate(name, FuncType.NativeDDel).natFunc_dg = nf; } - - // Same as above, but binds a function instead of a delegate. - void bind(char[] name, fn_callback nf) - { bind_locate(name, FuncType.NativeDFunc).natFunc_fn = nf; } - - // Used for C functions - void bind_c(char[] name, c_callback nf) - { bind_locate(name, FuncType.NativeCFunc).natFunc_c = nf; } - - // Bind an idle function - void bind(char[] name, IdleFunction idle) - { bind_locate(name, FuncType.Idle).idleFunc = idle; } - - void bindT(alias func)(char[] name="") - { - // Get the name from the alias parameter directly, if not - // specified. - if(name == "") - // Sort of a hack. func.stringof won't work (parses as a - // function call). (&func).stringof parses as "& funcname", - // but this could be implementation specific. - name = ((&func).stringof)[2..$]; - - // Let the Function handle the rest - findFunction(name).bindT!(func)(); - } - - // Find a function by index. Used internally, and works for all - // function types. - Function *findFunction(int index) - { - requireScope(); - assert(index >=0 && index < functions.length); - assert(functions[index] !is null); - return functions[index]; - } - - // Find a virtual function by index. ctree is the tree index of the - // class where the function is defined, findex is the intra-class - // function index. - Function *findVirtualFunc(int ctree, int findex) - { - requireScope(); - assert(ctree >= 0 && ctree <= treeIndex); - assert(findex >= 0 && findex < virtuals[ctree].length); - assert(virtuals[ctree][findex] !is null); - - return virtuals[ctree][findex]; - } - - // Find a given callable function, looking up parent classes if - // necessary. - Function *findFunction(char[] name) - { - requireScope(); - - // Get the function from the scope - auto ln = sc.lookupName(name); - - if(!ln.isFunc) - fail("Function '" ~ name ~ "' not found."); - - auto fn = ln.func; - - if(!fn.isNormal && !fn.isNative) - { - // Being here is always bad. Now we just need to find - // out what error message to give. - if(fn.isAbstract) - fail(name ~ " is abstract."); - - if(fn.isIdle) - fail("Idle function " ~ name ~ - " cannot be called from native code."); - assert(0); - } - - assert(fn !is null); - return fn; - } - - /******************************************************* - * * - * Binding of native constructors * - * * - *******************************************************/ - - // bindConst binds a native function that is run on all new - // objects. It is executed before the constructor defined in script - // code (if any.) - - // bindNew binds a function that is run on all objects created - // within script code (with the 'new' expression or the 'clone' - // property), but not on objects that are created in native code - // through createObject/createClone. This is handy when you want to - // bind with a native class, and want to be able to create objects - // in both places. It's executed before both bindConst and the - // script constructor. - - void bindConst(dg_callback nf) - { - assert(natConst.ftype == FuncType.Native, - "Cannot set native constructor for " ~ toString ~ ": already set"); - natConst.ftype = FuncType.NativeDDel; - natConst.natFunc_dg = nf; - } - - void bindConst(fn_callback nf) - { - assert(natConst.ftype == FuncType.Native, - "Cannot set native constructor for " ~ toString ~ ": already set"); - natConst.ftype = FuncType.NativeDFunc; - natConst.natFunc_fn = nf; - } - - void bindConst_c(c_callback nf) - { - assert(natConst.ftype == FuncType.Native, - "Cannot set native constructor for " ~ toString ~ ": already set"); - natConst.ftype = FuncType.NativeCFunc; - natConst.natFunc_c = nf; - } - - void bindNew(dg_callback nf) - { - assert(natNew.ftype == FuncType.Native, - "Cannot set native constructor for " ~ toString ~ ": already set"); - natNew.ftype = FuncType.NativeDDel; - natNew.natFunc_dg = nf; - } - - void bindNew(fn_callback nf) - { - assert(natNew.ftype == FuncType.Native, - "Cannot set native constructor for " ~ toString ~ ": already set"); - natNew.ftype = FuncType.NativeDFunc; - natNew.natFunc_fn = nf; - } - - void bindNew_c(c_callback nf) - { - assert(natNew.ftype == FuncType.Native, - "Cannot set native constructor for " ~ toString ~ ": already set"); - natNew.ftype = FuncType.NativeCFunc; - natNew.natFunc_c = nf; - } - - - /******************************************************* - * * - * Management of member variables * - * * - *******************************************************/ - - Variable* findVariable(char[] name) - { - requireScope(); - - auto ln = sc.lookupName(name); - - if(!ln.isVar) - fail("Variable " ~ name ~ " not found"); - - Variable *vb = ln.var; - - assert(vb.vtype == VarType.Class); - - return vb; - } - - - /******************************************************* - * * - * Management of member states * - * * - *******************************************************/ - - State* findState(char[] name) - { - requireScope(); - - auto ln = sc.lookupName(name); - if(!ln.isState) - fail("State " ~ name ~ " not found"); - - State *st = ln.state; - - return st; - } - - // Look up state and label based on indices. We allow lindex to be - // -1, in which case a null label is returned. - StateLabelPair findState(int sindex, int lindex) - { - requireScope(); - assert(sindex >=0 && sindex < states.length); - - StateLabelPair res; - res.state = states[sindex]; - - assert(res.state !is null); - - if(lindex == -1) - res.label = null; - else - { - assert(lindex >= 0 && lindex < res.state.labelList.length); - res.label = res.state.labelList[lindex]; - assert(res.label !is null); - } - - return res; - } - - // Find a state and a given label within it. Fails if it is not - // found. - StateLabelPair findState(char[] name, char[] label) - { - requireScope(); - assert(label != ""); - - StateLabelPair pr; - pr.state = findState(name); - pr.label = pr.state.findLabel(label); - - if(pr.label is null) - fail("State " ~ name ~ " does not have a label named " ~ label); - - return pr; - } - - - /******************************************************* - * * - * Object managament * - * * - *******************************************************/ - - // Loop through all objects of this type - int opApply(int delegate(ref MonsterObject v) del) - { - int dg(ref void *vp) - { - auto mop = cast(MonsterObject*)vp; - return del(*mop); - } - return objects.opApply(&dg); - } - - // Get the first object in the list for this class - MonsterObject* getFirst() - { return cast(MonsterObject*)objects.getHead().value; } - - MonsterObject* getNext(MonsterObject *ob) - { - auto iter = (*getListPtr(ob, treeIndex)).getNext(); - if(iter is null) return null; - return cast(MonsterObject*)iter.value; - } - - // Get the singleton object - MonsterObject* getSing() - { - if(!isSingleton) - fail("Class is not a singleton: " ~ name.str); - requireCompile(); - assert(singObj !is null); - return singObj; - } - alias getSing getSingleton; - - MonsterObject* createObject(bool callConst = true) - { return createClone(null, callConst); } - - // Call constructors on an object. If scriptNew is true, also call - // the natNew bindings (if any) - void callConstOn(MonsterObject *obj, bool scriptNew = false) - { - assert(obj.cls is this); - - // Needed to make sure execute() exits when the constructor is - // done. - if(cthread !is null) - cthread.fstack.pushExt("callConst"); - - // Call constructors - foreach(c; tree) - { - // Call 'new' callback if the object was created in script - if(scriptNew && c.natNew.ftype != FuncType.Native) - c.natNew.call(obj); - - // Call native constructor - if(c.natConst.ftype != FuncType.Native) - c.natConst.call(obj); - - // Call script constructor - if(c.scptConst !is null) - c.scptConst.fn.call(obj); - } - - if(cthread !is null) - cthread.fstack.pop(); - } - - // Get the whole allocated buffer belonging to this object - private int[] getDataBlock(MonsterObject *obj) - { - assert(obj !is null); - assert(obj.cls is this); - return (cast(int*)obj.data.ptr)[0..totalData.length]; - } - - private vpIter getListPtr(MonsterObject *obj, int i) - { - auto ep = cast(ExtraData*) &obj.data[i][$-MonsterObject.exSize]; - return &ep.node; - } - - // Create a new object based on an existing object - MonsterObject* createClone(MonsterObject *source, bool callConst = true) - { - requireCompile(); - - if(isModule && singObj !is null) - fail("Cannot create instances of module " ~ name.str); - - MonsterObject *obj = allObjects.getNew(); - - obj.state = null; - obj.cls = this; - - // Allocate the object data segment from a freelist - int[] odata = Buffers.getInt(totalData.length); - - // Copy the data, either from the class (in case of new objects) - // or from the source (when cloning.) - if(source !is null) - { - assert(!isAbstract); - assert(source.cls is this); - - assert(source.data.length == tree.length); - - // Copy data from the object - odata[] = getDataBlock(source); - } - else - { - if(isAbstract) - fail("Cannot create objects from abstract class " ~ name.str); - - // Copy init values from the class - odata[] = totalData[]; - } - - // Use this to get subslices of the data segment - int[] slice = odata; - int[] get(int ints) - { - assert(ints <= slice.length); - int[] res = slice[0..ints]; - slice = slice[ints..$]; - return res; - } - - // The beginning of the block is used for the int data[][] - // array. - obj.data = cast(int[][]) get(iasize*tree.length); - - // Set up the a slice for the data segment of each class - foreach(i, c; tree) - { - // Just get the slice - the actual data is already set up. - obj.data[i] = get(c.dataSize + MonsterObject.exSize); - - // Insert ourselves into the per-class list. We've already - // allocated size for a node, we just have to add it to the - // list. - auto node = getListPtr(obj, i); - node.value = obj; // Store the object pointer - c.objects.insertNode(node); - } - - // At this point we should have used up the entire slice - assert(slice.length == 0); - - // Set the same state as the source - if(source !is null) - obj.setState(source.state, null); - else - // Use the default state and label - obj.setState(defState, defLabel); - - // Make sure that getDataBlock works - assert(getDataBlock(obj).ptr == odata.ptr && - getDataBlock(obj).length == odata.length); - - // Call constructors - if(callConst) - callConstOn(obj); - - return obj; - } - - // Free an object and its thread - void deleteObject(MonsterObject *obj) - { - assert(obj.cls is this); - - if(isModule) - fail("Cannot delete instances of module " ~ name.str); - - // Shut down any active code in the thread - obj.clearState(); - - // clearState should also clear the thread - assert(obj.sthread is null); - - // This effectively marks the object as dead - obj.cls = null; - - foreach_reverse(i, c; tree) - { - // TODO: Call destructors here - - // Remove from class list - c.objects.removeNode(getListPtr(obj,i)); - } - - // Return it to the freelist - allObjects.remove(obj); - - // Return the data segment - Buffers.free(getDataBlock(obj)); - } - - - /******************************************************* - * * - * Misc. functions * - * * - *******************************************************/ - - bool isParsed() { return flags.has(CFlags.Parsed); } - bool isScoped() { return flags.has(CFlags.Scoped); } - bool isResolved() { return flags.has(CFlags.Resolved); } - bool isCompiled() { return flags.has(CFlags.Compiled); } - - bool isSingleton() { return flags.has(CFlags.Singleton); } - bool isModule() { return flags.has(CFlags.Module); } - bool isAbstract() { return flags.has(CFlags.Abstract); } - - // Call whenever you require this function to have its scope in - // order. If the scope is missing, this will call createScope if - // possible, or fail if the class has not been loaded. - void requireScope() - { - if(isScoped) return; - if(!isParsed) - fail("Cannot use class '" ~ name.str ~ - "': not found or forward reference", - name.loc); - - createScope(); - } - - // Called whenever we need a completely compiled class, for example - // when creating an object. Compiles the class if it isn't done - // already. - void requireCompile() { if(!isCompiled) compileBody(); } - - // Check if this class is a child of cls. - bool childOf(MonsterClass cls) - { - requireScope(); - - int ind = cls.treeIndex; - // If 'cls' is part of our parent tree, then we are a child. - return ind < tree.length && tree[ind] is cls; - } - - // Check if this class is a parent of cls. - bool parentOf(MonsterClass cls) - { return cls.childOf(this); } - - // Ditto for a given object - bool parentOf(MonsterObject *obj) - { return obj.cls.childOf(this); } - - // Get the tree-index of a given parent class - int upcast(MonsterClass mc) - { - requireScope(); - - int ind = mc.treeIndex; - if(ind < tree.length && tree[ind] is mc) - return ind; - - fail("Cannot upcast " ~ toString ~ " to " ~ mc.toString); - } - - // Get the given class from a tree index - MonsterClass upcast(int ind) - { - requireScope(); - - if(ind < tree.length) return tree[ind]; - - fail("Cannot upcast " ~toString ~ " to index " ~ .toString(ind)); - } - - // Get the global index of this class - CIndex getIndex() { requireScope(); return gIndex; } - int getTreeIndex() { requireScope(); return treeIndex; } - char[] getName() { assert(name.str != ""); return name.str; } - char[] toString() { return getName(); } - uint numObjects() { return objects.length; } - - // Used internally. Use the string version below instead if you want - // to change the default state of a class. - void setDefaultState(State *st, StateLabel *lb) - { - defState = st; - defLabel = lb; - } - - // Set the initial state and label for this class. This will affect - // all newly created objects, but not cloned objects. - void setDefaultState(char[] st, char[] lb="") - { - if(lb == "") - { - setDefaultState(findState(st), null); - return; - } - auto pr = findState(st, lb); - setDefaultState(pr.state, pr.label); - } - - // Converts a stream to tokens and parses it. - void parse(Stream str, char[] fname, int bom) - { - assert(str !is null); - TokenArray tokens = tokenizeStream(fname, str, bom); - parse(tokens, fname); - } - - // Parses a list of tokens, and do other setup. - void parse(ref TokenArray tokens, char[] fname) - { - assert(!isParsed(), "parse() called on a parsed class " ~ name.str); - - alias Block.isNext isNext; - - natConst.ftype = FuncType.Native; - natConst.name.str = "native constructor"; - natConst.owner = this; - natNew.ftype = FuncType.Native; - natNew.name.str = "native 'new' callback"; - natNew.owner = this; - - // Parse keywords. Uses a few local variables, so let's start a - // block. - { - assert(tokens.length > 0); - Floc loc = tokens[0].loc; - - // Check for / set a given keyword flag. Returns true if it - // was NOT found. - bool check(TT type, ref bool flag) - { - Token tok; - if(isNext(tokens, type, tok)) - { - if(flag) - fail("Keyword '" ~ tok.str ~ - "' was specified twice", tok.loc); - flag = true; - return false; - } - return true; - } - - // Flags for the various keywords - bool absSet; - bool classSet; - bool moduleSet; - bool singleSet; - bool something; - - while(true) - { - if(check(TT.Class, classSet) && - check(TT.Abstract, absSet) && - check(TT.Module, moduleSet) && - check(TT.Singleton, singleSet)) - // Abort if nothing was found this round - break; - - // If we get here at least once, then one of the keywords - // were present. - something = true; - } - - // Was anything found? - if(!something) - fail("File must begin with a class or module statement", tokens); - - // Module and singleton imply class as well - if(moduleSet || singleSet) - classSet = true; - - // Do error checking - if(moduleSet && singleSet) - fail("Cannot specify both 'module' and 'singleton' (module implies singleton)", - loc); - if(!classSet) - fail("Class must start with one of 'class', 'singleton' or 'module'", - loc); - - // Module implies singleton - if(moduleSet) - singleSet = true; - - // But singletons cannot be abstract - if(singleSet && absSet) - fail("Modules and singletons cannot be abstract", loc); - - // Process flags - if(singleSet) flags.set(CFlags.Singleton); - if(moduleSet) flags.set(CFlags.Module); - if(absSet) flags.set(CFlags.Abstract); - } - - if(!isNext(tokens, TT.Identifier, name)) - fail("Class statement expected identifier", tokens); - - // Module implies singleton - assert(isSingleton || !isModule); - assert(!isSingleton || !isAbstract); - - // Insert ourselves into the package scope. This will also - // resolve forward references to this class, if any. - pack.insertClass(this); - - // Get the parent classes, if any - if(isNext(tokens, TT.Colon)) - { - if(isModule) - fail("Inheritance not allowed for modules."); - - Token pName; - do - { - if(!isNext(tokens, TT.Identifier, pName)) - fail("Expected parent class identifier", tokens); - - parentNames ~= pName; - } - while(isNext(tokens, TT.Comma)); - } - - isNext(tokens, TT.Semicolon); - - if(parents.length > 1) - fail("Multiple inheritance is currently not supported", name.loc); - - // Parse the rest of the file - while(!isNext(tokens, TT.EOF)) store(tokens); - - // The tokenizer shouldn't allow more tokens after this point - assert(tokens.length == 0, "found tokens after end of file"); - - flags.set(CFlags.Parsed); - } - - private: - - /******************************************************* - * * - * Private variables * - * * - *******************************************************/ - - // Contains the entire class tree for this class, always with - // ourselves as the last entry. Any class in the list is always - // preceded by all the classes it inherits from. - MonsterClass tree[]; - - // List of variables and functions declared in this class, ordered - // by index. - Function* functions[]; - Variable* vars[]; - State* states[]; - - // Singleton object - used for singletons and modules only. - MonsterObject *singObj; - - // Function table translation list. Same length as tree[]. For each - // class in the parent tree, this list holds a list equivalent to - // the functions[] list in that class. The difference is that all - // overrided functions have been replaced by their successors. - Function*[][] virtuals; - - // Default state and label - State *defState = null; - StateLabel *defLabel = null; - - // The total data segment that's assigned to each object. It - // includes the data segment of all parent objects and some - // additional internal data. - int[] totalData; - - // Data segment size for *this* class, not including parents or - // extra information. - public int dataSize; - - // Total data, sliced up to match the class tree - int[][] totalSliced; - - // Direct parents of this class - public MonsterClass parents[]; - Token parentNames[]; - - // Used at compile time - VarDeclStatement[] vardecs; - FuncDeclaration[] funcdecs; - StateDeclaration[] statedecs; - StructDeclaration[] structdecs; - EnumDeclaration[] enumdecs; - ImportStatement[] imports; - ClassVarSet[] varsets; - - // Native constructors, if any - Function natConst, natNew; - - // Script constructor, if any - public Constructor scptConst; - - /******************************************************* - * * - * Various private functions * - * * - *******************************************************/ - - // Helper function for the bind() variants - Function* bind_locate(char[] name, FuncType ft) - { - requireScope(); - - // Look the function up in the scope - auto ln = sc.lookupName(name); - auto fn = ln.func; - - if(!ln.isFunc) - fail("Cannot bind to '" ~ name ~ "': no such function"); - - if(ft == FuncType.Idle) - { - if(!fn.isIdle()) - fail("Cannot bind to non-idle function '" ~ name ~ "'"); - } - else - { - if(!fn.isNative()) - fail("Cannot bind to non-native function '" ~ name ~ "'"); - } - - // Check that the function really belongs to this class. We cannot - // bind functions belonging to parent classes. - assert(fn.owner !is null); - if(fn.owner !is this) - fail("Cannot bind to function " ~ fn.toString() ~ - " - it is not a direct member of class " ~ toString()); - - fn.ftype = ft; - - return fn; - } - - - /******************************************************* - * * - * Compiler-related private functions * - * * - *******************************************************/ - - // 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) - { - if(FuncDeclaration.canParse(toks)) - { - auto fd = new FuncDeclaration; - funcdecs ~= fd; - fd.parse(toks); - } - else if(Constructor.canParse(toks)) - { - auto fd = new Constructor; - if(scptConst !is null) - fail("Class " ~ name.str ~ " cannot have more than one constructor", toks[0].loc); - scptConst = fd; - fd.parse(toks); - } - else if(ClassVarSet.canParse(toks)) - { - auto cv = new ClassVarSet; - cv.parse(toks); - - // Check if this variable is already set in this class - foreach(ocv; varsets) - { - if(cv.isState && ocv.isState) - fail(format("State already set on line %s", - ocv.loc.line), cv.loc); - else if(ocv.name.str == cv.name.str) - fail(format("Variable %s is already set on line %s", - cv.name.str, ocv.loc.line), - cv.loc); - } - - varsets ~= cv; - } - else if(VarDeclStatement.canParse(toks)) - { - auto vd = new VarDeclStatement; - vd.parse(toks); - vardecs ~= vd; - } - else if(StateDeclaration.canParse(toks)) - { - auto sd = new StateDeclaration; - sd.parse(toks); - statedecs ~= sd; - } - else if(StructDeclaration.canParse(toks)) - { - auto sd = new StructDeclaration; - sd.parse(toks); - structdecs ~= sd; - } - else if(EnumDeclaration.canParse(toks)) - { - auto sd = new EnumDeclaration; - sd.parse(toks); - enumdecs ~= sd; - } - else if(ImportStatement.canParse(toks)) - { - auto sd = new ImportStatement; - sd.parse(toks); - imports ~= sd; - } - else - fail("Illegal type or declaration", toks); - } - - // Insert the class into the scope system. All parent classes must - // be loaded before this is called. - void createScope() - { - // Since debugging self inheritance can be a little icky, add an - // explicit recursion check. - assert(!flags.has(CFlags.InScope), "createScope called recursively"); - flags.set(CFlags.InScope); - - assert(isParsed()); - assert(!isScoped(), "createScope called on already scoped class " ~ - name.str); - - // Set the scoped flag - this makes sure we are not called - // recursively below. - flags.set(CFlags.Scoped); - - // Transfer the parent list - parents.length = parentNames.length; - foreach(int i, pName; parentNames) - { - // Find the parent class. - assert(pack !is null); - auto sl = pack.lookupClass(pName); - if(!sl.isClass) - fail("Cannot inherit from " ~ pName.str ~ ": No such class.", - pName.loc); - auto mc = sl.mc; - assert(mc !is null); - mc.requireScope(); - - assert(mc !is null); - assert(mc.isScoped); - - parents[i] = mc; - - // Direct self inheritance - if(mc is this) - fail("Class " ~ name.str ~ " cannot inherit from itself", - name.loc); - - if(mc.isModule) - fail("Cannot inherit from module " ~ mc.name.str); - - // If a parent class is not a forward reference and still - // does not have a scope, it means that it is itself running - // this function. This can only happen if we are a parent of - // it. - if(mc.sc is null) - fail("Class " ~ name.str ~ " is a parent of itself (through " - ~ mc.name.str ~ ")", name.loc); - } - - // For now we only support one parent class. - assert(parents.length <= 1); - - // initialize() must already have been called before we get here - assert(baseObject !is null); - - // If we don't have a parent, and we aren't Object, then set - // Object as our parent. - if(parents.length != 1 && this !is baseObject) - parents = [baseObject]; - - // So at this point we either have a parent, or we are Object. - assert(parents.length == 1 || - this is baseObject); - - // Since there are only linear (single) inheritance graphs at - // the moment, we can just copy our parent's tree list and add - // ourself to it. - if(parents.length == 1) - tree = parents[0].tree; - else - tree = null; - tree = tree ~ this; - treeIndex = tree.length-1; - - assert(tree.length > 0); - assert(tree[$-1] is this); - assert(tree[treeIndex] is this); - - // The parent scope is the scope of the parent class, or the - // package scope if there is no parent. - Scope parSc; - if(parents.length != 0) parSc = parents[0].sc; - else - { - // For Object, use the package scope (which should be the - // global scope) - assert(this is baseObject); - assert(pack is global); - parSc = pack; - } - - assert(parSc !is null); - - // Create the scope for this class - sc = new ClassScope(parSc, this); - - // Set the type - objType = new ObjectType(this); - classType = objType.getMeta(); - - // Insert custom types first. This will never refer to other - // identifiers. - foreach(dec; structdecs) - dec.insertType(sc); - foreach(dec; enumdecs) - dec.insertType(sc); - - // Resolve imports next. May refer to custom types, but no other - // ids. - foreach(dec; imports) - dec.resolve(sc); - - // Then resolve the type headers. - foreach(dec; structdecs) - dec.resolve(sc); - foreach(dec; enumdecs) - dec.resolve(sc); - - // Resolve variable declarations. They will insert themselves - // into the scope. - foreach(dec; vardecs) - dec.resolve(sc); - - // Add function declarations to the scope. - foreach(dec; funcdecs) - sc.insertFunc(dec.fn); - - // Ditto for states. - foreach(dec; statedecs) - sc.insertState(dec.st); - - // Resolve function headers. - foreach(func; funcdecs) - func.resolve(sc); - - // Set up the function and state lists - functions.length = funcdecs.length; - foreach(fn; funcdecs) - functions[fn.fn.index] = fn.fn; - - states.length = statedecs.length; - foreach(st; statedecs) - states[st.st.index] = st.st; - - // Now set up the virtual function table. It's elements - // correspond to the classes in tree[]. - - if(parents.length) - { - // This will get a lot trickier if we allow multiple inheritance - assert(parents.length == 1); - - // Set up the virtuals list - auto pv = parents[0].virtuals; - virtuals.length = pv.length+1; - - // We have to copy every single sublist, since we're not - // allowed to change our parent's data - foreach(i,l; pv) - virtuals[i] = l.dup; - - // Add our own list - virtuals[$-1] = functions; - } - else - virtuals = [functions]; - - assert(virtuals.length == tree.length); - - // Trace all our own functions back to their origin, and replace - // them. Since we've copied our parents list, and assume it is - // all set up, we only have to worry about our own - // functions. (For multiple inheritance this might be a bit more - // troublesome, but definitely doable.) - foreach(fn; functions) - { - auto o = fn.overrides; - - // And we have to loop backwards through the overrides that - // o overrides as well. - while(o !is null) - { - // Find the owner class tree index of the function we're - // overriding - assert(o.owner !is this); - int clsInd = o.owner.treeIndex; - assert(clsInd < tree.length-1); - assert(tree[clsInd] == o.owner); - - // Next, get the function index and replace the pointer - virtuals[clsInd][o.index] = fn; - - // Get the function that o overrides too, and fix that - // one as well. - o = o.overrides; - } - } - - flags.unset(CFlags.InScope); - } - - // This calls resolve on the interior of functions and states. - void resolveBody() - { - requireScope(); - - assert(!isResolved, getName() ~ " is already resolved"); - - // Resolve the functions - foreach(func; funcdecs) - func.resolveBody(); - - // Including the constructor - if(scptConst !is null) - { - scptConst.resolve(sc); - assert(scptConst.fn.owner is this); - } - - // Resolve states - foreach(state; statedecs) - state.resolve(sc); - - // TODO: Resolve struct functions - /* - foredach(stru; structdecs) - stru.resolveBody(sc); - */ - - // Validate all variable types - foreach(var; vardecs) - var.validate(); - - // Resolve variable and state overrides. No other declarations - // depend on these (the values are only relevant at the - // compilation stage), so we can resolve these last. - foreach(dec; varsets) - dec.resolve(sc); - - flags.set(CFlags.Resolved); - } - - alias int[] ia; - // This is platform dependent: - static const iasize = ia.sizeof / int.sizeof; - - // Fill the data segment for this class. - void getDataSegment(int[] data) - { - assert(data.length == dataSize); - int totSize = 0; - - foreach(VarDeclStatement vds; vardecs) - foreach(VarDeclaration vd; vds.vars) - { - int size = vd.var.type.getSize(); - int[] val; - totSize += size; - - val = vd.getCTimeValue(); - - data[vd.var.number..vd.var.number+size] = val[]; - } - // Make sure the total size of the variables match the total size - // requested by variables through addNewDataVar. - assert(totSize == dataSize, "Data size mismatch in scope"); - } - - bool compiling = false; - void compileBody() - { - assert(!isCompiled, getName() ~ " is already compiled"); - assert(!compiling, "compileBody called recursively"); - compiling = true; - - // Resolve the class body if it's not already done - if(!isResolved) resolveBody(); - - // Require that all parent classes are compiled before us - foreach(mc; tree[0..$-1]) - mc.requireCompile(); - - // Generate byte code for functions and states. - foreach(f; funcdecs) f.compile(); - foreach(s; statedecs) s.compile(); - if(scptConst !is null) scptConst.compile(); - - // Get the data segment size for this class - assert(sc !is null && sc.isClass(), "Class does not have a class scope"); - dataSize = sc.getDataSize; - - // Calculate the total data size we need to allocate for each - // object - uint tsize = 0; - foreach(c; tree) - { - tsize += c.dataSize; // Data segment size - tsize += MonsterObject.exSize; // Extra data per object - tsize += iasize; // The size of our entry in the data[] - // table - } - - // Allocate the buffer - totalData = new int[tsize]; - - // Used below to get subslices of the data segment - int[] slice = totalData; - int[] get(int ints) - { - assert(ints <= slice.length); - int[] res = slice[0..ints]; - slice = slice[ints..$]; - return res; - } - - // The first part of the buffer is used for storing the obj.data - // array itself - skip that now. - get(iasize*tree.length); - - // Set up the slice list - totalSliced.length = tree.length; - foreach(i,c; tree) - { - // Data segment slice - totalSliced[i] = get(c.dataSize); - - // Skip the extra data - get(MonsterObject.exSize); - } - - // At this point we should have used up the entire slice - assert(slice.length == 0); - // Sanity check on the size - assert(totalSliced[$-1].length == dataSize); - - // Fill our own data segment - getDataSegment(totalSliced[$-1]); - - // The next part is only implemented for single inheritance - assert(parents.length <= 1); - if(parents.length == 1) - { - auto p = parents[0]; - - // Go through the parent's tree, and copy its data - // segments. This will make sure we include all cumulative - // variable changes from past classes. - assert(p.tree.length == tree.length - 1); - - foreach(i,c; p.tree) - { - assert(tree[i] is c); - - // Copy updated data segment for c from parent class - totalSliced[i][] = p.totalSliced[i][]; - } - - // Apply all variable changes defined in this class - foreach(vs; varsets) - { - if(vs.isState) continue; - - assert(vs.cls !is null); - int ind = vs.cls.treeIndex; - assert(ind < p.tree.length); - assert(tree[ind] is vs.cls); - - vs.apply(totalSliced[ind]); - } - } - - flags.set(CFlags.Compiled); - - // If it's a singleton, set up the object. - if(isSingleton) - { - assert(singObj is null); - singObj = createObject(); - } - compiling = false; - } -} diff --git a/old_d_version/monster/vm/mobject.d b/old_d_version/monster/vm/mobject.d deleted file mode 100644 index 9d524ac2f..000000000 --- a/old_d_version/monster/vm/mobject.d +++ /dev/null @@ -1,484 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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 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; -} diff --git a/old_d_version/monster/vm/params.d b/old_d_version/monster/vm/params.d deleted file mode 100644 index 87850204d..000000000 --- a/old_d_version/monster/vm/params.d +++ /dev/null @@ -1,52 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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; - } -} diff --git a/old_d_version/monster/vm/stack.d b/old_d_version/monster/vm/stack.d deleted file mode 100644 index e3eb417dd..000000000 --- a/old_d_version/monster/vm/stack.d +++ /dev/null @@ -1,454 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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); - } -} diff --git a/old_d_version/monster/vm/thread.d b/old_d_version/monster/vm/thread.d deleted file mode 100644 index 9457f9489..000000000 --- a/old_d_version/monster/vm/thread.d +++ /dev/null @@ -1,1792 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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.thread; - -import std.string; -import std.stdio; -import std.uni; -import std.c.string; - -import monster.util.freelist; -import monster.options; - -import monster.compiler.bytecode; -import monster.compiler.linespec; -import monster.compiler.states; -import monster.compiler.functions; -import monster.compiler.scopes; -import monster.compiler.types; - -import monster.vm.mclass; -import monster.vm.mobject; -import monster.vm.codestream; -import monster.vm.stack; -import monster.vm.dbg; -import monster.vm.idlefunction; -import monster.vm.arrays; -import monster.vm.iterators; -import monster.vm.error; -import monster.vm.fstack; -import monster.vm.vm; - -import std.math : floor; - -// Used for array copy below. It handles overlapping data for us. -extern(C) void* memmove(void *dest, void *src, size_t n); - -// Enable this to print bytecode instructions to stdout - -import monster.util.list; -alias _lstNode!(Thread) _tmp1; -alias __FreeNode!(Thread) _tmp2; -alias FreeList!(Thread) NodeList; - -// Current thread -Thread *cthread; - -// This represents an execution 'thread' in the system. Each object -// has its own thread. The thread contains a link to the object and -// the class, along with some other data. -struct Thread -{ - /******************************************************* - * * - * Public variables * - * * - *******************************************************/ - - // Function stack for this thread. This MUST be the first member of - // Thread, to make sure fstack.getThread() works properly. - FunctionStack fstack; - - // Some generic variables that idle functions can use to store - // temporary data off the stack. - SharedType idleData; - - // The contents of the idle object's extra data for the idle's owner - // class. - SharedType extraData; - - // Set to true when a state change is in progress. Only used when - // state is changed from within a function in active code. - bool shouldExit; - - /******************************************************* - * * - * Private variables * - * * - *******************************************************/ - - private: - NodeList *list; // List owning this thread - - // Stored copy of the stack. Used when the thread is not running. - int[] sstack; - - public: - /******************************************************* - * * - * Public functions * - * * - *******************************************************/ - - // Get a new thread. It starts in the 'transient' list. - static Thread* getNew() - { - vm.init(); - - auto cn = scheduler.transient.getNew(); - cn.list = &scheduler.transient; - - with(*cn) - { - // Initialize other variables - shouldExit = false; - sstack = null; - } - - return cn; - } - - // Get a paused thread - static Thread *getPaused() - { - auto cn = getNew(); - cn.moveTo(&scheduler.paused); - return cn; - } - - int getIndex() - { - return NodeList.getIndex(this)+1; - } - - // Schedule the function to run the next frame. Can only be used on - // paused threads. - void restart() - { - if(isDead) - fail("Cannot restart a dead thread"); - - if(!isPaused) - fail("Can only use restart() on paused threads"); - - // Move to the runlist - moveTo(scheduler.runNext); - } - - // Stop the thread and return it to the freelist - void kill() - { - stop(); - assert(fstack.isEmpty); - list.remove(this); - list = null; - assert(isDead); - - static if(logThreads) - dbg.log(format("------ killing thread=%s ------", getIndex)); - } - - // Stop the execution of a thread and cancel any scheduling. - void stop() - { - static if(traceThreads) - { - dbg.log("Thread.stop()"); - } - - assert(!isDead); - - // TODO: We also have to handle (forbid) cases where we are - // the caller of another thread. - - if(isRunning) - { - // We are running. - assert(sstack.length == 0); - - // Forbid stopping the thread if there are native functions on - // the function stack. - if(fstack.hasNatives) - fail("Cannot stop thread, there are native functions on the stack."); - - // Kill the stack and tell execute() to stop running - stack.reset(); - shouldExit = true; - } - else - { - // We are not running - - // Free the stack buffers - if(sstack.length) - Buffers.free(sstack); - sstack = null; - - // Abort any idle function - if(fstack.isIdle) - { - // Abort the idle function and pop it - getIdle().abort(this); - fstack.pop(); - } - assert(!fstack.hasNatives); - } - - // Kill the function stack - fstack.killAll(); - - // Move to the transient list (signalling that the thread is - // unused.) - moveTo(&scheduler.transient); - assert(!isScheduled); - } - - // Schedule this thread to run state code the next frame - void scheduleState(MonsterObject *obj, int offs) - { - static if(logThreads) - dbg.log(format("------ scheduling state in thread %s ------", getIndex)); - - assert(!isDead); - assert(!isScheduled, - "cannot schedule an already scheduled thread"); - assert(!fstack.isIdle); - assert(fstack.isEmpty); - assert(offs >= 0); - assert(obj !is null); - - assert(isRunning == shouldExit); - - // Move to the runlist - moveTo(scheduler.runNext); - - // Set up the function stack - fstack.push(obj.state, obj); - fstack.cur.code.jump(offs); - } - - // Push a function and pause the thread - void pushFunc(Function *fn, MonsterObject *obj) - { - assert(!isDead); - assert(!isScheduled, - "cannot schedule an already scheduled thread"); - assert(fstack.isEmpty); - assert(!fstack.isIdle); - assert(fn !is null); - assert(obj !is null); - assert(!fn.isIdle); - - // Set up the function stack - assert(fn.owner.parentOf(obj)); - fstack.push(fn, obj); - - moveTo(&scheduler.paused); - } - - // Are we currently scheduled? - bool isScheduled() - { - // The node is per definition scheduled if it is in one of these - // lists - return - !isDead && (list is &scheduler.wait || - list is scheduler.run || - list is scheduler.runNext); - } - - bool isTransient() { return list is &scheduler.transient; } - bool isRunning() { return cthread is this; } - bool isDead() { return list is null; } - bool isAlive() { return !isDead; } - bool isPaused() { return list is &scheduler.paused; } - - // Get the next node in the freelist - Thread* getNext() - { - // Simple hack. The Thread (pointed at by the Thread*) is the - // first part of, and therefore in the same location as, the - // iterator struct for the FreeList. This is per design, so it's - // ok to cast the pointer. - return cast(Thread*) - ( cast(NodeList.TList.Iterator)this ).getNext(); - } - - // Reenter this thread to the point where it was previously stopped. - void reenter() - { - static if(traceThreads) - { - dbg.log("Thread.reenter()"); - } - - assert(!isDead); - assert(cthread is null, - "cannot reenter when another thread is running"); - assert(!isRunning, - "reenter cannot be called when thread is already running"); - assert(isScheduled || isPaused); - assert(!fstack.isEmpty); - - // Put the thread in the foreground - foreground(); - assert(isRunning); - - // Notify the idle function, if any - if(fstack.isIdle) - { - // Tell the idle function that we we are reentering - assert(fstack.isIdle); - getIdle().reentry(this); - - // Remove the idle function - fstack.pop(); - } - - assert(fstack.cur.isNormal, - "can only reenter script code"); - - // Remove the current thread from the run list - moveTo(&scheduler.transient); - - // Run the code - execute(); - - // Exit immediately if the thread committed suicide. - if(isDead) return; - - shouldExit = false; - - // Background the thread - background(); - assert(cthread is null); - } - - // Put this thread in the background. Acquires the stack and - // function stack. - void background() - { - assert(!isDead); - assert(sstack.length == 0, - "Thread already has a stack"); - assert(isRunning, - "cannot put a non-running thread in the background"); - assert(!fstack.hasNatives, - "cannot put thread in the background, there are native functions on the stack"); - - // We're no longer the current thread - cthread = null; - - if(isTransient) - { - // The thread is not scheduled and will not be used - // anymore. Might as well kill it. - assert(!isRunning); - kill(); - } - else - { - // The thread will possibly be restored at some point. Store - // the stack and fstack for later. - - // Stack. - int len = stack.getPos(); - if(len) - { - // Get a new buffer, and copy the stack - sstack = Buffers.getInt(len); - sstack[] = stack.popInts(len); - } - } - - // Clear out our stack values - stack.reset(); - - static if(logThreads) - dbg.log(format("------ deactivate thread=%s (stack=%s) ------", - getIndex, sstack.length)); - - assert(!isRunning); - } - - // Put the thread in the foreground. Restore any stored stack - // values. - void foreground() - { - assert(!isDead); - assert(cthread is null, - "cannot restore thread, another thread is running"); - assert(!isRunning, - "cannot restore thread, it is already running"); - - assert((isTransient && fstack.isEmpty) || - stack.getPos() == 0, - "only empty transient threads kan restore into a non-empty stack"); - - static if(logThreads) - dbg.log(format("------ activate thread=%s (stack=%s) ------", - getIndex, sstack.length)); - - if(sstack.length) - { - assert(stack.getPos() == 0, - "cannot restore into a non-empty stack"); - assert(!isTransient, - "cannot restore a transent thread with stack"); - - // Push the values back, and free the buffer - stack.pushInts(sstack); - Buffers.free(sstack); - assert(stack.getPos == sstack.length); - sstack = null; - } - - // Set ourselves as the running thread - cthread = this; - } - - // Move this node to another list. - void moveTo(NodeList *to) - { - if(list is to) return; - assert(list !is null); - list.moveTo(*to, this); - list = to; - } - - private: - /******************************************************* - * * - * Private helper functions * - * * - *******************************************************/ - - IdleFunction getIdle() - { - assert(fstack.isIdle); - assert(fstack.cur.func !is null); - assert(fstack.cur.func.idleFunc !is null); - return fstack.cur.func.idleFunc; - } - - void fail(char[] msg) - { - Floc fl; - if(fstack.cur !is null) - fl = fstack.cur.getFloc(); - - msg ~= '\n' ~ fstack.toString(); - - .fail(msg, fl); - } - - // Handle the call and scheduling of an given idle function. Return - // true if we should exit execute(). - bool callIdle(MonsterObject *iObj, Function *idle) - { - static if(traceThreads) - { - dbg.log("Thread.callIdle()"); - } - - assert(isRunning); - assert(!isScheduled, "Thread is already scheduled"); - assert(iObj !is null); - - if(fstack.hasNatives) - fail("Cannot run idle function: there are native functions on the stack"); - - assert(idle !is null); - assert(idle.isIdle); - - // The IdleFunction object bound to this function is stored in - // idle.idleFunc - if(idle.idleFunc is null) - fail("Called unimplemented idle function '" ~ idle.name.str ~ "'"); - - // Set up extraData - extraData = *iObj.getExtra(idle.owner); - - // Push the idle function on the stack, with iObj as the 'this' - // object. - fstack.pushIdle(idle, iObj); - - // Notify the idle function that it was called - auto res = idle.idleFunc.initiate(this); - //writefln("Called %s, result was %s", idle.name, res); - - if(res == IS.Poll) - { - moveTo(&scheduler.wait); - return true; - } - - if(res == IS.Return) - { - // If we're returning, call reenter immediately - idle.idleFunc.reentry(this); - - // The function is done, pop it back of the stack - fstack.pop(); - - // 'false' means continue running - return false; - } - - assert(res == IS.Manual || res == IS.Kill); - - // The only difference between Manual and Kill is what list the - // thread ends in. The idle function itself is responsible for - // putting the thread in the correct list when returning - // IS.Manual. If the thread is left in the transient list (ie. if - // the idle doesn't move the thread), it will be killed - // automatically when it is no longer running. - assert( (res == IS.Kill) == isTransient, - res == IS.Manual ? "Manually scheduled threads must be moved to another list." : "Killed threads cannot be moved to another list."); - - // 'true' means exit execute() - return true; - } - - /******************************************************* - * * - * execute() - main VM function * - * * - *******************************************************/ - public: - - // Execute instructions in the current function stack entry. This is - // the main workhorse of the VM, the "byte-code CPU". The function - // is called (possibly recursively) whenever a byte-code function is - // called, and returns when the function exits. Function calls - void execute() - { - assert(!isDead); - assert(fstack.cur !is null, - "Thread.execute called but there is no code on the function stack."); - assert(fstack.cur.isNormal, - "execute() can only run script code"); - assert(isRunning, - "can only run the current thread"); - - // Get some values from the function stack - CodeStream *code = &fstack.cur.code; - MonsterObject *obj = fstack.cur.obj; - MonsterClass cls = fstack.cur.getCls(); - - // Only an object belonging to this thread can be passed to - // execute() on the function stack. - assert(obj is null || cls.parentOf(obj)); - assert(obj !is null || fstack.cur.isStatic); - - // Pops a pointer off the stack. Null pointers will throw an - // exception. - int *popPtr() - { - // TODO: A better optimization here would be to pop the entire - // structure all at once. Once we work more on references, - // create a structure that takes care of all the conversions - // for us. - - PT type; - int index; - decodePtr(stack.popInt(), type, index); - - int *res; - - // Null pointer? - if(type == PT.Null) - fail("Cannot access value, null pointer"); - - // Stack variable? - if(type == PT.Stack) - { - res = stack.getInt(index); - stack.pop(2); - } - - // Variable in this object - else if(type == PT.DataOffs) - { - res = obj.getDataInt(cls.treeIndex, index); - stack.pop(2); - } - - // This object, but another (parent) class - else if(type == PT.DataOffsCls) - { - // We have to pop the class index of the stack as well - res = obj.getDataInt(stack.popInt, index); - stack.popInt(); - } - - // Far pointer, with offset. Both the class index and the object - // reference is on the stack. - else if(type == PT.FarDataOffs) - { - int clsIndex = stack.popInt(); - - // Get the object reference from the stack - MonsterObject *tmp = stack.popObject(); - - // Return the correct pointer - res = tmp.getDataInt(clsIndex, index); - } - - // Array pointer - else if(type == PT.ArrayIndex) - { - assert(index==0); - // Array indices are on the stack - index = stack.popInt(); - ArrayRef *arf = stack.popArray(); - assert(!arf.isNull); - - if(arf.isConst) - fail("Cannot assign to constant array"); - - index *= arf.elemSize; - if(index < 0 || index >= arf.iarr.length) - fail("Array index " ~ .toString(index/arf.elemSize) ~ - " out of bounds (array length " ~ .toString(arf.length) ~ ")"); - res = &arf.iarr[index]; - } - else - fail("Unable to handle pointer type " ~ toString(cast(int)type)); - - assert(res !is null); - return res; - } - - // Various temporary stuff - int *ptr; - long *lptr; - float *fptr; - double *dptr; - int[] iarr; - ArrayRef *arf; - int val, val2; - long lval; - - static if(enableExecLimit) - long count = 0; - - for(;;) - { - static if(enableExecLimit) - { - count++; - - if(count > execLimit) - fail(format("Execution unterminated after %s instructions. ", - execLimit, " Possibly an infinite loop, aborting.")); - } - - ubyte opCode = code.get(); - - static if(traceVMOps) - { - writefln("exec: %s (at stack %s)", - bcToString[opCode], stack.getPos); - } - - switch(opCode) - { - case BC.Exit: - // Step down once on the function stack - fstack.pop(); - - // Leave execute() when the next level down is not a - // script function - if(!fstack.isNormal()) - return; - - assert(!shouldExit); - - // Set up the variables and continue running. - assert(fstack.cur !is null); - cls = fstack.cur.getCls(); - code = &fstack.cur.code; - obj = fstack.cur.obj; - - break; - - // Start a block so these variables are local - { - MonsterObject *mo; - Function *fn; - - // Call function in this object - case BC.Call: - fn = Function.fromIndex(stack.popInt()); - mo = obj; - goto FindFunc; - - // Call function in another object - case BC.CallFar: - fn = Function.fromIndex(stack.popInt()); - mo = stack.popObject(); - - FindFunc: - // Get the correct function from the virtual table - fn = fn.findVirtual(mo); - - if(fn.isNormal) - { - // Normal (script) function. We don't need to exit - // execute(), just change the function stack and the - // cls and code pointers. Then keep running. - fstack.push(fn, mo); - cls = fstack.cur.getCls(); - code = &fstack.cur.code; - obj = mo; - assert(obj is fstack.cur.obj); - } - else if(fn.isIdle) - { - if(callIdle(mo, fn)) return; - } - else if(fn.isAbstract) - fail("Cannot call abstract function " ~ fn.toString()); - else - { - // Native function. Let Function handle it. - assert(fn.isNative); - fn.call(mo); - if(shouldExit) return; - } - break; - } - - case BC.Return: - // Remove the given number of bytes from the stack, and - // exit the function. - stack.pop(code.getInt()); - goto case BC.Exit; - - case BC.ReturnVal: - stack.pop(code.getInt(), 1); - goto case BC.Exit; - - case BC.ReturnValN: - val = code.getInt(); // Get the param number - stack.pop(val, code.getInt()); - goto case BC.Exit; - - case BC.State: - val = code.getInt(); // State index - val2 = code.getInt(); // Label index - // Get the class index and let setState handle everything - - obj.setState(val, val2, code.getInt()); - if(shouldExit) return; - break; - - case BC.EnumValue: - { - auto t = cast(EnumType)Type.typeList[code.getInt()]; - assert(t !is null, "invalid type index"); - val = stack.popInt(); // Get enum index - if(val-- == 0) - fail("'Null' enum encountered, cannot get value (type " ~ t.name ~ ")"); - assert(val >= 0 && val < t.entries.length); - stack.pushLong(t.entries[val].value); - } - break; - - case BC.EnumField: - { - val2 = code.getInt(); // Field index - auto t = cast(EnumType)Type.typeList[code.getInt()]; - assert(t !is null, "invalid type index"); - assert(val2 >= 0 && val2 < t.fields.length); - val = stack.popInt(); // Get enum index - if(val-- == 0) - fail("'Null' enum encountered, cannot get field '" ~ t.fields[val2].name.str ~ "' (type " ~ t.name ~ ")"); - assert(val >= 0 && val < t.entries.length); - assert(t.entries[val].fields.length == t.fields.length); - stack.pushInts(t.entries[val].fields[val2]); - } - break; - - case BC.EnumValToIndex: - { - auto t = cast(EnumType)Type.typeList[code.getInt()]; - assert(t !is null, "invalid type index"); - lval = stack.popLong(); // The value - auto eptr = t.lookup(lval); - if(eptr is null) - fail("No matching value " ~ .toString(lval) ~ " in enum"); - stack.pushInt(eptr.index); - } - break; - - case BC.EnumNameToIndex: - { - auto t = cast(EnumType)Type.typeList[code.getInt()]; - assert(t !is null, "invalid type index"); - auto str = stack.popString8(); // The value - auto eptr = t.lookup(str); - if(eptr is null) - fail("No matching value " ~ str ~ " in enum"); - stack.pushInt(eptr.index); - } - break; - - case BC.New: - { - // Create a new object. Look up the class index in the - // global class table, and create an object from it. Do - // not call constructors yet. - auto mo = global.getClass(cast(CIndex)code.getInt()) - .createObject(false); - - // Set up the variable parameters - val = code.getInt(); - for(;val>0;val--) - { - // Get the variable pointer - int cIndex = stack.popInt(); - int vIndex = stack.popInt(); - int size = stack.popInt(); - int[] dest = mo.getDataArray(cIndex, vIndex, size); - - // Copy the value from the stack into place - dest[] = stack.popInts(size); - } - - // Now we can call the constructors - mo.cls.callConstOn(mo, true); - - // Push the resulting object - stack.pushObject(mo); - } - break; - - case BC.Clone: - { - auto mo = stack.popObject(); - - // Create a clone but don't call constructors - mo = mo.cls.createClone(mo, false); - - // Call them manually, and make sure the natNew bindings - // are invoked - mo.cls.callConstOn(mo, true); - - // Push the resulting object - stack.pushObject(mo); - } - break; - - case BC.Jump: - code.jump(code.getInt); - break; - - case BC.JumpZ: - val = code.getInt; - if(stack.popInt() == 0) - code.jump(val); - break; - - case BC.JumpNZ: - val = code.getInt; - if(stack.popInt() != 0) - code.jump(val); - break; - - case BC.PushData: - stack.pushInt(code.getInt()); - static if(traceVMOps) writefln(" Data: %s", *stack.getInt(0)); - break; - - case BC.PushLocal: - static if(traceVMOps) - { - auto p = code.getInt(); - auto v = *stack.getInt(p); - stack.pushInt(v); - writefln(" Pushed %s from position %s",v,p); - } - else - stack.pushInt(*stack.getInt(code.getInt())); - break; - - case BC.PushClassVar: - stack.pushInt(*obj.getDataInt(cls.treeIndex, code.getInt())); - break; - - case BC.PushParentVar: - // Get the tree index - val = code.getInt(); - stack.pushInt(*obj.getDataInt(val, code.getInt())); - break; - - case BC.PushFarClassVar: - { - // Get object to work on - MonsterObject *mo = stack.popObject(); - // And the tree index - val = code.getInt(); - stack.pushInt(*mo.getDataInt(val, code.getInt())); - } - break; - - case BC.PushFarClassMulti: - { - int siz = code.getInt(); // Variable size - // Get object to work on - MonsterObject *mo = stack.popObject(); - // And the tree index - val = code.getInt(); // Class tree index - val2 = code.getInt(); // Data segment offset - stack.pushInts(mo.getDataArray(val,val2,siz)); - } - break; - - case BC.PushThis: - // Push the index of this object. - stack.pushObject(obj); - break; - - case BC.PushSingleton: - stack.pushObject(global.getClass(cast(CIndex)code.getInt).getSing); - break; - - case BC.Pop: stack.popInt(); break; - - case BC.PopN: stack.pop(code.get()); break; - - case BC.Dup: stack.pushInt(*stack.getInt(0)); break; - - case BC.Store: - // Get the pointer off the stack, and convert it to a real - // pointer. - ptr = popPtr(); - // Pop the value and store it - *ptr = stack.popInt(); - break; - - case BC.Store8: - ptr = popPtr(); - *(cast(long*)ptr) = stack.popLong(); - break; - - case BC.StoreMult: - val = code.getInt(); // Size - ptr = popPtr(); - ptr[0..val] = stack.popInts(val); - break; - - // Int / uint operations - case BC.IAdd: - ptr = stack.getInt(1); - *ptr += stack.popInt; - break; - - case BC.ISub: - ptr = stack.getInt(1); - *ptr -= stack.popInt; - break; - - case BC.IMul: - ptr = stack.getInt(1); - *ptr *= stack.popInt; - break; - - case BC.IDiv: - ptr = stack.getInt(1); - val = stack.popInt; - if(val) - { - *ptr /= val; - break; - } - fail("Integer division by zero"); - - case BC.UDiv: - ptr = stack.getInt(1); - val = stack.popInt; - if(val) - { - *(cast(uint*)ptr) /= cast(uint)val; - break; - } - fail("Integer division by zero"); - - case BC.IDivRem: - ptr = stack.getInt(1); - val = stack.popInt; - if(val) - { - *ptr %= val; - break; - } - fail("Integer division by zero"); - - case BC.UDivRem: - ptr = stack.getInt(1); - val = stack.popInt; - if(val) - { - *(cast(uint*)ptr) %= cast(uint)val; - break; - } - fail("Integer division by zero"); - - case BC.INeg: - ptr = stack.getInt(0); - *ptr = -*ptr; - break; - - - // Float operations - case BC.FAdd: - fptr = stack.getFloat(1); - *fptr += stack.popFloat; - break; - - case BC.FSub: - fptr = stack.getFloat(1); - *fptr -= stack.popFloat; - break; - - case BC.FMul: - fptr = stack.getFloat(1); - *fptr *= stack.popFloat; - break; - - case BC.FDiv: - fptr = stack.getFloat(1); - *fptr /= stack.popFloat; - break; - - case BC.FIDiv: - fptr = stack.getFloat(1); - *fptr = floor(*fptr / stack.popFloat); - break; - - // Calculate a generalized reminder for floating point - // numbers - case BC.FDivRem: - { - fptr = stack.getFloat(1); - float fval = stack.popFloat; - *fptr -= fval * floor(*fptr/fval); - } - break; - - case BC.FNeg: - fptr = stack.getFloat(0); - *fptr = -*fptr; - break; - - // Long / ulong operations - case BC.LAdd: - lptr = stack.getLong(3); - *lptr += stack.popLong; - break; - - case BC.LSub: - lptr = stack.getLong(3); - *lptr -= stack.popLong; - break; - - case BC.LMul: - lptr = stack.getLong(3); - *lptr *= stack.popLong; - break; - - case BC.LDiv: - lptr = stack.getLong(3); - lval = stack.popLong; - if(lval) - { - *lptr /= lval; - break; - } - fail("Long division by zero"); - - case BC.ULDiv: - lptr = stack.getLong(3); - lval = stack.popLong; - if(lval) - { - *(cast(ulong*)lptr) /= cast(ulong)lval; - break; - } - fail("Long division by zero"); - - case BC.LDivRem: - lptr = stack.getLong(3); - lval = stack.popLong; - if(lval) - { - *lptr %= lval; - break; - } - fail("Long division by zero"); - - case BC.ULDivRem: - lptr = stack.getLong(3); - lval = stack.popLong; - if(lval) - { - *(cast(ulong*)lptr) %= cast(ulong)lval; - break; - } - fail("Long division by zero"); - - case BC.LNeg: - lptr = stack.getLong(1); - *lptr = -*lptr; - break; - - // Double operations - case BC.DAdd: - dptr = stack.getDouble(3); - *dptr += stack.popDouble; - break; - - case BC.DSub: - dptr = stack.getDouble(3); - *dptr -= stack.popDouble; - break; - - case BC.DMul: - dptr = stack.getDouble(3); - *dptr *= stack.popDouble; - break; - - case BC.DDiv: - dptr = stack.getDouble(3); - *dptr /= stack.popDouble; - break; - - case BC.DIDiv: - dptr = stack.getDouble(3); - *dptr = floor(*dptr / stack.popDouble); - break; - - // Calculate a generalized reminder for floating point - // numbers - case BC.DDivRem: - { - dptr = stack.getDouble(3); - double fval = stack.popDouble; - *dptr -= fval * floor(*dptr/fval); - } - break; - - case BC.DNeg: - dptr = stack.getDouble(1); - *dptr = -*dptr; - break; - - case BC.IsEqual: - stack.pushBool(stack.popInt == stack.popInt); - break; - - case BC.IsEqualMulti: - val = code.getInt(); // Get the variable size (in ints) - assert(val > 1); - stack.pushBool(stack.popInts(val) == - stack.popInts(val)); - break; - - case BC.IsCaseEqual: - if(toUniLower(stack.popChar) == toUniLower(stack.popChar)) stack.pushInt(1); - else stack.pushInt(0); - break; - - case BC.CmpArray: - stack.pushBool(stack.popArray().iarr == stack.popArray().iarr); - break; - - case BC.ICmpStr: - stack.pushBool(isUniCaseEqual(stack.popString(), - stack.popString())); - break; - - case BC.PreInc: - ptr = popPtr(); - stack.pushInt(++(*ptr)); - break; - - case BC.PreDec: - ptr = popPtr(); - stack.pushInt(--(*ptr)); - break; - - case BC.PostInc: - ptr = popPtr(); - stack.pushInt((*ptr)++); - break; - - case BC.PostDec: - ptr = popPtr(); - stack.pushInt((*ptr)--); - break; - - case BC.PreInc8: - lptr = cast(long*)popPtr(); - stack.pushLong(++(*lptr)); - break; - - case BC.PreDec8: - lptr = cast(long*)popPtr(); - stack.pushLong(--(*lptr)); - break; - - case BC.PostInc8: - lptr = cast(long*)popPtr(); - stack.pushLong((*lptr)++); - break; - - case BC.PostDec8: - lptr = cast(long*)popPtr(); - stack.pushLong((*lptr)--); - break; - - case BC.Not: - ptr = stack.getInt(0); - if(*ptr == 0) *ptr = 1; - else *ptr = 0; - break; - - case BC.ILess: - val = stack.popInt; - if(stack.popInt < val) stack.pushInt(1); - else stack.pushInt(0); - break; - - case BC.ULess: - val = stack.popInt; - if(stack.popUint < cast(uint)val) stack.pushInt(1); - else stack.pushInt(0); - break; - - case BC.LLess: - lval = stack.popLong; - if(stack.popLong < lval) stack.pushInt(1); - else stack.pushInt(0); - break; - - case BC.ULLess: - lval = stack.popLong; - if(stack.popUlong < cast(ulong)lval) stack.pushInt(1); - else stack.pushInt(0); - break; - - case BC.FLess: - { - float fval = stack.popFloat; - if(stack.popFloat < fval) stack.pushInt(1); - else stack.pushInt(0); - break; - } - - case BC.DLess: - { - double fval = stack.popDouble; - if(stack.popDouble < fval) stack.pushInt(1); - else stack.pushInt(0); - break; - } - - case BC.CastI2L: - if(*stack.getInt(0) < 0) stack.pushInt(-1); - else stack.pushInt(0); - break; - - // Cast int to float - case BC.CastI2F: - ptr = stack.getInt(0); - fptr = cast(float*) ptr; - *fptr = *ptr; - break; - - case BC.CastU2F: - ptr = stack.getInt(0); - fptr = cast(float*) ptr; - *fptr = *(cast(uint*)ptr); - break; - - case BC.CastL2F: - stack.pushFloat(stack.popLong); - break; - - case BC.CastUL2F: - stack.pushFloat(stack.popUlong); - break; - - case BC.CastD2F: - stack.pushFloat(stack.popDouble); - break; - - // Cast int to double - case BC.CastI2D: - stack.pushDouble(stack.popInt); - break; - - case BC.CastU2D: - stack.pushDouble(stack.popUint); - break; - - case BC.CastL2D: - stack.pushDouble(stack.popLong); - break; - - case BC.CastUL2D: - stack.pushDouble(stack.popUlong); - break; - - case BC.CastF2D: - stack.pushDouble(stack.popFloat); - break; - - // Cast floating point types back to integral ones - case BC.CastF2I: - ptr = stack.getInt(0); - fptr = cast(float*) ptr; - *ptr = cast(int)*fptr; - break; - - case BC.CastF2U: - ptr = stack.getInt(0); - fptr = cast(float*) ptr; - *(cast(uint*)ptr) = cast(uint)*fptr; - break; - - case BC.CastF2L: - stack.pushLong(cast(long)stack.popFloat); - break; - - case BC.CastF2UL: - stack.pushUlong(cast(ulong)stack.popFloat); - break; - - case BC.CastD2I: - stack.pushInt(cast(int)stack.popDouble); - break; - - case BC.CastD2U: - stack.pushUint(cast(uint)stack.popDouble); - break; - - case BC.CastD2L: - stack.pushLong(cast(long)stack.popDouble); - break; - - case BC.CastD2UL: - stack.pushUlong(cast(ulong)stack.popDouble); - break; - - case BC.CastS2C: - arf = stack.popArray(); // Get array - if(arf.carr.length == 0) - fail("Cannot cast empty string to 'char'"); - assert(arf.elemSize == 1); - if(arf.carr.length > 1) - fail("Cannot cast string of non-unit length " ~ - .toString(arf.carr.length) ~ " to char"); - stack.pushChar(arf.carr[0]); - break; - - case BC.CastT2S: - { - // Get the type to cast from - val = code.getInt(); - Type t = Type.typeList[val]; - // Get the data - iarr = stack.popInts(t.getSize()); - // Let the type convert to string - char[] str = t.valToString(iarr); - // And push it back - stack.pushArray(str); - } - break; - - case BC.DownCast: - { - // Get the object on the stack - auto mo = getMObject(cast(MIndex)*stack.getInt(0)); - // And the class we're checking against - auto mc = global.getClass(cast(CIndex)code.getInt()); - - if(!mc.parentOf(mo)) - fail("Cannot cast object " ~ mo.toString ~ " to class " ~ mc.toString); - } - break; - - case BC.RefFunc: - // Convert function reference into name - val = stack.popInt(); // Function index - stack.popInt(); // Ignore the object index - stack.pushString(functionList[val].toString()); - break; - - case BC.FetchElem: - // This is not very optimized - val = stack.popInt(); // Index - arf = stack.popArray(); // Get the array - if(val < 0 || val >= arf.length) - fail("Array index " ~ .toString(val) ~ " out of bounds (array length is " - ~ .toString(arf.length) ~ ")"); - val *= arf.elemSize; - for(int i = 0; i arf.iarr.length || val2 > arf.iarr.length || - val > val2) - fail(format("Slice indices [%s..%s] out of range (array length is %s)", - i1, i2, arf.length)); - - // Slices of constant arrays are also constant - if(arf.isConst) - arf = arrays.createConst(arf.iarr[val..val2], arf.elemSize); - else - arf = arrays.create(arf.iarr[val..val2], arf.elemSize); - - stack.pushArray(arf); - break; - } - - case BC.FillArray: - arf = stack.popArray(); - if(arf.isConst) - fail("Cannot fill a constant array"); - val = code.getInt(); // Element size - assert(val == arf.elemSize || arf.isNull); - iarr = stack.popInts(val); // Pop the value - - // Fill the array - assert(arf.iarr.length % val == 0); - for(int i=0; i 2) - { - assert(arf.iarr.length % val2 == 0); - val = arf.length / 2; // Half the number of elements (rounded down) - val *= val2; // Multiplied back up to number of ints - for(int i=0; i= bcToString.length) - fail(format("Invalid command opcode %s", opCode)); - else - fail(format("Unimplemented opcode '%s' (%s)", - bcToString[opCode], opCode)); - } - } - assert(0); - } -} - -// Helper function for reversing arrays. Swaps the contents of two -// arrays. -void swap(int[] a, int[] b) -{ - const BUF = 32; - - assert(a.length == b.length); - int[BUF] buf; - uint len = a.length; - - while(len >= BUF) - { - buf[] = a[0..BUF]; - a[0..BUF] = b[0..BUF]; - b[0..BUF] = buf[]; - - a = a[BUF..$]; - b = b[BUF..$]; - len -= BUF; - } - - if(len) - { - buf[0..len] = a[]; - a[] = b[]; - b[] = buf[0..len]; - } -} - -// The scheduler singleton -Scheduler scheduler; - -struct Scheduler -{ - // Run lists - threads that run this or the next round. - NodeList run1, run2; - - // Waiting list - idle threads that are actively checked each frame. - NodeList wait; - - // List of transient nodes. Any thread in this list (that is not - // actively running) can and will be deleted when it goes into the - // background. - NodeList transient; - - // List of threads that are not running or scheduled, but should not - // be deleted. - NodeList paused; - - // The run lists for this and the next round. We use pointers to the - // actual lists, since we want to swap them easily. - NodeList* runNext, run; - - void init() - { - // Assign the run list pointers - run = &run1; - runNext = &run2; - } - - // Statistics: - - // Number of elements in the waiting list - int numWait() { return wait.length; } - - // Number of elements scheduled to run the next frame - int numRun() { return runNext.length; } - - // Number of remaining elements this frame - int numLeft() { return run.length; } - - // Total number of objects scheduled or waiting - int numTotal() - { return numRun() + numWait() + numLeft(); } - - // Do a complete frame. TODO: Make a distinction between a round and - // a frame later. We could for example do several rounds per frame, - // measured by some criterion of how much time we want to spend on - // script code or whether there are any pending items in the run - // list. We could do several runs of the run-list (to handle state - // changes etc) but only one run on the condition list (actually - // that is a good idea.) We also do not have to execute everything in - // the run list if it is long (otoh, allowing a build-up is not - // good.) But all this falls in the "optimization" category. - void doFrame() - { - assert(cthread is null, - "cannot run doFrame while another thread is running"); - checkConditions(); - dispatch(); - } - - void checkConditions() - { - // Go through the condition list for this round. - Thread* cn = wait.getHead(); - Thread* next; - while(cn != null) - { - // Get the next node here, since the current node might move - // somewhere else during this iteration, and then getNext will - // point to another list. - next = cn.getNext(); - - assert(cn.isScheduled); - - // This is an idle function and it is finished. Note that - // hasFinished() is NOT allowed to change the wait list in any - // way, ie to change object states or interact with the - // scheduler. In fact, hasFinished() should do as little as - // possible. - if(cn.fstack.isIdle) - { - if(cn.getIdle().hasFinished(cn)) - // Schedule the code to start running again this round. We - // move it from the wait list to the run list. - cn.moveTo(runNext); - } - // Set the next item - cn = next; - } - } - - void dispatch() - { - // Swap the runlist for the next frame with the current one. All - // code that is scheduled after this point is executed the next - // frame. - auto tmp = runNext; - runNext = run; - run = tmp; - - // Now execute the run list for this frame. Note that items might - // be removed from the run list as we go (eg. if a scheduled - // object has it's state changed) but this is handled. New nodes - // might also be scheduled, but these are added to the runNext - // list. - - // First element - Thread* cn = run.getHead(); - while(cn != null) - { - // Execute - cn.reenter(); - - // Get the next item. - cn = run.getHead(); - } - - // Check that we cleared the run list - assert(run.length == 0); - } -} diff --git a/old_d_version/monster/vm/vm.d b/old_d_version/monster/vm/vm.d deleted file mode 100644 index 92b453af0..000000000 --- a/old_d_version/monster/vm/vm.d +++ /dev/null @@ -1,334 +0,0 @@ -/* - Monster - an advanced game scripting language - Copyright (C) 2007-2009 Nicolay Korslund - Email: - 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); - } -} diff --git a/old_d_version/mscripts/config.mn b/old_d_version/mscripts/config.mn deleted file mode 100644 index 8c23a4880..000000000 --- a/old_d_version/mscripts/config.mn +++ /dev/null @@ -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; } diff --git a/old_d_version/mscripts/console.mn b/old_d_version/mscripts/console.mn deleted file mode 100644 index 0ad88d683..000000000 --- a/old_d_version/mscripts/console.mn +++ /dev/null @@ -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() } - diff --git a/old_d_version/mscripts/fpsticker.mn b/old_d_version/mscripts/fpsticker.mn deleted file mode 100644 index e956a107b..000000000 --- a/old_d_version/mscripts/fpsticker.mn +++ /dev/null @@ -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; -} diff --git a/old_d_version/mscripts/game/activator.mn b/old_d_version/mscripts/game/activator.mn deleted file mode 100644 index 06290635c..000000000 --- a/old_d_version/mscripts/game/activator.mn +++ /dev/null @@ -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; - diff --git a/old_d_version/mscripts/game/actor.mn b/old_d_version/mscripts/game/actor.mn deleted file mode 100644 index 99fca9baf..000000000 --- a/old_d_version/mscripts/game/actor.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/apparatus.mn b/old_d_version/mscripts/game/apparatus.mn deleted file mode 100644 index 3c3b83e29..000000000 --- a/old_d_version/mscripts/game/apparatus.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/armor.mn b/old_d_version/mscripts/game/armor.mn deleted file mode 100644 index 0719a4533..000000000 --- a/old_d_version/mscripts/game/armor.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/book.mn b/old_d_version/mscripts/game/book.mn deleted file mode 100644 index 13133d3bb..000000000 --- a/old_d_version/mscripts/game/book.mn +++ /dev/null @@ -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 diff --git a/old_d_version/mscripts/game/clothing.mn b/old_d_version/mscripts/game/clothing.mn deleted file mode 100644 index 9162017d2..000000000 --- a/old_d_version/mscripts/game/clothing.mn +++ /dev/null @@ -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 - } diff --git a/old_d_version/mscripts/game/container.mn b/old_d_version/mscripts/game/container.mn deleted file mode 100644 index 10bad5ee7..000000000 --- a/old_d_version/mscripts/game/container.mn +++ /dev/null @@ -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? diff --git a/old_d_version/mscripts/game/creature.mn b/old_d_version/mscripts/game/creature.mn deleted file mode 100644 index 4c1c4f6ba..000000000 --- a/old_d_version/mscripts/game/creature.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/door.mn b/old_d_version/mscripts/game/door.mn deleted file mode 100644 index c30dd95be..000000000 --- a/old_d_version/mscripts/game/door.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/enchantitem.mn b/old_d_version/mscripts/game/enchantitem.mn deleted file mode 100644 index bc92262cd..000000000 --- a/old_d_version/mscripts/game/enchantitem.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/gameobject.mn b/old_d_version/mscripts/game/gameobject.mn deleted file mode 100644 index 721fce4b2..000000000 --- a/old_d_version/mscripts/game/gameobject.mn +++ /dev/null @@ -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; -} diff --git a/old_d_version/mscripts/game/ingredient.mn b/old_d_version/mscripts/game/ingredient.mn deleted file mode 100644 index 8c34103b5..000000000 --- a/old_d_version/mscripts/game/ingredient.mn +++ /dev/null @@ -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... diff --git a/old_d_version/mscripts/game/inventoryitem.mn b/old_d_version/mscripts/game/inventoryitem.mn deleted file mode 100644 index 045f227f8..000000000 --- a/old_d_version/mscripts/game/inventoryitem.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/light.mn b/old_d_version/mscripts/game/light.mn deleted file mode 100644 index aa3f171b3..000000000 --- a/old_d_version/mscripts/game/light.mn +++ /dev/null @@ -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; - diff --git a/old_d_version/mscripts/game/lockedobject.mn b/old_d_version/mscripts/game/lockedobject.mn deleted file mode 100644 index 51d091607..000000000 --- a/old_d_version/mscripts/game/lockedobject.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/lockpick.mn b/old_d_version/mscripts/game/lockpick.mn deleted file mode 100644 index 98d5a6d5f..000000000 --- a/old_d_version/mscripts/game/lockpick.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/misc.mn b/old_d_version/mscripts/game/misc.mn deleted file mode 100644 index 55ac71803..000000000 --- a/old_d_version/mscripts/game/misc.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/npc.mn b/old_d_version/mscripts/game/npc.mn deleted file mode 100644 index 98c0a1573..000000000 --- a/old_d_version/mscripts/game/npc.mn +++ /dev/null @@ -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 - diff --git a/old_d_version/mscripts/game/person.mn b/old_d_version/mscripts/game/person.mn deleted file mode 100644 index 88ef19634..000000000 --- a/old_d_version/mscripts/game/person.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/player.mn b/old_d_version/mscripts/game/player.mn deleted file mode 100644 index 092605613..000000000 --- a/old_d_version/mscripts/game/player.mn +++ /dev/null @@ -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 diff --git a/old_d_version/mscripts/game/potion.mn b/old_d_version/mscripts/game/potion.mn deleted file mode 100644 index 5f0f4f02c..000000000 --- a/old_d_version/mscripts/game/potion.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/probe.mn b/old_d_version/mscripts/game/probe.mn deleted file mode 100644 index 5a6c07759..000000000 --- a/old_d_version/mscripts/game/probe.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/repairable.mn b/old_d_version/mscripts/game/repairable.mn deleted file mode 100644 index 12a253226..000000000 --- a/old_d_version/mscripts/game/repairable.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/repairitem.mn b/old_d_version/mscripts/game/repairitem.mn deleted file mode 100644 index 275ed229c..000000000 --- a/old_d_version/mscripts/game/repairitem.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/static.mn b/old_d_version/mscripts/game/static.mn deleted file mode 100644 index 91852b3d5..000000000 --- a/old_d_version/mscripts/game/static.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/tool.mn b/old_d_version/mscripts/game/tool.mn deleted file mode 100644 index e06d13852..000000000 --- a/old_d_version/mscripts/game/tool.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/game/weapon.mn b/old_d_version/mscripts/game/weapon.mn deleted file mode 100644 index e21b31c57..000000000 --- a/old_d_version/mscripts/game/weapon.mn +++ /dev/null @@ -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; diff --git a/old_d_version/mscripts/gmst.mn b/old_d_version/mscripts/gmst.mn deleted file mode 100644 index 027262641..000000000 --- a/old_d_version/mscripts/gmst.mn +++ /dev/null @@ -1,2088 +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 (gamesettings.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/ . - - */ - -// Contains all the game settings (GMST) variables of Morrowind, -// Tribunal and Bloodmoon. Based on "Morrowind Scripting for Dummies" -// (v9). -singleton GMST; - -// Most of the comments are copied from MSfD. A bit of cleanup is -// still needed. - -// Dirty GMST entries are handled automatically by the plugin loader, -// so we don't have to worry about this here. - -// Sets the general effectiveness of the repair skill of the -// character, via the armorer's hammer used -float fRepairMult; - -// Tells the game how many points of health are returned to the item -// when repaired -float fRepairAmountMult; - -float fSpellValueMult; -float fSpellMakingValueMult; - -// The setting for the price you pay at an enchanter to enchant an item. Linear. -float fEnchantmentValueMult; - -// Sets the cost of Silt Strider and boat travel (I think). Multiplies -// cost of Travel. Raising it raises the cost of Fast Travel -float fTravelMult; - -// Tells the game how much time elapses during this sort of travel -float fTravelTimeMult; - -// Sets the cost of Guild Guide travel -float fMagesGuildTravel; - -// Is compared to your alchemy skill to determine which of the effects -// of an ingredient you can see. -(Wakim, Iudas) -float fWortChanceValue; - -float fMinWalkSpeed; // This is the minimum walking speed of the PC, - // regardless of stats, skills or encumbrance -float fMaxWalkSpeed; // This is the maximum walking speed of the PC, - // regardless of stats, skills, or encumbrance - -// The actual walking speed of NPC's (and the PC) is set by checking -// various factors (Speed, Athletics, etc.) and assigning a value -// between fMinWalkSpeed and fMaxWalkSpeed based on that. The two -// settings dictate the spectrum of Walk Speeds - -float fMinWalkSpeedCreature; // The same as for the PC, but if you - // badly encumber a creature it'll move - // veeerrry slowly. I've done this by - // accident. - -float fMaxWalkSpeedCreature; // Same as above, they get faster, so they - // cover the speed spectrum more rapidly - -float fEncumberedMoveEffect; // This sets how encumbrance affects - // walking and running speed, within the - // min/max limits set by other values. - -float fBaseRunMultiplier; // Exactly as it says. Changing the value - // will increase/decrease base running - // speed. Dictates how much faster Running is - // than the current Walk Speed - -float fAthleticsRunBonus; // Sets how Athletics affects running speed. - -float fJumpAcrobaticsBase; // Sets the base jumping distance for the - // PC. - -float fJumpAcroMultiplier; // Sets the multiplier for Acrobatics, which - // is why you can leap over tall buildings - // when your Acro is high enough. - -float fJumpEncumbranceBase; // Effects how greatly jumping ability is - // effected by Encumbrance, but I'm unsure - // how - -float fJumpEncumbranceMultiplier; // Effects how greatly jumping - // ability is effected by - // Encumbrance, but I'm unsure how - -float fJumpRunMultiplier; // UNSURE. Presumably effects Jump Distance - // while running (it doesn't seem to effect - // height, but I could be wrong) - -float fJumpMoveBase; -float fJumpMoveMult; -float fSwimWalkBase; // Multiplies your walking speed to achieve the - // swimming speed at a 'walk' Base swim speed - // while 'walking' - -float fSwimRunBase; // Multiplies your running speed to achieve the - // swimming speed at a 'run'. - -float fSwimWalkAthleticsMult; // Tells the game how Athletics affects - // 'walking' swimming speed. These low - // values keep you from flying through - // the water like you do on land when - // your Athletics is high. - -float fSwimRunAthleticsMult; // Same as above. - -float fSwimHeightScale; // Determines how close to the surface you have - // to be before the breathe indicator goes away - -float fHoldBreathTime; // The number of seconds your PC can hold her - // breath. Base time that a character can remain - // underwater before incurring suffocation - // damage - -float fHoldBreathEndMult; // How Endurance affects the time you can - // hold your breath. I believe this is a - // flat-out multiplier to End, added as - // seconds to HoldBreathTime. - -float fSuffocationDamage; // The amount of health damage you take each - // second you suffocate - -float fMinFlySpeed; // Exactly as it says. minimum flying speed. -float fMaxFlySpeed; - -float fStromWindSpeed; // UNSURE - Determines altered walk speed during - // an ash or blight storm, but I'm unsure - // how. Might be a separate value from that - // entirely. might determine speed of storm - // particles /sprites(Dust, etc.) Interesting - // (possibally related) note, while treading - // water in an ash storm, I noticed I was moving - // slightly. - -float fStromWalkMult; // Determines altered walk speed during an ash or - // blight storm, but I'm unsure how uses the - // getwindspeed to lower the PC movement speed - // during storms... - -float fFallDamageDistanceMin; // The minimum distance you have to fall - // before you take damage. (Presumably in - // units) In game units each unit - .0.56 - // inches - -float fFallDistanceBase; // This will increase/decrease the distance - // needed to fall before you take damage. - -float fFallDistanceMult; // Higher you are the more damage you take - // when you hit - -float fFallAcroBase; // Acrobatics skill increases the distance you can - // fall before you take damage. - -float fFallAcroMult; // Has to do w/ how the Acrobatics skill effects - // Fall Distance and Damage, but unsure how - -int iMaxActivateDist; // Maximum distance for the player to be able to - // 'Activate' an object - approx 9 feet - -int iMaxInfoDist; // Maximum distance for an Info message - // (object/NPC/creature name, etc.) to pop up in the - // Player's view - -float fVanityDelay; // Seconds until VanityMode begins. the camera - // starts circling the player if there is no input - // via mouse or keyboard. - -float fMaxHeadTrackDistance; // IIRC, this is the maximum distance an - // NPC or creature can be away from - // another NPC or creature and still - // trigger the 'head follow' routine you - // sometimes see. Put your PC in Balmora, - // let it go to Vanity View and you'll see - // your PC watch passing NPCs and 'follow' - // their movements for a certain amount of - // time. - -float fInteriorHeadTrackMult; // UNSURE. something to do w/ the - // modifier for this in Interiors. Do - // they track at half-distance in - // Interiors? - -// These values are used to set what weights are used to determine -// whether a piece of armor is light, medium, or heavy. Altering a -// value alters these categories for *all* armor of that type -// *everywhere* in the game. Rather nice, actually; I used it in redux -// to set weight categories for all of my armor types across the -// board. - -int iHelmWeight; -int iPauldronWeight; -int iCuirassWeight; -int iGauntletWeight; -int iGreavesWeight; -int iBootsWeight; -int iShieldWeight; - -// These values are used in conjunction with armor weights to set the -// weight classes (Light, Medium, Heavy). - -float fLightMaxMod; -float fMedMaxMod; - -// These two values dictate the range of AR for characters going -// Unarmored, based on the Unarmored skill. As they are, the settings -// produce a Maximum Unarmored AR of 65 (at 100 Unarmored skill), -// which you can see is some for of multiplication between the two -// settings. Reversing the values produces the same effect , so it -// seems the values are interchangeable (unless I missed -// something. could be wrong) - Changing one to 1.000 and the other to -// 0.0650 results in a max Unarmored AR of 650. The game multiplies -// the numbers and then multiplies the resulting number by 1000 to -// obtain the actual in-game Max Unarmored AR. Doesn't seem to effect -// Min AR independently, only max - Progression of AR (from low skill -// to high skill) appears to be hard-coded. It has also been found -// that the Unarmored skill doesn't work at all UNLESS atleast one -// item of armor is worn. (Forum Info / The other Felix) - -float fUnarmoredBase1; -float fUnarmoredBase2; - -int iBaseArmorSkill; // The Skill Level where in-game armors reach - // their base (i.e. 'In-Editor') AR value ' - // Example: Glass Armor has a Base AR of 50. At - // Light Armor skill level 30 (as indicated - // above), it will read as having AR 50 - // in-game. Before Skill Level 30, armors have - // diminished AR's from their Base Value, and - // after Skill Level 30, armors have higher AR's - // than their base until Skill Level reaches - // 100. I haven't figured out the game's scheme - // for determining the mult value yet - -float fBlockStillBonus; // UNSURE. Presumably the amount that standing - // still increases the chance to block - -float fDamageStrengthBase; // Your STR adds to the damage you do with - // weapons. This determines how much damage - // is added. - -float fDamageStrengthMult; // Effects amount that Strength effects - // damage dealt in combat (Unsure of how - // this value relates to in-game effect) -float fSwingBlockBase; -float fSwingBlockMult; - -float fFatigueBase; // How much fatigue you lose while - // walking. However, this appears to let you jump - // very high without getting hurt (Bug?. Forum Info - // / DinkumThinkum). All effect 'Fatigue' in-game, - // obviously. For separate actions, although not - // all of them actually have an effect in-game - // (I've never been able to get spells to reduce - // fatigue). They seem pretty self-explanatory, but - // I haven't tested them thoroughly - -float fFatigueMult; // Used to determine successful chance of casting - // if you are fatigued - -float fFatigueReturnBase; // How much fatigue you regain per - // second. This is why you don't actually - // fatigue while walking. - -float fFatigueReturnMult; // How much fatigue returns per second while - // walking - -float fEndFatigueMult; - -float fFatigueAttackBase; // How much fatigue you lose with every melee - // attack you make - -float fFatigueAttackMult; -float fWeaponFatigueMult; - -float fFatigueBlockBase; // How much fatigue you lose blocking with a - // shield. - -float fFatigueBlockMult; // This will increase the amount of fatigue - // lost when blocking with a shield. - -float fWeaponFatigueBlockMult; -float fFatigueRunBase; // How much fatigue you lose running. - -float fFatigueRunMult; // This one appears to work with encumbrance,the - // more encumbered the more fatigue you - // lose/second - -float fFatigueJumpBase; // How much fatigue you lose jumping. - -float fFatigueJumpMult; // Modifier for fatigue loss - -float fFatigueSwimWalkBase; // How much fatigue you lose swimming at a - // 'walk' - -float fFatigueSwimRunBase; // How much fatigue you lose swimming at a - // 'run'. - -float fFatigueSwimWalkMult; // Modifier for fatigue loss - -float fFatigueSwimRunMult; // Modifier for fatigue loss - -float fFatigueSneakBase; // The base level of fatigue loss while - // sneaking - -float fFatigueSneakMult; // Multiplier to that base level -float fMinHandToHandMult; -float fMaxHandToHandMult; -float fHandtoHandHealthPer; - -float fCombatInvisoMult; // Reduce the chance to hit the PC when he is - // chameleoned or invisible - -float fCombatKODamageMult; -float fCombatCriticalStrikeMult; // This one appears to only work if - // you hit someone unawares while - // sneaking. I never got it to do - // anything else. 4x damage from a - // successful sneak attack works when - // chameleoned or invisible - -int iBlockMinChance; // Minimum chance of blocking with a shield -int iBlockMaxChance; // Maximum chance of blocking with a shield - -float fLevelUpHealthEndMult; // Multiplies current END to get hit - // points added at level-up. - -float fSoulGemMult; // A soul gem's monetary value is multiplied by - // this value to determine the soul capacity of a - // soul gem. Creatures with a soul value less than - // or equal to that capacity can float it; // in - // the gem. - -float fEffectCostMult; // The setting for all magicka costs for all - // spell effects. Changing this will change what - // all spells and enchantments - // cost. Everything. Linear change. Doubling - // this makes all spells cost twice as much - // magicka, all enchanted items cost twice as - // many charges. - -float fSpellPriceMult; -float fFatigueSpellBase; -float fFatigueSpellMult; -float fFatigueSpellCostMult; -float fPotionStrengthMult; -float fPotionT1MagMult; -float fPotionT1DurMult; -float fPotionMinUsefulDuration; -float fPotionT4BaseStrengthMult; -float fPotionT4EquipStrengthMult; -float fIngredientMult; // Min # of an ingredient required to make a potion -float fMagicItemCostMult; -float fMagicItemPriceMult; -float fMagicItemOnceMult; -float fMagicItemUsedMult; -float fMagicItemStrikeMult; -float fMagicItemConstantMult; - -//UNUSED (? check the pdf again) -float fEnchantmentMult; // Setting for how much enchantment an item can - // hold based upon the value set in each - // individual item's property file. Linear, if - // an item in TESCS shows an enchantment value - // of 1200 (i.e. an exquisite ring) multiply it - // by fEnchantmentMult to get the actual - // enchantment you'll see in the make an - // enchanted item window. - -float fEnchantmentChanceMult; // These affect the PC's chance of making - // an enchantment - -float fPCbaseMagickaMult; // This sets the spell point multiplier for - // the PC with respect to INT (e.g., 1 x INT - // with this setting) - -float fNPCbaseMagickaMult; // This does the same thing for NPCs. - -float fAutoSpellChance; -float fAutoPCSpellChance; -int iAutoSpellTimesCanCast; -int iAutoSpellAttSkillMin; -int iAutoSpellAlterationMax; -int iAutoSpellConjurationMax; -int iAutoSpellDestructionMax; -int iAutoSpellIllusionMax; -int iAutoSpellMysticismMax; -int iAutoSpellRestorationMax; -int iAutoPCSpellMax; -int iAutoRepFacMod; // A positive modification to relations you get - // with people who belong to the same faction -int iAutoRepLevMod; // You can apparently add rep points with each - // level-up. I've never tried it. - -int iMagicItemChargeOnce; -int iMagicItemChargeConst; -int iMagicItemChargeUse; -int iMagicItemChargeStrike; - -// 1068-1071 effect the amount of charges auto-calculated on magic -// items based on their function. This value is the number of uses -// that the game will account for when calculating the max charges of -// a magic item (works universally, across the board with all 'ingame -// items '1068 is the setting for the number of charges an -// automatically calculated cast once effect enchanted item will -// have. Formula is BaseSpellEffectCost x -// iMagicItemChargeOnce. Linear. This way an item with a cast once -// effect will have exactly the number of charges needed to cast the -// effect upon it 1069 for const effect items 1070 is the setting for -// the multiplier for charges for automatically calculated cast when -// used effect enchanted items. See above for explanation. 1071 for ; -// //Cast on Strike; // items (charges are calculated to account for X -// 'uses' with this value as is) - -int iMonthsToRespawn; // The time to respawn things like the Fighters - // Guild/Mages Guild chests, etc. How many months - // before a picked plant respawns - // ingredients. Chests in guilds respawn contents - // the same as any other chests. - -float fCorpseClearDelay; // How many hours it takes before a - // non-persistent corpse disappears. Also - // controls time before certain temporary data - // is cleared if the object has not been in an - // active cell in that time (e.g. Talked to PC - // flag). - -float fCorpseRespawnDelay; // How many hours it takes before a - // respawnable creature actually respawns - // (note that this doesn't seem to work - // properly). - -float fBarterGoldResetDelay; // How many hours it takes before a trader - // resets it barter gold to its default - // value. - -float fEncumbranceStrMult; // A straight multiplier to STR to see how - // much a PC/NPC/creature can carry. - -float fPickLockMult; // Dictates amount that Lock pick difficulty - // raises according to Lock Level. Lower the value - // here, the harder it gets. Positive values make - // locks get easier with higher lock Levels - -float fTrapCostMult; // Dictates difficulty of traps based on the spell - // cost of the spell assigned as trap. Lower the - // value here, the harder it gets to disarm - // (again, based on spell cost of assigned 'trap') - // The values is multiplied by the spell cost of a - // trap and then added to your chance of disarming - // it. Since it's set to zero, the trap spell's - // cost is not incorporated into the chance. So - // basically it's also unused. - -float fMessageTimePerChar; -float fMagicItemRechargePerSecond; // This is the setting for the - // amount of charges restored to a - // charged magic item per second of - // game play. Linear. 0.05 x 20 - // seconds = 1 charge restored. - -int i1stPersonSneakDelta; - -// If you barter with a merchant successfully, your disposition with -// that merchant increases by one and falls by 1 if you fail a barter -// attempt. - -int iBarterSuccessDisposition; -int iBarterFailDisposition; - -int iLevelupTotal; // How many skill points you need before you level - // up. -int iLevelupMajorMult; // How much each major skill is worth in - // points. E.g., if you set this to 2 then each - // point earned in a major skill counts as 2 - // skill points for leveling up. - -int iLevelupMinorMult; // Same as above, but for minor skills. - -// I *think* - not sure if I remember this correctly - but I think -// this works like the above, but for skills governed by your two -// primary attributes. So if your primary attributes are STR and AGI, -// and you set 1087 to 2, then any major skill governed by one of -// these attributes which goes up by a point counts as 2 points for -// purposes of leveling. - -int iLevelupMajorMultAttribute; -int iLevelupMinorMultAttribute; -int iLevelupMiscMultAttriubte; - -int iLevelupSpecialization; -int iLevelUp01Mult; -int iLevelUp02Mult; -int iLevelUp03Mult; -int iLevelUp04Mult; -int iLevelUp05Mult; -int iLevelUp06Mult; -int iLevelUp07Mult; -int iLevelUp08Mult; -int iLevelUp09Mult; -int iLevelUp10Mult; - -// The game keeps track of how many skill points you've gained since -// the last level up. If you gained 8 skill points in skills governed -// by AGI, then when you get to distribute attribute points whatever -// number is in place for iLevelUp08Mult will be used for AGI. So if -// this value is 4, you'll see a x 4 next to AGI when you level up -// (you'll get 4 points in AGI if you pick this during the leveling -// process). - -int iSoulAmountForConstantEffect; // This is the setting for the - // minimum soul value to toggle the - // constant effect button in the - // enchantment creation window. - -float fConstantEffectMult; // UNUSED - -float fEnchantmentConstantDurationMult; // This setting is the - // multiplier for constant - // effect cast cost as compared - // to a 0 duration spell. so - // restore health 2-2 for 0 - // secs, which costs 0.50 to - // cast as a spell, costs 0.5 x - // 100 = 50 as a constant - // effect. - -float fEnchantmentConstantChanceMult; - -float fWeaponDamageMult; // weapon damage during combat. depreciation - // as it were. - -float fSeriousWoundMult; // UNUSED - -float fKnockDownMult; // This sets the chance for a knock-down - // factored on how much damage you do in a - // single blow. - -// Sets the base odds for a knockdown when the condition for it is met - -int iKnockDownOddsBase; -int iKnockDownOddsMult; - -float fCombatArmorMinMult; -float fHandToHandReach; // Sets the reach of HTH weapons. Values of - // less than 1.0 have no meaning. - -float fVoiceIdleOdds; // Controls likelihood of an NPC 'speaking' a - // voice clip when Idle (Unsure of specifics) - -int iVoiceAttackOdds; // Controls likelihood of an NPC 'speaking' a - // voice clip when attacking (Unsure of - // specifics) - -int iVoiceHitOdds; // Controls likelihood of an NPC 'speaking' a voice - // clip when being hit (Unsure of specifics) - -float fProjectileMinSpeed; // Sets the minimum speed of projectile weapons -float fProjectileMaxSpeed; // Dictates maximum speed of projectiles - // from bows and crossbows - -float fThrownWeaponMinSpeed; // Sets the minimum speed of thrown weapons -float fThrownWeaponMaxSpeed; // Dictates Max speed of thrown weapons -float fTargetSpellMaxSpeed; // Sets the speed of spells. Double this - // and your spells will *zip* across the - // screen! Min speed is apparently - // hard-coded - -float fProjectileThrownStoreChance; // The odds of getting arrows back - // when you loot a corpse. Thrown - // weapons also - -int iPickMinChance; // Minimum pickpocketing chance (forum info / - // Iudas) -int iPickMaxChance; // Maximum pickpocketing chance (forum info / - // Iudas) -float fDispRaceMod; // You have better relations with your own race - // than with others. - -// These determine how personality affect NPC disposition -float fDispPersonalityMult; -float fDispPersonalityBase; - -// These determine how your rank in a faction will alter your -// relations with people that belong to that faction. This is why when -// you reach high ranks in a faction everyone in that faction suddenly -// becomes very friendly. - -float fDispFactionMod; -float fDispFactionRankBase; -float fDispFactionRankMult; - -float fDispCrimeMod; // This is multiplied by the player's crime level - // (bounty) to determine how that information - // affects an NPC's disposition towards the - // player. - -float fDispDiseaseMod; // How much disposition is lowered when you're - // suffering from a disease. - -int iDispAttackMod; // Not completely sure. NPC disposition modifier if - // PC attacks said NPC - -float fDispWeaponDrawn; // How much disposition is lowered when you - // have a weapon drawn. - - -// I don't remember if these work the same as the previous barter -// disposition values, or if these are multipliers. These effect the -// long term disposition of the merchant - -float fDispBargainSuccessMod; -float fDispBargainFailMod; - -float fDispPickPocketMod; // NPC disposition modifier for catching the - // PC attempting to pickpocket them - -int iDaysinPrisonMod; // Determines prison time based on your crime - // level. - -float fDispAttacking; // Unsure. I believe it's an NPC Disposition - // modifier if the PC is attacking something - // other than the NPC, ie it effects - // non-combatants dispostion. - -float fDispStealing; // Unsure - I believe it's an NPC Disposition - // modifier if the PC is stealing from someone - // other than the NPC it effects - -int iDispTresspass; // NPC Disposition modifier for catching the PC - // 'trespassing'. Not sure what that exactly means - // in-game - -int iDispKilling; // Unsure NPC Disposition modifier for witnessing the - // PC kill an innocent NPC (I think) - -int iTrainingMod; // Determines training costs. The higher the value, - // the more training costs. Unsure of method of - // calculation - -int iAlchemyMod; // Controls the value of player-made potions. Set to 0 - // and player made potions have 0 value, set to 1 and - // player made potions have about 1/4 to 1/2 of what - // they have if you leave it at the default 2 (forum - // info / BeanCounter, Iudas). - -float fBargainOfferBase; // This is multiplied by the item's value to - // determine what the merchant will offer when - // selling. Base value is also modified by PC - // level. Base amount that merchants will buy - // items from you for, in percentage points - // Believe it goes both ways, but I'm unsure - // how it would work the 'other way' - -float fBargainOfferMulti; // Effects how much the merchant lowers his - // offers during a bargaining session - -float fDispositionMod; -float fPersonalityMod; -float fLuckMod; // IIRC, this is multiplied by your Luck as a - // percentage to get a base increase to all skills. -float fReputationMod; -float fLevelMod; -float fBribe10Mod; // Dictates amount that NPC Disposition will raise - // on a successful 10 Gold Bribe. Don't believe it's - // in straight disposition points. Could be - // percentages - (Other factors like race, sex, - // opposing faction etc. reduce this amount - // significantly) - -float fBribe100Mod; // Dictates amount that NPC Disposition will raise - // on a successful 100 Gold Bribe. See above. - -float fBribe1000Mod; // Dictates amount that NPC Disposition will raise - // on a successful 1000 Gold Bribe. See above. - -float fPerDieRollMult; -float fPerTempMult; // is used in just about every disposition - // modifying calculation. - -int iPerMinChance; -int iPerMinChange; - -// These all determine how fast you gain skill points in each -// skill. The lower the value, the faster you'll gain skill -// points. The values are multiplied by whatever rate you set for each -// individual skill. -float fSpecialSkillBonus; -float fMajorSkillBonus; -float fMinorSkillBonus; -float fMiscSkillBonus; - -int iAlarmKilling; -int iAlarmAttack; -int iAlarmStealing; -int iAlarmPickPocket; -int iAlarmTresspass; - -float fAlarmRadius; // When an NPC raises the alarm, this is the base - // radius for response by other affiliated NPCs. - -// These set the gold value for crimes. I believe that fCrimeStealing -// is multiplied by the price of the item stolen. - -int iCrimeKilling; -int iCrimeAttack; -float fCrimeStealing; -int iCrimePickPocket; -int iCrimeTresspass; - -int iCrimeThreshold; // When NPC's start to react negatively to the PC -int iCrimeThresholdMultiplier; -float fCrimeGoldDiscountMult; // Thieves guild discount when you have a - // price on your head. - -float fCrimeGoldTurnInMult; // Discount on the fine if you turn - // yourself in. -int iFightAttack; -int iFightAttacking; -int iFightDistanceBase; -float fFightDistanceMultiplier; -int iFightAlarmMult; -float fFightDispMult; -float fFightStealing; -int iFightPickpocket; -int iFightTrespass; -int iFightKilling; -int iFlee; // UNUSED -int iGreetDistanceMultiplier; // Used for those annoying voice - // greetings NPCs use when you get too - // close Specifically (if the - // Construction Set help is to be - // believed) this is multiplied by their - // hello rating to get the distance - // before they talk. -int iGreetDuration; -float fGreetDistanceReset; // How far away from an NPC you have to get - // before they check for a voice greeting - // again. -float fIdleChanceMultiplier; // Probability multiplier that an NPC will - // mumble something while standing idly - // near the PC -float fSneakUseDist; // Helps determine if you can sneak -float fSneakUseDelay; // Helps determine how long before the Sneak Icon - // come on - -float fSneakDistanceBase; // see above -float fSneakDistanceMultiplier; // see above -float fSneakSpeedMultiplier; // Multiplied by base walking speed to see - // how fast you move while sneaking. - -float fSneakViewMult; // Makes it more difficult to sneak when in view - // of an NPC. -float fSneakNoViewMult; // Makes it easier to sneak when you aren't in - // view. -float fSneakSkillMult; -float fSneakBootMult; // Multiplied by the boot value (weight?) to - // determine the reduction to Sneak skill. - -float fCombatDistance; // Combined with weapon reach, determines the - // effective distance that hits can be obtained -float fCombatAngleXY; -float fCombatAngleZ; -float fCombatForceSideAngle; -float fCombatTorsoSideAngle; -float fCombatTorsoStartPercent; -float fCombatTorsoStopPercent; -float fCombatBlockLeftAngle; -float fCombatBlockRightAngle; - -// Shields are worn on the left and partially block attacks from 90 -// degrees left to 30 degrees right of the PC's facing. - -float fCombatDelayCreature; -float fCombatDelayNPC; -float fAIMeleeWeaponMult; // Used in the determination of how far away - // an NPC will flee if they flee combat and - // the PC has a melee weapon in hand -float fAIRangeMeleeWeaponMult; // as above but the PC has a crossbow or Bow in hand -float fAIMagicSpellMult; -float fAIRangeMagicSpellMult; // As above but the PC has a spell readied -float fAIMeleeArmorMult; -float fAIMeleeSummWeaponMult; -float fAIFleeHealthMult; // Alters the opponents flee rating when health declines -float fAIFleeFleeMult; // Used to alter base flee ratings. -float fPickPocketMod; -// Multiplier to item's weight vs player's security. As far as I've - -// tested it, chances are as follows: -// Enter/Leave inventory: -// Player.Sneak > d100 + Victim.Sneak -// Take items: -// Player.Security > d100 + fPickPocketMod * item Weight - -// In the moment you successfully take an item, the inventory is -// reloaded (this is why sometimes items vanish or appear) and you -// have to make another Enter/Leave inventory check, so effectively -// both checks are made when taking items. When the victim can't -// detect you (sneaking, invisibility or chameleon) you always succeed -// in entering the inventory, though only chameleon will also help you -// in taking items and leaving the inventory. (JOG) - -float fSleepRandMod; // Affects the chance of a mob waking the PC up - // while asleep in the wilderness. - -float fSleepRestMod; // Unused (Thanks to Damar Stiehl for these two) -int iNumberCreatures; - -float fAudioDefaultMinDistance; -float fAudioDefaultMaxDistance; -float fAudioVoiceDefaultMinDistance; -float fAudioVoiceDefaultMaxDistance; -float fAudioMinDistanceMult; -float fAudioMaxDistanceMult; -float fNPCHealthBarTime; // Controls delay before the Opponents health - // bar disappears -float fNPCHealthBarFade; // Controls how many seconds the bar float - // fades (rather than abruptly vanishing) -float fDifficultyMult; -float fMagicDetectRefreshRate; -float fMagicStartIconBlink; // The number of seconds a spell icon will - // fade before the spell runs out, on the - // lower right-hand corner of the screen. -float fMagicCreatureCastDelay; -float fDiseaseXferChance; // The chance of catching a disease if hit by - // a creature, or looting a diseased - // creature's corpse. -float fElementalShieldMult; -float fMagicSunBlockedMult; // Vampire weakness -float fWereWolfRunMult; // Werewolf run speed multiplier. -float fWereWolfSilverWeaponDamageMult; // The damage multiplier for - // silver weapon damage against - // all werewolves. -int iWereWolfBounty; -float fWereWolfStrength; -float fWereWolfAgility; -float fWereWolfEndurance; -float fWereWolfSpeed; -float fWereWolfHandtoHand; -float fWereWolfUnarmored; -float fWereWolfAthletics; -float fWereWolfAcrobatics; -float fWereWolfInteligence; -float fWereWolfWillPower; -float fWereWolfPersonality; -float fWereWolfLuck; -float fWereWolfBlock; -float fWereWolfArmorer; -float fWereWolfMediumArmor; -float fWereWolfHeavyARmor; -float fWereWolfBluntWeapon; -float fWereWolfLongBlade; -float fWereWolfAxe; -float fWereWolfSpear; -float fWereWolfDestruction; -float fWereWolfAlteration; -float fWereWolfIllusion; -float fWereWolfConjuration; -float fWereWolfMysticism; -float fWereWolfRestoration; -float fWereWolfEnchant; -float fWereWolfAlchemy; -float fWereWolfSecurity; -float fWereWolfSneak; -float fWereWolfLightArmor; -float fWereWolfShortBlade; -float fWereWolfMarksman; -float fWereWolfSpeechcraft; - -// These are the skills and attributes for Werewolf form. - -int iWereWolfLevelToAttack; -int iWereWolfFightMod; -int iWereWolfFleeMod; -float fWereWolfHealth; -float fWereWolfFatigue; -float fWereWolfMagica; -float fCombatDistaceWereWolfMod; // Determines the attack range of a - // Werewolf. -float fFleeDistance; // Determines how far away someone will flee. - -// NOT documented in Scripting for Dummies. -float fWaterReflectUpdateAlways; -float fWaterReflectUpdateSeldom; -float fRestMagicMult; - -char[] sMonthMorningstar; -char[] sMonthSunsdawn; -char[] sMonthFirstseed; -char[] sMonthRainshand; -char[] sMonthSecondseed; -char[] sMonthMidyear; -char[] sMonthSunsheight; -char[] sMonthLastseed; -char[] sMonthHeartfire; -char[] sMonthFrostfall; -char[] sMonthSunsdusk; -char[] sMonthEveningstar; -char[] sSpecialization; -char[] sSpecializationCombat; -char[] sSpecializationMagic; -char[] sSpecializationStealth; -char[] sSkillClassMajor; -char[] sSkillClassMinor; -char[] sSkillClassMisc; -char[] sRacialTraits; -char[] sHealthPerHourOfRest; -char[] sHealthPerLevel; -char[] sGoverningAttribute; -char[] sClass; -char[] sTraits; -char[] sJournal; -char[] sOK; -char[] sClose; -char[] sPrev; -char[] sNext; -char[] sTake; -char[] sWeaponTab; -char[] sApparelTab; -char[] sMagicTab; -char[] sMiscTab; -char[] sWornTab; -char[] sAllTab; -char[] sAttack; -char[] sUses; -char[] sQuality; -char[] sCharges; -char[] sArmorRating; -char[] sSlash; -char[] sThrust; -char[] sChop; -char[] sEncumbrance; -char[] sCondition; -char[] sWeight; -char[] sValue; -char[] sLight; -char[] sMedium; -char[] sHeavy; -char[] sCancel; -char[] sTakeAll; -char[] sUntilHealed; -char[] sRest; -char[] sWait; -char[] sRestIllegal; -char[] sGold; -char[] sMove; -char[] sTo; -char[] sActorInCombat; -char[] sArmor; -char[] sTopics; -char[] sService; -char[] sPersuasion; -char[] sBye; -char[] sGoodbye; -char[] sYes; -char[] sNo; -char[] sOn; -char[] sOff; -char[] sFull; -char[] sWorld; -char[] sExpelled; -char[] sExpelledMessage; -char[] sFavoriteSkills; -char[] sGame; -char[] sVideo; -char[] sAudio; -char[] sControls; -char[] sPrefs; -char[] sPreferences; -char[] sNewGame; -char[] sExitGame; -char[] sOptions; -char[] sSaveGame; -char[] sLoadGame; -char[] sDeleteGame; -char[] sSaveGameDenied; -char[] sSaveGameFailed; -char[] sSaveGameTooBig; -char[] sSaveGameNoMemory; -char[] sMaximumSaveGameMessage; -char[] sRender_Distance; -char[] sNear; -char[] sFar; -char[] sGamma_Correction; -char[] sLight_Gamma; -char[] sDark_Gamma; -char[] sSeldom; -char[] sAlways; -char[] sWaterTerrainReflect; -char[] sWaterReflectUpdate; -char[] sMaster; -char[] sVoice; -char[] sEffects; -char[] sFootsteps; -char[] sMusic; -char[] sTransparency_Menu; -char[] sMenu_Help_Delay; -char[] sAIDistance; -char[] sHigh; -char[] sLow; -char[] sDetail_Level; -char[] sMenus; -char[] sStrip; -char[] sReturnToGame; -char[] sForward; -char[] sBack; -char[] sLeft; -char[] sRight; -char[] sUse; -char[] sActivate; -char[] sReady_Weapon; -char[] sReady_Magic; -char[] sCrouch_Sneak; -char[] sRun; -char[] sAlways_Run; -char[] sAuto_Run; -char[] sJump; -char[] sNextWeapon; -char[] sPrevWeapon; -char[] sNextSpell; -char[] sPrevSpell; -char[] sTogglePOVCmd; -char[] sMenu_Mode; -char[] sJournalCmd; -char[] sRestKey; -char[] sQuickMenu; -char[] sQuick1Cmd; -char[] sQuick2Cmd; -char[] sQuick3Cmd; -char[] sQuick4Cmd; -char[] sQuick5Cmd; -char[] sQuick6Cmd; -char[] sQuick7Cmd; -char[] sQuick8Cmd; -char[] sQuick9Cmd; -char[] sQuick10Cmd; -char[] sQuickSaveCmd; -char[] sQuickLoadCmd; -char[] sKeyName_00; -char[] sKeyName_01; -char[] sKeyName_02; -char[] sKeyName_03; -char[] sKeyName_04; -char[] sKeyName_05; -char[] sKeyName_06; -char[] sKeyName_07; -char[] sKeyName_08; -char[] sKeyName_09; -char[] sKeyName_0A; -char[] sKeyName_0B; -char[] sKeyName_0C; -char[] sKeyName_0D; -char[] sKeyName_0E; -char[] sKeyName_0F; -char[] sKeyName_10; -char[] sKeyName_11; -char[] sKeyName_12; -char[] sKeyName_13; -char[] sKeyName_14; -char[] sKeyName_15; -char[] sKeyName_16; -char[] sKeyName_17; -char[] sKeyName_18; -char[] sKeyName_19; -char[] sKeyName_1A; -char[] sKeyName_1B; -char[] sKeyName_1C; -char[] sKeyName_1D; -char[] sKeyName_1E; -char[] sKeyName_1F; -char[] sKeyName_20; -char[] sKeyName_21; -char[] sKeyName_22; -char[] sKeyName_23; -char[] sKeyName_24; -char[] sKeyName_25; -char[] sKeyName_26; -char[] sKeyName_27; -char[] sKeyName_28; -char[] sKeyName_29; -char[] sKeyName_2A; -char[] sKeyName_2B; -char[] sKeyName_2C; -char[] sKeyName_2D; -char[] sKeyName_2E; -char[] sKeyName_2F; -char[] sKeyName_30; -char[] sKeyName_31; -char[] sKeyName_32; -char[] sKeyName_33; -char[] sKeyName_34; -char[] sKeyName_35; -char[] sKeyName_36; -char[] sKeyName_37; -char[] sKeyName_38; -char[] sKeyName_39; -char[] sKeyName_3A; -char[] sKeyName_3B; -char[] sKeyName_3C; -char[] sKeyName_3D; -char[] sKeyName_3E; -char[] sKeyName_3F; -char[] sKeyName_40; -char[] sKeyName_41; -char[] sKeyName_42; -char[] sKeyName_43; -char[] sKeyName_44; -char[] sKeyName_45; -char[] sKeyName_46; -char[] sKeyName_47; -char[] sKeyName_48; -char[] sKeyName_49; -char[] sKeyName_4A; -char[] sKeyName_4B; -char[] sKeyName_4C; -char[] sKeyName_4D; -char[] sKeyName_4E; -char[] sKeyName_4F; -char[] sKeyName_50; -char[] sKeyName_51; -char[] sKeyName_52; -char[] sKeyName_53; -char[] sKeyName_54; -char[] sKeyName_55; -char[] sKeyName_56; -char[] sKeyName_57; -char[] sKeyName_58; -char[] sKeyName_59; -char[] sKeyName_5A; -char[] sKeyName_5B; -char[] sKeyName_5C; -char[] sKeyName_5D; -char[] sKeyName_5E; -char[] sKeyName_5F; -char[] sKeyName_60; -char[] sKeyName_61; -char[] sKeyName_62; -char[] sKeyName_63; -char[] sKeyName_64; -char[] sKeyName_65; -char[] sKeyName_66; -char[] sKeyName_67; -char[] sKeyName_68; -char[] sKeyName_69; -char[] sKeyName_6A; -char[] sKeyName_6B; -char[] sKeyName_6C; -char[] sKeyName_6D; -char[] sKeyName_6E; -char[] sKeyName_6F; -char[] sKeyName_70; -char[] sKeyName_71; -char[] sKeyName_72; -char[] sKeyName_73; -char[] sKeyName_74; -char[] sKeyName_75; -char[] sKeyName_76; -char[] sKeyName_77; -char[] sKeyName_78; -char[] sKeyName_79; -char[] sKeyName_7A; -char[] sKeyName_7B; -char[] sKeyName_7C; -char[] sKeyName_7D; -char[] sKeyName_7E; -char[] sKeyName_7F; -char[] sKeyName_80; -char[] sKeyName_81; -char[] sKeyName_82; -char[] sKeyName_83; -char[] sKeyName_84; -char[] sKeyName_85; -char[] sKeyName_86; -char[] sKeyName_87; -char[] sKeyName_88; -char[] sKeyName_89; -char[] sKeyName_8A; -char[] sKeyName_8B; -char[] sKeyName_8C; -char[] sKeyName_8D; -char[] sKeyName_8E; -char[] sKeyName_8F; -char[] sKeyName_90; -char[] sKeyName_91; -char[] sKeyName_92; -char[] sKeyName_93; -char[] sKeyName_94; -char[] sKeyName_95; -char[] sKeyName_96; -char[] sKeyName_97; -char[] sKeyName_98; -char[] sKeyName_99; -char[] sKeyName_9A; -char[] sKeyName_9B; -char[] sKeyName_9C; -char[] sKeyName_9D; -char[] sKeyName_9E; -char[] sKeyName_9F; -char[] sKeyName_A0; -char[] sKeyName_A1; -char[] sKeyName_A2; -char[] sKeyName_A3; -char[] sKeyName_A4; -char[] sKeyName_A5; -char[] sKeyName_A6; -char[] sKeyName_A7; -char[] sKeyName_A8; -char[] sKeyName_A9; -char[] sKeyName_AA; -char[] sKeyName_AB; -char[] sKeyName_AC; -char[] sKeyName_AD; -char[] sKeyName_AE; -char[] sKeyName_AF; -char[] sKeyName_B0; -char[] sKeyName_B1; -char[] sKeyName_B2; -char[] sKeyName_B3; -char[] sKeyName_B4; -char[] sKeyName_B5; -char[] sKeyName_B6; -char[] sKeyName_B7; -char[] sKeyName_B8; -char[] sKeyName_B9; -char[] sKeyName_BA; -char[] sKeyName_BB; -char[] sKeyName_BC; -char[] sKeyName_BD; -char[] sKeyName_BE; -char[] sKeyName_BF; -char[] sKeyName_C0; -char[] sKeyName_C1; -char[] sKeyName_C2; -char[] sKeyName_C3; -char[] sKeyName_C4; -char[] sKeyName_C5; -char[] sKeyName_C6; -char[] sKeyName_C7; -char[] sKeyName_C8; -char[] sKeyName_C9; -char[] sKeyName_CA; -char[] sKeyName_CB; -char[] sKeyName_CC; -char[] sKeyName_CD; -char[] sKeyName_CE; -char[] sKeyName_CF; -char[] sKeyName_D0; -char[] sKeyName_D1; -char[] sKeyName_D2; -char[] sKeyName_D3; -char[] sKeyName_D4; -char[] sKeyName_D5; -char[] sKeyName_D6; -char[] sKeyName_D7; -char[] sKeyName_D8; -char[] sKeyName_D9; -char[] sKeyName_DA; -char[] sKeyName_DB; -char[] sKeyName_DC; -char[] sKeyName_DD; -char[] sKeyName_DE; -char[] sKeyName_DF; -char[] sKeyName_E0; -char[] sKeyName_E1; -char[] sKeyName_E2; -char[] sKeyName_E3; -char[] sKeyName_E4; -char[] sKeyName_E5; -char[] sKeyName_E6; -char[] sKeyName_E7; -char[] sKeyName_E8; -char[] sKeyName_E9; -char[] sKeyName_EA; -char[] sKeyName_EB; -char[] sKeyName_EC; -char[] sKeyName_ED; -char[] sKeyName_EE; -char[] sKeyName_EF; -char[] sKeyName_F0; -char[] sKeyName_F1; -char[] sKeyName_F2; -char[] sKeyName_F3; -char[] sKeyName_F4; -char[] sKeyName_F5; -char[] sKeyName_F6; -char[] sKeyName_F7; -char[] sKeyName_F8; -char[] sKeyName_F9; -char[] sKeyName_FA; -char[] sKeyName_FB; -char[] sKeyName_FC; -char[] sKeyName_FD; -char[] sKeyName_FE; -char[] sKeyName_FF; -char[] sForwardXbox; -char[] sBackXbox; -char[] sSlideLeftXbox; -char[] sSlideRightXbox; -char[] sMenuModeXbox; -char[] sActivateXbox; -char[] sUseXbox; -char[] sReadyItemXbox; -char[] sReadyMagicXbox; -char[] sCrouchXbox; -char[] sRunXbox; -char[] sToggleRunXbox; -char[] sJumpXbox; -char[] sTogglePOVXbox; -char[] sMenuNextXbox; -char[] sMenuPrevXbox; -char[] sJournalXbox; -char[] sQuick4Xbox; -char[] sQuick5Xbox; -char[] sQuick6Xbox; -char[] sQuick7Xbox; -char[] sQuick8Xbox; -char[] sQuick9Xbox; -char[] sQuick0Xbox; -char[] sOptionsMenuXbox; -char[] sSystemMenuXbox; -char[] sRestMenuXbox; -char[] sQuickSaveXbox; -char[] sQuickLoadXbox; -char[] sMoveUpXbox; -char[] sMoveDownXbox; -char[] sLookUpXbox; -char[] sLookDownXbox; -char[] sTurnLeftXbox; -char[] sTurnRightXbox; -char[] sNextWeaponXbox; -char[] sPrevWeaponXbox; -char[] sNextSpellXbox; -char[] sPrevSpellXbox; -char[] sDialogText1Xbox; -char[] sDialogText2Xbox; -char[] sDialogText3Xbox; -char[] sQuick_Save; -char[] sShadowText; -char[] sLockSuccess; -char[] sLockFail; -char[] sLockImpossible; -char[] sTrapSuccess; -char[] sTrapFail; -char[] sTrapImpossible; -char[] sTrapped; -char[] sLockLevel; -char[] sKeyUsed; -char[] sUnlocked; -char[] sBarter; -char[] sRepair; -char[] sSpells; -char[] sTraining; -char[] sTravel; -char[] sSpellmaking; -char[] sEnchanting; -char[] sJoystickNotFound; -char[] sJournalEntry; -char[] sDay; -char[] sCreate; -char[] sIngredients; -char[] sApparatus; -char[] sCreatedEffects; -char[] sOnetypeEffectMessage; -char[] sName; -char[] sNameTitle; -char[] sSelect; -char[] sBuy; -char[] sInfo; -char[] sMagicEffects; -char[] sOnce; -char[] sCostCharge; -char[] sCostChance; -char[] sAttributesMenu1; -char[] sBirthsignmenu1; -char[] sBirthsignmenu2; -char[] sChooseClassMenu1; -char[] sChooseClassMenu2; -char[] sChooseClassMenu3; -char[] sChooseClassMenu4; -char[] sCreateClassMenuWarning; -char[] sClassChoiceMenu1; -char[] sClassChoiceMenu2; -char[] sClassChoiceMenu3; -char[] sNotifyMessage1; -char[] sNotifyMessage2; -char[] sNotifyMessage3; -char[] sNotifyMessage4; -char[] sNotifyMessage4XBOX; -char[] sNotifyMessage5; -char[] sNotifyMessage6; -char[] sNotifyMessage6a; -char[] sNotifyMessage7; -char[] sNotifyMessage8; -char[] sNotifyMessage9; -char[] sNotifyMessage10; -char[] sNotifyMessage11; -char[] sNotifyMessage12; -char[] sNotifyMessage13; -char[] sNotifyMessage14; -char[] sNotifyMessage15; -char[] sNotifyMessage16; -char[] sNotifyMessage16_a; -char[] sNotifyMessage17; -char[] sNotifyMessage18; -char[] sNotifyMessage19; -char[] sNotifyMessage20; -char[] sNotifyMessage21; -char[] sNotifyMessage22; -char[] sNotifyMessage23; -char[] sNotifyMessage24; -char[] sNotifyMessage25; -char[] sNotifyMessage26; -char[] sNotifyMessage27; -char[] sNotifyMessage28; -char[] sNotifyMessage29; -char[] sNotifyMessage30; -char[] sNotifyMessage31; -char[] sNotifyMessage32; -char[] sNotifyMessage33; -char[] sNotifyMessage34; -char[] sNotifyMessage35; -char[] sNotifyMessage36; -char[] sNotifyMessage37; -char[] sNotifyMessage38; -char[] sNotifyMessage39; -char[] sNotifyMessage40; -char[] sNotifyMessage41; -char[] sNotifyMessage42; -char[] sNotifyMessage43; -char[] sNotifyMessage44; -char[] sNotifyMessage45; -char[] sNotifyMessage46; -char[] sNotifyMessage47; -char[] sNotifyMessage48; -char[] sNotifyMessage49; -char[] sNotifyMessage50; -char[] sNotifyMessage51; -char[] sNotifyMessage52; -char[] sNotifyMessage53; -char[] sNotifyMessage54; -char[] sNotifyMessage55; -char[] sNotifyMessage56; -char[] sNotifyMessage57; -char[] sNotifyMessage58; -char[] sNotifyMessage59; -char[] sNotifyMessage60; -char[] sNotifyMessage61; -char[] sNotifyMessage62; -char[] sNotifyMessage63; -char[] sNotifyMessage64; -char[] sNotifyMessage65; -char[] sNotifyMessage66; -char[] sNotifyMessage67; -char[] sInPrisonTitle; -char[] sInfoRefusal; -char[] sAdmireSuccess; -char[] sAdmireFail; -char[] sIntimidateSuccess; -char[] sIntimidateFail; -char[] sTauntSuccess; -char[] sTauntFail; -char[] sServiceRefusal; -char[] sBribeSuccess; -char[] sBribeFail; -char[] sBookSkillMessage; -char[] sLoadingMessage1; -char[] sLoadingMessage2; -char[] sLoadingMessage3; -char[] sLoadingMessage4; -char[] sLoadingMessage5; -char[] sLoadingMessage9; -char[] sLoadingMessage14; -char[] sLoadingMessage15; -char[] sInvalidSaveGameMsg; -char[] sInvalidSaveGameMsgXBOX; -char[] sLoadingErrorsMsg; -char[] sMissingMastersMsg; -char[] sChangedMastersMsg; -char[] sMastPlugMismatchMsg; -char[] sGeneralMastPlugMismatchMsg; -char[] sLoadLastSaveMsg; -char[] sMessage1; -char[] sMessage2; -char[] sMessage3; -char[] sMessage4; -char[] sMessage5; -char[] sMessageQuestionAnswer1; -char[] sMessageQuestionAnswer2; -char[] sMessageQuestionAnswer3; -char[] sBarterDialog1; -char[] sBarterDialog2; -char[] sBarterDialog3; -char[] sBarterDialog4; -char[] sBarterDialog5; -char[] sBarterDialog6; -char[] sBarterDialog7; -char[] sBarterDialog8; -char[] sBarterDialog9; -char[] sBarterDialog10; -char[] sBarterDialog11; -char[] sBarterDialog12; -char[] sInventoryMessage1; -char[] sInventoryMessage2; -char[] sInventoryMessage3; -char[] sInventoryMessage4; -char[] sInventoryMessage5; -char[] sContentsMessage1; -char[] sContentsMessage2; -char[] sContentsMessage3; -char[] sAttributeListTitle; -char[] sConsoleTitle; -char[] sCreateClassMenu1; -char[] sCreateClassMenu2; -char[] sCreateClassMenu3; -char[] sCreateClassMenuHelp1; -char[] sCreateClassMenuHelp2; -char[] sControlsMenu1; -char[] sControlsMenu2; -char[] sControlsMenu3; -char[] sControlsMenu4; -char[] sControlsMenu5; -char[] sControlsMenu6; -char[] sGameWithoutLauncherXbox; -char[] sDialogMenu1; -char[] sEnchantmentMenu1; -char[] sEnchantmentMenu2; -char[] sEnchantmentMenu3; -char[] sEnchantmentMenu4; -char[] sEnchantmentMenu5; -char[] sEnchantmentMenu6; -char[] sEnchantmentMenu7; -char[] sEnchantmentMenu8; -char[] sEnchantmentMenu9; -char[] sEnchantmentMenu10; -char[] sEnchantmentMenu11; -char[] sEnchantmentMenu12; -char[] sEnchantmentHelp1; -char[] sEnchantmentHelp2; -char[] sEnchantmentHelp3; -char[] sEnchantmentHelp4; -char[] sEnchantmentHelp5; -char[] sEnchantmentHelp6; -char[] sEnchantmentHelp7; -char[] sEnchantmentHelp8; -char[] sEnchantmentHelp9; -char[] sEnchantmentHelp10; -char[] sInputMenu1; -char[] sInventoryMenu1; -char[] sLevelUpMenu1; -char[] sLevelUpMenu2; -char[] sLevelUpMenu3; -char[] sLevelUpMenu4; -char[] sLevelUpMsg; -char[] sLevelUp; -char[] sRange; -char[] sArea; -char[] sMagnitude; -char[] sDuration; -char[] sDrain; -char[] sAbsorb; -char[] sFortify; -char[] sRestore; -char[] sDamage; -char[] spoint; -char[] spoints; -char[] spercent; -char[] sfor; -char[] ssecond; -char[] sseconds; -char[] sin; -char[] sfootarea; -char[] sfeet; -char[] sXTimes; -char[] sXTimesINT; -char[] sonword; -char[] sNone; -char[] sDone; -char[] sStartCell; -char[] sStartError; -char[] sStartCellError; -char[] sAdmire; -char[] sIntimidate; -char[] sTaunt; -char[] sBribe_10_Gold; -char[] sBribe_100_Gold; -char[] sBribe_1000_Gold; -char[] sPersuasionMenuTitle; -char[] sFast; -char[] sSlow; -char[] sBestAttack; -char[] sSubtitles; -char[] sMouseFlip; -char[] sCursorOff; -char[] sEnableJoystick; -char[] sXStrafe; -char[] sStrafe; -char[] sTurn; -char[] sMouseWheelUpShort; -char[] sMouseWheelDownShort; -char[] sMouse; -char[] sJoystickShort; -char[] sJoystickHatShort; -char[] sPickUp; -char[] sItemName; -char[] sNoName; -char[] sItem; -char[] sRaceMenu1; -char[] sRaceMenu2; -char[] sRaceMenu3; -char[] sRaceMenu4; -char[] sRaceMenu5; -char[] sRaceMenu6; -char[] sRaceMenu7; -char[] sRestMenu1; -char[] sRestMenu2; -char[] sRestMenu3; -char[] sRestMenu4; -char[] sSaveMenu1; -char[] sServiceRepairTitle; -char[] sServiceTravelTitle; -char[] sServiceTrainingTitle; -char[] sServiceTrainingWords; -char[] sServiceSpellsTitle; -char[] sSkillsMenu1; -char[] sBonusSkillTitle; -char[] sSpecializationMenu1; -char[] sSpellmakingMenuTitle; -char[] sSpellmakingMenu1; -char[] sSpellmakingHelp1; -char[] sSpellmakingHelp2; -char[] sSpellmakingHelp3; -char[] sSpellmakingHelp4; -char[] sSpellmakingHelp5; -char[] sSpellmakingHelp6; -char[] sKilledEssential; -char[] sCrimeMessage; -char[] sPotionSuccess; -char[] sMagicItem; -char[] sMagnitudeDes; -char[] sAreaDes; -char[] sDurationDes; -char[] sRangeDes; -char[] sCrimeHelp; -char[] sSoulGem; -char[] sCastCost; -char[] sTravelServiceTitle; -char[] sSpellServiceTitle; -char[] sRepairServiceTitle; -char[] sTrainingServiceTitle; -char[] sMortar; -char[] sCalcinator; -char[] sAlembic; -char[] sRetort; -char[] sQuestionMark; -char[] s3dAudio; -char[] s3dHardware; -char[] s3dSoftware; -char[] sYourGold; -char[] sSellerGold; -char[] sTotalCost; -char[] sTotalSold; -char[] sOffer; -char[] sLess; -char[] sMore; -char[] sInventory; -char[] sBookPageOne; -char[] sBookPageTwo; -char[] sDisposeofCorpse; -char[] sCaughtStealingMessage; -char[] sUserDefinedClass; -char[] sAsk; -char[] sEnchantItems; -char[] sSoulGemsWithSouls; -char[] sDoYouWantTo; -char[] sRechargeEnchantment; -char[] sMake_Enchantment; -char[] sSell; -char[] sEquip; -char[] sDrop; -char[] sContainer; -char[] sStats; -char[] sMap; -char[] sCenter; -char[] sSaveMenuHelp01; -char[] sSaveMenuHelp02; -char[] sSaveMenuHelp03; -char[] sSaveMenuHelp04; -char[] sSaveMenuHelp05; -char[] sSaveMenuHelp06; -char[] sBlocks; -char[] sFileSize; -char[] sSave; -char[] sDeleteSpell; -char[] sQuestionDeleteSpell; -char[] sDeleteSpellError; -char[] sLocal; -char[] sLoadFailedMessage; -char[] sShadows; -char[] sControlerVibration; -char[] sQuanityMenuMessage01; -char[] sQuanityMenuMessage02; -char[] sSex; -char[] sHair; -char[] sFace; -char[] sRepairFailed; -char[] sRepairSuccess; -char[] sHeal; -char[] sgp; -char[] sSetValueMessage01; -char[] sDelete; -char[] sVideoWarning; -char[] sResChangeWarning; -char[] sShift; -char[] sBackspace; -char[] sCustomClassName; -char[] sSpace; -char[] sInventorySelectNoItems; -char[] sInventorySelectNoSoul; -char[] sInventorySelectNoIngredients; -char[] sDisposeCorpseFail; -char[] sQuickMenuTitle; -char[] sQuickMenuInstruc; -char[] sQuickMenu1; -char[] sQuickMenu2; -char[] sQuickMenu3; -char[] sQuickMenu4; -char[] sQuickMenu5; -char[] sQuickMenu6; -char[] sMagicSelectTitle; -char[] sNextRank; -char[] sNeedOneSkill; -char[] sNeedTwoSkills; -char[] sand; -char[] sOneHanded; -char[] sTwoHanded; -char[] sType; -char[] sAt; -char[] sUnequip; -char[] sScrolldown; -char[] sScrollup; -char[] sSleepInterrupt; -char[] sSoultrapSuccess; -char[] sCantEquipWeapWarning; -char[] sOfferMenuTitle; -char[] sPowers; -char[] sBreath; -char[] sDifficulty; -char[] sEasy; -char[] sHard; -char[] sAttributeStrength; -char[] sAttributeIntelligence; -char[] sAttributeWillpower; -char[] sAttributeAgility; -char[] sAttributeSpeed; -char[] sAttributeEndurance; -char[] sAttributePersonality; -char[] sAttributeLuck; -char[] sSkillBlock; -char[] sSkillArmorer; -char[] sSkillMediumarmor; -char[] sSkillHeavyarmor; -char[] sSkillBluntweapon; -char[] sSkillLongblade; -char[] sSkillAxe; -char[] sSkillSpear; -char[] sSkillAthletics; -char[] sSkillEnchant; -char[] sSkillDestruction; -char[] sSkillAlteration; -char[] sSkillIllusion; -char[] sSkillConjuration; -char[] sSkillMysticism; -char[] sSkillRestoration; -char[] sSkillAlchemy; -char[] sSkillUnarmored; -char[] sSkillSecurity; -char[] sSkillSneak; -char[] sSkillAcrobatics; -char[] sSkillLightarmor; -char[] sSkillShortblade; -char[] sSkillMarksman; -char[] sSkillMercantile; -char[] sSkillSpeechcraft; -char[] sSkillHandtohand; -char[] sHealth; -char[] sRace; -char[] sLevel; -char[] sLevels; -char[] sFaction; -char[] sBirthSign; -char[] sBounty; -char[] sReputation; -char[] sSchool; -char[] sSkill; -char[] sSkillProgress; -char[] sSkillMaxReached; -char[] sLevelProgress; -char[] sMagic; -char[] sMagicMenu; -char[] sFatigue; -char[] sSkillsMenuReputationHelp; -char[] sStrDesc; -char[] sIntDesc; -char[] sWilDesc; -char[] sAgiDesc; -char[] sSpdDesc; -char[] sEndDesc; -char[] sPerDesc; -char[] sLucDesc; -char[] sFatDesc; -char[] sHealthDesc; -char[] sMagDesc; -char[] sDefaultCellname; -char[] sTargetCriticalStrike; -char[] sEffectWaterBreathing; -char[] sEffectSwiftSwim; -char[] sEffectWaterWalking; -char[] sEffectShield; -char[] sEffectFireShield; -char[] sEffectLightningShield; -char[] sEffectFrostShield; -char[] sEffectBurden; -char[] sEffectFeather; -char[] sEffectJump; -char[] sEffectLevitate; -char[] sEffectSlowFall; -char[] sEffectLock; -char[] sEffectOpen; -char[] sEffectFireDamage; -char[] sEffectShockDamage; -char[] sEffectFrostDamage; -char[] sEffectDrainAttribute; -char[] sEffectDrainHealth; -char[] sEffectDrainSpellpoints; -char[] sEffectDrainFatigue; -char[] sEffectDrainSkill; -char[] sEffectDamageAttribute; -char[] sEffectDamageHealth; -char[] sEffectDamageMagicka; -char[] sEffectDamageFatigue; -char[] sEffectDamageSkill; -char[] sEffectPoison; -char[] sEffectWeaknessToFire; -char[] sEffectWeaknessToFrost; -char[] sEffectWeaknessToShock; -char[] sEffectWeaknessToMagicka; -char[] sEffectWeaknessToCommonDisease; -char[] sEffectWeaknessToBlightDisease; -char[] sEffectWeaknessToCorprusDisease; -char[] sEffectWeaknessToPoison; -char[] sEffectWeaknessToNormalWeapons; -char[] sEffectDisintegrateWeapon; -char[] sEffectDisintegrateArmor; -char[] sEffectInvisibility; -char[] sEffectChameleon; -char[] sEffectLight; -char[] sEffectSanctuary; -char[] sEffectNightEye; -char[] sEffectCharm; -char[] sEffectParalyze; -char[] sEffectSilence; -char[] sEffectBlind; -char[] sEffectSound; -char[] sEffectCalmHumanoid; -char[] sEffectCalmCreature; -char[] sEffectFrenzyHumanoid; -char[] sEffectFrenzyCreature; -char[] sEffectDemoralizeHumanoid; -char[] sEffectDemoralizeCreature; -char[] sEffectRallyHumanoid; -char[] sEffectRallyCreature; -char[] sEffectDispel; -char[] sEffectSoultrap; -char[] sEffectTelekinesis; -char[] sEffectMark; -char[] sEffectRecall; -char[] sEffectDivineIntervention; -char[] sEffectAlmsiviIntervention; -char[] sEffectDetectAnimal; -char[] sEffectDetectEnchantment; -char[] sEffectDetectKey; -char[] sEffectSpellAbsorption; -char[] sEffectReflect; -char[] sEffectCureCommonDisease; -char[] sEffectCureBlightDisease; -char[] sEffectCureCorprusDisease; -char[] sEffectCurePoison; -char[] sEffectCureParalyzation; -char[] sEffectRestoreAttribute; -char[] sEffectRestoreHealth; -char[] sEffectRestoreSpellPoints; -char[] sEffectRestoreFatigue; -char[] sEffectRestoreSkill; -char[] sEffectFortifyAttribute; -char[] sEffectFortifyHealth; -char[] sEffectFortifySpellpoints; -char[] sEffectFortifyFatigue; -char[] sEffectFortifySkill; -char[] sEffectFortifyMagickaMultiplier; -char[] sEffectAbsorbAttribute; -char[] sEffectAbsorbHealth; -char[] sEffectAbsorbSpellPoints; -char[] sEffectAbsorbFatigue; -char[] sEffectAbsorbSkill; -char[] sEffectResistFire; -char[] sEffectResistFrost; -char[] sEffectResistShock; -char[] sEffectResistMagicka; -char[] sEffectResistCommonDisease; -char[] sEffectResistBlightDisease; -char[] sEffectResistCorprusDisease; -char[] sEffectResistPoison; -char[] sEffectResistNormalWeapons; -char[] sEffectResistParalysis; -char[] sEffectRemoveCurse; -char[] sEffectTurnUndead; -char[] sEffectSummonScamp; -char[] sEffectSummonClannfear; -char[] sEffectSummonDaedroth; -char[] sEffectSummonDremora; -char[] sEffectSummonAncestralGhost; -char[] sEffectSummonSkeletalMinion; -char[] sEffectSummonLeastBonewalker; -char[] sEffectSummonGreaterBonewalker; -char[] sEffectSummonBonelord; -char[] sEffectSummonWingedTwilight; -char[] sEffectSummonHunger; -char[] sEffectSummonGoldensaint; -char[] sEffectSummonFlameAtronach; -char[] sEffectSummonFrostAtronach; -char[] sEffectSummonStormAtronach; -char[] sEffectFortifyAttackBonus; -char[] sEffectCommandCreatures; -char[] sEffectCommandHumanoids; -char[] sEffectBoundDagger; -char[] sEffectBoundLongsword; -char[] sEffectBoundMace; -char[] sEffectBoundBattleAxe; -char[] sEffectBoundSpear; -char[] sEffectBoundLongbow; -char[] sEffectExtraSpell; -char[] sEffectBoundCuirass; -char[] sEffectBoundHelm; -char[] sEffectBoundBoots; -char[] sEffectBoundShield; -char[] sEffectBoundGloves; -char[] sEffectCorpus; -char[] sEffectVampirism; -char[] sEffectSummonCenturionSphere; -char[] sEffectSunDamage; -char[] sEffectStuntedMagicka; -char[] sSchoolAlteration; -char[] sSchoolConjuration; -char[] sSchoolDestruction; -char[] sSchoolIllusion; -char[] sSchoolMysticism; -char[] sSchoolRestoration; -char[] sTypeSpell; -char[] sTypeAbility; -char[] sTypeBlightDisease; -char[] sTypeDisease; -char[] sTypeCurse; -char[] sTypePower; -char[] sItemCastOnce; -char[] sItemCastWhenStrikes; -char[] sItemCastWhenUsed; -char[] sItemCastConstant; -char[] sRangeSelf; -char[] sRangeTouch; -char[] sRangeTarget; -char[] sMagicSkillFail; -char[] sMagicInsufficientSP; -char[] sMagicInsufficientCharge; -char[] sPowerAlreadyUsed; -char[] sMagicInvalidTarget; -char[] sMagicLockSuccess; -char[] sMagicOpenSuccess; -char[] sMagicTargetResistsWeapons; -char[] sMagicContractDisease; -char[] sMagicScampID; -char[] sMagicClannfearID; -char[] sMagicDaedrothID; -char[] sMagicDremoraID; -char[] sMagicAncestralGhostID; -char[] sMagicSkeletalMinionID; -char[] sMagicLeastBonewalkerID; -char[] sMagicGreaterBonewalkerID; -char[] sMagicBonelordID; -char[] sMagicWingedTwilightID; -char[] sMagicHungerID; -char[] sMagicGoldenSaintID; -char[] sMagicFlameAtronachID; -char[] sMagicFrostAtronachID; -char[] sMagicStormAtronachID; -char[] sMagicCenturionSphereID; -char[] sMagicBoundDaggerID; -char[] sMagicBoundLongswordID; -char[] sMagicBoundMaceID; -char[] sMagicBoundBattleAxeID; -char[] sMagicBoundSpearID; -char[] sMagicBoundLongbowID; -char[] sMagicBoundCuirassID; -char[] sMagicBoundHelmID; -char[] sMagicBoundBootsID; -char[] sMagicBoundShieldID; -char[] sMagicBoundLeftGauntletID; -char[] sMagicBoundRightGauntletID; -char[] sMagicCorprusWorsens; -char[] sMagicCannotRecast; -char[] sMagicPCResisted; -char[] sMagicTargetResisted; -char[] sMagicInvalidEffect; -char[] sAcrobat; -char[] sAgent; -char[] sArcher; -char[] sAssassin; -char[] sBarbarian; -char[] sBard; -char[] sBattlemage; -char[] sCrusader; -char[] sCustom; -char[] sHealer; -char[] sKnight; -char[] sMage; -char[] sMonk; -char[] sNightblade; -char[] sPilgrim; -char[] sRogue; -char[] sScout; -char[] sSorceror; -char[] sSpellsword; -char[] sThief; -char[] sWarrior; -char[] sWitchhunter; - -// Not documented, from Tribunal and Bloodmoon -char[] sTeleportDisabled; -char[] sLevitateDisabled; -char[] sWerewolfRefusal; -char[] sWerewolfPopup; -char[] sWerewolfRestMessage; -char[] sWerewolfAlarmMessage; -char[] sMaxSale; -char[] sDeleteNote; -char[] sEditNote; -char[] sProfitValue; -char[] sCompanionShare; -char[] sCompanionWarningMessage; -char[] sCompanionWarningButtonOne; -char[] sCompanionWarningButtonTwo; -float fWereWolfMagicka; -float fWereWolfIntellegence; -float fWereWolfHeavyArmor; -float fWereWolfMerchantile; -float fCombatDistanceWerewolfMod; -char[] sEffectSummonFabricant; -char[] sEffectSummonCreature01; -char[] sEffectSummonCreature02; -char[] sEffectSummonCreature03; -char[] sEffectSummonCreature04; -char[] sEffectSummonCreature05; -char[] sMagicFabricantID; -char[] sMagicCreature01ID; -char[] sMagicCreature02ID; -char[] sMagicCreature03ID; -char[] sMagicCreature04ID; -char[] sMagicCreature05ID; diff --git a/old_d_version/mscripts/guiscripts/makegui.mn b/old_d_version/mscripts/guiscripts/makegui.mn deleted file mode 100644 index 334e0a783..000000000 --- a/old_d_version/mscripts/guiscripts/makegui.mn +++ /dev/null @@ -1,5 +0,0 @@ -/* -Widget hud; -hud = gui.loadLayout("openmw_hud_layout.xml"); -hud.setCoord(10,10,500,500); -*/ diff --git a/old_d_version/mscripts/guiscripts/module/gui.mn b/old_d_version/mscripts/guiscripts/module/gui.mn deleted file mode 100644 index a452fa524..000000000 --- a/old_d_version/mscripts/guiscripts/module/gui.mn +++ /dev/null @@ -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(); diff --git a/old_d_version/mscripts/guiscripts/module/widget.mn b/old_d_version/mscripts/guiscripts/module/widget.mn deleted file mode 100644 index 4f9df5860..000000000 --- a/old_d_version/mscripts/guiscripts/module/widget.mn +++ /dev/null @@ -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); diff --git a/old_d_version/mscripts/setup.d b/old_d_version/mscripts/setup.d deleted file mode 100644 index edaad84f5..000000000 --- a/old_d_version/mscripts/setup.d +++ /dev/null @@ -1,66 +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 (object.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 mscripts.setup; - -import monster.monster; -import monster.compiler.scopes : global; -import monster.modules.timer; - -import core.config; -import gui.gui; -import scene.player; - -import std.string; -import std.stdio; - -// Set up the base Monster classes we need in OpenMW -void initMonsterScripts() -{ - // Add the script directories - vm.addPath("mscripts/"); - - // Import some modules into scope of Object, so we won't have to - // import them manually into each script. - MonsterClass.getObject().sc.registerImport("random", "timer"); - - // Get the Config singleton object - config.mo = vm.load("Config").getSing(); - - // Set up the player object. - playerData.setup(); - - // Set up the GUI Monster module - setupGUIScripts(); - - // Add test cases for scripts here. This allows them to be tested - // without engaging the rendering engine (specify the -n command - // line switch.) -} - -// This should probably not be here: -import monster.vm.dbg; - -extern(C): -void dbg_trace(char*str) { dbg.trace(toString(str)); } -void dbg_untrace() { dbg.untrace(); } diff --git a/old_d_version/mscripts/sound/jukebox.mn b/old_d_version/mscripts/sound/jukebox.mn deleted file mode 100644 index c8acfedd3..000000000 --- a/old_d_version/mscripts/sound/jukebox.mn +++ /dev/null @@ -1,224 +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 (jukebox.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/ . - - */ - -/* - A simple jukebox with a playlist. It can play, stop, pause with - fade in and fade out, and adjust volume. -*/ - -class Jukebox; - -// Between 0 (off) and 1 (full volume) -float fadeLevel = 0.0; - -// How much to fade in and out each second -float fadeInRate = 0.14; -float fadeOutRate = 0.30; - -// Time between each fade step -float fadeInterval = 0.2; - -// List of sounds to play -char[][] playlist; - -// Name used for error messages -char[] name; - -// Index of current song -int index; - -// The music volume, set by the user. Does NOT change to adjust for -// fading, it is only updated when the user changes the volume -// settings. Updated from Config through updateVolume(). -float musVolume; - -bool isPlaying; // Is a song currently playing? -bool isPaused; // Is the song currently paused? - -// Native functions to control music -native setSound(char[] filename); -native setVolume(float f); -native playSound(); -native stopSound(); -idle waitUntilFinished(); - -// Fade out and then stop the music. TODO: Rename these to resume() -// etc and use super.resume, when this is possible. -pause() -{ - isPaused = true; - state = fadeOut; -} - -resume() -{ - if(!Config.useMusic) return; - - if(isPaused) playSound(); - else next(); - - isPaused = false; - state = fadeIn; -} - -// Stop the current song. Calling resume again after this is called -// will start a new song. -stop() -{ - stopSound(); - - isPlaying = false; - fadeLevel = 0.0; - state = null; -} - -play() -{ - if(!Config.useMusic) return; - - if(index >= playlist.length) - return; - - setSound(playlist[index]); - setVolume(musVolume*fadeLevel); - playSound(); - - isPlaying = true; - isPaused = false; - - state = playing; -} - -// Play the next song in the playlist -next() -{ - if(!Config.useMusic) return; - - if(isPlaying) - stop(); - - // Find the index of the next song, if any - if(playlist.length == 0) return; - - if(++index >= playlist.length) - { - index = 0; - randomize(); - } - - play(); -} - -// Set the new music volume setting. -updateVolume(float f) -{ - musVolume = f; - if(isPlaying) - setVolume(musVolume*fadeLevel); -} - -setPlaylist(char[][] lst) -{ - playlist = lst; - randomize(); -} - -// Randomize playlist. -randomize() -{ - if(playlist.length < 2) return; - - foreach(int i, char[] s; playlist) - { - // Index to switch with - int idx = randInt(i,playlist.length-1); - - // To avoid playing the same song twice in a row, don't set the - // first song to the previous last. - if(i == 0 && idx == playlist.length-1) - idx--; - - if(idx == i) // Skip if swapping with self - continue; - - playlist[i] = playlist[idx]; - playlist[idx] = s; - } -} - -// Fade in -state fadeIn -{ - begin: - setVolume(musVolume*fadeLevel); - - sleep(fadeInterval); - - fadeLevel += fadeInterval*fadeInRate; - - if(fadeLevel >= 1.0) - { - fadeLevel = 1.0; - setVolume(musVolume); - state = playing; - } - goto begin; -} - -// Fade out -state fadeOut -{ - begin: - sleep(fadeInterval); - - fadeLevel -= fadeInterval*fadeOutRate; - - if(fadeLevel <= 0.0) - { - fadeLevel = 0.0; - stopSound(); - isPlaying = false; - - state = null; - } - - setVolume(musVolume*fadeLevel); - goto begin; -} - -state playing -{ - begin: - setVolume(musVolume); - fadeLevel = 1.0; - - while(true) - { - // Wait for the song to play. Will return imediately if the song has - // already stopped or if no song is playing - waitUntilFinished(); - - // Start playing the next song - next(); - } -} diff --git a/old_d_version/mscripts/sound/music.mn b/old_d_version/mscripts/sound/music.mn deleted file mode 100644 index b09fd97c6..000000000 --- a/old_d_version/mscripts/sound/music.mn +++ /dev/null @@ -1,104 +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 (music.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/ . - - */ - -// This class controls all the music. -singleton Music; - -// Create one jukebox for normal music, and one for battle music. This -// way we can pause / fade out one while the other resumes / fades in. -Jukebox jukebox, battle; - -bool isBattle; -bool isMuted; - -// Toggle between normal and battle music -toggle() -{ - if(isBattle) - { - io.print("Switching to normal music"); - battle.pause(); - jukebox.resume(); - isBattle = false; - } - else - { - io.print("Switching to battle music"); - jukebox.pause(); - battle.resume(); - isBattle = true; - } -} - -toggleMute() -{ - if(!isMuted) - { - jukebox.updateVolume(0); - battle.updateVolume(0); - io.print("Muted"); - isMuted = true; - } - else - { - updateVolume(); - isMuted = false; - io.print("Mute off"); - } -} - -setup() -{ - jukebox = new Jukebox; - battle = new Jukebox; - - jukebox.name = "Main"; - battle.name = "Battle"; - - isBattle = false; - - updateVolume(); -} - -play() -{ - if(isBattle) battle.play(); - else jukebox.play(); -} - -// Called at startup, listing the files in the "explore" and "battle" -// music directories respectively. -setPlaylists(char[][] normal, char[][] battlelist) -{ - jukebox.setPlaylist(normal); - battle.setPlaylist(battlelist); -} - -// Called whenever the volume settings (music volume or main volume) -// have been changed by the user. -updateVolume() -{ - float v = Config.calcMusicVolume(); - jukebox.updateVolume(v); - battle.updateVolume(v); -} diff --git a/old_d_version/ogre/ogre.d b/old_d_version/ogre/ogre.d deleted file mode 100644 index 23f89d2e8..000000000 --- a/old_d_version/ogre/ogre.d +++ /dev/null @@ -1,41 +0,0 @@ -// Place a mesh in the 3D scene graph, at the given -// location/scale. Returns a node pointer to the inserted object. -NodePtr placeObject(MeshIndex mesh, Placement *pos, float scale, - bool collide) -{ - // Get a scene node for this object. mesh.getNode() will either load - // it from file or BSA archive, or give us a handle if it is already - // loaded. - - // This must be called BEFORE UniqueName below, because it might - // possibly use UniqueName itself and overwrite the data - // there. (That was a fun bug to track down...) Calling getNode() - // will load the mesh if it is not already loaded. - NodePtr node = mesh.getNode(); - - // First, convert the Morrowind rotation to a quaternion - float[4] quat; - ogre_mwToQuaternion(pos.rotation.ptr, quat.ptr); - - // Insert a mesh copy into Ogre. - char[] name = UniqueName(mesh.getName); - node = ogre_insertNode(node, name.ptr, pos.position.ptr, - quat.ptr, scale); - - // Insert a collision shape too, if the mesh has one. - if(collide && mesh.shape !is null) - bullet_insertStatic(mesh.shape, pos.position.ptr, - quat.ptr, scale); - - return node; -} - -// Gives the placement of an item in the scene (position and -// orientation). It must have this exact structure since we also use -// it when reading ES files. -align(1) struct Placement -{ - float[3] position; - float[3] rotation; -} -static assert(Placement.sizeof == 4*6); diff --git a/old_d_version/scene/celldata.d b/old_d_version/scene/celldata.d deleted file mode 100644 index 06d516f4f..000000000 --- a/old_d_version/scene/celldata.d +++ /dev/null @@ -1,592 +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 (celldata.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 scene.celldata; - -import std.stdio; - -public import esm.esmmain; - -import core.memory; -import monster.monster; - -import util.reglist; - -import ogre.ogre; -import ogre.bindings; - -import sound.audio; - -import scene.player; - -// Generic version of a "live" object. Do we even need this at all? -// No, I don't think so. -struct GenLive(T) -{ - // Instance of class GameObject or a derived class (depending on - // object type) - MonsterObject *obj; - T *m; - - Placement *getPos() - { - assert(obj !is null); - // This belongs in HACK-land - return cast(Placement*)obj.getFloatPtr("x"); - } - - float getScale() - { - assert(obj !is null); - return obj.getFloat("scale"); - } -} - -alias GenLive!(Static) LiveStatic; -alias GenLive!(NPC) LiveNPC; -alias GenLive!(Activator) LiveActivator; -alias GenLive!(Potion) LivePotion; -alias GenLive!(Apparatus) LiveApparatus; -alias GenLive!(Ingredient) LiveIngredient; -alias GenLive!(Armor) LiveArmor; -alias GenLive!(Weapon) LiveWeapon; -alias GenLive!(Book) LiveBook; -alias GenLive!(Clothing) LiveClothing; -alias GenLive!(Tool) LiveTool; -alias GenLive!(Creature) LiveCreature; -alias GenLive!(Door) LiveDoor; -alias GenLive!(Misc) LiveMisc; -alias GenLive!(Container) LiveContainer; - -// TODO: This is sort of redundant. Eliminate or rework it later. -struct LiveLight -{ - MonsterObject *obj; - Light *m; - - Placement *getPos() - { - assert(obj !is null); - // This belongs in HACK-land - return cast(Placement*)obj.getFloatPtr("x"); - } - - float getScale() - { - assert(obj !is null); - return obj.getFloat("scale"); - } - - NodePtr lightNode; - SoundInstance *loopSound; -} - -class CellData -{ - private: - RegionManager reg; - - public: - - InteriorCell *inCell; - ExteriorCell *exCell; - - // Ambient light data - AMBIStruct ambi; - - // Water height - int water; - - // Linked lists that hold references to all the objects in the cell - RegionList!(LiveStatic) statics; - RegionList!(LiveMisc) miscItems; - RegionList!(LiveLight) lights; - RegionList!(LiveLight) statLights; - RegionList!(LiveNPC) npcs; - RegionList!(LiveContainer) containers; - RegionList!(LiveDoor) doors; - RegionList!(LiveActivator) activators; - RegionList!(LivePotion) potions; - RegionList!(LiveApparatus) appas; - RegionList!(LiveIngredient) ingredients; - RegionList!(LiveArmor) armors; - RegionList!(LiveWeapon) weapons; - RegionList!(LiveBook) books; - RegionList!(LiveTool) tools; - RegionList!(LiveClothing) clothes; - RegionList!(LiveCreature) creatures; - - this(RegionManager r) - { - reg = r; - killCell(); // Make sure all data is initialized. - } - - // Kills all data and initialize the object for reuse. - void killCell() - { - inCell = null; - exCell = null; - - // Reset the lists - statics.init(reg); - miscItems.init(reg); - lights.init(reg); - statLights.init(reg); - npcs.init(reg); - containers.init(reg); - doors.init(reg); - activators.init(reg); - potions.init(reg); - appas.init(reg); - ingredients.init(reg); - armors.init(reg); - weapons.init(reg); - books.init(reg); - tools.init(reg); - clothes.init(reg); - creatures.init(reg); - - // Write some statistics - //writefln(reg); - - reg.freeAll(); - } - - // Load an exterior cell - void loadExtCell(int x, int y) - { - exCell = cells.getExt(x, y); - - writefln("Name: %s", exCell.name); - writefln("Region: %s", exCell.region.id); - - esFile.restoreContext(exCell.context, reg); - - // Always for exterior cells - water = 0; - - Color mapColor; - if(esFile.isNextSub("NAM5")) - esFile.readHExact(&mapColor, mapColor.sizeof); - - loadReferences(); - - const float cellWidth = 8192; - - // TODO/FIXME: This is temporary - playerData.position.position[0] = x*cellWidth; - playerData.position.position[1] = y*cellWidth; - playerData.position.position[2] = 6000; - playerData.position.rotation[] = 0; - } - - // Load an interior cell - void loadIntCell(char[] cName) - { - inCell = cells.getInt(cName); - /* - writefln("Cell id: '%s'", cName); - writefln("Cell name: '%s'", cell.id); - */ - - esFile.restoreContext(inCell.context, reg); - - // TODO: Read this in loadcell.d - if(esFile.isNextSub("INTV") || esFile.isNextSub("WHGT")) - water = esFile.getHInt(); - //writefln("Water height: ", water); - - if(inCell.flags & CellFlags.QuasiExt) - { - Region* reg = esFile.getHNOPtr!(Region)("RGNN", regions); - if(reg) writefln("Cell has region %s", reg.id); - else writefln("No region"); - // Determine weather from this region - } - else - { - // Only for interior cells - esFile.readHNExact(&ambi, ambi.sizeof, "AMBI"); - } - - /* - writefln("Ambient light: ", ambi.ambient.array); - writefln("Sunlight: ", ambi.sunlight.array); - writefln("Fog color: ", ambi.ambient.array); - writefln("Fog density: ", ambi.fogDensity); - */ - loadReferences(); - } - - private: - - void loadReferences() - { - with(esFile) - { - - // Now read all the references - while(hasMoreSubs) - { - // Number of references in the cell? Maximum once in each - // cell, but not always at the beginning, and not always - // right. In other words, completely useless. Strange - // people... - getHNOInt("NAM0", 0); - - int refnum = getHNInt("FRMR"); // Reference number - char[] refr = getHNString("NAME"); // ID of object - - // Used internally for optimizing - bool container; - bool door; - bool stat; - bool activator; - - // Identify the referenced object by looking up the id - // string 'ref' in the global database of all - // cell-referencable objects. - Item itm = cellRefs.lookup(refr); - ItemT it = ItemT(itm); - - MonsterObject *mo; - - // These should be ordered according to how commonly they - // occur. - - // Static mesh - probably the most common object type - if(Static *s = it.getStatic()) - { - LiveStatic ls; - ls.m = s; - ls.obj = s.proto.clone(); - mo = ls.obj; - statics.insert(ls); - stat = true; - } - // Misc items are also pretty common - else if(Misc *m = it.getMisc()) - { - LiveMisc ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - miscItems.insert(ls); - } - // Lights and containers too - else if(Light *m = it.getLight()) - { - LiveLight ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - - bool carry = (m.data.flags&Light.Flags.Carry) != 0; - if(carry) - lights.insert(ls); - else - statLights.insert(ls); - } - else if(Container *c = it.getContainer()) - { - LiveContainer ls; - ls.m = c; - ls.obj = c.proto.clone(); - mo = ls.obj; - containers.insert(ls); - container = true; - } - // From here on I doubt the order will matter much - else if(Door *d = it.getDoor()) - { - LiveDoor ls; - ls.m = d; - ls.obj = d.proto.clone(); - mo = ls.obj; - doors.insert(ls); - door = true; - } - // Activator? - else if(Activator *a = it.getActivator()) - { - LiveActivator ls; - ls.m = a; - ls.obj = a.proto.clone(); - mo = ls.obj; - activators.insert(ls); - activator = true; - } - // NPC? - else if(NPC *n = it.getNPC()) - { - LiveNPC ls; - ls.m = n; - ls.obj = n.proto.clone(); - mo = ls.obj; - npcs.insert(ls); - } - else if(Potion *p = it.getPotion()) - { - LivePotion ls; - ls.m = p; - ls.obj = p.proto.clone(); - mo = ls.obj; - potions.insert(ls); - } - else if(Apparatus *m = it.getApparatus()) - { - LiveApparatus ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - appas.insert(ls); - } - else if(Ingredient *m = it.getIngredient()) - { - LiveIngredient ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - ingredients.insert(ls); - } - else if(Armor *m = it.getArmor()) - { - LiveArmor ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - armors.insert(ls); - } - else if(Weapon *m = it.getWeapon()) - { - LiveWeapon ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - weapons.insert(ls); - } - else if(Book *m = it.getBook()) - { - LiveBook ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - books.insert(ls); - } - else if(Clothing *m = it.getClothing()) - { - LiveClothing ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - clothes.insert(ls); - } - else if(Tool *m = it.getPick()) - { - LiveTool ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - tools.insert(ls); - } - else if(Tool *m = it.getProbe()) - { - LiveTool ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - tools.insert(ls); - } - else if(Tool *m = it.getRepair()) - { - LiveTool ls; - ls.m = m; - ls.obj = m.proto.clone(); - mo = ls.obj; - tools.insert(ls); - } - else if(Creature *c = it.getCreature()) - { - LiveCreature ls; - ls.m = c; - ls.obj = c.proto.clone(); - mo = ls.obj; - creatures.insert(ls); - } - else if(LeveledCreatures *l = it.getCreatureList) - { - // Create a creature, based on current player level. - LiveCreature ls; - ls.m = l.instCreature(*playerData.level); - if(ls.m != null) - { - // Note that this clones a creature object, not a - // leveled list object. - ls.obj = ls.m.proto.clone(); mo = ls.obj; - creatures.insert(ls); - } - } - else fail(format(" UNKNOWN REFERENCE! Type ", cast(int)it.i.type)); - - // Now that the object has found it's place, load data - // into the object. - - with(*mo) - { - // Scale. Multiply with the existing scale value. - float *scale = getFloatPtr("scale"); - *scale *= getHNOFloat("XSCL", 1.0); - - // Statics only need the position data. Skip the rest - // as an optimization. - if(stat) goto readpos; - - // The NPC that owns this object (and will get angry - // if you steal it) - setString8("owner", getHNOString("ANAM")); - - // I have no idea, link to a global variable perhaps? - setString8("glob", getHNOString("BNAM")); - - // ID of creature trapped in a soul gem (?) - setString8("soulID", getHNOString("XSOL")); - - // ?? CNAM has a faction name, might be for - // objects/beds etc belonging to a faction. - { - char[] cnam = getHNOString("CNAM"); - setString8("cnam", cnam); - - // INDX might be PC faction rank required to use the - // item? Sometimes is -1. - if(cnam.length) setInt("indx", getHNInt("INDX")); - } - - // Possibly weapon health, number of uses left or - // weapon magic charge? - setFloat("xchg", getHNOFloat("XCHG", 0.0)); - - // I have no idea, these are present some times, often - // along with owner (ANAM) and sometimes otherwise. Is - // NAM9 is always 1? INTV is usually one, but big for - // lights. Perhaps something to do with remaining - // light "charge". I haven't tried reading it as a - // float in those cases. - setInt("intv", getHNOInt("INTV", 0)); - setInt("nam9", getHNOInt("NAM9", 0)); - - // Present for doors that teleport you to another - // cell. - if(door && isNextSub("DODT")) - { - setBool("teleport", true); - - // Warning, HACK! Will be fixed when we implement - // structs in Monster. - Placement *p = cast(Placement*)getFloatPtr("destx"); - readHExact(p, Placement.sizeof); - - // Destination cell (optional?) - setString8("destCell", getHNOString("DNAM")); - } - - if(door || container) - { - // Lock level (I think) - setInt("lockLevel", getHNOInt("FLTV", 0)); - - // For locked doors and containers - setString8("key", getHNOString("KNAM")); - setString8("trap", getHNOString("TNAM")); - } - - if(activator) - { - // Occurs ONCE in Morrowind.esm, for an activator. - setInt("unam", getHNOByte("UNAM", 0)); - - // Occurs in Tribunal.esm, eg. in the cell - // "Mournhold, Plaza Brindisi Dorom", where it has - // the value 100. - setInt("fltv", getHNOInt("FLTV", 0)); - } - - readpos: - - // Position and rotation of this object within the - // cell - - // TODO: This is a HACK. It assumes the class variable - // floats are placed consecutively in memory in the - // right order. This is true, but is a very bug prone - // method of doing this. Will fix when Monster gets - // structs. (See the DODT record above also.) - Placement *pos = cast(Placement*)getFloatPtr("x"); - readHNExact(pos, Placement.sizeof, "DATA"); - - // TODO/FIXME: Very temporary. Set player position at - // the first door we find. - if(door && !playerData.posSet) - { - playerData.posSet = true; - *playerData.position = *pos; - } - } - } // End of while(hasMoreSubs) - } - - } // End of loadReferences() -} // End of class CellData - -CellFreelist cellList; - -// Structure used as a free list for cell data objects and their -// respective regions. -struct CellFreelist -{ - // We love static arrays! And 100 cells should be enough for - // everybody :) - CellData[100] list; - uint next; - - // TODO: Figure out a good size to use here as well. - CellData get() - { - if(next) return list[--next]; - - // Since these are reused, there's no waste in allocating on the - // heap here. Also, this is only semi-runtime (executed when - // loading a cell), thus an occational GC slow down is not - // critical. - return new CellData(new RegionManager("CELL")); - } - - void release(CellData r) - { - assert(next < list.length); - - r.killCell(); - list[next++] = r; - } -} diff --git a/old_d_version/scene/player.d b/old_d_version/scene/player.d deleted file mode 100644 index 043b18e49..000000000 --- a/old_d_version/scene/player.d +++ /dev/null @@ -1,65 +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.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 scene.player; - -import ogre.ogre; -import monster.monster; - -/* - * Contains essential data about the player and other current game - * data. This will be moved to script code. - */ - -PlayerData playerData; - -struct PlayerData -{ - MonsterObject *mo; - - Placement *position; - int *level; - - // Set up the player object. This is still pretty hackish and - // temporary. - void setup() - { - assert(mo is null); - mo = vm.load("game.player").getSing; - level = mo.getIntPtr("level"); - - // Still an ugly hack - position = cast(Placement*)mo.getFloatPtr("x"); - } - - /* - char[] getName() - { - assert(mo !is null); - return mo.getString8("name"); - } - */ - - // Temp. way of selecting start point - used in celldata - bool posSet = false; -} diff --git a/old_d_version/sound/audio.d b/old_d_version/sound/audio.d deleted file mode 100644 index 546479b68..000000000 --- a/old_d_version/sound/audio.d +++ /dev/null @@ -1,85 +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 (audio.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 sound.audio; - -public import sound.sfx; -public import sound.music; - -import sound.al; -import sound.alc; - -import std.stdio; -import std.string; - -class SoundException : Exception -{ - this(char[] caller, char[] msg) { super(caller ~ " SoundException: " ~ msg); } -} - -ALCdevice *Device = null; -ALCcontext *Context = null; - -void initializeSound() -{ - Device = alcOpenDevice(null); - Context = alcCreateContext(Device, null); - - if(!Device || !Context) - throw new SoundException("initializeSound()", - "Failed to initialize music device"); - - alcMakeContextCurrent(Context); - - Music.init(); -} - -void shutdownSound() -{ - Music.shutdown(); - - alcMakeContextCurrent(null); - if(Context) alcDestroyContext(Context); - Context = null; - if(Device) alcCloseDevice(Device); - Device = null; -} - -float saneVol(float vol) -{ - if(!(vol >= 0)) vol = 0; - else if(!(vol <= 1)) vol = 1; - return vol; -} - -void checkALError(char[] what) -{ - ALenum err = alGetError(); - what = " while " ~ what; - if(err != AL_NO_ERROR) - throw new Exception(format("OpenAL error%s: (%x) %s", what, err, - toString(alGetString(err)))); -} - -bool noALError() -{ return alGetError() == AL_NO_ERROR; } diff --git a/old_d_version/sound/avcodec.d b/old_d_version/sound/avcodec.d deleted file mode 100644 index 71607f3fc..000000000 --- a/old_d_version/sound/avcodec.d +++ /dev/null @@ -1,65 +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 (avcodec.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 sound.avcodec; - -extern (C): - -// A unique handle that represents an AV file -typedef void* AVFile; - -// A unique handle representing an audio stream -typedef void* AVAudio; - -// In case we ever decide to implement more codec backends, here's what -// these functions need to do... - -// Open the named file, and return a unique handle representing it. -// Returns NULL on error -AVFile avc_openAVFile(char *fname); - -// Close the file handle, invalidating all streams taken from it -void avc_closeAVFile(AVFile file); - -// Get a unique handle to an audio stream in the file. The given number -// is for files that can contain multiple audio streams (generally you -// would pass 0, for the first audio stream) -AVAudio avc_getAVAudioStream(AVFile file, int streamnum); - -// Get audio info representing the current stream. Returns 0 for success -// (not likely to fail) -int avc_getAVAudioInfo(AVAudio stream, int *rate, int *channels, int *bits); - -// Decode the next bit of data for the given audio stream. The function -// must provide no less than the requested number of bytes, except for -// end-of-stream conditions, and is responsible for buffering data. For -// files with multiple streams, it must take care to preserve data for -// any stream that has had a stream handle returned. -// eg. if a file has one video stream and 2 audio streams and the app -// gets a handle to the video stream and one audio stream, it must -// not destroy video data for subsequent calls to avc_getAVVideoData if -// it has to read over it while decoding the audio stream. The other -// audio stream's data, however, may be discarded. -// Returns the number of bytes written to the buffer, which will be no -// more than the provided length. -int avc_getAVAudioData(AVAudio stream, void *data, int length); diff --git a/old_d_version/sound/cpp_avcodec.cpp b/old_d_version/sound/cpp_avcodec.cpp deleted file mode 100644 index bd64fcb0f..000000000 --- a/old_d_version/sound/cpp_avcodec.cpp +++ /dev/null @@ -1,215 +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 (cpp_avcodec.cpp) 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/ . - - */ - -#include -#include - -extern "C" { // the headers don't do this.. -#include -#include -} - -#include - -using std::vector; - -struct MyFile { - AVFormatContext *FmtCtx; - struct MyStream { - MyFile *parent; - - AVCodecContext *CodecCtx; - int StreamNum; - - vector Data; - vector DecodedData; - }; - vector Streams; -}; - -// TODO: -// extern "C" MyFile::MyStream *avc_getAVVideoStream(MyFile *file, int streamnum); -// extern "C" int avc_getAVVideoInfo(MyFile::MyStream *stream, float *fps, int *width, int * height); -// extern "C" int avc_getAVVideoData(MyFile::MyStream *stream, char *data, int length); - -extern "C" MyFile *avc_openAVFile(char *fname) -{ - static bool done = false; - if(!done) { av_register_all(); - av_log_set_level(AV_LOG_ERROR);} - done = true; - - MyFile *file = new MyFile; - if(av_open_input_file(&file->FmtCtx, fname, NULL, 0, NULL) == 0) - { - if(av_find_stream_info(file->FmtCtx) >= 0) - return file; - av_close_input_file(file->FmtCtx); - } - delete file; - return NULL; -} - -extern "C" void avc_closeAVFile(MyFile *file) -{ - if(!file) return; - - for(size_t i = 0;i < file->Streams.size();i++) - { - avcodec_close(file->Streams[i]->CodecCtx); - file->Streams[i]->Data.clear(); - file->Streams[i]->DecodedData.clear(); - delete file->Streams[i]; - } - file->Streams.clear(); - - av_close_input_file(file->FmtCtx); - delete file; -} - -extern "C" MyFile::MyStream *avc_getAVAudioStream(MyFile *file, int streamnum) -{ - if(!file) return NULL; - for(unsigned int i = 0;i < file->FmtCtx->nb_streams;i++) - { - if(file->FmtCtx->streams[i]->codec->codec_type != CODEC_TYPE_AUDIO) - continue; - - if(streamnum == 0) - { - MyFile::MyStream *stream = new MyFile::MyStream; - stream->parent = file; - stream->CodecCtx = file->FmtCtx->streams[i]->codec; - stream->StreamNum = i; - - AVCodec *codec = avcodec_find_decoder(stream->CodecCtx->codec_id); - if(!codec || avcodec_open(stream->CodecCtx, codec) < 0) - { - delete stream; - return NULL; - } - - file->Streams.push_back(stream); - return stream; - } - streamnum--; - } - return NULL; -} - -extern "C" int avc_getAVAudioInfo(MyFile::MyStream *stream, - int *rate, int *channels, int *bits) -{ - if(!stream) return 1; - - if(rate) *rate = stream->CodecCtx->sample_rate; - if(channels) *channels = stream->CodecCtx->channels; - if(bits) *bits = 16; - - return 0; -} - -static void getNextPacket(MyFile *file, int streamidx) -{ - AVPacket packet; - while(av_read_frame(file->FmtCtx, &packet) >= 0) - { - for(vector::iterator i = file->Streams.begin(); - i != file->Streams.end();i++) - { - if((*i)->StreamNum == packet.stream_index) - { - size_t idx = (*i)->Data.size(); - (*i)->Data.resize(idx + packet.size); - memcpy(&(*i)->Data[idx], packet.data, packet.size); - if(streamidx == packet.stream_index) - { - av_free_packet(&packet); - return; - } - break; - } - } - av_free_packet(&packet); - } -} - -extern "C" int avc_getAVAudioData(MyFile::MyStream *stream, char *data, int length) -{ - if(!stream) return 0; - - int dec = 0; - while(dec < length) - { - if(stream->DecodedData.size() > 0) - { - size_t rem = length-dec; - if(rem > stream->DecodedData.size()) - rem = stream->DecodedData.size(); - - memcpy(data, &stream->DecodedData[0], rem); - data += rem; - dec += rem; - if(rem < stream->DecodedData.size()) - memmove(&stream->DecodedData[0], &stream->DecodedData[rem], - stream->DecodedData.size() - rem); - stream->DecodedData.resize(stream->DecodedData.size()-rem); - } - if(stream->DecodedData.size() == 0) - { - // Must always get at least one more packet if possible, in case - // the previous one wasn't enough - getNextPacket(stream->parent, stream->StreamNum); - int insize = stream->Data.size(); - if(insize == 0) - break; - - // Temporarilly add padding to the input data since some - // codecs read in larger chunks and may accidently read - // past the end of the allocated buffer - stream->Data.resize(insize + FF_INPUT_BUFFER_PADDING_SIZE); - memset(&stream->Data[insize], 0, FF_INPUT_BUFFER_PADDING_SIZE); - stream->DecodedData.resize(AVCODEC_MAX_AUDIO_FRAME_SIZE); - - int16_t *ptr = (int16_t*)&stream->DecodedData[0]; - int size = stream->DecodedData.size(); - int len = avcodec_decode_audio2(stream->CodecCtx, ptr, &size, - &stream->Data[0], insize); - if(len < 0) - { - stream->Data.resize(insize); - break; - } - if(len > 0) - { - int datarem = insize-len; - if(datarem) - memmove(&stream->Data[0], &stream->Data[len], datarem); - stream->Data.resize(datarem); - } - - stream->DecodedData.resize(size); - } - } - return dec; -} diff --git a/old_d_version/sound/music.d b/old_d_version/sound/music.d deleted file mode 100644 index 8d064ad13..000000000 --- a/old_d_version/sound/music.d +++ /dev/null @@ -1,394 +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 (music.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 sound.music; - -import sound.avcodec; -import sound.audio; -import sound.al; - -import monster.monster; - -import std.stdio; -import std.string; - -import core.config; -import core.resource; - -class Idle_waitUntilFinished : IdleFunction -{ - override: - IS initiate(Thread*) { return IS.Poll; } - - bool hasFinished(Thread* trd) - { - Jukebox mgr = cast(Jukebox)trd.extraData.obj; - assert(mgr !is null); - - // Return when the music is no longer playing - return !mgr.isPlaying(); - } -} - -struct Music -{ - private: - static: - - // The Monster classes and objects - MonsterClass jukeC, controlC; - - public: - - MonsterObject *controlM; - - void init() - { - assert(jukeC is null); - assert(controlC is null); - assert(controlM is null); - - jukeC = vm.load("sound.Jukebox"); - jukeC.bind("waitUntilFinished", - new Idle_waitUntilFinished); - - jukeC.bind("setSound", { Jukebox.get().setSound(); }); - jukeC.bind("setVolume", { Jukebox.get().setVolume(); }); - jukeC.bind("playSound", { Jukebox.get().playSound(); }); - jukeC.bind("stopSound", { Jukebox.get().stopSound(); }); - - jukeC.bindConst({new Jukebox(params.obj()); }); - - controlC = vm.load("sound.Music"); - controlM = controlC.getSing(); - controlM.call("setup"); - } - - private void pushSArray(char[][] strs) - { - AIndex arr[]; - arr.length = strs.length; - - // Create the array indices for each element string - foreach(i, ref elm; arr) - elm = arrays.create(strs[i]).getIndex(); - - // Push the final array - stack.pushArray(arr); - } - - void setPlaylists(char[][] normal, char[][] battle) - { - if(controlM is null) return; - - pushSArray(normal); - pushSArray(battle); - controlM.call("setPlaylists"); - } - - void play() - { - if(controlM !is null) - controlM.call("play"); - } - - void toggle() - { - if(controlM !is null) - controlM.call("toggle"); - } - - void toggleMute() - { - if(controlM !is null) - controlM.call("toggleMute"); - } - - void updateBuffers() - { - if(controlM is null) return; - - foreach(ref MonsterObject b; jukeC) - Jukebox.get(b).updateBuffers(); - } - - void shutdown() - { - if(controlM is null) return; - - foreach(ref MonsterObject b; jukeC) - Jukebox.get(b).shutdown(); - } -} - -// Simple music player, has a playlist and can pause/resume -// music. Most of the high-level code is in mscripts/jukebox.mn. -class Jukebox -{ - private: - - // Maximum buffer length, divided up among OpenAL buffers - const uint bufLength = 128*1024; - const uint numBufs = 4; - - char[] getName() - { - if(mo is null) return "(no name)"; - else return mo.getString8("name"); - } - - void fail(char[] msg) - { - throw new SoundException(getName() ~ " Jukebox", msg); - } - - ALuint sID; // Sound id - ALuint bIDs[numBufs]; // Buffers - - ALenum bufFormat; - ALint bufRate; - - AVFile fileHandle; - AVAudio audioHandle; - - static ubyte[] outData; - - static this() - { - outData.length = bufLength / numBufs; - } - - // The jukebox Monster object - MonsterObject *mo; - - public: - - this(MonsterObject *m) - { - mo = m; - m.getExtra(Music.jukeC).obj = this; - - sID = 0; - bIDs[] = 0; - } - - static Jukebox get(MonsterObject *m) - { - auto j = cast(Jukebox)m.getExtra(Music.jukeC).obj; - assert(j !is null); - assert(j.mo == m); - return j; - } - - static Jukebox get(ref MonsterObject m) - { return get(&m); } - - static Jukebox get() - { return get(params.obj()); } - - private: - - // Disable music - void shutdown() - { - mo.call("stop"); - - if(fileHandle) avc_closeAVFile(fileHandle); - fileHandle = null; - audioHandle = null; - - if(sID) - { - alSourceStop(sID); - alDeleteSources(1, &sID); - checkALError("disabling music"); - sID = 0; - - alDeleteBuffers(bIDs.length, bIDs.ptr); - checkALError("deleting buffers"); - bIDs[] = 0; - } - } - - void setSound() - { - char[] fname = stack.popString8(); - - // Generate a source to play back with if needed - if(sID == 0) - { - alGenSources(1, &sID); - checkALError("generating buffers"); - - // Set listner relative coordinates (sound follows the player) - alSourcei(sID, AL_SOURCE_RELATIVE, AL_TRUE); - - alGenBuffers(bIDs.length, bIDs.ptr); - } - else - { - // Kill current track, but keep the sID source. - alSourceStop(sID); - checkALError("stopping current track"); - - alSourcei(sID, AL_BUFFER, 0); - //alDeleteBuffers(bIDs.length, bIDs.ptr); - //bIDs[] = 0; - checkALError("resetting buffer"); - } - - if(fileHandle) avc_closeAVFile(fileHandle); - fileHandle = null; - audioHandle = null; - - //alGenBuffers(bIDs.length, bIDs.ptr); - - // If something fails, clean everything up. - scope(failure) shutdown(); - - fileHandle = avc_openAVFile(toStringz(fname)); - if(!fileHandle) - fail("Unable to open " ~ fname); - - audioHandle = avc_getAVAudioStream(fileHandle, 0); - if(!audioHandle) - fail("Unable to load music track " ~ fname); - - int rate, ch, bits; - if(avc_getAVAudioInfo(audioHandle, &rate, &ch, &bits) != 0) - fail("Unable to get info for music track " ~ fname); - - // Translate format from avformat to OpenAL - - bufRate = rate; - bufFormat = 0; - - // TODO: These don't really fail gracefully for 4 and 6 channels - // if these aren't supported. - if(bits == 8) - { - if(ch == 1) bufFormat = AL_FORMAT_MONO8; - if(ch == 2) bufFormat = AL_FORMAT_STEREO8; - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if(ch == 4) bufFormat = alGetEnumValue("AL_FORMAT_QUAD8"); - if(ch == 6) bufFormat = alGetEnumValue("AL_FORMAT_51CHN8"); - } - } - if(bits == 16) - { - if(ch == 1) bufFormat = AL_FORMAT_MONO16; - if(ch == 2) bufFormat = AL_FORMAT_STEREO16; - if(alIsExtensionPresent("AL_EXT_MCFORMATS")) - { - if(ch == 4) bufFormat = alGetEnumValue("AL_FORMAT_QUAD16"); - if(ch == 6) bufFormat = alGetEnumValue("AL_FORMAT_51CHN16"); - } - } - - if(bufFormat == 0) - fail(format("Unhandled format (%d channels, %d bits) for music track %s", - ch, bits, fname)); - - // Fill the buffers - foreach(int i, ref b; bIDs) - { - int length = avc_getAVAudioData(audioHandle, outData.ptr, outData.length); - if(length) alBufferData(b, bufFormat, outData.ptr, length, bufRate); - if(length == 0 || !noALError()) - { - if(i == 0) - fail("No audio data in music track " ~ fname); - - alDeleteBuffers(bIDs.length-i, bIDs.ptr+i); - checkALError("running alDeleteBuffers"); - bIDs[i..$] = 0; - break; - } - } - - // Associate the buffers with the sound id - alSourceQueueBuffers(sID, bIDs.length, bIDs.ptr); - } - - void setVolume() - { - float volume = stack.popFloat(); - - volume = saneVol(volume); - - // Set the new volume - if(sID) alSourcef(sID, AL_GAIN, volume); - } - - void playSound() - { - if(!sID) return; - - alSourcePlay(sID); - checkALError("starting music"); - } - - void stopSound() - { - // How to stop / pause music - if(sID) alSourcePause(sID); - } - - bool isPlaying() - { - ALint state; - alGetSourcei(sID, AL_SOURCE_STATE, &state); - checkALError("getting status"); - - return state == AL_PLAYING; - } - - void updateBuffers() - { - if(!sID || !isPlaying) - return; - - // Get the number of processed buffers - ALint count; - alGetSourcei(sID, AL_BUFFERS_PROCESSED, &count); - - checkALError("getting number of unprocessed buffers"); - - for(int i = 0;i < count;i++) - { - int length = avc_getAVAudioData(audioHandle, outData.ptr, outData.length); - if(length <= 0) - break; - - ALuint bid; - alSourceUnqueueBuffers(sID, 1, &bid); - if(noALError()) - { - alBufferData(bid, bufFormat, outData.ptr, length, bufRate); - alSourceQueueBuffers(sID, 1, &bid); - checkALError("queueing buffers"); - } - } - } -} diff --git a/old_d_version/sound/sfx.d b/old_d_version/sound/sfx.d deleted file mode 100644 index 1bf8a1e19..000000000 --- a/old_d_version/sound/sfx.d +++ /dev/null @@ -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 (sfx.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 sound.sfx; - -import sound.avcodec; -import sound.audio; -import sound.al; - -import core.config; -import core.resource; - -import std.string; -import std.stdio; - -// Handle for a sound resource. This struct represents one sound -// effect file (not a music file, those are handled differently -// because of their size.) From this handle we may get instances, -// which may be played and managed independently of each other. TODO: -// Let the resource manager worry about only opening one resource per -// file, when to kill resources, etc. -struct SoundFile -{ - ALuint bID; - char[] name; - bool loaded; - - private int refs; - - private void fail(char[] msg) - { - throw new SoundException(format("SoundFile '%s'", name), msg); - } - - // Load a sound resource. - void load(char[] file) - { - name = file; - - loaded = false; - refs = 0; - bID = 0; - - ubyte[] outData; - AVFile fileHandle = avc_openAVFile(toStringz(file)); - AVAudio audioHandle = avc_getAVAudioStream(fileHandle, 0); - - if(!fileHandle) - { - writefln("Unable to open %s", file); - goto errclose; - } - if(!audioHandle) - { - writefln("Unable to load sound %s", file); - goto errclose; - } - - int ch, bits, rate; - if(avc_getAVAudioInfo(audioHandle, &rate, &ch, &bits) != 0) - { - writefln("Unable to get info for sound %s", file); - goto errclose; - } - - int fmt = 0; - if(bits == 8) - { - if(ch == 1) fmt = AL_FORMAT_MONO8; - if(ch == 2) fmt = AL_FORMAT_STEREO8; - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD8"); - } - if(bits == 16) - { - if(ch == 1) fmt = AL_FORMAT_MONO16; - if(ch == 2) fmt = AL_FORMAT_STEREO16; - if(ch == 4) fmt = alGetEnumValue("AL_FORMAT_QUAD16"); - } - - if(fmt == 0) - { - writefln("Unhandled format (%d channels, %d bits) for sound %s", ch, bits, file); - goto errclose; - } - - int total = 0; - do - { - // Grow by an arbitrary amount. Should be big enough to get - // the whole sound in one or two iterations, but not allocate - // too much memory in case it's short - outData.length = outData.length+8192; - int length = avc_getAVAudioData(audioHandle, outData.ptr+total, outData.length-total); - total += length; - } - while(total == outData.length); - - if(total) - { - alGenBuffers(1, &bID); - alBufferData(bID, fmt, outData.ptr, total, rate); - if(!noALError()) - { - writefln("Unable to load sound %s", file); - alDeleteBuffers(1, &bID); - bID = 0; - } - else loaded = true; - } - - errclose: - if(fileHandle) avc_closeAVFile(fileHandle); - fileHandle = null; - audioHandle = null; - } - - // Get an instance of this resource. - SoundInstance getInstance() - { - SoundInstance si; - si.owner = this; - alGenSources(1, &si.inst); - if(!noALError() || !si.inst) - fail("Failed to instantiate sound resource"); - - alSourcei(si.inst, AL_BUFFER, cast(ALint)bID); - if(!noALError()) - { - alDeleteSources(1, &si.inst); - fail("Failed to load sound resource"); - } - refs++; - - return si; - } - - // Return the sound instance when you're done with it - private void returnInstance(ALuint sid) - { - refs--; - alSourceStop(sid); - alDeleteSources(1, &sid); - if(refs == 0) unload(); - } - - // Unload the resource. - void unload() - { - loaded = false; - alDeleteBuffers(1, &bID); - } -} - -struct SoundInstance -{ - ALuint inst; - ALfloat volume; - SoundFile *owner; - - // Return this instance to the owner - void kill() - { - owner.returnInstance(inst); - owner = null; - } - - // Start playing a sound. - void play() - { - alSourcePlay(inst); - checkALError("playing sound instance"); - } - - // Go buy a cookie - void stop() - { - alSourceStop(inst); - checkALError("stopping sound instance"); - } - - // Set parameters such as max volume and range - void setParams(float volume, float minRange, float maxRange, bool repeat=false) - in - { - assert(volume >= 0 && volume <= 1.0, "Volume out of range"); - } - body - { - this.volume = volume; - alSourcef(inst, AL_GAIN, 0); - alSourcef(inst, AL_REFERENCE_DISTANCE, minRange); - alSourcef(inst, AL_MAX_DISTANCE, maxRange); - alSourcei(inst, AL_LOOPING, repeat ? AL_TRUE : AL_FALSE); - alSourcePlay(inst); - checkALError("setting sound parameters"); - } - - // Set 3D position of sounds. Need to convert from app's world coords to - // standard left-hand coords - void setPos(float x, float y, float z) - { - alSource3f(inst, AL_POSITION, x, z, -y); - checkALError("setting sound position"); - } - - // TODO: This should probably be merged in setPos. All active sounds - // should respecify their position after the listener does, which would - // be ideal for keeping moving sounds moving while handling distance - // culling for moving and stationary sounds - void updateSound() - { - if(owner is null) return; - - ALfloat lp[3]; - ALfloat p[3]; - ALfloat dist; - alGetSourcef(inst, AL_MAX_DISTANCE, &dist); - alGetSourcefv(inst, AL_POSITION, p.ptr); - alGetListenerfv(AL_POSITION, lp.ptr); - if(noALError()) - { - p[0] -= lp[0]; - p[1] -= lp[1]; - p[2] -= lp[2]; - - // If the sound is out of range, mute it - ALfloat d2 = p[0]*p[0] + p[1]*p[1] + p[2]*p[2]; - if(d2 > dist*dist) - { - alSourcef(inst, AL_GAIN, 0); - - // If the sound is really out of range, we should reclaim - // it. This is not implemented yet. - } - else - alSourcef(inst, AL_GAIN, volume); - } - } - - static void setPlayerPos(float x, float y, float z, - float frontx, float fronty, float frontz, - float upx, float upy, float upz) - { - ALfloat orient[6]; - orient[0] = frontx; - orient[1] = frontz; - orient[2] =-fronty; - orient[3] = upx; - orient[4] = upz; - orient[5] =-upy; - alListener3f(AL_POSITION, x, z, -y); - alListenerfv(AL_ORIENTATION, orient.ptr); - checkALError("setting player position"); - } -} diff --git a/old_d_version/util/cachefile.d b/old_d_version/util/cachefile.d deleted file mode 100644 index ef5cbfbf9..000000000 --- a/old_d_version/util/cachefile.d +++ /dev/null @@ -1,48 +0,0 @@ -/* - OpenMW - The completely unofficial reimplementation of Morrowind - Copyright (C) 2008-2009 Nicolay Korslund - Email: < korslund@gmail.com > - WWW: http://openmw.sourceforge.net/ - - This file (cachefile.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 util.cachefile; -import monster.util.string; -import std.file; - -void makeDir(char[] pt) -{ - if(exists(pt)) - { - if(!isdir(pt)) - throw new Exception(pt ~ " is not a directory"); - } - else - mkdir(pt); -} - -void makePath(char[] pt) -{ - assert(!pt.begins("/")); - foreach(int i, char c; pt) - if(c == '/') - makeDir(pt[0..i]); - - if(!pt.ends("/")) - makeDir(pt); -} diff --git a/old_d_version/util/dbg.h b/old_d_version/util/dbg.h deleted file mode 100644 index 422dfe70c..000000000 --- a/old_d_version/util/dbg.h +++ /dev/null @@ -1,16 +0,0 @@ -#include - -extern "C" void dbg_trace(const char *); -extern "C" void dbg_untrace(); - -class MTrace -{ - public: - MTrace(const char* str) - { dbg_trace(str); } - - ~MTrace() - { dbg_untrace(); } -}; - -#define TRACE(x) MTrace __trc(x); diff --git a/old_d_version/util/regions.d b/old_d_version/util/regions.d deleted file mode 100644 index b975e499c..000000000 --- a/old_d_version/util/regions.d +++ /dev/null @@ -1,633 +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 (regions.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 util.regions; - -private import std.gc; -private import std.string; -private import std.c.stdlib; - -private import monster.util.string; - -alias std.c.stdlib.malloc malloc; - -class RegionManagerException : Exception -{ - this(char[] msg, char[] name) - { super( format("Memory Region Manager '%s': %s", name, msg ) ); } -} - -// A resizable array using a region for memory allocation. These can -// safely be resized back and forth without wasting large amounts of -// memory. -class RegionBuffer(T) -{ - final: - private: - T[] buffer; - T[] inUse; - - int reserve = 20; - - RegionManager reg; - - public: - - // Size is initial size, reserve gives an indicator of the minimum - // amount to increase the array with at once. - this(RegionManager m, int size = 0, int reserve = 0) - { - reg = m; - if(reserve) this.reserve = reserve; - if(size) alloc(size); - } - - new(uint size, RegionManager r) - { - return r.allocate(size).ptr; - } - - delete(void *p) { assert(0); } - - // Check if the buffer can hold 'size' more elements. If not, - // increase it. NOTE: This works even if data = null, and inUse - // is not. This allows us to make copy-on-resize slices. - private void alloc(ulong size) - { - if(inUse.length + size <= buffer.length) - { - inUse = buffer[0..inUse.length+size]; - return; - } - - // Allocate a new array, with more entries than requested - buffer = reg.allocateT!(T)(buffer.length + size + reserve); - - // Copy the old data - buffer[0..inUse.length] = inUse; - - // Set up the array in use - inUse = buffer[0..(inUse.length+size)]; - } - - T opIndex(int i) - { - return inUse[i]; - } - T opIndexAssign(T val, int i) { return inUse[i] = val; } - - RegionBuffer opSlice(int x, int y) - { - RegionBuffer b = new(reg) RegionBuffer(reg,0,reserve); - b.inUse = inUse[x..y]; - return b; - } - - RegionBuffer opSlice() { return opSlice(0,inUse.length); } - - RegionBuffer opCatAssign(T t) - { - alloc(1); - inUse[$-1] = t; - return this; - } - - RegionBuffer opCatAssign(T[] t) - { - alloc(t.length); - inUse[($-t.length)..$] = t; - return this; - } - - RegionBuffer opCatAssign(RegionBuffer t) - { - alloc(t.inUse.length); - inUse[($-t.inUse.length)..$] = t.inUse; - return this; - } - - RegionBuffer opCat(T t) { return this.dup() ~= t; } - RegionBuffer opCat(T[] t) { return this.dup() ~= t; } - RegionBuffer opCat(RegionBuffer t) { return this.dup() ~= t; } - - RegionBuffer dup() - { - RegionBuffer b = new(reg) RegionBuffer(reg, inUse.length, reserve); - b.inUse[] = inUse[]; - return b; - } - - ulong length() { return inUse.length; } - - void length(ulong size) - { - // Grow array - if(size > inUse.length) alloc(size - inUse.length); - - // Shrink array - else inUse = inUse[0..size]; - } - - // For direct access - T[] array() { return inUse; } -} - -alias RegionBuffer!(char) RegionCharBuffer; -alias RegionBuffer!(int) RegionIntBuffer; - -/* - * Region manager, a mark-sweep memory manager. You use it to allocate - * a lot of buffers, and when you are done with them you deallocate - * them all in one fell sweep. - * - */ -class RegionManager -{ - final: - private: - // Identifying name, used for error messages. - char[] name; - - // Use a default buffer size of one meg. Might change later. - const ulong defaultBufferSize = 1024*1024; - - // The size to use for new buffers. - ulong bufferSize; - - // Current amount of space that is 'lost' in unused end-of-buffer - // areas. Since we have proceeded to other buffers, this space will - // remain unused until freeAll is called. - ulong lost; - - ubyte[][] buffers; // Actual memory buffers - void *gcRanges[]; // List of ranges added to gc - - ubyte[] left; // Slice of what is left of the current buffer - - int currentBuffer; // Index into the next unused buffer - int currentRange; // Index into the next unused gcRanges entry - - void fail(char[] msg) - { - throw new RegionManagerException(msg, name); - } - - // We have run out of space, add a new buffer. I want this to be an - // exception rather than the rule, the default bufferSize should be - // large enough to prevent this from being necessary in most cases. - void nextBuffer() - { - // We should never have to increase the number of buffers! - if(currentBuffer >= buffers.length) fail("Out of buffers"); - - // Set up the buffer (if it is not already allocated.) - //buffers[currentBuffer].length = bufferSize; - if(buffers[currentBuffer].length != bufferSize) - { - assert(buffers[currentBuffer].length == 0); - ubyte *p = cast(ubyte*)malloc(bufferSize); - if(!p) fail("Malloc failed"); - buffers[currentBuffer] = p[0..bufferSize]; - } - - // Remember the amount of space we just lost - lost += left.length; - - // The entire buffer is available to us - left = buffers[currentBuffer]; - - // Point to the next unused buffer - currentBuffer++; - } - - public: - - this(char[] name = "", ulong bufferSize = defaultBufferSize) - { - this.name = name; - this.bufferSize = bufferSize; - - // Pointers are cheap. Let's preallocate these arrays big enough - // from the start. It's inflexible, but on purpose. We shouldn't - // NEED to grow them later, so we don't. - buffers.length = 100; - gcRanges.length = 10000; - - freeAll(); - } - - ~this() - { - // Don't leave any loose ends dangeling for the GC. - freeAll(); - - // Kill everything - foreach(ubyte[] arr; buffers) - free(arr.ptr); - - delete buffers; - delete gcRanges; - } - - ulong getBufferSize() { return bufferSize; } - - // Allocates an array from the region. - ubyte[] allocate(ulong size) - { - if(size > bufferSize) - fail(format("Tried to allocate %d, but maximum allowed allocation size is %d", - size, bufferSize)); - - // If the array cannot fit inside this buffer, get a new one. - if(size > left.length) nextBuffer(); - - ubyte[] ret = left[0..size]; - - left = left[size..$]; - - //writefln("Allocated %d, %d left in buffer", size, left.length); - - return ret; - } - - // Allocate an array and add it to the GC as a root region. This - // should be used for classes and other data that might contain - // pointers / class references to GC-managed data. - ubyte[] allocateGC(ulong size) - { - if(currentRange >= gcRanges.length) - fail("No more available GC ranges"); - - ubyte[] ret = allocate(size); - - // Add it to the GC - void *p = ret.ptr; - std.gc.addRange(p, p+ret.length); - gcRanges[currentRange++] = p; - - return ret; - } - - // Allocate an array of a specific type, eg. to allocate 4 ints, do - // int[] array = allocateT!(int)(4); - template allocateT(T) - { - T[] allocateT(ulong number) - { - return cast(T[])allocate(number * T.sizeof); - } - } - - alias allocateT!(int) getInts; - alias allocateT!(char) getString; - - template newT(T) - { - T* newT() - { - return cast(T*)allocate(T.sizeof); - } - } - - template allocateGCT(T) - { - T[] allocateGCT(ulong number) - { - return cast(T[])allocateGC(number * T.sizeof); - } - } - - // Copies an array of a given type - template copyT(T) - { - T[] copyT(T[] input) - { - T[] output = cast(T[]) allocate(input.length * T.sizeof); - output[] = input[]; - return output; - } - } - - alias copyT!(int) copy; - alias copyT!(char) copy; - - // Copies a string and ensures that it is null-terminated - char[] copyz(char[] str) - { - char[] res = cast(char[]) allocate(str.length+1); - res[$-1] = 0; - res = res[0..$-1]; - res[] = str[]; - return res; - } - - // Resets the region manager, but does not deallocate the - // buffers. To do that, delete the object. - void freeAll() - { - lost = 0; - currentBuffer = 0; - left = null; - - // Free the ranges from the GC's evil clutch. - foreach(inout void *p; gcRanges[0..currentRange]) - if(p) - { - std.gc.removeRange(p); - p = null; - } - - currentRange = 0; - } - - // Number of used buffers, including the current one - ulong usedBuffers() { return currentBuffer; } - - // Total number of allocated buffers - ulong totalBuffers() - { - ulong i; - - // Count number of allocated buffers - while(i < buffers.length && buffers[i].length) i++; - - return i; - } - - // Total number of allocated bytes - ulong poolSize() - { - return bufferSize * totalBuffers(); - } - - // Total number of bytes that are unavailable for use. (They might - // not be used, as such, if they are at the end of a buffer but the - // next buffer is in use.) - ulong usedSize() - { - return currentBuffer*bufferSize - left.length; - } - - // The total size of data that the user has requested. - ulong dataSize() - { - return usedSize() - lostSize(); - } - - // Number of lost bytes - ulong lostSize() - { - return lost; - } - - // Total amount of allocated space that is not used - ulong wastedSize() - { - return poolSize() - dataSize(); - } - - // Give some general info and stats - char[] toString() - { - return format("Memory Region Manager '%s':", name, - "\n pool %s (%d blocks)", comma(poolSize), totalBuffers, - "\n used %s", comma(usedSize), - "\n data %s", comma(dataSize), - "\n wasted %s (%.2f%%)", comma(wastedSize), - poolSize()?100.0*wastedSize()/poolSize():0, - "\n lost %s (%.2f%%)", comma(lost), usedSize()?100.0*lost/usedSize:0); - } - - // Get a RegionBuffer of a given type - template getBuffer(T) - { - RegionBuffer!(T) getBuffer(int size = 0, int reserve = 0) - { - return new(this) RegionBuffer!(T)(this,size,reserve); - } - } - - alias getBuffer!(char) getCharBuffer; - alias getBuffer!(int) getIntBuffer; -} - -unittest -{ - RegionManager r = new RegionManager("UT", 100); - - // Test normal allocations first - assert(r.poolSize == 0); - assert(r.usedSize == 0); - assert(r.dataSize == 0); - assert(r.lostSize == 0); - assert(r.wastedSize == 0); - - ubyte [] arr = r.allocate(30); - void *p = arr.ptr; - - assert(p == r.buffers[0].ptr); - assert(arr.length == 30); - assert(r.poolSize == 100); - assert(r.usedSize == 30); - assert(r.dataSize == 30); - assert(r.lostSize == 0); - assert(r.wastedSize == 70); - - arr = r.allocate(70); - assert(arr.ptr == p + 30); - assert(arr.length == 70); - assert(r.poolSize == 100); - assert(r.usedSize == 100); - assert(r.dataSize == 100); - assert(r.lostSize == 0); - assert(r.wastedSize == 0); - - // Overflow the buffer - p = r.allocate(2).ptr; - assert(p == r.buffers[1].ptr); - assert(r.poolSize == 200); - assert(r.usedSize == 102); - assert(r.dataSize == 102); - assert(r.lostSize == 0); - assert(r.wastedSize == 98); - - // Overflow the buffer and leave lost space behind - r.freeAll(); - assert(r.poolSize == 200); - assert(r.usedSize == 0); - assert(r.dataSize == 0); - assert(r.lostSize == 0); - assert(r.wastedSize == 200); - - r.allocate(1); - r.allocate(100); - assert(r.poolSize == 200); - assert(r.usedSize == 200); - assert(r.dataSize == 101); - assert(r.lostSize == 99); - assert(r.wastedSize == 99); - - // Try to allocate a buffer that is too large - bool threw = false; - try r.allocate(101); - catch(RegionManagerException e) - { - threw = true; - } - assert(threw); - - // The object should still be in a valid state. - - // Try an allocation with roots - assert(r.currentRange == 0); - arr = r.allocateGC(50); - assert(r.poolSize == 300); - assert(r.usedSize == 250); - assert(r.dataSize == 151); - assert(r.lostSize == 99); - assert(r.wastedSize == 149); - assert(r.currentRange == 1); - assert(r.gcRanges[0] == arr.ptr); - - int[] i1 = r.allocateGCT!(int)(10); - assert(i1.length == 10); - assert(r.poolSize == 300); - assert(r.usedSize == 290); - assert(r.dataSize == 191); - assert(r.lostSize == 99); - assert(r.currentRange == 2); - assert(r.gcRanges[1] == i1.ptr); - - r.freeAll(); - assert(r.currentRange == 0); - assert(r.poolSize == 300); - assert(r.usedSize == 0); - assert(r.dataSize == 0); - assert(r.lostSize == 0); - - // Allocate some floats - float[] fl = r.allocateT!(float)(24); - assert(fl.length == 24); - assert(r.poolSize == 300); - assert(r.usedSize == 96); - assert(r.dataSize == 96); - assert(r.lostSize == 0); - - // Copy an array - r.freeAll(); - char[] stat = "hello little guy"; - assert(r.dataSize == 0); - char[] copy = r.copy(stat); - assert(copy == stat); - assert(copy.ptr != stat.ptr); - copy[0] = 'a'; - copy[$-1] = 'a'; - assert(stat != copy); - assert(stat == "hello little guy"); - assert(r.dataSize == stat.length); - - // Test copyz() - r.freeAll(); - stat = "ABC"; - char *pp = cast(char*) r.copyz(stat).ptr; - assert(pp[2] == 'C'); - assert(pp[3] == 0); - copy = r.copyz(stat); - assert(cast(char*)copy.ptr - pp == 4); - assert(pp[4] == 'A'); - copy[0] = 'F'; - assert(pp[3] == 0); - assert(pp[4] == 'F'); - - // Test of the buffer function - r.freeAll(); - RegionBuffer!(int) b = r.getBuffer!(int)(); - assert(b.inUse.length == 0); - assert(b.reserve == 20); - - b.reserve = 5; - - assert(b.length == 0); - b ~= 10; - b ~= 13; - - assert(b.length == 2); - assert(b[0] == 10); - assert(b[1] == 13); - assert(b.buffer.length == 6); - p = b.buffer.ptr; - - b.length = 0; - b.length = 6; - assert(p == b.buffer.ptr); - assert(b.length == 6); - - b.length = 3; - assert(p == b.buffer.ptr); - assert(b.length == 3); - - b[2] = 167; - - b.length = 7; - assert(p != b.buffer.ptr); // The buffer was reallocated - assert(b.length == 7); - - assert(b[2] == 167); - - i1 = new int[5]; - foreach(int v, inout int i; i1) - i = v; - - p = b.buffer.ptr; - RegionBuffer!(int) a = b ~ i1; - assert(p != a.buffer.ptr); // A new buffer has been allocated - assert(p == b.buffer.ptr); // B should be unchanged - assert(a.length == b.length + i1.length); - - for(int i=0; i < b.length; i++) - assert(a[i] == b[i]); - - for(int i=0; i < i1.length; i++) - assert(a[i+7] == i); - - // Make sure the arrays are truly different - a[5] = b[5] + 2; - assert(a[5] != b[5]); - - // Make a slice - a = b[2..5]; - assert(a.inUse.ptr == b.inUse.ptr+2); - a[1] = 4; - assert(a[1] == b[3]); - b[3] = -12; - assert(a[1] == b[3]); - - a.length = a.length + 1; - assert(a.inUse.ptr != b.inUse.ptr+2); - a[1] = 4; - assert(a[1] != b[3]); - b[3] = -12; - assert(a[1] != b[3]); - - r.freeAll(); -} diff --git a/old_d_version/util/reglist.d b/old_d_version/util/reglist.d deleted file mode 100644 index ccbc59370..000000000 --- a/old_d_version/util/reglist.d +++ /dev/null @@ -1,214 +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 (reglist.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 util.reglist; - -import util.regions; - -import std.string; - -class RegionListException : Exception -{ - this(char[] msg) - { - super("RegionListException: " ~ msg); - } -} - -/* - * Internal structure used below - */ - -struct _aaNode(Value) -{ - Value value; - - _aaNode* next; // Next node - _aaNode* prev; // Previous node -} - -/* - * Very simple linked list that uses a specified region for - * allocation. - */ - -struct RegionList(Value) -{ - private: - alias _aaNode!(Value) Node; - - Node *head; // This is the head of the linked list (first element) - Node *tail; // New nodes are inserted here - uint totalNum; // Number of elements - - RegionManager reg; - - // Throw an exception - void fail(char[] msg) - { - msg = format("RegionList!(%s) exception: %s", typeid(Value).toString, msg); - throw new RegionListException(msg); - } - - public: - - alias Node* Iterator; - - // Equivalent to calling remove() on all elements - void clear() - { - head = tail = null; - totalNum = 0; - } - - // Reset the object and assign a new region manager - void init(RegionManager reg) - { - clear(); - this.reg = reg; - } - - Iterator insert(Value v) - { - Node* p = createNode(); - - 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.value = v; - - return p; - } - - void remove(Iterator p) - { - // Remove from the list - if(p.next) p.next.prev = p.prev; - else // We're the tail - { - assert(tail == p); - tail = p.prev; - } - - if(p.prev) p.prev.next = p.next; - else // We're head - { - assert(head == p); - head = p.next; - } - - totalNum--; - } - - // Create a new node and return it's pointer - private Node* createNode() - { - Node *p = cast(Node*) reg.allocate(Node.sizeof).ptr; - - // Initialize pointers - p.next = null; - p.prev = null; - - totalNum++; - - 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; } -} - -/* -unittest -{ - RegionManager r = new RegionManager(); - - RegionList!(float) ll; - ll.reg = r; - - assert(ll.length == 0); - ll.Iterator it = ll.insert(10.4); - writefln(r); - assert(ll.length == 1); - ll.insert(23); - it = ll.insert(6.3); - ll.insert(-1000); - - writefln(r); - assert(ll.length == 4); - - foreach(float f; ll) writefln(f); - - ll.remove(it); - - assert(ll.length == 3); - - assert(r.dataSize() == 12*4); - - foreach(int i, float f; ll) writefln(i, " ", f); -} -import std.stdio; -*/ diff --git a/old_d_version/util/uniquename.d b/old_d_version/util/uniquename.d deleted file mode 100644 index d3da6a0f9..000000000 --- a/old_d_version/util/uniquename.d +++ /dev/null @@ -1,96 +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 (uniquename.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 util.uniquename; - -import std.stdio; - -// Simple auxiliary code for creating unique names in succession, -// eg. "Screenshot_0000000001.png", "Screenshot_0000000002.png", etc.. -// WARNING: Uses static storage, so do NOT use persistent references -// or slices to the strings produced here. Intended only for OGRE -// calls, where copies are always made anyway. - -struct UniqueName -{ - static this() - { - buffer.length = 100; - number = buffer[0..10]; - number[] = "0000000000"; - } - - static char[] opCall(char[] addon = "") - { - // Point p at last digit - char *p = &number[$-1]; - char *pbeg = number.ptr; - while(p != pbeg-1) - { - if(*p == '9') - { - // Carry over - *p = '0'; - p--; - } - else - { - // Increase this digit and exit - (*p)++; - break; - } - } - assert(p != pbeg-1); // Overflow - - int totLen = 10 + addon.length; - if(totLen >= buffer.length) totLen = buffer.length - 1; - if(totLen > 10) buffer[10..totLen] = addon; - buffer[totLen] = 0; // String must null-terminate - - //writefln("UniqueName result=%s", buffer[0..totLen]); - - return buffer[0..totLen]; - } - - private: - static char[] buffer; - static char[] number; -} - -unittest -{ - assert(UniqueName() == "0000000001"); - assert(UniqueName() == "0000000002"); - assert(UniqueName() == "0000000003"); - assert(UniqueName() == "0000000004"); - assert(UniqueName() == "0000000005"); - assert(UniqueName() == "0000000006"); - assert(UniqueName("blah") == "0000000007blah"); - - assert(UniqueName() == "0000000008"); - assert(UniqueName() == "0000000009"); - assert(UniqueName() == "0000000010"); - assert(UniqueName() == "0000000011"); - assert(UniqueName(" bottles of beer on the wall") == - "0000000012 bottles of beer on the wall"); -} diff --git a/old_d_version/util/utfconvert.d b/old_d_version/util/utfconvert.d deleted file mode 100644 index 62083cad2..000000000 --- a/old_d_version/util/utfconvert.d +++ /dev/null @@ -1,114 +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 (utfconvert.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 util.utfconvert; - -import std.utf; - -// A specialized version of std.utf.decode(). It returns a bool rather -// than throwing an exception. -private bool fdecode(char[] s, inout size_t idx) - { - size_t len = s.length; - dchar V; - size_t i = idx; - char u = s[i]; - - if (u & 0x80) - { uint n; - char u2; - - /* The following encodings are valid, except for the 5 and 6 byte - * combinations: - * 0xxxxxxx - * 110xxxxx 10xxxxxx - * 1110xxxx 10xxxxxx 10xxxxxx - * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - * 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - * 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx - */ - for (n = 1; ; n++) - { - if (n > 4) - return false; // only do the first 4 of 6 encodings - if (((u << n) & 0x80) == 0) - { - if (n == 1) - return false; - break; - } - } - - // Pick off (7 - n) significant bits of B from first byte of octet - V = cast(dchar)(u & ((1 << (7 - n)) - 1)); - - if (i + (n - 1) >= len) - return false; // off end of string - - /* The following combinations are overlong, and illegal: - * 1100000x (10xxxxxx) - * 11100000 100xxxxx (10xxxxxx) - * 11110000 1000xxxx (10xxxxxx 10xxxxxx) - * 11111000 10000xxx (10xxxxxx 10xxxxxx 10xxxxxx) - * 11111100 100000xx (10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx) - */ - u2 = s[i + 1]; - if ((u & 0xFE) == 0xC0 || - (u == 0xE0 && (u2 & 0xE0) == 0x80) || - (u == 0xF0 && (u2 & 0xF0) == 0x80) || - (u == 0xF8 && (u2 & 0xF8) == 0x80) || - (u == 0xFC && (u2 & 0xFC) == 0x80)) - return false; // overlong combination - - for (uint j = 1; j != n; j++) - { - u = s[i + j]; - if ((u & 0xC0) != 0x80) - return false; // trailing bytes are 10xxxxxx - V = (V << 6) | (u & 0x3F); - } - if (!isValidDchar(V)) - return false; - i += n; - } - else - { - V = cast(dchar) u; - i++; - } - - idx = i; - return true; - } - -// Converts any string to valid UTF8 so it can be safely printed. It -// does not translate from other encodings but simply replaces invalid -// characters with 'replace'. Does everything in place. -char[] makeUTF8(char[] str, char replace = '?') -{ - size_t idx = 0; - while(idx < str.length) - if(!fdecode(str, idx)) - str[idx++] = replace; - return str; -}