2016-10-08 06:35:16 +00:00
/*
* * zcc_compile . cpp
* *
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* * Copyright - 2016 Randy Heit
2016-10-08 20:16:10 +00:00
* * Copyright 2016 Christoph Oelckers
2016-10-08 06:35:16 +00:00
* * 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 .
* * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* *
*/
2016-10-10 22:56:47 +00:00
# include "actor.h"
# include "thingdef.h"
2013-10-24 04:06:32 +00:00
# include "sc_man.h"
# include "c_console.h"
# include "c_dispatch.h"
2016-10-10 22:56:47 +00:00
# include "doomerrors.h"
2013-10-24 04:06:32 +00:00
# include "w_wad.h"
# include "cmdlib.h"
# include "m_alloc.h"
# include "zcc_parser.h"
2016-10-20 23:12:54 +00:00
# include "zcc-parse.h"
2013-10-24 04:06:32 +00:00
# include "zcc_compile.h"
# include "v_text.h"
2016-10-08 20:16:10 +00:00
# include "p_lnspec.h"
2016-10-13 22:40:20 +00:00
# include "i_system.h"
2013-10-24 04:06:32 +00:00
# include "gdtoa.h"
2016-10-15 08:43:02 +00:00
# include "codegeneration/codegen.h"
2016-10-12 22:53:59 +00:00
# include "vmbuilder.h"
2013-10-24 04:06:32 +00:00
2013-10-26 03:01:18 +00:00
# define DEFINING_CONST ((PSymbolConst *)(void *)1)
2016-10-08 13:27:20 +00:00
//==========================================================================
//
// ZCCCompiler :: ProcessClass
//
//==========================================================================
2016-10-09 07:09:17 +00:00
void ZCCCompiler : : ProcessClass ( ZCC_Class * cnode , PSymbolTreeNode * treenode )
2016-10-08 13:27:20 +00:00
{
2016-10-09 23:18:47 +00:00
Classes . Push ( new ZCC_ClassWork ( static_cast < ZCC_Class * > ( cnode ) , treenode ) ) ;
auto cls = Classes . Last ( ) ;
2016-10-08 13:27:20 +00:00
auto node = cnode - > Body ;
2016-10-09 07:09:17 +00:00
PSymbolTreeNode * childnode ;
2016-10-09 13:47:31 +00:00
ZCC_Enum * enumType = nullptr ;
2016-10-08 13:27:20 +00:00
2016-10-09 23:18:47 +00:00
FString name ;
name < < " nodes - " < < FName ( cnode - > NodeName ) ;
cls - > TreeNodes . SetName ( name ) ;
2016-10-13 18:45:52 +00:00
if ( cnode - > Replaces ! = nullptr & & ! static_cast < PClassActor * > ( cnode - > Type ) - > SetReplacement ( cnode - > Replaces - > Id ) )
2016-10-12 18:42:41 +00:00
{
Warn ( cnode , " Replaced type '%s' not found for %s " , FName ( cnode - > Replaces - > Id ) . GetChars ( ) , cnode - > Type - > TypeName . GetChars ( ) ) ;
}
2016-10-09 23:18:47 +00:00
2016-10-09 13:47:31 +00:00
// Need to check if the class actually has a body.
if ( node ! = nullptr ) do
2016-10-08 13:27:20 +00:00
{
switch ( node - > NodeType )
{
case AST_Struct :
case AST_ConstantDef :
2016-10-09 13:47:31 +00:00
case AST_Enum :
2016-10-09 23:18:47 +00:00
if ( ( childnode = AddTreeNode ( static_cast < ZCC_NamedNode * > ( node ) - > NodeName , node , & cls - > TreeNodes ) ) )
2016-10-08 13:27:20 +00:00
{
switch ( node - > NodeType )
{
2016-10-09 13:47:31 +00:00
case AST_Enum :
enumType = static_cast < ZCC_Enum * > ( node ) ;
2016-10-09 23:18:47 +00:00
cls - > Enums . Push ( enumType ) ;
2016-10-09 13:47:31 +00:00
break ;
case AST_Struct :
2016-10-09 23:18:47 +00:00
ProcessStruct ( static_cast < ZCC_Struct * > ( node ) , childnode , cls - > cls ) ;
2016-10-09 13:47:31 +00:00
break ;
case AST_ConstantDef :
2016-10-09 23:18:47 +00:00
cls - > Constants . Push ( static_cast < ZCC_ConstantDef * > ( node ) ) ;
cls - > Constants . Last ( ) - > Type = enumType ;
2016-10-09 13:47:31 +00:00
break ;
default :
assert ( 0 & & " Default case is just here to make GCC happy. It should never be reached " ) ;
2016-10-08 13:27:20 +00:00
}
}
break ;
2016-10-09 13:47:31 +00:00
case AST_VarDeclarator :
2016-10-09 23:18:47 +00:00
cls - > Fields . Push ( static_cast < ZCC_VarDeclarator * > ( node ) ) ;
2016-10-09 13:47:31 +00:00
break ;
case AST_EnumTerminator :
enumType = nullptr ;
break ;
2016-10-08 13:27:20 +00:00
case AST_States :
2016-10-12 15:50:23 +00:00
cls - > States . Push ( static_cast < ZCC_States * > ( node ) ) ;
2016-10-11 16:53:10 +00:00
break ;
2016-10-08 13:27:20 +00:00
case AST_FuncDeclarator :
2016-10-11 16:53:10 +00:00
cls - > Functions . Push ( static_cast < ZCC_FuncDeclarator * > ( node ) ) ;
2016-10-10 22:56:47 +00:00
break ;
2016-10-08 13:39:54 +00:00
case AST_Default :
2016-10-10 22:56:47 +00:00
cls - > Defaults . Push ( static_cast < ZCC_Default * > ( node ) ) ;
break ;
2016-10-08 13:39:54 +00:00
default :
assert ( 0 & & " Unhandled AST node type " ) ;
break ;
2016-10-08 13:27:20 +00:00
}
node = node - > SiblingNext ;
}
while ( node ! = cnode - > Body ) ;
}
2016-10-08 13:39:54 +00:00
//==========================================================================
//
2016-10-09 13:47:31 +00:00
// ZCCCompiler :: ProcessStruct
2016-10-08 13:39:54 +00:00
//
//==========================================================================
2016-10-09 13:47:31 +00:00
void ZCCCompiler : : ProcessStruct ( ZCC_Struct * cnode , PSymbolTreeNode * treenode , ZCC_Class * outer )
2016-10-08 13:39:54 +00:00
{
2016-10-09 23:18:47 +00:00
Structs . Push ( new ZCC_StructWork ( static_cast < ZCC_Struct * > ( cnode ) , treenode , outer ) ) ;
ZCC_StructWork * cls = Structs . Last ( ) ;
2016-10-08 13:39:54 +00:00
auto node = cnode - > Body ;
2016-10-09 07:09:17 +00:00
PSymbolTreeNode * childnode ;
2016-10-09 13:47:31 +00:00
ZCC_Enum * enumType = nullptr ;
2016-10-08 13:39:54 +00:00
2016-10-09 13:47:31 +00:00
// Need to check if the struct actually has a body.
if ( node ! = nullptr ) do
2016-10-08 13:39:54 +00:00
{
switch ( node - > NodeType )
{
case AST_ConstantDef :
2016-10-09 13:47:31 +00:00
case AST_Enum :
2016-10-09 23:18:47 +00:00
if ( ( childnode = AddTreeNode ( static_cast < ZCC_NamedNode * > ( node ) - > NodeName , node , & cls - > TreeNodes ) ) )
2016-10-08 13:39:54 +00:00
{
2016-10-09 07:09:17 +00:00
switch ( node - > NodeType )
{
2016-10-09 13:47:31 +00:00
case AST_Enum :
enumType = static_cast < ZCC_Enum * > ( node ) ;
2016-10-09 23:18:47 +00:00
cls - > Enums . Push ( enumType ) ;
2016-10-09 13:47:31 +00:00
break ;
case AST_ConstantDef :
2016-10-09 23:18:47 +00:00
cls - > Constants . Push ( static_cast < ZCC_ConstantDef * > ( node ) ) ;
cls - > Constants . Last ( ) - > Type = enumType ;
2016-10-09 13:47:31 +00:00
break ;
default :
assert ( 0 & & " Default case is just here to make GCC happy. It should never be reached " ) ;
2016-10-09 07:09:17 +00:00
}
2016-10-08 13:39:54 +00:00
}
break ;
2016-10-09 13:47:31 +00:00
case AST_VarDeclarator :
2016-10-09 23:18:47 +00:00
cls - > Fields . Push ( static_cast < ZCC_VarDeclarator * > ( node ) ) ;
2016-10-09 13:47:31 +00:00
break ;
case AST_EnumTerminator :
enumType = nullptr ;
break ;
2016-10-08 13:39:54 +00:00
default :
assert ( 0 & & " Unhandled AST node type " ) ;
break ;
}
node = node - > SiblingNext ;
}
while ( node ! = cnode - > Body ) ;
}
2013-10-24 04:06:32 +00:00
//==========================================================================
//
// ZCCCompiler Constructor
//
//==========================================================================
2016-10-08 11:34:37 +00:00
ZCCCompiler : : ZCCCompiler ( ZCC_AST & ast , DObject * _outer , PSymbolTable & _symbols , PSymbolTable & _outsymbols )
2016-10-15 10:15:25 +00:00
: Outer ( _outer ) , GlobalTreeNodes ( & _symbols ) , OutputSymbols ( & _outsymbols ) , AST ( ast )
2013-10-24 04:06:32 +00:00
{
2016-10-15 10:15:25 +00:00
FScriptPosition : : ResetErrorCounter ( ) ;
2013-10-24 04:06:32 +00:00
// Group top-level nodes by type
if ( ast . TopNode ! = NULL )
{
ZCC_TreeNode * node = ast . TopNode ;
2016-10-08 13:27:20 +00:00
PSymbolTreeNode * tnode ;
2016-10-09 13:47:31 +00:00
PType * enumType = nullptr ;
ZCC_Enum * zenumType = nullptr ;
2013-10-24 04:06:32 +00:00
do
{
switch ( node - > NodeType )
{
2013-10-26 03:01:18 +00:00
case AST_Class :
case AST_Struct :
2016-03-13 03:22:29 +00:00
case AST_ConstantDef :
2016-10-09 13:47:31 +00:00
case AST_Enum :
2016-10-09 23:18:47 +00:00
if ( ( tnode = AddTreeNode ( static_cast < ZCC_NamedNode * > ( node ) - > NodeName , node , GlobalTreeNodes ) ) )
2013-10-26 03:01:18 +00:00
{
2016-03-13 03:22:29 +00:00
switch ( node - > NodeType )
{
2016-10-09 13:47:31 +00:00
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 " ) ;
2016-03-13 03:22:29 +00:00
}
2013-10-26 03:01:18 +00:00
}
break ;
2016-10-09 13:47:31 +00:00
case AST_EnumTerminator :
zenumType = nullptr ;
break ;
2013-10-26 03:01:18 +00:00
default :
assert ( 0 & & " Unhandled AST node type " ) ;
break ;
2013-10-24 04:06:32 +00:00
}
node = node - > SiblingNext ;
2016-10-09 13:47:31 +00:00
} while ( node ! = ast . TopNode ) ;
2013-10-24 04:06:32 +00:00
}
}
2016-10-09 23:18:47 +00:00
ZCCCompiler : : ~ ZCCCompiler ( )
{
for ( auto s : Structs )
{
delete s ;
}
for ( auto c : Classes )
{
delete c ;
}
Structs . Clear ( ) ;
Classes . Clear ( ) ;
}
2013-10-26 03:01:18 +00:00
//==========================================================================
//
2016-10-09 23:18:47 +00:00
// ZCCCompiler :: AddTreeNode
2013-10-26 03:01:18 +00:00
//
// Keeps track of definition nodes by their names. Ensures that all names
// in this scope are unique.
//
//==========================================================================
2016-10-09 23:18:47 +00:00
PSymbolTreeNode * ZCCCompiler : : AddTreeNode ( FName name , ZCC_TreeNode * node , PSymbolTable * treenodes , bool searchparents )
2013-10-26 03:01:18 +00:00
{
2016-10-09 23:18:47 +00:00
PSymbol * check = treenodes - > FindSymbol ( name , searchparents ) ;
2016-03-13 03:22:29 +00:00
if ( check ! = NULL )
2013-10-26 03:01:18 +00:00
{
2016-03-13 03:22:29 +00:00
assert ( check - > IsA ( RUNTIME_CLASS ( PSymbolTreeNode ) ) ) ;
Error ( node , " Attempt to redefine '%s' " , name . GetChars ( ) ) ;
Error ( static_cast < PSymbolTreeNode * > ( check ) - > Node , " Original definition is here " ) ;
2016-10-08 13:27:20 +00:00
return nullptr ;
2013-10-26 03:01:18 +00:00
}
else
{
2016-10-08 13:27:20 +00:00
auto sy = new PSymbolTreeNode ( name , node ) ;
2016-10-08 20:16:10 +00:00
FString name ;
2016-10-09 07:09:17 +00:00
treenodes - > AddSymbol ( sy ) ;
2016-10-08 13:27:20 +00:00
return sy ;
2013-10-26 03:01:18 +00:00
}
}
2013-10-24 04:06:32 +00:00
//==========================================================================
//
2016-03-13 03:22:29 +00:00
// ZCCCompiler :: Warn
2013-10-24 04:06:32 +00:00
//
2016-03-13 03:22:29 +00:00
// Prints a warning message, and increments WarnCount.
2013-10-24 04:06:32 +00:00
//
//==========================================================================
2016-03-13 03:22:29 +00:00
void ZCCCompiler : : Warn ( ZCC_TreeNode * node , const char * msg , . . . )
2013-10-24 04:06:32 +00:00
{
2016-03-13 03:22:29 +00:00
va_list argptr ;
va_start ( argptr , msg ) ;
MessageV ( node , TEXTCOLOR_ORANGE , msg , argptr ) ;
va_end ( argptr ) ;
2013-10-24 04:06:32 +00:00
2016-10-15 10:15:25 +00:00
FScriptPosition : : WarnCounter + + ;
2016-03-13 03:22:29 +00:00
}
2013-10-24 04:06:32 +00:00
2016-03-13 03:22:29 +00:00
//==========================================================================
//
// ZCCCompiler :: Error
//
// Prints an error message, and increments ErrorCount.
//
//==========================================================================
void ZCCCompiler : : Error ( ZCC_TreeNode * node , const char * msg , . . . )
{
2013-10-24 04:06:32 +00:00
va_list argptr ;
va_start ( argptr , msg ) ;
2016-03-13 03:22:29 +00:00
MessageV ( node , TEXTCOLOR_RED , msg , argptr ) ;
2013-10-24 04:06:32 +00:00
va_end ( argptr ) ;
2016-10-15 10:15:25 +00:00
FScriptPosition : : ErrorCounter + + ;
2016-03-13 03:22:29 +00:00
}
//==========================================================================
//
// 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 ) ;
2013-10-24 04:06:32 +00:00
composed + = ' \n ' ;
PrintString ( PRINT_HIGH , composed ) ;
}
//==========================================================================
//
// ZCCCompiler :: Compile
//
// Compile everything defined at this level.
//
//==========================================================================
int ZCCCompiler : : Compile ( )
{
2016-10-08 11:34:37 +00:00
CreateClassTypes ( ) ;
CreateStructTypes ( ) ;
2016-10-08 20:16:10 +00:00
CompileAllConstants ( ) ;
2016-10-09 13:47:31 +00:00
CompileAllFields ( ) ;
2016-10-10 22:56:47 +00:00
InitDefaults ( ) ;
2016-10-11 16:53:10 +00:00
InitFunctions ( ) ;
2016-10-12 15:50:23 +00:00
CompileStates ( ) ;
2016-10-15 10:15:25 +00:00
return FScriptPosition : : ErrorCounter ;
2013-10-24 04:06:32 +00:00
}
2016-10-08 11:34:37 +00:00
//==========================================================================
//
// ZCCCompiler :: CreateStructTypes
//
// Creates a PStruct for every struct.
//
//==========================================================================
void ZCCCompiler : : CreateStructTypes ( )
{
for ( auto s : Structs )
{
2016-10-09 23:18:47 +00:00
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 ) ;
2016-10-09 13:47:31 +00:00
2016-10-09 23:18:47 +00:00
for ( auto e : s - > Enums )
2016-10-09 13:47:31 +00:00
{
2016-10-09 23:18:47 +00:00
auto etype = NewEnum ( e - > NodeName , s - > Type ( ) ) ;
s - > Type ( ) - > Symbols . AddSymbol ( new PSymbolType ( e - > NodeName , etype ) ) ;
2016-10-09 13:47:31 +00:00
}
2016-10-08 11:34:37 +00:00
}
}
2016-10-08 09:54:33 +00:00
//==========================================================================
//
// 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.
//
//==========================================================================
2016-10-08 11:34:37 +00:00
void ZCCCompiler : : CreateClassTypes ( )
2016-10-08 09:54:33 +00:00
{
2016-10-09 23:18:47 +00:00
// we are going to sort the classes array so that entries are sorted in order of inheritance.
2016-10-08 09:54:33 +00:00
auto OrigClasses = std : : move ( Classes ) ;
Classes . Clear ( ) ;
bool donesomething = true ;
while ( donesomething )
{
2016-10-08 11:34:37 +00:00
donesomething = false ;
for ( unsigned i = 0 ; i < OrigClasses . Size ( ) ; i + + )
2016-10-08 09:54:33 +00:00
{
auto c = OrigClasses [ i ] ;
// Check if we got the parent already defined.
PClass * parent ;
2016-10-09 23:18:47 +00:00
auto ParentName = c - > cls - > ParentName ;
2016-10-08 11:34:37 +00:00
2016-10-09 23:18:47 +00:00
if ( ParentName ! = nullptr & & ParentName - > SiblingNext = = ParentName ) parent = PClass : : FindClass ( ParentName - > Id ) ;
else if ( ParentName = = nullptr ) parent = RUNTIME_CLASS ( DObject ) ;
2016-10-08 09:54:33 +00:00
else
{
// The parent is a dotted name which the type system currently does not handle.
// Once it does this needs to be implemented here.
2016-10-09 23:18:47 +00:00
auto p = ParentName ;
2016-10-08 09:54:33 +00:00
FString build ;
do
{
if ( build . IsNotEmpty ( ) ) build + = ' . ' ;
build + = FName ( p - > Id ) ;
p = static_cast < decltype ( p ) > ( p - > SiblingNext ) ;
2016-10-09 23:18:47 +00:00
} while ( p ! = ParentName ) ;
Error ( c - > cls , " Qualified name '%s' for base class not supported in '%s' " , build . GetChars ( ) , FName ( c - > NodeName ( ) ) . GetChars ( ) ) ;
2016-10-08 09:54:33 +00:00
parent = RUNTIME_CLASS ( DObject ) ;
}
if ( parent ! = nullptr )
{
// The parent exists, we may create a type for this class
2016-10-09 23:18:47 +00:00
if ( c - > cls - > Flags & ZCC_Native )
2016-10-08 09:54:33 +00:00
{
2016-10-08 11:34:37 +00:00
// If this is a native class, its own type must also already exist and not be a runtime class.
2016-10-09 23:18:47 +00:00
auto me = PClass : : FindClass ( c - > NodeName ( ) ) ;
2016-10-08 09:54:33 +00:00
if ( me = = nullptr )
{
2016-10-09 23:18:47 +00:00
Error ( c - > cls , " Unknown native class %s " , c - > NodeName ( ) . GetChars ( ) ) ;
me = parent - > FindClassTentative ( c - > NodeName ( ) ) ;
2016-10-08 09:54:33 +00:00
}
2016-10-08 11:34:37 +00:00
else if ( me - > bRuntimeClass )
{
2016-10-09 23:18:47 +00:00
Error ( c - > cls , " %s is not a native class " , c - > NodeName ( ) . GetChars ( ) ) ;
2016-10-08 11:34:37 +00:00
}
2016-10-08 09:54:33 +00:00
else
{
DPrintf ( DMSG_SPAMMY , " Registered %s as native with parent %s \n " , me - > TypeName . GetChars ( ) , parent - > TypeName . GetChars ( ) ) ;
}
2016-10-09 23:18:47 +00:00
c - > cls - > Type = me ;
2016-10-08 09:54:33 +00:00
}
else
{
2016-10-08 11:34:37 +00:00
// We will never get here if the name is a duplicate, so we can just do the assignment.
2016-10-09 23:18:47 +00:00
c - > cls - > Type = parent - > FindClassTentative ( c - > NodeName ( ) ) ;
2016-10-08 09:54:33 +00:00
}
2016-10-09 23:18:47 +00:00
c - > cls - > Symbol = new PSymbolType ( c - > NodeName ( ) , c - > Type ( ) ) ;
GlobalSymbols . AddSymbol ( c - > cls - > Symbol ) ;
c - > Type ( ) - > Symbols . SetName ( c - > NodeName ( ) ) ;
2016-10-08 09:54:33 +00:00
Classes . Push ( c ) ;
2016-10-09 23:18:47 +00:00
OrigClasses . Delete ( i - - ) ;
2016-10-08 09:54:33 +00:00
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 )
{
2016-10-09 23:18:47 +00:00
if ( d - > NodeName ( ) = = c - > cls - > ParentName - > Id )
2016-10-08 09:54:33 +00:00
{
found = true ;
break ;
}
}
if ( ! found )
{
2016-10-09 23:18:47 +00:00
Error ( c - > cls , " Class %s has unknown base class %s " , c - > NodeName ( ) . GetChars ( ) , FName ( c - > cls - > ParentName - > Id ) . GetChars ( ) ) ;
2016-10-08 09:54:33 +00:00
// create a placeholder so that the compiler can continue looking for errors.
2016-10-09 23:18:47 +00:00
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 ( ) ) ;
2016-10-08 09:54:33 +00:00
Classes . Push ( c ) ;
2016-10-09 23:18:47 +00:00
OrigClasses . Delete ( i - - ) ;
2016-10-08 09:54:33 +00:00
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 )
{
2016-10-09 23:18:47 +00:00
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 ) ;
2016-10-08 09:54:33 +00:00
Classes . Push ( c ) ;
}
2016-10-09 13:47:31 +00:00
2016-10-09 23:18:47 +00:00
// 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
2016-10-09 13:47:31 +00:00
for ( auto cd : Classes )
{
2016-10-09 23:18:47 +00:00
for ( auto e : cd - > Enums )
2016-10-09 13:47:31 +00:00
{
2016-10-09 23:18:47 +00:00
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 ;
}
2016-10-09 13:47:31 +00:00
}
}
2016-10-08 09:54:33 +00:00
}
2013-10-24 04:06:32 +00:00
//==========================================================================
//
2016-10-08 20:16:10 +00:00
// ZCCCompiler :: AddConstants
2013-10-24 04:06:32 +00:00
//
2016-10-08 20:16:10 +00:00
// Helper for CompileAllConstants
2013-10-24 04:06:32 +00:00
//
//==========================================================================
2016-10-09 07:09:17 +00:00
void ZCCCompiler : : CopyConstants ( TArray < ZCC_ConstantWork > & dest , TArray < ZCC_ConstantDef * > & Constants , PSymbolTable * ot )
2013-10-24 04:06:32 +00:00
{
2016-10-08 20:16:10 +00:00
for ( auto c : Constants )
2013-10-24 04:06:32 +00:00
{
2016-10-09 07:09:17 +00:00
dest . Push ( { c , ot } ) ;
2016-10-08 20:16:10 +00:00
}
}
//==========================================================================
//
// 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 ;
2016-10-09 07:09:17 +00:00
CopyConstants ( constantwork , Constants , OutputSymbols ) ;
2016-10-09 23:18:47 +00:00
for ( auto c : Classes )
2016-10-08 20:16:10 +00:00
{
2016-10-09 23:18:47 +00:00
CopyConstants ( constantwork , c - > Constants , & c - > Type ( ) - > Symbols ) ;
2016-10-08 20:16:10 +00:00
}
2016-10-09 23:18:47 +00:00
for ( auto s : Structs )
2016-10-08 20:16:10 +00:00
{
2016-10-09 23:18:47 +00:00
CopyConstants ( constantwork , s - > Constants , & s - > Type ( ) - > Symbols ) ;
2016-10-08 20:16:10 +00:00
}
// 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 + + )
2013-10-24 04:06:32 +00:00
{
2016-10-08 20:16:10 +00:00
if ( CompileConstant ( constantwork [ i ] . node , constantwork [ i ] . outputtable ) )
{
AddConstant ( constantwork [ i ] ) ;
// Remove the constant from the list
constantwork . Delete ( i ) ;
i - - ;
donesomething = true ;
}
2013-10-24 04:06:32 +00:00
}
}
2016-10-08 20:16:10 +00:00
for ( unsigned i = 0 ; i < constantwork . Size ( ) ; i + + )
{
Error ( constantwork [ i ] . node , " %s is not a constant " , FName ( constantwork [ i ] . node - > NodeName ) . GetChars ( ) ) ;
}
2013-10-24 04:06:32 +00:00
}
//==========================================================================
//
2016-10-08 20:16:10 +00:00
// ZCCCompiler :: AddConstant
2013-10-24 04:06:32 +00:00
//
2016-10-08 20:16:10 +00:00
// Adds a constant to its assigned symbol table
2013-10-24 04:06:32 +00:00
//
//==========================================================================
2016-10-08 20:16:10 +00:00
void ZCCCompiler : : AddConstant ( ZCC_ConstantWork & constant )
2013-10-24 04:06:32 +00:00
{
2016-10-08 20:16:10 +00:00
auto def = constant . node ;
auto val = def - > Value ;
2013-10-24 04:06:32 +00:00
if ( val - > NodeType = = AST_ExprConstant )
{
ZCC_ExprConstant * cval = static_cast < ZCC_ExprConstant * > ( val ) ;
if ( cval - > Type = = TypeString )
{
2016-10-08 20:16:10 +00:00
def - > Symbol = new PSymbolConstString ( def - > NodeName , * ( cval - > StringVal ) ) ;
2013-10-24 04:06:32 +00:00
}
else if ( cval - > Type - > IsA ( RUNTIME_CLASS ( PInt ) ) )
{
2016-10-09 13:47:31 +00:00
// How do we get an Enum type in here without screwing everything up???
//auto type = def->Type != nullptr ? def->Type : cval->Type;
2016-10-08 20:16:10 +00:00
def - > Symbol = new PSymbolConstNumeric ( def - > NodeName , cval - > Type , cval - > IntVal ) ;
2013-10-24 04:06:32 +00:00
}
else if ( cval - > Type - > IsA ( RUNTIME_CLASS ( PFloat ) ) )
{
2016-10-09 13:47:31 +00:00
if ( def - > Type ! = nullptr )
{
Error ( def , " Enum members must be integer values " ) ;
}
2016-10-08 20:16:10 +00:00
def - > Symbol = new PSymbolConstNumeric ( def - > NodeName , cval - > Type , cval - > DoubleVal ) ;
2013-10-24 04:06:32 +00:00
}
else
{
2016-03-13 03:22:29 +00:00
Error ( def - > Value , " Bad type for constant definiton " ) ;
2016-10-08 20:16:10 +00:00
def - > Symbol = nullptr ;
2013-10-24 04:06:32 +00:00
}
2016-10-08 20:16:10 +00:00
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 ) ;
2013-10-24 04:06:32 +00:00
}
2016-10-08 20:16:10 +00:00
}
//==========================================================================
//
// 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
2016-10-15 23:33:36 +00:00
ZCC_Expression * val = Simplify ( def - > Value , sym , true ) ;
2016-10-08 20:16:10 +00:00
def - > Value = val ;
if ( def - > Symbol = = DEFINING_CONST ) def - > Symbol = nullptr ;
return ( val - > NodeType = = AST_ExprConstant ) ;
2013-10-24 04:06:32 +00:00
}
//==========================================================================
//
// 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.
//
//==========================================================================
2016-10-15 23:33:36 +00:00
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 )
2013-10-24 04:06:32 +00:00
{
2013-11-02 02:28:00 +00:00
if ( root - > NodeType = = AST_ExprUnary )
2013-10-31 01:53:02 +00:00
{
2016-10-08 20:16:10 +00:00
return SimplifyUnary ( static_cast < ZCC_ExprUnary * > ( root ) , sym ) ;
2013-10-31 01:53:02 +00:00
}
2013-11-02 02:28:00 +00:00
else if ( root - > NodeType = = AST_ExprBinary )
2013-10-31 01:53:02 +00:00
{
2016-10-08 20:16:10 +00:00
return SimplifyBinary ( static_cast < ZCC_ExprBinary * > ( root ) , sym ) ;
2013-10-31 01:53:02 +00:00
}
else if ( root - > Operation = = PEX_ID )
2013-10-24 04:06:32 +00:00
{
2016-10-08 20:16:10 +00:00
return IdentifyIdentifier ( static_cast < ZCC_ExprID * > ( root ) , sym ) ;
2013-10-24 04:06:32 +00:00
}
2013-10-30 02:55:32 +00:00
else if ( root - > Operation = = PEX_MemberAccess )
{
2016-10-08 20:16:10 +00:00
return SimplifyMemberAccess ( static_cast < ZCC_ExprMemberAccess * > ( root ) , sym ) ;
2013-10-30 02:55:32 +00:00
}
2013-10-31 01:53:02 +00:00
else if ( root - > Operation = = PEX_FuncCall )
2013-10-24 04:06:32 +00:00
{
2016-10-08 20:16:10 +00:00
return SimplifyFunctionCall ( static_cast < ZCC_ExprFuncCall * > ( root ) , sym ) ;
2013-10-24 04:06:32 +00:00
}
return root ;
}
//==========================================================================
//
// ZCCCompiler :: SimplifyUnary
//
//==========================================================================
2016-10-08 20:16:10 +00:00
ZCC_Expression * ZCCCompiler : : SimplifyUnary ( ZCC_ExprUnary * unary , PSymbolTable * sym )
2013-10-24 04:06:32 +00:00
{
2016-10-15 23:33:36 +00:00
unary - > Operand = DoSimplify ( unary - > Operand , sym ) ;
2016-10-08 20:16:10 +00:00
if ( unary - > Operand - > Type = = nullptr )
{
return unary ;
}
2013-10-24 04:06:32 +00:00
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
//
//==========================================================================
2016-10-08 20:16:10 +00:00
ZCC_Expression * ZCCCompiler : : SimplifyBinary ( ZCC_ExprBinary * binary , PSymbolTable * sym )
2013-10-24 04:06:32 +00:00
{
2016-10-15 23:33:36 +00:00
binary - > Left = DoSimplify ( binary - > Left , sym ) ;
binary - > Right = DoSimplify ( binary - > Right , sym ) ;
2016-10-08 20:16:10 +00:00
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 ;
}
2013-10-24 04:06:32 +00:00
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 ;
}
2013-10-30 02:55:32 +00:00
//==========================================================================
//
// ZCCCompiler :: SimplifyMemberAccess
//
//==========================================================================
2016-10-08 20:16:10 +00:00
ZCC_Expression * ZCCCompiler : : SimplifyMemberAccess ( ZCC_ExprMemberAccess * dotop , PSymbolTable * symt )
2013-10-30 02:55:32 +00:00
{
2016-10-08 20:16:10 +00:00
PSymbolTable * symtable ;
2016-10-15 23:33:36 +00:00
// TBD: Is it safe to simplify the left side here when not processing a constant?
dotop - > Left = DoSimplify ( dotop - > Left , symt ) ;
2013-10-30 02:55:32 +00:00
if ( dotop - > Left - > Operation = = PEX_TypeRef )
{ // Type refs can be evaluated now.
PType * ref = static_cast < ZCC_ExprTypeRef * > ( dotop - > Left ) - > RefType ;
2016-03-13 03:22:29 +00:00
PSymbol * sym = ref - > Symbols . FindSymbolInTable ( dotop - > Right , symtable ) ;
2016-10-08 20:16:10 +00:00
if ( sym ! = nullptr )
2013-10-30 02:55:32 +00:00
{
2016-03-13 03:22:29 +00:00
ZCC_Expression * expr = NodeFromSymbol ( sym , dotop , symtable ) ;
2016-10-08 20:16:10 +00:00
if ( expr ! = nullptr )
2013-10-30 02:55:32 +00:00
{
2016-10-08 20:16:10 +00:00
return expr ;
2013-10-30 02:55:32 +00:00
}
2016-10-08 20:16:10 +00:00
}
}
else if ( dotop - > Left - > Operation = = PEX_Super )
{
symt = symt - > GetParentTable ( ) ;
if ( symt ! = nullptr )
{
PSymbol * sym = symt - > FindSymbolInTable ( dotop - > Right , symtable ) ;
if ( sym ! = nullptr )
2013-10-30 02:55:32 +00:00
{
2016-10-08 20:16:10 +00:00
ZCC_Expression * expr = NodeFromSymbol ( sym , dotop , symtable ) ;
if ( expr ! = nullptr )
{
return expr ;
}
2013-10-30 02:55:32 +00:00
}
}
}
return dotop ;
}
2013-10-31 01:53:02 +00:00
//==========================================================================
//
// ZCCCompiler :: SimplifyFunctionCall
//
// This may replace a function call with cast(s), since they look like the
// same thing to the parser.
//
//==========================================================================
2016-10-08 20:16:10 +00:00
ZCC_Expression * ZCCCompiler : : SimplifyFunctionCall ( ZCC_ExprFuncCall * callop , PSymbolTable * sym )
2013-10-31 01:53:02 +00:00
{
ZCC_FuncParm * parm ;
int parmcount = 0 ;
parm = callop - > Parameters ;
if ( parm ! = NULL )
{
do
{
parmcount + + ;
assert ( parm - > NodeType = = AST_FuncParm ) ;
2016-10-15 23:33:36 +00:00
parm - > Value = DoSimplify ( parm - > Value , sym ) ;
2013-10-31 01:53:02 +00:00
parm = static_cast < ZCC_FuncParm * > ( parm - > SiblingNext ) ;
}
while ( parm ! = callop - > Parameters ) ;
}
2016-10-15 23:33:36 +00:00
// 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 ) ;
}
2013-10-31 01:53:02 +00:00
// 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 )
{
2016-03-13 03:22:29 +00:00
Error ( callop , " Type cast requires one parameter " ) ;
2013-10-31 01:53:02 +00:00
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 )
{
2016-03-13 03:22:29 +00:00
///FIXME: Need real type names
2016-10-16 17:42:22 +00:00
Error ( callop , " Cannot convert %s to %s " , parm - > Value - > Type - > DescriptiveName ( ) , dest - > DescriptiveName ( ) ) ;
2013-10-31 01:53:02 +00:00
callop - > ToErrorNode ( ) ;
}
else
{
ZCC_Expression * val = ApplyConversion ( parm - > Value , route , routelen ) ;
assert ( val - > Type = = dest ) ;
return val ;
}
}
}
return callop ;
}
2013-10-24 04:06:32 +00:00
//==========================================================================
//
// 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.
//
//==========================================================================
2016-10-08 20:16:10 +00:00
ZCC_Expression * ZCCCompiler : : IdentifyIdentifier ( ZCC_ExprID * idnode , PSymbolTable * symt )
2013-10-24 04:06:32 +00:00
{
2016-03-13 03:22:29 +00:00
// Check the symbol table for the identifier.
PSymbolTable * table ;
2016-10-08 20:16:10 +00:00
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 )
2013-10-24 04:06:32 +00:00
{
2016-03-13 03:22:29 +00:00
ZCC_Expression * node = NodeFromSymbol ( sym , idnode , table ) ;
2013-10-30 02:55:32 +00:00
if ( node ! = NULL )
2013-10-24 04:06:32 +00:00
{
2013-10-30 02:55:32 +00:00
return node ;
2013-10-24 04:06:32 +00:00
}
}
2016-10-16 07:31:03 +00:00
else if ( SimplifyingConstant ) // leave unknown identifiers alone when simplifying non-constants. It is impossible to know what they are here.
2016-03-13 03:22:29 +00:00
{
2016-10-08 20:16:10 +00:00
// 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 )
2016-03-13 03:22:29 +00:00
{
2016-10-08 20:16:10 +00:00
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 ;
2016-03-13 03:22:29 +00:00
}
2016-10-08 20:16:10 +00:00
Error ( idnode , " Unknown identifier '%s' " , FName ( idnode - > Identifier ) . GetChars ( ) ) ;
idnode - > ToErrorNode ( ) ;
2016-03-13 03:22:29 +00:00
}
2016-10-08 20:16:10 +00:00
return idnode ;
2016-03-13 03:22:29 +00:00
}
2013-10-24 04:06:32 +00:00
//==========================================================================
//
2013-10-30 02:55:32 +00:00
// ZCCCompiler :: NodeFromSymbol
//
//==========================================================================
2016-03-13 03:22:29 +00:00
ZCC_Expression * ZCCCompiler : : NodeFromSymbol ( PSymbol * sym , ZCC_Expression * source , PSymbolTable * table )
2013-10-30 02:55:32 +00:00
{
2016-10-08 20:16:10 +00:00
assert ( sym ! = nullptr ) ;
2013-10-30 02:55:32 +00:00
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
2013-10-24 04:06:32 +00:00
//
// Returns a new AST constant node with the symbol's content.
//
//==========================================================================
2013-10-30 02:55:32 +00:00
ZCC_ExprConstant * ZCCCompiler : : NodeFromSymbolConst ( PSymbolConst * sym , ZCC_Expression * idnode )
2013-10-24 04:06:32 +00:00
{
ZCC_ExprConstant * val = static_cast < ZCC_ExprConstant * > ( AST . InitNode ( sizeof ( * val ) , AST_ExprConstant , idnode ) ) ;
val - > Operation = PEX_ConstValue ;
2013-10-26 03:01:18 +00:00
if ( sym = = NULL )
{
val - > Type = TypeError ;
val - > IntVal = 0 ;
}
else if ( sym - > IsKindOf ( RUNTIME_CLASS ( PSymbolConstString ) ) )
2013-10-24 04:06:32 +00:00
{
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 ;
}
2013-10-30 02:55:32 +00:00
//==========================================================================
//
// 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 ;
}
2016-10-09 13:47:31 +00:00
//==========================================================================
//
// 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 ] ;
2016-10-09 23:18:47 +00:00
if ( c - > Type ( ) - > Size ! = TentativeClass & & c - > Fields . Size ( ) > 0 )
2016-10-09 13:47:31 +00:00
{
// We need to search the global class table here because not all children may have a scripted definition attached.
for ( auto ac : PClass : : AllClasses )
{
2016-10-09 23:18:47 +00:00
if ( ac - > ParentClass = = c - > Type ( ) & & ac - > Size ! = TentativeClass )
2016-10-09 13:47:31 +00:00
{
2016-10-09 23:18:47 +00:00
Error ( c - > cls , " Trying to add fields to class '%s' with native children " , c - > Type ( ) - > TypeName . GetChars ( ) ) ;
Classes . Delete ( i - - ) ;
2016-10-09 13:47:31 +00:00
break ;
}
}
}
}
bool donesomething = true ;
while ( donesomething & & ( Structs . Size ( ) > 0 | | Classes . Size ( ) > 0 ) )
{
donesomething = false ;
for ( unsigned i = 0 ; i < Structs . Size ( ) ; i + + )
{
2016-10-09 23:18:47 +00:00
if ( CompileFields ( Structs [ i ] - > Type ( ) , Structs [ i ] - > Fields , Structs [ i ] - > Outer , & Structs [ i ] - > TreeNodes , true ) )
2016-10-09 13:47:31 +00:00
{
// Remove from the list if all fields got compiled.
2016-10-09 23:18:47 +00:00
Structs . Delete ( i - - ) ;
2016-10-09 13:47:31 +00:00
donesomething = true ;
}
}
for ( unsigned i = 0 ; i < Classes . Size ( ) ; i + + )
{
2016-10-09 23:18:47 +00:00
auto type = Classes [ i ] - > Type ( ) ;
if ( type - > Size = = TentativeClass )
2016-10-09 13:47:31 +00:00
{
2016-10-09 23:18:47 +00:00
if ( type - > ParentClass - > Size = = TentativeClass )
2016-10-09 13:47:31 +00:00
{
// 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
2016-10-09 23:18:47 +00:00
type - > Size = Classes [ i ] - > Type ( ) - > ParentClass - > Size ;
2016-10-09 13:47:31 +00:00
}
}
2016-10-09 23:18:47 +00:00
if ( CompileFields ( type , Classes [ i ] - > Fields , nullptr , & Classes [ i ] - > TreeNodes , false ) )
2016-10-09 13:47:31 +00:00
{
// Remove from the list if all fields got compiled.
2016-10-09 23:18:47 +00:00
Classes . Delete ( i - - ) ;
2016-10-09 13:47:31 +00:00
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 )
2016-10-09 13:47:31 +00:00
{
2016-10-09 23:18:47 +00:00
Error ( s - > strct , " Unable to resolve all fields for struct %s " , FName ( s - > NodeName ( ) ) . GetChars ( ) ) ;
2016-10-09 13:47:31 +00:00
}
2016-10-09 20:01:23 +00:00
for ( auto s : Classes )
2016-10-09 13:47:31 +00:00
{
2016-10-09 23:18:47 +00:00
Error ( s - > cls , " Unable to resolve all fields for class %s " , FName ( s - > NodeName ( ) ) . GetChars ( ) ) ;
2016-10-09 13:47:31 +00:00
}
}
//==========================================================================
//
// ZCCCompiler :: CompileFields
//
// builds the internal structure of a single class or struct
//
//==========================================================================
2016-10-09 23:18:47 +00:00
bool ZCCCompiler : : CompileFields ( PStruct * type , TArray < ZCC_VarDeclarator * > & Fields , PClass * Outer , PSymbolTable * TreeNodes , bool forstruct )
2016-10-09 13:47:31 +00:00
{
while ( Fields . Size ( ) > 0 )
{
auto field = Fields [ 0 ] ;
2016-10-16 10:47:26 +00:00
PType * fieldtype = DetermineType ( type , field , field - > Names - > Name , field - > Type , true , true ) ;
2016-10-09 13:47:31 +00:00
// 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 ;
}
2016-10-09 23:18:47 +00:00
uint32_t varflags = 0 ;
2016-10-09 13:47:31 +00:00
// 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
{
2016-10-09 23:18:47 +00:00
if ( AddTreeNode ( name - > Name , name , TreeNodes , ! forstruct ) )
2016-10-09 13:47:31 +00:00
{
2016-10-09 23:18:47 +00:00
auto thisfieldtype = fieldtype ;
if ( name - > ArraySize ! = nullptr )
{
thisfieldtype = ResolveArraySize ( thisfieldtype , name - > ArraySize , & type - > Symbols ) ;
}
2016-10-09 13:47:31 +00:00
2016-10-09 23:18:47 +00:00
type - > AddField ( name - > Name , thisfieldtype , varflags ) ;
}
2016-10-09 13:47:31 +00:00
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.
//
//==========================================================================
2016-10-16 10:47:26 +00:00
PType * ZCCCompiler : : DetermineType ( PType * outertype , ZCC_TreeNode * field , FName name , ZCC_Type * ztype , bool allowarraytypes , bool formember )
2016-10-09 13:47:31 +00:00
{
2016-10-16 10:47:26 +00:00
PType * retval = TypeError ;
2016-10-09 13:47:31 +00:00
if ( ! allowarraytypes & & ztype - > ArraySize ! = nullptr )
{
2016-10-11 16:53:10 +00:00
Error ( field , " %s: Array type not allowed " , name . GetChars ( ) ) ;
2016-10-09 13:47:31 +00:00
return TypeError ;
}
switch ( ztype - > NodeType )
{
case AST_BasicType :
{
auto btype = static_cast < ZCC_BasicType * > ( ztype ) ;
switch ( btype - > Type )
{
case ZCC_SInt8 :
2016-10-16 10:47:26 +00:00
retval = TypeSInt8 ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_UInt8 :
2016-10-16 10:47:26 +00:00
retval = TypeUInt8 ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_SInt16 :
2016-10-16 10:47:26 +00:00
retval = TypeSInt16 ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_UInt16 :
2016-10-16 10:47:26 +00:00
retval = TypeUInt16 ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_SInt32 :
case ZCC_IntAuto : // todo: for enums, autoselect appropriately sized int
2016-10-16 10:47:26 +00:00
retval = TypeSInt32 ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_UInt32 :
2016-10-16 10:47:26 +00:00
retval = TypeUInt32 ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_Bool :
2016-10-16 10:47:26 +00:00
retval = TypeBool ;
break ;
2016-10-09 13:47:31 +00:00
// 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 :
2016-10-16 10:47:26 +00:00
retval = TypeFloat64 ;
2016-10-16 17:42:22 +00:00
break ;
2016-10-09 13:47:31 +00:00
case ZCC_String :
2016-10-16 10:47:26 +00:00
retval = TypeString ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_Name :
2016-10-16 10:47:26 +00:00
retval = TypeName ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_Vector2 :
2016-10-16 10:47:26 +00:00
retval = TypeVector2 ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_Vector3 :
2016-10-16 10:47:26 +00:00
retval = TypeVector3 ;
break ;
2016-10-09 13:47:31 +00:00
case ZCC_Vector4 :
// This has almost no use, so we really shouldn't bother.
2016-10-11 16:53:10 +00:00
Error ( field , " vector<4> not implemented for %s " , name . GetChars ( ) ) ;
2016-10-09 13:47:31 +00:00
return TypeError ;
2016-10-11 16:53:10 +00:00
case ZCC_State :
2016-10-16 10:47:26 +00:00
retval = TypeState ;
break ;
2016-10-11 16:53:10 +00:00
case ZCC_Color :
2016-10-16 10:47:26 +00:00
retval = TypeColor ;
break ;
2016-10-11 16:53:10 +00:00
case ZCC_Sound :
2016-10-16 10:47:26 +00:00
retval = TypeSound ;
break ;
2016-10-11 16:53:10 +00:00
2016-10-09 13:47:31 +00:00
case ZCC_UserType :
2016-10-16 10:47:26 +00:00
retval = ResolveUserType ( btype , & outertype - > Symbols ) ;
2016-10-09 13:47:31 +00:00
break ;
}
2016-10-16 10:47:26 +00:00
break ;
2016-10-09 13:47:31 +00:00
}
case AST_MapType :
if ( allowarraytypes )
{
2016-10-11 16:53:10 +00:00
Error ( field , " %s: Map types not implemented yet " , name . GetChars ( ) ) ;
2016-10-09 13:47:31 +00:00
// Todo: Decide what we allow here and if it makes sense to allow more complex constructs.
auto mtype = static_cast < ZCC_MapType * > ( ztype ) ;
2016-10-16 10:47:26 +00:00
retval = NewMap ( DetermineType ( outertype , field , name , mtype - > KeyType , false , false ) , DetermineType ( outertype , field , name , mtype - > ValueType , false , false ) ) ;
break ;
2016-10-09 13:47:31 +00:00
}
break ;
case AST_DynArrayType :
if ( allowarraytypes )
{
2016-10-11 16:53:10 +00:00
Error ( field , " %s: Dynamic array types not implemented yet " , name . GetChars ( ) ) ;
2016-10-09 13:47:31 +00:00
auto atype = static_cast < ZCC_DynArrayType * > ( ztype ) ;
2016-10-16 10:47:26 +00:00
retval = NewDynArray ( DetermineType ( outertype , field , name , atype - > ElementType , false , false ) ) ;
break ;
2016-10-09 13:47:31 +00:00
}
break ;
case AST_ClassType :
{
auto ctype = static_cast < ZCC_ClassType * > ( ztype ) ;
if ( ctype - > Restriction = = nullptr )
{
2016-10-16 10:47:26 +00:00
retval = NewClassPointer ( RUNTIME_CLASS ( DObject ) ) ;
2016-10-09 13:47:31 +00:00
}
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 ;
}
2016-10-16 10:47:26 +00:00
retval = NewClassPointer ( static_cast < PClass * > ( typesym - > Type ) ) ;
2016-10-09 13:47:31 +00:00
}
2016-10-16 10:47:26 +00:00
break ;
2016-10-09 13:47:31 +00:00
}
}
2016-10-16 10:47:26 +00:00
if ( retval ! = TypeError & & retval - > MemberOnly & & ! formember )
{
2016-10-16 17:42:22 +00:00
Error ( field , " Invalid type %s " , retval - > DescriptiveName ( ) ) ; // fixme: Types need a descriptive name that can be output here.
2016-10-16 10:47:26 +00:00
return TypeError ;
}
return retval ;
2016-10-09 13:47:31 +00:00
}
//==========================================================================
//
// 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.
2016-10-15 23:33:36 +00:00
arraysize = Simplify ( arraysize , sym , true ) ;
2016-10-09 13:47:31 +00:00
ZCC_Expression * val ;
do
{
2016-10-15 23:33:36 +00:00
val = Simplify ( arraysize , sym , true ) ;
2016-10-09 13:47:31 +00:00
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 ;
}
2016-10-10 22:56:47 +00:00
//==========================================================================
//
// 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 )
{
2016-10-18 08:09:02 +00:00
return nullptr ;
2016-10-10 22:56:47 +00:00
}
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 " ) ;
2016-10-18 08:09:02 +00:00
return nullptr ;
2016-10-10 22:56:47 +00:00
}
}
//==========================================================================
//
// 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 ;
}
2016-10-15 23:33:36 +00:00
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.
2016-10-10 22:56:47 +00:00
const char * p = prop - > params ;
auto exp = property - > Values ;
while ( true )
{
FPropParam conv ;
FPropParam pref ;
if ( exp - > NodeType ! = AST_ExprConstant )
{
2016-10-13 18:45:52 +00:00
// 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 ;
2016-10-10 22:56:47 +00:00
}
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.
2016-10-13 20:52:31 +00:00
conv . i = GetInt ( exp ) ;
params . Push ( conv ) ;
conv . exp = nullptr ;
break ;
case ' I ' :
2016-10-10 22:56:47 +00:00
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 ;
2016-10-18 08:09:02 +00:00
case ' Z ' : // an optional string. Does not allow any numeric value.
2016-10-10 22:56:47 +00:00
if ( ! GetString ( exp , true ) )
{
// apply this expression to the next argument on the list.
params . Push ( conv ) ;
params [ 0 ] . i + + ;
p + + ;
continue ;
}
2016-10-18 08:09:02 +00:00
conv . s = GetString ( exp ) ;
break ;
2016-10-10 22:56:47 +00:00
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 + + ;
}
2016-10-15 23:33:36 +00:00
exp = Simplify ( static_cast < ZCC_Expression * > ( exp - > SiblingNext ) , & bag . Info - > Symbols , true ) ;
2016-10-10 22:56:47 +00:00
} 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 + + ;
2016-10-15 23:33:36 +00:00
exp = Simplify ( static_cast < ZCC_Expression * > ( exp - > SiblingNext ) , & bag . Info - > Symbols , true ) ;
2016-10-10 22:56:47 +00:00
endofparm :
p + + ;
// Skip the DECORATE 'no comma' marker
if ( * p = = ' _ ' ) p + + ;
2016-10-14 18:08:41 +00:00
if ( * p = = 0 )
2016-10-10 22:56:47 +00:00
{
if ( exp ! = property - > Values )
{
Error ( property , " Too many values for '%s' " , prop - > name ) ;
2016-10-13 18:45:52 +00:00
return ;
2016-10-10 22:56:47 +00:00
}
break ;
}
else if ( exp = = property - > Values )
{
if ( * p < ' a ' )
{
Error ( property , " Insufficient parameters for %s " , prop - > name ) ;
2016-10-13 18:45:52 +00:00
return ;
2016-10-10 22:56:47 +00:00
}
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 ;
2016-10-13 18:45:52 +00:00
bag . Lumpnum = c - > cls - > SourceLump ;
2016-10-10 22:56:47 +00:00
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 ;
2016-10-14 18:08:41 +00:00
if ( content ! = nullptr ) do
2016-10-10 22:56:47 +00:00
{
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 ) ;
}
2016-10-12 18:42:41 +00:00
if ( bag . DropItemSet )
{
bag . Info - > SetDropItems ( bag . DropItemList ) ;
}
2016-10-10 22:56:47 +00:00
}
}
}
}
2016-10-11 16:53:10 +00:00
//==========================================================================
//
// Parses the functions list
//
//==========================================================================
void ZCCCompiler : : InitFunctions ( )
{
TArray < PType * > rets ( 1 ) ;
TArray < PType * > args ;
2016-10-14 18:08:41 +00:00
TArray < uint32_t > argflags ;
2016-10-15 12:36:08 +00:00
TArray < FName > argnames ;
2016-10-11 16:53:10 +00:00
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
{
2016-10-16 10:47:26 +00:00
auto type = DetermineType ( c - > Type ( ) , f , f - > Name , t , false , false ) ;
2016-10-15 15:40:27 +00:00
if ( type - > IsKindOf ( RUNTIME_CLASS ( PStruct ) ) )
{
// structs and classes only get passed by pointer.
type = NewPointer ( type ) ;
}
2016-10-11 16:53:10 +00:00
// 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 ( ) ) ;
}
}
2016-10-15 12:36:08 +00:00
SetImplicitArgs ( & args , & argflags , & argnames , c - > Type ( ) , varflags ) ;
2016-10-11 16:53:10 +00:00
auto p = f - > Params ;
if ( p ! = nullptr )
{
do
{
if ( p - > Type ! = nullptr )
{
2016-10-16 10:47:26 +00:00
auto type = DetermineType ( c - > Type ( ) , p , f - > Name , p - > Type , false , false ) ;
2016-10-11 16:53:10 +00:00
int flags ;
if ( p - > Flags & ZCC_In ) flags | = VARF_In ;
if ( p - > Flags & ZCC_Out ) flags | = VARF_Out ;
if ( p - > Default ! = nullptr )
{
2016-10-15 23:33:36 +00:00
auto val = Simplify ( p - > Default , & c - > Type ( ) - > Symbols , true ) ;
2016-10-11 16:53:10 +00:00
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 ) ;
}
2016-10-15 13:50:45 +00:00
PFunction * sym = new PFunction ( c - > Type ( ) , f - > Name ) ;
sym - > AddVariant ( NewPrototype ( rets , args ) , argflags , argnames , afd = = nullptr ? nullptr : * ( afd - > VMPointer ) , varflags ) ;
2016-10-11 16:53:10 +00:00
c - > Type ( ) - > Symbols . ReplaceSymbol ( sym ) ;
// todo: Check inheritance.
// todo: Process function bodies.
}
}
}
}
2016-10-12 15:50:23 +00:00
//==========================================================================
//
// 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 ) ;
2016-10-15 18:16:44 +00:00
// 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 )
2016-10-12 15:50:23 +00:00
{
2016-10-15 18:16:44 +00:00
PFunction * afd = dyn_cast < PFunction > ( cls - > Symbols . FindSymbol ( id - > Identifier , true ) ) ;
if ( afd ! = nullptr )
2016-10-12 15:50:23 +00:00
{
2016-10-15 18:16:44 +00:00
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 ) ;
}
2016-10-12 15:50:23 +00:00
}
2016-10-15 18:16:44 +00:00
else
2016-10-14 18:08:41 +00:00
{
2016-10-15 18:16:44 +00:00
// 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.
2016-10-14 18:08:41 +00:00
}
2016-10-12 15:50:23 +00:00
}
}
2016-10-15 12:36:08 +00:00
ConvertClass = cls ;
2016-10-13 22:40:20 +00:00
return ConvertAST ( af ) ;
2016-10-12 15:50:23 +00:00
}
//==========================================================================
//
// 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 ;
2016-10-13 18:45:52 +00:00
statedef . MakeStateDefines ( dyn_cast < PClassActor > ( c - > Type ( ) - > ParentClass ) ) ;
2016-10-12 15:50:23 +00:00
for ( auto s : c - > States )
{
auto st = s - > Body ;
2016-10-14 18:08:41 +00:00
if ( st ! = nullptr ) do
2016-10-12 15:50:23 +00:00
{
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 ) )
{
2016-10-15 23:33:36 +00:00
auto func = static_cast < ZCC_ExprFuncCall * > ( Simplify ( sl - > Duration , & c - > Type ( ) - > Symbols , true ) ) ;
2016-10-12 15:50:23 +00:00
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
{
2016-10-15 23:33:36 +00:00
auto duration = Simplify ( sl - > Duration , & c - > Type ( ) - > Symbols , true ) ;
2016-10-12 15:50:23 +00:00
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 )
{
2016-10-15 23:33:36 +00:00
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 ) ) ;
2016-10-12 15:50:23 +00:00
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 )
{
2016-10-15 12:36:08 +00:00
auto funcsym = CreateAnonymousFunction ( c - > Type ( ) , nullptr , VARF_Method | VARF_Action ) ;
2016-10-15 19:35:31 +00:00
state . ActionFunc = FunctionBuildList . AddFunction ( funcsym , code , FStringf ( " %s.StateFunction.%d " , c - > Type ( ) - > TypeName . GetChars ( ) , statedef . GetStateCount ( ) ) , false ) ;
2016-10-12 15:50:23 +00:00
}
}
2016-10-14 19:56:45 +00:00
int count = statedef . AddStates ( & state , sl - > Frames - > GetChars ( ) ) ;
if ( count < 0 )
{
Error ( sl , " Invalid frame character string '%s' " , sl - > Frames - > GetChars ( ) ) ;
count = - count ;
}
2016-10-12 15:50:23 +00:00
break ;
}
case AST_StateGoto :
{
auto sg = static_cast < ZCC_StateGoto * > ( st ) ;
statename = " " ;
if ( sg - > Qualifier ! = nullptr )
{
2016-10-14 18:08:41 +00:00
statename < < FName ( sg - > Qualifier - > Id ) < < " :: " ;
2016-10-12 15:50:23 +00:00
}
auto part = sg - > Label ;
do
{
2016-10-13 20:52:31 +00:00
statename < < FName ( part - > Id ) < < ' . ' ;
2016-10-12 15:50:23 +00:00
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 )
{
2016-10-15 23:33:36 +00:00
auto ofs = Simplify ( sg - > Offset , & c - > Type ( ) - > Symbols , true ) ;
2016-10-12 15:50:23 +00:00
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 ) ;
}
2016-10-12 18:42:41 +00:00
try
{
static_cast < PClassActor * > ( c - > Type ( ) ) - > Finalize ( statedef ) ;
}
catch ( CRecoverableError & err )
{
Error ( c - > cls , " %s " , err . GetMessage ( ) ) ;
}
2016-10-12 15:50:23 +00:00
}
}
2016-10-13 22:40:20 +00:00
//==========================================================================
//
// Convert the AST data for the code generator.
//
//==========================================================================
FxExpression * ZCCCompiler : : ConvertAST ( ZCC_TreeNode * ast )
{
2016-10-19 17:05:48 +00:00
// 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 ;
}
2016-10-13 22:40:20 +00:00
}
2016-10-21 12:18:31 +00:00
# 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
//
//==========================================================================
2016-10-13 22:40:20 +00:00
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 ) ;
2016-10-16 08:59:12 +00:00
// 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 ) ;
2016-10-20 23:12:54 +00:00
// not yet done
case AST_ExprTypeRef :
case AST_SwitchStmt :
case AST_CaseStmt :
2016-10-16 08:59:12 +00:00
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 ;
2016-10-13 22:40:20 +00:00
}
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 ) ;
}
2016-10-15 22:12:33 +00:00
case AST_ExprID :
{
auto id = static_cast < ZCC_ExprID * > ( ast ) ;
return new FxIdentifier ( id - > Identifier , * ast ) ;
}
2016-10-13 22:40:20 +00:00
case AST_ExprConstant :
{
auto cnst = static_cast < ZCC_ExprConstant * > ( ast ) ;
2016-10-15 18:16:44 +00:00
if ( cnst - > Type - > IsA ( RUNTIME_CLASS ( PName ) ) )
{
return new FxConstant ( FName ( ENamedName ( cnst - > IntVal ) ) , * ast ) ;
}
else if ( cnst - > Type - > IsA ( RUNTIME_CLASS ( PInt ) ) )
2016-10-13 22:40:20 +00:00
{
return new FxConstant ( cnst - > IntVal , * ast ) ;
}
2016-10-17 10:58:23 +00:00
else if ( cnst - > Type - > IsA ( RUNTIME_CLASS ( PBool ) ) )
{
return new FxConstant ( ! ! cnst - > IntVal , * ast ) ;
}
2016-10-15 18:16:44 +00:00
else if ( cnst - > Type - > IsA ( RUNTIME_CLASS ( PFloat ) ) )
2016-10-13 22:40:20 +00:00
{
return new FxConstant ( cnst - > DoubleVal , * ast ) ;
}
2016-10-15 18:16:44 +00:00
else if ( cnst - > Type - > IsA ( RUNTIME_CLASS ( PString ) ) )
2016-10-13 22:40:20 +00:00
{
return new FxConstant ( * cnst - > StringVal , * ast ) ;
}
else
{
// can there be other types?
2016-10-17 10:58:23 +00:00
Error ( cnst , " Unknown constant type %s " , cnst - > Type - > DescriptiveName ( ) ) ;
2016-10-13 22:40:20 +00:00
return new FxConstant ( 0 , * ast ) ;
}
}
2016-10-17 18:33:35 +00:00
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 :
2016-10-21 12:18:31 +00:00
return new FxPostIncrDecr ( operand , Pex2Tok [ op ] ) ;
2016-10-17 18:33:35 +00:00
case PEX_PreDec :
case PEX_PreInc :
2016-10-21 12:18:31 +00:00
return new FxPreIncrDecr ( operand , Pex2Tok [ op ] ) ;
2016-10-17 18:33:35 +00:00
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 :
2016-10-21 12:18:31 +00:00
return new FxSizeAlign ( operand , Pex2Tok [ op ] ) ;
2016-10-17 18:33:35 +00:00
default :
assert ( 0 & & " Unknown unary operator. " ) ; // should never happen
Error ( unary , " Unknown unary operator ID #%d " , op ) ;
return new FxNop ( * ast ) ;
}
break ;
}
2016-10-16 08:59:12 +00:00
case AST_ExprBinary :
{
auto binary = static_cast < ZCC_ExprBinary * > ( ast ) ;
auto left = ConvertNode ( binary - > Left ) ;
auto right = ConvertNode ( binary - > Right ) ;
auto op = binary - > Operation ;
2016-10-21 12:18:31 +00:00
auto tok = Pex2Tok [ op ] ;
2016-10-16 08:59:12 +00:00
switch ( op )
{
case PEX_Add :
case PEX_Sub :
2016-10-21 12:18:31 +00:00
return new FxAddSub ( tok , left , right ) ;
2016-10-16 08:59:12 +00:00
case PEX_Mul :
case PEX_Div :
case PEX_Mod :
2016-10-21 12:18:31 +00:00
return new FxMulDiv ( tok , left , right ) ;
2016-10-16 08:59:12 +00:00
2016-10-17 13:17:48 +00:00
case PEX_Pow :
return new FxPow ( left , right ) ;
2016-10-17 13:52:29 +00:00
case PEX_LeftShift :
case PEX_RightShift :
case PEX_URightShift :
case PEX_BitAnd :
case PEX_BitOr :
case PEX_BitXor :
2016-10-21 12:18:31 +00:00
return new FxBinaryInt ( tok , left , right ) ;
2016-10-17 13:52:29 +00:00
2016-10-17 18:33:35 +00:00
case PEX_BoolOr :
case PEX_BoolAnd :
2016-10-21 12:18:31 +00:00
return new FxBinaryLogical ( tok , left , right ) ;
2016-10-17 18:33:35 +00:00
case PEX_LT :
case PEX_LTEQ :
case PEX_GT :
case PEX_GTEQ :
2016-10-21 12:18:31 +00:00
return new FxCompareRel ( tok , left , right ) ;
2016-10-17 18:33:35 +00:00
case PEX_EQEQ :
case PEX_NEQ :
2016-10-21 12:18:31 +00:00
return new FxCompareEq ( tok , left , right ) ;
2016-10-17 18:33:35 +00:00
2016-10-21 10:22:42 +00:00
case PEX_Assign :
return new FxAssign ( left , right ) ;
2016-10-21 12:18:31 +00:00
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 ) ;
2016-10-17 18:33:35 +00:00
case PEX_LTGTEQ :
2016-10-21 14:35:07 +00:00
return new FxLtGtEq ( left , right ) ;
// todo: These do not have representations in DECORATE and no implementation exists yet.
2016-10-17 18:33:35 +00:00
case PEX_Concat :
case PEX_Is :
// more esoteric operators
case PEX_APREQ :
2016-10-17 19:43:14 +00:00
// vector operations will be done later.
2016-10-17 18:33:35 +00:00
case PEX_CrossProduct :
case PEX_DotProduct :
2016-10-21 10:22:42 +00:00
2016-10-16 08:59:12 +00:00
default :
I_Error ( " Binary operator %d not implemented yet " , op ) ;
}
break ;
}
2016-10-17 19:43:14 +00:00
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 ) ;
}
2016-10-19 17:05:48 +00:00
2016-10-19 23:09:35 +00:00
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 ;
}
2016-10-20 23:12:54 +00:00
list - > Add ( new FxLocalVariableDeclaration ( type , node - > Name , val , 0 , * node ) ) ; // todo: Handle flags in the grammar.
2016-10-19 23:09:35 +00:00
}
node = static_cast < decltype ( node ) > ( node - > SiblingNext ) ;
} while ( node ! = loc - > Vars ) ;
return list ;
}
2016-10-19 17:05:48 +00:00
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 ) ;
}
2016-10-13 22:40:20 +00:00
}
2016-10-16 08:59:12 +00:00
2016-10-20 23:12:54 +00:00
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 ) ;
}
}
2016-10-19 17:05:48 +00:00
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 ;
}
}
2016-10-16 08:59:12 +00:00
// 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 ;
2016-10-13 22:40:20 +00:00
}
FArgumentList * ZCCCompiler : : ConvertNodeList ( ZCC_TreeNode * head )
{
FArgumentList * list = new FArgumentList ;
2016-10-16 08:59:12 +00:00
if ( head ! = nullptr )
2016-10-13 22:40:20 +00:00
{
2016-10-16 08:59:12 +00:00
auto node = head ;
do
{
list - > Push ( ConvertNode ( node ) ) ;
node = node - > SiblingNext ;
} while ( node ! = head ) ;
}
2016-10-13 22:40:20 +00:00
return list ;
}