2020-09-10 00:56:16 +00:00
/*************/
/*GEMWIRE */
/* ERYTHRO*/
/*************/
# include <Defs.h>
# include <Data.h>
2021-01-17 06:37:39 +00:00
# include <stdbool.h>
2023-04-24 19:41:49 +00:00
static void ParseEnumDeclaration ( ) ;
static struct SymbolTableEntry * ParseDeclarationSymbol ( int Type , struct SymbolTableEntry * CompositeType , int Storage ) ;
static int ParseAliasDeclaration ( struct SymbolTableEntry * * CompositeType ) ;
/*
* Handles parsing multiple statements or expressions in a row .
* These are typically grouped together with the Compound tokens " { } "
* and seperated by the semicolon " ; " .
*
* Single Statements are parsed until a semicolon is reached , at which
* point another statement will be parsed , or until a Right Compound
* token is reached ( " } " ) , at which point parsing will stop .
*
* It is useful for :
* * Tightly identifying related blocks of code
* * Containing the many statements of functions
*
* @ return the AST Node representing this compound statement
*
*/
struct ASTNode * ParseCompound ( ) {
struct ASTNode * Left = NULL , * Tree ;
while ( 1 ) {
printf ( " \t New branch in compound \n " ) ;
Tree = ParseStatement ( ) ;
/*if (Tree && (Tree->Operation == OP_PRINT || Tree->Operation == OP_ASSIGN
| | Tree - > Operation = = OP_RET | | Tree - > Operation = = OP_CALL
| | Tree - > Operation = = OP_BREAK | | Tree - > Operation = = OP_CONTINUE ) )
VerifyToken ( LI_SEMIC , " ; " ) ; */
Safe ( ) ;
if ( Tree ) {
if ( Left = = NULL )
Left = Tree ;
else
Left = ConstructASTNode ( OP_COMP , RET_NONE , Left , NULL , Tree , NULL , 0 ) ;
}
if ( CurrentFile - > CurrentSymbol . type = = LI_RBRAC ) {
fflush ( stdout ) ;
return Left ;
}
if ( CurrentFile - > SwitchStatement & & ( CurrentFile - > CurrentSymbol . type = = KW_CASE | | CurrentFile - > CurrentSymbol . type = = KW_DEFAULT ) ) {
return Left ;
}
}
}
2023-04-24 21:26:54 +00:00
/*
* Read a literal of the given type .
* @ param Type integer or char *
* @ return the integer literal , or label value of the string .
*/
int ParseLiteral ( int Type ) {
if ( ( Type = = PointerTo ( RET_CHAR ) ) & & ( CurrentFile - > CurrentSymbol . type = = LI_STR ) )
return Assembler - > vtable - > AsNewString ( CurrentIdentifier ) ;
if ( CurrentFile - > CurrentSymbol . type = = LI_INT ) {
switch ( Type ) {
case RET_CHAR :
if ( CurrentFile - > CurrentSymbol . value < 0 | | CurrentFile - > CurrentSymbol . value > 255 )
ErrorReport ( " Integer literal value too big for char \n " ) ;
case RET_INT :
case RET_LONG :
break ;
default : ErrorReport ( " Type Mismatch. Integer Literal vs Variable. \n " ) ;
}
} else {
ErrorReport ( " Expecting an integer literal or char array. \n " ) ;
}
return CurrentFile - > CurrentSymbol . value ;
}
2023-04-24 19:41:49 +00:00
/*
* Resolve a typename to a type struct .
* Short circuit on the case where a definition is present , as definitions are typeless .
*/
static int ParseType ( struct SymbolTableEntry * * CompositeType , int * Scope ) {
int Type = - 1 , Extern = 1 ;
while ( Extern ) {
switch ( CurrentFile - > CurrentSymbol . type ) {
default :
Extern = 0 ;
}
}
switch ( CurrentFile - > CurrentSymbol . type ) {
case KW_IMPORT :
Type = - 1 ;
ImportModule ( ) ;
break ;
case TY_VOID :
Type = RET_VOID ;
Tokenise ( ) ;
break ;
case TY_CHAR :
Type = RET_CHAR ;
Tokenise ( ) ;
break ;
case TY_INT :
Type = RET_INT ;
Tokenise ( ) ;
break ;
case TY_LONG :
Type = RET_LONG ;
Tokenise ( ) ;
break ;
case TY_IDENTIFIER :
case KW_ALIAS :
Type = ParseAliasDeclaration ( CompositeType ) ;
if ( CurrentFile - > CurrentSymbol . type = = LI_SEMIC )
Type = - 1 ;
break ;
case KW_ENUM :
Type = RET_INT ;
ParseEnumDeclaration ( ) ;
if ( CurrentFile - > CurrentSymbol . type = = LI_SEMIC )
Type = - 1 ;
break ;
case KW_STRUCT :
Type = DAT_STRUCT ;
* CompositeType = BeginCompositeDeclaration ( Type ) ;
if ( CurrentFile - > CurrentSymbol . type = = LI_SEMIC )
Type = - 1 ;
break ;
case KW_UNION :
Type = DAT_UNION ;
* CompositeType = BeginCompositeDeclaration ( Type ) ;
if ( CurrentFile - > CurrentSymbol . type = = LI_SEMIC )
Type = - 1 ;
break ;
default :
ErrorReport ( " Illegal type on token %s \n " , CurrentFile - > CurrentSymbol . type ) ;
}
return Type ;
}
/*
* Given a Type passed by ParseType , read following dereferences and return pointer type .
*/
static int ParsePointerType ( int Type ) {
while ( 1 ) {
// But, skip parsing if we're looking at an import.
if ( CurrentFile - > CurrentSymbol . type = = KW_IMPORT )
break ;
printf ( " \t \t \t Type on parsing is %s \n " , TokenNames [ CurrentFile - > CurrentSymbol . type ] ) ;
if ( CurrentFile - > CurrentSymbol . type ! = AR_STAR )
break ;
Type = PointerTo ( Type ) ;
Tokenise ( ) ;
}
return Type ;
}
/*
* Parse a declaration of an array - the [ < int > ] part .
*
* @ param name the name of the array
* @ param Type the type of the array , if scalar
* @ param CompositeType the type of the array , if composite
* @ param Storage the storage class of the array
* @ return the defined array symbol
*/
static struct SymbolTableEntry * ParseArrayDeclaration ( char * name , int Type , struct SymbolTableEntry * CompositeType , int Storage ) {
struct SymbolTableEntry * Symbol = NULL ;
2023-04-24 21:26:54 +00:00
int Elems = - 1 , MaxElems , * InitialList , i , j ;
2023-04-24 19:41:49 +00:00
Tokenise ( ) ;
Safe ( ) ;
if ( CurrentFile - > CurrentSymbol . type = = LI_INT ) {
2023-04-24 21:26:54 +00:00
if ( CurrentFile - > CurrentSymbol . value < = 0 )
ErrorReport ( " Array size in definition cannot be negative. \n " ) ;
Elems = CurrentFile - > CurrentSymbol . value ;
Tokenise ( ) ;
2023-04-24 19:41:49 +00:00
}
VerifyToken ( LI_RBRAC , " ] " ) ;
Safe ( ) ;
2023-04-24 21:26:54 +00:00
if ( CurrentFile - > CurrentSymbol . type = = LI_EQUAL ) {
if ( Storage ! = SC_GLOBAL )
ErrorReport ( " Non-global array cannot be initialized. \n " ) ;
Tokenise ( ) ;
Safe ( ) ;
VerifyToken ( LI_LBRAC , " { " ) ;
if ( Elems ! = - 1 )
MaxElems = Elems ;
else
MaxElems = 10 ;
InitialList = ( int * ) malloc ( MaxElems * sizeof ( int ) ) ;
while ( 1 ) {
if ( Elems ! = - 1 & & i = = MaxElems )
ErrorReport ( " Too many items in initializer list \n " ) ;
InitialList [ i + + ] = ParseLiteral ( Type ) ;
Tokenise ( ) ;
Safe ( ) ;
if ( Elems = = - 1 & & i = = MaxElems ) {
MaxElems + = 10 ;
InitialList = ( int * ) realloc ( InitialList , MaxElems * sizeof ( int ) ) ;
}
if ( CurrentFile - > CurrentSymbol . type = = LI_RBRAC ) {
Tokenise ( ) ;
break ;
}
VerifyToken ( LI_COM , " , " ) ;
Safe ( ) ;
}
for ( j = i ; j < Symbol - > Length ; j + + )
InitialList [ j ] = 0 ;
if ( i > Elems )
Elems = i ;
Symbol - > InitialValues = InitialList ;
}
Symbol - > Length = Elems ;
Symbol - > Size = Symbol - > Length * TypeSize ( Type , CompositeType ) ;
if ( Storage = = SC_GLOBAL )
Assembler - > vtable - > AsGlobalSymbol ( Symbol ) ;
2023-04-24 19:41:49 +00:00
return Symbol ;
}
// A short redirect to add a Scalar definition to the variable tables.
static struct SymbolTableEntry * ParseScalarDeclaration ( char * name , int Type , struct SymbolTableEntry * CompositeType , int Storage ) {
2023-04-24 21:26:54 +00:00
struct SymbolTableEntry * sym = AddSymbol ( name , Type , ST_VAR , Storage , 1 , 0 , CompositeType ) ;
// Being assigned.
if ( CurrentFile - > CurrentSymbol . type = = LI_EQUAL ) {
if ( Storage ! = SC_GLOBAL & & Storage ! = SC_LOCAL )
ErrorReport ( " Non-static, non-local variable cannot be initialized. \n " ) ;
Tokenise ( ) ;
Safe ( ) ;
if ( Storage = = SC_GLOBAL ) {
sym - > InitialValues = ( int * ) malloc ( sizeof ( int ) ) ;
sym - > InitialValues [ 0 ] = ParseLiteral ( Type ) ;
Tokenise ( ) ;
}
}
if ( Storage = = SC_GLOBAL )
Assembler - > vtable - > AsGlobalSymbol ( sym ) ;
return sym ;
2023-04-24 19:41:49 +00:00
}
2021-01-20 19:22:15 +00:00
/*
* Handles reading in a comma - separated list of declarations .
* Erythro treats structs , enums and function parameters the same in this regard -
* comma separated .
2023-04-24 19:41:49 +00:00
*
2021-01-20 19:22:15 +00:00
* C and C + + tend to treat enums and structs differently - the former separated by commas ,
* the latter separated by semicolons .
2023-04-24 19:41:49 +00:00
*
2021-01-20 19:22:15 +00:00
* Note that since functions are read in through parentheses , and structs / enums are read in
* through brackets , the end character is configurable .
2023-04-24 19:41:49 +00:00
*
* Parse declarations , including lists thereof , until the Terminate symbol is encountered .
* Will first parse a type name , then parse the identifier using ParseSymbolDeclaration .
* Declaration lists must be separated by a comma or terminated with the StatementEndSymbol .
*
* @ param CompositeType out : the type of the declaration list .
* @ param ClassType the type of the class
* @ param StatementEndSymbool the symbol that marks the end of the declaration list
* @ param TerminateSymbol the symbol that marks the end of parsing
* @ return the type of the declaration
*
2021-01-20 19:22:15 +00:00
*/
2023-04-24 19:41:49 +00:00
int ParseDeclarationList ( struct SymbolTableEntry * * CompositeType , int ClassType , int StatementEndSymbool , int TerminateSymbol ) {
int initType , type ;
struct SymbolTableEntry * symbol ;
fflush ( stdout ) ;
if ( ( initType = ParseType ( CompositeType , & ClassType ) ) = = - 1 )
return initType ;
while ( 1 ) {
type = ParsePointerType ( initType ) ;
symbol = ParseDeclarationSymbol ( type , * CompositeType , ClassType ) ;
printf ( " \t Reading a new element: %s of type %d, scope %s \n " , CurrentIdentifier , type , ScopeNames [ ClassType ] ) ;
// Lists of function declarations are not valid.
if ( symbol - > Type = = ST_FUNC ) {
if ( ClassType ! = SC_GLOBAL )
ErrorReport ( " Function definition not at global scope \n " ) ;
return type ;
}
2021-01-20 19:22:15 +00:00
2023-04-24 19:41:49 +00:00
// Terminate at either symbol
if ( CurrentFile - > CurrentSymbol . type = = StatementEndSymbool | | CurrentFile - > CurrentSymbol . type = = TerminateSymbol )
return type ;
// We must be continuing the list, so parse a comma
VerifyToken ( LI_COM , " , " ) ;
}
}
/*
* Parse the full list of parameter declarations .
* Each has a type , a name , may be a pointer , or an array .
*
* @ param FunctionDeclaration the type of the declaration of the function , if declared already .
* @ param FunctionDefinition the type of the definition of the function , which we are parsing
* @ return the number of parameters parsed
*/
static int ParseParameterDeclarationList ( struct SymbolTableEntry * FunctionDeclaration , struct SymbolTableEntry * FunctionDefinition ) {
2021-01-20 01:05:41 +00:00
int TokenType , ParamCount = 0 ;
2022-03-03 00:05:10 +00:00
struct SymbolTableEntry * PrototypePointer = NULL , * Composite ;
2021-01-18 00:20:58 +00:00
2023-04-24 19:41:49 +00:00
if ( FunctionDeclaration ! = NULL )
PrototypePointer = FunctionDeclaration - > Start ;
2021-01-17 06:37:39 +00:00
2023-04-24 19:41:49 +00:00
while ( CurrentFile - > CurrentSymbol . type ! = LI_RPARE ) {
// Doing int x, y, float z is valid, so parse a list of declarations per parameter.
TokenType = ParseDeclarationList ( & Composite , SC_PARAM , LI_COM , LI_RPARE ) ;
if ( TokenType = = - 1 )
ErrorReport ( " Bad type in parameter list " ) ;
2021-01-17 06:37:39 +00:00
2023-04-24 19:41:49 +00:00
printf ( " \t Reading a new parameter: %s of type %d \n " , CurrentIdentifier , TokenType ) ;
2021-01-22 01:01:53 +00:00
2022-03-03 00:05:10 +00:00
if ( PrototypePointer ! = NULL ) {
if ( TokenType ! = PrototypePointer - > Type )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Function parameter has invalid type at index %d \n " , ParamCount + 1 ) ;
2022-03-03 00:05:10 +00:00
PrototypePointer = PrototypePointer - > NextSymbol ;
2021-01-18 00:20:58 +00:00
}
2023-04-24 19:41:49 +00:00
Safe ( ) ;
2021-01-17 06:37:39 +00:00
ParamCount + + ;
2023-04-24 19:41:49 +00:00
if ( CurrentFile - > CurrentSymbol . type = = LI_RPARE )
break ;
2022-03-03 00:05:10 +00:00
2023-04-24 19:41:49 +00:00
VerifyToken ( LI_COM , " , " ) ;
Safe ( ) ;
2021-01-17 06:37:39 +00:00
}
2020-09-10 00:56:16 +00:00
2023-04-24 19:41:49 +00:00
if ( ( FunctionDeclaration ! = NULL ) & & ( ParamCount ! = FunctionDeclaration - > Length ) )
ErrorReport ( " Function definition has different number of parameters than the function declaration (%d vs %d). \n " , ParamCount , FunctionDeclaration - > Length ) ;
2021-01-18 00:20:58 +00:00
2021-01-17 06:37:39 +00:00
return ParamCount ;
}
2020-09-10 00:56:16 +00:00
2023-04-24 19:41:49 +00:00
/*
* Parse a function declaration , and optionally definition .
* < type > < identifier > ( parameter ( , ? ) * ) ;
* < type > < identiier > ( parameter ( , ? ) * ) compound ;
*
* @ param name the name of the function
* @ param Type the type of the function , if primitive
* @ param CompositeType the type of the function , if composite
* @ param Storage the scope of the function
* @ return the new symbol table entry for the function
*/
static struct SymbolTableEntry * ParseFunctionDeclaration ( char * name , int Type , struct SymbolTableEntry * CompositeType , int Storage ) {
struct ASTNode * Tree ;
struct ASTNode * FinalStatement ;
struct SymbolTableEntry * OldFunction , * NewFunction = NULL ;
int BreakLabel = 0 , ParamCount = 0 ;
VerifyToken ( KW_FUNC , " :: " ) ;
Safe ( ) ;
VerifyToken ( TY_IDENTIFIER , " Identifier " ) ;
Safe ( ) ;
if ( ( OldFunction = FindSymbol ( CurrentIdentifier ) ) ! = NULL )
if ( OldFunction - > Storage ! = ST_FUNC )
OldFunction = NULL ;
if ( OldFunction = = NULL ) {
BreakLabel = Assembler - > vtable - > NewLabel ( ) ;
NewFunction = AddSymbol ( CurrentIdentifier , Type , ST_FUNC , SC_GLOBAL , BreakLabel , 0 , NULL ) ;
}
VerifyToken ( LI_LPARE , " ( " ) ;
Safe ( ) ;
ParamCount = ParseParameterDeclarationList ( OldFunction , NewFunction ) ;
VerifyToken ( LI_RPARE , " ) " ) ;
Safe ( ) ;
printf ( " \n Identified%sfunction %s of return type %s, end label %d \n " ,
( OldFunction = = NULL ) ? " new " : " overloaded " ,
( OldFunction = = NULL ) ? NewFunction - > Name : OldFunction - > Name ,
TypeNames ( Type ) , BreakLabel ) ;
if ( NewFunction ) {
2023-04-24 21:26:54 +00:00
NewFunction - > Length = ParamCount ;
2023-04-24 19:41:49 +00:00
NewFunction - > Start = Params ;
NewFunction - > Type = RET_LONG ;
OldFunction = NewFunction ;
}
Params = ParamsEnd = NULL ;
if ( CurrentFile - > CurrentSymbol . type = = LI_SEMIC ) {
return OldFunction ;
}
CurrentFile - > FunctionEntry = OldFunction ;
CurrentFile - > CurrentLoopDepth = 0 ;
VerifyToken ( LI_LBRAC , " { " ) ;
Safe ( ) ;
Tree = ParseCompound ( ) ;
Safe ( ) ;
VerifyToken ( LI_RBRAC , " } " ) ;
if ( Type ! = RET_VOID ) {
// Functions with one statement have no composite node, so we have to check
FinalStatement = ( Tree - > Operation = = OP_COMP ) ? Tree - > Right : Tree ;
if ( FinalStatement = = NULL | | FinalStatement - > Operation ! = OP_RET ) {
ErrorReport ( " Function with non-void type does not return " ) ;
}
}
Tree = ConstructASTBranch ( OP_FUNC , Tree - > ExprType , Tree , OldFunction , BreakLabel ) ;
if ( Tree & & CurrentFile - > AllowDefinitions ) {
printf ( " \n Beginning assembler creation of new function %s \n " , Tree - > Symbol - > Name ) ;
if ( OptDumpTree ) {
DumpTree ( Tree , 0 ) ;
fprintf ( stdout , " \n \n " ) ;
}
// Emit the function now
Assembler - > vtable - > AssembleTree ( Tree , - 1 , - 1 , - 1 , 0 ) ;
FreeLocals ( ) ;
} else {
printf ( " \n Function prototype saved \r \n " ) ;
}
Safe ( ) ;
return OldFunction ;
}
/*
* The " alias " keyword allows one to create a new keyword that is accepted in lieu of another ( or a chain of another )
* It does this by reading in sequence :
* * The " alias " keyword
* * The thing to alias ( any valid primary type )
* * The new name
*
* They are stored in a separate symbol table and can be used anywhere the original is valid .
*/
static int ParseAliasDeclaration ( struct SymbolTableEntry * * CompositeType ) {
int Type , Storage = 0 ;
// "alias"
Tokenise ( ) ;
Safe ( ) ;
Type = ParseType ( CompositeType , & Storage ) ;
if ( Storage ! = 0 )
ErrorReport ( " Cannot extern an alias definition. \n " ) ;
if ( FindAlias ( CurrentIdentifier ) ! = NULL )
ErrorReport ( " Duplicate type alias. \n " ) ;
// It may be a pointer definition
Type = ParsePointerType ( Type ) ;
AddSymbol ( CurrentIdentifier , Type , ST_VAR , SC_ALIAS , 0 , 0 , * CompositeType ) ;
Tokenise ( ) ;
Safe ( ) ;
return Type ;
}
/*
* Get the type that a typedef declaration aliases .
* @ param name the name of the typedef
* @ param CompositeType out : the type if composite
* @ return the type if scalar
*/
static int GetTypedef ( char * name , struct SymbolTableEntry * * CompositeType ) {
struct SymbolTableEntry * type ;
type = FindAlias ( name ) ;
if ( type = = NULL )
ErrorReport ( " Unknown alias type " ) ;
Tokenise ( ) ;
Safe ( ) ;
* CompositeType = type - > CompositeType ;
return type - > Type ;
}
/*
* Parse an array initialization .
* Everything after the = , for example .
* Every element must match the type of the array , and the number of elements must match the size of the array .
* @ param Symbol the symbol of the array we ' re initializing
* @ param Type the type of the array , if primitive
* @ param CompositeType the type of the array , if composite
* @ param Storage the storage class of the array we ' re initializing
*/
static void ParseArrayInitialization ( struct SymbolTableEntry * Symbol , int Type , struct SymbolTableEntry * CompositeType , int Storage ) {
ErrorReport ( " Array initialization not permitted. \n " ) ;
}
/*
* Parse a name symbol for a declaration .
* Calls out to parse functions , arrays and scalars alike .
* Also parses an inline initialization if present .
*
* @ param Type the type of the declaration , if primitive
* @ param CompositeType a reference to the type , if composite ( struct )
* @ param Storage the storage class of the declaration
* @ return the symbol table entry to the new symbol
*/
static struct SymbolTableEntry * ParseDeclarationSymbol ( int Type , struct SymbolTableEntry * CompositeType , int Storage ) {
struct SymbolTableEntry * symbol = NULL ;
char * variableName = strdup ( CurrentIdentifier ) ;
int structureType = ST_VAR ;
Safe ( ) ;
if ( CurrentFile - > CurrentSymbol . type = = KW_FUNC )
return ParseFunctionDeclaration ( variableName , Type , CompositeType , Storage ) ;
VerifyToken ( TY_IDENTIFIER , " Identifier " ) ;
// Check for duplicate declarations
switch ( Storage ) {
case SC_GLOBAL :
if ( FindGlobal ( variableName ) ! = NULL )
ErrorReport ( " Duplicate global declaration \n " ) ;
case SC_LOCAL :
case SC_PARAM :
if ( FindLocal ( variableName ) ! = NULL )
ErrorReport ( " Duplicate local declaration \n " ) ;
case SC_MEMBER :
if ( FindMember ( variableName ) ! = NULL )
ErrorReport ( " Duplicate member declaration \n " ) ;
default : break ;
}
// Determine whether this is an array or scalar.
if ( CurrentFile - > CurrentSymbol . type = = LI_LBRAC ) {
symbol = ParseArrayDeclaration ( variableName , Type , CompositeType , Storage ) ;
structureType = ST_ARR ;
} else {
symbol = ParseScalarDeclaration ( variableName , Type , CompositeType , Storage ) ;
}
// Determine whether we're initializing immediately
if ( CurrentFile - > CurrentSymbol . type = = LI_EQUAL ) {
// TODO: Default parameters
if ( Storage = = SC_PARAM )
ErrorReport ( " Initialization of parameter not permitted. \n " ) ;
// TODO: Enum initialization
if ( Storage = = SC_MEMBER )
ErrorReport ( " Initialization of a member not permitted. \n " ) ;
Tokenise ( ) ;
if ( structureType = = ST_ARR ) {
ParseArrayInitialization ( symbol , Type , CompositeType , Storage ) ;
} else {
// TODO: Inline initialization
ErrorReport ( " Initialization of a scalar not permitted. \n " ) ;
}
}
return symbol ;
}
2021-01-20 19:22:15 +00:00
/*
2022-03-03 02:44:07 +00:00
* Handles the declaration of a new composite type .
* For example , a struct is a composite of multiple different named positions :
2021-01-20 19:22:15 +00:00
* struct thisStct { int x , int y , int z } ;
*
* Verifies that the current identifier is not used ,
* verifies that this is not a redefinition ( excluding
* the case where there is a declaration but no definition )
2022-03-03 02:44:07 +00:00
* and then saves it into the appropriate symbol table .
2021-01-20 19:22:15 +00:00
*
2022-03-03 02:44:07 +00:00
* @ return the Symbol Table entry of this new composite .
2021-01-20 19:22:15 +00:00
*/
2022-03-03 02:44:07 +00:00
struct SymbolTableEntry * BeginCompositeDeclaration ( int Type ) {
2023-04-24 19:41:49 +00:00
struct SymbolTableEntry * Composite = NULL , * Member ;
2022-03-03 02:44:07 +00:00
int Offset = 0 , Largest = 0 ;
2021-01-20 19:22:15 +00:00
2023-04-24 19:41:49 +00:00
// "struct" / "union"
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
2021-01-20 19:22:15 +00:00
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type = = TY_IDENTIFIER ) {
2022-03-03 02:44:07 +00:00
Composite = Type = = DAT_STRUCT ? FindStruct ( CurrentIdentifier ) : FindUnion ( CurrentIdentifier ) ;
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
}
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type ! = LI_LBRAC ) {
2022-03-03 00:05:10 +00:00
if ( Composite = = NULL )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Use of undefined composite " ) ;
2021-01-20 19:22:15 +00:00
return Composite ;
}
2022-03-03 00:05:10 +00:00
if ( Composite )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Redefinition of composite " ) ;
2021-01-20 19:22:15 +00:00
2022-03-04 01:11:04 +00:00
Composite = AddSymbol ( CurrentIdentifier , Type , ST_RUCT , Type = = DAT_STRUCT ? SC_STRUCT : SC_UNION , 0 , 0 , NULL ) ;
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
2022-03-03 02:44:07 +00:00
printf ( " Reading a composite declaration.. Type is %s \n " , Type = = DAT_STRUCT ? " struct " : " union " ) ;
2023-04-24 19:41:49 +00:00
while ( 1 ) {
Type = ParseDeclarationList ( & Member , SC_MEMBER , LI_SEMIC , LI_RBRAC ) ;
if ( Type = = - 1 )
ErrorReport ( " Bad type in member list of composite \n " ) ;
OptionallyConsume ( LI_SEMIC ) ;
Safe ( ) ;
if ( CurrentFile - > CurrentSymbol . type = = LI_RBRAC )
break ;
}
2021-01-22 01:01:53 +00:00
VerifyToken ( LI_RBRAC , " } " ) ;
2021-01-20 19:22:15 +00:00
2023-04-24 19:41:49 +00:00
if ( CompositeMembers = = NULL )
ErrorReport ( " No members in struct. \n " ) ;
2022-03-03 02:44:07 +00:00
Composite - > Start = CompositeMembers ;
CompositeMembers = CompositeMembersEnd = NULL ;
2021-01-20 19:22:15 +00:00
Member = Composite - > Start ;
2022-03-03 02:44:07 +00:00
printf ( " \t Setting first entry in composite to %s \r \n " , Member - > Name ) ;
2021-01-20 19:22:15 +00:00
Member - > SinkOffset = 0 ;
Offset = TypeSize ( Member - > Type , Member - > CompositeType ) ;
2022-03-03 00:05:10 +00:00
for ( Member = Member - > NextSymbol ; Member ! = NULL ; Member = Member - > NextSymbol ) {
2022-03-03 02:44:07 +00:00
if ( Type = = DAT_STRUCT )
2023-04-23 16:32:02 +00:00
Member - > SinkOffset = Assembler - > vtable - > AsAlignMemory ( Member - > Type , Offset , 1 ) ;
2022-03-03 02:44:07 +00:00
else
Member - > SinkOffset = 0 ;
int CurrentSize = TypeSize ( Member - > Type , Member - > CompositeType ) ;
Offset + = CurrentSize ;
Largest = CurrentSize > Largest ? CurrentSize : Largest ;
2021-01-20 19:22:15 +00:00
}
2022-03-03 02:44:07 +00:00
Composite - > Length = Type = = DAT_STRUCT ? Offset : Largest ;
2023-04-24 21:26:54 +00:00
Composite - > Size = Offset ;
2021-01-20 19:22:15 +00:00
return Composite ;
}
2023-04-24 19:41:49 +00:00
static void ParseEnumDeclaration ( ) {
2022-03-04 01:11:04 +00:00
struct SymbolTableEntry * Type = NULL ;
char * Name ;
int Value = 0 ;
2023-04-24 19:41:49 +00:00
// "enum"
2022-03-04 01:11:04 +00:00
Tokenise ( ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
2022-03-04 01:11:04 +00:00
// enum name
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type = = TY_IDENTIFIER ) {
2022-03-04 01:11:04 +00:00
Type = FindEnum ( CurrentIdentifier ) ;
Name = strdup ( CurrentIdentifier ) ;
Tokenise ( ) ;
}
2023-04-24 19:41:49 +00:00
// We're expecting to declare an enum, so make sure the content follows.
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type ! = LI_LBRAC ) {
2022-03-04 01:11:04 +00:00
if ( Type = = NULL )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Enum used but not yet declared. \n " ) ;
2022-03-04 01:11:04 +00:00
return ;
}
// Skip the { that we have
Tokenise ( ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
2022-03-04 01:11:04 +00:00
if ( Type ! = NULL )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Enum redeclared. \n " ) ;
2022-03-04 01:11:04 +00:00
else
Type = AddSymbol ( Name , DAT_ENUM , ST_ENUM , SC_ENUM , 0 , 0 , NULL ) ;
while ( 1 ) {
VerifyToken ( TY_IDENTIFIER , " Enum Entry " ) ;
Name = strdup ( CurrentIdentifier ) ;
Type = FindEnumMember ( Name ) ;
if ( Type ! = NULL )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Enum value already declared \n " ) ;
Safe ( ) ;
2022-03-04 01:11:04 +00:00
// Parse equality
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type = = LI_EQUAL ) {
2022-03-04 01:11:04 +00:00
Tokenise ( ) ;
// Expect a number after the equals
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type ! = LI_INT )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Expected integer in enum assignment \n " ) ;
2022-03-05 01:13:45 +00:00
Value = CurrentFile - > CurrentSymbol . value ;
2023-04-24 19:41:49 +00:00
// int
2022-03-04 01:11:04 +00:00
Tokenise ( ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
2022-03-04 01:11:04 +00:00
}
Type = AddSymbol ( Name , DAT_ENUM , ST_ENUM , SC_ENUMENTRY , Value + + , 0 , NULL ) ;
// Break on right brace
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type = = LI_RBRAC )
2022-03-04 01:11:04 +00:00
break ;
VerifyToken ( LI_COM , " Comma " ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
2022-03-04 01:11:04 +00:00
}
// Skip right brace
Tokenise ( ) ;
2020-09-10 00:56:16 +00:00
}
struct ASTNode * ReturnStatement ( ) {
struct ASTNode * Tree ;
2023-04-24 19:41:49 +00:00
VerifyToken ( KW_RETURN , " return " ) ;
2020-09-10 00:56:16 +00:00
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > FunctionEntry - > Type = = RET_VOID )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Attempt to return from void function " ) ;
2020-09-10 00:56:16 +00:00
2023-04-24 02:03:31 +00:00
bool bracketed = OptionallyConsume ( LI_LPARE ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
2020-09-10 00:56:16 +00:00
Tree = ParsePrecedenceASTNode ( 0 ) ;
2022-03-05 01:13:45 +00:00
Tree = MutateType ( Tree , CurrentFile - > FunctionEntry - > Type , 0 ) ;
2022-03-03 00:05:10 +00:00
if ( Tree = = NULL )
2023-04-24 19:41:49 +00:00
ErrorReport ( " Returning a value of incorrect type for function. Expected %s. \n " , TypeNames ( CurrentFile - > FunctionEntry - > Type ) ) ;
2020-11-15 06:40:05 +00:00
2022-03-05 01:13:45 +00:00
Tree = ConstructASTBranch ( OP_RET , RET_NONE , Tree , CurrentFile - > FunctionEntry , 0 ) ;
2020-09-10 00:56:16 +00:00
2022-03-05 01:13:45 +00:00
printf ( " \t \t Returning from function %s \n " , CurrentFile - > FunctionEntry - > Name ) ;
2020-09-10 00:56:16 +00:00
2023-04-24 02:03:31 +00:00
if ( bracketed ) VerifyToken ( LI_RPARE , " ) " ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
VerifyToken ( LI_SEMIC , " ; " ) ;
2020-09-10 00:56:16 +00:00
return Tree ;
}
2021-01-20 19:22:15 +00:00
2020-09-10 00:56:16 +00:00
/*
2021-01-20 19:22:15 +00:00
* Handles the surrounding logic for If statements .
*
* If statements have the basic form :
* * if ( condition ) body
* * if ( condition )
* body
* * if ( condition ) {
* body
* }
2020-09-10 00:56:16 +00:00
*
2021-01-20 19:22:15 +00:00
* Conditions may be any truthy statement ( such as a pointer ,
* object , integer ) , as conditions not recognized are auto -
* matically converted to booleans .
2020-09-10 00:56:16 +00:00
*
2021-01-20 19:22:15 +00:00
* This meaning , any object that can be resolved to 0 or NULL
* can be placed as the condition and used as a check .
*
* For example :
* struct ASTNode * Node = NULL ;
* if ( Node ) {
* // This will not run, as Node is ((void*)0)
* }
2020-09-10 00:56:16 +00:00
*
*/
struct ASTNode * IfStatement ( ) {
2022-03-03 00:05:10 +00:00
struct ASTNode * Condition , * True , * False = NULL ;
2020-09-10 00:56:16 +00:00
VerifyToken ( KW_IF , " if " ) ;
VerifyToken ( LI_LPARE , " ( " ) ;
Condition = ParsePrecedenceASTNode ( 0 ) ;
// Limit if(x) to =? != < > <= =>
// No null checking, no arithmetic, no functions.
// TODO: this
2022-03-03 00:05:10 +00:00
if ( Condition - > Operation < OP_EQUAL | | Condition - > Operation > OP_GREATE )
2021-01-20 01:05:41 +00:00
Condition = ConstructASTBranch ( OP_BOOLCONV , Condition - > ExprType , Condition , NULL , 0 ) ;
2022-03-03 00:05:10 +00:00
2020-09-10 00:56:16 +00:00
VerifyToken ( LI_RPARE , " ) " ) ;
2022-03-06 01:52:37 +00:00
True = ParseStatement ( ) ;
2020-09-10 00:56:16 +00:00
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type = = KW_ELSE ) {
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2022-03-06 01:52:37 +00:00
False = ParseStatement ( ) ;
2020-09-10 00:56:16 +00:00
}
2021-01-20 01:05:41 +00:00
return ConstructASTNode ( OP_IF , RET_NONE , Condition , True , False , NULL , 0 ) ;
2020-09-10 00:56:16 +00:00
}
2021-01-20 19:22:15 +00:00
/*
* Handles the surrounding logic for While loops .
*
* While loops have the basic form :
* while ( condition ) { body }
*
* When reaching the condition ( which alike an If statement ,
* can be any truthy value ) , if it resolves to true :
* The body is executed , and immediately the condition is checked
* again .
* This repeats until the condition resolves false , at which point
* the loop executes no more .
*
* This can be prototyped as the following pseudo - assembler :
*
* cond :
* check < condition >
* jne exit
* < body >
* jump cond
* exit :
* < more code >
*
* @ return the AST of this statement
*
*/
2020-09-10 00:56:16 +00:00
struct ASTNode * WhileStatement ( ) {
2022-03-03 00:05:10 +00:00
struct ASTNode * Condition , * Body ;
2020-09-10 00:56:16 +00:00
VerifyToken ( KW_WHILE , " while " ) ;
VerifyToken ( LI_LPARE , " ( " ) ;
Condition = ParsePrecedenceASTNode ( 0 ) ;
2022-03-03 00:05:10 +00:00
if ( Condition - > Operation < OP_EQUAL | | Condition - > Operation > OP_GREATE )
2021-01-20 01:05:41 +00:00
Condition = ConstructASTBranch ( OP_BOOLCONV , Condition - > ExprType , Condition , NULL , 0 ) ;
2022-03-03 00:05:10 +00:00
2020-09-10 00:56:16 +00:00
VerifyToken ( LI_RPARE , " ) " ) ;
2022-03-05 23:42:01 +00:00
CurrentFile - > CurrentLoopDepth + + ;
2022-03-06 01:52:37 +00:00
Body = ParseStatement ( ) ;
2022-03-05 23:42:01 +00:00
CurrentFile - > CurrentLoopDepth - - ;
2020-09-10 00:56:16 +00:00
2021-01-20 01:05:41 +00:00
return ConstructASTNode ( OP_LOOP , RET_NONE , Condition , NULL , Body , NULL , 0 ) ;
2020-09-10 00:56:16 +00:00
}
2021-01-20 19:22:15 +00:00
/*
* Handles the surrounding logic for For loops .
*
* They have the basic form of :
* for ( init ; condition ; iterator ) { body }
*
* The initialiser is run only once upon reaching the for loop .
* Then the condition is checked , and if true , the body is executed .
* After execution of the body , the iterator is run and the condition
* checked again .
*
* It can be prototyped as the following pseudo - assembler code :
*
* for :
* < init >
* cond :
* check < condition >
* jne exit
* < body >
* < iterator >
* jump cond
* exit :
* < loop exit >
*
* In the case of the implementation , " init " is the preoperator ,
* " iterator " is the postoperator .
*
* @ return the AST of this statement
*/
2020-09-10 00:56:16 +00:00
struct ASTNode * ForStatement ( ) {
2022-03-03 00:05:10 +00:00
struct ASTNode * Condition , * Body ;
struct ASTNode * Preop , * Postop ;
2020-09-10 00:56:16 +00:00
struct ASTNode * Tree ;
VerifyToken ( KW_FOR , " for " ) ;
VerifyToken ( LI_LPARE , " ( " ) ;
2023-04-24 19:41:49 +00:00
Preop = ParseExpressionList ( LI_SEMIC ) ;
2020-09-10 00:56:16 +00:00
VerifyToken ( LI_SEMIC , " ; " ) ;
Condition = ParsePrecedenceASTNode ( 0 ) ;
2022-03-03 00:05:10 +00:00
if ( Condition - > Operation < OP_EQUAL | | Condition - > Operation > OP_GREATE )
2021-01-20 01:05:41 +00:00
Condition = ConstructASTBranch ( OP_BOOLCONV , Condition - > ExprType , Condition , NULL , 0 ) ;
2020-11-24 13:17:01 +00:00
2020-09-10 00:56:16 +00:00
VerifyToken ( LI_SEMIC , " ; " ) ;
2023-04-24 19:41:49 +00:00
Postop = ParseExpressionList ( LI_RPARE ) ;
2020-09-10 00:56:16 +00:00
VerifyToken ( LI_RPARE , " ) " ) ;
2022-03-05 23:42:01 +00:00
CurrentFile - > CurrentLoopDepth + + ;
2022-03-06 01:52:37 +00:00
Body = ParseStatement ( ) ;
2022-03-05 23:42:01 +00:00
CurrentFile - > CurrentLoopDepth - - ;
2020-09-10 00:56:16 +00:00
// We need to be able to skip over the body and the postop, so we group them together.
2021-01-20 01:05:41 +00:00
Tree = ConstructASTNode ( OP_COMP , RET_NONE , Body , NULL , Postop , NULL , 0 ) ;
2020-09-10 00:56:16 +00:00
// We need to be able to jump to the top of the condition and fall through to the body,
// so we group it with the last block
2021-01-20 01:05:41 +00:00
Tree = ConstructASTNode ( OP_LOOP , RET_NONE , Condition , NULL , Tree , NULL , 0 ) ;
2020-09-10 00:56:16 +00:00
// We need to append the postop to the loop, to form the final for loop
2021-01-20 01:05:41 +00:00
return ConstructASTNode ( OP_COMP , RET_NONE , Preop , NULL , Tree , NULL , 0 ) ;
2020-09-10 00:56:16 +00:00
}
2021-01-20 19:22:15 +00:00
/*
* Handles the surrounding logic for the Print statement .
*
* This is a legacy hold - over from the early testing , and it
2022-03-03 00:05:10 +00:00
* serves merely as a wrapper around the cstdlib printf . er function .
2021-01-20 19:22:15 +00:00
*
* It does , however ( //TODO), attempt to guess the type that you
* want to print , which takes a lot of the guesswork out of printing .
*
* @ return the AST of this statement
*/
2020-09-10 00:56:16 +00:00
struct ASTNode * PrintStatement ( void ) {
struct ASTNode * Tree ;
int LeftType , RightType ;
VerifyToken ( KW_PRINT , " print " ) ;
Tree = ParsePrecedenceASTNode ( 0 ) ;
LeftType = RET_INT ;
RightType = Tree - > ExprType ;
2020-09-14 01:05:24 +00:00
Tree = MutateType ( Tree , RightType , 0 ) ;
2022-03-03 00:05:10 +00:00
if ( ! Tree )
2020-09-10 00:56:16 +00:00
DieDecimal ( " Attempting to print an invalid type: " , RightType ) ;
2022-03-03 00:05:10 +00:00
if ( RightType )
2021-01-20 19:22:15 +00:00
Tree = ConstructASTBranch ( Tree - > Right - > Operation , RET_INT , Tree , NULL , 0 ) ;
2022-03-03 00:05:10 +00:00
2021-01-20 01:05:41 +00:00
Tree = ConstructASTBranch ( OP_PRINT , RET_NONE , Tree , NULL , 0 ) ;
2020-09-10 00:56:16 +00:00
//ParseAST(Tree);
return Tree ;
2022-03-03 00:05:10 +00:00
2020-09-13 01:26:49 +00:00
}
2023-04-24 02:03:31 +00:00
struct ASTNode * SwitchStatement ( ) {
struct ASTNode * left , * root , * c , * casetree = NULL , * casetail ;
int looping = 1 , cases = 0 ;
int defaultpresent = 0 ;
int ASTOp , casevalue ;
printf ( " \t Parsing switch statement \n " ) ;
2023-04-24 02:57:58 +00:00
CurrentFile - > SwitchStatement = true ;
2023-04-24 02:03:31 +00:00
// Skip switch(
Tokenise ( ) ;
VerifyToken ( LI_LPARE , " ( " ) ;
printf ( " \t Switch: Reading switch expression \n " ) ;
// Fetch switch expression
left = ParsePrecedenceASTNode ( 0 ) ;
// Consume ) {
VerifyToken ( LI_RPARE , " ) " ) ;
VerifyToken ( LI_LBRAC , " { " ) ;
// Verify the switch expression (must be integer-compatible)
2023-04-24 21:26:54 +00:00
if ( ! TypeIsInt ( left - > ExprType ) )
ErrorReport ( " Switch expression is not of integer type, instead %s. \n " , TypeNames ( left - > ExprType ) ) ;
2023-04-24 02:03:31 +00:00
Safe ( ) ;
// Create the root Switch node
root = ConstructASTBranch ( OP_SWITCH , 0 , left , NULL , 0 ) ;
// Iterate down the switch node, generating cases
while ( looping ) {
switch ( CurrentFile - > CurrentSymbol . type ) {
case LI_RBRAC :
if ( cases = = 0 )
Die ( " No cases in switch statement " ) ;
looping = 0 ;
break ;
case KW_CASE :
if ( defaultpresent )
Die ( " Case present after default in switch. " ) ;
ASTOp = OP_CASE ;
Safe ( ) ;
Tokenise ( ) ;
// Parse case value
left = ParsePrecedenceASTNode ( 0 ) ;
if ( left - > Operation ! = TERM_INTLITERAL )
Die ( " Expecting integer literal for case value " ) ;
casevalue = left - > IntValue ;
printf ( " \t \t Switch case %d found \n " , casevalue ) ;
// Make sure nothing resolves to the same case value
for ( c = casetree ; c ! = NULL ; c = c - > Right )
if ( casevalue = = c - > IntValue )
Die ( " Duplicate case ID in switch statement " ) ;
// Fallthrough so that we get the case tree logic deduplicated
case KW_DEFAULT :
if ( defaultpresent )
Die ( " Duplicate default entries in switch " ) ;
// Duplicate check because CASE falls through into this block
if ( CurrentFile - > CurrentSymbol . type = = KW_DEFAULT ) {
ASTOp = OP_DEFAULT ;
defaultpresent = true ;
Tokenise ( ) ;
printf ( " \t \t Switch default case found \n " ) ;
}
VerifyToken ( LI_COLON , " : " ) ;
Safe ( ) ;
2023-04-24 02:57:58 +00:00
left = ParseCompound ( ) ;
2023-04-24 02:03:31 +00:00
OptionallyConsume ( LI_SEMIC ) ;
cases + + ;
Safe ( ) ;
// Append this new case to the tree
if ( casetree = = NULL ) {
casetree = casetail = ConstructASTBranch ( ASTOp , 0 , left , NULL , casevalue ) ;
} else {
casetail - > Right = ConstructASTBranch ( ASTOp , 0 , left , NULL , casevalue ) ;
casetail = casetail - > Right ;
}
break ;
default :
ErrorReport ( " Unexpected token in switch statement: %s \n " , TokenNames [ CurrentFile - > CurrentSymbol . type ] ) ;
exit ( 1 ) ;
}
}
root - > IntValue = cases ;
root - > Right = casetree ;
// Consume the right brace immediately
VerifyToken ( LI_RBRAC , " } " ) ;
2023-04-24 02:57:58 +00:00
CurrentFile - > SwitchStatement = false ;
2023-04-24 02:03:31 +00:00
return root ;
}
2022-03-05 23:42:01 +00:00
/**
* Handles the surrounding logic for break statements
*
* They have the basic form of :
* break ;
*
* If there is a loop currently being evaluated , break will insert an immediate jump to the end of the loop .
* All locals inside the loop will lose their binding at this point .
*
* It can be prototyped as the following pseudo - assembler code :
*
* while :
* check < condition >
* jne exit
* < body >
* < break > : jump exit
* jump while
* exit :
* < loop exit >
*
*
* @ return the AST of this statement
*/
struct ASTNode * BreakStatement ( ) {
2023-04-24 02:57:58 +00:00
if ( CurrentFile - > CurrentLoopDepth = = 0 & & ! CurrentFile - > SwitchStatement )
Die ( " Unable to break without a loop or switch statement " ) ;
2022-03-05 23:42:01 +00:00
Tokenise ( ) ;
2023-04-24 19:41:49 +00:00
Safe ( ) ;
VerifyToken ( LI_SEMIC , " ; " ) ;
Safe ( ) ;
2022-03-05 23:42:01 +00:00
return ConstructASTLeaf ( OP_BREAK , 0 , NULL , 0 ) ;
}
/**
* Handles the surrounding logic for continue statements
*
* They have the basic form of :
* continue ;
*
* If there is a loop currently being evaluated , continue will insert an immediate jump to the start of the loop .
*
* It can be prototyped as the following pseudo - assembler code :
*
* while :
* check < condition >
* jne exit
* < body >
* < continue > : jump while
* jump while
* exit :
* < loop exit >
*
*
* @ return the AST of this statement
*/
struct ASTNode * ContinueStatement ( ) {
if ( CurrentFile - > CurrentLoopDepth = = 0 )
Die ( " Unable to break without a loop " ) ;
Tokenise ( ) ;
return ConstructASTLeaf ( OP_CONTINUE , 0 , NULL , 0 ) ;
}
2021-01-20 19:22:15 +00:00
/*
* Handles the surrounding logic for all of the logical and semantic
* postfixes .
*
* Postfixes are tokens that are affixed to the end of another , and
* change behaviour in some way . These can be added calculations ,
* some form of transformation , or other .
*
* A current list of postfixes :
* * ( ) : Call a function
* * [ ] : Index or define an array .
* * + + : Increment a variable AFTER it is returned
* NOTE : there is a prefix variant of this for incrementing BEFOREhand .
* * - - : Decrement a variable AFTER it is returned
* NOTE : there is a prefix variant of this for decrementing BEFOREhand .
*
* Planned postfixes :
* * > > : Arithmetic - Shift - Right a variable by one ( Divide by two )
* NOTE : there is a prefix variant of this for shifting left - multiplying by two .
*
* @ return the AST of the statement plus its ' postfix
*/
2020-11-23 21:42:32 +00:00
struct ASTNode * PostfixStatement ( ) {
struct ASTNode * Tree ;
2021-01-20 01:05:41 +00:00
struct SymbolTableEntry * Entry ;
2020-11-23 21:42:32 +00:00
2022-03-04 01:11:04 +00:00
// Early exit if we find an enum value
if ( ( Entry = FindEnumMember ( CurrentIdentifier ) ) ! = NULL ) {
Tokenise ( ) ;
return ConstructASTLeaf ( TERM_INTLITERAL , RET_INT , NULL , Entry - > IntValue ) ;
}
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2022-03-03 00:05:10 +00:00
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type = = LI_LPARE )
2020-11-23 21:42:32 +00:00
return CallFunction ( ) ;
2022-03-03 00:05:10 +00:00
2022-03-05 01:13:45 +00:00
if ( CurrentFile - > CurrentSymbol . type = = LI_LBRAS )
2020-11-23 21:42:32 +00:00
return AccessArray ( ) ;
2022-03-03 00:05:10 +00:00
2020-11-23 21:42:32 +00:00
// If we get here, we must be a variable.
2021-01-20 19:22:15 +00:00
// (as functions have been called and arrays have been indexed)
// Check that the variable is recognized..
2020-11-23 21:42:32 +00:00
2022-03-03 00:05:10 +00:00
if ( ( Entry = FindSymbol ( CurrentIdentifier ) ) = = NULL | |
( Entry - > Structure ! = ST_VAR & & Entry - > Structure ! = ST_FUNC ) ) {
2021-03-15 01:22:47 +00:00
DumpAllLists ( ) ;
2020-11-23 21:42:32 +00:00
DieMessage ( " Unknown Variable " , CurrentIdentifier ) ;
2021-03-15 01:22:47 +00:00
}
2020-11-23 21:42:32 +00:00
// Here we check for postincrement and postdecrement.
2022-03-05 01:13:45 +00:00
switch ( CurrentFile - > CurrentSymbol . type ) {
2021-07-04 23:07:04 +00:00
case LI_DOT :
return AccessMember ( false ) ;
case LI_ARROW :
return AccessMember ( true ) ;
2020-11-23 21:42:32 +00:00
case PPMM_PLUS :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2021-01-20 01:05:41 +00:00
Tree = ConstructASTLeaf ( OP_POSTINC , Entry - > Type , Entry , 0 ) ;
2020-11-23 21:42:32 +00:00
break ;
case PPMM_MINUS :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2021-01-20 01:05:41 +00:00
Tree = ConstructASTLeaf ( OP_POSTDEC , Entry - > Type , Entry , 0 ) ;
2020-11-23 21:42:32 +00:00
break ;
default :
2021-01-20 01:05:41 +00:00
Tree = ConstructASTLeaf ( REF_IDENT , Entry - > Type , Entry , 0 ) ;
2020-11-23 21:42:32 +00:00
}
return Tree ;
2022-03-03 00:05:10 +00:00
2020-11-23 21:42:32 +00:00
}
2021-01-20 19:22:15 +00:00
/*
* Handles the surrounding logic for all of the logical and semantic
* prefixes .
*
* Prefixes are tokens that are affixed to the start of another , and
* change behaviour in some way . These can be added calculations ,
* some form of transformation , or other .
*
* A current list of prefixes :
* * ! : Invert the boolean result of a statement or truthy value .
* * ~ : Invert the individual bits in a number
* * - : Invert the number around the axis of 0 ( negative - > positive , positive - > negative )
* * + + : Increment a variable BEFORE it is returned .
* NOTE : there is a postfix variant of this for incrementing AFTER the fact .
* * - - : Decrement a variable BEFORE it is returned .
* NOTE : there is a postfix variant of this for decrementing AFTER the fact .
* * & : Dereference the following object ( Get the address that contains it )
* * * : Get the object pointed at by the number following
*
* Planned prefixes :
* * < < : Arithmetic - Shift - Left a variable by one ( Multiply by two )
* NOTE : there is a postfix variant of this for shifting right - dividing by two .
*
* @ return the AST of this statement , plus its ' prefixes and any postfixes .
*/
2020-09-13 01:26:49 +00:00
struct ASTNode * PrefixStatement ( ) {
struct ASTNode * Tree ;
2022-03-05 01:13:45 +00:00
switch ( CurrentFile - > CurrentSymbol . type ) {
2020-11-23 21:42:32 +00:00
case BOOL_INVERT :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2020-11-23 21:42:32 +00:00
Tree = PrefixStatement ( ) ;
Tree - > RVal = 1 ;
2021-01-20 01:05:41 +00:00
Tree = ConstructASTBranch ( OP_BOOLNOT , Tree - > ExprType , Tree , NULL , 0 ) ;
2020-11-23 21:42:32 +00:00
break ;
case BIT_NOT :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2020-11-23 21:42:32 +00:00
Tree = PrefixStatement ( ) ;
Tree - > RVal = 1 ;
2021-01-20 01:05:41 +00:00
Tree = ConstructASTBranch ( OP_BITNOT , Tree - > ExprType , Tree , NULL , 0 ) ;
2020-11-23 21:42:32 +00:00
break ;
2022-03-03 00:05:10 +00:00
2020-11-24 13:17:01 +00:00
case AR_MINUS :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2020-11-24 13:17:01 +00:00
Tree = PrefixStatement ( ) ;
2022-03-03 00:05:10 +00:00
2021-01-20 01:05:41 +00:00
Tree = ConstructASTBranch ( OP_NEGATE , Tree - > ExprType , Tree , NULL , 0 ) ;
2020-11-24 13:17:01 +00:00
break ;
2020-11-23 21:42:32 +00:00
case PPMM_PLUS :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2020-11-23 21:42:32 +00:00
Tree = PrefixStatement ( ) ;
2022-03-03 00:05:10 +00:00
if ( Tree - > Operation ! = REF_IDENT )
2020-11-23 21:42:32 +00:00
Die ( " ++ not followed by identifier " ) ;
2021-01-20 01:05:41 +00:00
Tree = ConstructASTBranch ( OP_PREINC , Tree - > ExprType , Tree , NULL , 0 ) ;
2020-11-23 21:42:32 +00:00
break ;
case PPMM_MINUS :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2020-11-23 21:42:32 +00:00
Tree = PrefixStatement ( ) ;
2022-03-03 00:05:10 +00:00
if ( Tree - > Operation ! = REF_IDENT )
2020-11-23 21:42:32 +00:00
Die ( " -- not followed by identifier " ) ;
2022-03-03 00:05:10 +00:00
2021-01-20 01:05:41 +00:00
Tree = ConstructASTBranch ( OP_PREDEC , Tree - > ExprType , Tree , NULL , 0 ) ;
2020-11-23 21:42:32 +00:00
break ;
case BIT_AND :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2020-09-13 01:26:49 +00:00
// To allow things like:
// x = &&y;
// We need to recursively parse prefixes;
Tree = PrefixStatement ( ) ;
2022-03-03 00:05:10 +00:00
if ( Tree - > Operation ! = REF_IDENT )
2020-09-13 01:26:49 +00:00
Die ( " & must be followed by another & or an identifier. " ) ;
Tree - > Operation = OP_ADDRESS ;
Tree - > ExprType = PointerTo ( Tree - > ExprType ) ;
break ;
case AR_STAR :
2021-01-20 19:22:15 +00:00
Tokenise ( ) ;
2020-09-13 01:26:49 +00:00
Tree = PrefixStatement ( ) ;
2022-03-03 00:05:10 +00:00
if ( Tree - > Operation ! = REF_IDENT & & Tree - > Operation ! = OP_DEREF )
2020-09-13 01:26:49 +00:00
Die ( " * must be followed by another * or an identifier. " ) ;
2020-11-15 06:40:05 +00:00
2021-01-20 01:05:41 +00:00
Tree = ConstructASTBranch ( OP_DEREF , ValueAt ( Tree - > ExprType ) , Tree , NULL , 0 ) ;
2020-09-13 01:26:49 +00:00
break ;
default :
Tree = ParsePrimary ( ) ;
2022-03-03 00:05:10 +00:00
2020-09-13 01:26:49 +00:00
}
return Tree ;
2020-09-10 00:56:16 +00:00
}