qzdoom/src/scripting/zscript/zcc_compile.cpp

2696 lines
76 KiB
C++
Raw Normal View History

/*
** zcc_compile.cpp
**
**---------------------------------------------------------------------------
** Copyright -2016 Randy Heit
** Copyright 2016 Christoph Oelckers
** 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 "actor.h"
#include "thingdef.h"
#include "sc_man.h"
#include "c_console.h"
#include "c_dispatch.h"
#include "doomerrors.h"
#include "w_wad.h"
#include "cmdlib.h"
#include "m_alloc.h"
#include "zcc_parser.h"
#include "zcc-parse.h"
#include "zcc_compile.h"
#include "v_text.h"
#include "p_lnspec.h"
#include "i_system.h"
#include "gdtoa.h"
#include "codegeneration/codegen.h"
#include "vmbuilder.h"
#define DEFINING_CONST ((PSymbolConst *)(void *)1)
//==========================================================================
//
// ZCCCompiler :: ProcessClass
//
//==========================================================================
void ZCCCompiler::ProcessClass(ZCC_Class *cnode, PSymbolTreeNode *treenode)
{
Classes.Push(new ZCC_ClassWork(static_cast<ZCC_Class *>(cnode), treenode));
auto cls = Classes.Last();
auto node = cnode->Body;
PSymbolTreeNode *childnode;
ZCC_Enum *enumType = nullptr;
FString name;
name << "nodes - " << FName(cnode->NodeName);
cls->TreeNodes.SetName(name);
if (cnode->Replaces != nullptr && !static_cast<PClassActor *>(cnode->Type)->SetReplacement(cnode->Replaces->Id))
{
Warn(cnode, "Replaced type '%s' not found for %s", FName(cnode->Replaces->Id).GetChars(), cnode->Type->TypeName.GetChars());
}
// Need to check if the class actually has a body.
if (node != nullptr) do
{
switch (node->NodeType)
{
case AST_Struct:
case AST_ConstantDef:
case AST_Enum:
if ((childnode = AddTreeNode(static_cast<ZCC_NamedNode *>(node)->NodeName, node, &cls->TreeNodes)))
{
switch (node->NodeType)
{
case AST_Enum:
enumType = static_cast<ZCC_Enum *>(node);
cls->Enums.Push(enumType);
break;
case AST_Struct:
ProcessStruct(static_cast<ZCC_Struct *>(node), childnode, cls->cls);
break;
case AST_ConstantDef:
cls->Constants.Push(static_cast<ZCC_ConstantDef *>(node));
cls->Constants.Last()->Type = enumType;
break;
default:
assert(0 && "Default case is just here to make GCC happy. It should never be reached");
}
}
break;
case AST_VarDeclarator:
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
break;
case AST_EnumTerminator:
enumType = nullptr;
break;
case AST_States:
cls->States.Push(static_cast<ZCC_States *>(node));
break;
case AST_FuncDeclarator:
cls->Functions.Push(static_cast<ZCC_FuncDeclarator *>(node));
break;
case AST_Default:
cls->Defaults.Push(static_cast<ZCC_Default *>(node));
break;
default:
assert(0 && "Unhandled AST node type");
break;
}
node = node->SiblingNext;
}
while (node != cnode->Body);
}
//==========================================================================
//
// ZCCCompiler :: ProcessStruct
//
//==========================================================================
void ZCCCompiler::ProcessStruct(ZCC_Struct *cnode, PSymbolTreeNode *treenode, ZCC_Class *outer)
{
Structs.Push(new ZCC_StructWork(static_cast<ZCC_Struct *>(cnode), treenode, outer));
ZCC_StructWork *cls = Structs.Last();
auto node = cnode->Body;
PSymbolTreeNode *childnode;
ZCC_Enum *enumType = nullptr;
// Need to check if the struct actually has a body.
if (node != nullptr) do
{
switch (node->NodeType)
{
case AST_ConstantDef:
case AST_Enum:
if ((childnode = AddTreeNode(static_cast<ZCC_NamedNode *>(node)->NodeName, node, &cls->TreeNodes)))
{
switch (node->NodeType)
{
case AST_Enum:
enumType = static_cast<ZCC_Enum *>(node);
cls->Enums.Push(enumType);
break;
case AST_ConstantDef:
cls->Constants.Push(static_cast<ZCC_ConstantDef *>(node));
cls->Constants.Last()->Type = enumType;
break;
default:
assert(0 && "Default case is just here to make GCC happy. It should never be reached");
}
}
break;
case AST_VarDeclarator:
cls->Fields.Push(static_cast<ZCC_VarDeclarator *>(node));
break;
case AST_EnumTerminator:
enumType = nullptr;
break;
default:
assert(0 && "Unhandled AST node type");
break;
}
node = node->SiblingNext;
}
while (node != cnode->Body);
}
//==========================================================================
//
// ZCCCompiler Constructor
//
//==========================================================================
ZCCCompiler::ZCCCompiler(ZCC_AST &ast, DObject *_outer, PSymbolTable &_symbols, PSymbolTable &_outsymbols)
: Outer(_outer), GlobalTreeNodes(&_symbols), OutputSymbols(&_outsymbols), AST(ast)
{
FScriptPosition::ResetErrorCounter();
// Group top-level nodes by type
if (ast.TopNode != NULL)
{
ZCC_TreeNode *node = ast.TopNode;
PSymbolTreeNode *tnode;
PType *enumType = nullptr;
ZCC_Enum *zenumType = nullptr;
do
{
switch (node->NodeType)
{
case AST_Class:
case AST_Struct:
case AST_ConstantDef:
case AST_Enum:
if ((tnode = AddTreeNode(static_cast<ZCC_NamedNode *>(node)->NodeName, node, GlobalTreeNodes)))
{
switch (node->NodeType)
{
case AST_Enum:
zenumType = static_cast<ZCC_Enum *>(node);
enumType = NewEnum(zenumType->NodeName, nullptr);
GlobalSymbols.AddSymbol(new PSymbolType(zenumType->NodeName, enumType));
break;
case AST_Class:
ProcessClass(static_cast<ZCC_Class *>(node), tnode);
break;
case AST_Struct:
ProcessStruct(static_cast<ZCC_Struct *>(node), tnode, nullptr);
break;
case AST_ConstantDef:
Constants.Push(static_cast<ZCC_ConstantDef *>(node));
Constants.Last()->Type = zenumType;
break;
default:
assert(0 && "Default case is just here to make GCC happy. It should never be reached");
}
}
break;
case AST_EnumTerminator:
zenumType = nullptr;
break;
default:
assert(0 && "Unhandled AST node type");
break;
}
node = node->SiblingNext;
} while (node != ast.TopNode);
}
}
ZCCCompiler::~ZCCCompiler()
{
for (auto s : Structs)
{
delete s;
}
for (auto c : Classes)
{
delete c;
}
Structs.Clear();
Classes.Clear();
}
//==========================================================================
//
// ZCCCompiler :: AddTreeNode
//
// Keeps track of definition nodes by their names. Ensures that all names
// in this scope are unique.
//
//==========================================================================
PSymbolTreeNode *ZCCCompiler::AddTreeNode(FName name, ZCC_TreeNode *node, PSymbolTable *treenodes, bool searchparents)
{
PSymbol *check = treenodes->FindSymbol(name, searchparents);
if (check != NULL)
{
assert(check->IsA(RUNTIME_CLASS(PSymbolTreeNode)));
Error(node, "Attempt to redefine '%s'", name.GetChars());
Error(static_cast<PSymbolTreeNode *>(check)->Node, " Original definition is here");
return nullptr;
}
else
{
auto sy = new PSymbolTreeNode(name, node);
FString name;
treenodes->AddSymbol(sy);
return sy;
}
}
//==========================================================================
//
// ZCCCompiler :: Warn
//
// Prints a warning message, and increments WarnCount.
//
//==========================================================================
void ZCCCompiler::Warn(ZCC_TreeNode *node, const char *msg, ...)
{
va_list argptr;
va_start(argptr, msg);
MessageV(node, TEXTCOLOR_ORANGE, msg, argptr);
va_end(argptr);
FScriptPosition::WarnCounter++;
}
//==========================================================================
//
// ZCCCompiler :: Error
//
// Prints an error message, and increments ErrorCount.
//
//==========================================================================
void ZCCCompiler::Error(ZCC_TreeNode *node, const char *msg, ...)
{
va_list argptr;
va_start(argptr, msg);
MessageV(node, TEXTCOLOR_RED, msg, argptr);
va_end(argptr);
FScriptPosition::ErrorCounter++;
}
//==========================================================================
//
// ZCCCompiler :: MessageV
//
// Prints a message, annotated with the source location for the tree node.
//
//==========================================================================
void ZCCCompiler::MessageV(ZCC_TreeNode *node, const char *txtcolor, const char *msg, va_list argptr)
{
FString composed;
composed.Format("%s%s, line %d: ", txtcolor, node->SourceName->GetChars(), node->SourceLoc);
composed.VAppendFormat(msg, argptr);
composed += '\n';
PrintString(PRINT_HIGH, composed);
}
//==========================================================================
//
// ZCCCompiler :: Compile
//
// Compile everything defined at this level.
//
//==========================================================================
int ZCCCompiler::Compile()
{
CreateClassTypes();
CreateStructTypes();
CompileAllConstants();
CompileAllFields();
InitDefaults();
InitFunctions();
CompileStates();
return FScriptPosition::ErrorCounter;
}
//==========================================================================
//
// ZCCCompiler :: CreateStructTypes
//
// Creates a PStruct for every struct.
//
//==========================================================================
void ZCCCompiler::CreateStructTypes()
{
for(auto s : Structs)
{
s->Outer = s->OuterDef == nullptr? nullptr : s->OuterDef->Type;
s->strct->Type = NewStruct(s->NodeName(), s->Outer);
s->strct->Symbol = new PSymbolType(s->NodeName(), s->Type());
s->Type()->Symbols.SetName(FName(s->NodeName()));
GlobalSymbols.AddSymbol(s->strct->Symbol);
for (auto e : s->Enums)
{
auto etype = NewEnum(e->NodeName, s->Type());
s->Type()->Symbols.AddSymbol(new PSymbolType(e->NodeName, etype));
}
}
}
//==========================================================================
//
// ZCCCompiler :: CreateClassTypes
//
// Creates a PClass for every class so that we get access to the symbol table
// These will be created with unknown size because for that we need to
// process all fields first, but to do that we need the PClass and some
// other info depending on the PClass.
//
//==========================================================================
void ZCCCompiler::CreateClassTypes()
{
// we are going to sort the classes array so that entries are sorted in order of inheritance.
auto OrigClasses = std::move(Classes);
Classes.Clear();
bool donesomething = true;
while (donesomething)
{
donesomething = false;
for (unsigned i = 0; i<OrigClasses.Size(); i++)
{
auto c = OrigClasses[i];
// Check if we got the parent already defined.
PClass *parent;
auto ParentName = c->cls->ParentName;
if (ParentName != nullptr && ParentName->SiblingNext == ParentName) parent = PClass::FindClass(ParentName->Id);
else if (ParentName == nullptr) parent = RUNTIME_CLASS(DObject);
else
{
// The parent is a dotted name which the type system currently does not handle.
// Once it does this needs to be implemented here.
auto p = ParentName;
FString build;
do
{
if (build.IsNotEmpty()) build += '.';
build += FName(p->Id);
p = static_cast<decltype(p)>(p->SiblingNext);
} while (p != ParentName);
Error(c->cls, "Qualified name '%s' for base class not supported in '%s'", build.GetChars(), FName(c->NodeName()).GetChars());
parent = RUNTIME_CLASS(DObject);
}
if (parent != nullptr)
{
// The parent exists, we may create a type for this class
if (c->cls->Flags & ZCC_Native)
{
// If this is a native class, its own type must also already exist and not be a runtime class.
auto me = PClass::FindClass(c->NodeName());
if (me == nullptr)
{
Error(c->cls, "Unknown native class %s", c->NodeName().GetChars());
me = parent->FindClassTentative(c->NodeName());
}
else if (me->bRuntimeClass)
{
Error(c->cls, "%s is not a native class", c->NodeName().GetChars());
}
else
{
DPrintf(DMSG_SPAMMY, "Registered %s as native with parent %s\n", me->TypeName.GetChars(), parent->TypeName.GetChars());
}
c->cls->Type = me;
}
else
{
// We will never get here if the name is a duplicate, so we can just do the assignment.
c->cls->Type = parent->FindClassTentative(c->NodeName());
}
c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
GlobalSymbols.AddSymbol(c->cls->Symbol);
c->Type()->Symbols.SetName(c->NodeName());
Classes.Push(c);
OrigClasses.Delete(i--);
donesomething = true;
}
else
{
// No base class found. Now check if something in the unprocessed classes matches.
// If not, print an error. If something is found let's retry again in the next iteration.
bool found = false;
for (auto d : OrigClasses)
{
if (d->NodeName() == c->cls->ParentName->Id)
{
found = true;
break;
}
}
if (!found)
{
Error(c->cls, "Class %s has unknown base class %s", c->NodeName().GetChars(), FName(c->cls->ParentName->Id).GetChars());
// create a placeholder so that the compiler can continue looking for errors.
c->cls->Type = RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName());
c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
GlobalSymbols.AddSymbol(c->cls->Symbol);
c->Type()->Symbols.SetName(c->NodeName());
Classes.Push(c);
OrigClasses.Delete(i--);
donesomething = true;
}
}
}
}
// What's left refers to some other class in the list but could not be resolved.
// This normally means a circular reference.
for (auto c : OrigClasses)
{
Error(c->cls, "Class %s has circular inheritance", FName(c->NodeName()).GetChars());
c->cls->Type = RUNTIME_CLASS(DObject)->FindClassTentative(c->NodeName());
c->cls->Symbol = new PSymbolType(c->NodeName(), c->Type());
c->Type()->Symbols.SetName(FName(c->NodeName()).GetChars());
GlobalSymbols.AddSymbol(c->cls->Symbol);
Classes.Push(c);
}
// Last but not least: Now that all classes have been created, we can create the symbols for the internal enums and link the treenode symbol tables
for (auto cd : Classes)
{
for (auto e : cd->Enums)
{
auto etype = NewEnum(e->NodeName, cd->Type());
cd->Type()->Symbols.AddSymbol(new PSymbolType(e->NodeName, etype));
}
// Link the tree node tables. We only can do this after we know the class relations.
for (auto cc : Classes)
{
if (cc->Type() == cd->Type()->ParentClass)
{
cd->TreeNodes.SetParentTable(&cc->TreeNodes);
break;
}
}
}
}
//==========================================================================
//
// ZCCCompiler :: AddConstants
//
// Helper for CompileAllConstants
//
//==========================================================================
void ZCCCompiler::CopyConstants(TArray<ZCC_ConstantWork> &dest, TArray<ZCC_ConstantDef*> &Constants, PSymbolTable *ot)
{
for (auto c : Constants)
{
dest.Push({ c, ot });
}
}
//==========================================================================
//
// ZCCCompiler :: CompileAllConstants
//
// Make symbols from every constant defined at all levels.
// Since constants may only depend on other constants this can be done
// without any more involved processing of the AST as a first step.
//
//==========================================================================
void ZCCCompiler::CompileAllConstants()
{
// put all constants in one list to make resolving this easier.
TArray<ZCC_ConstantWork> constantwork;
CopyConstants(constantwork, Constants, OutputSymbols);
for (auto c : Classes)
{
CopyConstants(constantwork, c->Constants, &c->Type()->Symbols);
}
for (auto s : Structs)
{
CopyConstants(constantwork, s->Constants, &s->Type()->Symbols);
}
// Before starting to resolve the list, let's create symbols for all already resolved ones first (i.e. all literal constants), to reduce work.
for (unsigned i = 0; i<constantwork.Size(); i++)
{
if (constantwork[i].node->Value->NodeType == AST_ExprConstant)
{
AddConstant(constantwork[i]);
// Remove the constant from the list
constantwork.Delete(i);
i--;
}
}
bool donesomething = true;
// Now go through this list until no more constants can be resolved. The remaining ones will be non-constant values.
while (donesomething && constantwork.Size() > 0)
{
donesomething = false;
for (unsigned i = 0; i < constantwork.Size(); i++)
{
if (CompileConstant(constantwork[i].node, constantwork[i].outputtable))
{
AddConstant(constantwork[i]);
// Remove the constant from the list
constantwork.Delete(i);
i--;
donesomething = true;
}
}
}
for (unsigned i = 0; i < constantwork.Size(); i++)
{
Error(constantwork[i].node, "%s is not a constant", FName(constantwork[i].node->NodeName).GetChars());
}
}
//==========================================================================
//
// ZCCCompiler :: AddConstant
//
// Adds a constant to its assigned symbol table
//
//==========================================================================
void ZCCCompiler::AddConstant(ZCC_ConstantWork &constant)
{
auto def = constant.node;
auto val = def->Value;
if (val->NodeType == AST_ExprConstant)
{
ZCC_ExprConstant *cval = static_cast<ZCC_ExprConstant *>(val);
if (cval->Type == TypeString)
{
def->Symbol = new PSymbolConstString(def->NodeName, *(cval->StringVal));
}
else if (cval->Type->IsA(RUNTIME_CLASS(PInt)))
{
// How do we get an Enum type in here without screwing everything up???
//auto type = def->Type != nullptr ? def->Type : cval->Type;
def->Symbol = new PSymbolConstNumeric(def->NodeName, cval->Type, cval->IntVal);
}
else if (cval->Type->IsA(RUNTIME_CLASS(PFloat)))
{
if (def->Type != nullptr)
{
Error(def, "Enum members must be integer values");
}
def->Symbol = new PSymbolConstNumeric(def->NodeName, cval->Type, cval->DoubleVal);
}
else
{
Error(def->Value, "Bad type for constant definiton");
def->Symbol = nullptr;
}
if (def->Symbol == nullptr)
{
// Create a dummy constant so we don't make any undefined value warnings.
def->Symbol = new PSymbolConstNumeric(def->NodeName, TypeError, 0);
}
constant.outputtable->ReplaceSymbol(def->Symbol);
}
}
//==========================================================================
//
// ZCCCompiler :: CompileConstant
//
// For every constant definition, evaluate its value (which should result
// in a constant), and create a symbol for it.
//
//==========================================================================
bool ZCCCompiler::CompileConstant(ZCC_ConstantDef *def, PSymbolTable *sym)
{
assert(def->Symbol == nullptr);
def->Symbol = DEFINING_CONST; // avoid recursion
ZCC_Expression *val = Simplify(def->Value, sym, true);
def->Value = val;
if (def->Symbol == DEFINING_CONST) def->Symbol = nullptr;
return (val->NodeType == AST_ExprConstant);
}
//==========================================================================
//
// ZCCCompiler :: Simplify
//
// For an expression,
// Evaluate operators whose arguments are both constants, replacing it
// with a new constant.
// For a binary operator with one constant argument, put it on the right-
// hand operand, where permitted.
// Perform automatic type promotion.
//
//==========================================================================
ZCC_Expression *ZCCCompiler::Simplify(ZCC_Expression *root, PSymbolTable *sym, bool wantconstant)
{
SimplifyingConstant = wantconstant;
return DoSimplify(root, sym);
}
ZCC_Expression *ZCCCompiler::DoSimplify(ZCC_Expression *root, PSymbolTable *sym)
{
if (root->NodeType == AST_ExprUnary)
{
return SimplifyUnary(static_cast<ZCC_ExprUnary *>(root), sym);
}
else if (root->NodeType == AST_ExprBinary)
{
return SimplifyBinary(static_cast<ZCC_ExprBinary *>(root), sym);
}
else if (root->Operation == PEX_ID)
{
return IdentifyIdentifier(static_cast<ZCC_ExprID *>(root), sym);
}
else if (root->Operation == PEX_MemberAccess)
{
return SimplifyMemberAccess(static_cast<ZCC_ExprMemberAccess *>(root), sym);
}
else if (root->Operation == PEX_FuncCall)
{
return SimplifyFunctionCall(static_cast<ZCC_ExprFuncCall *>(root), sym);
}
return root;
}
//==========================================================================
//
// ZCCCompiler :: SimplifyUnary
//
//==========================================================================
ZCC_Expression *ZCCCompiler::SimplifyUnary(ZCC_ExprUnary *unary, PSymbolTable *sym)
{
unary->Operand = DoSimplify(unary->Operand, sym);
if (unary->Operand->Type == nullptr)
{
return unary;
}
ZCC_OpProto *op = PromoteUnary(unary->Operation, unary->Operand);
if (op == NULL)
{ // Oh, poo!
unary->Type = TypeError;
}
else if (unary->Operand->Operation == PEX_ConstValue)
{
return op->EvalConst1(static_cast<ZCC_ExprConstant *>(unary->Operand));
}
return unary;
}
//==========================================================================
//
// ZCCCompiler :: SimplifyBinary
//
//==========================================================================
ZCC_Expression *ZCCCompiler::SimplifyBinary(ZCC_ExprBinary *binary, PSymbolTable *sym)
{
binary->Left = DoSimplify(binary->Left, sym);
binary->Right = DoSimplify(binary->Right, sym);
if (binary->Left->Type == nullptr || binary->Right->Type == nullptr)
{
// We do not know yet what this is so we cannot promote it (yet.)
return binary;
}
ZCC_OpProto *op = PromoteBinary(binary->Operation, binary->Left, binary->Right);
if (op == NULL)
{
binary->Type = TypeError;
}
else if (binary->Left->Operation == PEX_ConstValue &&
binary->Right->Operation == PEX_ConstValue)
{
return op->EvalConst2(static_cast<ZCC_ExprConstant *>(binary->Left),
static_cast<ZCC_ExprConstant *>(binary->Right), AST.Strings);
}
return binary;
}
//==========================================================================
//
// ZCCCompiler :: SimplifyMemberAccess
//
//==========================================================================
ZCC_Expression *ZCCCompiler::SimplifyMemberAccess(ZCC_ExprMemberAccess *dotop, PSymbolTable *symt)
{
PSymbolTable *symtable;
// TBD: Is it safe to simplify the left side here when not processing a constant?
dotop->Left = DoSimplify(dotop->Left, symt);
if (dotop->Left->Operation == PEX_TypeRef)
{ // Type refs can be evaluated now.
PType *ref = static_cast<ZCC_ExprTypeRef *>(dotop->Left)->RefType;
PSymbol *sym = ref->Symbols.FindSymbolInTable(dotop->Right, symtable);
if (sym != nullptr)
{
ZCC_Expression *expr = NodeFromSymbol(sym, dotop, symtable);
if (expr != nullptr)
{
return expr;
}
}
}
else if (dotop->Left->Operation == PEX_Super)
{
symt = symt->GetParentTable();
if (symt != nullptr)
{
PSymbol *sym = symt->FindSymbolInTable(dotop->Right, symtable);
if (sym != nullptr)
{
ZCC_Expression *expr = NodeFromSymbol(sym, dotop, symtable);
if (expr != nullptr)
{
return expr;
}
}
}
}
return dotop;
}
//==========================================================================
//
// ZCCCompiler :: SimplifyFunctionCall
//
// This may replace a function call with cast(s), since they look like the
// same thing to the parser.
//
//==========================================================================
ZCC_Expression *ZCCCompiler::SimplifyFunctionCall(ZCC_ExprFuncCall *callop, PSymbolTable *sym)
{
ZCC_FuncParm *parm;
int parmcount = 0;
parm = callop->Parameters;
if (parm != NULL)
{
do
{
parmcount++;
assert(parm->NodeType == AST_FuncParm);
parm->Value = DoSimplify(parm->Value, sym);
parm = static_cast<ZCC_FuncParm *>(parm->SiblingNext);
}
while (parm != callop->Parameters);
}
// Only simplify the 'function' part if we want to retrieve a constant.
// This is necessary to evaluate the type casts, but for actual functions
// the simplification process is destructive and has to be avoided.
if (SimplifyingConstant)
{
callop->Function = DoSimplify(callop->Function, sym);
}
// If the left side is a type ref, then this is actually a cast
// and not a function call.
if (callop->Function->Operation == PEX_TypeRef)
{
if (parmcount != 1)
{
Error(callop, "Type cast requires one parameter");
callop->ToErrorNode();
}
else
{
PType *dest = static_cast<ZCC_ExprTypeRef *>(callop->Function)->RefType;
const PType::Conversion *route[CONVERSION_ROUTE_SIZE];
int routelen = parm->Value->Type->FindConversion(dest, route, countof(route));
if (routelen < 0)
{
///FIXME: Need real type names
Error(callop, "Cannot convert %s to %s", parm->Value->Type->DescriptiveName(), dest->DescriptiveName());
callop->ToErrorNode();
}
else
{
ZCC_Expression *val = ApplyConversion(parm->Value, route, routelen);
assert(val->Type == dest);
return val;
}
}
}
return callop;
}
//==========================================================================
//
// ZCCCompiler :: PromoteUnary
//
// Converts the operand into a format preferred by the operator.
//
//==========================================================================
ZCC_OpProto *ZCCCompiler::PromoteUnary(EZCCExprType op, ZCC_Expression *&expr)
{
if (expr->Type == TypeError)
{
return NULL;
}
const PType::Conversion *route[CONVERSION_ROUTE_SIZE];
int routelen = countof(route);
ZCC_OpProto *proto = ZCC_OpInfo[op].FindBestProto(expr->Type, route, routelen);
if (proto != NULL)
{
expr = ApplyConversion(expr, route, routelen);
}
return proto;
}
//==========================================================================
//
// ZCCCompiler :: PromoteBinary
//
// Converts the operands into a format (hopefully) compatible with the
// operator.
//
//==========================================================================
ZCC_OpProto *ZCCCompiler::PromoteBinary(EZCCExprType op, ZCC_Expression *&left, ZCC_Expression *&right)
{
// If either operand is of type 'error', the result is also 'error'
if (left->Type == TypeError || right->Type == TypeError)
{
return NULL;
}
const PType::Conversion *route1[CONVERSION_ROUTE_SIZE], *route2[CONVERSION_ROUTE_SIZE];
int route1len = countof(route1), route2len = countof(route2);
ZCC_OpProto *proto = ZCC_OpInfo[op].FindBestProto(left->Type, route1, route1len, right->Type, route2, route2len);
if (proto != NULL)
{
left = ApplyConversion(left, route1, route1len);
right = ApplyConversion(right, route2, route2len);
}
return proto;
}
//==========================================================================
//
// ZCCCompiler :: ApplyConversion
//
//==========================================================================
ZCC_Expression *ZCCCompiler::ApplyConversion(ZCC_Expression *expr, const PType::Conversion **route, int routelen)
{
for (int i = 0; i < routelen; ++i)
{
if (expr->Operation != PEX_ConstValue)
{
expr = AddCastNode(route[i]->TargetType, expr);
}
else
{
route[i]->ConvertConstant(static_cast<ZCC_ExprConstant *>(expr), AST.Strings);
}
}
return expr;
}
//==========================================================================
//
// ZCCCompiler :: AddCastNode
//
//==========================================================================
ZCC_Expression *ZCCCompiler::AddCastNode(PType *type, ZCC_Expression *expr)
{
assert(expr->Operation != PEX_ConstValue && "Expression must not be constant");
// TODO: add a node here
return expr;
}
//==========================================================================
//
// ZCCCompiler :: IdentifyIdentifier
//
// Returns a node that represents what the identifer stands for.
//
//==========================================================================
ZCC_Expression *ZCCCompiler::IdentifyIdentifier(ZCC_ExprID *idnode, PSymbolTable *symt)
{
// Check the symbol table for the identifier.
PSymbolTable *table;
PSymbol *sym = symt->FindSymbolInTable(idnode->Identifier, table);
// GlobalSymbols cannot be the parent of a class's symbol table so we have to look for global symbols explicitly.
if (sym == nullptr && symt != &GlobalSymbols) sym = GlobalSymbols.FindSymbolInTable(idnode->Identifier, table);
if (sym != nullptr)
{
ZCC_Expression *node = NodeFromSymbol(sym, idnode, table);
if (node != NULL)
{
return node;
}
}
else if (SimplifyingConstant) // leave unknown identifiers alone when simplifying non-constants. It is impossible to know what they are here.
{
// Also handle line specials.
// To call this like a function this needs to be done differently, but for resolving constants this is ok.
int spec = P_FindLineSpecial(FName(idnode->Identifier).GetChars());
if (spec != 0)
{
ZCC_ExprConstant *val = static_cast<ZCC_ExprConstant *>(AST.InitNode(sizeof(*val), AST_ExprConstant, idnode));
val->Operation = PEX_ConstValue;
val->Type = TypeSInt32;
val->IntVal = spec;
return val;
}
Error(idnode, "Unknown identifier '%s'", FName(idnode->Identifier).GetChars());
idnode->ToErrorNode();
}
return idnode;
}
//==========================================================================
//
// ZCCCompiler :: NodeFromSymbol
//
//==========================================================================
ZCC_Expression *ZCCCompiler::NodeFromSymbol(PSymbol *sym, ZCC_Expression *source, PSymbolTable *table)
{
assert(sym != nullptr);
if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConst)))
{
return NodeFromSymbolConst(static_cast<PSymbolConst *>(sym), source);
}
else if (sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
{
return NodeFromSymbolType(static_cast<PSymbolType *>(sym), source);
}
return NULL;
}
//==========================================================================
//
// ZCCCompiler :: NodeFromSymbolConst
//
// Returns a new AST constant node with the symbol's content.
//
//==========================================================================
ZCC_ExprConstant *ZCCCompiler::NodeFromSymbolConst(PSymbolConst *sym, ZCC_Expression *idnode)
{
ZCC_ExprConstant *val = static_cast<ZCC_ExprConstant *>(AST.InitNode(sizeof(*val), AST_ExprConstant, idnode));
val->Operation = PEX_ConstValue;
if (sym == NULL)
{
val->Type = TypeError;
val->IntVal = 0;
}
else if (sym->IsKindOf(RUNTIME_CLASS(PSymbolConstString)))
{
val->StringVal = AST.Strings.Alloc(static_cast<PSymbolConstString *>(sym)->Str);
val->Type = TypeString;
}
else
{
val->Type = sym->ValueType;
if (val->Type != TypeError)
{
assert(sym->IsKindOf(RUNTIME_CLASS(PSymbolConstNumeric)));
if (sym->ValueType->IsKindOf(RUNTIME_CLASS(PInt)))
{
val->IntVal = static_cast<PSymbolConstNumeric *>(sym)->Value;
}
else
{
assert(sym->ValueType->IsKindOf(RUNTIME_CLASS(PFloat)));
val->DoubleVal = static_cast<PSymbolConstNumeric *>(sym)->Float;
}
}
}
return val;
}
//==========================================================================
//
// ZCCCompiler :: NodeFromSymbolType
//
// Returns a new AST type ref node with the symbol's content.
//
//==========================================================================
ZCC_ExprTypeRef *ZCCCompiler::NodeFromSymbolType(PSymbolType *sym, ZCC_Expression *idnode)
{
ZCC_ExprTypeRef *ref = static_cast<ZCC_ExprTypeRef *>(AST.InitNode(sizeof(*ref), AST_ExprTypeRef, idnode));
ref->Operation = PEX_TypeRef;
ref->RefType = sym->Type;
ref->Type = NewClassPointer(RUNTIME_CLASS(PType));
return ref;
}
//==========================================================================
//
// ZCCCompiler :: CompileAllFields
//
// builds the internal structure of all classes and structs
//
//==========================================================================
void ZCCCompiler::CompileAllFields()
{
// Create copies of the arrays which can be altered
auto Classes = this->Classes;
auto Structs = this->Structs;
// first step: Look for native classes with native children.
// These may not have any variables added to them because it'd clash with the native definitions.
for (unsigned i = 0; i < Classes.Size(); i++)
{
auto c = Classes[i];
if (c->Type()->Size != TentativeClass && c->Fields.Size() > 0)
{
// We need to search the global class table here because not all children may have a scripted definition attached.
for (auto ac : PClass::AllClasses)
{
if (ac->ParentClass == c->Type() && ac->Size != TentativeClass)
{
Error(c->cls, "Trying to add fields to class '%s' with native children", c->Type()->TypeName.GetChars());
Classes.Delete(i--);
break;
}
}
}
}
bool donesomething = true;
while (donesomething && (Structs.Size() > 0 || Classes.Size() > 0))
{
donesomething = false;
for (unsigned i = 0; i < Structs.Size(); i++)
{
if (CompileFields(Structs[i]->Type(), Structs[i]->Fields, Structs[i]->Outer, &Structs[i]->TreeNodes, true))
{
// Remove from the list if all fields got compiled.
Structs.Delete(i--);
donesomething = true;
}
}
for (unsigned i = 0; i < Classes.Size(); i++)
{
auto type = Classes[i]->Type();
if (type->Size == TentativeClass)
{
if (type->ParentClass->Size == TentativeClass)
{
// we do not know the parent class's size yet, so skip this class for now.
continue;
}
else
{
// Inherit the size of the parent class
type->Size = Classes[i]->Type()->ParentClass->Size;
}
}
if (CompileFields(type, Classes[i]->Fields, nullptr, &Classes[i]->TreeNodes, false))
{
// Remove from the list if all fields got compiled.
Classes.Delete(i--);
donesomething = true;
}
}
}
2016-10-09 20:01:23 +00:00
// This really should never happen, but if it does, let's better print an error.
for (auto s : Structs)
{
Error(s->strct, "Unable to resolve all fields for struct %s", FName(s->NodeName()).GetChars());
}
2016-10-09 20:01:23 +00:00
for (auto s : Classes)
{
Error(s->cls, "Unable to resolve all fields for class %s", FName(s->NodeName()).GetChars());
}
}
//==========================================================================
//
// ZCCCompiler :: CompileFields
//
// builds the internal structure of a single class or struct
//
//==========================================================================
bool ZCCCompiler::CompileFields(PStruct *type, TArray<ZCC_VarDeclarator *> &Fields, PClass *Outer, PSymbolTable *TreeNodes, bool forstruct)
{
while (Fields.Size() > 0)
{
auto field = Fields[0];
PType *fieldtype = DetermineType(type, field, field->Names->Name, field->Type, true, true);
// For structs only allow 'deprecated', for classes exclude function qualifiers.
int notallowed = forstruct? ~ZCC_Deprecated : ZCC_Latent | ZCC_Final | ZCC_Action | ZCC_Static | ZCC_FuncConst | ZCC_Abstract;
if (field->Flags & notallowed)
{
Error(field, "Invalid qualifiers for %s (%s not allowed)", FName(field->Names->Name).GetChars(), FlagsToString(field->Flags & notallowed));
field->Flags &= notallowed;
}
uint32_t varflags = 0;
// These map directly to implementation flags.
if (field->Flags & ZCC_Private) varflags |= VARF_Private;
if (field->Flags & ZCC_Protected) varflags |= VARF_Protected;
if (field->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (field->Flags & ZCC_ReadOnly) varflags |= VARF_ReadOnly;
if (field->Flags & ZCC_Native)
{
// todo: get the native address of this field.
}
if (field->Flags & ZCC_Meta)
{
varflags |= VARF_ReadOnly; // metadata implies readonly
// todo: this needs to go into the metaclass and needs some handling
}
if (field->Type->ArraySize != nullptr)
{
fieldtype = ResolveArraySize(fieldtype, field->Type->ArraySize, &type->Symbols);
}
auto name = field->Names;
do
{
if (AddTreeNode(name->Name, name, TreeNodes, !forstruct))
{
auto thisfieldtype = fieldtype;
if (name->ArraySize != nullptr)
{
thisfieldtype = ResolveArraySize(thisfieldtype, name->ArraySize, &type->Symbols);
}
type->AddField(name->Name, thisfieldtype, varflags);
}
name = static_cast<ZCC_VarName*>(name->SiblingNext);
} while (name != field->Names);
Fields.Delete(0);
}
return Fields.Size() == 0;
}
//==========================================================================
//
// ZCCCompiler :: FieldFlagsToString
//
// creates a string for a field's flags
//
//==========================================================================
FString ZCCCompiler::FlagsToString(uint32_t flags)
{
const char *flagnames[] = { "native", "static", "private", "protected", "latent", "final", "meta", "action", "deprecated", "readonly", "funcconst", "abstract" };
FString build;
for (int i = 0; i < 12; i++)
{
if (flags & (1 << i))
{
if (build.IsNotEmpty()) build += ", ";
build += flagnames[i];
}
}
return build;
}
//==========================================================================
//
// ZCCCompiler :: DetermineType
//
// retrieves the type for this field, for arrays the type of a single entry.
//
//==========================================================================
PType *ZCCCompiler::DetermineType(PType *outertype, ZCC_TreeNode *field, FName name, ZCC_Type *ztype, bool allowarraytypes, bool formember)
{
PType *retval = TypeError;
if (!allowarraytypes && ztype->ArraySize != nullptr)
{
Error(field, "%s: Array type not allowed", name.GetChars());
return TypeError;
}
switch (ztype->NodeType)
{
case AST_BasicType:
{
auto btype = static_cast<ZCC_BasicType *>(ztype);
switch (btype->Type)
{
case ZCC_SInt8:
retval = TypeSInt8;
break;
case ZCC_UInt8:
retval = TypeUInt8;
break;
case ZCC_SInt16:
retval = TypeSInt16;
break;
case ZCC_UInt16:
retval = TypeUInt16;
break;
case ZCC_SInt32:
case ZCC_IntAuto: // todo: for enums, autoselect appropriately sized int
retval = TypeSInt32;
break;
case ZCC_UInt32:
retval = TypeUInt32;
break;
case ZCC_Bool:
retval = TypeBool;
break;
// Do we really want to allow single precision floats, despite all the problems they cause?
// These are nearly guaranteed to desync between MSVC and GCC on x87, because GCC does not implement an IEEE compliant mode
case ZCC_Float32:
case ZCC_FloatAuto:
//return TypeFloat32;
case ZCC_Float64:
retval = TypeFloat64;
break;
case ZCC_String:
retval = TypeString;
break;
case ZCC_Name:
retval = TypeName;
break;
case ZCC_Vector2:
retval = TypeVector2;
break;
case ZCC_Vector3:
retval = TypeVector3;
break;
case ZCC_Vector4:
// This has almost no use, so we really shouldn't bother.
Error(field, "vector<4> not implemented for %s", name.GetChars());
return TypeError;
case ZCC_State:
retval = TypeState;
break;
case ZCC_Color:
retval = TypeColor;
break;
case ZCC_Sound:
retval = TypeSound;
break;
case ZCC_UserType:
retval = ResolveUserType(btype, &outertype->Symbols);
break;
}
break;
}
case AST_MapType:
if (allowarraytypes)
{
Error(field, "%s: Map types not implemented yet", name.GetChars());
// Todo: Decide what we allow here and if it makes sense to allow more complex constructs.
auto mtype = static_cast<ZCC_MapType *>(ztype);
retval = NewMap(DetermineType(outertype, field, name, mtype->KeyType, false, false), DetermineType(outertype, field, name, mtype->ValueType, false, false));
break;
}
break;
case AST_DynArrayType:
if (allowarraytypes)
{
Error(field, "%s: Dynamic array types not implemented yet", name.GetChars());
auto atype = static_cast<ZCC_DynArrayType *>(ztype);
retval = NewDynArray(DetermineType(outertype, field, name, atype->ElementType, false, false));
break;
}
break;
case AST_ClassType:
{
auto ctype = static_cast<ZCC_ClassType *>(ztype);
if (ctype->Restriction == nullptr)
{
retval = NewClassPointer(RUNTIME_CLASS(DObject));
}
else
{
auto sym = outertype->Symbols.FindSymbol(ctype->Restriction->Id, true);
if (sym == nullptr) sym = GlobalSymbols.FindSymbol(ctype->Restriction->Id, false);
if (sym == nullptr)
{
Error(field, "%s: Unknown identifier", FName(ctype->Restriction->Id).GetChars());
return TypeError;
}
auto typesym = dyn_cast<PSymbolType>(sym);
if (typesym == nullptr || !typesym->Type->IsKindOf(RUNTIME_CLASS(PClass)))
{
Error(field, "%s does not represent a class type", FName(ctype->Restriction->Id).GetChars());
return TypeError;
}
retval = NewClassPointer(static_cast<PClass *>(typesym->Type));
}
break;
}
}
if (retval != TypeError && retval->MemberOnly && !formember)
{
Error(field, "Invalid type %s", retval->DescriptiveName()); // fixme: Types need a descriptive name that can be output here.
return TypeError;
}
return retval;
}
//==========================================================================
//
// ZCCCompiler :: ResolveUserType
//
// resolves a user type and returns a matching PType
//
//==========================================================================
PType *ZCCCompiler::ResolveUserType(ZCC_BasicType *type, PSymbolTable *symt)
{
// Check the symbol table for the identifier.
PSymbolTable *table;
PSymbol *sym = symt->FindSymbolInTable(type->UserType->Id, table);
// GlobalSymbols cannot be the parent of a class's symbol table so we have to look for global symbols explicitly.
if (sym == nullptr && symt != &GlobalSymbols) sym = GlobalSymbols.FindSymbolInTable(type->UserType->Id, table);
if (sym != nullptr && sym->IsKindOf(RUNTIME_CLASS(PSymbolType)))
{
auto type = static_cast<PSymbolType *>(sym)->Type;
if (type->IsKindOf(RUNTIME_CLASS(PEnum)))
{
return TypeSInt32; // hack this to an integer until we can resolve the enum mess.
}
return type;
}
return TypeError;
}
//==========================================================================
//
// ZCCCompiler :: ResolveArraySize
//
// resolves the array size and returns a matching type.
//
//==========================================================================
PType *ZCCCompiler::ResolveArraySize(PType *baseType, ZCC_Expression *arraysize, PSymbolTable *sym)
{
// The duplicate Simplify call is necessary because if the head node gets replaced there is no way to detect the end of the list otherwise.
arraysize = Simplify(arraysize, sym, true);
ZCC_Expression *val;
do
{
val = Simplify(arraysize, sym, true);
if (val->Operation != PEX_ConstValue || !val->Type->IsA(RUNTIME_CLASS(PInt)))
{
Error(arraysize, "Array index must be an integer constant");
return TypeError;
}
int size = static_cast<ZCC_ExprConstant *>(val)->IntVal;
if (size < 1)
{
Error(arraysize, "Array size must be positive");
return TypeError;
}
baseType = NewArray(baseType, size);
val = static_cast<ZCC_Expression *>(val->SiblingNext);
} while (val != arraysize);
return baseType;
}
//==========================================================================
//
// ZCCCompiler :: GetInt - Input must be a constant expression
//
//==========================================================================
int ZCCCompiler::GetInt(ZCC_Expression *expr)
{
if (expr->Type == TypeError)
{
return 0;
}
const PType::Conversion *route[CONVERSION_ROUTE_SIZE];
int routelen = expr->Type->FindConversion(TypeSInt32, route, countof(route));
if (routelen < 0)
{
Error(expr, "Cannot convert to integer");
return 0;
}
else
{
if (expr->Type->IsKindOf(RUNTIME_CLASS(PFloat)))
{
Warn(expr, "Truncation of floating point value");
}
auto ex = static_cast<ZCC_ExprConstant *>(ApplyConversion(expr, route, routelen));
return ex->IntVal;
}
}
double ZCCCompiler::GetDouble(ZCC_Expression *expr)
{
if (expr->Type == TypeError)
{
return 0;
}
const PType::Conversion *route[CONVERSION_ROUTE_SIZE];
int routelen = expr->Type->FindConversion(TypeFloat64, route, countof(route));
if (routelen < 0)
{
Error(expr, "Cannot convert to float");
return 0;
}
else
{
auto ex = static_cast<ZCC_ExprConstant *>(ApplyConversion(expr, route, routelen));
return ex->DoubleVal;
}
}
const char *ZCCCompiler::GetString(ZCC_Expression *expr, bool silent)
{
if (expr->Type == TypeError)
{
return nullptr;
}
else if (expr->Type->IsKindOf(RUNTIME_CLASS(PString)))
{
return static_cast<ZCC_ExprConstant *>(expr)->StringVal->GetChars();
}
else if (expr->Type->IsKindOf(RUNTIME_CLASS(PName)))
{
// Ugh... What a mess...
return FName(ENamedName(static_cast<ZCC_ExprConstant *>(expr)->IntVal)).GetChars();
}
else
{
if (!silent) Error(expr, "Cannot convert to string");
return nullptr;
}
}
//==========================================================================
//
// Parses an actor property's parameters and calls the handler
//
//==========================================================================
void ZCCCompiler::DispatchProperty(FPropertyInfo *prop, ZCC_PropertyStmt *property, AActor *defaults, Baggage &bag)
{
static TArray<FPropParam> params;
static TArray<FString> strings;
params.Clear();
strings.Clear();
params.Reserve(1);
params[0].i = 0;
if (prop->params[0] != '0')
{
if (property->Values == nullptr)
{
Error(property, "%s: arguments missing", prop->name);
return;
}
property->Values = Simplify(property->Values, &bag.Info->Symbols, true); // need to do this before the loop so that we can find the head node again.
const char * p = prop->params;
auto exp = property->Values;
while (true)
{
FPropParam conv;
FPropParam pref;
if (exp->NodeType != AST_ExprConstant)
{
// If we get TypeError, there has already been a message from deeper down so do not print another one.
if (exp->Type != TypeError) Error(exp, "%s: non-constant parameter", prop->name);
return;
}
conv.s = nullptr;
pref.s = nullptr;
pref.i = -1;
switch ((*p) & 223)
{
case 'X': // Expression in parentheses or number. We only support the constant here. The function will have to be handled by a separate property to get past the parser.
conv.i = GetInt(exp);
params.Push(conv);
conv.exp = nullptr;
break;
case 'I':
case 'M': // special case for morph styles in DECORATE . This expression-aware parser will not need this.
case 'N': // special case for thing activations in DECORATE. This expression-aware parser will not need this.
conv.i = GetInt(exp);
break;
case 'F':
conv.d = GetDouble(exp);
break;
case 'Z': // an optional string. Does not allow any numeric value.
if (!GetString(exp, true))
{
// apply this expression to the next argument on the list.
params.Push(conv);
params[0].i++;
p++;
continue;
}
conv.s = GetString(exp);
break;
case 'C': // this parser accepts colors only in string form.
pref.i = 1;
case 'S':
conv.s = GetString(exp);
break;
case 'T': // a filtered string
conv.s = strings[strings.Reserve(1)] = strbin1(GetString(exp));
break;
case 'L': // Either a number or a list of strings
if (!GetString(exp, true))
{
pref.i = 0;
conv.i = GetInt(exp);
}
else
{
pref.i = 1;
params.Push(pref);
params[0].i++;
do
{
conv.s = GetString(exp);
if (conv.s != nullptr)
{
params.Push(conv);
params[0].i++;
}
exp = Simplify(static_cast<ZCC_Expression *>(exp->SiblingNext), &bag.Info->Symbols, true);
} while (exp != property->Values);
goto endofparm;
}
break;
default:
assert(false);
break;
}
if (pref.i != -1)
{
params.Push(pref);
params[0].i++;
}
params.Push(conv);
params[0].i++;
exp = Simplify(static_cast<ZCC_Expression *>(exp->SiblingNext), &bag.Info->Symbols, true);
endofparm:
p++;
// Skip the DECORATE 'no comma' marker
if (*p == '_') p++;
if (*p == 0)
{
if (exp != property->Values)
{
Error(property, "Too many values for '%s'", prop->name);
return;
}
break;
}
else if (exp == property->Values)
{
if (*p < 'a')
{
Error(property, "Insufficient parameters for %s", prop->name);
return;
}
break;
}
}
}
// call the handler
try
{
prop->Handler(defaults, bag.Info, bag, &params[0]);
}
catch (CRecoverableError &error)
{
Error(property, "%s", error.GetMessage());
}
}
//==========================================================================
//
// Parses an actor property
//
//==========================================================================
void ZCCCompiler::ProcessDefaultProperty(PClassActor *cls, ZCC_PropertyStmt *prop, Baggage &bag)
{
auto namenode = prop->Prop;
FString propname;
if (namenode->SiblingNext == namenode)
{
// a one-name property
propname = FName(namenode->Id);
}
else if (namenode->SiblingNext->SiblingNext == namenode)
{
// a two-name property
propname << FName(namenode->Id) << "." << FName(static_cast<ZCC_Identifier *>(namenode->SiblingNext)->Id);
}
else
{
Error(prop, "Property name may at most contain two parts");
return;
}
FPropertyInfo *property = FindProperty(propname);
if (property != nullptr && property->category != CAT_INFO)
{
if (cls->IsDescendantOf(*property->cls))
{
DispatchProperty(property, prop, (AActor *)bag.Info->Defaults, bag);
}
else
{
Error(prop, "'%s' requires an actor of type '%s'\n", propname.GetChars(), (*property->cls)->TypeName.GetChars());
}
}
else
{
Error(prop, "'%s' is an unknown actor property\n", propname.GetChars());
}
}
//==========================================================================
//
// Finds a flag and sets or clears it
//
//==========================================================================
void ZCCCompiler::ProcessDefaultFlag(PClassActor *cls, ZCC_FlagStmt *flg)
{
auto namenode = flg->name;
const char *n1 = FName(namenode->Id).GetChars(), *n2;
if (namenode->SiblingNext == namenode)
{
// a one-name flag
n2 = nullptr;
}
else if (namenode->SiblingNext->SiblingNext == namenode)
{
// a two-name flag
n2 = FName(static_cast<ZCC_Identifier *>(namenode->SiblingNext)->Id).GetChars();
}
else
{
Error(flg, "Flag name may at most contain two parts");
return;
}
auto fd = FindFlag(cls, n1, n2, true);
if (fd != nullptr)
{
if (fd->structoffset == -1)
{
Warn(flg, "Deprecated flag '%s%s%s' used", n1, n2 ? "." : "", n2 ? n2 : "");
HandleDeprecatedFlags((AActor*)cls->Defaults, cls, flg->set, fd->flagbit);
}
else
{
ModActorFlag((AActor*)cls->Defaults, fd, flg->set);
}
}
else
{
Error(flg, "Unknown flag '%s%s%s'", n1, n2 ? "." : "", n2 ? n2 : "");
}
}
//==========================================================================
//
// Parses the default list
//
//==========================================================================
void ZCCCompiler::InitDefaults()
{
for (auto c : Classes)
{
// This may be removed if the conditions change, but right now only subclasses of Actor can define a Default block.
if (!c->Type()->IsDescendantOf(RUNTIME_CLASS(AActor)))
{
if (c->Defaults.Size()) Error(c->cls, "%s: Non-actor classes may not have defaults", c->Type()->TypeName.GetChars());
}
else
{
// This should never happen.
if (c->Type()->Defaults != nullptr)
{
Error(c->cls, "%s already has defaults", c->Type()->TypeName.GetChars());
}
// This can only occur if a native parent is not initialized. In all other cases the sorting of the class list should prevent this from ever happening.
else if (c->Type()->ParentClass->Defaults == nullptr && c->Type() != RUNTIME_CLASS(AActor))
{
Error(c->cls, "Parent class %s of %s is not initialized", c->Type()->ParentClass->TypeName.GetChars(), c->Type()->TypeName.GetChars());
}
else
{
// Copy the parent's defaults and meta data.
auto ti = static_cast<PClassActor *>(c->Type());
ti->InitializeNativeDefaults();
ti->ParentClass->DeriveData(ti);
Baggage bag;
#ifdef _DEBUG
bag.ClassName = c->Type()->TypeName;
#endif
bag.Info = ti;
bag.DropItemSet = false;
bag.StateSet = false;
bag.fromZScript = true;
bag.CurrentState = 0;
bag.Lumpnum = c->cls->SourceLump;
bag.DropItemList = nullptr;
bag.ScriptPosition.StrictErrors = true;
// The actual script position needs to be set per property.
for (auto d : c->Defaults)
{
auto content = d->Content;
if (content != nullptr) do
{
switch (content->NodeType)
{
case AST_PropertyStmt:
bag.ScriptPosition.FileName = *content->SourceName;
bag.ScriptPosition.ScriptLine = content->SourceLoc;
ProcessDefaultProperty(ti, static_cast<ZCC_PropertyStmt *>(content), bag);
break;
case AST_FlagStmt:
ProcessDefaultFlag(ti, static_cast<ZCC_FlagStmt *>(content));
break;
}
content = static_cast<decltype(content)>(content->SiblingNext);
} while (content != d->Content);
}
if (bag.DropItemSet)
{
bag.Info->SetDropItems(bag.DropItemList);
}
}
}
}
}
//==========================================================================
//
// Parses the functions list
//
//==========================================================================
void ZCCCompiler::InitFunctions()
{
TArray<PType *> rets(1);
TArray<PType *> args;
TArray<uint32_t> argflags;
TArray<FName> argnames;
for (auto c : Classes)
{
for (auto f : c->Functions)
{
rets.Clear();
args.Clear();
argflags.Clear();
// For the time being, let's not allow overloading. This may be reconsidered later but really just adds an unnecessary amount of complexity here.
if (AddTreeNode(f->Name, f, &c->TreeNodes, false))
{
auto t = f->Type;
if (t != nullptr)
{
do
{
auto type = DetermineType(c->Type(), f, f->Name, t, false, false);
if (type->IsKindOf(RUNTIME_CLASS(PStruct)))
{
// structs and classes only get passed by pointer.
type = NewPointer(type);
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
rets.Push(type);
t = static_cast<decltype(t)>(t->SiblingNext);
} while (t != f->Type);
}
int notallowed = ZCC_Latent | ZCC_Meta | ZCC_ReadOnly | ZCC_FuncConst | ZCC_Abstract;
if (f->Flags & notallowed)
{
Error(f, "Invalid qualifiers for %s (%s not allowed)", FName(f->Name).GetChars(), FlagsToString(f->Flags & notallowed));
f->Flags &= notallowed;
}
uint32_t varflags = VARF_Method;
AFuncDesc *afd = nullptr;
// map to implementation flags.
if (f->Flags & ZCC_Private) varflags |= VARF_Private;
if (f->Flags & ZCC_Protected) varflags |= VARF_Protected;
if (f->Flags & ZCC_Deprecated) varflags |= VARF_Deprecated;
if (f->Flags & ZCC_Action) varflags |= VARF_Action|VARF_Final; // Action implies Final.
if (f->Flags & ZCC_Static) varflags = (varflags & ~VARF_Method) | VARF_Final; // Static implies Final.
if ((f->Flags & (ZCC_Action | ZCC_Static)) == (ZCC_Action | ZCC_Static))
{
Error(f, "%s: Action and Static on the same function is not allowed.", FName(f->Name).GetChars());
varflags |= VARF_Method;
}
if (f->Flags & ZCC_Native)
{
varflags |= VARF_Native;
afd = FindFunction(FName(f->Name).GetChars());
if (afd == nullptr)
{
Error(f, "The function '%s' has not been exported from the executable.", FName(f->Name).GetChars());
}
}
SetImplicitArgs(&args, &argflags, &argnames, c->Type(), varflags);
auto p = f->Params;
if (p != nullptr)
{
do
{
if (p->Type != nullptr)
{
auto type = DetermineType(c->Type(), p, f->Name, p->Type, false, false);
int flags;
if (p->Flags & ZCC_In) flags |= VARF_In;
if (p->Flags & ZCC_Out) flags |= VARF_Out;
if (p->Default != nullptr)
{
auto val = Simplify(p->Default, &c->Type()->Symbols, true);
flags |= VARF_Optional;
if (val->Operation != PEX_ConstValue)
{
Error(c->cls, "Default parameter %s is not constant in %s", FName(p->Name).GetChars(), FName(f->Name).GetChars());
}
// Todo: Store and handle the default value (native functions will discard it anyway but for scripted ones this should be done decently.)
}
// TBD: disallow certain types? For now, let everything pass that isn't an array.
args.Push(type);
argflags.Push(flags);
}
else
{
args.Push(nullptr);
argflags.Push(0);
}
p = static_cast<decltype(p)>(p->SiblingNext);
} while (p != f->Params);
}
PFunction *sym = new PFunction(c->Type(), f->Name);
sym->AddVariant(NewPrototype(rets, args), argflags, argnames, afd == nullptr? nullptr : *(afd->VMPointer), varflags);
c->Type()->Symbols.ReplaceSymbol(sym);
// todo: Check inheritance.
// todo: Process function bodies.
}
}
}
}
//==========================================================================
//
// very complicated check for random duration.
//
//==========================================================================
static bool CheckRandom(ZCC_Expression *duration)
{
if (duration->NodeType != AST_ExprFuncCall) return false;
auto func = static_cast<ZCC_ExprFuncCall *>(duration);
if (func->Function == nullptr) return false;
if (func->Function->NodeType != AST_ExprID) return false;
auto f2 = static_cast<ZCC_ExprID *>(func->Function);
return f2->Identifier == NAME_Random;
}
//==========================================================================
//
// Sets up the action function call
//
//==========================================================================
FxExpression *ZCCCompiler::SetupActionFunction(PClassActor *cls, ZCC_TreeNode *af)
{
// We have 3 cases to consider here:
// 1. An action function without parameters. This can be called directly
// 2. An action functon with parameters or a non-action function. This needs to be wrapped into a helper function to set everything up.
// 3. An anonymous function.
// 1. and 2. are exposed through AST_ExprFunctionCall
if (af->NodeType == AST_ExprFuncCall)
{
auto fc = static_cast<ZCC_ExprFuncCall *>(af);
assert(fc->Function->NodeType == AST_ExprID);
auto id = static_cast<ZCC_ExprID *>(fc->Function);
// We must skip ACS_NamedExecuteWithResult here, because this name both exists as an action function and as a builtin.
// The code which gets called from here can easily make use of the builtin, so let's just pass this name without any checks.
// The actual action function is still needed by DECORATE:
if (id->Identifier != NAME_ACS_NamedExecuteWithResult)
{
PFunction *afd = dyn_cast<PFunction>(cls->Symbols.FindSymbol(id->Identifier, true));
if (afd != nullptr)
{
if (fc->Parameters == nullptr && (afd->Variants[0].Flags & VARF_Action))
{
// We can use this function directly without wrapping it in a caller.
return new FxVMFunctionCall(afd, nullptr, *af, true);
}
}
else
{
// it may also be an action special so check that first before printing an error.
if (!P_FindLineSpecial(FName(id->Identifier).GetChars()))
{
Error(af, "%s: action function not found in %s", FName(id->Identifier).GetChars(), cls->TypeName.GetChars());
return nullptr;
}
// Action specials fall through to the code generator.
}
}
}
ConvertClass = cls;
return ConvertAST(af);
}
//==========================================================================
//
// Compile the states
//
//==========================================================================
void ZCCCompiler::CompileStates()
{
for (auto c : Classes)
{
if (!c->Type()->IsDescendantOf(RUNTIME_CLASS(AActor)))
{
Error(c->cls, "%s: States can only be defined for actors.", c->Type()->TypeName.GetChars());
continue;
}
FString statename; // The state builder wants the label as one complete string, not separated into tokens.
FStateDefinitions statedef;
statedef.MakeStateDefines(dyn_cast<PClassActor>(c->Type()->ParentClass));
for (auto s : c->States)
{
auto st = s->Body;
if (st != nullptr) do
{
switch (st->NodeType)
{
case AST_StateLabel:
{
auto sl = static_cast<ZCC_StateLabel *>(st);
statename = FName(sl->Label);
statedef.AddStateLabel(statename);
break;
}
case AST_StateLine:
{
auto sl = static_cast<ZCC_StateLine *>(st);
FState state;
memset(&state, 0, sizeof(state));
if (sl->Sprite->Len() != 4)
{
Error(sl, "Sprite name must be exactly 4 characters. Found '%s'", sl->Sprite->GetChars());
}
else
{
state.sprite = GetSpriteIndex(sl->Sprite->GetChars());
}
// It is important to call CheckRandom before Simplify, because Simplify will resolve the function's name to nonsense
if (CheckRandom(sl->Duration))
{
auto func = static_cast<ZCC_ExprFuncCall *>(Simplify(sl->Duration, &c->Type()->Symbols, true));
if (func->Parameters == func->Parameters->SiblingNext || func->Parameters != func->Parameters->SiblingNext->SiblingNext)
{
Error(sl, "Random duration requires exactly 2 parameters");
}
int v1 = GetInt(func->Parameters->Value);
int v2 = GetInt(static_cast<ZCC_FuncParm *>(func->Parameters->SiblingNext)->Value);
if (v1 > v2) std::swap(v1, v2);
state.Tics = (int16_t)clamp<int>(v1, 0, INT16_MAX);
state.TicRange = (uint16_t)clamp<int>(v2 - v1, 0, UINT16_MAX);
}
else
{
auto duration = Simplify(sl->Duration, &c->Type()->Symbols, true);
if (duration->Operation == PEX_ConstValue)
{
state.Tics = (int16_t)clamp<int>(GetInt(duration), -1, INT16_MAX);
state.TicRange = 0;
}
else
{
Error(sl, "Duration is not a constant");
}
}
state.Fullbright = sl->bBright;
state.Fast = sl->bFast;
state.Slow = sl->bSlow;
state.CanRaise = sl->bCanRaise;
if ((state.NoDelay = sl->bNoDelay))
{
if (statedef.GetStateLabelIndex(NAME_Spawn) != statedef.GetStateCount())
{
Warn(sl, "NODELAY only has an effect on the first state after 'Spawn:'");
}
}
if (sl->Offset != nullptr)
{
auto o1 = static_cast<ZCC_Expression *>(Simplify(sl->Offset, &c->Type()->Symbols, true));
auto o2 = static_cast<ZCC_Expression *>(Simplify(static_cast<ZCC_Expression *>(o1->SiblingNext), &c->Type()->Symbols, true));
if (o1->Operation != PEX_ConstValue || o2->Operation != PEX_ConstValue)
{
Error(o1, "State offsets must be constant");
}
else
{
state.Misc1 = GetInt(o1);
state.Misc2 = GetInt(o2);
}
}
#ifdef DYNLIGHT
if (sl->Lights != nullptr)
{
auto l = sl->Lights;
do
{
AddStateLight(&state, GetString(l));
l = static_cast<decltype(l)>(l->SiblingNext);
} while (l != sl->Lights);
}
#endif
if (sl->Action != nullptr)
{
auto code = SetupActionFunction(static_cast<PClassActor *>(c->Type()), sl->Action);
if (code != nullptr)
{
auto funcsym = CreateAnonymousFunction(c->Type(), nullptr, VARF_Method | VARF_Action);
state.ActionFunc = FunctionBuildList.AddFunction(funcsym, code, FStringf("%s.StateFunction.%d", c->Type()->TypeName.GetChars(), statedef.GetStateCount()), false);
}
}
int count = statedef.AddStates(&state, sl->Frames->GetChars());
if (count < 0)
{
Error(sl, "Invalid frame character string '%s'", sl->Frames->GetChars());
count = -count;
}
break;
}
case AST_StateGoto:
{
auto sg = static_cast<ZCC_StateGoto *>(st);
statename = "";
if (sg->Qualifier != nullptr)
{
statename << FName(sg->Qualifier->Id) << "::";
}
auto part = sg->Label;
do
{
statename << FName(part->Id) << '.';
part = static_cast<decltype(part)>(part->SiblingNext);
} while (part != sg->Label);
statename.Truncate((long)statename.Len() - 1); // remove the last '.' in the label name
if (sg->Offset != nullptr)
{
auto ofs = Simplify(sg->Offset, &c->Type()->Symbols, true);
if (ofs->Operation != PEX_ConstValue)
{
Error(sg, "Constant offset expected for GOTO");
}
else
{
int offset = GetInt(ofs);
if (offset < 0)
{
Error(sg, "GOTO offset must be positive");
offset = 0;
}
if (offset > 0)
{
statename.AppendFormat("+%d", offset);
}
}
}
if (!statedef.SetGotoLabel(statename))
{
Error(sg, "GOTO before first state");
}
break;
}
case AST_StateFail:
case AST_StateWait:
if (!statedef.SetWait())
{
Error(st, "%s before first state", st->NodeType == AST_StateFail ? "Fail" : "Wait");
continue;
}
break;
case AST_StateLoop:
if (!statedef.SetLoop())
{
Error(st, "LOOP before first state");
continue;
}
break;
case AST_StateStop:
if (!statedef.SetStop())
{
Error(st, "STOP before first state");
}
break;
default:
assert(0 && "Bad AST node in state");
}
st = static_cast<decltype(st)>(st->SiblingNext);
} while (st != s->Body);
}
try
{
static_cast<PClassActor *>(c->Type())->Finalize(statedef);
}
catch (CRecoverableError &err)
{
Error(c->cls, "%s", err.GetMessage());
}
}
}
//==========================================================================
//
// Convert the AST data for the code generator.
//
//==========================================================================
FxExpression *ZCCCompiler::ConvertAST(ZCC_TreeNode *ast)
{
// there are two possibilities here: either a single function call or a compound statement. For a compound statement we also need to check if the last thing added was a return.
if (ast->NodeType == AST_ExprFuncCall)
{
return new FxReturnStatement(ConvertNode(ast), *ast);
}
else
{
// This must be done here so that we can check for a trailing return statement.
auto x = new FxCompoundStatement(*ast);
auto compound = static_cast<ZCC_CompoundStmt *>(ast);
bool isreturn = false;
auto node = compound->Content;
if (node != nullptr) do
{
x->Add(ConvertNode(node));
isreturn = node->NodeType == AST_ReturnStmt;
node = static_cast<decltype(node)>(node->SiblingNext);
} while (node != compound->Content);
if (!isreturn) x->Add(new FxReturnStatement(nullptr, *ast));
return x;
}
}
#define xx(a,z) z,
static int Pex2Tok[] = {
#include "zcc_exprlist.h"
};
//==========================================================================
//
// Helper for modify/assign operators
//
//==========================================================================
static FxExpression *ModifyAssign(FxBinary *operation, FxExpression *left)
{
auto assignself = static_cast<FxAssignSelf *>(operation->left);
auto assignment = new FxAssign(left, operation);
assignself->Assignment = assignment;
return assignment;
}
//==========================================================================
//
// Convert an AST node and its children
//
//==========================================================================
FxExpression *ZCCCompiler::ConvertNode(ZCC_TreeNode *ast)
{
// Note: Do not call 'Simplify' here because that function tends to destroy identifiers due to lack of context in which to resolve them.
// The Fx nodes created here will be better suited for that.
switch (ast->NodeType)
{
case AST_ExprFuncCall:
{
auto fcall = static_cast<ZCC_ExprFuncCall *>(ast);
// function names can either be
// - plain identifiers
// - class members
// - array syntax for random() calls.
// Everything else coming here is a syntax error.
switch (fcall->Function->NodeType)
{
case AST_ExprID:
// The function name is a simple identifier.
return new FxFunctionCall(static_cast<ZCC_ExprID *>(fcall->Function)->Identifier, NAME_None, ConvertNodeList(fcall->Parameters), *ast);
// not yet done
case AST_ExprTypeRef:
case AST_SwitchStmt:
case AST_CaseStmt:
case AST_ExprMemberAccess:
// calling a class member through its pointer
// todo.
break;
case AST_ExprBinary:
// Array syntax for randoms. They are internally stored as ExprBinary with both an identifier on the left and right side.
if (fcall->Function->Operation == PEX_ArrayAccess)
{
auto binary = static_cast<ZCC_ExprBinary *>(fcall->Function);
if (binary->Left->NodeType == AST_ExprID && binary->Right->NodeType == AST_ExprID)
{
return new FxFunctionCall(static_cast<ZCC_ExprID *>(binary->Left)->Identifier, static_cast<ZCC_ExprID *>(binary->Right)->Identifier, ConvertNodeList(fcall->Parameters), *ast);
}
}
// fall through if this isn't an array access node.
default:
Error(fcall, "Invalid function identifier");
return new FxNop(*ast); // return something so that the compiler can continue.
}
break;
}
case AST_FuncParm:
{
auto fparm = static_cast<ZCC_FuncParm *>(ast);
// ignore the label for now, that's stuff for far later, when a bit more here is working.
return ConvertNode(fparm->Value);
}
case AST_ExprID:
{
auto id = static_cast<ZCC_ExprID *>(ast);
return new FxIdentifier(id->Identifier, *ast);
}
case AST_ExprConstant:
{
auto cnst = static_cast<ZCC_ExprConstant *>(ast);
if (cnst->Type->IsA(RUNTIME_CLASS(PName)))
{
return new FxConstant(FName(ENamedName(cnst->IntVal)), *ast);
}
else if (cnst->Type->IsA(RUNTIME_CLASS(PInt)))
{
return new FxConstant(cnst->IntVal, *ast);
}
else if (cnst->Type->IsA(RUNTIME_CLASS(PBool)))
{
return new FxConstant(!!cnst->IntVal, *ast);
}
else if (cnst->Type->IsA(RUNTIME_CLASS(PFloat)))
{
return new FxConstant(cnst->DoubleVal, *ast);
}
else if (cnst->Type->IsA(RUNTIME_CLASS(PString)))
{
return new FxConstant(*cnst->StringVal, *ast);
}
else
{
// can there be other types?
Error(cnst, "Unknown constant type %s", cnst->Type->DescriptiveName());
return new FxConstant(0, *ast);
}
}
case AST_ExprUnary:
{
auto unary = static_cast<ZCC_ExprUnary *>(ast);
auto operand = ConvertNode(unary->Operand);
auto op = unary->Operation;
switch (op)
{
case PEX_PostDec:
case PEX_PostInc:
return new FxPostIncrDecr(operand, Pex2Tok[op]);
case PEX_PreDec:
case PEX_PreInc:
return new FxPreIncrDecr(operand, Pex2Tok[op]);
case PEX_Negate:
return new FxMinusSign(operand);
case PEX_AntiNegate:
return new FxPlusSign(operand);
case PEX_BitNot:
return new FxUnaryNotBitwise(operand);
case PEX_BoolNot:
return new FxUnaryNotBoolean(operand);
case PEX_SizeOf:
case PEX_AlignOf:
return new FxSizeAlign(operand, Pex2Tok[op]);
default:
assert(0 && "Unknown unary operator."); // should never happen
Error(unary, "Unknown unary operator ID #%d", op);
return new FxNop(*ast);
}
break;
}
case AST_ExprBinary:
{
auto binary = static_cast<ZCC_ExprBinary *>(ast);
auto left = ConvertNode(binary->Left);
auto right = ConvertNode(binary->Right);
auto op = binary->Operation;
auto tok = Pex2Tok[op];
switch (op)
{
case PEX_Add:
case PEX_Sub:
return new FxAddSub(tok, left, right);
case PEX_Mul:
case PEX_Div:
case PEX_Mod:
return new FxMulDiv(tok, left, right);
case PEX_Pow:
return new FxPow(left, right);
case PEX_LeftShift:
case PEX_RightShift:
case PEX_URightShift:
case PEX_BitAnd:
case PEX_BitOr:
case PEX_BitXor:
return new FxBinaryInt(tok, left, right);
case PEX_BoolOr:
case PEX_BoolAnd:
return new FxBinaryLogical(tok, left, right);
case PEX_LT:
case PEX_LTEQ:
case PEX_GT:
case PEX_GTEQ:
return new FxCompareRel(tok, left, right);
case PEX_EQEQ:
case PEX_NEQ:
return new FxCompareEq(tok, left, right);
case PEX_Assign:
return new FxAssign(left, right);
case PEX_AddAssign:
case PEX_SubAssign:
return ModifyAssign(new FxAddSub(tok, new FxAssignSelf(*ast), right), left);
case PEX_MulAssign:
case PEX_DivAssign:
case PEX_ModAssign:
return ModifyAssign(new FxMulDiv(tok, new FxAssignSelf(*ast), right), left);
case PEX_LshAssign:
case PEX_RshAssign:
case PEX_URshAssign:
case PEX_AndAssign:
case PEX_OrAssign:
case PEX_XorAssign:
return ModifyAssign(new FxBinaryInt(tok, new FxAssignSelf(*ast), right), left);
case PEX_LTGTEQ:
return new FxLtGtEq(left, right);
// todo: These do not have representations in DECORATE and no implementation exists yet.
case PEX_Concat:
case PEX_Is:
// more esoteric operators
case PEX_APREQ:
// vector operations will be done later.
case PEX_CrossProduct:
case PEX_DotProduct:
default:
I_Error("Binary operator %d not implemented yet", op);
}
break;
}
case AST_ExprTrinary:
{
auto trinary = static_cast<ZCC_ExprTrinary *>(ast);
auto condition = ConvertNode(trinary->Test);
auto left = ConvertNode(trinary->Left);
auto right = ConvertNode(trinary->Right);
return new FxConditional(condition, left, right);
}
case AST_LocalVarStmt:
{
auto loc = static_cast<ZCC_LocalVarStmt *>(ast);
auto node = loc->Vars;
FxSequence *list = new FxSequence(*ast);
do
{
// Type determination must be done for each field to properly handle array definitions.
PType *type = DetermineType(ConvertClass, node, node->Name, loc->Type, true, false);
if (type->IsKindOf(RUNTIME_CLASS(PArray)))
{
Error(loc, "Local array variables not implemented yet.");
}
else
{
FxExpression *val;
if (node->InitIsArray)
{
Error(node, "Tried to initialize %s with an array", FName(node->Name).GetChars());
val = nullptr;
}
else
{
val = node->Init ? ConvertNode(node->Init) : nullptr;
}
list->Add(new FxLocalVariableDeclaration(type, node->Name, val, 0, *node)); // todo: Handle flags in the grammar.
}
node = static_cast<decltype(node)>(node->SiblingNext);
} while (node != loc->Vars);
return list;
}
case AST_ExpressionStmt:
return ConvertNode(static_cast<ZCC_ExpressionStmt *>(ast)->Expression);
case AST_ReturnStmt:
{
auto ret = static_cast<ZCC_ReturnStmt *>(ast);
FArgumentList *args = ConvertNodeList(ret->Values);
if (args->Size() == 0)
{
return new FxReturnStatement(nullptr, *ast);
}
else if (args->Size() == 1)
{
return new FxReturnStatement((*args)[0], *ast);
}
else
{
Error(ast, "Return with multiple values not implemented yet.");
return new FxReturnStatement(nullptr, *ast);
}
}
case AST_BreakStmt:
case AST_ContinueStmt:
return new FxJumpStatement(ast->NodeType == AST_BreakStmt ? TK_Break : TK_Continue, *ast);
case AST_IfStmt:
{
auto iff = static_cast<ZCC_IfStmt *>(ast);
return new FxIfStatement(ConvertNode(iff->Condition), ConvertNode(iff->TruePath), ConvertNode(iff->FalsePath), *ast);
}
case AST_IterationStmt:
{
auto iter = static_cast<ZCC_IterationStmt *>(ast);
if (iter->CheckAt == ZCC_IterationStmt::End)
{
assert(iter->LoopBumper == nullptr);
return new FxDoWhileLoop(ConvertNode(iter->LoopCondition), ConvertNode(iter->LoopStatement), *ast);
}
else if (iter->LoopBumper != nullptr)
{
return new FxForLoop(nullptr, ConvertNode(iter->LoopCondition), ConvertNode(iter->LoopBumper), ConvertNode(iter->LoopStatement), *ast);
}
else
{
return new FxWhileLoop(ConvertNode(iter->LoopCondition), ConvertNode(iter->LoopStatement), *ast);
}
}
case AST_CompoundStmt:
{
auto x = new FxCompoundStatement(*ast);
auto compound = static_cast<ZCC_CompoundStmt *>(ast);
auto node = compound->Content;
if (node != nullptr) do
{
x->Add(ConvertNode(node));
node = static_cast<decltype(node)>(node->SiblingNext);
} while (node != compound->Content);
return x;
}
}
// only for development. I_Error is more convenient here than a normal error.
I_Error("ConvertNode encountered unsupported node of type %d", ast->NodeType);
return nullptr;
}
FArgumentList *ZCCCompiler::ConvertNodeList(ZCC_TreeNode *head)
{
FArgumentList *list = new FArgumentList;
if (head != nullptr)
{
auto node = head;
do
{
list->Push(ConvertNode(node));
node = node->SiblingNext;
} while (node != head);
}
return list;
}