mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-05-30 16:40:58 +00:00
- started with cleanup and separation of DECORATE code.
* everything related to scripting is now placed in a subdirectory 'scripting', which itself is separated into DECORATE, ZSCRIPT, the VM and code generation. * a few items have been moved to different headers so that the DECORATE parser definitions can mostly be kept local. The only exception at the moment is the flags interface on which 3 source files depend.
This commit is contained in:
parent
6a8ab9a4d3
commit
b1a83bfd26
132 changed files with 234 additions and 198 deletions
1214
src/scripting/codegeneration/thingdef_exp.h
Normal file
1214
src/scripting/codegeneration/thingdef_exp.h
Normal file
File diff suppressed because it is too large
Load diff
5287
src/scripting/codegeneration/thingdef_expression.cpp
Normal file
5287
src/scripting/codegeneration/thingdef_expression.cpp
Normal file
File diff suppressed because it is too large
Load diff
720
src/scripting/decorate/olddecorations.cpp
Normal file
720
src/scripting/decorate/olddecorations.cpp
Normal file
|
@ -0,0 +1,720 @@
|
|||
/*
|
||||
** decorations.cpp
|
||||
** Loads custom actors out of DECORATE lumps.
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2006 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
// HEADER FILES ------------------------------------------------------------
|
||||
|
||||
#include "actor.h"
|
||||
#include "info.h"
|
||||
#include "sc_man.h"
|
||||
#include "tarray.h"
|
||||
#include "templates.h"
|
||||
#include "r_defs.h"
|
||||
#include "a_pickups.h"
|
||||
#include "s_sound.h"
|
||||
#include "cmdlib.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "a_action.h"
|
||||
#include "decallib.h"
|
||||
#include "i_system.h"
|
||||
#include "thingdef.h"
|
||||
#include "codegeneration/thingdef_exp.h"
|
||||
#include "r_data/r_translate.h"
|
||||
|
||||
// TYPES -------------------------------------------------------------------
|
||||
|
||||
struct FExtraInfo
|
||||
{
|
||||
char DeathSprite[5];
|
||||
unsigned int SpawnStart, SpawnEnd;
|
||||
unsigned int DeathStart, DeathEnd;
|
||||
unsigned int IceDeathStart, IceDeathEnd;
|
||||
unsigned int FireDeathStart, FireDeathEnd;
|
||||
bool bSolidOnDeath, bSolidOnBurn;
|
||||
bool bBurnAway, bDiesAway, bGenericIceDeath;
|
||||
bool bExplosive;
|
||||
double DeathHeight, BurnHeight;
|
||||
};
|
||||
|
||||
class AFakeInventory : public AInventory
|
||||
{
|
||||
DECLARE_CLASS (AFakeInventory, AInventory);
|
||||
public:
|
||||
bool Respawnable;
|
||||
|
||||
bool ShouldRespawn ()
|
||||
{
|
||||
return Respawnable && Super::ShouldRespawn();
|
||||
}
|
||||
|
||||
bool TryPickup (AActor *&toucher)
|
||||
{
|
||||
INTBOOL success = P_ExecuteSpecial(special, NULL, toucher, false,
|
||||
args[0], args[1], args[2], args[3], args[4]);
|
||||
|
||||
if (success)
|
||||
{
|
||||
GoAwayAndDie ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void DoPickupSpecial (AActor *toucher)
|
||||
{
|
||||
// The special was already executed by TryPickup, so do nothing here
|
||||
}
|
||||
};
|
||||
IMPLEMENT_CLASS (AFakeInventory)
|
||||
|
||||
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
|
||||
|
||||
// PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
|
||||
|
||||
static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
|
||||
FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray<FState> &StateArray);
|
||||
static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, FScanner &sc);
|
||||
|
||||
// PRIVATE DATA DEFINITIONS ------------------------------------------------
|
||||
|
||||
static const char *RenderStyles[] =
|
||||
{
|
||||
"STYLE_None",
|
||||
"STYLE_Normal",
|
||||
"STYLE_Fuzzy",
|
||||
"STYLE_SoulTrans",
|
||||
"STYLE_OptFuzzy",
|
||||
"STYLE_Stencil",
|
||||
"STYLE_Translucent",
|
||||
"STYLE_Add",
|
||||
//"STYLE_Shaded",
|
||||
NULL
|
||||
};
|
||||
|
||||
// CODE --------------------------------------------------------------------
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//==========================================================================
|
||||
DEFINE_CLASS_PROPERTY(respawns, 0, FakeInventory)
|
||||
{
|
||||
defaults->Respawnable = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseOldDecoration
|
||||
//
|
||||
// Reads an old style decoration object
|
||||
//
|
||||
//==========================================================================
|
||||
PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FName typeName);
|
||||
|
||||
void ParseOldDecoration(FScanner &sc, EDefinitionType def)
|
||||
{
|
||||
Baggage bag;
|
||||
TArray<FState> StateArray;
|
||||
FExtraInfo extra;
|
||||
PClassActor *type;
|
||||
PClassActor *parent;
|
||||
FName typeName;
|
||||
|
||||
parent = (def == DEF_Pickup) ? RUNTIME_CLASS(AFakeInventory) : RUNTIME_CLASS(AActor);
|
||||
|
||||
sc.MustGetString();
|
||||
typeName = FName(sc.String);
|
||||
type = DecoDerivedClass(FScriptPosition(sc), parent, typeName);
|
||||
ResetBaggage(&bag, parent);
|
||||
bag.Info = type;
|
||||
#ifdef _DEBUG
|
||||
bag.ClassName = type->TypeName;
|
||||
#endif
|
||||
|
||||
type->GameFilter = GAME_Any;
|
||||
sc.MustGetStringName("{");
|
||||
|
||||
memset (&extra, 0, sizeof(extra));
|
||||
ParseInsideDecoration (bag, (AActor *)(type->Defaults), extra, def, sc, StateArray);
|
||||
|
||||
bag.Info->NumOwnedStates = StateArray.Size();
|
||||
if (bag.Info->NumOwnedStates == 0)
|
||||
{
|
||||
sc.ScriptError ("%s does not define any animation frames", typeName.GetChars() );
|
||||
}
|
||||
else if (extra.SpawnEnd == 0)
|
||||
{
|
||||
sc.ScriptError ("%s does not have a Frames definition", typeName.GetChars() );
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && extra.DeathEnd == 0)
|
||||
{
|
||||
sc.ScriptError ("%s does not have a DeathFrames definition", typeName.GetChars() );
|
||||
}
|
||||
else if (extra.IceDeathEnd != 0 && extra.bGenericIceDeath)
|
||||
{
|
||||
sc.ScriptError ("You cannot use IceDeathFrames and GenericIceDeath together");
|
||||
}
|
||||
|
||||
if (extra.IceDeathEnd != 0)
|
||||
{
|
||||
// Make a copy of the final frozen frame for A_FreezeDeathChunks
|
||||
FState icecopy = StateArray[extra.IceDeathEnd-1];
|
||||
StateArray.Push (icecopy);
|
||||
type->NumOwnedStates += 1;
|
||||
}
|
||||
|
||||
type->OwnedStates = new FState[type->NumOwnedStates];
|
||||
memcpy (type->OwnedStates, &StateArray[0], type->NumOwnedStates * sizeof(type->OwnedStates[0]));
|
||||
if (type->NumOwnedStates == 1)
|
||||
{
|
||||
type->OwnedStates->Tics = -1;
|
||||
type->OwnedStates->TicRange = 0;
|
||||
type->OwnedStates->Misc1 = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t i;
|
||||
|
||||
// Spawn states loop endlessly
|
||||
for (i = extra.SpawnStart; i < extra.SpawnEnd-1; ++i)
|
||||
{
|
||||
type->OwnedStates[i].NextState = &type->OwnedStates[i+1];
|
||||
}
|
||||
type->OwnedStates[i].NextState = &type->OwnedStates[extra.SpawnStart];
|
||||
|
||||
// Death states are one-shot and freeze on the final state
|
||||
if (extra.DeathEnd != 0)
|
||||
{
|
||||
for (i = extra.DeathStart; i < extra.DeathEnd-1; ++i)
|
||||
{
|
||||
type->OwnedStates[i].NextState = &type->OwnedStates[i+1];
|
||||
}
|
||||
if (extra.bDiesAway || def == DEF_Projectile)
|
||||
{
|
||||
type->OwnedStates[i].NextState = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
type->OwnedStates[i].Tics = -1;
|
||||
type->OwnedStates[i].TicRange = 0;
|
||||
type->OwnedStates[i].Misc1 = 0;
|
||||
}
|
||||
|
||||
if (def == DEF_Projectile)
|
||||
{
|
||||
if (extra.bExplosive)
|
||||
{
|
||||
type->OwnedStates[extra.DeathStart].SetAction("A_Explode");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// The first frame plays the death sound and
|
||||
// the second frame makes it nonsolid.
|
||||
type->OwnedStates[extra.DeathStart].SetAction("A_Scream");
|
||||
if (extra.bSolidOnDeath)
|
||||
{
|
||||
}
|
||||
else if (extra.DeathStart + 1 < extra.DeathEnd)
|
||||
{
|
||||
type->OwnedStates[extra.DeathStart+1].SetAction("A_NoBlocking");
|
||||
}
|
||||
else
|
||||
{
|
||||
type->OwnedStates[extra.DeathStart].SetAction("A_ScreamAndUnblock");
|
||||
}
|
||||
|
||||
if (extra.DeathHeight == 0)
|
||||
{
|
||||
extra.DeathHeight = ((AActor*)(type->Defaults))->Height;
|
||||
}
|
||||
type->DeathHeight = extra.DeathHeight;
|
||||
}
|
||||
bag.statedef.SetStateLabel("Death", &type->OwnedStates[extra.DeathStart]);
|
||||
}
|
||||
|
||||
// Burn states are the same as death states, except they can optionally terminate
|
||||
if (extra.FireDeathEnd != 0)
|
||||
{
|
||||
for (i = extra.FireDeathStart; i < extra.FireDeathEnd-1; ++i)
|
||||
{
|
||||
type->OwnedStates[i].NextState = &type->OwnedStates[i+1];
|
||||
}
|
||||
if (extra.bBurnAway)
|
||||
{
|
||||
type->OwnedStates[i].NextState = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
type->OwnedStates[i].Tics = -1;
|
||||
type->OwnedStates[i].TicRange = 0;
|
||||
type->OwnedStates[i].Misc1 = 0;
|
||||
}
|
||||
|
||||
// The first frame plays the burn sound and
|
||||
// the second frame makes it nonsolid.
|
||||
type->OwnedStates[extra.FireDeathStart].SetAction("A_ActiveSound");
|
||||
if (extra.bSolidOnBurn)
|
||||
{
|
||||
}
|
||||
else if (extra.FireDeathStart + 1 < extra.FireDeathEnd)
|
||||
{
|
||||
type->OwnedStates[extra.FireDeathStart+1].SetAction("A_NoBlocking");
|
||||
}
|
||||
else
|
||||
{
|
||||
type->OwnedStates[extra.FireDeathStart].SetAction("A_ActiveAndUnblock");
|
||||
}
|
||||
|
||||
if (extra.BurnHeight == 0) extra.BurnHeight = ((AActor*)(type->Defaults))->Height;
|
||||
type->BurnHeight = extra.BurnHeight;
|
||||
|
||||
bag.statedef.SetStateLabel("Burn", &type->OwnedStates[extra.FireDeathStart]);
|
||||
}
|
||||
|
||||
// Ice states are similar to burn and death, except their final frame enters
|
||||
// a loop that eventually causes them to bust to pieces.
|
||||
if (extra.IceDeathEnd != 0)
|
||||
{
|
||||
for (i = extra.IceDeathStart; i < extra.IceDeathEnd-1; ++i)
|
||||
{
|
||||
type->OwnedStates[i].NextState = &type->OwnedStates[i+1];
|
||||
}
|
||||
type->OwnedStates[i].NextState = &type->OwnedStates[type->NumOwnedStates-1];
|
||||
type->OwnedStates[i].Tics = 5;
|
||||
type->OwnedStates[i].TicRange = 0;
|
||||
type->OwnedStates[i].Misc1 = 0;
|
||||
type->OwnedStates[i].SetAction("A_FreezeDeath");
|
||||
|
||||
i = type->NumOwnedStates - 1;
|
||||
type->OwnedStates[i].NextState = &type->OwnedStates[i];
|
||||
type->OwnedStates[i].Tics = 1;
|
||||
type->OwnedStates[i].TicRange = 0;
|
||||
type->OwnedStates[i].Misc1 = 0;
|
||||
type->OwnedStates[i].SetAction("A_FreezeDeathChunks");
|
||||
bag.statedef.SetStateLabel("Ice", &type->OwnedStates[extra.IceDeathStart]);
|
||||
}
|
||||
else if (extra.bGenericIceDeath)
|
||||
{
|
||||
bag.statedef.SetStateLabel("Ice", RUNTIME_CLASS(AActor)->FindState(NAME_GenericFreezeDeath));
|
||||
}
|
||||
}
|
||||
if (def == DEF_BreakableDecoration)
|
||||
{
|
||||
((AActor *)(type->Defaults))->flags |= MF_SHOOTABLE;
|
||||
}
|
||||
if (def == DEF_Projectile)
|
||||
{
|
||||
((AActor *)(type->Defaults))->flags |= MF_DROPOFF|MF_MISSILE;
|
||||
}
|
||||
bag.statedef.SetStateLabel("Spawn", &type->OwnedStates[extra.SpawnStart]);
|
||||
bag.statedef.InstallStates (type, ((AActor *)(type->Defaults)));
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseInsideDecoration
|
||||
//
|
||||
// Parses attributes out of a definition terminated with a }
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void ParseInsideDecoration (Baggage &bag, AActor *defaults,
|
||||
FExtraInfo &extra, EDefinitionType def, FScanner &sc, TArray<FState> &StateArray)
|
||||
{
|
||||
AFakeInventory *const inv = static_cast<AFakeInventory *>(defaults);
|
||||
char sprite[5] = "TNT1";
|
||||
|
||||
sc.MustGetString ();
|
||||
while (!sc.Compare ("}"))
|
||||
{
|
||||
if (sc.Compare ("DoomEdNum"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
if (sc.Number < -1 || sc.Number > 32767)
|
||||
{
|
||||
sc.ScriptError ("DoomEdNum must be in the range [-1,32767]");
|
||||
}
|
||||
bag.Info->DoomEdNum = (SWORD)sc.Number;
|
||||
}
|
||||
else if (sc.Compare ("SpawnNum"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
if (sc.Number < 0 || sc.Number > 255)
|
||||
{
|
||||
sc.ScriptError ("SpawnNum must be in the range [0,255]");
|
||||
}
|
||||
bag.Info->SpawnID = (BYTE)sc.Number;
|
||||
}
|
||||
else if (sc.Compare ("Sprite") || (
|
||||
(def == DEF_BreakableDecoration || def == DEF_Projectile) &&
|
||||
sc.Compare ("DeathSprite") &&
|
||||
(extra.DeathSprite[0] = 1)) // This is intentionally = and not ==
|
||||
)
|
||||
{
|
||||
sc.MustGetString ();
|
||||
if (sc.StringLen != 4)
|
||||
{
|
||||
sc.ScriptError ("Sprite name must be exactly four characters long");
|
||||
}
|
||||
if (extra.DeathSprite[0] == 1)
|
||||
{
|
||||
memcpy (extra.DeathSprite, sc.String, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (sprite, sc.String, 4);
|
||||
}
|
||||
}
|
||||
else if (sc.Compare ("Frames"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
extra.SpawnStart = StateArray.Size();
|
||||
ParseSpriteFrames (bag.Info, StateArray, sc);
|
||||
extra.SpawnEnd = StateArray.Size();
|
||||
}
|
||||
else if ((def == DEF_BreakableDecoration || def == DEF_Projectile) &&
|
||||
sc.Compare ("DeathFrames"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
extra.DeathStart = StateArray.Size();
|
||||
ParseSpriteFrames (bag.Info, StateArray, sc);
|
||||
extra.DeathEnd = StateArray.Size();
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("IceDeathFrames"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
extra.IceDeathStart = StateArray.Size();
|
||||
ParseSpriteFrames (bag.Info, StateArray, sc);
|
||||
extra.IceDeathEnd = StateArray.Size();
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("BurnDeathFrames"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
extra.FireDeathStart = StateArray.Size();
|
||||
ParseSpriteFrames (bag.Info, StateArray, sc);
|
||||
extra.FireDeathEnd = StateArray.Size();
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("GenericIceDeath"))
|
||||
{
|
||||
extra.bGenericIceDeath = true;
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("BurnsAway"))
|
||||
{
|
||||
extra.bBurnAway = true;
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("DiesAway"))
|
||||
{
|
||||
extra.bDiesAway = true;
|
||||
}
|
||||
else if (sc.Compare ("Alpha"))
|
||||
{
|
||||
sc.MustGetFloat ();
|
||||
defaults->Alpha = clamp (sc.Float, 0.0, 1.0);
|
||||
}
|
||||
else if (sc.Compare ("Scale"))
|
||||
{
|
||||
sc.MustGetFloat ();
|
||||
defaults->Scale.X = defaults->Scale.Y = sc.Float;
|
||||
}
|
||||
else if (sc.Compare ("RenderStyle"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
defaults->RenderStyle = LegacyRenderStyles[sc.MustMatchString (RenderStyles)];
|
||||
}
|
||||
else if (sc.Compare ("Radius"))
|
||||
{
|
||||
sc.MustGetFloat ();
|
||||
defaults->radius = sc.Float;
|
||||
}
|
||||
else if (sc.Compare ("Height"))
|
||||
{
|
||||
sc.MustGetFloat ();
|
||||
defaults->Height = sc.Float;
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("DeathHeight"))
|
||||
{
|
||||
sc.MustGetFloat ();
|
||||
extra.DeathHeight = sc.Float;
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("BurnHeight"))
|
||||
{
|
||||
sc.MustGetFloat ();
|
||||
extra.BurnHeight = sc.Float;
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("Health"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
defaults->health = sc.Number;
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("ExplosionRadius"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
bag.Info->ExplosionRadius = sc.Number;
|
||||
extra.bExplosive = true;
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("ExplosionDamage"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
bag.Info->ExplosionDamage = sc.Number;
|
||||
extra.bExplosive = true;
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("DoNotHurtShooter"))
|
||||
{
|
||||
bag.Info->DontHurtShooter = true;
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("Damage"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
defaults->SetDamage(sc.Number);
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("DamageType"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
if (sc.Compare ("Normal"))
|
||||
{
|
||||
defaults->DamageType = NAME_None;
|
||||
}
|
||||
else
|
||||
{
|
||||
defaults->DamageType = sc.String;
|
||||
}
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("Speed"))
|
||||
{
|
||||
sc.MustGetFloat ();
|
||||
defaults->Speed = sc.Float;
|
||||
}
|
||||
else if (sc.Compare ("Mass"))
|
||||
{
|
||||
sc.MustGetFloat ();
|
||||
defaults->Mass = SDWORD(sc.Float);
|
||||
}
|
||||
else if (sc.Compare ("Translation1"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
if (sc.Number < 0 || sc.Number > 2)
|
||||
{
|
||||
sc.ScriptError ("Translation1 must be in the range [0,2]");
|
||||
}
|
||||
defaults->Translation = TRANSLATION(TRANSLATION_Standard, sc.Number);
|
||||
}
|
||||
else if (sc.Compare ("Translation2"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
if (sc.Number < 0 || sc.Number >= MAX_ACS_TRANSLATIONS)
|
||||
{
|
||||
#define ERROR(foo) "Translation2 must be in the range [0," #foo "]"
|
||||
sc.ScriptError (ERROR(MAX_ACS_TRANSLATIONS));
|
||||
#undef ERROR
|
||||
}
|
||||
defaults->Translation = TRANSLATION(TRANSLATION_LevelScripted, sc.Number);
|
||||
}
|
||||
else if ((def == DEF_BreakableDecoration || def == DEF_Projectile) &&
|
||||
sc.Compare ("DeathSound"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
defaults->DeathSound = sc.String;
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("BurnDeathSound"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
defaults->ActiveSound = sc.String;
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("SpawnSound"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
defaults->SeeSound = sc.String;
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("DoomBounce"))
|
||||
{
|
||||
defaults->BounceFlags = BOUNCE_DoomCompat;
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("HereticBounce"))
|
||||
{
|
||||
defaults->BounceFlags = BOUNCE_HereticCompat;
|
||||
}
|
||||
else if (def == DEF_Projectile && sc.Compare ("HexenBounce"))
|
||||
{
|
||||
defaults->BounceFlags = BOUNCE_HexenCompat;
|
||||
}
|
||||
else if (def == DEF_Pickup && sc.Compare ("PickupSound"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
inv->PickupSound = sc.String;
|
||||
}
|
||||
else if (def == DEF_Pickup && sc.Compare ("PickupMessage"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
static_cast<PClassInventory *>(bag.Info)->PickupMessage = sc.String;
|
||||
}
|
||||
else if (def == DEF_Pickup && sc.Compare ("Respawns"))
|
||||
{
|
||||
inv->Respawnable = true;
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("SolidOnDeath"))
|
||||
{
|
||||
extra.bSolidOnDeath = true;
|
||||
}
|
||||
else if (def == DEF_BreakableDecoration && sc.Compare ("SolidOnBurn"))
|
||||
{
|
||||
extra.bSolidOnBurn = true;
|
||||
}
|
||||
else if (sc.String[0] != '*')
|
||||
{
|
||||
HandleActorFlag(sc, bag, sc.String, NULL, '+');
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.ScriptError (NULL);
|
||||
}
|
||||
sc.MustGetString ();
|
||||
}
|
||||
|
||||
unsigned int i;
|
||||
int spr = GetSpriteIndex(sprite);
|
||||
|
||||
for (i = 0; i < StateArray.Size(); ++i)
|
||||
{
|
||||
StateArray[i].sprite = spr;
|
||||
}
|
||||
if (extra.DeathSprite[0] && extra.DeathEnd != 0)
|
||||
{
|
||||
int spr = GetSpriteIndex(extra.DeathSprite);
|
||||
for (i = extra.DeathStart; i < extra.DeathEnd; ++i)
|
||||
{
|
||||
StateArray[i].sprite = spr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseSpriteFrames
|
||||
//
|
||||
// Parses a sprite-based animation sequence out of a decoration definition.
|
||||
// You can have multiple sequences, and they will be appended together.
|
||||
// A sequence definition looks like this:
|
||||
//
|
||||
// "<rate>:<frames>,<rate>:<frames>,<rate>:<frames>,..."
|
||||
//
|
||||
// Rate is a number describing the number of tics between frames in this
|
||||
// sequence. If you don't specify it, then a rate of 4 is used. Frames is
|
||||
// a list of consecutive frame characters. Each frame can be postfixed with
|
||||
// the * character to indicate that it is fullbright.
|
||||
//
|
||||
// Examples:
|
||||
// ShortRedTorch looks like this:
|
||||
// "ABCD"
|
||||
//
|
||||
// HeadCandles looks like this:
|
||||
// "6:AB"
|
||||
//
|
||||
// TechLamp looks like this:
|
||||
// "A*B*C*D*"
|
||||
//
|
||||
// BloodyTwich looks like this:
|
||||
// "10:A, 15:B, 8:C, 6:B"
|
||||
//==========================================================================
|
||||
|
||||
static void ParseSpriteFrames (PClassActor *info, TArray<FState> &states, FScanner &sc)
|
||||
{
|
||||
FState state;
|
||||
char *token = strtok (sc.String, ",\t\n\r");
|
||||
|
||||
memset (&state, 0, sizeof(state));
|
||||
|
||||
while (token != NULL)
|
||||
{
|
||||
// Skip leading white space
|
||||
while (*token == ' ')
|
||||
token++;
|
||||
|
||||
int rate = 4;
|
||||
bool firstState = true;
|
||||
char *colon = strchr (token, ':');
|
||||
|
||||
if (colon != NULL)
|
||||
{
|
||||
char *stop;
|
||||
|
||||
*colon = 0;
|
||||
rate = strtol (token, &stop, 10);
|
||||
if (stop == token || rate < 1 || rate > 65534)
|
||||
{
|
||||
sc.ScriptError ("Rates must be in the range [0,65534]");
|
||||
}
|
||||
token = colon + 1;
|
||||
}
|
||||
|
||||
state.Tics = rate;
|
||||
state.TicRange = 0;
|
||||
|
||||
while (*token)
|
||||
{
|
||||
if (*token == ' ')
|
||||
{
|
||||
}
|
||||
else if (*token == '*')
|
||||
{
|
||||
if (firstState)
|
||||
{
|
||||
sc.ScriptError ("* must come after a frame");
|
||||
}
|
||||
state.Fullbright = true;
|
||||
}
|
||||
else if (*token < 'A' || *token > ']')
|
||||
{
|
||||
sc.ScriptError ("Frames must be A-Z, [, \\, or ]");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!firstState)
|
||||
{
|
||||
states.Push (state);
|
||||
}
|
||||
firstState = false;
|
||||
state.Frame = *token-'A';
|
||||
}
|
||||
++token;
|
||||
}
|
||||
if (!firstState)
|
||||
{
|
||||
states.Push (state);
|
||||
}
|
||||
|
||||
token = strtok (NULL, ",\t\n\r");
|
||||
}
|
||||
}
|
||||
|
696
src/scripting/decorate/thingdef_exp.cpp
Normal file
696
src/scripting/decorate/thingdef_exp.cpp
Normal file
|
@ -0,0 +1,696 @@
|
|||
/*
|
||||
** thingdef_exp.cpp
|
||||
**
|
||||
** Expression parsing for DECORATE
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2005 Jan Cholasta
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "actor.h"
|
||||
#include "sc_man.h"
|
||||
#include "tarray.h"
|
||||
#include "templates.h"
|
||||
#include "cmdlib.h"
|
||||
#include "i_system.h"
|
||||
#include "m_random.h"
|
||||
#include "a_pickups.h"
|
||||
#include "thingdef.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "doomstat.h"
|
||||
#include "codegeneration/thingdef_exp.h"
|
||||
|
||||
FRandom pr_exrandom ("EX_Random");
|
||||
|
||||
static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cls);
|
||||
static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls);
|
||||
static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseAbs(FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseAtan2(FScanner &sc, FName identifier, PClassActor *cls);
|
||||
static FxExpression *ParseMinMax(FScanner &sc, FName identifier, PClassActor *cls);
|
||||
static FxExpression *ParseClamp(FScanner &sc, PClassActor *cls);
|
||||
|
||||
//
|
||||
// ParseExpression
|
||||
// [GRB] Parses an expression and stores it into Expression array
|
||||
// It's worth mentioning that this is using C++ operator precedence
|
||||
//
|
||||
|
||||
static FxExpression *ParseExpressionM (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionL (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionK (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionJ (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionI (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionH (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionG (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionF (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionE (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionD (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionC (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionB (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpressionA (FScanner &sc, PClassActor *cls);
|
||||
static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls);
|
||||
|
||||
FxExpression *ParseExpression (FScanner &sc, PClassActor *cls, bool mustresolve)
|
||||
{
|
||||
FxExpression *data = ParseExpressionM (sc, cls);
|
||||
|
||||
if (mustresolve)
|
||||
{
|
||||
FCompileContext ctx(cls);
|
||||
data = data->Resolve(ctx);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionM (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *base = ParseExpressionL (sc, cls);
|
||||
|
||||
if (sc.CheckToken('?'))
|
||||
{
|
||||
FxExpression *truex = ParseExpressionM (sc, cls);
|
||||
sc.MustGetToken(':');
|
||||
FxExpression *falsex = ParseExpressionM (sc, cls);
|
||||
return new FxConditional(base, truex, falsex);
|
||||
}
|
||||
else if (sc.CheckToken('='))
|
||||
{
|
||||
FxExpression *right = ParseExpressionM(sc, cls);
|
||||
return new FxAssign(base, right);
|
||||
}
|
||||
else
|
||||
{
|
||||
FxBinary *exp;
|
||||
FxAssignSelf *left = new FxAssignSelf(sc);
|
||||
|
||||
sc.GetToken();
|
||||
switch (sc.TokenType)
|
||||
{
|
||||
case TK_AddEq:
|
||||
exp = new FxAddSub('+', left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_SubEq:
|
||||
exp = new FxAddSub('-', left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_MulEq:
|
||||
exp = new FxMulDiv('*', left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_DivEq:
|
||||
exp = new FxMulDiv('/', left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_ModEq:
|
||||
exp = new FxMulDiv('%', left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_LShiftEq:
|
||||
exp = new FxBinaryInt(TK_LShift, left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_RShiftEq:
|
||||
exp = new FxBinaryInt(TK_RShift, left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_URShiftEq:
|
||||
exp = new FxBinaryInt(TK_URShift, left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_AndEq:
|
||||
exp = new FxBinaryInt('&', left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_XorEq:
|
||||
exp = new FxBinaryInt('^', left, nullptr);
|
||||
break;
|
||||
|
||||
case TK_OrEq:
|
||||
exp = new FxBinaryInt('|', left, nullptr);
|
||||
break;
|
||||
|
||||
default:
|
||||
sc.UnGet();
|
||||
delete left;
|
||||
return base;
|
||||
}
|
||||
|
||||
exp->right = ParseExpressionM(sc, cls);
|
||||
|
||||
FxAssign *ret = new FxAssign(base, exp);
|
||||
left->Assignment = ret;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionL (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionK (sc, cls);
|
||||
|
||||
while (sc.CheckToken(TK_OrOr))
|
||||
{
|
||||
FxExpression *right = ParseExpressionK (sc, cls);
|
||||
tmp = new FxBinaryLogical(TK_OrOr, tmp, right);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionK (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionJ (sc, cls);
|
||||
|
||||
while (sc.CheckToken(TK_AndAnd))
|
||||
{
|
||||
FxExpression *right = ParseExpressionJ (sc, cls);
|
||||
tmp = new FxBinaryLogical(TK_AndAnd, tmp, right);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionJ (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionI (sc, cls);
|
||||
|
||||
while (sc.CheckToken('|'))
|
||||
{
|
||||
FxExpression *right = ParseExpressionI (sc, cls);
|
||||
tmp = new FxBinaryInt('|', tmp, right);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionI (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionH (sc, cls);
|
||||
|
||||
while (sc.CheckToken('^'))
|
||||
{
|
||||
FxExpression *right = ParseExpressionH (sc, cls);
|
||||
tmp = new FxBinaryInt('^', tmp, right);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionH (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionG (sc, cls);
|
||||
|
||||
while (sc.CheckToken('&'))
|
||||
{
|
||||
FxExpression *right = ParseExpressionG (sc, cls);
|
||||
tmp = new FxBinaryInt('&', tmp, right);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionG (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionF (sc, cls);
|
||||
|
||||
while (sc.GetToken() && (sc.TokenType == TK_Eq || sc.TokenType == TK_Neq))
|
||||
{
|
||||
int token = sc.TokenType;
|
||||
FxExpression *right = ParseExpressionF (sc, cls);
|
||||
tmp = new FxCompareEq(token, tmp, right);
|
||||
}
|
||||
if (!sc.End) sc.UnGet();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionF (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionE (sc, cls);
|
||||
|
||||
while (sc.GetToken() && (sc.TokenType == '<' || sc.TokenType == '>' || sc.TokenType == TK_Leq || sc.TokenType == TK_Geq))
|
||||
{
|
||||
int token = sc.TokenType;
|
||||
FxExpression *right = ParseExpressionE (sc, cls);
|
||||
tmp = new FxCompareRel(token, tmp, right);
|
||||
}
|
||||
if (!sc.End) sc.UnGet();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionE (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionD (sc, cls);
|
||||
|
||||
while (sc.GetToken() && (sc.TokenType == TK_LShift || sc.TokenType == TK_RShift || sc.TokenType == TK_URShift))
|
||||
{
|
||||
int token = sc.TokenType;
|
||||
FxExpression *right = ParseExpressionD (sc, cls);
|
||||
tmp = new FxBinaryInt(token, tmp, right);
|
||||
}
|
||||
if (!sc.End) sc.UnGet();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionD (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionC (sc, cls);
|
||||
|
||||
while (sc.GetToken() && (sc.TokenType == '+' || sc.TokenType == '-'))
|
||||
{
|
||||
int token = sc.TokenType;
|
||||
FxExpression *right = ParseExpressionC (sc, cls);
|
||||
tmp = new FxAddSub(token, tmp, right);
|
||||
}
|
||||
if (!sc.End) sc.UnGet();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionC (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *tmp = ParseExpressionB (sc, cls);
|
||||
|
||||
while (sc.GetToken() && (sc.TokenType == '*' || sc.TokenType == '/' || sc.TokenType == '%'))
|
||||
{
|
||||
int token = sc.TokenType;
|
||||
FxExpression *right = ParseExpressionB (sc, cls);
|
||||
tmp = new FxMulDiv(token, tmp, right);
|
||||
}
|
||||
if (!sc.End) sc.UnGet();
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static FxExpression *ParseExpressionB (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
sc.GetToken();
|
||||
int token = sc.TokenType;
|
||||
switch(token)
|
||||
{
|
||||
case '~':
|
||||
return new FxUnaryNotBitwise(ParseExpressionA (sc, cls));
|
||||
|
||||
case '!':
|
||||
return new FxUnaryNotBoolean(ParseExpressionA (sc, cls));
|
||||
|
||||
case '-':
|
||||
return new FxMinusSign(ParseExpressionA (sc, cls));
|
||||
|
||||
case '+':
|
||||
return new FxPlusSign(ParseExpressionA (sc, cls));
|
||||
|
||||
case TK_Incr:
|
||||
case TK_Decr:
|
||||
return new FxPreIncrDecr(ParseExpressionA(sc, cls), token);
|
||||
|
||||
default:
|
||||
sc.UnGet();
|
||||
return ParseExpressionA (sc, cls);
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseExpressionB
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FxExpression *ParseExpressionA (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *base_expr = ParseExpression0 (sc, cls);
|
||||
|
||||
while(1)
|
||||
{
|
||||
FScriptPosition pos(sc);
|
||||
|
||||
#if 0
|
||||
if (sc.CheckToken('.'))
|
||||
{
|
||||
if (sc.CheckToken(TK_Default))
|
||||
{
|
||||
sc.MustGetToken('.');
|
||||
base_expr = new FxClassDefaults(base_expr, pos);
|
||||
}
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
|
||||
FName FieldName = sc.String;
|
||||
pos = sc;
|
||||
/* later!
|
||||
if (SC_CheckToken('('))
|
||||
{
|
||||
if (base_expr->IsDefaultObject())
|
||||
{
|
||||
SC_ScriptError("Cannot call methods for default.");
|
||||
}
|
||||
base_expr = ParseFunctionCall(base_expr, FieldName, false, false, pos);
|
||||
}
|
||||
else
|
||||
*/
|
||||
{
|
||||
base_expr = new FxDotIdentifier(base_expr, FieldName, pos);
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (sc.CheckToken('['))
|
||||
{
|
||||
FxExpression *index = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(']');
|
||||
base_expr = new FxArrayElement(base_expr, index);
|
||||
}
|
||||
else if (sc.CheckToken(TK_Incr))
|
||||
{
|
||||
return new FxPostIncrDecr(base_expr, TK_Incr);
|
||||
}
|
||||
else if (sc.CheckToken(TK_Decr))
|
||||
{
|
||||
return new FxPostIncrDecr(base_expr, TK_Decr);
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
return base_expr;
|
||||
}
|
||||
|
||||
|
||||
|
||||
static FxExpression *ParseExpression0 (FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FScriptPosition scpos(sc);
|
||||
if (sc.CheckToken('('))
|
||||
{
|
||||
FxExpression *data = ParseExpressionM (sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
return data;
|
||||
}
|
||||
else if (sc.CheckToken(TK_True))
|
||||
{
|
||||
return new FxConstant(true, scpos);
|
||||
}
|
||||
else if (sc.CheckToken(TK_False))
|
||||
{
|
||||
return new FxConstant(false, scpos);
|
||||
}
|
||||
else if (sc.CheckToken(TK_IntConst))
|
||||
{
|
||||
return new FxConstant(sc.Number, scpos);
|
||||
}
|
||||
else if (sc.CheckToken(TK_FloatConst))
|
||||
{
|
||||
return new FxConstant(sc.Float, scpos);
|
||||
}
|
||||
else if (sc.CheckToken(TK_NameConst))
|
||||
{
|
||||
return new FxConstant(sc.Name, scpos);
|
||||
}
|
||||
else if (sc.CheckToken(TK_StringConst))
|
||||
{
|
||||
// String parameters are converted to names. Technically, this should be
|
||||
// done at a higher level, as needed, but since no functions take string
|
||||
// arguments and ACS_NamedExecuteWithResult/CallACS need names, this is
|
||||
// a cheap way to get them working when people use "name" instead of 'name'.
|
||||
return new FxConstant(FName(sc.String), scpos);
|
||||
}
|
||||
else if (sc.CheckToken(TK_Bool))
|
||||
{
|
||||
sc.MustGetToken('(');
|
||||
FxExpression *exp = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
return new FxBoolCast(exp);
|
||||
}
|
||||
else if (sc.CheckToken(TK_Int))
|
||||
{
|
||||
sc.MustGetToken('(');
|
||||
FxExpression *exp = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
return new FxIntCast(exp);
|
||||
}
|
||||
else if (sc.CheckToken(TK_Float))
|
||||
{
|
||||
sc.MustGetToken('(');
|
||||
FxExpression *exp = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
return new FxFloatCast(exp);
|
||||
}
|
||||
else if (sc.CheckToken(TK_State))
|
||||
{
|
||||
sc.MustGetToken('(');
|
||||
FxExpression *exp;
|
||||
if (sc.CheckToken(TK_StringConst))
|
||||
{
|
||||
if (sc.String[0] == 0 || sc.Compare("None"))
|
||||
{
|
||||
exp = new FxConstant((FState*)nullptr, sc);
|
||||
}
|
||||
else
|
||||
{
|
||||
exp = new FxMultiNameState(sc.String, sc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
exp = new FxRuntimeStateIndex(ParseExpressionM(sc, cls));
|
||||
}
|
||||
sc.MustGetToken(')');
|
||||
return exp;
|
||||
}
|
||||
else if (sc.CheckToken(TK_Identifier))
|
||||
{
|
||||
FName identifier = FName(sc.String);
|
||||
FArgumentList *args;
|
||||
PFunction *func;
|
||||
|
||||
switch (identifier)
|
||||
{
|
||||
case NAME_Random:
|
||||
case NAME_FRandom:
|
||||
return ParseRandom(sc, identifier, cls);
|
||||
case NAME_RandomPick:
|
||||
case NAME_FRandomPick:
|
||||
return ParseRandomPick(sc, identifier, cls);
|
||||
case NAME_Random2:
|
||||
return ParseRandom2(sc, cls);
|
||||
default:
|
||||
if (cls != nullptr)
|
||||
{
|
||||
func = dyn_cast<PFunction>(cls->Symbols.FindSymbol(identifier, true));
|
||||
|
||||
// There is an action function ACS_NamedExecuteWithResult which must be ignored here for this to work.
|
||||
if (func != nullptr && identifier != NAME_ACS_NamedExecuteWithResult)
|
||||
{
|
||||
args = new FArgumentList;
|
||||
if (sc.CheckToken('('))
|
||||
{
|
||||
sc.UnGet();
|
||||
ParseFunctionParameters(sc, cls, *args, func, "", nullptr);
|
||||
}
|
||||
if (args->Size() == 0)
|
||||
{
|
||||
delete args;
|
||||
args = nullptr;
|
||||
}
|
||||
|
||||
return new FxVMFunctionCall(func, args, sc, false);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
if (sc.CheckToken('('))
|
||||
{
|
||||
switch (identifier)
|
||||
{
|
||||
case NAME_Min:
|
||||
case NAME_Max:
|
||||
return ParseMinMax(sc, identifier, cls);
|
||||
case NAME_Clamp:
|
||||
return ParseClamp(sc, cls);
|
||||
case NAME_Abs:
|
||||
return ParseAbs(sc, cls);
|
||||
case NAME_ATan2:
|
||||
case NAME_VectorAngle:
|
||||
return ParseAtan2(sc, identifier, cls);
|
||||
default:
|
||||
args = new FArgumentList;
|
||||
try
|
||||
{
|
||||
if (!sc.CheckToken(')'))
|
||||
{
|
||||
do
|
||||
{
|
||||
args->Push(ParseExpressionM (sc, cls));
|
||||
}
|
||||
while (sc.CheckToken(','));
|
||||
sc.MustGetToken(')');
|
||||
}
|
||||
return new FxFunctionCall(NULL, identifier, args, sc);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
delete args;
|
||||
throw;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return new FxIdentifier(identifier, sc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FString tokname = sc.TokenName(sc.TokenType, sc.String);
|
||||
sc.ScriptError ("Unexpected token %s", tokname.GetChars());
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static FRandom *ParseRNG(FScanner &sc)
|
||||
{
|
||||
FRandom *rng;
|
||||
|
||||
if (sc.CheckToken('['))
|
||||
{
|
||||
sc.MustGetToken(TK_Identifier);
|
||||
rng = FRandom::StaticFindRNG(sc.String);
|
||||
sc.MustGetToken(']');
|
||||
}
|
||||
else
|
||||
{
|
||||
rng = &pr_exrandom;
|
||||
}
|
||||
return rng;
|
||||
}
|
||||
|
||||
static FxExpression *ParseRandom(FScanner &sc, FName identifier, PClassActor *cls)
|
||||
{
|
||||
FRandom *rng = ParseRNG(sc);
|
||||
|
||||
sc.MustGetToken('(');
|
||||
FxExpression *min = ParseExpressionM (sc, cls);
|
||||
sc.MustGetToken(',');
|
||||
FxExpression *max = ParseExpressionM (sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
|
||||
if (identifier == NAME_Random)
|
||||
{
|
||||
return new FxRandom(rng, min, max, sc);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new FxFRandom(rng, min, max, sc);
|
||||
}
|
||||
}
|
||||
|
||||
static FxExpression *ParseRandomPick(FScanner &sc, FName identifier, PClassActor *cls)
|
||||
{
|
||||
bool floaty = identifier == NAME_FRandomPick;
|
||||
FRandom *rng;
|
||||
TArray<FxExpression*> list;
|
||||
list.Clear();
|
||||
int index = 0;
|
||||
|
||||
rng = ParseRNG(sc);
|
||||
sc.MustGetToken('(');
|
||||
|
||||
for (;;)
|
||||
{
|
||||
FxExpression *expr = ParseExpressionM(sc, cls);
|
||||
list.Push(expr);
|
||||
if (sc.CheckToken(')'))
|
||||
break;
|
||||
sc.MustGetToken(',');
|
||||
}
|
||||
return new FxRandomPick(rng, list, floaty, sc);
|
||||
}
|
||||
|
||||
static FxExpression *ParseRandom2(FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FRandom *rng = ParseRNG(sc);
|
||||
FxExpression *mask = NULL;
|
||||
|
||||
sc.MustGetToken('(');
|
||||
|
||||
if (!sc.CheckToken(')'))
|
||||
{
|
||||
mask = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
}
|
||||
return new FxRandom2(rng, mask, sc);
|
||||
}
|
||||
|
||||
static FxExpression *ParseAbs(FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *x = ParseExpressionM (sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
return new FxAbs(x);
|
||||
}
|
||||
|
||||
static FxExpression *ParseAtan2(FScanner &sc, FName identifier, PClassActor *cls)
|
||||
{
|
||||
FxExpression *a = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(',');
|
||||
FxExpression *b = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
return identifier == NAME_ATan2 ? new FxATan2(a, b, sc) : new FxATan2(b, a, sc);
|
||||
}
|
||||
|
||||
static FxExpression *ParseMinMax(FScanner &sc, FName identifier, PClassActor *cls)
|
||||
{
|
||||
TArray<FxExpression*> list;
|
||||
for (;;)
|
||||
{
|
||||
FxExpression *expr = ParseExpressionM(sc, cls);
|
||||
list.Push(expr);
|
||||
if (sc.CheckToken(')'))
|
||||
break;
|
||||
sc.MustGetToken(',');
|
||||
}
|
||||
return new FxMinMax(list, identifier, sc);
|
||||
}
|
||||
|
||||
static FxExpression *ParseClamp(FScanner &sc, PClassActor *cls)
|
||||
{
|
||||
FxExpression *src = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(',');
|
||||
FxExpression *min = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(',');
|
||||
FxExpression *max = ParseExpressionM(sc, cls);
|
||||
sc.MustGetToken(')');
|
||||
|
||||
// Build clamp(a,x,y) as min(max(a,x),y)
|
||||
TArray<FxExpression *> list(2);
|
||||
list.Reserve(2);
|
||||
list[0] = src;
|
||||
list[1] = min;
|
||||
FxExpression *maxexpr = new FxMinMax(list, NAME_Max, sc);
|
||||
list[0] = maxexpr;
|
||||
list[1] = max;
|
||||
return new FxMinMax(list, NAME_Min, sc);
|
||||
}
|
1434
src/scripting/decorate/thingdef_parse.cpp
Normal file
1434
src/scripting/decorate/thingdef_parse.cpp
Normal file
File diff suppressed because it is too large
Load diff
725
src/scripting/decorate/thingdef_states.cpp
Normal file
725
src/scripting/decorate/thingdef_states.cpp
Normal file
|
@ -0,0 +1,725 @@
|
|||
/*
|
||||
** thingdef_states.cpp
|
||||
**
|
||||
** Actor definitions - the state parser
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2007 Christoph Oelckers
|
||||
** Copyright 2004-2007 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "actor.h"
|
||||
#include "info.h"
|
||||
#include "sc_man.h"
|
||||
#include "tarray.h"
|
||||
#include "templates.h"
|
||||
#include "cmdlib.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "a_action.h"
|
||||
#include "p_local.h"
|
||||
#include "v_palette.h"
|
||||
#include "doomerrors.h"
|
||||
#include "thingdef.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "s_sound.h"
|
||||
#include "i_system.h"
|
||||
#include "colormatcher.h"
|
||||
#include "codegeneration/thingdef_exp.h"
|
||||
#include "version.h"
|
||||
#include "templates.h"
|
||||
|
||||
TDeletingArray<FStateTempCall *> StateTempCalls;
|
||||
|
||||
//==========================================================================
|
||||
//***
|
||||
// DoActionSpecials
|
||||
// handles action specials as code pointers
|
||||
//
|
||||
//==========================================================================
|
||||
FxVMFunctionCall *DoActionSpecials(FScanner &sc, FState & state, Baggage &bag)
|
||||
{
|
||||
int i;
|
||||
int min_args, max_args;
|
||||
FString specname = sc.String;
|
||||
|
||||
int special = P_FindLineSpecial(sc.String, &min_args, &max_args);
|
||||
|
||||
if (special > 0 && min_args >= 0)
|
||||
{
|
||||
FArgumentList *args = new FArgumentList;
|
||||
args->Push(new FxConstant(special, sc));
|
||||
i = 0;
|
||||
|
||||
// Make this consistent with all other parameter parsing
|
||||
if (sc.CheckToken('('))
|
||||
{
|
||||
while (i < 5)
|
||||
{
|
||||
args->Push(new FxIntCast(ParseExpression(sc, bag.Info)));
|
||||
i++;
|
||||
if (!sc.CheckToken (',')) break;
|
||||
}
|
||||
sc.MustGetToken (')');
|
||||
}
|
||||
|
||||
if (i < min_args)
|
||||
{
|
||||
sc.ScriptError ("Too few arguments to %s", specname.GetChars());
|
||||
}
|
||||
if (i > max_args)
|
||||
{
|
||||
sc.ScriptError ("Too many arguments to %s", specname.GetChars());
|
||||
}
|
||||
return new FxVMFunctionCall(FindGlobalActionFunction("A_CallSpecial"), args, sc, false);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//***
|
||||
// Reads a state label that may contain '.'s.
|
||||
// processes a state block
|
||||
//
|
||||
//==========================================================================
|
||||
static FString ParseStateString(FScanner &sc)
|
||||
{
|
||||
FString statestring;
|
||||
|
||||
sc.MustGetString();
|
||||
statestring = sc.String;
|
||||
if (sc.CheckString("::"))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
statestring << "::" << sc.String;
|
||||
}
|
||||
while (sc.CheckString ("."))
|
||||
{
|
||||
sc.MustGetString ();
|
||||
statestring << "." << sc.String;
|
||||
}
|
||||
return statestring;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//***
|
||||
// ParseStates
|
||||
// parses a state block
|
||||
//
|
||||
//==========================================================================
|
||||
void ParseStates(FScanner &sc, PClassActor * actor, AActor * defaults, Baggage &bag)
|
||||
{
|
||||
FString statestring;
|
||||
FState state;
|
||||
char lastsprite[5] = "";
|
||||
FStateTempCall *tcall = NULL;
|
||||
FArgumentList *args = NULL;
|
||||
|
||||
sc.MustGetStringName ("{");
|
||||
sc.SetEscape(false); // disable escape sequences in the state parser
|
||||
while (!sc.CheckString ("}") && !sc.End)
|
||||
{
|
||||
memset(&state,0,sizeof(state));
|
||||
statestring = ParseStateString(sc);
|
||||
if (!statestring.CompareNoCase("GOTO"))
|
||||
{
|
||||
do_goto:
|
||||
statestring = ParseStateString(sc);
|
||||
if (sc.CheckString ("+"))
|
||||
{
|
||||
sc.MustGetNumber ();
|
||||
statestring += '+';
|
||||
statestring += sc.String;
|
||||
}
|
||||
if (!bag.statedef.SetGotoLabel(statestring))
|
||||
{
|
||||
sc.ScriptError("GOTO before first state");
|
||||
}
|
||||
}
|
||||
else if (!statestring.CompareNoCase("STOP"))
|
||||
{
|
||||
do_stop:
|
||||
if (!bag.statedef.SetStop())
|
||||
{
|
||||
sc.ScriptError("STOP before first state");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (!statestring.CompareNoCase("WAIT") || !statestring.CompareNoCase("FAIL"))
|
||||
{
|
||||
if (!bag.statedef.SetWait())
|
||||
{
|
||||
sc.ScriptError("%s before first state", sc.String);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (!statestring.CompareNoCase("LOOP"))
|
||||
{
|
||||
if (!bag.statedef.SetLoop())
|
||||
{
|
||||
sc.ScriptError("LOOP before first state");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.MustGetString();
|
||||
if (sc.Compare (":"))
|
||||
{
|
||||
do
|
||||
{
|
||||
bag.statedef.AddStateLabel(statestring);
|
||||
statestring = ParseStateString(sc);
|
||||
if (!statestring.CompareNoCase("GOTO"))
|
||||
{
|
||||
goto do_goto;
|
||||
}
|
||||
else if (!statestring.CompareNoCase("STOP"))
|
||||
{
|
||||
goto do_stop;
|
||||
}
|
||||
sc.MustGetString ();
|
||||
} while (sc.Compare (":"));
|
||||
// continue;
|
||||
}
|
||||
|
||||
sc.UnGet ();
|
||||
|
||||
if (statestring.Len() != 4)
|
||||
{
|
||||
sc.ScriptError ("Sprite names must be exactly 4 characters\n");
|
||||
}
|
||||
|
||||
state.sprite = GetSpriteIndex(statestring);
|
||||
state.Misc1 = state.Misc2 = 0;
|
||||
sc.MustGetString();
|
||||
statestring = sc.String;
|
||||
|
||||
if (tcall == NULL)
|
||||
{
|
||||
tcall = new FStateTempCall;
|
||||
}
|
||||
if (sc.CheckString("RANDOM"))
|
||||
{
|
||||
int min, max;
|
||||
|
||||
sc.MustGetStringName("(");
|
||||
sc.MustGetNumber();
|
||||
min = clamp<int>(sc.Number, -1, SHRT_MAX);
|
||||
sc.MustGetStringName(",");
|
||||
sc.MustGetNumber();
|
||||
max = clamp<int>(sc.Number, -1, SHRT_MAX);
|
||||
sc.MustGetStringName(")");
|
||||
if (min > max)
|
||||
{
|
||||
swapvalues(min, max);
|
||||
}
|
||||
state.Tics = min;
|
||||
state.TicRange = max - min;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.MustGetNumber();
|
||||
state.Tics = clamp<int>(sc.Number, -1, SHRT_MAX);
|
||||
state.TicRange = 0;
|
||||
}
|
||||
|
||||
while (sc.GetString() && (!sc.Crossed || sc.Compare("{")))
|
||||
{
|
||||
if (sc.Compare("BRIGHT"))
|
||||
{
|
||||
state.Fullbright = true;
|
||||
continue;
|
||||
}
|
||||
if (sc.Compare("FAST"))
|
||||
{
|
||||
state.Fast = true;
|
||||
continue;
|
||||
}
|
||||
if (sc.Compare("SLOW"))
|
||||
{
|
||||
state.Slow = true;
|
||||
continue;
|
||||
}
|
||||
if (sc.Compare("NODELAY"))
|
||||
{
|
||||
if (bag.statedef.GetStateLabelIndex(NAME_Spawn) == bag.statedef.GetStateCount())
|
||||
{
|
||||
state.NoDelay = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.ScriptMessage("NODELAY may only be used immediately after Spawn:");
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (sc.Compare("OFFSET"))
|
||||
{
|
||||
// specify a weapon offset
|
||||
sc.MustGetStringName("(");
|
||||
sc.MustGetNumber();
|
||||
state.Misc1 = sc.Number;
|
||||
sc.MustGetStringName (",");
|
||||
sc.MustGetNumber();
|
||||
state.Misc2 = sc.Number;
|
||||
sc.MustGetStringName(")");
|
||||
continue;
|
||||
}
|
||||
if (sc.Compare("LIGHT"))
|
||||
{
|
||||
sc.MustGetStringName("(");
|
||||
do
|
||||
{
|
||||
sc.MustGetString();
|
||||
#ifdef DYNLIGHT
|
||||
AddStateLight(&state, sc.String);
|
||||
#endif
|
||||
}
|
||||
while (sc.CheckString(","));
|
||||
sc.MustGetStringName(")");
|
||||
continue;
|
||||
}
|
||||
if (sc.Compare("CANRAISE"))
|
||||
{
|
||||
state.CanRaise = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool hasfinalret;
|
||||
tcall->Code = ParseActions(sc, state, statestring, bag, hasfinalret);
|
||||
if (!hasfinalret && tcall->Code != nullptr)
|
||||
{
|
||||
static_cast<FxSequence *>(tcall->Code)->Add(new FxReturnStatement(nullptr, sc));
|
||||
}
|
||||
goto endofstate;
|
||||
}
|
||||
sc.UnGet();
|
||||
endofstate:
|
||||
int count = bag.statedef.AddStates(&state, statestring);
|
||||
if (count < 0)
|
||||
{
|
||||
sc.ScriptError ("Invalid frame character string '%s'", statestring.GetChars());
|
||||
count = -count;
|
||||
}
|
||||
if (tcall->Code != NULL)
|
||||
{
|
||||
tcall->ActorClass = actor;
|
||||
tcall->FirstState = bag.statedef.GetStateCount() - count;
|
||||
tcall->NumStates = count;
|
||||
StateTempCalls.Push(tcall);
|
||||
tcall = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tcall != NULL)
|
||||
{
|
||||
delete tcall;
|
||||
}
|
||||
if (args != NULL)
|
||||
{
|
||||
delete args;
|
||||
}
|
||||
sc.SetEscape(true); // re-enable escape sequences
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseActions
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static FxExpression *ParseIf(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret)
|
||||
{
|
||||
FxExpression *add, *cond;
|
||||
FxExpression *true_part, *false_part = nullptr;
|
||||
bool true_ret, false_ret = false;
|
||||
sc.MustGetStringName("(");
|
||||
cond = ParseExpression(sc, bag.Info);
|
||||
sc.MustGetStringName(")");
|
||||
sc.MustGetStringName("{"); // braces are mandatory
|
||||
true_part = ParseActions(sc, state, statestring, bag, true_ret);
|
||||
sc.MustGetString();
|
||||
if (sc.Compare("else"))
|
||||
{
|
||||
if (sc.CheckString("if"))
|
||||
{
|
||||
false_part = ParseIf(sc, state, statestring, bag, false_ret);
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.MustGetStringName("{"); // braces are still mandatory
|
||||
false_part = ParseActions(sc, state, statestring, bag, false_ret);
|
||||
sc.MustGetString();
|
||||
}
|
||||
}
|
||||
add = new FxIfStatement(cond, true_part, false_part, sc);
|
||||
// If one side does not end with a return, we don't consider the if statement
|
||||
// to end with a return. If the else case is missing, it can never be considered
|
||||
// as ending with a return.
|
||||
if (true_ret && false_ret)
|
||||
{
|
||||
lastwasret = true;
|
||||
}
|
||||
return add;
|
||||
}
|
||||
|
||||
static FxExpression *ParseWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret)
|
||||
{
|
||||
FxExpression *cond, *code;
|
||||
bool ret;
|
||||
|
||||
sc.MustGetStringName("(");
|
||||
cond = ParseExpression(sc, bag.Info);
|
||||
sc.MustGetStringName(")");
|
||||
sc.MustGetStringName("{"); // Enforce braces like for if statements.
|
||||
|
||||
code = ParseActions(sc, state, statestring, bag, ret);
|
||||
sc.MustGetString();
|
||||
|
||||
lastwasret = false; // A while loop always jumps back.
|
||||
|
||||
return new FxWhileLoop(cond, code, sc);
|
||||
}
|
||||
|
||||
static FxExpression *ParseDoWhile(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret)
|
||||
{
|
||||
FxExpression *cond, *code;
|
||||
bool ret;
|
||||
|
||||
sc.MustGetStringName("{"); // Enforce braces like for if statements.
|
||||
code = ParseActions(sc, state, statestring, bag, ret);
|
||||
|
||||
sc.MustGetStringName("while");
|
||||
sc.MustGetStringName("(");
|
||||
cond = ParseExpression(sc, bag.Info);
|
||||
sc.MustGetStringName(")");
|
||||
sc.MustGetStringName(";");
|
||||
sc.MustGetString();
|
||||
|
||||
lastwasret = false;
|
||||
|
||||
return new FxDoWhileLoop(cond, code, sc);
|
||||
}
|
||||
|
||||
static FxExpression *ParseFor(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &lastwasret)
|
||||
{
|
||||
FxExpression *init = nullptr;
|
||||
FxExpression *cond = nullptr;
|
||||
FxExpression *iter = nullptr;
|
||||
FxExpression *code = nullptr;
|
||||
bool ret;
|
||||
|
||||
// Parse the statements.
|
||||
sc.MustGetStringName("(");
|
||||
sc.MustGetString();
|
||||
if (!sc.Compare(";"))
|
||||
{
|
||||
sc.UnGet();
|
||||
init = ParseExpression(sc, bag.Info);
|
||||
sc.MustGetStringName(";");
|
||||
}
|
||||
sc.MustGetString();
|
||||
if (!sc.Compare(";"))
|
||||
{
|
||||
sc.UnGet();
|
||||
cond = ParseExpression(sc, bag.Info);
|
||||
sc.MustGetStringName(";");
|
||||
}
|
||||
sc.MustGetString();
|
||||
if (!sc.Compare(")"))
|
||||
{
|
||||
sc.UnGet();
|
||||
iter = ParseExpression(sc, bag.Info);
|
||||
sc.MustGetStringName(")");
|
||||
}
|
||||
|
||||
// Now parse the loop's content.
|
||||
sc.MustGetStringName("{"); // Enforce braces like for if statements.
|
||||
code = ParseActions(sc, state, statestring, bag, ret);
|
||||
sc.MustGetString();
|
||||
|
||||
lastwasret = false;
|
||||
|
||||
return new FxForLoop(init, cond, iter, code, sc);
|
||||
}
|
||||
|
||||
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret)
|
||||
{
|
||||
// If it's not a '{', then it should be a single action.
|
||||
// Otherwise, it's a sequence of actions.
|
||||
if (!sc.Compare("{"))
|
||||
{
|
||||
FxVMFunctionCall *call = ParseAction(sc, state, statestring, bag);
|
||||
endswithret = true;
|
||||
return new FxReturnStatement(call, sc);
|
||||
}
|
||||
|
||||
const FScriptPosition pos(sc);
|
||||
|
||||
FxSequence *seq = NULL;
|
||||
bool lastwasret = false;
|
||||
|
||||
sc.MustGetString();
|
||||
while (!sc.Compare("}"))
|
||||
{
|
||||
FxExpression *add;
|
||||
lastwasret = false;
|
||||
if (sc.Compare("if"))
|
||||
{ // Handle an if statement
|
||||
add = ParseIf(sc, state, statestring, bag, lastwasret);
|
||||
}
|
||||
else if (sc.Compare("while"))
|
||||
{ // Handle a while loop
|
||||
add = ParseWhile(sc, state, statestring, bag, lastwasret);
|
||||
}
|
||||
else if (sc.Compare("do"))
|
||||
{ // Handle a do-while loop
|
||||
add = ParseDoWhile(sc, state, statestring, bag, lastwasret);
|
||||
}
|
||||
else if (sc.Compare("for"))
|
||||
{ // Handle a for loop
|
||||
add = ParseFor(sc, state, statestring, bag, lastwasret);
|
||||
}
|
||||
else if (sc.Compare("return"))
|
||||
{ // Handle a return statement
|
||||
lastwasret = true;
|
||||
FxExpression *retexp = nullptr;
|
||||
sc.MustGetString();
|
||||
if (!sc.Compare(";"))
|
||||
{
|
||||
sc.UnGet();
|
||||
retexp = ParseExpression(sc, bag.Info);
|
||||
sc.MustGetStringName(";");
|
||||
}
|
||||
sc.MustGetString();
|
||||
add = new FxReturnStatement(retexp, sc);
|
||||
}
|
||||
else if (sc.Compare("break"))
|
||||
{
|
||||
add = new FxJumpStatement(TK_Break, sc);
|
||||
sc.MustGetStringName(";");
|
||||
sc.MustGetString();
|
||||
}
|
||||
else if (sc.Compare("continue"))
|
||||
{
|
||||
add = new FxJumpStatement(TK_Continue, sc);
|
||||
sc.MustGetStringName(";");
|
||||
sc.MustGetString();
|
||||
}
|
||||
else
|
||||
{ // Handle a regular expression
|
||||
sc.UnGet();
|
||||
add = ParseExpression(sc, bag.Info);
|
||||
sc.MustGetStringName(";");
|
||||
sc.MustGetString();
|
||||
}
|
||||
// Only return a sequence if it has actual content.
|
||||
if (add != NULL)
|
||||
{
|
||||
if (seq == NULL)
|
||||
{
|
||||
seq = new FxSequence(pos);
|
||||
}
|
||||
seq->Add(add);
|
||||
}
|
||||
}
|
||||
endswithret = lastwasret;
|
||||
return seq;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseAction
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag)
|
||||
{
|
||||
FxVMFunctionCall *call;
|
||||
|
||||
// Make the action name lowercase
|
||||
strlwr (sc.String);
|
||||
|
||||
call = DoActionSpecials(sc, state, bag);
|
||||
if (call != NULL)
|
||||
{
|
||||
return call;
|
||||
}
|
||||
|
||||
FName symname = FName(sc.String, true);
|
||||
symname = CheckCastKludges(symname);
|
||||
PFunction *afd = dyn_cast<PFunction>(bag.Info->Symbols.FindSymbol(symname, true));
|
||||
if (afd != NULL)
|
||||
{
|
||||
FArgumentList *args = new FArgumentList;
|
||||
ParseFunctionParameters(sc, bag.Info, *args, afd, statestring, &bag.statedef);
|
||||
call = new FxVMFunctionCall(afd, args->Size() > 0 ? args : NULL, sc, false);
|
||||
if (args->Size() == 0)
|
||||
{
|
||||
delete args;
|
||||
}
|
||||
return call;
|
||||
}
|
||||
sc.ScriptError("Invalid parameter '%s'\n", sc.String);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ParseFunctionParameters
|
||||
//
|
||||
// Parses the parameters for a VM function. Called by both ParseStates
|
||||
// (which will set statestring and statedef) and by ParseExpression0 (which
|
||||
// will not set them). The first token returned by the scanner when entering
|
||||
// this function should be '('.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
|
||||
PFunction *afd, FString statestring, FStateDefinitions *statedef)
|
||||
{
|
||||
const TArray<PType *> ¶ms = afd->Variants[0].Implementation->Proto->ArgumentTypes;
|
||||
const TArray<DWORD> ¶mflags = afd->Variants[0].ArgFlags;
|
||||
int numparams = (int)params.Size();
|
||||
int pnum = 0;
|
||||
bool zeroparm;
|
||||
|
||||
if (afd->Flags & VARF_Method)
|
||||
{
|
||||
numparams--;
|
||||
pnum++;
|
||||
}
|
||||
if (afd->Flags & VARF_Action)
|
||||
{
|
||||
numparams -= 2;
|
||||
pnum += 2;
|
||||
}
|
||||
assert(numparams >= 0);
|
||||
zeroparm = numparams == 0;
|
||||
if (numparams > 0 && !(paramflags[pnum] & VARF_Optional))
|
||||
{
|
||||
sc.MustGetStringName("(");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!sc.CheckString("("))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
while (numparams > 0)
|
||||
{
|
||||
FxExpression *x;
|
||||
if (statedef != NULL && params[pnum] == TypeState && sc.CheckNumber())
|
||||
{
|
||||
// Special case: State label as an offset
|
||||
if (sc.Number > 0 && statestring.Len() > 1)
|
||||
{
|
||||
sc.ScriptError("You cannot use state jumps commands with a jump offset on multistate definitions\n");
|
||||
}
|
||||
|
||||
int v = sc.Number;
|
||||
if (v < 0)
|
||||
{
|
||||
sc.ScriptError("Negative jump offsets are not allowed");
|
||||
}
|
||||
|
||||
if (v > 0)
|
||||
{
|
||||
x = new FxStateByIndex(statedef->GetStateCount() + v, sc);
|
||||
}
|
||||
else
|
||||
{
|
||||
x = new FxConstant((FState*)NULL, sc);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Use the generic parameter parser for everything else
|
||||
x = ParseParameter(sc, cls, params[pnum], false);
|
||||
}
|
||||
out_params.Push(x);
|
||||
pnum++;
|
||||
numparams--;
|
||||
if (numparams > 0)
|
||||
{
|
||||
if (params[pnum] == NULL)
|
||||
{ // varargs function
|
||||
if (sc.CheckString(")"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
pnum--;
|
||||
numparams++;
|
||||
}
|
||||
else if ((paramflags[pnum] & VARF_Optional) && sc.CheckString(")"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
sc.MustGetStringName (",");
|
||||
}
|
||||
}
|
||||
if (zeroparm)
|
||||
{
|
||||
if (!sc.CheckString(")"))
|
||||
{
|
||||
sc.ScriptError("You cannot pass parameters to '%s'\n", afd->SymbolName.GetChars());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.MustGetStringName(")");
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// CheckCastKludges
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FName CheckCastKludges(FName in)
|
||||
{
|
||||
switch (in)
|
||||
{
|
||||
case NAME_Int:
|
||||
return NAME___decorate_internal_int__;
|
||||
case NAME_Bool:
|
||||
return NAME___decorate_internal_bool__;
|
||||
case NAME_State:
|
||||
return NAME___decorate_internal_state__;
|
||||
case NAME_Float:
|
||||
return NAME___decorate_internal_float__;
|
||||
default:
|
||||
return in;
|
||||
}
|
||||
}
|
474
src/scripting/thingdef.cpp
Normal file
474
src/scripting/thingdef.cpp
Normal file
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
** thingdef.cpp
|
||||
**
|
||||
** Actor definitions
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2008 Christoph Oelckers
|
||||
** Copyright 2004-2008 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "gi.h"
|
||||
#include "actor.h"
|
||||
#include "info.h"
|
||||
#include "sc_man.h"
|
||||
#include "tarray.h"
|
||||
#include "w_wad.h"
|
||||
#include "templates.h"
|
||||
#include "r_defs.h"
|
||||
#include "a_pickups.h"
|
||||
#include "s_sound.h"
|
||||
#include "cmdlib.h"
|
||||
#include "p_lnspec.h"
|
||||
#include "a_action.h"
|
||||
#include "decallib.h"
|
||||
#include "m_random.h"
|
||||
#include "i_system.h"
|
||||
#include "m_argv.h"
|
||||
#include "p_local.h"
|
||||
#include "doomerrors.h"
|
||||
#include "a_weaponpiece.h"
|
||||
#include "p_conversation.h"
|
||||
#include "v_text.h"
|
||||
#include "thingdef.h"
|
||||
#include "codegeneration/thingdef_exp.h"
|
||||
#include "a_sharedglobal.h"
|
||||
#include "vmbuilder.h"
|
||||
#include "stats.h"
|
||||
|
||||
TDeletingArray<class FxExpression *> ActorDamageFuncs;
|
||||
|
||||
|
||||
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
|
||||
void InitThingdef();
|
||||
void ParseDecorate(FScanner &ctx);
|
||||
|
||||
// STATIC FUNCTION PROTOTYPES --------------------------------------------
|
||||
PClassActor *QuestItemClasses[31];
|
||||
|
||||
EXTERN_CVAR(Bool, strictdecorate);
|
||||
|
||||
PClassActor *DecoDerivedClass(const FScriptPosition &sc, PClassActor *parent, FName typeName)
|
||||
{
|
||||
PClassActor *type = static_cast<PClassActor *>(parent->CreateDerivedClass(typeName, parent->Size));
|
||||
if (type == nullptr)
|
||||
{
|
||||
FString newname = typeName.GetChars();
|
||||
FString sourcefile = sc.FileName;
|
||||
|
||||
sourcefile.Substitute(":", "@");
|
||||
newname << '@' << sourcefile;
|
||||
if (strictdecorate)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Tried to define class '%s' more than once.", typeName.GetChars());
|
||||
}
|
||||
else
|
||||
{
|
||||
// Due to backwards compatibility issues this cannot be an unconditional error.
|
||||
sc.Message(MSG_WARNING, "Tried to define class '%s' more than once. Renaming class to '%s'", typeName.GetChars(), newname.GetChars());
|
||||
}
|
||||
type = static_cast<PClassActor *>(parent->CreateDerivedClass(newname, parent->Size));
|
||||
if (type == nullptr)
|
||||
{
|
||||
// This we cannot handle cleanly anymore. Let's just abort and forget about the odd mod out that was this careless.
|
||||
sc.Message(MSG_FATAL, "Tried to define class '%s' more than twice in the same file.", typeName.GetChars());
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
//==========================================================================
|
||||
//
|
||||
// Starts a new actor definition
|
||||
//
|
||||
//==========================================================================
|
||||
PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, bool native)
|
||||
{
|
||||
PClassActor *replacee = NULL;
|
||||
PClassActor *ti = NULL;
|
||||
|
||||
PClassActor *parent = RUNTIME_CLASS(AActor);
|
||||
|
||||
if (parentName != NAME_None)
|
||||
{
|
||||
parent = PClass::FindActor(parentName);
|
||||
|
||||
PClassActor *p = parent;
|
||||
while (p != NULL)
|
||||
{
|
||||
if (p->TypeName == typeName)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "'%s' inherits from a class with the same name", typeName.GetChars());
|
||||
break;
|
||||
}
|
||||
p = dyn_cast<PClassActor>(p->ParentClass);
|
||||
}
|
||||
|
||||
if (parent == NULL)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Parent type '%s' not found in %s", parentName.GetChars(), typeName.GetChars());
|
||||
parent = RUNTIME_CLASS(AActor);
|
||||
}
|
||||
else if (!parent->IsDescendantOf(RUNTIME_CLASS(AActor)))
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Parent type '%s' is not an actor in %s", parentName.GetChars(), typeName.GetChars());
|
||||
parent = RUNTIME_CLASS(AActor);
|
||||
}
|
||||
}
|
||||
|
||||
if (native)
|
||||
{
|
||||
ti = PClass::FindActor(typeName);
|
||||
if (ti == NULL)
|
||||
{
|
||||
extern void DumpTypeTable();
|
||||
DumpTypeTable();
|
||||
sc.Message(MSG_ERROR, "Unknown native actor '%s'", typeName.GetChars());
|
||||
goto create;
|
||||
}
|
||||
else if (ti != RUNTIME_CLASS(AActor) && ti->ParentClass->NativeClass() != parent->NativeClass())
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Native class '%s' does not inherit from '%s'", typeName.GetChars(), parentName.GetChars());
|
||||
parent = RUNTIME_CLASS(AActor);
|
||||
goto create;
|
||||
}
|
||||
else if (ti->Defaults != NULL)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Redefinition of internal class '%s'", typeName.GetChars());
|
||||
goto create;
|
||||
}
|
||||
ti->InitializeNativeDefaults();
|
||||
ti->ParentClass->DeriveData(ti);
|
||||
}
|
||||
else
|
||||
{
|
||||
create:
|
||||
ti = DecoDerivedClass(sc, parent, typeName);
|
||||
}
|
||||
|
||||
ti->Replacee = ti->Replacement = NULL;
|
||||
ti->DoomEdNum = -1;
|
||||
return ti;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void SetReplacement(FScanner &sc, PClassActor *info, FName replaceName)
|
||||
{
|
||||
// Check for "replaces"
|
||||
if (replaceName != NAME_None)
|
||||
{
|
||||
// Get actor name
|
||||
PClassActor *replacee = PClass::FindActor(replaceName);
|
||||
|
||||
if (replacee == NULL)
|
||||
{
|
||||
sc.ScriptMessage("Replaced type '%s' not found for %s", replaceName.GetChars(), info->TypeName.GetChars());
|
||||
return;
|
||||
}
|
||||
if (replacee != NULL)
|
||||
{
|
||||
replacee->Replacement = info;
|
||||
info->Replacee = replacee;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Finalizes an actor definition
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void FinishActor(const FScriptPosition &sc, PClassActor *info, Baggage &bag)
|
||||
{
|
||||
AActor *defaults = (AActor*)info->Defaults;
|
||||
|
||||
try
|
||||
{
|
||||
bag.statedef.FinishStates (info, defaults);
|
||||
}
|
||||
catch (CRecoverableError &err)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "%s", err.GetMessage());
|
||||
bag.statedef.MakeStateDefines(NULL);
|
||||
return;
|
||||
}
|
||||
bag.statedef.InstallStates (info, defaults);
|
||||
bag.statedef.MakeStateDefines(NULL);
|
||||
if (bag.DropItemSet)
|
||||
{
|
||||
info->DropItems = bag.DropItemList;
|
||||
GC::WriteBarrier(info, info->DropItems);
|
||||
}
|
||||
if (info->IsDescendantOf (RUNTIME_CLASS(AInventory)))
|
||||
{
|
||||
defaults->flags |= MF_SPECIAL;
|
||||
}
|
||||
|
||||
// Weapons must be checked for all relevant states. They may crash the game otherwise.
|
||||
if (info->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
|
||||
{
|
||||
FState *ready = info->FindState(NAME_Ready);
|
||||
FState *select = info->FindState(NAME_Select);
|
||||
FState *deselect = info->FindState(NAME_Deselect);
|
||||
FState *fire = info->FindState(NAME_Fire);
|
||||
|
||||
// Consider any weapon without any valid state abstract and don't output a warning
|
||||
// This is for creating base classes for weapon groups that only set up some properties.
|
||||
if (ready || select || deselect || fire)
|
||||
{
|
||||
if (!ready)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Weapon %s doesn't define a ready state.\n", info->TypeName.GetChars());
|
||||
}
|
||||
if (!select)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Weapon %s doesn't define a select state.\n", info->TypeName.GetChars());
|
||||
}
|
||||
if (!deselect)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Weapon %s doesn't define a deselect state.\n", info->TypeName.GetChars());
|
||||
}
|
||||
if (!fire)
|
||||
{
|
||||
sc.Message(MSG_ERROR, "Weapon %s doesn't define a fire state.\n", info->TypeName.GetChars());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Do some postprocessing after everything has been defined
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static void DumpFunction(FILE *dump, VMScriptFunction *sfunc, const char *label, int labellen)
|
||||
{
|
||||
const char *marks = "=======================================================";
|
||||
fprintf(dump, "\n%.*s %s %.*s", MAX(3, 38 - labellen / 2), marks, label, MAX(3, 38 - labellen / 2), marks);
|
||||
fprintf(dump, "\nInteger regs: %-3d Float regs: %-3d Address regs: %-3d String regs: %-3d\nStack size: %d\n",
|
||||
sfunc->NumRegD, sfunc->NumRegF, sfunc->NumRegA, sfunc->NumRegS, sfunc->MaxParam);
|
||||
VMDumpConstants(dump, sfunc);
|
||||
fprintf(dump, "\nDisassembly @ %p:\n", sfunc->Code);
|
||||
VMDisasm(dump, sfunc->Code, sfunc->CodeSize, sfunc);
|
||||
}
|
||||
|
||||
static void FinishThingdef()
|
||||
{
|
||||
int errorcount = 0;
|
||||
unsigned i;
|
||||
int codesize = 0;
|
||||
FILE *dump = NULL;
|
||||
|
||||
if (Args->CheckParm("-dumpdisasm")) dump = fopen("disasm.txt", "w");
|
||||
|
||||
for (i = 0; i < StateTempCalls.Size(); ++i)
|
||||
{
|
||||
FStateTempCall *tcall = StateTempCalls[i];
|
||||
VMFunction *func = nullptr;
|
||||
|
||||
assert(tcall->Code != NULL);
|
||||
|
||||
// We don't know the return type in advance for anonymous functions.
|
||||
FCompileContext ctx(tcall->ActorClass, nullptr);
|
||||
tcall->Code = tcall->Code->Resolve(ctx);
|
||||
tcall->Proto = ctx.ReturnProto;
|
||||
|
||||
// Make sure resolving it didn't obliterate it.
|
||||
if (tcall->Code != nullptr)
|
||||
{
|
||||
// Can we call this function directly without wrapping it in an
|
||||
// anonymous function? e.g. Are we passing any parameters to it?
|
||||
func = tcall->Code->GetDirectFunction();
|
||||
|
||||
if (func == nullptr)
|
||||
{
|
||||
VMFunctionBuilder buildit(true);
|
||||
|
||||
assert(tcall->Proto != nullptr);
|
||||
|
||||
// Allocate registers used to pass parameters in.
|
||||
// self, stateowner, state (all are pointers)
|
||||
buildit.Registers[REGT_POINTER].Get(3);
|
||||
|
||||
// Emit code
|
||||
tcall->Code->Emit(&buildit);
|
||||
|
||||
VMScriptFunction *sfunc = buildit.MakeFunction();
|
||||
sfunc->NumArgs = NAP;
|
||||
|
||||
// Generate prototype for this anonymous function
|
||||
TArray<PType *> args(3);
|
||||
SetImplicitArgs(&args, NULL, tcall->ActorClass, VARF_Method | VARF_Action);
|
||||
sfunc->Proto = NewPrototype(tcall->Proto->ReturnTypes, args);
|
||||
|
||||
func = sfunc;
|
||||
|
||||
if (dump != NULL)
|
||||
{
|
||||
char label[64];
|
||||
int labellen = mysnprintf(label, countof(label), "Function %s.States[%d] (*%d)",
|
||||
tcall->ActorClass->TypeName.GetChars(), tcall->FirstState, tcall->NumStates);
|
||||
DumpFunction(dump, sfunc, label, labellen);
|
||||
codesize += sfunc->CodeSize;
|
||||
}
|
||||
}
|
||||
|
||||
delete tcall->Code;
|
||||
tcall->Code = nullptr;
|
||||
for (int k = 0; k < tcall->NumStates; ++k)
|
||||
{
|
||||
tcall->ActorClass->OwnedStates[tcall->FirstState + k].SetAction(func);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < PClassActor::AllActorClasses.Size(); i++)
|
||||
{
|
||||
PClassActor *ti = PClassActor::AllActorClasses[i];
|
||||
|
||||
if (ti->Size == TentativeClass)
|
||||
{
|
||||
Printf(TEXTCOLOR_RED "Class %s referenced but not defined\n", ti->TypeName.GetChars());
|
||||
errorcount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
AActor *def = GetDefaultByType(ti);
|
||||
|
||||
if (!def)
|
||||
{
|
||||
Printf(TEXTCOLOR_RED "No ActorInfo defined for class '%s'\n", ti->TypeName.GetChars());
|
||||
errorcount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (def->DamageFunc != nullptr)
|
||||
{
|
||||
FxDamageValue *dmg = (FxDamageValue *)ActorDamageFuncs[(uintptr_t)def->DamageFunc - 1];
|
||||
VMScriptFunction *sfunc;
|
||||
sfunc = dmg->GetFunction();
|
||||
if (sfunc == nullptr)
|
||||
{
|
||||
FCompileContext ctx(ti);
|
||||
dmg = static_cast<FxDamageValue *>(dmg->Resolve(ctx));
|
||||
|
||||
if (dmg != nullptr)
|
||||
{
|
||||
VMFunctionBuilder buildit;
|
||||
buildit.Registers[REGT_POINTER].Get(1); // The self pointer
|
||||
dmg->Emit(&buildit);
|
||||
sfunc = buildit.MakeFunction();
|
||||
sfunc->NumArgs = 1;
|
||||
sfunc->Proto = nullptr; ///FIXME: Need a proper prototype here
|
||||
// Save this function in case this damage value was reused
|
||||
// (which happens quite easily with inheritance).
|
||||
dmg->SetFunction(sfunc);
|
||||
}
|
||||
}
|
||||
def->DamageFunc = sfunc;
|
||||
|
||||
if (dump != nullptr && sfunc != nullptr)
|
||||
{
|
||||
char label[64];
|
||||
int labellen = mysnprintf(label, countof(label), "Function %s.Damage",
|
||||
ti->TypeName.GetChars());
|
||||
DumpFunction(dump, sfunc, label, labellen);
|
||||
codesize += sfunc->CodeSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dump != NULL)
|
||||
{
|
||||
fprintf(dump, "\n*************************************************************************\n%i code bytes\n", codesize * 4);
|
||||
fclose(dump);
|
||||
}
|
||||
if (errorcount > 0)
|
||||
{
|
||||
I_Error("%d errors during actor postprocessing", errorcount);
|
||||
}
|
||||
|
||||
ActorDamageFuncs.DeleteAndClear();
|
||||
StateTempCalls.DeleteAndClear();
|
||||
|
||||
// Since these are defined in DECORATE now the table has to be initialized here.
|
||||
for(int i = 0; i < 31; i++)
|
||||
{
|
||||
char fmt[20];
|
||||
mysnprintf(fmt, countof(fmt), "QuestItem%d", i+1);
|
||||
QuestItemClasses[i] = PClass::FindActor(fmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// LoadActors
|
||||
//
|
||||
// Called from FActor::StaticInit()
|
||||
//
|
||||
//==========================================================================
|
||||
void ParseScripts();
|
||||
|
||||
void LoadActors ()
|
||||
{
|
||||
int lastlump, lump;
|
||||
cycle_t timer;
|
||||
|
||||
timer.Reset(); timer.Clock();
|
||||
ActorDamageFuncs.Clear();
|
||||
FScriptPosition::ResetErrorCounter();
|
||||
InitThingdef();
|
||||
lastlump = 0;
|
||||
ParseScripts();
|
||||
while ((lump = Wads.FindLump ("DECORATE", &lastlump)) != -1)
|
||||
{
|
||||
FScanner sc(lump);
|
||||
ParseDecorate (sc);
|
||||
}
|
||||
FinishThingdef();
|
||||
if (FScriptPosition::ErrorCounter > 0)
|
||||
{
|
||||
I_Error("%d errors while parsing DECORATE scripts", FScriptPosition::ErrorCounter);
|
||||
}
|
||||
timer.Unclock();
|
||||
if (!batchrun) Printf("DECORATE parsing took %.2f ms\n", timer.TimeMS());
|
||||
// Base time: ~52 ms
|
||||
}
|
295
src/scripting/thingdef.h
Normal file
295
src/scripting/thingdef.h
Normal file
|
@ -0,0 +1,295 @@
|
|||
#ifndef __THINGDEF_H
|
||||
#define __THINGDEF_H
|
||||
|
||||
#include "doomtype.h"
|
||||
#include "info.h"
|
||||
#include "s_sound.h"
|
||||
#include "sc_man.h"
|
||||
#include "cmdlib.h"
|
||||
#include "vm.h"
|
||||
|
||||
|
||||
class FScanner;
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// A flag descriptor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct FFlagDef
|
||||
{
|
||||
unsigned int flagbit;
|
||||
const char *name;
|
||||
int structoffset;
|
||||
int fieldsize;
|
||||
};
|
||||
|
||||
FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict = false);
|
||||
void HandleDeprecatedFlags(AActor *defaults, PClassActor *info, bool set, int index);
|
||||
bool CheckDeprecatedFlags(const AActor *actor, PClassActor *info, int index);
|
||||
const char *GetFlagName(unsigned int flagnum, int flagoffset);
|
||||
void ModActorFlag(AActor *actor, FFlagDef *fd, bool set);
|
||||
bool ModActorFlag(AActor *actor, FString &flagname, bool set, bool printerror = true);
|
||||
INTBOOL CheckActorFlag(const AActor *actor, FFlagDef *fd);
|
||||
INTBOOL CheckActorFlag(const AActor *owner, const char *flagname, bool printerror = true);
|
||||
|
||||
#define FLAG_NAME(flagnum, flagvar) GetFlagName(flagnum, myoffsetof(AActor, flagvar))
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// State parser
|
||||
//
|
||||
//==========================================================================
|
||||
class FxExpression;
|
||||
|
||||
struct FStateLabels;
|
||||
|
||||
struct FStateDefine
|
||||
{
|
||||
FName Label;
|
||||
TArray<FStateDefine> Children;
|
||||
FState *State;
|
||||
BYTE DefineFlags;
|
||||
};
|
||||
|
||||
class FStateDefinitions
|
||||
{
|
||||
TArray<FStateDefine> StateLabels;
|
||||
FState *laststate;
|
||||
FState *laststatebeforelabel;
|
||||
intptr_t lastlabel;
|
||||
TArray<FState> StateArray;
|
||||
|
||||
static FStateDefine *FindStateLabelInList(TArray<FStateDefine> &list, FName name, bool create);
|
||||
static FStateLabels *CreateStateLabelList(TArray<FStateDefine> &statelist);
|
||||
static void MakeStateList(const FStateLabels *list, TArray<FStateDefine> &dest);
|
||||
static void RetargetStatePointers(intptr_t count, const char *target, TArray<FStateDefine> & statelist);
|
||||
FStateDefine *FindStateAddress(const char *name);
|
||||
FState *FindState(const char *name);
|
||||
|
||||
FState *ResolveGotoLabel(AActor *actor, PClassActor *mytype, char *name);
|
||||
static void FixStatePointers(PClassActor *actor, TArray<FStateDefine> & list);
|
||||
void ResolveGotoLabels(PClassActor *actor, AActor *defaults, TArray<FStateDefine> & list);
|
||||
|
||||
public:
|
||||
|
||||
FStateDefinitions()
|
||||
{
|
||||
laststate = NULL;
|
||||
laststatebeforelabel = NULL;
|
||||
lastlabel = -1;
|
||||
}
|
||||
|
||||
void SetStateLabel(const char *statename, FState *state, BYTE defflags = SDF_STATE);
|
||||
void AddStateLabel(const char *statename);
|
||||
int GetStateLabelIndex (FName statename);
|
||||
void InstallStates(PClassActor *info, AActor *defaults);
|
||||
int FinishStates(PClassActor *actor, AActor *defaults);
|
||||
|
||||
void MakeStateDefines(const PClassActor *cls);
|
||||
void AddStateDefines(const FStateLabels *list);
|
||||
void RetargetStates (intptr_t count, const char *target);
|
||||
|
||||
bool SetGotoLabel(const char *string);
|
||||
bool SetStop();
|
||||
bool SetWait();
|
||||
bool SetLoop();
|
||||
int AddStates(FState *state, const char *framechars);
|
||||
int GetStateCount() const { return StateArray.Size(); }
|
||||
};
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
struct FStateTempCall
|
||||
{
|
||||
FStateTempCall() : ActorClass(NULL), Code(NULL), FirstState(0), NumStates(0) {}
|
||||
|
||||
PClassActor *ActorClass;
|
||||
class FxExpression *Code;
|
||||
class PPrototype *Proto;
|
||||
int FirstState;
|
||||
int NumStates;
|
||||
};
|
||||
extern TDeletingArray<FStateTempCall *> StateTempCalls;
|
||||
extern TDeletingArray<class FxExpression *> ActorDamageFuncs;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Extra info maintained while defining an actor.
|
||||
//
|
||||
//==========================================================================
|
||||
class DDropItem;
|
||||
|
||||
struct Baggage
|
||||
{
|
||||
#ifdef _DEBUG
|
||||
FString ClassName; // This is here so that during debugging the class name can be seen
|
||||
#endif
|
||||
PClassActor *Info;
|
||||
bool DropItemSet;
|
||||
bool StateSet;
|
||||
bool fromZScript;
|
||||
int CurrentState;
|
||||
int Lumpnum;
|
||||
FStateDefinitions statedef;
|
||||
|
||||
DDropItem *DropItemList;
|
||||
|
||||
FScriptPosition ScriptPosition;
|
||||
};
|
||||
|
||||
inline void ResetBaggage (Baggage *bag, PClassActor *stateclass)
|
||||
{
|
||||
bag->DropItemList = NULL;
|
||||
bag->DropItemSet = false;
|
||||
bag->CurrentState = 0;
|
||||
bag->StateSet = false;
|
||||
bag->statedef.MakeStateDefines(stateclass);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Action function lookup
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
AFuncDesc *FindFunction(const char * string);
|
||||
|
||||
|
||||
void ParseStates(FScanner &sc, PClassActor *actor, AActor *defaults, Baggage &bag);
|
||||
void ParseFunctionParameters(FScanner &sc, PClassActor *cls, TArray<FxExpression *> &out_params,
|
||||
PFunction *afd, FString statestring, FStateDefinitions *statedef);
|
||||
FxExpression *ParseActions(FScanner &sc, FState state, FString statestring, Baggage &bag, bool &endswithret);
|
||||
class FxVMFunctionCall *ParseAction(FScanner &sc, FState state, FString statestring, Baggage &bag);
|
||||
FName CheckCastKludges(FName in);
|
||||
void SetImplicitArgs(TArray<PType *> *args, TArray<DWORD> *argflags, PClass *cls, DWORD funcflags);
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Property parser
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PClassActor *CreateNewActor(const FScriptPosition &sc, FName typeName, FName parentName, bool native);
|
||||
void SetReplacement(FScanner &sc, PClassActor *info, FName replaceName);
|
||||
|
||||
void HandleActorFlag(FScanner &sc, Baggage &bag, const char *part1, const char *part2, int mod);
|
||||
void FinishActor(const FScriptPosition &sc, PClassActor *info, Baggage &bag);
|
||||
FxExpression *ParseParameter(FScanner &sc, PClassActor *cls, PType *type, bool constant);
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
DEPF_UNUSED,
|
||||
DEPF_FIREDAMAGE,
|
||||
DEPF_ICEDAMAGE,
|
||||
DEPF_LOWGRAVITY,
|
||||
DEPF_LONGMELEERANGE,
|
||||
DEPF_SHORTMISSILERANGE,
|
||||
DEPF_PICKUPFLASH,
|
||||
DEPF_QUARTERGRAVITY,
|
||||
DEPF_FIRERESIST,
|
||||
DEPF_HERETICBOUNCE,
|
||||
DEPF_HEXENBOUNCE,
|
||||
DEPF_DOOMBOUNCE,
|
||||
DEPF_INTERHUBSTRIP,
|
||||
};
|
||||
|
||||
// Types of old style decorations
|
||||
enum EDefinitionType
|
||||
{
|
||||
DEF_Decoration,
|
||||
DEF_BreakableDecoration,
|
||||
DEF_Pickup,
|
||||
DEF_Projectile,
|
||||
};
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma section(".greg$u",read)
|
||||
|
||||
#define MSVC_PSEG __declspec(allocate(".greg$u"))
|
||||
#define GCC_PSEG
|
||||
#else
|
||||
#define MSVC_PSEG
|
||||
#define GCC_PSEG __attribute__((section(SECTION_GREG))) __attribute__((used))
|
||||
#endif
|
||||
|
||||
|
||||
union FPropParam
|
||||
{
|
||||
int i;
|
||||
double d;
|
||||
const char *s;
|
||||
FxExpression *exp;
|
||||
};
|
||||
|
||||
typedef void (*PropHandler)(AActor *defaults, PClassActor *info, Baggage &bag, FPropParam *params);
|
||||
|
||||
enum ECategory
|
||||
{
|
||||
CAT_PROPERTY, // Inheritable property
|
||||
CAT_INFO // non-inheritable info (spawn ID, Doomednum, game filter, conversation ID, not usable in ZScript)
|
||||
};
|
||||
|
||||
struct FPropertyInfo
|
||||
{
|
||||
const char *name;
|
||||
const char *params;
|
||||
const PClass * const *cls;
|
||||
PropHandler Handler;
|
||||
int category;
|
||||
};
|
||||
|
||||
FPropertyInfo *FindProperty(const char * string);
|
||||
int MatchString (const char *in, const char **strings);
|
||||
|
||||
|
||||
#define DEFINE_PROPERTY_BASE(name, paramlist, clas, cat) \
|
||||
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \
|
||||
static FPropertyInfo Prop_##name##_##paramlist##_##clas = \
|
||||
{ #name, #paramlist, &RUNTIME_CLASS_CASTLESS(A##clas), (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
|
||||
MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \
|
||||
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params)
|
||||
|
||||
#define DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, cat) \
|
||||
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params); \
|
||||
static FPropertyInfo Prop_##name##_##paramlist##_##clas = \
|
||||
{ #prefix"."#name, #paramlist, &RUNTIME_CLASS_CASTLESS(A##clas), (PropHandler)Handler_##name##_##paramlist##_##clas, cat }; \
|
||||
MSVC_PSEG FPropertyInfo *infoptr_##name##_##paramlist##_##clas GCC_PSEG = &Prop_##name##_##paramlist##_##clas; \
|
||||
static void Handler_##name##_##paramlist##_##clas(A##clas *defaults, PClassActor *info, Baggage &bag, FPropParam *params)
|
||||
|
||||
|
||||
#define DEFINE_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_PROPERTY)
|
||||
#define DEFINE_INFO_PROPERTY(name, paramlist, clas) DEFINE_PROPERTY_BASE(name, paramlist, clas, CAT_INFO)
|
||||
|
||||
#define DEFINE_CLASS_PROPERTY(name, paramlist, clas) DEFINE_PREFIXED_PROPERTY_BASE(clas, name, paramlist, clas, CAT_PROPERTY)
|
||||
#define DEFINE_CLASS_PROPERTY_PREFIX(prefix, name, paramlist, clas) DEFINE_PREFIXED_PROPERTY_BASE(prefix, name, paramlist, clas, CAT_PROPERTY)
|
||||
|
||||
#define PROP_PARM_COUNT (params[0].i)
|
||||
|
||||
#define PROP_STRING_PARM(var, no) \
|
||||
const char *var = params[(no)+1].s;
|
||||
|
||||
#define PROP_EXP_PARM(var, no) \
|
||||
FxExpression *var = params[(no)+1].exp;
|
||||
|
||||
#define PROP_INT_PARM(var, no) \
|
||||
int var = params[(no)+1].i;
|
||||
|
||||
#define PROP_FLOAT_PARM(var, no) \
|
||||
float var = float(params[(no)+1].d);
|
||||
|
||||
#define PROP_DOUBLE_PARM(var, no) \
|
||||
double var = params[(no)+1].d;
|
||||
|
||||
#define PROP_COLOR_PARM(var, no) \
|
||||
int var = params[(no)+1].i== 0? params[(no)+2].i : V_GetColor(NULL, params[(no)+2].s);
|
||||
|
||||
#endif
|
678
src/scripting/thingdef_data.cpp
Normal file
678
src/scripting/thingdef_data.cpp
Normal file
|
@ -0,0 +1,678 @@
|
|||
/*
|
||||
** thingdef_data.cpp
|
||||
**
|
||||
** DECORATE data tables
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright 2002-2008 Christoph Oelckers
|
||||
** Copyright 2004-2008 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
** 4. When not used as part of ZDoom or a ZDoom derivative, this code will be
|
||||
** covered by the terms of the GNU General Public License as published by
|
||||
** the Free Software Foundation; either version 2 of the License, or (at
|
||||
** your option) any later version.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "thingdef.h"
|
||||
#include "actor.h"
|
||||
#include "d_player.h"
|
||||
#include "p_effect.h"
|
||||
#include "autosegs.h"
|
||||
|
||||
static TArray<FPropertyInfo*> properties;
|
||||
static TArray<AFuncDesc> AFTable;
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// List of all flags
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
// [RH] Keep GCC quiet by not using offsetof on Actor types.
|
||||
#define DEFINE_FLAG(prefix, name, type, variable) { (unsigned int)prefix##_##name, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable) }
|
||||
#define DEFINE_FLAG2(symbol, name, type, variable) { (unsigned int)symbol, #name, (int)(size_t)&((type*)1)->variable - 1, sizeof(((type *)0)->variable) }
|
||||
#define DEFINE_DEPRECATED_FLAG(name) { DEPF_##name, #name, -1, 0 }
|
||||
#define DEFINE_DUMMY_FLAG(name) { DEPF_UNUSED, #name, -1, 0 }
|
||||
|
||||
static FFlagDef ActorFlagDefs[]=
|
||||
{
|
||||
DEFINE_FLAG(MF, PICKUP, APlayerPawn, flags),
|
||||
DEFINE_FLAG(MF, SPECIAL, APlayerPawn, flags),
|
||||
DEFINE_FLAG(MF, SOLID, AActor, flags),
|
||||
DEFINE_FLAG(MF, SHOOTABLE, AActor, flags),
|
||||
DEFINE_FLAG(MF, NOSECTOR, AActor, flags),
|
||||
DEFINE_FLAG(MF, NOBLOCKMAP, AActor, flags),
|
||||
DEFINE_FLAG(MF, AMBUSH, AActor, flags),
|
||||
DEFINE_FLAG(MF, JUSTHIT, AActor, flags),
|
||||
DEFINE_FLAG(MF, JUSTATTACKED, AActor, flags),
|
||||
DEFINE_FLAG(MF, SPAWNCEILING, AActor, flags),
|
||||
DEFINE_FLAG(MF, NOGRAVITY, AActor, flags),
|
||||
DEFINE_FLAG(MF, DROPOFF, AActor, flags),
|
||||
DEFINE_FLAG(MF, NOCLIP, AActor, flags),
|
||||
DEFINE_FLAG(MF, FLOAT, AActor, flags),
|
||||
DEFINE_FLAG(MF, TELEPORT, AActor, flags),
|
||||
DEFINE_FLAG(MF, MISSILE, AActor, flags),
|
||||
DEFINE_FLAG(MF, DROPPED, AActor, flags),
|
||||
DEFINE_FLAG(MF, SHADOW, AActor, flags),
|
||||
DEFINE_FLAG(MF, NOBLOOD, AActor, flags),
|
||||
DEFINE_FLAG(MF, CORPSE, AActor, flags),
|
||||
DEFINE_FLAG(MF, INFLOAT, AActor, flags),
|
||||
DEFINE_FLAG(MF, COUNTKILL, AActor, flags),
|
||||
DEFINE_FLAG(MF, COUNTITEM, AActor, flags),
|
||||
DEFINE_FLAG(MF, SKULLFLY, AActor, flags),
|
||||
DEFINE_FLAG(MF, NOTDMATCH, AActor, flags),
|
||||
DEFINE_FLAG(MF, SPAWNSOUNDSOURCE, AActor, flags),
|
||||
DEFINE_FLAG(MF, FRIENDLY, AActor, flags),
|
||||
DEFINE_FLAG(MF, NOLIFTDROP, AActor, flags),
|
||||
DEFINE_FLAG(MF, STEALTH, AActor, flags),
|
||||
DEFINE_FLAG(MF, ICECORPSE, AActor, flags),
|
||||
|
||||
DEFINE_FLAG(MF2, DONTREFLECT, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, WINDTHRUST, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, DONTSEEKINVISIBLE, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, BLASTED, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, FLOORCLIP, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, SPAWNFLOAT, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, NOTELEPORT, AActor, flags2),
|
||||
DEFINE_FLAG2(MF2_RIP, RIPPER, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, PUSHABLE, AActor, flags2),
|
||||
DEFINE_FLAG2(MF2_SLIDE, SLIDESONWALLS, AActor, flags2),
|
||||
DEFINE_FLAG2(MF2_PASSMOBJ, CANPASS, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, CANNOTPUSH, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, THRUGHOST, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, BOSS, AActor, flags2),
|
||||
DEFINE_FLAG2(MF2_NODMGTHRUST, NODAMAGETHRUST, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, DONTTRANSLATE, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, TELESTOMP, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, FLOATBOB, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, THRUACTORS, AActor, flags2),
|
||||
DEFINE_FLAG2(MF2_IMPACT, ACTIVATEIMPACT, AActor, flags2),
|
||||
DEFINE_FLAG2(MF2_PUSHWALL, CANPUSHWALLS, AActor, flags2),
|
||||
DEFINE_FLAG2(MF2_MCROSS, ACTIVATEMCROSS, AActor, flags2),
|
||||
DEFINE_FLAG2(MF2_PCROSS, ACTIVATEPCROSS, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, CANTLEAVEFLOORPIC, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, NONSHOOTABLE, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, INVULNERABLE, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, DORMANT, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, SEEKERMISSILE, AActor, flags2),
|
||||
DEFINE_FLAG(MF2, REFLECTIVE, AActor, flags2),
|
||||
|
||||
DEFINE_FLAG(MF3, FLOORHUGGER, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, CEILINGHUGGER, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, NORADIUSDMG, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, GHOST, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, SPECIALFLOORCLIP, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, ALWAYSPUFF, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, DONTSPLASH, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, DONTOVERLAP, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, DONTMORPH, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, DONTSQUASH, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, EXPLOCOUNT, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, FULLVOLACTIVE, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, ISMONSTER, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, SKYEXPLODE, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, STAYMORPHED, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, DONTBLAST, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, CANBLAST, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, NOTARGET, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, DONTGIB, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, NOBLOCKMONST, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, FULLVOLDEATH, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, AVOIDMELEE, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, SCREENSEEKER, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, FOILINVUL, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, NOTELEOTHER, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, BLOODLESSIMPACT, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, NOEXPLODEFLOOR, AActor, flags3),
|
||||
DEFINE_FLAG(MF3, PUFFONACTORS, AActor, flags3),
|
||||
|
||||
DEFINE_FLAG(MF4, QUICKTORETALIATE, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, NOICEDEATH, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, RANDOMIZE, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, FIXMAPTHINGPOS , AActor, flags4),
|
||||
DEFINE_FLAG(MF4, ACTLIKEBRIDGE, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, STRIFEDAMAGE, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, CANUSEWALLS, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, MISSILEMORE, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, MISSILEEVENMORE, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, FORCERADIUSDMG, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, DONTFALL, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, SEESDAGGERS, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, INCOMBAT, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, LOOKALLAROUND, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, STANDSTILL, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, SPECTRAL, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, NOSPLASHALERT, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, SYNCHRONIZED, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, NOTARGETSWITCH, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, DONTHARMCLASS, AActor, flags4),
|
||||
DEFINE_FLAG2(MF4_DONTHARMCLASS, DONTHURTSPECIES, AActor, flags4), // Deprecated name as an alias
|
||||
DEFINE_FLAG(MF4, SHIELDREFLECT, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, DEFLECT, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, ALLOWPARTICLES, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, EXTREMEDEATH, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, NOEXTREMEDEATH, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, FRIGHTENED, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, NOSKIN, AActor, flags4),
|
||||
DEFINE_FLAG(MF4, BOSSDEATH, AActor, flags4),
|
||||
|
||||
DEFINE_FLAG(MF5, DONTDRAIN, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, GETOWNER, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NODROPOFF, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NOFORWARDFALL, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, COUNTSECRET, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NODAMAGE, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, BLOODSPLATTER, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, OLDRADIUSDMG, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, DEHEXPLOSION, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, PIERCEARMOR, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NOBLOODDECALS, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, USESPECIAL, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NOPAIN, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, ALWAYSFAST, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NEVERFAST, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, ALWAYSRESPAWN, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NEVERRESPAWN, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, DONTRIP, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NOINFIGHTING, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NOINTERACTION, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NOTIMEFREEZE, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, PUFFGETSOWNER, AActor, flags5), // [BB] added PUFFGETSOWNER
|
||||
DEFINE_FLAG(MF5, SPECIALFIREDAMAGE, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, SUMMONEDMONSTER, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, NOVERTICALMELEERANGE, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, BRIGHT, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, CANTSEEK, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, PAINLESS, AActor, flags5),
|
||||
DEFINE_FLAG(MF5, MOVEWITHSECTOR, AActor, flags5),
|
||||
|
||||
DEFINE_FLAG(MF6, NOBOSSRIP, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, THRUSPECIES, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, MTHRUSPECIES, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, FORCEPAIN, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, NOFEAR, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, BUMPSPECIAL, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, DONTHARMSPECIES, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, STEPMISSILE, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, NOTELEFRAG, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, TOUCHY, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, CANJUMP, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, JUMPDOWN, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, VULNERABLE, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, NOTRIGGER, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, ADDITIVEPOISONDAMAGE, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, ADDITIVEPOISONDURATION, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, BLOCKEDBYSOLIDACTORS, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, NOMENU, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, SEEINVISIBLE, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, DONTCORPSE, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, DOHARMSPECIES, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, POISONALWAYS, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, NOTAUTOAIMED, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, NOTONAUTOMAP, AActor, flags6),
|
||||
DEFINE_FLAG(MF6, RELATIVETOFLOOR, AActor, flags6),
|
||||
|
||||
DEFINE_FLAG(MF7, NEVERTARGET, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, NOTELESTOMP, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, ALWAYSTELEFRAG, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, WEAPONSPAWN, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, HARMFRIENDS, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, BUDDHA, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, FOILBUDDHA, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, DONTTHRUST, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, ALLOWPAIN, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, CAUSEPAIN, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, THRUREFLECT, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, MIRRORREFLECT, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, AIMREFLECT, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, HITTARGET, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, HITMASTER, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, HITTRACER, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, NODECAL, AActor, flags7), // [ZK] Decal flags
|
||||
DEFINE_FLAG(MF7, FORCEDECAL, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, LAXTELEFRAGDMG, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, ICESHATTER, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, ALLOWTHRUFLAGS, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, USEKILLSCRIPTS, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, NOKILLSCRIPTS, AActor, flags7),
|
||||
DEFINE_FLAG(MF7, SPRITEANGLE, AActor, flags7),
|
||||
|
||||
// Effect flags
|
||||
DEFINE_FLAG(FX, VISIBILITYPULSE, AActor, effects),
|
||||
DEFINE_FLAG2(FX_ROCKET, ROCKETTRAIL, AActor, effects),
|
||||
DEFINE_FLAG2(FX_GRENADE, GRENADETRAIL, AActor, effects),
|
||||
DEFINE_FLAG(RF, INVISIBLE, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, FORCEYBILLBOARD, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, FORCEXYBILLBOARD, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, ROLLSPRITE, AActor, renderflags), // [marrub] roll the sprite billboard
|
||||
// [fgsfds] Flat sprites
|
||||
DEFINE_FLAG(RF, FLATSPRITE, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, WALLSPRITE, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, DONTFLIP, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, ROLLCENTER, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, MASKROTATION, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, ABSMASKANGLE, AActor, renderflags),
|
||||
DEFINE_FLAG(RF, ABSMASKPITCH, AActor, renderflags),
|
||||
|
||||
// Bounce flags
|
||||
DEFINE_FLAG2(BOUNCE_Walls, BOUNCEONWALLS, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_Floors, BOUNCEONFLOORS, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_Ceilings, BOUNCEONCEILINGS, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_Actors, ALLOWBOUNCEONACTORS, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_AutoOff, BOUNCEAUTOOFF, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_HereticType, BOUNCELIKEHERETIC, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_CanBounceWater, CANBOUNCEWATER, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_NoWallSound, NOWALLBOUNCESND, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_Quiet, NOBOUNCESOUND, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_AllActors, BOUNCEONACTORS, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_ExplodeOnWater, EXPLODEONWATER, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_MBF, MBFBOUNCER, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_AutoOffFloorOnly, BOUNCEAUTOOFFFLOORONLY, AActor, BounceFlags),
|
||||
DEFINE_FLAG2(BOUNCE_UseBounceState, USEBOUNCESTATE, AActor, BounceFlags),
|
||||
|
||||
// Deprecated flags. Handling must be performed in HandleDeprecatedFlags
|
||||
DEFINE_DEPRECATED_FLAG(FIREDAMAGE),
|
||||
DEFINE_DEPRECATED_FLAG(ICEDAMAGE),
|
||||
DEFINE_DEPRECATED_FLAG(LOWGRAVITY),
|
||||
DEFINE_DEPRECATED_FLAG(SHORTMISSILERANGE),
|
||||
DEFINE_DEPRECATED_FLAG(LONGMELEERANGE),
|
||||
DEFINE_DEPRECATED_FLAG(QUARTERGRAVITY),
|
||||
DEFINE_DEPRECATED_FLAG(FIRERESIST),
|
||||
DEFINE_DEPRECATED_FLAG(HERETICBOUNCE),
|
||||
DEFINE_DEPRECATED_FLAG(HEXENBOUNCE),
|
||||
DEFINE_DEPRECATED_FLAG(DOOMBOUNCE),
|
||||
|
||||
// Deprecated flags with no more existing functionality.
|
||||
DEFINE_DUMMY_FLAG(FASTER), // obsolete, replaced by 'Fast' state flag
|
||||
DEFINE_DUMMY_FLAG(FASTMELEE), // obsolete, replaced by 'Fast' state flag
|
||||
|
||||
// Various Skulltag flags that are quite irrelevant to ZDoom
|
||||
DEFINE_DUMMY_FLAG(NONETID), // netcode-based
|
||||
DEFINE_DUMMY_FLAG(ALLOWCLIENTSPAWN), // netcode-based
|
||||
DEFINE_DUMMY_FLAG(CLIENTSIDEONLY), // netcode-based
|
||||
DEFINE_DUMMY_FLAG(SERVERSIDEONLY), // netcode-based
|
||||
DEFINE_DUMMY_FLAG(EXPLODEONDEATH), // seems useless
|
||||
};
|
||||
|
||||
static FFlagDef InventoryFlagDefs[] =
|
||||
{
|
||||
// Inventory flags
|
||||
DEFINE_FLAG(IF, QUIET, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, AUTOACTIVATE, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, UNDROPPABLE, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, INVBAR, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, HUBPOWER, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, UNTOSSABLE, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, ADDITIVETIME, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, ALWAYSPICKUP, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, FANCYPICKUPSOUND, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, BIGPOWERUP, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, KEEPDEPLETED, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, IGNORESKILL, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, NOATTENPICKUPSOUND, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, PERSISTENTPOWER, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, RESTRICTABSOLUTELY, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, NEVERRESPAWN, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, NOSCREENFLASH, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, TOSSED, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, ALWAYSRESPAWN, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, TRANSFER, AInventory, ItemFlags),
|
||||
DEFINE_FLAG(IF, NOTELEPORTFREEZE, AInventory, ItemFlags),
|
||||
|
||||
DEFINE_DEPRECATED_FLAG(PICKUPFLASH),
|
||||
DEFINE_DEPRECATED_FLAG(INTERHUBSTRIP),
|
||||
};
|
||||
|
||||
static FFlagDef WeaponFlagDefs[] =
|
||||
{
|
||||
// Weapon flags
|
||||
DEFINE_FLAG(WIF, NOAUTOFIRE, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, READYSNDHALF, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, DONTBOB, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, AXEBLOOD, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, NOALERT, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, AMMO_OPTIONAL, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, ALT_AMMO_OPTIONAL, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, PRIMARY_USES_BOTH, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, WIMPY_WEAPON, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, POWERED_UP, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, STAFF2_KICKBACK, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF_BOT, EXPLOSIVE, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, MELEEWEAPON, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF_BOT, BFG, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, CHEATNOTWEAPON, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, NO_AUTO_SWITCH, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, AMMO_CHECKBOTH, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, NOAUTOAIM, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, NODEATHDESELECT, AWeapon, WeaponFlags),
|
||||
DEFINE_FLAG(WIF, NODEATHINPUT, AWeapon, WeaponFlags),
|
||||
|
||||
DEFINE_DUMMY_FLAG(NOLMS),
|
||||
DEFINE_FLAG(WIF, ALT_USES_BOTH, AWeapon, WeaponFlags),
|
||||
DEFINE_DUMMY_FLAG(ALLOW_WITH_RESPAWN_INVUL),
|
||||
};
|
||||
|
||||
static FFlagDef PlayerPawnFlagDefs[] =
|
||||
{
|
||||
// PlayerPawn flags
|
||||
DEFINE_FLAG(PPF, NOTHRUSTWHENINVUL, APlayerPawn, PlayerFlags),
|
||||
DEFINE_FLAG(PPF, CANSUPERMORPH, APlayerPawn, PlayerFlags),
|
||||
DEFINE_FLAG(PPF, CROUCHABLEMORPH, APlayerPawn, PlayerFlags),
|
||||
};
|
||||
|
||||
static FFlagDef PowerSpeedFlagDefs[] =
|
||||
{
|
||||
// PowerSpeed flags
|
||||
DEFINE_FLAG(PSF, NOTRAIL, APowerSpeed, SpeedFlags),
|
||||
};
|
||||
|
||||
static const struct FFlagList { const PClass * const *Type; FFlagDef *Defs; int NumDefs; } FlagLists[] =
|
||||
{
|
||||
{ &RUNTIME_CLASS_CASTLESS(AActor), ActorFlagDefs, countof(ActorFlagDefs) },
|
||||
{ &RUNTIME_CLASS_CASTLESS(AInventory), InventoryFlagDefs, countof(InventoryFlagDefs) },
|
||||
{ &RUNTIME_CLASS_CASTLESS(AWeapon), WeaponFlagDefs, countof(WeaponFlagDefs) },
|
||||
{ &RUNTIME_CLASS_CASTLESS(APlayerPawn), PlayerPawnFlagDefs, countof(PlayerPawnFlagDefs) },
|
||||
{ &RUNTIME_CLASS_CASTLESS(APowerSpeed), PowerSpeedFlagDefs, countof(PowerSpeedFlagDefs) },
|
||||
};
|
||||
#define NUM_FLAG_LISTS (countof(FlagLists))
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Find a flag by name using a binary search
|
||||
//
|
||||
//==========================================================================
|
||||
static FFlagDef *FindFlag (FFlagDef *flags, int numflags, const char *flag)
|
||||
{
|
||||
int min = 0, max = numflags - 1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int mid = (min + max) / 2;
|
||||
int lexval = stricmp (flag, flags[mid].name);
|
||||
if (lexval == 0)
|
||||
{
|
||||
return &flags[mid];
|
||||
}
|
||||
else if (lexval > 0)
|
||||
{
|
||||
min = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
max = mid - 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Finds a flag that may have a qualified name
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FFlagDef *FindFlag (const PClass *type, const char *part1, const char *part2, bool strict)
|
||||
{
|
||||
FFlagDef *def;
|
||||
size_t i;
|
||||
|
||||
if (part2 == NULL)
|
||||
{ // Search all lists
|
||||
int max = strict ? 1 : NUM_FLAG_LISTS;
|
||||
for (i = 0; i < max; ++i)
|
||||
{
|
||||
if (type->IsDescendantOf (*FlagLists[i].Type))
|
||||
{
|
||||
def = FindFlag (FlagLists[i].Defs, FlagLists[i].NumDefs, part1);
|
||||
if (def != NULL)
|
||||
{
|
||||
return def;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Search just the named list
|
||||
for (i = 0; i < NUM_FLAG_LISTS; ++i)
|
||||
{
|
||||
if (stricmp ((*FlagLists[i].Type)->TypeName.GetChars(), part1) == 0)
|
||||
{
|
||||
if (type->IsDescendantOf (*FlagLists[i].Type))
|
||||
{
|
||||
return FindFlag (FlagLists[i].Defs, FlagLists[i].NumDefs, part2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Gets the name of an actor flag
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
const char *GetFlagName(unsigned int flagnum, int flagoffset)
|
||||
{
|
||||
for(size_t i = 0; i < countof(ActorFlagDefs); i++)
|
||||
{
|
||||
if (ActorFlagDefs[i].flagbit == flagnum && ActorFlagDefs[i].structoffset == flagoffset)
|
||||
{
|
||||
return ActorFlagDefs[i].name;
|
||||
}
|
||||
}
|
||||
return "(unknown)"; // return something printable
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Find a property by name using a binary search
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
FPropertyInfo *FindProperty(const char * string)
|
||||
{
|
||||
int min = 0, max = properties.Size()-1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int mid = (min + max) / 2;
|
||||
int lexval = stricmp (string, properties[mid]->name);
|
||||
if (lexval == 0)
|
||||
{
|
||||
return properties[mid];
|
||||
}
|
||||
else if (lexval > 0)
|
||||
{
|
||||
min = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
max = mid - 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Find a function by name using a binary search
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
AFuncDesc *FindFunction(const char * string)
|
||||
{
|
||||
int min = 0, max = AFTable.Size()-1;
|
||||
|
||||
while (min <= max)
|
||||
{
|
||||
int mid = (min + max) / 2;
|
||||
int lexval = stricmp (string, AFTable[mid].Name);
|
||||
if (lexval == 0)
|
||||
{
|
||||
return &AFTable[mid];
|
||||
}
|
||||
else if (lexval > 0)
|
||||
{
|
||||
min = mid + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
max = mid - 1;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Find an action function in AActor's table
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
PFunction *FindGlobalActionFunction(const char *name)
|
||||
{
|
||||
return dyn_cast<PFunction>(RUNTIME_CLASS(AActor)->Symbols.FindSymbol(name, false));
|
||||
}
|
||||
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Sorting helpers
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
static int flagcmp (const void * a, const void * b)
|
||||
{
|
||||
return stricmp( ((FFlagDef*)a)->name, ((FFlagDef*)b)->name);
|
||||
}
|
||||
|
||||
static int propcmp(const void * a, const void * b)
|
||||
{
|
||||
return stricmp( (*(FPropertyInfo**)a)->name, (*(FPropertyInfo**)b)->name);
|
||||
}
|
||||
|
||||
static int funccmp(const void * a, const void * b)
|
||||
{
|
||||
return stricmp( ((AFuncDesc*)a)->Name, ((AFuncDesc*)b)->Name);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// Initialization
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void InitThingdef()
|
||||
{
|
||||
// Sort the flag lists
|
||||
for (size_t i = 0; i < NUM_FLAG_LISTS; ++i)
|
||||
{
|
||||
qsort (FlagLists[i].Defs, FlagLists[i].NumDefs, sizeof(FFlagDef), flagcmp);
|
||||
}
|
||||
|
||||
// Create a sorted list of properties
|
||||
if (properties.Size() == 0)
|
||||
{
|
||||
FAutoSegIterator probe(GRegHead, GRegTail);
|
||||
|
||||
while (*++probe != NULL)
|
||||
{
|
||||
properties.Push((FPropertyInfo *)*probe);
|
||||
}
|
||||
properties.ShrinkToFit();
|
||||
qsort(&properties[0], properties.Size(), sizeof(properties[0]), propcmp);
|
||||
}
|
||||
|
||||
// Create a sorted list of native action functions
|
||||
AFTable.Clear();
|
||||
if (AFTable.Size() == 0)
|
||||
{
|
||||
FAutoSegIterator probe(ARegHead, ARegTail);
|
||||
|
||||
while (*++probe != NULL)
|
||||
{
|
||||
AFuncDesc *afunc = (AFuncDesc *)*probe;
|
||||
assert(afunc->VMPointer != NULL);
|
||||
*(afunc->VMPointer) = new VMNativeFunction(afunc->Function, afunc->Name);
|
||||
AFTable.Push(*afunc);
|
||||
}
|
||||
AFTable.ShrinkToFit();
|
||||
qsort(&AFTable[0], AFTable.Size(), sizeof(AFTable[0]), funccmp);
|
||||
}
|
||||
|
||||
// Define some member variables we feel like exposing to the user
|
||||
PSymbolTable &symt = RUNTIME_CLASS(AActor)->Symbols;
|
||||
PType *array5 = NewArray(TypeSInt32, 5);
|
||||
symt.AddSymbol(new PField(NAME_Alpha, TypeFloat64, VARF_Native, myoffsetof(AActor, Alpha)));
|
||||
symt.AddSymbol(new PField(NAME_Angle, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Yaw)));
|
||||
symt.AddSymbol(new PField(NAME_Args, array5, VARF_Native, myoffsetof(AActor, args)));
|
||||
symt.AddSymbol(new PField(NAME_CeilingZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, ceilingz)));
|
||||
symt.AddSymbol(new PField(NAME_FloorZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, floorz)));
|
||||
symt.AddSymbol(new PField(NAME_Health, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, health)));
|
||||
symt.AddSymbol(new PField(NAME_Mass, TypeSInt32, VARF_Native, myoffsetof(AActor, Mass)));
|
||||
symt.AddSymbol(new PField(NAME_Pitch, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Pitch)));
|
||||
symt.AddSymbol(new PField(NAME_Roll, TypeFloat64, VARF_Native, myoffsetof(AActor, Angles.Roll)));
|
||||
symt.AddSymbol(new PField(NAME_Special, TypeSInt32, VARF_Native, myoffsetof(AActor, special)));
|
||||
symt.AddSymbol(new PField(NAME_TID, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, tid)));
|
||||
symt.AddSymbol(new PField(NAME_TIDtoHate, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, TIDtoHate)));
|
||||
symt.AddSymbol(new PField(NAME_WaterLevel, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, waterlevel)));
|
||||
symt.AddSymbol(new PField(NAME_X, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.X))); // must remain read-only!
|
||||
symt.AddSymbol(new PField(NAME_Y, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.Y))); // must remain read-only!
|
||||
symt.AddSymbol(new PField(NAME_Z, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, __Pos.Z))); // must remain read-only!
|
||||
symt.AddSymbol(new PField(NAME_VelX, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.X)));
|
||||
symt.AddSymbol(new PField(NAME_VelY, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Y)));
|
||||
symt.AddSymbol(new PField(NAME_VelZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Z)));
|
||||
symt.AddSymbol(new PField(NAME_MomX, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.X)));
|
||||
symt.AddSymbol(new PField(NAME_MomY, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Y)));
|
||||
symt.AddSymbol(new PField(NAME_MomZ, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Vel.Z)));
|
||||
symt.AddSymbol(new PField(NAME_ScaleX, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.X)));
|
||||
symt.AddSymbol(new PField(NAME_ScaleY, TypeFloat64, VARF_Native, myoffsetof(AActor, Scale.Y)));
|
||||
symt.AddSymbol(new PField(NAME_Score, TypeSInt32, VARF_Native, myoffsetof(AActor, Score)));
|
||||
symt.AddSymbol(new PField(NAME_Accuracy, TypeSInt32, VARF_Native, myoffsetof(AActor, accuracy)));
|
||||
symt.AddSymbol(new PField(NAME_Stamina, TypeSInt32, VARF_Native, myoffsetof(AActor, stamina)));
|
||||
symt.AddSymbol(new PField(NAME_Height, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, Height)));
|
||||
symt.AddSymbol(new PField(NAME_Radius, TypeFloat64, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, radius)));
|
||||
symt.AddSymbol(new PField(NAME_ReactionTime, TypeSInt32, VARF_Native, myoffsetof(AActor, reactiontime)));
|
||||
symt.AddSymbol(new PField(NAME_MeleeRange, TypeFloat64, VARF_Native, myoffsetof(AActor, meleerange)));
|
||||
symt.AddSymbol(new PField(NAME_Speed, TypeFloat64, VARF_Native, myoffsetof(AActor, Speed)));
|
||||
symt.AddSymbol(new PField(NAME_Threshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, threshold)));
|
||||
symt.AddSymbol(new PField(NAME_DefThreshold, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DefThreshold)));
|
||||
symt.AddSymbol(new PField(NAME_Damage, TypeSInt32, VARF_Native|VARF_ReadOnly, myoffsetof(AActor, DamageVal)));
|
||||
symt.AddSymbol(new PField(NAME_VisibleStartAngle, TypeFloat64, VARF_Native, myoffsetof(AActor, VisibleStartAngle)));
|
||||
symt.AddSymbol(new PField(NAME_VisibleStartPitch, TypeFloat64, VARF_Native, myoffsetof(AActor, VisibleStartPitch)));
|
||||
symt.AddSymbol(new PField(NAME_VisibleEndAngle, TypeFloat64, VARF_Native, myoffsetof(AActor, VisibleEndAngle)));
|
||||
symt.AddSymbol(new PField(NAME_VisibleEndPitch, TypeFloat64, VARF_Native, myoffsetof(AActor, VisibleEndPitch)));
|
||||
}
|
3039
src/scripting/thingdef_properties.cpp
Normal file
3039
src/scripting/thingdef_properties.cpp
Normal file
File diff suppressed because it is too large
Load diff
1023
src/scripting/vm/vm.h
Normal file
1023
src/scripting/vm/vm.h
Normal file
File diff suppressed because it is too large
Load diff
636
src/scripting/vm/vmbuilder.cpp
Normal file
636
src/scripting/vm/vmbuilder.cpp
Normal file
|
@ -0,0 +1,636 @@
|
|||
/*
|
||||
** vmbuilder.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "vmbuilder.h"
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder - Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
VMFunctionBuilder::VMFunctionBuilder(bool selfcheck)
|
||||
{
|
||||
NumIntConstants = 0;
|
||||
NumFloatConstants = 0;
|
||||
NumAddressConstants = 0;
|
||||
NumStringConstants = 0;
|
||||
MaxParam = 0;
|
||||
ActiveParam = 0;
|
||||
IsActionFunc = selfcheck;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder - Destructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
VMFunctionBuilder::~VMFunctionBuilder()
|
||||
{
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: MakeFunction
|
||||
//
|
||||
// Creates a new VMScriptFunction out of the data passed to this class.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
VMScriptFunction *VMFunctionBuilder::MakeFunction()
|
||||
{
|
||||
VMScriptFunction *func = new VMScriptFunction;
|
||||
|
||||
func->Alloc(Code.Size(), NumIntConstants, NumFloatConstants, NumStringConstants, NumAddressConstants);
|
||||
|
||||
// Copy code block.
|
||||
memcpy(func->Code, &Code[0], Code.Size() * sizeof(VMOP));
|
||||
|
||||
// Create constant tables.
|
||||
if (NumIntConstants > 0)
|
||||
{
|
||||
FillIntConstants(func->KonstD);
|
||||
}
|
||||
if (NumFloatConstants > 0)
|
||||
{
|
||||
FillFloatConstants(func->KonstF);
|
||||
}
|
||||
if (NumAddressConstants > 0)
|
||||
{
|
||||
FillAddressConstants(func->KonstA, func->KonstATags());
|
||||
}
|
||||
if (NumStringConstants > 0)
|
||||
{
|
||||
FillStringConstants(func->KonstS);
|
||||
}
|
||||
|
||||
// Assign required register space.
|
||||
func->NumRegD = Registers[REGT_INT].MostUsed;
|
||||
func->NumRegF = Registers[REGT_FLOAT].MostUsed;
|
||||
func->NumRegA = Registers[REGT_POINTER].MostUsed;
|
||||
func->NumRegS = Registers[REGT_STRING].MostUsed;
|
||||
func->MaxParam = MaxParam;
|
||||
|
||||
// Technically, there's no reason why we can't end the function with
|
||||
// entries on the parameter stack, but it means the caller probably
|
||||
// did something wrong.
|
||||
assert(ActiveParam == 0);
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: FillIntConstants
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::FillIntConstants(int *konst)
|
||||
{
|
||||
TMapIterator<int, int> it(IntConstants);
|
||||
TMap<int, int>::Pair *pair;
|
||||
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
konst[pair->Value] = pair->Key;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: FillFloatConstants
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::FillFloatConstants(double *konst)
|
||||
{
|
||||
TMapIterator<double, int> it(FloatConstants);
|
||||
TMap<double, int>::Pair *pair;
|
||||
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
konst[pair->Value] = pair->Key;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: FillAddressConstants
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::FillAddressConstants(FVoidObj *konst, VM_ATAG *tags)
|
||||
{
|
||||
TMapIterator<void *, AddrKonst> it(AddressConstants);
|
||||
TMap<void *, AddrKonst>::Pair *pair;
|
||||
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
konst[pair->Value.KonstNum].v = pair->Key;
|
||||
tags[pair->Value.KonstNum] = pair->Value.Tag;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: FillStringConstants
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::FillStringConstants(FString *konst)
|
||||
{
|
||||
TMapIterator<FString, int> it(StringConstants);
|
||||
TMap<FString, int>::Pair *pair;
|
||||
|
||||
while (it.NextPair(pair))
|
||||
{
|
||||
konst[pair->Value] = pair->Key;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantInt
|
||||
//
|
||||
// Returns a constant register initialized with the given value, or -1 if
|
||||
// there were no more constants free.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int VMFunctionBuilder::GetConstantInt(int val)
|
||||
{
|
||||
int *locp = IntConstants.CheckKey(val);
|
||||
if (locp != NULL)
|
||||
{
|
||||
return *locp;
|
||||
}
|
||||
else
|
||||
{
|
||||
int loc = NumIntConstants++;
|
||||
IntConstants.Insert(val, loc);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantFloat
|
||||
//
|
||||
// Returns a constant register initialized with the given value, or -1 if
|
||||
// there were no more constants free.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int VMFunctionBuilder::GetConstantFloat(double val)
|
||||
{
|
||||
int *locp = FloatConstants.CheckKey(val);
|
||||
if (locp != NULL)
|
||||
{
|
||||
return *locp;
|
||||
}
|
||||
else
|
||||
{
|
||||
int loc = NumFloatConstants++;
|
||||
FloatConstants.Insert(val, loc);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantString
|
||||
//
|
||||
// Returns a constant register initialized with the given value, or -1 if
|
||||
// there were no more constants free.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int VMFunctionBuilder::GetConstantString(FString val)
|
||||
{
|
||||
int *locp = StringConstants.CheckKey(val);
|
||||
if (locp != NULL)
|
||||
{
|
||||
return *locp;
|
||||
}
|
||||
else
|
||||
{
|
||||
int loc = NumStringConstants++;
|
||||
StringConstants.Insert(val, loc);
|
||||
return loc;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetConstantAddress
|
||||
//
|
||||
// Returns a constant register initialized with the given value, or -1 if
|
||||
// there were no more constants free.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int VMFunctionBuilder::GetConstantAddress(void *ptr, VM_ATAG tag)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{ // Make all NULL pointers generic. (Or should we allow typed NULLs?)
|
||||
tag = ATAG_GENERIC;
|
||||
}
|
||||
AddrKonst *locp = AddressConstants.CheckKey(ptr);
|
||||
if (locp != NULL)
|
||||
{
|
||||
// There should only be one tag associated with a memory location.
|
||||
assert(locp->Tag == tag);
|
||||
return locp->KonstNum;
|
||||
}
|
||||
else
|
||||
{
|
||||
AddrKonst loc = { NumAddressConstants++, tag };
|
||||
AddressConstants.Insert(ptr, loc);
|
||||
return loc.KonstNum;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: ParamChange
|
||||
//
|
||||
// Adds delta to ActiveParam and keeps track of MaxParam.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::ParamChange(int delta)
|
||||
{
|
||||
assert(delta > 0 || -delta <= ActiveParam);
|
||||
ActiveParam += delta;
|
||||
if (ActiveParam > MaxParam)
|
||||
{
|
||||
MaxParam = ActiveParam;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailability - Constructor
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
VMFunctionBuilder::RegAvailability::RegAvailability()
|
||||
{
|
||||
memset(Used, 0, sizeof(Used));
|
||||
MostUsed = 0;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailability :: Get
|
||||
//
|
||||
// Gets one or more unused registers. If getting multiple registers, they
|
||||
// will all be consecutive. Returns -1 if there were not enough consecutive
|
||||
// registers to satisfy the request.
|
||||
//
|
||||
// Preference is given to low-numbered registers in an attempt to keep
|
||||
// the maximum register count low so as to preserve VM stack space when this
|
||||
// function is executed.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
int VMFunctionBuilder::RegAvailability::Get(int count)
|
||||
{
|
||||
VM_UWORD mask;
|
||||
int i, firstbit;
|
||||
|
||||
// Getting fewer than one register makes no sense, and
|
||||
// the algorithm used here can only obtain ranges of up to 32 bits.
|
||||
if (count < 1 || count > 32)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
mask = count == 32 ? ~0u : (1 << count) - 1;
|
||||
|
||||
for (i = 0; i < 256/32; ++i)
|
||||
{
|
||||
// Find the first word with free registers
|
||||
VM_UWORD bits = Used[i];
|
||||
if (bits != ~0u)
|
||||
{
|
||||
// Are there enough consecutive bits to satisfy the request?
|
||||
// Search by 16, then 8, then 1 bit at a time for the first
|
||||
// free register.
|
||||
if ((bits & 0xFFFF) == 0xFFFF)
|
||||
{
|
||||
firstbit = ((bits & 0xFF0000) == 0xFF0000) ? 24 : 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
firstbit = ((bits & 0xFF) == 0xFF) ? 8 : 0;
|
||||
}
|
||||
for (; firstbit < 32; ++firstbit)
|
||||
{
|
||||
if (((bits >> firstbit) & mask) == 0)
|
||||
{
|
||||
if (firstbit + count <= 32)
|
||||
{ // Needed bits all fit in one word, so we got it.
|
||||
if (firstbit + count > MostUsed)
|
||||
{
|
||||
MostUsed = firstbit + count;
|
||||
}
|
||||
Used[i] |= mask << firstbit;
|
||||
return i * 32 + firstbit;
|
||||
}
|
||||
// Needed bits span two words, so check the next word.
|
||||
else if (i < 256/32 - 1)
|
||||
{ // There is a next word.
|
||||
if (((Used[i + 1]) & (mask >> (32 - firstbit))) == 0)
|
||||
{ // The next word has the needed open space, too.
|
||||
if (firstbit + count > MostUsed)
|
||||
{
|
||||
MostUsed = firstbit + count;
|
||||
}
|
||||
Used[i] |= mask << firstbit;
|
||||
Used[i + 1] |= mask >> (32 - firstbit);
|
||||
return i * 32 + firstbit;
|
||||
}
|
||||
else
|
||||
{ // Skip to the next word, because we know we won't find
|
||||
// what we need if we stay inside this one. All bits
|
||||
// from firstbit to the end of the word are 0. If the
|
||||
// next word does not start with the x amount of 0's, we
|
||||
// need to satisfy the request, then it certainly won't
|
||||
// have the x+1 0's we would need if we started at
|
||||
// firstbit+1 in this one.
|
||||
firstbit = 32;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // Out of words.
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// No room!
|
||||
return -1;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailibity :: Return
|
||||
//
|
||||
// Marks a range of registers as free again.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::RegAvailability::Return(int reg, int count)
|
||||
{
|
||||
assert(count >= 1 && count <= 32);
|
||||
assert(reg >= 0 && reg + count <= 256);
|
||||
|
||||
VM_UWORD mask, partialmask;
|
||||
int firstword, firstbit;
|
||||
|
||||
mask = count == 32 ? ~0u : (1 << count) - 1;
|
||||
firstword = reg / 32;
|
||||
firstbit = reg & 31;
|
||||
|
||||
if (firstbit + count <= 32)
|
||||
{ // Range is all in one word.
|
||||
mask <<= firstbit;
|
||||
// If we are trying to return registers that are already free,
|
||||
// it probably means that the caller messed up somewhere.
|
||||
assert((Used[firstword] & mask) == mask);
|
||||
Used[firstword] &= ~mask;
|
||||
}
|
||||
else
|
||||
{ // Range is in two words.
|
||||
partialmask = mask << firstbit;
|
||||
assert((Used[firstword] & partialmask) == partialmask);
|
||||
Used[firstword] &= ~partialmask;
|
||||
|
||||
partialmask = mask >> (32 - firstbit);
|
||||
assert((Used[firstword + 1] & partialmask) == partialmask);
|
||||
Used[firstword + 1] &= ~partialmask;
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: RegAvailability :: Reuse
|
||||
//
|
||||
// Marks an unused register as in-use. Returns false if the register is
|
||||
// already in use or true if it was successfully reused.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
bool VMFunctionBuilder::RegAvailability::Reuse(int reg)
|
||||
{
|
||||
assert(reg >= 0 && reg <= 255);
|
||||
assert(reg < MostUsed && "Attempt to reuse a register that was never used");
|
||||
|
||||
VM_UWORD mask = 1 << (reg & 31);
|
||||
int word = reg / 32;
|
||||
|
||||
if (Used[word] & mask)
|
||||
{ // It's already in use!
|
||||
return false;
|
||||
}
|
||||
Used[word] |= mask;
|
||||
return true;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: GetAddress
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::GetAddress()
|
||||
{
|
||||
return Code.Size();
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: Emit
|
||||
//
|
||||
// Just dumbly output an instruction. Returns instruction position, not
|
||||
// byte position. (Because all instructions are exactly four bytes long.)
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opa, int opb, int opc)
|
||||
{
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opa >= 0 && opa <= 255);
|
||||
assert(opb >= 0 && opb <= 255);
|
||||
assert(opc >= 0 && opc <= 255);
|
||||
if (opcode == OP_PARAM)
|
||||
{
|
||||
ParamChange(1);
|
||||
}
|
||||
else if (opcode == OP_CALL || opcode == OP_CALL_K || opcode == OP_TAIL || opcode == OP_TAIL_K)
|
||||
{
|
||||
ParamChange(-opb);
|
||||
}
|
||||
VMOP op;
|
||||
op.op = opcode;
|
||||
op.a = opa;
|
||||
op.b = opb;
|
||||
op.c = opc;
|
||||
return Code.Push(op);
|
||||
}
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opa, VM_SHALF opbc)
|
||||
{
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opa >= 0 && opa <= 255);
|
||||
//assert(opbc >= -32768 && opbc <= 32767); always true due to parameter's width
|
||||
VMOP op;
|
||||
op.op = opcode;
|
||||
op.a = opa;
|
||||
op.i16 = opbc;
|
||||
return Code.Push(op);
|
||||
}
|
||||
|
||||
size_t VMFunctionBuilder::Emit(int opcode, int opabc)
|
||||
{
|
||||
assert(opcode >= 0 && opcode < NUM_OPS);
|
||||
assert(opabc >= -(1 << 23) && opabc <= (1 << 24) - 1);
|
||||
if (opcode == OP_PARAMI)
|
||||
{
|
||||
ParamChange(1);
|
||||
}
|
||||
VMOP op;
|
||||
op.op = opcode;
|
||||
op.i24 = opabc;
|
||||
return Code.Push(op);
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: EmitParamInt
|
||||
//
|
||||
// Passes a constant integer parameter, using either PARAMI and an immediate
|
||||
// value or PARAM and a constant register, as appropriate.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::EmitParamInt(int value)
|
||||
{
|
||||
// Immediates for PARAMI must fit in 24 bits.
|
||||
if (((value << 8) >> 8) == value)
|
||||
{
|
||||
return Emit(OP_PARAMI, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Emit(OP_PARAM, 0, REGT_INT | REGT_KONST, GetConstantInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: EmitLoadInt
|
||||
//
|
||||
// Loads an integer constant into a register, using either an immediate
|
||||
// value or a constant register, as appropriate.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::EmitLoadInt(int regnum, int value)
|
||||
{
|
||||
assert(regnum >= 0 && regnum < Registers[REGT_INT].MostUsed);
|
||||
if (value >= -32768 && value <= 32767)
|
||||
{
|
||||
return Emit(OP_LI, regnum, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Emit(OP_LK, regnum, GetConstantInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: EmitRetInt
|
||||
//
|
||||
// Returns an integer, using either an immediate value or a constant
|
||||
// register, as appropriate.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
size_t VMFunctionBuilder::EmitRetInt(int retnum, bool final, int value)
|
||||
{
|
||||
assert(retnum >= 0 && retnum <= 127);
|
||||
if (value >= -32768 && value <= 32767)
|
||||
{
|
||||
return Emit(OP_RETI, retnum | (final << 7), value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Emit(OP_RET, retnum | (final << 7), REGT_INT | REGT_KONST, GetConstantInt(value));
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: Backpatch
|
||||
//
|
||||
// Store a JMP instruction at <loc> that points at <target>.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::Backpatch(size_t loc, size_t target)
|
||||
{
|
||||
assert(loc < Code.Size());
|
||||
int offset = int(target - loc - 1);
|
||||
assert(((offset << 8) >> 8) == offset);
|
||||
Code[loc].op = OP_JMP;
|
||||
Code[loc].i24 = offset;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// VMFunctionBuilder :: BackpatchToHere
|
||||
//
|
||||
// Store a JMP instruction at <loc> that points to the current code gen
|
||||
// location.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void VMFunctionBuilder::BackpatchToHere(size_t loc)
|
||||
{
|
||||
Backpatch(loc, Code.Size());
|
||||
}
|
90
src/scripting/vm/vmbuilder.h
Normal file
90
src/scripting/vm/vmbuilder.h
Normal file
|
@ -0,0 +1,90 @@
|
|||
#ifndef VMUTIL_H
|
||||
#define VMUTIL_H
|
||||
|
||||
#include "vm.h"
|
||||
|
||||
class VMFunctionBuilder
|
||||
{
|
||||
public:
|
||||
// Keeps track of which registers are available by way of a bitmask table.
|
||||
class RegAvailability
|
||||
{
|
||||
public:
|
||||
RegAvailability();
|
||||
int GetMostUsed() { return MostUsed; }
|
||||
int Get(int count); // Returns the first register in the range
|
||||
void Return(int reg, int count);
|
||||
bool Reuse(int regnum);
|
||||
|
||||
private:
|
||||
VM_UWORD Used[256/32]; // Bitmap of used registers (bit set means reg is used)
|
||||
int MostUsed;
|
||||
|
||||
friend class VMFunctionBuilder;
|
||||
};
|
||||
|
||||
VMFunctionBuilder(bool checkself = false);
|
||||
~VMFunctionBuilder();
|
||||
|
||||
VMScriptFunction *MakeFunction();
|
||||
|
||||
// Returns the constant register holding the value.
|
||||
int GetConstantInt(int val);
|
||||
int GetConstantFloat(double val);
|
||||
int GetConstantAddress(void *ptr, VM_ATAG tag);
|
||||
int GetConstantString(FString str);
|
||||
|
||||
// Returns the address of the next instruction to be emitted.
|
||||
size_t GetAddress();
|
||||
|
||||
// Returns the address of the newly-emitted instruction.
|
||||
size_t Emit(int opcode, int opa, int opb, int opc);
|
||||
size_t Emit(int opcode, int opa, VM_SHALF opbc);
|
||||
size_t Emit(int opcode, int opabc);
|
||||
size_t EmitParamInt(int value);
|
||||
size_t EmitLoadInt(int regnum, int value);
|
||||
size_t EmitRetInt(int retnum, bool final, int value);
|
||||
|
||||
void Backpatch(size_t addr, size_t target);
|
||||
void BackpatchToHere(size_t addr);
|
||||
|
||||
// Write out complete constant tables.
|
||||
void FillIntConstants(int *konst);
|
||||
void FillFloatConstants(double *konst);
|
||||
void FillAddressConstants(FVoidObj *konst, VM_ATAG *tags);
|
||||
void FillStringConstants(FString *strings);
|
||||
|
||||
// PARAM increases ActiveParam; CALL decreases it.
|
||||
void ParamChange(int delta);
|
||||
|
||||
// Track available registers.
|
||||
RegAvailability Registers[4];
|
||||
|
||||
// For use by DECORATE's self/stateowner sanitizer.
|
||||
bool IsActionFunc;
|
||||
|
||||
private:
|
||||
struct AddrKonst
|
||||
{
|
||||
int KonstNum;
|
||||
VM_ATAG Tag;
|
||||
};
|
||||
// These map from the constant value to its position in the constant table.
|
||||
TMap<int, int> IntConstants;
|
||||
TMap<double, int> FloatConstants;
|
||||
TMap<void *, AddrKonst> AddressConstants;
|
||||
TMap<FString, int> StringConstants;
|
||||
|
||||
int NumIntConstants;
|
||||
int NumFloatConstants;
|
||||
int NumAddressConstants;
|
||||
int NumStringConstants;
|
||||
|
||||
int MaxParam;
|
||||
int ActiveParam;
|
||||
|
||||
TArray<VMOP> Code;
|
||||
|
||||
};
|
||||
|
||||
#endif
|
600
src/scripting/vm/vmdisasm.cpp
Normal file
600
src/scripting/vm/vmdisasm.cpp
Normal file
|
@ -0,0 +1,600 @@
|
|||
/*
|
||||
** vmdisasm.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "vm.h"
|
||||
#include "c_console.h"
|
||||
|
||||
#define NOP MODE_AUNUSED | MODE_BUNUSED | MODE_CUNUSED
|
||||
|
||||
#define LI MODE_AI | MODE_BCJOINT | MODE_BCIMMS
|
||||
#define LKI MODE_AI | MODE_BCJOINT | MODE_BCKI
|
||||
#define LKF MODE_AF | MODE_BCJOINT | MODE_BCKF
|
||||
#define LKS MODE_AS | MODE_BCJOINT | MODE_BCKS
|
||||
#define LKP MODE_AP | MODE_BCJOINT | MODE_BCKP
|
||||
#define LFP MODE_AP | MODE_BUNUSED | MODE_CUNUSED
|
||||
|
||||
#define RIRPKI MODE_AI | MODE_BP | MODE_CKI
|
||||
#define RIRPRI MODE_AI | MODE_BP | MODE_CI
|
||||
#define RFRPKI MODE_AF | MODE_BP | MODE_CKI
|
||||
#define RFRPRI MODE_AF | MODE_BP | MODE_CI
|
||||
#define RSRPKI MODE_AS | MODE_BP | MODE_CKI
|
||||
#define RSRPRI MODE_AS | MODE_BP | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RVRPKI MODE_AV | MODE_BP | MODE_CKI
|
||||
#define RVRPRI MODE_AV | MODE_BP | MODE_CI
|
||||
#define RIRPI8 MODE_AI | MODE_BP | MODE_CIMMZ
|
||||
|
||||
#define RPRIKI MODE_AP | MODE_BI | MODE_CKI
|
||||
#define RPRIRI MODE_AP | MODE_BI | MODE_CI
|
||||
#define RPRFKI MODE_AP | MODE_BF | MODE_CKI
|
||||
#define RPRFRI MODE_AP | MODE_BF | MODE_CI
|
||||
#define RPRSKI MODE_AP | MODE_BS | MODE_CKI
|
||||
#define RPRSRI MODE_AP | MODE_BS | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RPRVKI MODE_AP | MODE_BV | MODE_CKI
|
||||
#define RPRVRI MODE_AP | MODE_BV | MODE_CI
|
||||
#define RPRII8 MODE_AP | MODE_BI | MODE_CIMMZ
|
||||
|
||||
#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED
|
||||
#define RFRF MODE_AF | MODE_BF | MODE_CUNUSED
|
||||
#define RSRS MODE_AS | MODE_BS | MODE_CUNUSED
|
||||
#define RPRP MODE_AP | MODE_BP | MODE_CUNUSED
|
||||
#define RXRXI8 MODE_AX | MODE_BX | MODE_CIMMZ
|
||||
#define RPRPRP MODE_AP | MODE_BP | MODE_CP
|
||||
#define RPRPKP MODE_AP | MODE_BP | MODE_CKP
|
||||
|
||||
#define RII16 MODE_AI | MODE_BCJOINT | MODE_BCIMMS
|
||||
#define I24 MODE_ABCJOINT
|
||||
#define I8 MODE_AIMMZ | MODE_BUNUSED | MODE_CUNUSED
|
||||
#define I8I16 MODE_AIMMZ | MODE_BCIMMZ
|
||||
#define __BCP MODE_AUNUSED | MODE_BCJOINT | MODE_BCPARAM
|
||||
#define RPI8 MODE_AP | MODE_BIMMZ | MODE_CUNUSED
|
||||
#define KPI8 MODE_AKP | MODE_BIMMZ | MODE_CUNUSED
|
||||
#define RPI8I8 MODE_AP | MODE_BIMMZ | MODE_CIMMZ
|
||||
#define KPI8I8 MODE_AKP | MODE_BIMMZ | MODE_CIMMZ
|
||||
#define I8BCP MODE_AIMMZ | MODE_BCJOINT | MODE_BCPARAM
|
||||
#define THROW MODE_AIMMZ | MODE_BCTHROW
|
||||
#define CATCH MODE_AIMMZ | MODE_BCCATCH
|
||||
#define CAST MODE_AX | MODE_BX | MODE_CIMMZ | MODE_BCCAST
|
||||
|
||||
#define RSRSRS MODE_AS | MODE_BS | MODE_CS
|
||||
#define RIRS MODE_AI | MODE_BS | MODE_CUNUSED
|
||||
#define I8RXRX MODE_AIMMZ | MODE_BX | MODE_CX
|
||||
|
||||
#define RIRIRI MODE_AI | MODE_BI | MODE_CI
|
||||
#define RIRII8 MODE_AI | MODE_BI | MODE_CIMMZ
|
||||
#define RIRIKI MODE_AI | MODE_BI | MODE_CKI
|
||||
#define RIKIRI MODE_AI | MODE_BKI | MODE_CI
|
||||
#define RIKII8 MODE_AI | MODE_BKI | MODE_CIMMZ
|
||||
#define RIRIIs MODE_AI | MODE_BI | MODE_CIMMS
|
||||
#define RIRI MODE_AI | MODE_BI | MODE_CUNUSED
|
||||
#define I8RIRI MODE_AIMMZ | MODE_BI | MODE_CI
|
||||
#define I8RIKI MODE_AIMMZ | MODE_BI | MODE_CKI
|
||||
#define I8KIRI MODE_AIMMZ | MODE_BKI | MODE_CI
|
||||
|
||||
#define RFRFRF MODE_AF | MODE_BF | MODE_CF
|
||||
#define RFRFKF MODE_AF | MODE_BF | MODE_CKF
|
||||
#define RFKFRF MODE_AF | MODE_BKF | MODE_CF
|
||||
#define I8RFRF MODE_AIMMZ | MODE_BF | MODE_CF
|
||||
#define I8RFKF MODE_AIMMZ | MODE_BF | MODE_CKF
|
||||
#define I8KFRF MODE_AIMMZ | MODE_BKF | MODE_CF
|
||||
#define RFRFI8 MODE_AF | MODE_BF | MODE_CIMMZ
|
||||
|
||||
#define RVRV MODE_AV | MODE_BV | MODE_CUNUSED
|
||||
#define RVRVRV MODE_AV | MODE_BV | MODE_CV
|
||||
#define RVRVKV MODE_AV | MODE_BV | MODE_CKV
|
||||
#define RVKVRV MODE_AV | MODE_BKV | MODE_CV
|
||||
#define RFRV MODE_AF | MODE_BV | MODE_CUNUSED
|
||||
#define I8RVRV MODE_AIMMZ | MODE_BV | MODE_CV
|
||||
#define I8RVKV MODE_AIMMZ | MODE_BV | MODE_CKV
|
||||
|
||||
#define RPRPRI MODE_AP | MODE_BP | MODE_CI
|
||||
#define RPRPKI MODE_AP | MODE_BP | MODE_CKI
|
||||
#define RIRPRP MODE_AI | MODE_BP | MODE_CP
|
||||
#define I8RPRP MODE_AIMMZ | MODE_BP | MODE_CP
|
||||
#define I8RPKP MODE_AIMMZ | MODE_BP | MODE_CKP
|
||||
|
||||
#define CIRR MODE_ACMP | MODE_BI | MODE_CI
|
||||
#define CIRK MODE_ACMP | MODE_BI | MODE_CKI
|
||||
#define CIKR MODE_ACMP | MODE_BKI | MODE_CI
|
||||
#define CFRR MODE_ACMP | MODE_BF | MODE_CF
|
||||
#define CFRK MODE_ACMP | MODE_BF | MODE_CKF
|
||||
#define CFKR MODE_ACMP | MODE_BKF | MODE_CF
|
||||
#define CVRR MODE_ACMP | MODE_BV | MODE_CV
|
||||
#define CVRK MODE_ACMP | MODE_BV | MODE_CKV
|
||||
#define CPRR MODE_ACMP | MODE_BP | MODE_CP
|
||||
#define CPRK MODE_ACMP | MODE_BP | MODE_CKP
|
||||
|
||||
const VMOpInfo OpInfo[NUM_OPS] =
|
||||
{
|
||||
#define xx(op, name, mode) { #name, mode }
|
||||
#include "vmops.h"
|
||||
};
|
||||
|
||||
static const char *const FlopNames[] =
|
||||
{
|
||||
"abs",
|
||||
"neg",
|
||||
"exp",
|
||||
"log",
|
||||
"log10",
|
||||
"sqrt",
|
||||
"ceil",
|
||||
"floor",
|
||||
|
||||
"acos rad",
|
||||
"asin rad",
|
||||
"atan rad",
|
||||
"cos rad",
|
||||
"sin rad",
|
||||
"tan rad",
|
||||
|
||||
"acos deg",
|
||||
"asin deg",
|
||||
"atan deg",
|
||||
"cos deg",
|
||||
"sin deg",
|
||||
"tan deg",
|
||||
|
||||
"cosh",
|
||||
"sinh",
|
||||
"tanh",
|
||||
};
|
||||
|
||||
static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const VMScriptFunction *func);
|
||||
|
||||
static int printf_wrapper(FILE *f, const char *fmt, ...)
|
||||
{
|
||||
va_list argptr;
|
||||
int count;
|
||||
|
||||
va_start(argptr, fmt);
|
||||
if (f == NULL)
|
||||
{
|
||||
count = VPrintf(PRINT_HIGH, fmt, argptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
count = vfprintf(f, fmt, argptr);
|
||||
}
|
||||
va_end(argptr);
|
||||
return count;
|
||||
}
|
||||
|
||||
void VMDumpConstants(FILE *out, const VMScriptFunction *func)
|
||||
{
|
||||
char tmp[21];
|
||||
int i, j, k, kk;
|
||||
|
||||
if (func->KonstD != NULL && func->NumKonstD != 0)
|
||||
{
|
||||
printf_wrapper(out, "\nConstant integers:\n");
|
||||
kk = (func->NumKonstD + 3) / 4;
|
||||
for (i = 0; i < kk; ++i)
|
||||
{
|
||||
for (j = 0, k = i; j < 4 && k < func->NumKonstD; j++, k += kk)
|
||||
{
|
||||
mysnprintf(tmp, countof(tmp), "%3d. %d", k, func->KonstD[k]);
|
||||
printf_wrapper(out, "%-20s", tmp);
|
||||
}
|
||||
printf_wrapper(out, "\n");
|
||||
}
|
||||
}
|
||||
if (func->KonstF != NULL && func->NumKonstF != 0)
|
||||
{
|
||||
printf_wrapper(out, "\nConstant floats:\n");
|
||||
kk = (func->NumKonstF + 3) / 4;
|
||||
for (i = 0; i < kk; ++i)
|
||||
{
|
||||
for (j = 0, k = i; j < 4 && k < func->NumKonstF; j++, k += kk)
|
||||
{
|
||||
mysnprintf(tmp, countof(tmp), "%3d. %.16f", k, func->KonstF[k]);
|
||||
printf_wrapper(out, "%-20s", tmp);
|
||||
}
|
||||
printf_wrapper(out, "\n");
|
||||
}
|
||||
}
|
||||
if (func->KonstA != NULL && func->NumKonstA != 0)
|
||||
{
|
||||
printf_wrapper(out, "\nConstant addresses:\n");
|
||||
kk = (func->NumKonstA + 3) / 4;
|
||||
for (i = 0; i < kk; ++i)
|
||||
{
|
||||
for (j = 0, k = i; j < 4 && k < func->NumKonstA; j++, k += kk)
|
||||
{
|
||||
mysnprintf(tmp, countof(tmp), "%3d. %p:%d", k, func->KonstA[k].v, func->KonstATags()[k]);
|
||||
printf_wrapper(out, "%-20s", tmp);
|
||||
}
|
||||
printf_wrapper(out, "\n");
|
||||
}
|
||||
}
|
||||
if (func->KonstS != NULL && func->NumKonstS != 0)
|
||||
{
|
||||
printf_wrapper(out, "\nConstant strings:\n");
|
||||
for (i = 0; i < func->NumKonstS; ++i)
|
||||
{
|
||||
printf_wrapper(out, "%3d. %s\n", i, func->KonstS[i].GetChars());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VMDisasm(FILE *out, const VMOP *code, int codesize, const VMScriptFunction *func)
|
||||
{
|
||||
VMFunction *callfunc;
|
||||
const char *callname;
|
||||
const char *name;
|
||||
int col;
|
||||
int mode;
|
||||
int a;
|
||||
bool cmp;
|
||||
char cmpname[8];
|
||||
|
||||
for (int i = 0; i < codesize; ++i)
|
||||
{
|
||||
name = OpInfo[code[i].op].Name;
|
||||
mode = OpInfo[code[i].op].Mode;
|
||||
a = code[i].a;
|
||||
cmp = (mode & MODE_ATYPE) == MODE_ACMP;
|
||||
|
||||
// String comparison encodes everything in a single instruction.
|
||||
if (code[i].op == OP_CMPS)
|
||||
{
|
||||
switch (a & CMP_METHOD_MASK)
|
||||
{
|
||||
case CMP_EQ: name = "beq"; break;
|
||||
case CMP_LT: name = "blt"; break;
|
||||
case CMP_LE: name = "ble"; break;
|
||||
}
|
||||
mode = MODE_AIMMZ;
|
||||
mode |= (a & CMP_BK) ? MODE_BKS : MODE_BS;
|
||||
mode |= (a & CMP_CK) ? MODE_CKS : MODE_CS;
|
||||
a &= CMP_CHECK | CMP_APPROX;
|
||||
cmp = true;
|
||||
}
|
||||
if (cmp)
|
||||
{ // Comparison instruction. Modify name for inverted test.
|
||||
if (!(a & CMP_CHECK))
|
||||
{
|
||||
strcpy(cmpname, name);
|
||||
if (name[1] == 'e')
|
||||
{ // eq -> ne
|
||||
cmpname[1] = 'n', cmpname[2] = 'e';
|
||||
}
|
||||
else if (name[2] == 't')
|
||||
{ // lt -> ge
|
||||
cmpname[1] = 'g', cmpname[2] = 'e';
|
||||
}
|
||||
else
|
||||
{ // le -> gt
|
||||
cmpname[1] = 'g', cmpname[2] = 't';
|
||||
}
|
||||
name = cmpname;
|
||||
}
|
||||
}
|
||||
printf_wrapper(out, "%08x: %02x%02x%02x%02x %-8s", i << 2, code[i].op, code[i].a, code[i].b, code[i].c, name);
|
||||
col = 0;
|
||||
switch (code[i].op)
|
||||
{
|
||||
case OP_JMP:
|
||||
case OP_TRY:
|
||||
col = printf_wrapper(out, "%08x", (i + 1 + code[i].i24) << 2);
|
||||
break;
|
||||
|
||||
case OP_PARAMI:
|
||||
col = printf_wrapper(out, "%d", code[i].i24);
|
||||
break;
|
||||
|
||||
case OP_CALL_K:
|
||||
case OP_TAIL_K:
|
||||
callfunc = (VMFunction *)func->KonstA[code[i].a].o;
|
||||
callname = callfunc->Name != NAME_None ? callfunc->Name : "[anonfunc]";
|
||||
col = printf_wrapper(out, "%.23s,%d", callname, code[i].b);
|
||||
if (code[i].op == OP_CALL_K)
|
||||
{
|
||||
col += printf_wrapper(out, ",%d", code[i].c);
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_RET:
|
||||
if (code[i].b != REGT_NIL)
|
||||
{
|
||||
if (a == RET_FINAL)
|
||||
{
|
||||
col = print_reg(out, 0, code[i].i16u, MODE_PARAM, 16, func);
|
||||
}
|
||||
else
|
||||
{
|
||||
col = print_reg(out, 0, a & ~RET_FINAL, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
|
||||
col += print_reg(out, col, code[i].i16u, MODE_PARAM, 16, func);
|
||||
if (a & RET_FINAL)
|
||||
{
|
||||
col += printf_wrapper(out, " [final]");
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_RETI:
|
||||
if (a == RET_FINAL)
|
||||
{
|
||||
col = printf_wrapper(out, "%d", code[i].i16);
|
||||
}
|
||||
else
|
||||
{
|
||||
col = print_reg(out, 0, a & ~RET_FINAL, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
|
||||
col += print_reg(out, col, code[i].i16, MODE_IMMS, 16, func);
|
||||
if (a & RET_FINAL)
|
||||
{
|
||||
col += printf_wrapper(out, " [final]");
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case OP_FLOP:
|
||||
col = printf_wrapper(out, "f%d,f%d,%d", code[i].a, code[i].b, code[i].c);
|
||||
if (code[i].c < countof(FlopNames))
|
||||
{
|
||||
col += printf_wrapper(out, " [%s]", FlopNames[code[i].c]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
if ((mode & MODE_BCTYPE) == MODE_BCCAST)
|
||||
{
|
||||
switch (code[i].c)
|
||||
{
|
||||
case CAST_I2F:
|
||||
mode = MODE_AF | MODE_BI | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_I2S:
|
||||
mode = MODE_AS | MODE_BI | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_F2I:
|
||||
mode = MODE_AI | MODE_BF | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_F2S:
|
||||
mode = MODE_AS | MODE_BF | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_P2S:
|
||||
mode = MODE_AS | MODE_BP | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_S2I:
|
||||
mode = MODE_AI | MODE_BS | MODE_CUNUSED;
|
||||
break;
|
||||
case CAST_S2F:
|
||||
mode = MODE_AF | MODE_BS | MODE_CUNUSED;
|
||||
break;
|
||||
default:
|
||||
mode = MODE_AX | MODE_BX | MODE_CIMMZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
col = print_reg(out, 0, a, (mode & MODE_ATYPE) >> MODE_ASHIFT, 24, func);
|
||||
if ((mode & MODE_BCTYPE) == MODE_BCTHROW)
|
||||
{
|
||||
if (code[i].a == 0)
|
||||
{
|
||||
mode = (MODE_BP | MODE_CUNUSED);
|
||||
}
|
||||
else if (code[i].a == 1)
|
||||
{
|
||||
mode = (MODE_BKP | MODE_CUNUSED);
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = (MODE_BCJOINT | MODE_BCIMMS);
|
||||
}
|
||||
}
|
||||
else if ((mode & MODE_BCTYPE) == MODE_BCCATCH)
|
||||
{
|
||||
switch (code[i].a)
|
||||
{
|
||||
case 0:
|
||||
mode = MODE_BUNUSED | MODE_CUNUSED;
|
||||
break;
|
||||
case 1:
|
||||
mode = MODE_BUNUSED | MODE_CP;
|
||||
break;
|
||||
case 2:
|
||||
mode = MODE_BP | MODE_CP;
|
||||
break;
|
||||
case 3:
|
||||
mode = MODE_BKP | MODE_CP;
|
||||
break;
|
||||
default:
|
||||
mode = MODE_BIMMZ | MODE_CIMMZ;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ((mode & (MODE_BTYPE | MODE_CTYPE)) == MODE_BCJOINT)
|
||||
{
|
||||
col += print_reg(out, col, code[i].i16u, (mode & MODE_BCTYPE) >> MODE_BCSHIFT, 16, func);
|
||||
}
|
||||
else
|
||||
{
|
||||
col += print_reg(out, col, code[i].b, (mode & MODE_BTYPE) >> MODE_BSHIFT, 24, func);
|
||||
col += print_reg(out, col, code[i].c, (mode & MODE_CTYPE) >> MODE_CSHIFT, 24, func);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (cmp && i + 1 < codesize)
|
||||
{
|
||||
if (code[i+1].op != OP_JMP)
|
||||
{ // comparison instructions must be followed by jump
|
||||
col += printf_wrapper(out, " => *!*!*!*\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
col += printf_wrapper(out, " => %08x", (i + 2 + code[i+1].i24) << 2);
|
||||
}
|
||||
}
|
||||
if (col > 30)
|
||||
{
|
||||
col = 30;
|
||||
}
|
||||
printf_wrapper(out, "%*c", 30 - col, ';');
|
||||
if (!cmp && (code[i].op == OP_JMP || code[i].op == OP_TRY || code[i].op == OP_PARAMI))
|
||||
{
|
||||
printf_wrapper(out, "%d\n", code[i].i24);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf_wrapper(out, "%d,%d,%d", code[i].a, code[i].b, code[i].c);
|
||||
if (cmp && i + 1 < codesize && code[i+1].op == OP_JMP)
|
||||
{
|
||||
printf_wrapper(out, ",%d\n", code[++i].i24);
|
||||
}
|
||||
else if (code[i].op == OP_CALL_K || code[i].op == OP_TAIL_K)
|
||||
{
|
||||
printf_wrapper(out, " [%p]\n", callfunc);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf_wrapper(out, "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int print_reg(FILE *out, int col, int arg, int mode, int immshift, const VMScriptFunction *func)
|
||||
{
|
||||
if (mode == MODE_UNUSED || mode == MODE_CMP)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (col > 0)
|
||||
{
|
||||
col = printf_wrapper(out, ",");
|
||||
}
|
||||
switch(mode)
|
||||
{
|
||||
case MODE_I:
|
||||
return col+printf_wrapper(out, "d%d", arg);
|
||||
case MODE_F:
|
||||
return col+printf_wrapper(out, "f%d", arg);
|
||||
case MODE_S:
|
||||
return col+printf_wrapper(out, "s%d", arg);
|
||||
case MODE_P:
|
||||
return col+printf_wrapper(out, "a%d", arg);
|
||||
case MODE_V:
|
||||
return col+printf_wrapper(out, "v%d", arg);
|
||||
|
||||
case MODE_KI:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "%d", func->KonstD[arg]);
|
||||
}
|
||||
return printf_wrapper(out, "kd%d", arg);
|
||||
case MODE_KF:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "%#g", func->KonstF[arg]);
|
||||
}
|
||||
return col+printf_wrapper(out, "kf%d", arg);
|
||||
case MODE_KS:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "\"%.27s\"", func->KonstS[arg].GetChars());
|
||||
}
|
||||
return col+printf_wrapper(out, "ks%d", arg);
|
||||
case MODE_KP:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "%p", func->KonstA[arg]);
|
||||
}
|
||||
return col+printf_wrapper(out, "ka%d", arg);
|
||||
case MODE_KV:
|
||||
if (func != NULL)
|
||||
{
|
||||
return col+printf_wrapper(out, "(%f,%f,%f)", func->KonstF[arg], func->KonstF[arg+1], func->KonstF[arg+2]);
|
||||
}
|
||||
return col+printf_wrapper(out, "kv%d", arg);
|
||||
|
||||
case MODE_IMMS:
|
||||
return col+printf_wrapper(out, "%d", (arg << immshift) >> immshift);
|
||||
|
||||
case MODE_IMMZ:
|
||||
return col+printf_wrapper(out, "%d", arg);
|
||||
|
||||
case MODE_PARAM:
|
||||
{
|
||||
int regtype, regnum;
|
||||
#ifdef __BIG_ENDIAN__
|
||||
regtype = (arg >> 8) & 255;
|
||||
regnum = arg & 255;
|
||||
#else
|
||||
regtype = arg & 255;
|
||||
regnum = (arg >> 8) & 255;
|
||||
#endif
|
||||
switch (regtype & (REGT_TYPE | REGT_KONST | REGT_MULTIREG))
|
||||
{
|
||||
case REGT_INT:
|
||||
return col+printf_wrapper(out, "d%d", regnum);
|
||||
case REGT_FLOAT:
|
||||
return col+printf_wrapper(out, "f%d", regnum);
|
||||
case REGT_STRING:
|
||||
return col+printf_wrapper(out, "s%d", regnum);
|
||||
case REGT_POINTER:
|
||||
return col+printf_wrapper(out, "a%d", regnum);
|
||||
case REGT_FLOAT | REGT_MULTIREG:
|
||||
return col+printf_wrapper(out, "v%d", regnum);
|
||||
case REGT_INT | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KI, 0, func);
|
||||
case REGT_FLOAT | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KF, 0, func);
|
||||
case REGT_STRING | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KS, 0, func);
|
||||
case REGT_POINTER | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KP, 0, func);
|
||||
case REGT_FLOAT | REGT_MULTIREG | REGT_KONST:
|
||||
return col+print_reg(out, 0, regnum, MODE_KV, 0, func);
|
||||
default:
|
||||
if (regtype == REGT_NIL)
|
||||
{
|
||||
return col+printf_wrapper(out, "nil");
|
||||
}
|
||||
return col+printf_wrapper(out, "param[t=%d,%c,%c,n=%d]",
|
||||
regtype & REGT_TYPE,
|
||||
regtype & REGT_KONST ? 'k' : 'r',
|
||||
regtype & REGT_MULTIREG ? 'm' : 's',
|
||||
regnum);
|
||||
}
|
||||
}
|
||||
|
||||
default:
|
||||
return col+printf_wrapper(out, "$%d", arg);
|
||||
}
|
||||
return col;
|
||||
}
|
218
src/scripting/vm/vmexec.cpp
Normal file
218
src/scripting/vm/vmexec.cpp
Normal file
|
@ -0,0 +1,218 @@
|
|||
/*
|
||||
** vmexec.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "vm.h"
|
||||
#include "xs_Float.h"
|
||||
#include "math/cmath.h"
|
||||
|
||||
#define IMPLEMENT_VMEXEC
|
||||
|
||||
#if !defined(COMPGOTO) && defined(__GNUC__)
|
||||
#define COMPGOTO 1
|
||||
#endif
|
||||
|
||||
#if COMPGOTO
|
||||
#define OP(x) x
|
||||
#define NEXTOP do { unsigned op = pc->op; a = pc->a; pc++; goto *ops[op]; } while(0)
|
||||
#else
|
||||
#define OP(x) case OP_##x
|
||||
#define NEXTOP break
|
||||
#endif
|
||||
|
||||
#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b))
|
||||
|
||||
#define A (pc[-1].a)
|
||||
#define B (pc[-1].b)
|
||||
#define C (pc[-1].c)
|
||||
#define Cs (pc[-1].cs)
|
||||
#define BC (pc[-1].i16u)
|
||||
#define BCs (pc[-1].i16)
|
||||
#define ABCs (pc[-1].i24)
|
||||
#define JMPOFS(x) ((x)->i24)
|
||||
|
||||
#define KC (konstd[C])
|
||||
#define RC (reg.d[C])
|
||||
|
||||
#define PA (reg.a[A])
|
||||
#define PB (reg.a[B])
|
||||
|
||||
#define ASSERTD(x) assert((unsigned)(x) < f->NumRegD)
|
||||
#define ASSERTF(x) assert((unsigned)(x) < f->NumRegF)
|
||||
#define ASSERTA(x) assert((unsigned)(x) < f->NumRegA)
|
||||
#define ASSERTS(x) assert((unsigned)(x) < f->NumRegS)
|
||||
|
||||
#define ASSERTKD(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstD)
|
||||
#define ASSERTKF(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstF)
|
||||
#define ASSERTKA(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstA)
|
||||
#define ASSERTKS(x) assert(sfunc != NULL && (unsigned)(x) < sfunc->NumKonstS)
|
||||
|
||||
#define THROW(x) throw(EVMAbortException(x))
|
||||
|
||||
#define CMPJMP(test) \
|
||||
if ((test) == (a & CMP_CHECK)) { \
|
||||
assert(pc->op == OP_JMP); \
|
||||
pc += 1 + JMPOFS(pc); \
|
||||
} else { \
|
||||
pc += 1; \
|
||||
}
|
||||
|
||||
#define GETADDR(a,o,x) \
|
||||
if (a == NULL) { THROW(x); } \
|
||||
ptr = (VM_SBYTE *)a + o
|
||||
|
||||
static const VM_UWORD ZapTable[16] =
|
||||
{
|
||||
0x00000000, 0x000000FF, 0x0000FF00, 0x0000FFFF,
|
||||
0x00FF0000, 0x00FF00FF, 0x00FFFF00, 0x00FFFFFF,
|
||||
0xFF000000, 0xFF0000FF, 0xFF00FF00, 0xFF00FFFF,
|
||||
0xFFFF0000, 0xFFFF00FF, 0xFFFFFF00, 0xFFFFFFFF
|
||||
};
|
||||
|
||||
#ifdef NDEBUG
|
||||
#define WAS_NDEBUG 1
|
||||
#else
|
||||
#define WAS_NDEBUG 0
|
||||
#endif
|
||||
|
||||
#if WAS_NDEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
#undef assert
|
||||
#include <assert.h>
|
||||
struct VMExec_Checked
|
||||
{
|
||||
#include "vmexec.h"
|
||||
};
|
||||
#if WAS_NDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
|
||||
#if !WAS_NDEBUG
|
||||
#define NDEBUG
|
||||
#endif
|
||||
#undef assert
|
||||
#include <assert.h>
|
||||
struct VMExec_Unchecked
|
||||
{
|
||||
#include "vmexec.h"
|
||||
};
|
||||
#if !WAS_NDEBUG
|
||||
#undef NDEBUG
|
||||
#endif
|
||||
#undef assert
|
||||
#include <assert.h>
|
||||
|
||||
int (*VMExec)(VMFrameStack *stack, const VMOP *pc, VMReturn *ret, int numret) =
|
||||
#ifdef NDEBUG
|
||||
VMExec_Unchecked::Exec
|
||||
#else
|
||||
VMExec_Checked::Exec
|
||||
#endif
|
||||
;
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMSelectEngine
|
||||
//
|
||||
// Selects the VM engine, either checked or unchecked. Default will decide
|
||||
// based on the NDEBUG preprocessor definition.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void VMSelectEngine(EVMEngine engine)
|
||||
{
|
||||
switch (engine)
|
||||
{
|
||||
case VMEngine_Default:
|
||||
#ifdef NDEBUG
|
||||
VMExec = VMExec_Unchecked::Exec;
|
||||
#else
|
||||
#endif
|
||||
VMExec = VMExec_Checked::Exec;
|
||||
break;
|
||||
case VMEngine_Unchecked:
|
||||
VMExec = VMExec_Unchecked::Exec;
|
||||
break;
|
||||
case VMEngine_Checked:
|
||||
VMExec = VMExec_Checked::Exec;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFillParams
|
||||
//
|
||||
// Takes parameters from the parameter stack and stores them in the callee's
|
||||
// registers.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void VMFillParams(VMValue *params, VMFrame *callee, int numparam)
|
||||
{
|
||||
unsigned int regd, regf, regs, rega;
|
||||
VMScriptFunction *calleefunc = static_cast<VMScriptFunction *>(callee->Func);
|
||||
const VMRegisters calleereg(callee);
|
||||
|
||||
assert(calleefunc != NULL && !calleefunc->Native);
|
||||
assert(numparam == calleefunc->NumArgs);
|
||||
assert(REGT_INT == 0 && REGT_FLOAT == 1 && REGT_STRING == 2 && REGT_POINTER == 3);
|
||||
|
||||
regd = regf = regs = rega = 0;
|
||||
for (int i = 0; i < numparam; ++i)
|
||||
{
|
||||
VMValue &p = params[i];
|
||||
if (p.Type < REGT_STRING)
|
||||
{
|
||||
if (p.Type == REGT_INT)
|
||||
{
|
||||
calleereg.d[regd++] = p.i;
|
||||
}
|
||||
else // p.Type == REGT_FLOAT
|
||||
{
|
||||
calleereg.f[regf++] = p.f;
|
||||
}
|
||||
}
|
||||
else if (p.Type == REGT_STRING)
|
||||
{
|
||||
calleereg.s[regs++] = p.s();
|
||||
}
|
||||
else
|
||||
{
|
||||
assert(p.Type == REGT_POINTER);
|
||||
calleereg.a[rega] = p.a;
|
||||
calleereg.atag[rega++] = p.atag;
|
||||
}
|
||||
}
|
||||
}
|
1608
src/scripting/vm/vmexec.h
Normal file
1608
src/scripting/vm/vmexec.h
Normal file
File diff suppressed because it is too large
Load diff
493
src/scripting/vm/vmframe.cpp
Normal file
493
src/scripting/vm/vmframe.cpp
Normal file
|
@ -0,0 +1,493 @@
|
|||
/*
|
||||
** vmframe.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <new>
|
||||
#include "vm.h"
|
||||
|
||||
IMPLEMENT_CLASS(VMException)
|
||||
IMPLEMENT_ABSTRACT_POINTY_CLASS(VMFunction)
|
||||
DECLARE_POINTER(Proto)
|
||||
END_POINTERS
|
||||
IMPLEMENT_CLASS(VMScriptFunction)
|
||||
IMPLEMENT_CLASS(VMNativeFunction)
|
||||
|
||||
VMScriptFunction::VMScriptFunction(FName name)
|
||||
{
|
||||
Native = false;
|
||||
Name = name;
|
||||
Code = NULL;
|
||||
KonstD = NULL;
|
||||
KonstF = NULL;
|
||||
KonstS = NULL;
|
||||
KonstA = NULL;
|
||||
ExtraSpace = 0;
|
||||
CodeSize = 0;
|
||||
NumRegD = 0;
|
||||
NumRegF = 0;
|
||||
NumRegS = 0;
|
||||
NumRegA = 0;
|
||||
NumKonstD = 0;
|
||||
NumKonstF = 0;
|
||||
NumKonstS = 0;
|
||||
NumKonstA = 0;
|
||||
MaxParam = 0;
|
||||
NumArgs = 0;
|
||||
}
|
||||
|
||||
VMScriptFunction::~VMScriptFunction()
|
||||
{
|
||||
if (Code != NULL)
|
||||
{
|
||||
if (KonstS != NULL)
|
||||
{
|
||||
for (int i = 0; i < NumKonstS; ++i)
|
||||
{
|
||||
KonstS[i].~FString();
|
||||
}
|
||||
}
|
||||
M_Free(Code);
|
||||
}
|
||||
}
|
||||
|
||||
void VMScriptFunction::Alloc(int numops, int numkonstd, int numkonstf, int numkonsts, int numkonsta)
|
||||
{
|
||||
assert(Code == NULL);
|
||||
assert(numops > 0);
|
||||
assert(numkonstd >= 0 && numkonstd <= 255);
|
||||
assert(numkonstf >= 0 && numkonstf <= 255);
|
||||
assert(numkonsts >= 0 && numkonsts <= 255);
|
||||
assert(numkonsta >= 0 && numkonsta <= 255);
|
||||
void *mem = M_Malloc(numops * sizeof(VMOP) +
|
||||
numkonstd * sizeof(int) +
|
||||
numkonstf * sizeof(double) +
|
||||
numkonsts * sizeof(FString) +
|
||||
numkonsta * (sizeof(FVoidObj) + 1));
|
||||
Code = (VMOP *)mem;
|
||||
mem = (void *)((VMOP *)mem + numops);
|
||||
|
||||
if (numkonstd > 0)
|
||||
{
|
||||
KonstD = (int *)mem;
|
||||
mem = (void *)((int *)mem + numkonstd);
|
||||
}
|
||||
else
|
||||
{
|
||||
KonstD = NULL;
|
||||
}
|
||||
if (numkonstf > 0)
|
||||
{
|
||||
KonstF = (double *)mem;
|
||||
mem = (void *)((double *)mem + numkonstf);
|
||||
}
|
||||
else
|
||||
{
|
||||
KonstF = NULL;
|
||||
}
|
||||
if (numkonsts > 0)
|
||||
{
|
||||
KonstS = (FString *)mem;
|
||||
for (int i = 0; i < numkonsts; ++i)
|
||||
{
|
||||
::new(&KonstS[i]) FString;
|
||||
}
|
||||
mem = (void *)((FString *)mem + numkonsts);
|
||||
}
|
||||
else
|
||||
{
|
||||
KonstS = NULL;
|
||||
}
|
||||
if (numkonsta > 0)
|
||||
{
|
||||
KonstA = (FVoidObj *)mem;
|
||||
}
|
||||
else
|
||||
{
|
||||
KonstA = NULL;
|
||||
}
|
||||
CodeSize = numops;
|
||||
NumKonstD = numkonstd;
|
||||
NumKonstF = numkonstf;
|
||||
NumKonstS = numkonsts;
|
||||
NumKonstA = numkonsta;
|
||||
}
|
||||
|
||||
size_t VMScriptFunction::PropagateMark()
|
||||
{
|
||||
if (KonstA != NULL)
|
||||
{
|
||||
FVoidObj *konsta = KonstA;
|
||||
VM_UBYTE *atag = KonstATags();
|
||||
for (int count = NumKonstA; count > 0; --count)
|
||||
{
|
||||
if (*atag++ == ATAG_OBJECT)
|
||||
{
|
||||
GC::Mark(konsta->o);
|
||||
}
|
||||
konsta++;
|
||||
}
|
||||
}
|
||||
return NumKonstA * sizeof(void *) + Super::PropagateMark();
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrame :: InitRegS
|
||||
//
|
||||
// Initialize the string registers of a newly-allocated VMFrame.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
void VMFrame::InitRegS()
|
||||
{
|
||||
FString *regs = GetRegS();
|
||||
for (int i = 0; i < NumRegS; ++i)
|
||||
{
|
||||
::new(®s[i]) FString;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack - Constructor
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrameStack::VMFrameStack()
|
||||
{
|
||||
Blocks = NULL;
|
||||
UnusedBlocks = NULL;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack - Destructor
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrameStack::~VMFrameStack()
|
||||
{
|
||||
while (PopFrame() != NULL)
|
||||
{ }
|
||||
if (Blocks != NULL)
|
||||
{
|
||||
BlockHeader *block, *next;
|
||||
for (block = Blocks; block != NULL; block = next)
|
||||
{
|
||||
next = block->NextBlock;
|
||||
delete[] (VM_UBYTE *)block;
|
||||
}
|
||||
}
|
||||
if (UnusedBlocks != NULL)
|
||||
{
|
||||
BlockHeader *block, *next;
|
||||
for (block = UnusedBlocks; block != NULL; block = next)
|
||||
{
|
||||
next = block->NextBlock;
|
||||
delete[] (VM_UBYTE *)block;
|
||||
}
|
||||
}
|
||||
Blocks = NULL;
|
||||
UnusedBlocks = NULL;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: AllocFrame
|
||||
//
|
||||
// Allocates a frame from the stack with the desired number of registers.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrame *VMFrameStack::AllocFrame(int numregd, int numregf, int numregs, int numrega)
|
||||
{
|
||||
assert((unsigned)numregd < 255);
|
||||
assert((unsigned)numregf < 255);
|
||||
assert((unsigned)numregs < 255);
|
||||
assert((unsigned)numrega < 255);
|
||||
// To keep the arguments to this function simpler, it assumes that every
|
||||
// register might be used as a parameter for a single call.
|
||||
int numparam = numregd + numregf + numregs + numrega;
|
||||
int size = VMFrame::FrameSize(numregd, numregf, numregs, numrega, numparam, 0);
|
||||
VMFrame *frame = Alloc(size);
|
||||
frame->NumRegD = numregd;
|
||||
frame->NumRegF = numregf;
|
||||
frame->NumRegS = numregs;
|
||||
frame->NumRegA = numrega;
|
||||
frame->MaxParam = numparam;
|
||||
frame->InitRegS();
|
||||
return frame;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: AllocFrame
|
||||
//
|
||||
// Allocates a frame from the stack suitable for calling a particular
|
||||
// function.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrame *VMFrameStack::AllocFrame(VMScriptFunction *func)
|
||||
{
|
||||
int size = VMFrame::FrameSize(func->NumRegD, func->NumRegF, func->NumRegS, func->NumRegA,
|
||||
func->MaxParam, func->ExtraSpace);
|
||||
VMFrame *frame = Alloc(size);
|
||||
frame->Func = func;
|
||||
frame->NumRegD = func->NumRegD;
|
||||
frame->NumRegF = func->NumRegF;
|
||||
frame->NumRegS = func->NumRegS;
|
||||
frame->NumRegA = func->NumRegA;
|
||||
frame->MaxParam = func->MaxParam;
|
||||
frame->Func = func;
|
||||
frame->InitRegS();
|
||||
return frame;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: Alloc
|
||||
//
|
||||
// Allocates space for a frame. Its size will be rounded up to a multiple
|
||||
// of 16 bytes.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrame *VMFrameStack::Alloc(int size)
|
||||
{
|
||||
BlockHeader *block;
|
||||
VMFrame *frame, *parent;
|
||||
|
||||
size = (size + 15) & ~15;
|
||||
block = Blocks;
|
||||
if (block != NULL)
|
||||
{
|
||||
parent = block->LastFrame;
|
||||
}
|
||||
else
|
||||
{
|
||||
parent = NULL;
|
||||
}
|
||||
if (block == NULL || ((VM_UBYTE *)block + block->BlockSize) < (block->FreeSpace + size))
|
||||
{ // Not enough space. Allocate a new block.
|
||||
int blocksize = ((sizeof(BlockHeader) + 15) & ~15) + size;
|
||||
BlockHeader **blockp;
|
||||
if (blocksize < BLOCK_SIZE)
|
||||
{
|
||||
blocksize = BLOCK_SIZE;
|
||||
}
|
||||
for (blockp = &UnusedBlocks, block = *blockp; block != NULL; block = block->NextBlock)
|
||||
{
|
||||
if (block->BlockSize >= blocksize)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (block != NULL)
|
||||
{
|
||||
*blockp = block->NextBlock;
|
||||
}
|
||||
else
|
||||
{
|
||||
block = (BlockHeader *)new VM_UBYTE[blocksize];
|
||||
block->BlockSize = blocksize;
|
||||
}
|
||||
block->InitFreeSpace();
|
||||
block->LastFrame = NULL;
|
||||
block->NextBlock = Blocks;
|
||||
Blocks = block;
|
||||
}
|
||||
frame = (VMFrame *)block->FreeSpace;
|
||||
memset(frame, 0, size);
|
||||
frame->ParentFrame = parent;
|
||||
block->FreeSpace += size;
|
||||
block->LastFrame = frame;
|
||||
return frame;
|
||||
}
|
||||
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: PopFrame
|
||||
//
|
||||
// Pops the top frame off the stack, returning a pointer to the new top
|
||||
// frame.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
VMFrame *VMFrameStack::PopFrame()
|
||||
{
|
||||
if (Blocks == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
VMFrame *frame = Blocks->LastFrame;
|
||||
if (frame == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
// Free any string registers this frame had.
|
||||
FString *regs = frame->GetRegS();
|
||||
for (int i = frame->NumRegS; i != 0; --i)
|
||||
{
|
||||
(regs++)->~FString();
|
||||
}
|
||||
// Free any parameters this frame left behind.
|
||||
VMValue *param = frame->GetParam();
|
||||
for (int i = frame->NumParam; i != 0; --i)
|
||||
{
|
||||
(param++)->~VMValue();
|
||||
}
|
||||
VMFrame *parent = frame->ParentFrame;
|
||||
if (parent == NULL)
|
||||
{
|
||||
// Popping the last frame off the stack.
|
||||
if (Blocks != NULL)
|
||||
{
|
||||
assert(Blocks->NextBlock == NULL);
|
||||
Blocks->LastFrame = NULL;
|
||||
Blocks->InitFreeSpace();
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if ((VM_UBYTE *)parent < (VM_UBYTE *)Blocks || (VM_UBYTE *)parent >= (VM_UBYTE *)Blocks + Blocks->BlockSize)
|
||||
{ // Parent frame is in a different block, so move this one to the unused list.
|
||||
BlockHeader *next = Blocks->NextBlock;
|
||||
assert(next != NULL);
|
||||
assert((VM_UBYTE *)parent >= (VM_UBYTE *)next && (VM_UBYTE *)parent < (VM_UBYTE *)next + next->BlockSize);
|
||||
Blocks->NextBlock = UnusedBlocks;
|
||||
UnusedBlocks = Blocks;
|
||||
Blocks = next;
|
||||
}
|
||||
else
|
||||
{
|
||||
Blocks->LastFrame = parent;
|
||||
Blocks->FreeSpace = (VM_UBYTE *)frame;
|
||||
}
|
||||
return parent;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
//
|
||||
// VMFrameStack :: Call
|
||||
//
|
||||
// Calls a function, either native or scripted. If an exception occurs while
|
||||
// executing, the stack is cleaned up. If trap is non-NULL, it is set to the
|
||||
// VMException that was caught and the return value is negative. Otherwise,
|
||||
// any caught exceptions will be rethrown. Under normal termination, the
|
||||
// return value is the number of results from the function.
|
||||
//
|
||||
//===========================================================================
|
||||
|
||||
int VMFrameStack::Call(VMFunction *func, VMValue *params, int numparams, VMReturn *results, int numresults, VMException **trap)
|
||||
{
|
||||
bool allocated = false;
|
||||
try
|
||||
{
|
||||
if (func->Native)
|
||||
{
|
||||
return static_cast<VMNativeFunction *>(func)->NativeCall(this, params, numparams, results, numresults);
|
||||
}
|
||||
else
|
||||
{
|
||||
AllocFrame(static_cast<VMScriptFunction *>(func));
|
||||
allocated = true;
|
||||
VMFillParams(params, TopFrame(), numparams);
|
||||
int numret = VMExec(this, static_cast<VMScriptFunction *>(func)->Code, results, numresults);
|
||||
PopFrame();
|
||||
return numret;
|
||||
}
|
||||
}
|
||||
catch (VMException *exception)
|
||||
{
|
||||
if (allocated)
|
||||
{
|
||||
PopFrame();
|
||||
}
|
||||
if (trap != NULL)
|
||||
{
|
||||
*trap = exception;
|
||||
return -1;
|
||||
}
|
||||
throw;
|
||||
}
|
||||
catch (EVMAbortException exception)
|
||||
{
|
||||
if (allocated)
|
||||
{
|
||||
PopFrame();
|
||||
}
|
||||
if (trap != nullptr)
|
||||
{
|
||||
*trap = nullptr;
|
||||
}
|
||||
|
||||
Printf("VM execution aborted: ");
|
||||
switch (exception)
|
||||
{
|
||||
case X_READ_NIL:
|
||||
Printf("tried to read from address zero.");
|
||||
break;
|
||||
|
||||
case X_WRITE_NIL:
|
||||
Printf("tried to write to address zero.");
|
||||
break;
|
||||
|
||||
case X_TOO_MANY_TRIES:
|
||||
Printf("too many try-catch blocks.");
|
||||
break;
|
||||
|
||||
case X_ARRAY_OUT_OF_BOUNDS:
|
||||
Printf("array access out of bounds.");
|
||||
break;
|
||||
|
||||
case X_DIVISION_BY_ZERO:
|
||||
Printf("division by zero.");
|
||||
break;
|
||||
|
||||
case X_BAD_SELF:
|
||||
Printf("invalid self pointer.");
|
||||
break;
|
||||
}
|
||||
Printf("\n");
|
||||
|
||||
return -1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
if (allocated)
|
||||
{
|
||||
PopFrame();
|
||||
}
|
||||
throw;
|
||||
}
|
||||
}
|
215
src/scripting/vm/vmops.h
Normal file
215
src/scripting/vm/vmops.h
Normal file
|
@ -0,0 +1,215 @@
|
|||
#ifndef xx
|
||||
#define xx(op, name, mode) OP_##op
|
||||
#endif
|
||||
|
||||
xx(NOP, nop, NOP), // no operation
|
||||
|
||||
// Load constants.
|
||||
xx(LI, li, LI), // load immediate signed 16-bit constant
|
||||
xx(LK, lk, LKI), // load integer constant
|
||||
xx(LKF, lk, LKF), // load float constant
|
||||
xx(LKS, lk, LKS), // load string constant
|
||||
xx(LKP, lk, LKP), // load pointer constant
|
||||
xx(LFP, lf, LFP), // load frame pointer
|
||||
|
||||
// Load from memory. rA = *(rB + rkC)
|
||||
xx(LB, lb, RIRPKI), // load byte
|
||||
xx(LB_R, lb, RIRPRI),
|
||||
xx(LH, lh, RIRPKI), // load halfword
|
||||
xx(LH_R, lh, RIRPRI),
|
||||
xx(LW, lw, RIRPKI), // load word
|
||||
xx(LW_R, lw, RIRPRI),
|
||||
xx(LBU, lbu, RIRPKI), // load byte unsigned
|
||||
xx(LBU_R, lbu, RIRPRI),
|
||||
xx(LHU, lhu, RIRPKI), // load halfword unsigned
|
||||
xx(LHU_R, lhu, RIRPRI),
|
||||
xx(LSP, lsp, RFRPKI), // load single-precision fp
|
||||
xx(LSP_R, lsp, RFRPRI),
|
||||
xx(LDP, ldp, RFRPKI), // load double-precision fp
|
||||
xx(LDP_R, ldp, RFRPRI),
|
||||
xx(LS, ls, RSRPKI), // load string
|
||||
xx(LS_R, ls, RSRPRI),
|
||||
xx(LO, lo, RPRPKI), // load object
|
||||
xx(LO_R, lo, RPRPRI),
|
||||
xx(LP, lp, RPRPKI), // load pointer
|
||||
xx(LP_R, lp, RPRPRI),
|
||||
xx(LV, lv, RVRPKI), // load vector
|
||||
xx(LV_R, lv, RVRPRI),
|
||||
|
||||
xx(LBIT, lbit, RIRPI8), // rA = !!(*rB & C) -- *rB is a byte
|
||||
|
||||
// Store instructions. *(rA + rkC) = rB
|
||||
xx(SB, sb, RPRIKI), // store byte
|
||||
xx(SB_R, sb, RPRIRI),
|
||||
xx(SH, sh, RPRIKI), // store halfword
|
||||
xx(SH_R, sh, RPRIRI),
|
||||
xx(SW, sw, RPRIKI), // store word
|
||||
xx(SW_R, sw, RPRIRI),
|
||||
xx(SSP, ssp, RPRFKI), // store single-precision fp
|
||||
xx(SSP_R, ssp, RPRFRI),
|
||||
xx(SDP, sdp, RPRFKI), // store double-precision fp
|
||||
xx(SDP_R, sdp, RPRFRI),
|
||||
xx(SS, ss, RPRSKI), // store string
|
||||
xx(SS_R, ss, RPRSRI),
|
||||
xx(SP, sp, RPRPKI), // store pointer
|
||||
xx(SP_R, sp, RPRPRI),
|
||||
xx(SV, sv, RPRVKI), // store vector
|
||||
xx(SV_R, sv, RPRVRI),
|
||||
|
||||
xx(SBIT, sbit, RPRII8), // *rA |= C if rB is true, *rA &= ~C otherwise
|
||||
|
||||
// Move instructions.
|
||||
xx(MOVE, mov, RIRI), // dA = dB
|
||||
xx(MOVEF, mov, RFRF), // fA = fB
|
||||
xx(MOVES, mov, RSRS), // sA = sB
|
||||
xx(MOVEA, mov, RPRP), // aA = aB
|
||||
xx(CAST, cast, CAST), // xA = xB, conversion specified by C
|
||||
xx(DYNCAST_R, dyncast,RPRPRP), // aA = aB after casting to rkC (specifying a class)
|
||||
xx(DYNCAST_K, dyncast,RPRPKP),
|
||||
|
||||
// Control flow.
|
||||
xx(TEST, test, RII16), // if (dA != BC) then pc++
|
||||
xx(JMP, jmp, I24), // pc += ABC -- The ABC fields contain a signed 24-bit offset.
|
||||
xx(IJMP, ijmp, RII16), // pc += dA + BC -- BC is a signed offset. The target instruction must be a JMP.
|
||||
xx(PARAM, param, __BCP), // push parameter encoded in BC for function call (B=regtype, C=regnum)
|
||||
xx(PARAMI, parami, I24), // push immediate, signed integer for function call
|
||||
xx(CALL, call, RPI8I8), // Call function pkA with parameter count B and expected result count C
|
||||
xx(CALL_K, call, KPI8I8),
|
||||
xx(TAIL, tail, RPI8), // Call+Ret in a single instruction
|
||||
xx(TAIL_K, tail, KPI8),
|
||||
xx(RESULT, result, __BCP), // Result should go in register encoded in BC (in caller, after CALL)
|
||||
xx(RET, ret, I8BCP), // Copy value from register encoded in BC to return value A, possibly returning
|
||||
xx(RETI, reti, I8I16), // Copy immediate from BC to return value A, possibly returning
|
||||
xx(TRY, try, I24), // When an exception is thrown, start searching for a handler at pc + ABC
|
||||
xx(UNTRY, untry, I8), // Pop A entries off the exception stack
|
||||
xx(THROW, throw, THROW), // A == 0: Throw exception object pB
|
||||
// A == 1: Throw exception object pkB
|
||||
// A >= 2: Throw VM exception of type BC
|
||||
xx(CATCH, catch, CATCH), // A == 0: continue search on next try
|
||||
// A == 1: continue execution at instruction immediately following CATCH (catches any exception)
|
||||
// A == 2: (pB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
|
||||
// A == 3: (pkB == <type of exception thrown>) then pc++ ; next instruction must JMP to another CATCH
|
||||
// for A > 0, exception is stored in pC
|
||||
xx(BOUND, bound, RII16), // if rA >= BC, throw exception
|
||||
|
||||
// String instructions.
|
||||
xx(CONCAT, concat, RSRSRS), // sA = sB.. ... ..sC
|
||||
xx(LENS, lens, RIRS), // dA = sB.Length
|
||||
xx(CMPS, cmps, I8RXRX), // if ((skB op skC) != (A & 1)) then pc++
|
||||
|
||||
// Integer math.
|
||||
xx(SLL_RR, sll, RIRIRI), // dA = dkB << diC
|
||||
xx(SLL_RI, sll, RIRII8),
|
||||
xx(SLL_KR, sll, RIKIRI),
|
||||
xx(SRL_RR, srl, RIRIRI), // dA = dkB >> diC -- unsigned
|
||||
xx(SRL_RI, srl, RIRII8),
|
||||
xx(SRL_KR, srl, RIKIRI),
|
||||
xx(SRA_RR, sra, RIRIRI), // dA = dkB >> diC -- signed
|
||||
xx(SRA_RI, sra, RIRII8),
|
||||
xx(SRA_KR, sra, RIKIRI),
|
||||
xx(ADD_RR, add, RIRIRI), // dA = dB + dkC
|
||||
xx(ADD_RK, add, RIRIKI),
|
||||
xx(ADDI, addi, RIRIIs), // dA = dB + C -- C is a signed 8-bit constant
|
||||
xx(SUB_RR, sub, RIRIRI), // dA = dkB - dkC
|
||||
xx(SUB_RK, sub, RIRIKI),
|
||||
xx(SUB_KR, sub, RIKIRI),
|
||||
xx(MUL_RR, mul, RIRIRI), // dA = dB * dkC
|
||||
xx(MUL_RK, mul, RIRIKI),
|
||||
xx(DIV_RR, div, RIRIRI), // dA = dkB / dkC
|
||||
xx(DIV_RK, div, RIRIKI),
|
||||
xx(DIV_KR, div, RIKIRI),
|
||||
xx(MOD_RR, mod, RIRIRI), // dA = dkB % dkC
|
||||
xx(MOD_RK, mod, RIRIKI),
|
||||
xx(MOD_KR, mod, RIKIRI),
|
||||
xx(AND_RR, and, RIRIRI), // dA = dB & dkC
|
||||
xx(AND_RK, and, RIRIKI),
|
||||
xx(OR_RR, or, RIRIRI), // dA = dB | dkC
|
||||
xx(OR_RK, or, RIRIKI),
|
||||
xx(XOR_RR, xor, RIRIRI), // dA = dB ^ dkC
|
||||
xx(XOR_RK, xor, RIRIKI),
|
||||
xx(MIN_RR, min, RIRIRI), // dA = min(dB,dkC)
|
||||
xx(MIN_RK, min, RIRIKI),
|
||||
xx(MAX_RR, max, RIRIRI), // dA = max(dB,dkC)
|
||||
xx(MAX_RK, max, RIRIKI),
|
||||
xx(ABS, abs, RIRI), // dA = abs(dB)
|
||||
xx(NEG, neg, RIRI), // dA = -dB
|
||||
xx(NOT, not, RIRI), // dA = ~dB
|
||||
xx(SEXT, sext, RIRII8), // dA = dB, sign extended by shifting left then right by C
|
||||
xx(ZAP_R, zap, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are one
|
||||
xx(ZAP_I, zap, RIRII8),
|
||||
xx(ZAPNOT_R, zapnot, RIRIRI), // dA = dB, with bytes zeroed where bits in C/dC are zero
|
||||
xx(ZAPNOT_I, zapnot, RIRII8),
|
||||
xx(EQ_R, beq, CIRR), // if ((dB == dkC) != A) then pc++
|
||||
xx(EQ_K, beq, CIRK),
|
||||
xx(LT_RR, blt, CIRR), // if ((dkB < dkC) != A) then pc++
|
||||
xx(LT_RK, blt, CIRK),
|
||||
xx(LT_KR, blt, CIKR),
|
||||
xx(LE_RR, ble, CIRR), // if ((dkB <= dkC) != A) then pc++
|
||||
xx(LE_RK, ble, CIRK),
|
||||
xx(LE_KR, ble, CIKR),
|
||||
xx(LTU_RR, bltu, CIRR), // if ((dkB < dkC) != A) then pc++ -- unsigned
|
||||
xx(LTU_RK, bltu, CIRK),
|
||||
xx(LTU_KR, bltu, CIKR),
|
||||
xx(LEU_RR, bleu, CIRR), // if ((dkB <= dkC) != A) then pc++ -- unsigned
|
||||
xx(LEU_RK, bleu, CIRK),
|
||||
xx(LEU_KR, bleu, CIKR),
|
||||
|
||||
// Double-precision floating point math.
|
||||
xx(ADDF_RR, add, RFRFRF), // fA = fB + fkC
|
||||
xx(ADDF_RK, add, RFRFKF),
|
||||
xx(SUBF_RR, sub, RFRFRF), // fA = fkB - fkC
|
||||
xx(SUBF_RK, sub, RFRFKF),
|
||||
xx(SUBF_KR, sub, RFKFRF),
|
||||
xx(MULF_RR, mul, RFRFRF), // fA = fB * fkC
|
||||
xx(MULF_RK, mul, RFRFKF),
|
||||
xx(DIVF_RR, div, RFRFRF), // fA = fkB / fkC
|
||||
xx(DIVF_RK, div, RFRFKF),
|
||||
xx(DIVF_KR, div, RFKFRF),
|
||||
xx(MODF_RR, mod, RFRFRF), // fA = fkB % fkC
|
||||
xx(MODF_RK, mod, RFRFKF),
|
||||
xx(MODF_KR, mod, RFKFRF),
|
||||
xx(POWF_RR, pow, RFRFRF), // fA = fkB ** fkC
|
||||
xx(POWF_RK, pow, RFRFKF),
|
||||
xx(POWF_KR, pow, RFKFRF),
|
||||
xx(MINF_RR, min, RFRFRF), // fA = min(fB),fkC)
|
||||
xx(MINF_RK, min, RFRFKF),
|
||||
xx(MAXF_RR, max, RFRFRF), // fA = max(fB),fkC)
|
||||
xx(MAXF_RK, max, RFRFKF),
|
||||
xx(ATAN2, atan2, RFRFRF), // fA = atan2(fB,fC), result is in degrees
|
||||
xx(FLOP, flop, RFRFI8), // fA = f(fB), where function is selected by C
|
||||
xx(EQF_R, beq, CFRR), // if ((fB == fkC) != (A & 1)) then pc++
|
||||
xx(EQF_K, beq, CFRK),
|
||||
xx(LTF_RR, blt, CFRR), // if ((fkB < fkC) != (A & 1)) then pc++
|
||||
xx(LTF_RK, blt, CFRK),
|
||||
xx(LTF_KR, blt, CFKR),
|
||||
xx(LEF_RR, ble, CFRR), // if ((fkb <= fkC) != (A & 1)) then pc++
|
||||
xx(LEF_RK, ble, CFRK),
|
||||
xx(LEF_KR, ble, CFKR),
|
||||
|
||||
// Vector math.
|
||||
xx(NEGV, negv, RVRV), // vA = -vB
|
||||
xx(ADDV_RR, addv, RVRVRV), // vA = vB + vkC
|
||||
xx(ADDV_RK, addv, RVRVKV),
|
||||
xx(SUBV_RR, subv, RVRVRV), // vA = vkB - vkC
|
||||
xx(SUBV_RK, subv, RVRVKV),
|
||||
xx(SUBV_KR, subv, RVKVRV),
|
||||
xx(DOTV_RR, dotv, RVRVRV), // va = vB dot vkC
|
||||
xx(DOTV_RK, dotv, RVRVKV),
|
||||
xx(CROSSV_RR, crossv, RVRVRV), // vA = vkB cross vkC
|
||||
xx(CROSSV_RK, crossv, RVRVKV),
|
||||
xx(CROSSV_KR, crossv, RVKVRV),
|
||||
xx(MULVF_RR, mulv, RVRVRV), // vA = vkB * fkC
|
||||
xx(MULVF_RK, mulv, RVRVKV),
|
||||
xx(MULVF_KR, mulv, RVKVRV),
|
||||
xx(LENV, lenv, RFRV), // fA = vB.Length
|
||||
xx(EQV_R, beqv, CVRR), // if ((vB == vkC) != A) then pc++ (inexact if A & 32)
|
||||
xx(EQV_K, beqv, CVRK),
|
||||
|
||||
// Pointer math.
|
||||
xx(ADDA_RR, add, RPRPRI), // pA = pB + dkC
|
||||
xx(ADDA_RK, add, RPRPKI),
|
||||
xx(SUBA, sub, RIRPRP), // dA = pB - pC
|
||||
xx(EQA_R, beq, CPRR), // if ((pB == pkC) != A) then pc++
|
||||
xx(EQA_K, beq, CPRK),
|
||||
|
||||
#undef xx
|
904
src/scripting/zscript/ast.cpp
Normal file
904
src/scripting/zscript/ast.cpp
Normal file
|
@ -0,0 +1,904 @@
|
|||
/*
|
||||
** ast.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "dobject.h"
|
||||
#include "sc_man.h"
|
||||
#include "memarena.h"
|
||||
#include "zcc_parser.h"
|
||||
#include "zcc-parse.h"
|
||||
|
||||
class FLispString;
|
||||
extern void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *);
|
||||
|
||||
static const char *BuiltInTypeNames[] =
|
||||
{
|
||||
"sint8", "uint8",
|
||||
"sint16", "uint16",
|
||||
"sint32", "uint32",
|
||||
"intauto",
|
||||
|
||||
"bool",
|
||||
"float32", "float64", "floatauto",
|
||||
"string",
|
||||
"vector2",
|
||||
"vector3",
|
||||
"vector4",
|
||||
"name",
|
||||
"color",
|
||||
"state",
|
||||
"sound",
|
||||
|
||||
"usertype",
|
||||
|
||||
};
|
||||
|
||||
class FLispString
|
||||
{
|
||||
public:
|
||||
operator FString &() { return Str; }
|
||||
|
||||
FLispString()
|
||||
{
|
||||
NestDepth = Column = 0;
|
||||
WrapWidth = 200;
|
||||
NeedSpace = false;
|
||||
ConsecOpens = 0;
|
||||
}
|
||||
|
||||
void Open(const char *label)
|
||||
{
|
||||
size_t labellen = label != NULL ? strlen(label) : 0;
|
||||
CheckWrap(labellen + 1 + NeedSpace);
|
||||
if (NeedSpace)
|
||||
{
|
||||
Str << ' ';
|
||||
ConsecOpens = 0;
|
||||
}
|
||||
Str << '(';
|
||||
ConsecOpens++;
|
||||
if (label != NULL)
|
||||
{
|
||||
Str.AppendCStrPart(label, labellen);
|
||||
}
|
||||
Column += labellen + 1 + NeedSpace;
|
||||
NestDepth++;
|
||||
NeedSpace = (label != NULL);
|
||||
}
|
||||
void Close()
|
||||
{
|
||||
assert(NestDepth != 0);
|
||||
Str << ')';
|
||||
Column++;
|
||||
NestDepth--;
|
||||
NeedSpace = true;
|
||||
}
|
||||
void Break()
|
||||
{
|
||||
// Don't break if not needed.
|
||||
if (Column != NestDepth)
|
||||
{
|
||||
if (NeedSpace)
|
||||
{
|
||||
ConsecOpens = 0;
|
||||
}
|
||||
else
|
||||
{ // Move hanging ( characters to the new line
|
||||
Str.Truncate(long(Str.Len() - ConsecOpens));
|
||||
NestDepth -= ConsecOpens;
|
||||
}
|
||||
Str << '\n';
|
||||
Column = NestDepth;
|
||||
NeedSpace = false;
|
||||
if (NestDepth > 0)
|
||||
{
|
||||
Str.AppendFormat("%*s", (int)NestDepth, "");
|
||||
}
|
||||
if (ConsecOpens > 0)
|
||||
{
|
||||
for (size_t i = 0; i < ConsecOpens; ++i)
|
||||
{
|
||||
Str << '(';
|
||||
}
|
||||
NestDepth += ConsecOpens;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool CheckWrap(size_t len)
|
||||
{
|
||||
if (len + Column > WrapWidth)
|
||||
{
|
||||
Break();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void Add(const char *str, size_t len)
|
||||
{
|
||||
CheckWrap(len + NeedSpace);
|
||||
if (NeedSpace)
|
||||
{
|
||||
Str << ' ';
|
||||
}
|
||||
Str.AppendCStrPart(str, len);
|
||||
Column += len + NeedSpace;
|
||||
NeedSpace = true;
|
||||
}
|
||||
void Add(const char *str)
|
||||
{
|
||||
Add(str, strlen(str));
|
||||
}
|
||||
void Add(FString &str)
|
||||
{
|
||||
Add(str.GetChars(), str.Len());
|
||||
}
|
||||
void AddName(FName name)
|
||||
{
|
||||
size_t namelen = strlen(name.GetChars());
|
||||
CheckWrap(namelen + 2 + NeedSpace);
|
||||
if (NeedSpace)
|
||||
{
|
||||
NeedSpace = false;
|
||||
Str << ' ';
|
||||
}
|
||||
Str << '\'' << name.GetChars() << '\'';
|
||||
Column += namelen + 2 + NeedSpace;
|
||||
NeedSpace = true;
|
||||
}
|
||||
void AddChar(char c)
|
||||
{
|
||||
Add(&c, 1);
|
||||
}
|
||||
void AddInt(int i, bool un=false)
|
||||
{
|
||||
char buf[16];
|
||||
size_t len;
|
||||
if (!un)
|
||||
{
|
||||
len = mysnprintf(buf, countof(buf), "%d", i);
|
||||
}
|
||||
else
|
||||
{
|
||||
len = mysnprintf(buf, countof(buf), "%uu", i);
|
||||
}
|
||||
Add(buf, len);
|
||||
}
|
||||
void AddHex(unsigned x)
|
||||
{
|
||||
char buf[10];
|
||||
size_t len = mysnprintf(buf, countof(buf), "%08x", x);
|
||||
Add(buf, len);
|
||||
}
|
||||
void AddFloat(double f, bool single)
|
||||
{
|
||||
char buf[32];
|
||||
size_t len = mysnprintf(buf, countof(buf), "%.4f", f);
|
||||
if (single)
|
||||
{
|
||||
buf[len++] = 'f';
|
||||
buf[len] = '\0';
|
||||
}
|
||||
Add(buf, len);
|
||||
}
|
||||
private:
|
||||
FString Str;
|
||||
size_t NestDepth;
|
||||
size_t Column;
|
||||
size_t WrapWidth;
|
||||
size_t ConsecOpens;
|
||||
bool NeedSpace;
|
||||
};
|
||||
|
||||
static void PrintNode(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
assert(TreeNodePrinter[NUM_AST_NODE_TYPES-1] != NULL);
|
||||
if (node->NodeType >= 0 && node->NodeType < NUM_AST_NODE_TYPES)
|
||||
{
|
||||
TreeNodePrinter[node->NodeType](out, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
out.Open("unknown-node-type");
|
||||
out.AddInt(node->NodeType);
|
||||
out.Close();
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintNodes(FLispString &out, ZCC_TreeNode *node, bool newlist=true, bool addbreaks=false)
|
||||
{
|
||||
ZCC_TreeNode *p;
|
||||
|
||||
if (node == NULL)
|
||||
{
|
||||
out.Add("nil", 3);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newlist)
|
||||
{
|
||||
out.Open(NULL);
|
||||
}
|
||||
p = node;
|
||||
do
|
||||
{
|
||||
if (addbreaks)
|
||||
{
|
||||
out.Break();
|
||||
}
|
||||
PrintNode(out, p);
|
||||
p = p->SiblingNext;
|
||||
} while (p != node);
|
||||
if (newlist)
|
||||
{
|
||||
out.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintBuiltInType(FLispString &out, EZCCBuiltinType type)
|
||||
{
|
||||
assert(ZCC_NUM_BUILT_IN_TYPES == countof(BuiltInTypeNames));
|
||||
if (unsigned(type) >= unsigned(ZCC_NUM_BUILT_IN_TYPES))
|
||||
{
|
||||
char buf[30];
|
||||
size_t len = mysnprintf(buf, countof(buf), "bad-type-%u", type);
|
||||
out.Add(buf, len);
|
||||
}
|
||||
else
|
||||
{
|
||||
out.Add(BuiltInTypeNames[type]);
|
||||
}
|
||||
}
|
||||
|
||||
static void PrintIdentifier(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_Identifier *inode = (ZCC_Identifier *)node;
|
||||
out.Open("identifier");
|
||||
out.AddName(inode->Id);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStringConst(FLispString &out, FString str)
|
||||
{
|
||||
FString outstr;
|
||||
outstr << '"';
|
||||
for (size_t i = 0; i < str.Len(); ++i)
|
||||
{
|
||||
if (str[i] == '"')
|
||||
{
|
||||
outstr << "\"";
|
||||
}
|
||||
else if (str[i] == '\\')
|
||||
{
|
||||
outstr << "\\\\";
|
||||
}
|
||||
else if (str[i] >= 32)
|
||||
{
|
||||
outstr << str[i];
|
||||
}
|
||||
else
|
||||
{
|
||||
outstr.AppendFormat("\\x%02X", str[i]);
|
||||
}
|
||||
}
|
||||
outstr << '"';
|
||||
out.Add(outstr);
|
||||
}
|
||||
|
||||
static void PrintClass(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_Class *cnode = (ZCC_Class *)node;
|
||||
out.Break();
|
||||
out.Open("class");
|
||||
out.AddName(cnode->NodeName);
|
||||
PrintNodes(out, cnode->ParentName);
|
||||
PrintNodes(out, cnode->Replaces);
|
||||
out.AddHex(cnode->Flags);
|
||||
PrintNodes(out, cnode->Body, false, true);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStruct(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_Struct *snode = (ZCC_Struct *)node;
|
||||
out.Break();
|
||||
out.Open("struct");
|
||||
out.AddName(snode->NodeName);
|
||||
PrintNodes(out, snode->Body, false, true);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintEnum(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_Enum *enode = (ZCC_Enum *)node;
|
||||
out.Break();
|
||||
out.Open("enum");
|
||||
out.AddName(enode->NodeName);
|
||||
PrintBuiltInType(out, enode->EnumType);
|
||||
out.Add(enode->Elements == NULL ? "nil" : "...", 3);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintEnumTerminator(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Open("enum-term");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStates(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_States *snode = (ZCC_States *)node;
|
||||
out.Break();
|
||||
out.Open("states");
|
||||
PrintNodes(out, snode->Body, false, true);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStatePart(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Open("state-part");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStateLabel(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_StateLabel *snode = (ZCC_StateLabel *)node;
|
||||
out.Open("state-label");
|
||||
out.AddName(snode->Label);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStateStop(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Open("state-stop");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStateWait(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Open("state-wait");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStateFail(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Open("state-fail");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStateLoop(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Open("state-loop");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStateGoto(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_StateGoto *snode = (ZCC_StateGoto *)node;
|
||||
out.Open("state-goto");
|
||||
PrintNodes(out, snode->Qualifier);
|
||||
PrintNodes(out, snode->Label);
|
||||
PrintNodes(out, snode->Offset);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStateLine(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_StateLine *snode = (ZCC_StateLine *)node;
|
||||
out.Open("state-line");
|
||||
out.Add(*(snode->Sprite));
|
||||
PrintNodes(out, snode->Duration);
|
||||
if (snode->bNoDelay) out.Add("nodelay", 7);
|
||||
if (snode->bBright) out.Add("bright", 6);
|
||||
if (snode->bFast) out.Add("fast", 4);
|
||||
if (snode->bSlow) out.Add("slow", 4);
|
||||
if (snode->bCanRaise) out.Add("canraise", 8);
|
||||
out.Add(*(snode->Frames));
|
||||
PrintNodes(out, snode->Offset);
|
||||
PrintNodes(out, snode->Action, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintVarName(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_VarName *vnode = (ZCC_VarName *)node;
|
||||
out.Open("var-name");
|
||||
PrintNodes(out, vnode->ArraySize);
|
||||
out.AddName(vnode->Name);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintType(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_Type *tnode = (ZCC_Type *)node;
|
||||
out.Open("bad-type");
|
||||
PrintNodes(out, tnode->ArraySize);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintBasicType(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_BasicType *tnode = (ZCC_BasicType *)node;
|
||||
out.Open("basic-type");
|
||||
PrintNodes(out, tnode->ArraySize);
|
||||
PrintBuiltInType(out, tnode->Type);
|
||||
if (tnode->Type == ZCC_UserType)
|
||||
{
|
||||
PrintNodes(out, tnode->UserType, false);
|
||||
}
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintMapType(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_MapType *tnode = (ZCC_MapType *)node;
|
||||
out.Open("map-type");
|
||||
PrintNodes(out, tnode->ArraySize);
|
||||
PrintNodes(out, tnode->KeyType);
|
||||
PrintNodes(out, tnode->ValueType);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintDynArrayType(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_DynArrayType *tnode = (ZCC_DynArrayType *)node;
|
||||
out.Open("dyn-array-type");
|
||||
PrintNodes(out, tnode->ArraySize);
|
||||
PrintNodes(out, tnode->ElementType);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintClassType(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ClassType *tnode = (ZCC_ClassType *)node;
|
||||
out.Open("class-type");
|
||||
PrintNodes(out, tnode->ArraySize);
|
||||
PrintNodes(out, tnode->Restriction);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void OpenExprType(FLispString &out, EZCCExprType type)
|
||||
{
|
||||
char buf[32];
|
||||
|
||||
if (unsigned(type) < PEX_COUNT_OF)
|
||||
{
|
||||
mysnprintf(buf, countof(buf), "expr-%s", ZCC_OpInfo[type].OpName);
|
||||
}
|
||||
else
|
||||
{
|
||||
mysnprintf(buf, countof(buf), "bad-pex-%u", type);
|
||||
}
|
||||
out.Open(buf);
|
||||
}
|
||||
|
||||
static void PrintExpression(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_Expression *enode = (ZCC_Expression *)node;
|
||||
OpenExprType(out, enode->Operation);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExprID(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExprID *enode = (ZCC_ExprID *)node;
|
||||
assert(enode->Operation == PEX_ID);
|
||||
out.Open("expr-id");
|
||||
out.AddName(enode->Identifier);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExprTypeRef(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExprTypeRef *enode = (ZCC_ExprTypeRef *)node;
|
||||
assert(enode->Operation == PEX_TypeRef);
|
||||
out.Open("expr-type-ref");
|
||||
if (enode->RefType == TypeSInt8) { out.Add("sint8"); }
|
||||
else if (enode->RefType == TypeUInt8) { out.Add("uint8"); }
|
||||
else if (enode->RefType == TypeSInt16) { out.Add("sint16"); }
|
||||
else if (enode->RefType == TypeSInt32) { out.Add("sint32"); }
|
||||
else if (enode->RefType == TypeFloat32) { out.Add("float32"); }
|
||||
else if (enode->RefType == TypeFloat64) { out.Add("float64"); }
|
||||
else if (enode->RefType == TypeString) { out.Add("string"); }
|
||||
else if (enode->RefType == TypeName) { out.Add("name"); }
|
||||
else if (enode->RefType == TypeColor) { out.Add("color"); }
|
||||
else if (enode->RefType == TypeSound) { out.Add("sound"); }
|
||||
else { out.Add("other"); }
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExprConstant(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExprConstant *enode = (ZCC_ExprConstant *)node;
|
||||
assert(enode->Operation == PEX_ConstValue);
|
||||
out.Open("expr-const");
|
||||
if (enode->Type == TypeString)
|
||||
{
|
||||
PrintStringConst(out, *enode->StringVal);
|
||||
}
|
||||
else if (enode->Type == TypeFloat64)
|
||||
{
|
||||
out.AddFloat(enode->DoubleVal, false);
|
||||
}
|
||||
else if (enode->Type == TypeFloat32)
|
||||
{
|
||||
out.AddFloat(enode->DoubleVal, true);
|
||||
}
|
||||
else if (enode->Type == TypeName)
|
||||
{
|
||||
out.AddName(ENamedName(enode->IntVal));
|
||||
}
|
||||
else if (enode->Type->IsKindOf(RUNTIME_CLASS(PInt)))
|
||||
{
|
||||
out.AddInt(enode->IntVal, static_cast<PInt *>(enode->Type)->Unsigned);
|
||||
}
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExprFuncCall(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExprFuncCall *enode = (ZCC_ExprFuncCall *)node;
|
||||
assert(enode->Operation == PEX_FuncCall);
|
||||
out.Open("expr-func-call");
|
||||
PrintNodes(out, enode->Function);
|
||||
PrintNodes(out, enode->Parameters, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExprMemberAccess(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExprMemberAccess *enode = (ZCC_ExprMemberAccess *)node;
|
||||
assert(enode->Operation == PEX_MemberAccess);
|
||||
out.Open("expr-member-access");
|
||||
PrintNodes(out, enode->Left);
|
||||
out.AddName(enode->Right);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExprUnary(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExprUnary *enode = (ZCC_ExprUnary *)node;
|
||||
OpenExprType(out, enode->Operation);
|
||||
PrintNodes(out, enode->Operand, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExprBinary(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExprBinary *enode = (ZCC_ExprBinary *)node;
|
||||
OpenExprType(out, enode->Operation);
|
||||
PrintNodes(out, enode->Left);
|
||||
PrintNodes(out, enode->Right);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExprTrinary(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExprTrinary *enode = (ZCC_ExprTrinary *)node;
|
||||
OpenExprType(out, enode->Operation);
|
||||
PrintNodes(out, enode->Test);
|
||||
PrintNodes(out, enode->Left);
|
||||
PrintNodes(out, enode->Right);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintFuncParam(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_FuncParm *pnode = (ZCC_FuncParm *)node;
|
||||
out.Break();
|
||||
out.Open("func-parm");
|
||||
out.AddName(pnode->Label);
|
||||
PrintNodes(out, pnode->Value, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintStatement(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Open("statement");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintCompoundStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_CompoundStmt *snode = (ZCC_CompoundStmt *)node;
|
||||
out.Break();
|
||||
out.Open("compound-stmt");
|
||||
PrintNodes(out, snode->Content, false, true);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintDefault(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_Default *snode = (ZCC_Default *)node;
|
||||
out.Break();
|
||||
out.Open("default");
|
||||
PrintNodes(out, snode->Content, false, true);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintContinueStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Break();
|
||||
out.Open("continue-stmt");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintBreakStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
out.Break();
|
||||
out.Open("break-stmt");
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintReturnStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ReturnStmt *snode = (ZCC_ReturnStmt *)node;
|
||||
out.Break();
|
||||
out.Open("return-stmt");
|
||||
PrintNodes(out, snode->Values, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintExpressionStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ExpressionStmt *snode = (ZCC_ExpressionStmt *)node;
|
||||
out.Break();
|
||||
out.Open("expression-stmt");
|
||||
PrintNodes(out, snode->Expression, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintIterationStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_IterationStmt *snode = (ZCC_IterationStmt *)node;
|
||||
out.Break();
|
||||
out.Open("iteration-stmt");
|
||||
out.Add((snode->CheckAt == ZCC_IterationStmt::Start) ? "start" : "end");
|
||||
out.Break();
|
||||
PrintNodes(out, snode->LoopCondition);
|
||||
out.Break();
|
||||
PrintNodes(out, snode->LoopBumper);
|
||||
out.Break();
|
||||
PrintNodes(out, snode->LoopStatement);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintIfStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_IfStmt *snode = (ZCC_IfStmt *)node;
|
||||
out.Break();
|
||||
out.Open("if-stmt");
|
||||
PrintNodes(out, snode->Condition);
|
||||
out.Break();
|
||||
PrintNodes(out, snode->TruePath);
|
||||
out.Break();
|
||||
PrintNodes(out, snode->FalsePath);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintSwitchStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_SwitchStmt *snode = (ZCC_SwitchStmt *)node;
|
||||
out.Break();
|
||||
out.Open("switch-stmt");
|
||||
PrintNodes(out, snode->Condition);
|
||||
out.Break();
|
||||
PrintNodes(out, snode->Content, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintCaseStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_CaseStmt *snode = (ZCC_CaseStmt *)node;
|
||||
out.Break();
|
||||
out.Open("case-stmt");
|
||||
PrintNodes(out, snode->Condition, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void BadAssignOp(FLispString &out, int op)
|
||||
{
|
||||
char buf[32];
|
||||
size_t len = mysnprintf(buf, countof(buf), "assign-op-%d", op);
|
||||
out.Add(buf, len);
|
||||
}
|
||||
|
||||
static void PrintAssignStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_AssignStmt *snode = (ZCC_AssignStmt *)node;
|
||||
out.Open("assign-stmt");
|
||||
switch (snode->AssignOp)
|
||||
{
|
||||
case ZCC_EQ: out.AddChar('='); break;
|
||||
case ZCC_MULEQ: out.Add("*=", 2); break;
|
||||
case ZCC_DIVEQ: out.Add("/=", 2); break;
|
||||
case ZCC_MODEQ: out.Add("%=", 2); break;
|
||||
case ZCC_ADDEQ: out.Add("+=", 2); break;
|
||||
case ZCC_SUBEQ: out.Add("-=", 2); break;
|
||||
case ZCC_LSHEQ: out.Add("<<=", 2); break;
|
||||
case ZCC_RSHEQ: out.Add(">>=", 2); break;
|
||||
case ZCC_ANDEQ: out.Add("&=", 2); break;
|
||||
case ZCC_OREQ: out.Add("|=", 2); break;
|
||||
case ZCC_XOREQ: out.Add("^=", 2); break;
|
||||
default: BadAssignOp(out, snode->AssignOp); break;
|
||||
}
|
||||
PrintNodes(out, snode->Dests);
|
||||
PrintNodes(out, snode->Sources);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintLocalVarStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_LocalVarStmt *snode = (ZCC_LocalVarStmt *)node;
|
||||
out.Open("local-var-stmt");
|
||||
PrintNodes(out, snode->Type);
|
||||
PrintNodes(out, snode->Vars);
|
||||
PrintNodes(out, snode->Inits);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintFuncParamDecl(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_FuncParamDecl *dnode = (ZCC_FuncParamDecl *)node;
|
||||
out.Break();
|
||||
out.Open("func-param-decl");
|
||||
PrintNodes(out, dnode->Type);
|
||||
out.AddName(dnode->Name);
|
||||
out.AddHex(dnode->Flags);
|
||||
PrintNodes(out, dnode->Default);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintConstantDef(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_ConstantDef *dnode = (ZCC_ConstantDef *)node;
|
||||
out.Break();
|
||||
out.Open("constant-def");
|
||||
out.AddName(dnode->NodeName);
|
||||
PrintNodes(out, dnode->Value, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintDeclarator(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_Declarator *dnode = (ZCC_Declarator *)node;
|
||||
out.Break();
|
||||
out.Open("declarator");
|
||||
out.AddHex(dnode->Flags);
|
||||
PrintNodes(out, dnode->Type);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintVarDeclarator(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_VarDeclarator *dnode = (ZCC_VarDeclarator *)node;
|
||||
out.Break();
|
||||
out.Open("var-declarator");
|
||||
out.AddHex(dnode->Flags);
|
||||
PrintNodes(out, dnode->Type);
|
||||
PrintNodes(out, dnode->Names);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintFuncDeclarator(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
ZCC_FuncDeclarator *dnode = (ZCC_FuncDeclarator *)node;
|
||||
out.Break();
|
||||
out.Open("func-declarator");
|
||||
out.AddHex(dnode->Flags);
|
||||
PrintNodes(out, dnode->Type);
|
||||
out.AddName(dnode->Name);
|
||||
PrintNodes(out, dnode->Params);
|
||||
PrintNodes(out, dnode->Body, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintFlagStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
auto dnode = (ZCC_FlagStmt *)node;
|
||||
out.Break();
|
||||
out.Open("flag-stmt");
|
||||
PrintNodes(out, dnode->name, false);
|
||||
out.AddInt(dnode->set);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
static void PrintPropertyStmt(FLispString &out, ZCC_TreeNode *node)
|
||||
{
|
||||
auto dnode = (ZCC_PropertyStmt *)node;
|
||||
out.Break();
|
||||
out.Open("property-stmt");
|
||||
PrintNodes(out, dnode->Prop, false);
|
||||
PrintNodes(out, dnode->Values, false);
|
||||
out.Close();
|
||||
}
|
||||
|
||||
void (* const TreeNodePrinter[NUM_AST_NODE_TYPES])(FLispString &, ZCC_TreeNode *) =
|
||||
{
|
||||
PrintIdentifier,
|
||||
PrintClass,
|
||||
PrintStruct,
|
||||
PrintEnum,
|
||||
PrintEnumTerminator,
|
||||
PrintStates,
|
||||
PrintStatePart,
|
||||
PrintStateLabel,
|
||||
PrintStateStop,
|
||||
PrintStateWait,
|
||||
PrintStateFail,
|
||||
PrintStateLoop,
|
||||
PrintStateGoto,
|
||||
PrintStateLine,
|
||||
PrintVarName,
|
||||
PrintType,
|
||||
PrintBasicType,
|
||||
PrintMapType,
|
||||
PrintDynArrayType,
|
||||
PrintClassType,
|
||||
PrintExpression,
|
||||
PrintExprID,
|
||||
PrintExprTypeRef,
|
||||
PrintExprConstant,
|
||||
PrintExprFuncCall,
|
||||
PrintExprMemberAccess,
|
||||
PrintExprUnary,
|
||||
PrintExprBinary,
|
||||
PrintExprTrinary,
|
||||
PrintFuncParam,
|
||||
PrintStatement,
|
||||
PrintCompoundStmt,
|
||||
PrintContinueStmt,
|
||||
PrintBreakStmt,
|
||||
PrintReturnStmt,
|
||||
PrintExpressionStmt,
|
||||
PrintIterationStmt,
|
||||
PrintIfStmt,
|
||||
PrintSwitchStmt,
|
||||
PrintCaseStmt,
|
||||
PrintAssignStmt,
|
||||
PrintLocalVarStmt,
|
||||
PrintFuncParamDecl,
|
||||
PrintConstantDef,
|
||||
PrintDeclarator,
|
||||
PrintVarDeclarator,
|
||||
PrintFuncDeclarator,
|
||||
PrintDefault,
|
||||
PrintFlagStmt,
|
||||
PrintPropertyStmt
|
||||
};
|
||||
|
||||
FString ZCC_PrintAST(ZCC_TreeNode *root)
|
||||
{
|
||||
FLispString out;
|
||||
PrintNodes(out, root);
|
||||
return out;
|
||||
}
|
1613
src/scripting/zscript/zcc-parse.lemon
Normal file
1613
src/scripting/zscript/zcc-parse.lemon
Normal file
File diff suppressed because it is too large
Load diff
2243
src/scripting/zscript/zcc_compile.cpp
Normal file
2243
src/scripting/zscript/zcc_compile.cpp
Normal file
File diff suppressed because it is too large
Load diff
149
src/scripting/zscript/zcc_compile.h
Normal file
149
src/scripting/zscript/zcc_compile.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
#ifndef ZCC_COMPILE_H
|
||||
#define ZCC_COMPILE_H
|
||||
|
||||
struct Baggage;
|
||||
struct FPropertyInfo;
|
||||
class AActor;
|
||||
class FxExpression;
|
||||
|
||||
struct ZCC_StructWork
|
||||
{
|
||||
PSymbolTable TreeNodes;
|
||||
ZCC_Struct *strct;
|
||||
ZCC_Class *OuterDef;
|
||||
PClass *Outer;
|
||||
PSymbolTreeNode *node;
|
||||
TArray<ZCC_Enum *> Enums;
|
||||
TArray<ZCC_ConstantDef *> Constants;
|
||||
TArray<ZCC_VarDeclarator *> Fields;
|
||||
|
||||
ZCC_StructWork(ZCC_Struct * s, PSymbolTreeNode *n, ZCC_Class *outer)
|
||||
{
|
||||
strct = s;
|
||||
node = n;
|
||||
OuterDef = outer;
|
||||
Outer = nullptr;
|
||||
};
|
||||
|
||||
FName NodeName() const
|
||||
{
|
||||
return strct->NodeName;
|
||||
}
|
||||
|
||||
PStruct *Type()
|
||||
{
|
||||
return strct->Type;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct ZCC_ClassWork
|
||||
{
|
||||
ZCC_Class *cls;
|
||||
PSymbolTable TreeNodes;
|
||||
PSymbolTreeNode *node;
|
||||
TArray<ZCC_Enum *> Enums;
|
||||
TArray<ZCC_ConstantDef *> Constants;
|
||||
TArray<ZCC_VarDeclarator *> Fields;
|
||||
TArray<ZCC_Default *> Defaults;
|
||||
TArray<ZCC_FuncDeclarator *> Functions;
|
||||
TArray<ZCC_States *> States;
|
||||
|
||||
ZCC_ClassWork(ZCC_Class * s, PSymbolTreeNode *n)
|
||||
{
|
||||
cls = s;
|
||||
node = n;
|
||||
}
|
||||
|
||||
FName NodeName() const
|
||||
{
|
||||
return cls->NodeName;
|
||||
}
|
||||
|
||||
PClass *Type()
|
||||
{
|
||||
return cls->Type;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct ZCC_ConstantWork
|
||||
{
|
||||
ZCC_ConstantDef *node;
|
||||
PSymbolTable *outputtable;
|
||||
};
|
||||
|
||||
class ZCCCompiler
|
||||
{
|
||||
public:
|
||||
ZCCCompiler(ZCC_AST &tree, DObject *outer, PSymbolTable &symbols, PSymbolTable &outsymbols);
|
||||
~ZCCCompiler();
|
||||
int Compile();
|
||||
|
||||
private:
|
||||
void ProcessClass(ZCC_Class *node, PSymbolTreeNode *tnode);
|
||||
void ProcessStruct(ZCC_Struct *node, PSymbolTreeNode *tnode, ZCC_Class *outer);
|
||||
void CreateStructTypes();
|
||||
void CreateClassTypes();
|
||||
void CopyConstants(TArray<ZCC_ConstantWork> &dest, TArray<ZCC_ConstantDef*> &Constants, PSymbolTable *ot);
|
||||
void CompileAllConstants();
|
||||
void AddConstant(ZCC_ConstantWork &constant);
|
||||
bool CompileConstant(ZCC_ConstantDef *def, PSymbolTable *Symbols);
|
||||
|
||||
void CompileAllFields();
|
||||
bool CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct);
|
||||
FString FlagsToString(uint32_t flags);
|
||||
PType *DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes);
|
||||
PType *ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym);
|
||||
PType *ResolveUserType(ZCC_BasicType *type, PSymbolTable *sym);
|
||||
|
||||
void InitDefaults();
|
||||
void ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg);
|
||||
void ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *flg, Baggage &bag);
|
||||
void DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *pex, AActor *defaults, Baggage &bag);
|
||||
int GetInt(ZCC_Expression *expr);
|
||||
double GetDouble(ZCC_Expression *expr);
|
||||
const char *GetString(ZCC_Expression *expr, bool silent = false);
|
||||
|
||||
void InitFunctions();
|
||||
void CompileStates();
|
||||
FxExpression *SetupActionFunction(PClassActor *cls, ZCC_TreeNode *sl);
|
||||
|
||||
TArray<ZCC_ConstantDef *> Constants;
|
||||
TArray<ZCC_StructWork *> Structs;
|
||||
TArray<ZCC_ClassWork *> Classes;
|
||||
|
||||
PSymbolTreeNode *AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents = false);
|
||||
|
||||
ZCC_Expression *Simplify(ZCC_Expression *root, PSymbolTable *Symbols);
|
||||
ZCC_Expression *SimplifyUnary(ZCC_ExprUnary *unary, PSymbolTable *Symbols);
|
||||
ZCC_Expression *SimplifyBinary(ZCC_ExprBinary *binary, PSymbolTable *Symbols);
|
||||
ZCC_Expression *SimplifyMemberAccess(ZCC_ExprMemberAccess *dotop, PSymbolTable *Symbols);
|
||||
ZCC_Expression *SimplifyFunctionCall(ZCC_ExprFuncCall *callop, PSymbolTable *Symbols);
|
||||
ZCC_OpProto *PromoteUnary(EZCCExprType op, ZCC_Expression *&expr);
|
||||
ZCC_OpProto *PromoteBinary(EZCCExprType op, ZCC_Expression *&left, ZCC_Expression *&right);
|
||||
|
||||
ZCC_Expression *ApplyConversion(ZCC_Expression *expr, const PType::Conversion **route, int routelen);
|
||||
ZCC_Expression *AddCastNode(PType *type, ZCC_Expression *expr);
|
||||
|
||||
ZCC_Expression *IdentifyIdentifier(ZCC_ExprID *idnode, PSymbolTable *sym);
|
||||
ZCC_Expression *NodeFromSymbol(PSymbol *sym, ZCC_Expression *source, PSymbolTable *table);
|
||||
ZCC_ExprConstant *NodeFromSymbolConst(PSymbolConst *sym, ZCC_Expression *idnode);
|
||||
ZCC_ExprTypeRef *NodeFromSymbolType(PSymbolType *sym, ZCC_Expression *idnode);
|
||||
|
||||
|
||||
void Warn(ZCC_TreeNode *node, const char *msg, ...);
|
||||
void Error(ZCC_TreeNode *node, const char *msg, ...);
|
||||
void MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr);
|
||||
|
||||
DObject *Outer;
|
||||
PSymbolTable *GlobalTreeNodes;
|
||||
PSymbolTable *OutputSymbols;
|
||||
ZCC_AST &AST;
|
||||
int ErrorCount;
|
||||
int WarnCount;
|
||||
};
|
||||
|
||||
void ZCC_InitConversions();
|
||||
|
||||
#endif
|
517
src/scripting/zscript/zcc_expr.cpp
Normal file
517
src/scripting/zscript/zcc_expr.cpp
Normal file
|
@ -0,0 +1,517 @@
|
|||
/*
|
||||
** zcc_expr.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include <math.h>
|
||||
#include "dobject.h"
|
||||
#include "sc_man.h"
|
||||
#include "c_console.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "w_wad.h"
|
||||
#include "cmdlib.h"
|
||||
#include "m_alloc.h"
|
||||
#include "zcc_parser.h"
|
||||
#include "templates.h"
|
||||
|
||||
#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b))
|
||||
|
||||
static void FtoD(ZCC_ExprConstant *expr, FSharedStringArena &str_arena);
|
||||
|
||||
ZCC_OpInfoType ZCC_OpInfo[PEX_COUNT_OF] =
|
||||
{
|
||||
#define xx(a,z) { #a, NULL },
|
||||
#include "zcc_exprlist.h"
|
||||
};
|
||||
|
||||
// Structures used for initializing operator overloads
|
||||
struct OpProto1
|
||||
{
|
||||
EZCCExprType Op;
|
||||
PType **Type;
|
||||
EvalConst1op EvalConst;
|
||||
};
|
||||
|
||||
struct OpProto2
|
||||
{
|
||||
EZCCExprType Op;
|
||||
PType **Res, **Ltype, **Rtype;
|
||||
EvalConst2op EvalConst;
|
||||
};
|
||||
|
||||
static struct FreeOpInfoProtos
|
||||
{
|
||||
~FreeOpInfoProtos()
|
||||
{
|
||||
for (size_t i = 0; i < countof(ZCC_OpInfo); ++i)
|
||||
{
|
||||
ZCC_OpInfo[i].FreeAllProtos();
|
||||
}
|
||||
}
|
||||
} ProtoFreeer;
|
||||
|
||||
void ZCC_OpInfoType::FreeAllProtos()
|
||||
{
|
||||
for (ZCC_OpProto *proto = Protos, *next = NULL; proto != NULL; proto = next)
|
||||
{
|
||||
next = proto->Next;
|
||||
delete proto;
|
||||
}
|
||||
Protos = NULL;
|
||||
}
|
||||
|
||||
void ZCC_OpInfoType::AddProto(PType *res, PType *optype, EvalConst1op evalconst)
|
||||
{
|
||||
ZCC_OpProto *proto = new ZCC_OpProto(res, optype, NULL);
|
||||
proto->EvalConst1 = evalconst;
|
||||
proto->Next = Protos;
|
||||
Protos = proto;
|
||||
}
|
||||
|
||||
void ZCC_OpInfoType::AddProto(PType *res, PType *ltype, PType *rtype, EvalConst2op evalconst)
|
||||
{
|
||||
assert(ltype != NULL);
|
||||
ZCC_OpProto *proto = new ZCC_OpProto(res, ltype, rtype);
|
||||
proto->EvalConst2 = evalconst;
|
||||
proto->Next = Protos;
|
||||
Protos = proto;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ZCC_OpInfoType :: FindBestProto (Unary)
|
||||
//
|
||||
// Finds the "best" prototype for this operand type. Best is defined as the
|
||||
// one that requires the fewest conversions. Also returns the conversion
|
||||
// route necessary to get from the input type to the desired type.
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ZCC_OpProto *ZCC_OpInfoType::FindBestProto(PType *optype, const PType::Conversion **route, int &numslots)
|
||||
{
|
||||
assert(optype != NULL);
|
||||
|
||||
const PType::Conversion *routes[2][CONVERSION_ROUTE_SIZE];
|
||||
const PType::Conversion **best_route = NULL;
|
||||
int cur_route = 0;
|
||||
ZCC_OpProto *best_proto = NULL;
|
||||
int best_dist = INT_MAX;
|
||||
|
||||
// Find the best prototype.
|
||||
for (ZCC_OpProto *proto = Protos; best_dist != 0 && proto != NULL; proto = proto->Next)
|
||||
{
|
||||
if (proto->Type2 != NULL)
|
||||
{ // Not a unary prototype.
|
||||
continue;
|
||||
}
|
||||
int dist = optype->FindConversion(proto->Type1, routes[cur_route], CONVERSION_ROUTE_SIZE);
|
||||
if (dist >= 0 && dist < best_dist)
|
||||
{
|
||||
best_dist = dist;
|
||||
best_proto = proto;
|
||||
best_route = routes[cur_route];
|
||||
cur_route ^= 1;
|
||||
}
|
||||
}
|
||||
// Copy best conversion route to the caller's array.
|
||||
if (best_route != NULL && route != NULL && numslots > 0)
|
||||
{
|
||||
numslots = MIN(numslots, best_dist);
|
||||
if (numslots > 0)
|
||||
{
|
||||
memcpy(route, best_route, sizeof(*route) * numslots);
|
||||
}
|
||||
}
|
||||
return best_proto;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ZCC_OpInfoType :: FindBestProto (Binary)
|
||||
//
|
||||
// Finds the "best" prototype for the given operand types. Here, best is
|
||||
// defined as the one that requires the fewest conversions for *one* of the
|
||||
// operands. For prototypes with matching distances, the first one found
|
||||
// is used. ZCC_InitOperators() initializes the prototypes in order such
|
||||
// that this will result in the precedences: double > uint > int
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
ZCC_OpProto *ZCC_OpInfoType::FindBestProto(
|
||||
PType *left, const PType::Conversion **route1, int &numslots1,
|
||||
PType *right, const PType::Conversion **route2, int &numslots2)
|
||||
{
|
||||
assert(left != NULL && right != NULL);
|
||||
|
||||
const PType::Conversion *routes[2][2][CONVERSION_ROUTE_SIZE];
|
||||
const PType::Conversion **best_route1 = NULL, **best_route2 = NULL;
|
||||
int cur_route1 = 0, cur_route2 = 0;
|
||||
int best_dist1 = INT_MAX, best_dist2 = INT_MAX;
|
||||
|
||||
ZCC_OpProto *best_proto = NULL;
|
||||
int best_low_dist = INT_MAX;
|
||||
|
||||
for (ZCC_OpProto *proto = Protos; best_low_dist != 0 && proto != NULL; proto = proto->Next)
|
||||
{
|
||||
if (proto->Type2 == NULL)
|
||||
{ // Not a binary prototype
|
||||
continue;
|
||||
}
|
||||
int dist1 = left->FindConversion(proto->Type1, routes[0][cur_route1], CONVERSION_ROUTE_SIZE);
|
||||
int dist2 = right->FindConversion(proto->Type2, routes[1][cur_route2], CONVERSION_ROUTE_SIZE);
|
||||
if (dist1 < 0 || dist2 < 0)
|
||||
{ // one or both operator types are unreachable
|
||||
continue;
|
||||
}
|
||||
// Do not count F32->F64 conversions in the distance comparisons. If we do, then
|
||||
// [[float32 (op) int]] will choose the integer version instead of the floating point
|
||||
// version, which we do not want.
|
||||
int test_dist1 = dist1, test_dist2 = dist2;
|
||||
if (test_dist1 > 0 && routes[0][cur_route1][0]->ConvertConstant == FtoD)
|
||||
{
|
||||
test_dist1--;
|
||||
}
|
||||
if (test_dist2 > 0 && routes[1][cur_route2][0]->ConvertConstant == FtoD)
|
||||
{
|
||||
test_dist2--;
|
||||
}
|
||||
int dist = MIN(test_dist1, test_dist2);
|
||||
if (dist < best_low_dist)
|
||||
{
|
||||
best_low_dist = dist;
|
||||
best_proto = proto;
|
||||
best_dist1 = dist1;
|
||||
best_dist2 = dist2;
|
||||
best_route1 = routes[0][cur_route1];
|
||||
best_route2 = routes[1][cur_route2];
|
||||
cur_route1 ^= 1;
|
||||
cur_route2 ^= 1;
|
||||
}
|
||||
}
|
||||
// Copy best conversion route to the caller's arrays.
|
||||
if (best_route1 != NULL && route1 != NULL && numslots1 > 0)
|
||||
{
|
||||
numslots1 = MIN(numslots1, best_dist1);
|
||||
if (numslots1 > 0)
|
||||
{
|
||||
memcpy(route1, best_route1, sizeof(*route1) * numslots1);
|
||||
}
|
||||
}
|
||||
if (best_route2 != NULL && route2 != NULL && numslots2 > 0)
|
||||
{
|
||||
numslots2 = MIN(numslots2, best_dist2);
|
||||
if (numslots2 > 0)
|
||||
{
|
||||
memcpy(route2, best_route2, sizeof(*route2) * numslots2);
|
||||
}
|
||||
}
|
||||
return best_proto;
|
||||
}
|
||||
|
||||
static ZCC_ExprConstant *EvalIdentity(ZCC_ExprConstant *val)
|
||||
{
|
||||
return val;
|
||||
}
|
||||
|
||||
|
||||
static ZCC_ExprConstant *EvalConcat(ZCC_ExprConstant *l, ZCC_ExprConstant *r, FSharedStringArena &strings)
|
||||
{
|
||||
FString str = *l->StringVal + *r->StringVal;
|
||||
l->StringVal = strings.Alloc(str);
|
||||
return l;
|
||||
}
|
||||
|
||||
static ZCC_ExprConstant *EvalLTGTEQSInt32(ZCC_ExprConstant *l, ZCC_ExprConstant *r, FSharedStringArena &)
|
||||
{
|
||||
l->IntVal = l->IntVal < r->IntVal ? -1 : l->IntVal == r->IntVal ? 0 : 1;
|
||||
return l;
|
||||
}
|
||||
|
||||
static ZCC_ExprConstant *EvalLTGTEQUInt32(ZCC_ExprConstant *l, ZCC_ExprConstant *r, FSharedStringArena &)
|
||||
{
|
||||
l->IntVal = l->UIntVal < r->UIntVal ? -1 : l->UIntVal == r->UIntVal ? 0 : 1;
|
||||
l->Type = TypeSInt32;
|
||||
return l;
|
||||
}
|
||||
|
||||
static ZCC_ExprConstant *EvalLTGTEQFloat64(ZCC_ExprConstant *l, ZCC_ExprConstant *r, FSharedStringArena &)
|
||||
{
|
||||
l->IntVal = l->DoubleVal < r->DoubleVal ? -1 : l->DoubleVal == r->DoubleVal ? 0 : 1;
|
||||
l->Type = TypeSInt32;
|
||||
return l;
|
||||
}
|
||||
|
||||
void ZCC_InitOperators()
|
||||
{
|
||||
// Prototypes are added from lowest to highest conversion precedence.
|
||||
|
||||
// Unary operators
|
||||
static const OpProto1 UnaryOpInit[] =
|
||||
{
|
||||
{ PEX_PostInc , (PType **)&TypeSInt32, EvalIdentity },
|
||||
{ PEX_PostInc , (PType **)&TypeUInt32, EvalIdentity },
|
||||
{ PEX_PostInc , (PType **)&TypeFloat64, EvalIdentity },
|
||||
|
||||
{ PEX_PostDec , (PType **)&TypeSInt32, EvalIdentity },
|
||||
{ PEX_PostDec , (PType **)&TypeUInt32, EvalIdentity },
|
||||
{ PEX_PostDec , (PType **)&TypeFloat64, EvalIdentity },
|
||||
|
||||
{ PEX_PreInc , (PType **)&TypeSInt32, [](auto *val) { val->IntVal += 1; return val; } },
|
||||
{ PEX_PreInc , (PType **)&TypeUInt32, [](auto *val) { val->UIntVal += 1; return val; } },
|
||||
{ PEX_PreInc , (PType **)&TypeFloat64, [](auto *val) { val->DoubleVal += 1; return val; } },
|
||||
|
||||
{ PEX_PreDec , (PType **)&TypeSInt32, [](auto *val) { val->IntVal -= 1; return val; } },
|
||||
{ PEX_PreDec , (PType **)&TypeUInt32, [](auto *val) { val->UIntVal -= 1; return val; } },
|
||||
{ PEX_PreDec , (PType **)&TypeFloat64, [](auto *val) { val->DoubleVal -= 1; return val; } },
|
||||
|
||||
{ PEX_Negate , (PType **)&TypeSInt32, [](auto *val) { val->IntVal = -val->IntVal; return val; } },
|
||||
{ PEX_Negate , (PType **)&TypeFloat64, [](auto *val) { val->DoubleVal = -val->DoubleVal; return val; } },
|
||||
|
||||
{ PEX_AntiNegate , (PType **)&TypeSInt32, EvalIdentity },
|
||||
{ PEX_AntiNegate , (PType **)&TypeUInt32, EvalIdentity },
|
||||
{ PEX_AntiNegate , (PType **)&TypeFloat64, EvalIdentity },
|
||||
|
||||
{ PEX_BitNot , (PType **)&TypeSInt32, [](auto *val) { val->IntVal = ~val->IntVal; return val; } },
|
||||
{ PEX_BitNot , (PType **)&TypeUInt32, [](auto *val) { val->UIntVal = ~val->UIntVal; return val; } },
|
||||
|
||||
{ PEX_BoolNot , (PType **)&TypeBool, [](auto *val) { val->IntVal = !val->IntVal; return val; } },
|
||||
};
|
||||
for (size_t i = 0; i < countof(UnaryOpInit); ++i)
|
||||
{
|
||||
ZCC_OpInfo[UnaryOpInit[i].Op].AddProto(*UnaryOpInit[i].Type, *UnaryOpInit[i].Type, UnaryOpInit[i].EvalConst);
|
||||
}
|
||||
|
||||
// Binary operators
|
||||
static const OpProto2 BinaryOpInit[] =
|
||||
{
|
||||
{ PEX_Add , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal += r->IntVal; return l; } },
|
||||
{ PEX_Add , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal += r->UIntVal; return l; } },
|
||||
{ PEX_Add , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal += r->DoubleVal; return l; } },
|
||||
|
||||
{ PEX_Sub , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal -= r->IntVal; return l; } },
|
||||
{ PEX_Sub , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal -= r->UIntVal; return l; } },
|
||||
{ PEX_Sub , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal -= r->DoubleVal; return l; } },
|
||||
|
||||
{ PEX_Mul , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal *= r->IntVal; return l; } },
|
||||
{ PEX_Mul , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal *= r->UIntVal; return l; } },
|
||||
{ PEX_Mul , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal *= r->DoubleVal; return l; } },
|
||||
|
||||
{ PEX_Div , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal /= r->IntVal; return l; } },
|
||||
{ PEX_Div , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal /= r->UIntVal; return l; } },
|
||||
{ PEX_Div , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal /= r->DoubleVal; return l; } },
|
||||
|
||||
{ PEX_Mod , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal %= r->IntVal; return l; } },
|
||||
{ PEX_Mod , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal %= r->UIntVal; return l; } },
|
||||
{ PEX_Mod , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal = luai_nummod(l->DoubleVal, r->DoubleVal); return l; } },
|
||||
|
||||
{ PEX_Pow , (PType **)&TypeFloat64, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->DoubleVal = pow(l->DoubleVal, r->DoubleVal); return l; } },
|
||||
|
||||
{ PEX_Concat , (PType **)&TypeString, (PType **)&TypeString, (PType **)&TypeString, EvalConcat },
|
||||
|
||||
{ PEX_BitAnd , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal &= r->IntVal; return l; } },
|
||||
{ PEX_BitAnd , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal &= r->UIntVal; return l; } },
|
||||
|
||||
{ PEX_BitOr , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal |= r->IntVal; return l; } },
|
||||
{ PEX_BitOr , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal |= r->UIntVal; return l; } },
|
||||
|
||||
{ PEX_BitXor , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal ^= r->IntVal; return l; } },
|
||||
{ PEX_BitXor , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal ^= r->UIntVal; return l; } },
|
||||
|
||||
{ PEX_BoolAnd , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal && r->IntVal; l->Type = TypeBool; return l; } },
|
||||
{ PEX_BoolAnd , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal && r->UIntVal; l->Type = TypeBool; return l; } },
|
||||
|
||||
{ PEX_BoolOr , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal || r->IntVal; l->Type = TypeBool; return l; } },
|
||||
{ PEX_BoolOr , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal || r->UIntVal; l->Type = TypeBool; return l; } },
|
||||
|
||||
{ PEX_LeftShift , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->IntVal <<= r->UIntVal; return l; } },
|
||||
{ PEX_LeftShift , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal <<= r->UIntVal; return l; } },
|
||||
|
||||
{ PEX_RightShift , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->IntVal >>= r->UIntVal; return l; } },
|
||||
{ PEX_RightShift , (PType **)&TypeUInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->UIntVal >>= r->UIntVal; return l; } },
|
||||
|
||||
{ PEX_LT , (PType **)&TypeBool, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal < r->IntVal; l->Type = TypeBool; return l; } },
|
||||
{ PEX_LT , (PType **)&TypeBool, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal < r->UIntVal; l->Type = TypeBool; return l; } },
|
||||
{ PEX_LT , (PType **)&TypeBool, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal < r->DoubleVal; l->Type = TypeBool; return l; } },
|
||||
|
||||
{ PEX_LTEQ , (PType **)&TypeBool, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal <= r->IntVal; l->Type = TypeBool; return l; } },
|
||||
{ PEX_LTEQ , (PType **)&TypeBool, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal <= r->UIntVal; l->Type = TypeBool; return l; } },
|
||||
{ PEX_LTEQ , (PType **)&TypeBool, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal <= r->DoubleVal; l->Type = TypeBool; return l; } },
|
||||
|
||||
{ PEX_EQEQ , (PType **)&TypeBool, (PType **)&TypeSInt32, (PType **)&TypeSInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->IntVal == r->IntVal; l->Type = TypeBool; return l; } },
|
||||
{ PEX_EQEQ , (PType **)&TypeBool, (PType **)&TypeUInt32, (PType **)&TypeUInt32, [](auto *l, auto *r, auto &) { l->IntVal = l->UIntVal == r->UIntVal; l->Type = TypeBool; return l; } },
|
||||
{ PEX_EQEQ , (PType **)&TypeBool, (PType **)&TypeFloat64, (PType **)&TypeFloat64, [](auto *l, auto *r, auto &) { l->IntVal = l->DoubleVal == r->DoubleVal; l->Type = TypeBool; return l; } },
|
||||
|
||||
{ PEX_LTGTEQ , (PType **)&TypeSInt32, (PType **)&TypeSInt32, (PType **)&TypeSInt32, EvalLTGTEQSInt32 },
|
||||
{ PEX_LTGTEQ , (PType **)&TypeSInt32, (PType **)&TypeUInt32, (PType **)&TypeUInt32, EvalLTGTEQUInt32 },
|
||||
{ PEX_LTGTEQ , (PType **)&TypeSInt32, (PType **)&TypeFloat64, (PType **)&TypeFloat64, EvalLTGTEQFloat64 },
|
||||
};
|
||||
for (size_t i = 0; i < countof(BinaryOpInit); ++i)
|
||||
{
|
||||
ZCC_OpInfo[BinaryOpInit[i].Op].AddProto(*BinaryOpInit[i].Res, *BinaryOpInit[i].Ltype, *BinaryOpInit[i].Rtype, BinaryOpInit[i].EvalConst);
|
||||
}
|
||||
}
|
||||
|
||||
static void IntToS32(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
// Integers always fill out the full sized 32-bit field, so converting
|
||||
// from a smaller sized integer to a 32-bit one is as simple as changing
|
||||
// the type field.
|
||||
expr->Type = TypeSInt32;
|
||||
}
|
||||
|
||||
static void S32toS8(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->IntVal = ((expr->IntVal << 24) >> 24);
|
||||
expr->Type = TypeSInt8;
|
||||
}
|
||||
|
||||
static void S32toS16(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->IntVal = ((expr->IntVal << 16) >> 16);
|
||||
expr->Type = TypeSInt16;
|
||||
}
|
||||
|
||||
static void S32toU8(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->IntVal &= 0xFF;
|
||||
expr->Type = TypeUInt8;
|
||||
}
|
||||
|
||||
static void S32toU16(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->IntVal &= 0xFFFF;
|
||||
expr->Type = TypeUInt16;
|
||||
}
|
||||
|
||||
static void S32toU32(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->Type = TypeUInt32;
|
||||
}
|
||||
|
||||
static void S32toD(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->DoubleVal = expr->IntVal;
|
||||
expr->Type = TypeFloat64;
|
||||
}
|
||||
|
||||
static void DtoS32(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->IntVal = (int)expr->DoubleVal;
|
||||
expr->Type = TypeSInt32;
|
||||
}
|
||||
|
||||
static void U32toD(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->DoubleVal = expr->UIntVal;
|
||||
expr->Type = TypeFloat64;
|
||||
}
|
||||
|
||||
static void DtoU32(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
expr->UIntVal = (unsigned int)expr->DoubleVal;
|
||||
expr->Type = TypeUInt32;
|
||||
}
|
||||
|
||||
static void FtoD(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
// Constant single precision numbers are stored as doubles.
|
||||
assert(expr->Type == TypeFloat32);
|
||||
expr->Type = TypeFloat64;
|
||||
}
|
||||
|
||||
static void DtoF(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
// Truncate double precision to single precision.
|
||||
float poop = (float)expr->DoubleVal;
|
||||
expr->DoubleVal = poop;
|
||||
expr->Type = TypeFloat32;
|
||||
}
|
||||
|
||||
static void S32toS(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
char str[16];
|
||||
int len = mysnprintf(str, countof(str), "%i", expr->IntVal);
|
||||
expr->StringVal = str_arena.Alloc(str, len);
|
||||
expr->Type = TypeString;
|
||||
}
|
||||
|
||||
static void U32toS(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
char str[16];
|
||||
int len = mysnprintf(str, countof(str), "%u", expr->UIntVal);
|
||||
expr->StringVal = str_arena.Alloc(str, len);
|
||||
expr->Type = TypeString;
|
||||
}
|
||||
|
||||
static void DtoS(ZCC_ExprConstant *expr, FSharedStringArena &str_arena)
|
||||
{
|
||||
// Convert to a string with enough precision such that converting
|
||||
// back to a double will not lose any data.
|
||||
char str[64];
|
||||
IGNORE_FORMAT_PRE
|
||||
int len = mysnprintf(str, countof(str), "%H", expr->DoubleVal);
|
||||
IGNORE_FORMAT_POST
|
||||
expr->StringVal = str_arena.Alloc(str, len);
|
||||
expr->Type = TypeString;
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
// ZCC_InitConversions
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
void ZCC_InitConversions()
|
||||
{
|
||||
TypeUInt8->AddConversion(TypeSInt32, IntToS32);
|
||||
TypeSInt8->AddConversion(TypeSInt32, IntToS32);
|
||||
TypeUInt16->AddConversion(TypeSInt32, IntToS32);
|
||||
TypeSInt16->AddConversion(TypeSInt32, IntToS32);
|
||||
|
||||
TypeUInt32->AddConversion(TypeSInt32, IntToS32);
|
||||
TypeUInt32->AddConversion(TypeFloat64, U32toD);
|
||||
TypeUInt32->AddConversion(TypeString, U32toS);
|
||||
|
||||
TypeSInt32->AddConversion(TypeUInt8, S32toU8);
|
||||
TypeSInt32->AddConversion(TypeSInt8, S32toS8);
|
||||
TypeSInt32->AddConversion(TypeSInt16, S32toS16);
|
||||
TypeSInt32->AddConversion(TypeUInt16, S32toU16);
|
||||
TypeSInt32->AddConversion(TypeUInt32, S32toU32);
|
||||
TypeSInt32->AddConversion(TypeFloat64, S32toD);
|
||||
TypeSInt32->AddConversion(TypeString, S32toS);
|
||||
|
||||
TypeFloat32->AddConversion(TypeFloat64, FtoD);
|
||||
|
||||
TypeFloat64->AddConversion(TypeUInt32, DtoU32);
|
||||
TypeFloat64->AddConversion(TypeSInt32, DtoS32);
|
||||
TypeFloat64->AddConversion(TypeFloat32, DtoF);
|
||||
TypeFloat64->AddConversion(TypeString, DtoS);
|
||||
}
|
57
src/scripting/zscript/zcc_exprlist.h
Normal file
57
src/scripting/zscript/zcc_exprlist.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
// Name n-ary
|
||||
xx(Nil, )
|
||||
|
||||
xx(ID, )
|
||||
xx(Super, )
|
||||
xx(Self, )
|
||||
xx(ConstValue, )
|
||||
xx(FuncCall, )
|
||||
xx(ArrayAccess, )
|
||||
xx(MemberAccess, )
|
||||
xx(TypeRef, )
|
||||
|
||||
xx(PostInc, )
|
||||
xx(PostDec, )
|
||||
|
||||
xx(PreInc, )
|
||||
xx(PreDec, )
|
||||
xx(Negate, )
|
||||
xx(AntiNegate, )
|
||||
xx(BitNot, )
|
||||
xx(BoolNot, )
|
||||
xx(SizeOf, )
|
||||
xx(AlignOf, )
|
||||
|
||||
xx(Add, )
|
||||
xx(Sub, )
|
||||
xx(Mul, )
|
||||
xx(Div, )
|
||||
xx(Mod, )
|
||||
xx(Pow, )
|
||||
xx(CrossProduct, )
|
||||
xx(DotProduct, )
|
||||
xx(LeftShift, )
|
||||
xx(RightShift, )
|
||||
xx(Concat, )
|
||||
|
||||
xx(LT, )
|
||||
xx(LTEQ, )
|
||||
xx(LTGTEQ, )
|
||||
xx(Is, )
|
||||
|
||||
xx(EQEQ, )
|
||||
xx(APREQ, )
|
||||
|
||||
xx(BitAnd, )
|
||||
xx(BitOr, )
|
||||
xx(BitXor, )
|
||||
xx(BoolAnd, )
|
||||
xx(BoolOr, )
|
||||
|
||||
xx(Scope, )
|
||||
|
||||
xx(Trinary, )
|
||||
|
||||
xx(Cast, )
|
||||
|
||||
#undef xx
|
412
src/scripting/zscript/zcc_parser.cpp
Normal file
412
src/scripting/zscript/zcc_parser.cpp
Normal file
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
** zcc_expr.cpp
|
||||
**
|
||||
**---------------------------------------------------------------------------
|
||||
** Copyright -2016 Randy Heit
|
||||
** All rights reserved.
|
||||
**
|
||||
** Redistribution and use in source and binary forms, with or without
|
||||
** modification, are permitted provided that the following conditions
|
||||
** are met:
|
||||
**
|
||||
** 1. Redistributions of source code must retain the above copyright
|
||||
** notice, this list of conditions and the following disclaimer.
|
||||
** 2. Redistributions in binary form must reproduce the above copyright
|
||||
** notice, this list of conditions and the following disclaimer in the
|
||||
** documentation and/or other materials provided with the distribution.
|
||||
** 3. The name of the author may not be used to endorse or promote products
|
||||
** derived from this software without specific prior written permission.
|
||||
**
|
||||
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
**---------------------------------------------------------------------------
|
||||
**
|
||||
*/
|
||||
|
||||
#include "dobject.h"
|
||||
#include "sc_man.h"
|
||||
#include "c_console.h"
|
||||
#include "c_dispatch.h"
|
||||
#include "w_wad.h"
|
||||
#include "cmdlib.h"
|
||||
#include "m_alloc.h"
|
||||
#include "i_system.h"
|
||||
#include "zcc_parser.h"
|
||||
#include "zcc_compile.h"
|
||||
|
||||
static FString ZCCTokenName(int terminal);
|
||||
|
||||
#include "zcc-parse.h"
|
||||
#include "zcc-parse.c"
|
||||
|
||||
struct TokenMapEntry
|
||||
{
|
||||
SWORD TokenType;
|
||||
WORD TokenName;
|
||||
TokenMapEntry(SWORD a, WORD b)
|
||||
: TokenType(a), TokenName(b)
|
||||
{ }
|
||||
};
|
||||
static TMap<SWORD, TokenMapEntry> TokenMap;
|
||||
static SWORD BackTokenMap[YYERRORSYMBOL]; // YYERRORSYMBOL immediately follows the terminals described by the grammar
|
||||
|
||||
#define TOKENDEF2(sc, zcc, name) { TokenMapEntry tme(zcc, name); TokenMap.Insert(sc, tme); } BackTokenMap[zcc] = sc
|
||||
#define TOKENDEF(sc, zcc) TOKENDEF2(sc, zcc, NAME_None)
|
||||
|
||||
static void InitTokenMap()
|
||||
{
|
||||
TOKENDEF ('=', ZCC_EQ);
|
||||
TOKENDEF (TK_MulEq, ZCC_MULEQ);
|
||||
TOKENDEF (TK_DivEq, ZCC_DIVEQ);
|
||||
TOKENDEF (TK_ModEq, ZCC_MODEQ);
|
||||
TOKENDEF (TK_AddEq, ZCC_ADDEQ);
|
||||
TOKENDEF (TK_SubEq, ZCC_SUBEQ);
|
||||
TOKENDEF (TK_LShiftEq, ZCC_LSHEQ);
|
||||
TOKENDEF (TK_RShiftEq, ZCC_RSHEQ);
|
||||
TOKENDEF (TK_AndEq, ZCC_ANDEQ);
|
||||
TOKENDEF (TK_OrEq, ZCC_OREQ);
|
||||
TOKENDEF (TK_XorEq, ZCC_XOREQ);
|
||||
TOKENDEF ('?', ZCC_QUESTION);
|
||||
TOKENDEF (':', ZCC_COLON);
|
||||
TOKENDEF (TK_OrOr, ZCC_OROR);
|
||||
TOKENDEF (TK_AndAnd, ZCC_ANDAND);
|
||||
TOKENDEF (TK_Eq, ZCC_EQEQ);
|
||||
TOKENDEF (TK_Neq, ZCC_NEQ);
|
||||
TOKENDEF (TK_ApproxEq, ZCC_APPROXEQ);
|
||||
TOKENDEF ('<', ZCC_LT);
|
||||
TOKENDEF ('>', ZCC_GT);
|
||||
TOKENDEF (TK_Leq, ZCC_LTEQ);
|
||||
TOKENDEF (TK_Geq, ZCC_GTEQ);
|
||||
TOKENDEF (TK_LtGtEq, ZCC_LTGTEQ);
|
||||
TOKENDEF (TK_Is, ZCC_IS);
|
||||
TOKENDEF (TK_DotDot, ZCC_DOTDOT);
|
||||
TOKENDEF (TK_Ellipsis, ZCC_ELLIPSIS);
|
||||
TOKENDEF ('|', ZCC_OR);
|
||||
TOKENDEF ('^', ZCC_XOR);
|
||||
TOKENDEF ('&', ZCC_AND);
|
||||
TOKENDEF (TK_LShift, ZCC_LSH);
|
||||
TOKENDEF (TK_RShift, ZCC_RSH);
|
||||
TOKENDEF ('-', ZCC_SUB);
|
||||
TOKENDEF ('+', ZCC_ADD);
|
||||
TOKENDEF ('*', ZCC_MUL);
|
||||
TOKENDEF ('/', ZCC_DIV);
|
||||
TOKENDEF ('%', ZCC_MOD);
|
||||
TOKENDEF (TK_Cross, ZCC_CROSSPROD);
|
||||
TOKENDEF (TK_Dot, ZCC_DOTPROD);
|
||||
TOKENDEF (TK_MulMul, ZCC_POW);
|
||||
TOKENDEF (TK_Incr, ZCC_ADDADD);
|
||||
TOKENDEF (TK_Decr, ZCC_SUBSUB);
|
||||
TOKENDEF ('.', ZCC_DOT);
|
||||
TOKENDEF ('(', ZCC_LPAREN);
|
||||
TOKENDEF (')', ZCC_RPAREN);
|
||||
TOKENDEF (TK_ColonColon, ZCC_SCOPE);
|
||||
TOKENDEF (';', ZCC_SEMICOLON);
|
||||
TOKENDEF (',', ZCC_COMMA);
|
||||
TOKENDEF (TK_Class, ZCC_CLASS);
|
||||
TOKENDEF (TK_Abstract, ZCC_ABSTRACT);
|
||||
TOKENDEF (TK_Native, ZCC_NATIVE);
|
||||
TOKENDEF (TK_Action, ZCC_ACTION);
|
||||
TOKENDEF (TK_Replaces, ZCC_REPLACES);
|
||||
TOKENDEF (TK_Static, ZCC_STATIC);
|
||||
TOKENDEF (TK_Private, ZCC_PRIVATE);
|
||||
TOKENDEF (TK_Protected, ZCC_PROTECTED);
|
||||
TOKENDEF (TK_Latent, ZCC_LATENT);
|
||||
TOKENDEF (TK_Final, ZCC_FINAL);
|
||||
TOKENDEF (TK_Meta, ZCC_META);
|
||||
TOKENDEF (TK_Deprecated, ZCC_DEPRECATED);
|
||||
TOKENDEF (TK_ReadOnly, ZCC_READONLY);
|
||||
TOKENDEF ('{', ZCC_LBRACE);
|
||||
TOKENDEF ('}', ZCC_RBRACE);
|
||||
TOKENDEF (TK_Struct, ZCC_STRUCT);
|
||||
TOKENDEF (TK_Enum, ZCC_ENUM);
|
||||
TOKENDEF2(TK_SByte, ZCC_SBYTE, NAME_sByte);
|
||||
TOKENDEF2(TK_Byte, ZCC_BYTE, NAME_Byte);
|
||||
TOKENDEF2(TK_Short, ZCC_SHORT, NAME_Short);
|
||||
TOKENDEF2(TK_UShort, ZCC_USHORT, NAME_uShort);
|
||||
TOKENDEF2(TK_Int, ZCC_INT, NAME_Int);
|
||||
TOKENDEF2(TK_UInt, ZCC_UINT, NAME_uInt);
|
||||
TOKENDEF2(TK_Bool, ZCC_BOOL, NAME_Bool);
|
||||
TOKENDEF2(TK_Float, ZCC_FLOAT, NAME_Float);
|
||||
TOKENDEF2(TK_Double, ZCC_DOUBLE, NAME_Double);
|
||||
TOKENDEF2(TK_String, ZCC_STRING, NAME_String);
|
||||
TOKENDEF2(TK_Vector, ZCC_VECTOR, NAME_Vector);
|
||||
TOKENDEF2(TK_Name, ZCC_NAME, NAME_Name);
|
||||
TOKENDEF2(TK_Map, ZCC_MAP, NAME_Map);
|
||||
TOKENDEF2(TK_Array, ZCC_ARRAY, NAME_Array);
|
||||
TOKENDEF (TK_Void, ZCC_VOID);
|
||||
TOKENDEF (TK_True, ZCC_TRUE);
|
||||
TOKENDEF (TK_False, ZCC_FALSE);
|
||||
TOKENDEF ('[', ZCC_LBRACKET);
|
||||
TOKENDEF (']', ZCC_RBRACKET);
|
||||
TOKENDEF (TK_In, ZCC_IN);
|
||||
TOKENDEF (TK_Out, ZCC_OUT);
|
||||
TOKENDEF (TK_Optional, ZCC_OPTIONAL);
|
||||
TOKENDEF (TK_Super, ZCC_SUPER);
|
||||
TOKENDEF (TK_Self, ZCC_SELF);
|
||||
TOKENDEF ('~', ZCC_TILDE);
|
||||
TOKENDEF ('!', ZCC_BANG);
|
||||
TOKENDEF (TK_SizeOf, ZCC_SIZEOF);
|
||||
TOKENDEF (TK_AlignOf, ZCC_ALIGNOF);
|
||||
TOKENDEF (TK_Continue, ZCC_CONTINUE);
|
||||
TOKENDEF (TK_Break, ZCC_BREAK);
|
||||
TOKENDEF (TK_Return, ZCC_RETURN);
|
||||
TOKENDEF (TK_Do, ZCC_DO);
|
||||
TOKENDEF (TK_For, ZCC_FOR);
|
||||
TOKENDEF (TK_While, ZCC_WHILE);
|
||||
TOKENDEF (TK_Until, ZCC_UNTIL);
|
||||
TOKENDEF (TK_If, ZCC_IF);
|
||||
TOKENDEF (TK_Else, ZCC_ELSE);
|
||||
TOKENDEF (TK_Switch, ZCC_SWITCH);
|
||||
TOKENDEF (TK_Case, ZCC_CASE);
|
||||
TOKENDEF2(TK_Default, ZCC_DEFAULT, NAME_Default);
|
||||
TOKENDEF (TK_Const, ZCC_CONST);
|
||||
TOKENDEF (TK_Stop, ZCC_STOP);
|
||||
TOKENDEF (TK_Wait, ZCC_WAIT);
|
||||
TOKENDEF (TK_Fail, ZCC_FAIL);
|
||||
TOKENDEF (TK_Loop, ZCC_LOOP);
|
||||
TOKENDEF (TK_Goto, ZCC_GOTO);
|
||||
TOKENDEF (TK_States, ZCC_STATES);
|
||||
TOKENDEF (TK_State, ZCC_STATE);
|
||||
TOKENDEF (TK_Color, ZCC_COLOR);
|
||||
TOKENDEF (TK_Sound, ZCC_SOUND);
|
||||
|
||||
TOKENDEF (TK_Identifier, ZCC_IDENTIFIER);
|
||||
TOKENDEF (TK_StringConst, ZCC_STRCONST);
|
||||
TOKENDEF (TK_NameConst, ZCC_NAMECONST);
|
||||
TOKENDEF (TK_IntConst, ZCC_INTCONST);
|
||||
TOKENDEF (TK_UIntConst, ZCC_UINTCONST);
|
||||
TOKENDEF (TK_FloatConst, ZCC_FLOATCONST);
|
||||
TOKENDEF (TK_NonWhitespace, ZCC_NWS);
|
||||
|
||||
TOKENDEF (TK_Bright, ZCC_BRIGHT);
|
||||
TOKENDEF (TK_Slow, ZCC_SLOW);
|
||||
TOKENDEF (TK_Fast, ZCC_FAST);
|
||||
TOKENDEF (TK_NoDelay, ZCC_NODELAY);
|
||||
TOKENDEF (TK_Offset, ZCC_OFFSET);
|
||||
TOKENDEF (TK_CanRaise, ZCC_CANRAISE);
|
||||
TOKENDEF (TK_Light, ZCC_LIGHT);
|
||||
|
||||
ZCC_InitOperators();
|
||||
ZCC_InitConversions();
|
||||
}
|
||||
#undef TOKENDEF
|
||||
#undef TOKENDEF2
|
||||
|
||||
static void ParseSingleFile(const char *filename, void *parser, ZCCParseState &state)
|
||||
{
|
||||
int tokentype;
|
||||
int lump;
|
||||
//bool failed;
|
||||
ZCCToken value;
|
||||
FScanner sc;
|
||||
|
||||
lump = Wads.CheckNumForFullName(filename, true);
|
||||
if (lump >= 0)
|
||||
{
|
||||
sc.OpenLumpNum(lump);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("Could not find script lump '%s'\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
state.sc = ≻
|
||||
while (sc.GetToken())
|
||||
{
|
||||
value.SourceLoc = sc.GetMessageLine();
|
||||
switch (sc.TokenType)
|
||||
{
|
||||
case TK_StringConst:
|
||||
value.String = state.Strings.Alloc(sc.String, sc.StringLen);
|
||||
tokentype = ZCC_STRCONST;
|
||||
break;
|
||||
|
||||
case TK_NameConst:
|
||||
value.Int = sc.Name;
|
||||
tokentype = ZCC_NAMECONST;
|
||||
break;
|
||||
|
||||
case TK_IntConst:
|
||||
value.Int = sc.Number;
|
||||
tokentype = ZCC_INTCONST;
|
||||
break;
|
||||
|
||||
case TK_UIntConst:
|
||||
value.Int = sc.Number;
|
||||
tokentype = ZCC_UINTCONST;
|
||||
break;
|
||||
|
||||
case TK_FloatConst:
|
||||
value.Float = sc.Float;
|
||||
tokentype = ZCC_FLOATCONST;
|
||||
break;
|
||||
|
||||
case TK_Identifier:
|
||||
value.Int = FName(sc.String);
|
||||
tokentype = ZCC_IDENTIFIER;
|
||||
break;
|
||||
|
||||
case TK_NonWhitespace:
|
||||
value.Int = FName(sc.String);
|
||||
tokentype = ZCC_NWS;
|
||||
break;
|
||||
|
||||
default:
|
||||
TokenMapEntry *zcctoken = TokenMap.CheckKey(sc.TokenType);
|
||||
if (zcctoken != nullptr)
|
||||
{
|
||||
tokentype = zcctoken->TokenType;
|
||||
value.Int = zcctoken->TokenName;
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.ScriptMessage("Unexpected token %s.\n", sc.TokenName(sc.TokenType).GetChars());
|
||||
goto parse_end;
|
||||
}
|
||||
break;
|
||||
}
|
||||
ZCCParse(parser, tokentype, value, &state);
|
||||
}
|
||||
parse_end:
|
||||
value.Int = -1;
|
||||
ZCCParse(parser, ZCC_EOF, value, &state);
|
||||
state.sc = nullptr;
|
||||
}
|
||||
|
||||
static void DoParse(int lumpnum)
|
||||
{
|
||||
if (TokenMap.CountUsed() == 0)
|
||||
{
|
||||
InitTokenMap();
|
||||
}
|
||||
|
||||
FScanner sc;
|
||||
void *parser;
|
||||
ZCCToken value;
|
||||
|
||||
parser = ZCCParseAlloc(malloc);
|
||||
ZCCParseState state;
|
||||
#ifdef _DEBUG
|
||||
FILE *f = fopen("trace.txt", "w");
|
||||
char prompt = '\0';
|
||||
ZCCParseTrace(f, &prompt);
|
||||
#endif
|
||||
|
||||
sc.OpenLumpNum(lumpnum);
|
||||
// parse all files from this list in one go.
|
||||
while (sc.GetString())
|
||||
{
|
||||
if (Wads.GetLumpFile(sc.LumpNum) == 0)
|
||||
{
|
||||
int includefile = Wads.GetLumpFile(Wads.CheckNumForFullName(sc.String, true));
|
||||
if (includefile != 0)
|
||||
{
|
||||
I_FatalError("File %s is overriding core lump %s.",
|
||||
Wads.GetWadFullName(includefile), sc.String);
|
||||
}
|
||||
}
|
||||
|
||||
ParseSingleFile(sc.String, parser, state);
|
||||
}
|
||||
|
||||
value.Int = -1;
|
||||
value.SourceLoc = sc.GetMessageLine();
|
||||
ZCCParse(parser, 0, value, &state);
|
||||
ZCCParseFree(parser, free);
|
||||
|
||||
{
|
||||
// Make a dump of the AST before running the compiler for diagnostic purposes.
|
||||
#ifdef _DEBUG
|
||||
if (f != NULL)
|
||||
{
|
||||
fclose(f);
|
||||
}
|
||||
FString ast = ZCC_PrintAST(state.TopNode);
|
||||
FString filename = Wads.GetLumpFullName(lumpnum);
|
||||
FString astfile = ExtractFileBase(filename, false);
|
||||
astfile << "-before.ast";
|
||||
f = fopen(astfile, "w");
|
||||
if (f != NULL)
|
||||
{
|
||||
fputs(ast.GetChars(), f);
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
PSymbolTable symtable;
|
||||
symtable.SetName("Global_Node");
|
||||
ZCCCompiler cc(state, NULL, symtable, GlobalSymbols);
|
||||
cc.Compile();
|
||||
}
|
||||
|
||||
void ParseScripts()
|
||||
{
|
||||
int lump, lastlump = 0;
|
||||
while ((lump = Wads.FindLump("ZSCRIPT", &lastlump)) != -1)
|
||||
{
|
||||
DoParse(lump);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
CCMD(parse)
|
||||
{
|
||||
if (argv.argc() == 2)
|
||||
{
|
||||
DoParse(argv[1]);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
static FString ZCCTokenName(int terminal)
|
||||
{
|
||||
if (terminal == ZCC_EOF)
|
||||
{
|
||||
return "end of file";
|
||||
}
|
||||
int sc_token;
|
||||
if (terminal > 0 && terminal < (int)countof(BackTokenMap))
|
||||
{
|
||||
sc_token = BackTokenMap[terminal];
|
||||
if (sc_token == 0)
|
||||
{ // This token was not initialized. Whoops!
|
||||
sc_token = -terminal;
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // This should never happen.
|
||||
sc_token = -terminal;
|
||||
}
|
||||
return FScanner::TokenName(sc_token);
|
||||
}
|
||||
|
||||
ZCC_TreeNode *ZCC_AST::InitNode(size_t size, EZCCTreeNodeType type, ZCC_TreeNode *basis)
|
||||
{
|
||||
ZCC_TreeNode *node = (ZCC_TreeNode *)SyntaxArena.Alloc(size);
|
||||
node->SiblingNext = node;
|
||||
node->SiblingPrev = node;
|
||||
node->NodeType = type;
|
||||
if (basis != NULL)
|
||||
{
|
||||
node->SourceName = basis->SourceName;
|
||||
node->SourceLoc = basis->SourceLoc;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
|
||||
ZCC_TreeNode *ZCCParseState::InitNode(size_t size, EZCCTreeNodeType type)
|
||||
{
|
||||
ZCC_TreeNode *node = ZCC_AST::InitNode(size, type, NULL);
|
||||
node->SourceName = Strings.Alloc(sc->ScriptName);
|
||||
return node;
|
||||
}
|
564
src/scripting/zscript/zcc_parser.h
Normal file
564
src/scripting/zscript/zcc_parser.h
Normal file
|
@ -0,0 +1,564 @@
|
|||
#ifndef ZCC_PARSER_H
|
||||
#define ZCC_PARSER_H
|
||||
|
||||
#include "memarena.h"
|
||||
#include "sc_man.h"
|
||||
|
||||
struct ZCCToken
|
||||
{
|
||||
union
|
||||
{
|
||||
int Int;
|
||||
double Float;
|
||||
FString *String;
|
||||
};
|
||||
int SourceLoc;
|
||||
|
||||
ENamedName Name() { return ENamedName(Int); }
|
||||
};
|
||||
|
||||
// Variable / Function / Class modifiers
|
||||
enum
|
||||
{
|
||||
ZCC_Native = 1 << 0,
|
||||
ZCC_Static = 1 << 1,
|
||||
ZCC_Private = 1 << 2,
|
||||
ZCC_Protected = 1 << 3,
|
||||
ZCC_Latent = 1 << 4,
|
||||
ZCC_Final = 1 << 5,
|
||||
ZCC_Meta = 1 << 6,
|
||||
ZCC_Action = 1 << 7,
|
||||
ZCC_Deprecated = 1 << 8,
|
||||
ZCC_ReadOnly = 1 << 9,
|
||||
ZCC_FuncConst = 1 << 10,
|
||||
ZCC_Abstract = 1 << 11,
|
||||
};
|
||||
|
||||
// Function parameter modifiers
|
||||
enum
|
||||
{
|
||||
ZCC_In = 1 << 0,
|
||||
ZCC_Out = 1 << 1,
|
||||
ZCC_Optional = 1 << 2,
|
||||
};
|
||||
|
||||
|
||||
|
||||
// Syntax tree structures.
|
||||
enum EZCCTreeNodeType
|
||||
{
|
||||
AST_Identifier,
|
||||
AST_Class,
|
||||
AST_Struct,
|
||||
AST_Enum,
|
||||
AST_EnumTerminator,
|
||||
AST_States,
|
||||
AST_StatePart,
|
||||
AST_StateLabel,
|
||||
AST_StateStop,
|
||||
AST_StateWait,
|
||||
AST_StateFail,
|
||||
AST_StateLoop,
|
||||
AST_StateGoto,
|
||||
AST_StateLine,
|
||||
AST_VarName,
|
||||
AST_Type,
|
||||
AST_BasicType,
|
||||
AST_MapType,
|
||||
AST_DynArrayType,
|
||||
AST_ClassType,
|
||||
AST_Expression,
|
||||
AST_ExprID,
|
||||
AST_ExprTypeRef,
|
||||
AST_ExprConstant,
|
||||
AST_ExprFuncCall,
|
||||
AST_ExprMemberAccess,
|
||||
AST_ExprUnary,
|
||||
AST_ExprBinary,
|
||||
AST_ExprTrinary,
|
||||
AST_FuncParm,
|
||||
AST_Statement,
|
||||
AST_CompoundStmt,
|
||||
AST_ContinueStmt,
|
||||
AST_BreakStmt,
|
||||
AST_ReturnStmt,
|
||||
AST_ExpressionStmt,
|
||||
AST_IterationStmt,
|
||||
AST_IfStmt,
|
||||
AST_SwitchStmt,
|
||||
AST_CaseStmt,
|
||||
AST_AssignStmt,
|
||||
AST_LocalVarStmt,
|
||||
AST_FuncParamDecl,
|
||||
AST_ConstantDef,
|
||||
AST_Declarator,
|
||||
AST_VarDeclarator,
|
||||
AST_FuncDeclarator,
|
||||
AST_Default,
|
||||
AST_FlagStmt,
|
||||
AST_PropertyStmt,
|
||||
|
||||
NUM_AST_NODE_TYPES
|
||||
};
|
||||
|
||||
enum EZCCBuiltinType
|
||||
{
|
||||
ZCC_SInt8,
|
||||
ZCC_UInt8,
|
||||
ZCC_SInt16,
|
||||
ZCC_UInt16,
|
||||
ZCC_SInt32,
|
||||
ZCC_UInt32,
|
||||
ZCC_IntAuto, // for enums, autoselect appropriately sized int
|
||||
|
||||
ZCC_Bool,
|
||||
ZCC_Float32,
|
||||
ZCC_Float64,
|
||||
ZCC_FloatAuto, // 32-bit in structs/classes, 64-bit everywhere else
|
||||
ZCC_String,
|
||||
ZCC_Vector2,
|
||||
ZCC_Vector3,
|
||||
ZCC_Vector4,
|
||||
ZCC_Name,
|
||||
|
||||
ZCC_Color, // special types for ZDoom.
|
||||
ZCC_State,
|
||||
ZCC_Sound,
|
||||
|
||||
ZCC_UserType,
|
||||
|
||||
ZCC_NUM_BUILT_IN_TYPES
|
||||
};
|
||||
|
||||
enum EZCCExprType
|
||||
{
|
||||
#define xx(a,z) PEX_##a,
|
||||
#include "zcc_exprlist.h"
|
||||
|
||||
PEX_COUNT_OF
|
||||
};
|
||||
|
||||
struct ZCC_TreeNode
|
||||
{
|
||||
// This tree node's siblings are stored in a circular linked list.
|
||||
// When you get back to this node, you know you've been through
|
||||
// the whole list.
|
||||
ZCC_TreeNode *SiblingNext;
|
||||
ZCC_TreeNode *SiblingPrev;
|
||||
|
||||
// can't use FScriptPosition, because the string wouldn't have a chance to
|
||||
// destruct if we did that.
|
||||
FString *SourceName;
|
||||
int SourceLoc;
|
||||
|
||||
// Node type is one of the node types above, which corresponds with
|
||||
// one of the structures below.
|
||||
EZCCTreeNodeType NodeType;
|
||||
|
||||
// Appends a sibling to this node's sibling list.
|
||||
void AppendSibling(ZCC_TreeNode *sibling)
|
||||
{
|
||||
if (sibling == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Check integrity of our sibling list.
|
||||
assert(SiblingPrev->SiblingNext == this);
|
||||
assert(SiblingNext->SiblingPrev == this);
|
||||
|
||||
// Check integrity of new sibling list.
|
||||
assert(sibling->SiblingPrev->SiblingNext == sibling);
|
||||
assert(sibling->SiblingNext->SiblingPrev == sibling);
|
||||
|
||||
ZCC_TreeNode *siblingend = sibling->SiblingPrev;
|
||||
SiblingPrev->SiblingNext = sibling;
|
||||
sibling->SiblingPrev = SiblingPrev;
|
||||
SiblingPrev = siblingend;
|
||||
siblingend->SiblingNext = this;
|
||||
}
|
||||
|
||||
operator FScriptPosition()
|
||||
{
|
||||
return FScriptPosition(*SourceName, SourceLoc);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
struct ZCC_Identifier : ZCC_TreeNode
|
||||
{
|
||||
ENamedName Id;
|
||||
};
|
||||
|
||||
struct ZCC_NamedNode : ZCC_TreeNode
|
||||
{
|
||||
ENamedName NodeName;
|
||||
PSymbolType *Symbol;
|
||||
};
|
||||
|
||||
struct ZCC_Class : ZCC_NamedNode
|
||||
{
|
||||
ZCC_Identifier *ParentName;
|
||||
ZCC_Identifier *Replaces;
|
||||
VM_UWORD Flags;
|
||||
ZCC_TreeNode *Body;
|
||||
PClass *Type;
|
||||
};
|
||||
|
||||
struct ZCC_Struct : ZCC_NamedNode
|
||||
{
|
||||
ZCC_TreeNode *Body;
|
||||
PStruct *Type;
|
||||
};
|
||||
|
||||
struct ZCC_Enum : ZCC_NamedNode
|
||||
{
|
||||
EZCCBuiltinType EnumType;
|
||||
struct ZCC_ConstantDef *Elements;
|
||||
};
|
||||
|
||||
struct ZCC_EnumTerminator : ZCC_TreeNode
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_States : ZCC_TreeNode
|
||||
{
|
||||
struct ZCC_StatePart *Body;
|
||||
};
|
||||
|
||||
struct ZCC_StatePart : ZCC_TreeNode
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_StateLabel : ZCC_StatePart
|
||||
{
|
||||
ENamedName Label;
|
||||
};
|
||||
|
||||
struct ZCC_StateStop : ZCC_StatePart
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_StateWait : ZCC_StatePart
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_StateFail : ZCC_StatePart
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_StateLoop : ZCC_StatePart
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_Expression : ZCC_TreeNode
|
||||
{
|
||||
EZCCExprType Operation;
|
||||
PType *Type;
|
||||
|
||||
// Repurposes this node as an error node
|
||||
void ToErrorNode()
|
||||
{
|
||||
Type = TypeError;
|
||||
Operation = PEX_Nil;
|
||||
NodeType = AST_Expression;
|
||||
}
|
||||
};
|
||||
|
||||
struct ZCC_StateGoto : ZCC_StatePart
|
||||
{
|
||||
ZCC_Identifier *Qualifier;
|
||||
ZCC_Identifier *Label;
|
||||
ZCC_Expression *Offset;
|
||||
};
|
||||
|
||||
struct ZCC_StateLine : ZCC_StatePart
|
||||
{
|
||||
FString *Sprite;
|
||||
BITFIELD bBright : 1;
|
||||
BITFIELD bFast : 1;
|
||||
BITFIELD bSlow : 1;
|
||||
BITFIELD bNoDelay : 1;
|
||||
BITFIELD bCanRaise : 1;
|
||||
FString *Frames;
|
||||
ZCC_Expression *Duration;
|
||||
ZCC_Expression *Offset;
|
||||
ZCC_ExprConstant *Lights;
|
||||
ZCC_TreeNode *Action;
|
||||
};
|
||||
|
||||
struct ZCC_VarName : ZCC_TreeNode
|
||||
{
|
||||
ENamedName Name;
|
||||
ZCC_Expression *ArraySize; // NULL if not an array
|
||||
};
|
||||
|
||||
struct ZCC_Type : ZCC_TreeNode
|
||||
{
|
||||
ZCC_Expression *ArraySize; // NULL if not an array
|
||||
};
|
||||
|
||||
struct ZCC_BasicType : ZCC_Type
|
||||
{
|
||||
EZCCBuiltinType Type;
|
||||
ZCC_Identifier *UserType;
|
||||
};
|
||||
|
||||
struct ZCC_MapType : ZCC_Type
|
||||
{
|
||||
ZCC_Type *KeyType;
|
||||
ZCC_Type *ValueType;
|
||||
};
|
||||
|
||||
struct ZCC_DynArrayType : ZCC_Type
|
||||
{
|
||||
ZCC_Type *ElementType;
|
||||
};
|
||||
|
||||
struct ZCC_ClassType : ZCC_Type
|
||||
{
|
||||
ZCC_Identifier *Restriction;
|
||||
};
|
||||
|
||||
struct ZCC_ExprID : ZCC_Expression
|
||||
{
|
||||
ENamedName Identifier;
|
||||
};
|
||||
|
||||
struct ZCC_ExprTypeRef : ZCC_Expression
|
||||
{
|
||||
PType *RefType;
|
||||
};
|
||||
|
||||
struct ZCC_ExprConstant : ZCC_Expression
|
||||
{
|
||||
union
|
||||
{
|
||||
FString *StringVal;
|
||||
int IntVal;
|
||||
unsigned int UIntVal;
|
||||
double DoubleVal;
|
||||
};
|
||||
};
|
||||
|
||||
struct ZCC_FuncParm : ZCC_TreeNode
|
||||
{
|
||||
ZCC_Expression *Value;
|
||||
ENamedName Label;
|
||||
};
|
||||
|
||||
struct ZCC_ExprFuncCall : ZCC_Expression
|
||||
{
|
||||
ZCC_Expression *Function;
|
||||
ZCC_FuncParm *Parameters;
|
||||
};
|
||||
|
||||
struct ZCC_ExprMemberAccess : ZCC_Expression
|
||||
{
|
||||
ZCC_Expression *Left;
|
||||
ENamedName Right;
|
||||
};
|
||||
|
||||
struct ZCC_ExprUnary : ZCC_Expression
|
||||
{
|
||||
ZCC_Expression *Operand;
|
||||
};
|
||||
|
||||
struct ZCC_ExprBinary : ZCC_Expression
|
||||
{
|
||||
ZCC_Expression *Left;
|
||||
ZCC_Expression *Right;
|
||||
};
|
||||
|
||||
struct ZCC_ExprTrinary : ZCC_Expression
|
||||
{
|
||||
ZCC_Expression *Test;
|
||||
ZCC_Expression *Left;
|
||||
ZCC_Expression *Right;
|
||||
};
|
||||
|
||||
struct ZCC_Statement : ZCC_TreeNode
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_CompoundStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Statement *Content;
|
||||
};
|
||||
|
||||
struct ZCC_ContinueStmt : ZCC_Statement
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_BreakStmt : ZCC_Statement
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_ReturnStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Expression *Values;
|
||||
};
|
||||
|
||||
struct ZCC_ExpressionStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Expression *Expression;
|
||||
};
|
||||
|
||||
struct ZCC_IterationStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Expression *LoopCondition;
|
||||
ZCC_Statement *LoopStatement;
|
||||
ZCC_Statement *LoopBumper;
|
||||
|
||||
// Should the loop condition be checked at the
|
||||
// start of the loop (before the LoopStatement)
|
||||
// or at the end (after the LoopStatement)?
|
||||
enum { Start, End } CheckAt;
|
||||
};
|
||||
|
||||
struct ZCC_IfStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Expression *Condition;
|
||||
ZCC_Statement *TruePath;
|
||||
ZCC_Statement *FalsePath;
|
||||
};
|
||||
|
||||
struct ZCC_SwitchStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Expression *Condition;
|
||||
ZCC_Statement *Content;
|
||||
};
|
||||
|
||||
struct ZCC_CaseStmt : ZCC_Statement
|
||||
{
|
||||
// A NULL Condition represents the default branch
|
||||
ZCC_Expression *Condition;
|
||||
};
|
||||
|
||||
struct ZCC_AssignStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Expression *Dests;
|
||||
ZCC_Expression *Sources;
|
||||
int AssignOp;
|
||||
};
|
||||
|
||||
struct ZCC_LocalVarStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Type *Type;
|
||||
ZCC_VarName *Vars;
|
||||
ZCC_Expression *Inits;
|
||||
};
|
||||
|
||||
struct ZCC_FuncParamDecl : ZCC_TreeNode
|
||||
{
|
||||
ZCC_Type *Type;
|
||||
ZCC_Expression *Default;
|
||||
ENamedName Name;
|
||||
int Flags;
|
||||
};
|
||||
|
||||
struct ZCC_ConstantDef : ZCC_NamedNode
|
||||
{
|
||||
ZCC_Expression *Value;
|
||||
PSymbolConst *Symbol;
|
||||
ZCC_Enum *Type; // gets set when the constant originates from an enum.
|
||||
};
|
||||
|
||||
struct ZCC_Declarator : ZCC_TreeNode
|
||||
{
|
||||
ZCC_Type *Type;
|
||||
int Flags;
|
||||
};
|
||||
|
||||
// A variable in a class or struct.
|
||||
struct ZCC_VarDeclarator : ZCC_Declarator
|
||||
{
|
||||
ZCC_VarName *Names;
|
||||
};
|
||||
|
||||
// A function in a class.
|
||||
struct ZCC_FuncDeclarator : ZCC_Declarator
|
||||
{
|
||||
ZCC_FuncParamDecl *Params;
|
||||
ENamedName Name;
|
||||
ZCC_Statement *Body;
|
||||
};
|
||||
|
||||
struct ZCC_Default : ZCC_CompoundStmt
|
||||
{
|
||||
};
|
||||
|
||||
struct ZCC_PropertyStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Identifier *Prop;
|
||||
ZCC_Expression *Values;
|
||||
};
|
||||
|
||||
|
||||
struct ZCC_FlagStmt : ZCC_Statement
|
||||
{
|
||||
ZCC_Identifier *name;
|
||||
bool set;
|
||||
};
|
||||
|
||||
typedef ZCC_ExprConstant *(*EvalConst1op)(ZCC_ExprConstant *);
|
||||
typedef ZCC_ExprConstant *(*EvalConst2op)(ZCC_ExprConstant *, ZCC_ExprConstant *, FSharedStringArena &);
|
||||
|
||||
struct ZCC_OpProto
|
||||
{
|
||||
ZCC_OpProto *Next;
|
||||
PType *ResType;
|
||||
PType *Type1;
|
||||
PType *Type2;
|
||||
union
|
||||
{
|
||||
EvalConst1op EvalConst1;
|
||||
EvalConst2op EvalConst2;
|
||||
};
|
||||
|
||||
ZCC_OpProto(PType *res, PType *t1, PType *t2)
|
||||
: ResType(res), Type1(t1), Type2(t2) {}
|
||||
};
|
||||
|
||||
struct ZCC_OpInfoType
|
||||
{
|
||||
const char *OpName;
|
||||
ZCC_OpProto *Protos;
|
||||
|
||||
void AddProto(PType *res, PType *optype, EvalConst1op evalconst);
|
||||
void AddProto(PType *res, PType *left, PType *right, EvalConst2op evalconst);
|
||||
|
||||
ZCC_OpProto *FindBestProto(PType *optype, const PType::Conversion **route, int &numslots);
|
||||
ZCC_OpProto *FindBestProto(PType *left, const PType::Conversion **route1, int &numslots,
|
||||
PType *right, const PType::Conversion **route2, int &numslots2);
|
||||
|
||||
void FreeAllProtos();
|
||||
};
|
||||
|
||||
#define CONVERSION_ROUTE_SIZE 8
|
||||
|
||||
FString ZCC_PrintAST(ZCC_TreeNode *root);
|
||||
|
||||
void ZCC_InitOperators();
|
||||
|
||||
extern ZCC_OpInfoType ZCC_OpInfo[PEX_COUNT_OF];
|
||||
|
||||
struct ZCC_AST
|
||||
{
|
||||
ZCC_AST() : TopNode(NULL) {}
|
||||
ZCC_TreeNode *InitNode(size_t size, EZCCTreeNodeType type, ZCC_TreeNode *basis);
|
||||
|
||||
FSharedStringArena Strings;
|
||||
FMemArena SyntaxArena;
|
||||
struct ZCC_TreeNode *TopNode;
|
||||
};
|
||||
|
||||
struct ZCCParseState : public ZCC_AST
|
||||
{
|
||||
ZCCParseState(FScanner *scanner = nullptr) : sc(scanner) {}
|
||||
ZCC_TreeNode *InitNode(size_t size, EZCCTreeNodeType type);
|
||||
|
||||
FScanner *sc;
|
||||
};
|
||||
|
||||
#endif
|
Loading…
Add table
Add a link
Reference in a new issue