Compare commits
2 Commits
264c50509e
...
610c45e3e1
Author | SHA1 | Date | |
---|---|---|---|
610c45e3e1 | |||
a0d2575a83 |
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
extern_ struct SymbolTable Symbols[SYMBOLS];
|
extern_ struct SymbolTable Symbols[SYMBOLS];
|
||||||
|
|
||||||
|
extern_ int TypeSizes[9];
|
||||||
extern_ char* TypeNames[9];
|
extern_ char* TypeNames[9];
|
||||||
|
|
||||||
extern_ char* TokenStrings[];
|
extern_ char* TokenStrings[];
|
||||||
|
|
|
@ -107,6 +107,7 @@ enum SyntaxOps {
|
||||||
LV_IDENT, // Write an identifier in the form of an l-value.
|
LV_IDENT, // Write an identifier in the form of an l-value.
|
||||||
|
|
||||||
OP_WIDEN, // Something contains a type that needs to be casted up
|
OP_WIDEN, // Something contains a type that needs to be casted up
|
||||||
|
OP_SCALE, // We have a pointer that needs to be scaled!
|
||||||
|
|
||||||
OP_CALL, // Call a function
|
OP_CALL, // Call a function
|
||||||
OP_RET, // Return from a function
|
OP_RET, // Return from a function
|
||||||
|
@ -128,6 +129,7 @@ struct ASTNode {
|
||||||
struct ASTNode* Middle;
|
struct ASTNode* Middle;
|
||||||
struct ASTNode* Right;
|
struct ASTNode* Right;
|
||||||
union {
|
union {
|
||||||
|
int Size; // OP_SCALE's linear representation
|
||||||
int IntValue; // TERM_INTLIT's Value
|
int IntValue; // TERM_INTLIT's Value
|
||||||
int ID; // LV_IDENT's Symbols[] index.
|
int ID; // LV_IDENT's Symbols[] index.
|
||||||
} Value;
|
} Value;
|
||||||
|
@ -190,14 +192,21 @@ enum StructureType {
|
||||||
|
|
||||||
int Tokenise(struct Token* Token);
|
int Tokenise(struct Token* Token);
|
||||||
|
|
||||||
int TypesCompatible(int* Left, int* Right, int STRICT);
|
|
||||||
|
|
||||||
void VerifyToken(int Type, char* TokenExpected);
|
void VerifyToken(int Type, char* TokenExpected);
|
||||||
void RejectToken(struct Token* Token);
|
void RejectToken(struct Token* Token);
|
||||||
|
|
||||||
static int ReadIdentifier(int Char, char* Buffer, int Limit);
|
static int ReadIdentifier(int Char, char* Buffer, int Limit);
|
||||||
static int ReadKeyword(char* Str);
|
static int ReadKeyword(char* Str);
|
||||||
|
|
||||||
|
/* * * * * * * * * * * * * * * * * * * *
|
||||||
|
* * * * * T Y P E S * * * * * *
|
||||||
|
* * * * * * * * * * * * * * * * * * * */
|
||||||
|
|
||||||
|
struct ASTNode* MutateType(struct ASTNode* Tree, int RightType, int Operation);
|
||||||
|
|
||||||
|
int TypeIsInt(int Type);
|
||||||
|
int TypeIsPtr(int Type);
|
||||||
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
* * * * * * S Y N T A X T R E E * * * * * *
|
* * * * * * S Y N T A X T R E E * * * * * *
|
||||||
|
@ -318,6 +327,8 @@ int NewLabel(void);
|
||||||
void AsJmp(int Label);
|
void AsJmp(int Label);
|
||||||
void AsLabel(int Label);
|
void AsLabel(int Label);
|
||||||
|
|
||||||
|
int AsShl(int Register, int Val);
|
||||||
|
|
||||||
int AsReturn(int Register, int FuncID);
|
int AsReturn(int Register, int FuncID);
|
||||||
int AsCall(int Register, int FuncID);
|
int AsCall(int Register, int FuncID);
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,18 @@ int AssembleTree(struct ASTNode* Node, int Register, int ParentOp) {
|
||||||
case OP_DIVIDE:
|
case OP_DIVIDE:
|
||||||
return AsDiv(LeftVal, RightVal);
|
return AsDiv(LeftVal, RightVal);
|
||||||
|
|
||||||
|
case OP_SCALE:
|
||||||
|
// We can (ab)use the powers of 2 to do
|
||||||
|
// efficient scaling with bitshifting.
|
||||||
|
switch(Node->Value.Size) {
|
||||||
|
case 2: return AsShl(LeftVal, 1);
|
||||||
|
case 4: return AsShl(LeftVal, 2);
|
||||||
|
case 8: return AsShl(LeftVal, 3);
|
||||||
|
|
||||||
|
default:
|
||||||
|
RightVal = AsLoad(Node->Value.Size);
|
||||||
|
return AsMul(LeftVal, RightVal);
|
||||||
|
}
|
||||||
case OP_ADDRESS:
|
case OP_ADDRESS:
|
||||||
return AsAddr(Node->Value.ID);
|
return AsAddr(Node->Value.ID);
|
||||||
|
|
||||||
|
@ -344,6 +356,12 @@ int AsDiv(int Left, int Right) {
|
||||||
return Left;
|
return Left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AsShl(int Register, int Val) {
|
||||||
|
printf("\tShifting %s to the left by %d bits.\n", Registers[Register], Val);
|
||||||
|
fprintf(OutputFile, "\tsalq\t$%d, %s\n", Val, Registers[Register]);
|
||||||
|
return Register;
|
||||||
|
}
|
||||||
|
|
||||||
int AsLdVar(int ID) {
|
int AsLdVar(int ID) {
|
||||||
int Reg = RetrieveRegister();
|
int Reg = RetrieveRegister();
|
||||||
|
|
||||||
|
@ -431,8 +449,16 @@ void AsNewSymb(int ID) {
|
||||||
|
|
||||||
TypeSize = PrimitiveSize(Symbols[ID].Type);
|
TypeSize = PrimitiveSize(Symbols[ID].Type);
|
||||||
|
|
||||||
fprintf(OutputFile, "\t.comm\t%s, %d, %d\n", Symbols[ID].Name, TypeSize, TypeSize);
|
fprintf(OutputFile, "\t.data\n"
|
||||||
|
"\t.globl\t%s\n",
|
||||||
|
Symbols[ID].Name);
|
||||||
|
|
||||||
|
switch(TypeSize) {
|
||||||
|
case 1: fprintf(OutputFile, "%s:\t.byte\t0\n", Symbols[ID].Name);
|
||||||
|
case 4: fprintf(OutputFile, "%s:\t.long\t0\n", Symbols[ID].Name);
|
||||||
|
case 8: fprintf(OutputFile, "%s:\t.quad\t0\n", Symbols[ID].Name);
|
||||||
|
default: DieDecimal("Unknown type in AsNewSymbol", TypeSize);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int AsCall(int Register, int FuncID) {
|
int AsCall(int Register, int FuncID) {
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
#undef extern_
|
#undef extern_
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
|
||||||
|
int TypeSizes[9] = { 0, 1, 4, 8, 0, 8, 8, 8, 8}; // in BYTES
|
||||||
|
|
||||||
char* TokenStrings[] = { "+", "-", "*", "/", "int" };
|
char* TokenStrings[] = { "+", "-", "*", "/", "int" };
|
||||||
char* TokenNames[] = {
|
char* TokenNames[] = {
|
||||||
"End of file",
|
"End of file",
|
||||||
|
|
95
src/Parser.c
95
src/Parser.c
|
@ -144,35 +144,11 @@ struct ASTNode* ParsePrimary(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct ASTNode* ParseNewASTNode(void) {
|
|
||||||
//fprintf(stdout, "New node requested.");
|
|
||||||
struct ASTNode* LeftNode, *RightNode;
|
|
||||||
int NodeType;
|
|
||||||
|
|
||||||
|
|
||||||
LeftNode = ParsePrimary(); // Fetches next token!
|
|
||||||
|
|
||||||
// If there's just a number, then this is the AST Node.
|
|
||||||
// Return, as the root of the tree is the end of the tree.
|
|
||||||
if(CurrentToken.type == LI_EOF)
|
|
||||||
return(LeftNode);
|
|
||||||
|
|
||||||
|
|
||||||
NodeType = ParseTokenToOperation(CurrentToken.type);
|
|
||||||
|
|
||||||
Tokenise(&CurrentToken);
|
|
||||||
|
|
||||||
RightNode = ParseNewASTNode();
|
|
||||||
|
|
||||||
return ConstructASTNode(NodeType, LeftNode->ExprType, LeftNode, NULL, RightNode, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
struct ASTNode* ParsePrecedenceASTNode(int PreviousTokenPrecedence) {
|
struct ASTNode* ParsePrecedenceASTNode(int PreviousTokenPrecedence) {
|
||||||
struct ASTNode* LeftNode, *RightNode;
|
struct ASTNode* LeftNode, *RightNode;
|
||||||
|
struct ASTNode* LeftTemp, *RightTemp;
|
||||||
int LeftType, RightType;
|
int LeftType, RightType;
|
||||||
int NodeType;
|
int NodeType, OpType;
|
||||||
|
|
||||||
LeftNode = PrefixStatement();
|
LeftNode = PrefixStatement();
|
||||||
|
|
||||||
|
@ -190,8 +166,45 @@ struct ASTNode* ParsePrecedenceASTNode(int PreviousTokenPrecedence) {
|
||||||
LeftType = LeftNode->ExprType;
|
LeftType = LeftNode->ExprType;
|
||||||
RightType = RightNode->ExprType;
|
RightType = RightNode->ExprType;
|
||||||
|
|
||||||
if(!TypesCompatible(&LeftType, &RightType, 0))
|
/**
|
||||||
Die("Assignment between incompatible types");
|
* While parsing this node, we may need to widen some types.
|
||||||
|
* This requires a few functions and checks.
|
||||||
|
*/
|
||||||
|
|
||||||
|
OpType = ParseTokenToOperation(NodeType);
|
||||||
|
LeftTemp = MutateType(LeftNode, RightType, OpType);
|
||||||
|
RightTemp = MutateType(RightNode, LeftType, OpType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If both are null, the types are incompatible.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(LeftTemp == NULL && RightTemp == NULL)
|
||||||
|
Die("Incompatible types in parsing nodes");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the left was valid, or valid for
|
||||||
|
* expansion, then it will be non-null.
|
||||||
|
*
|
||||||
|
* If it was valid, then this will be
|
||||||
|
* equivalent to LeftNode = LeftNode
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(LeftTemp)
|
||||||
|
LeftNode = LeftTemp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same here, but there is a higher chance
|
||||||
|
* for the right node to be incompatible due
|
||||||
|
* to the nature of widening types.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(RightTemp)
|
||||||
|
RightNode = RightTemp;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks over, back to normal parsing.
|
||||||
|
*/
|
||||||
|
|
||||||
if(LeftType)
|
if(LeftType)
|
||||||
LeftNode = ConstructASTBranch(LeftType, RightNode->ExprType, LeftNode, 0);
|
LeftNode = ConstructASTBranch(LeftType, RightNode->ExprType, LeftNode, 0);
|
||||||
|
@ -439,29 +452,3 @@ void ParseGlobals() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* void ParseStatements() {
|
|
||||||
while(1) {
|
|
||||||
switch(CurrentToken.type) {
|
|
||||||
|
|
||||||
case KW_PRINT:
|
|
||||||
PrintStatement();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TY_INT:
|
|
||||||
BeginVariableDeclaration();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case TY_IDENTIFIER:
|
|
||||||
AssignVariable();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case LI_EOF:
|
|
||||||
return;
|
|
||||||
|
|
||||||
default:
|
|
||||||
DieDecimal("Syntax error; Token", CurrentToken.type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} */
|
|
||||||
|
|
||||||
|
|
||||||
|
|
128
src/Statements.c
128
src/Statements.c
|
@ -8,110 +8,6 @@
|
||||||
#include <Data.h>
|
#include <Data.h>
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Turn a token type into its appropriate
|
|
||||||
* primitive type.
|
|
||||||
*
|
|
||||||
* This is where we do redirections like:
|
|
||||||
* short -> s16
|
|
||||||
* long -> s64
|
|
||||||
* int -> s32
|
|
||||||
* char -> u8
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
static int TypeSize[9] = { 0, 1, 4, 8, 0, 8, 8, 8, 8}; // in BYTES
|
|
||||||
|
|
||||||
int PrimitiveSize(int Type) {
|
|
||||||
if(Type < RET_NONE || Type > PTR_VOID)
|
|
||||||
DieDecimal("Checking size of bad data type", Type);
|
|
||||||
|
|
||||||
return TypeSize[Type];
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Given two types, determine if they are compatible.
|
|
||||||
*
|
|
||||||
* Depending on the value of STRICT, it will try to
|
|
||||||
* fit the right value into the left value.
|
|
||||||
*
|
|
||||||
* This is valid, for ie. a char into an int, as int is larger than char.
|
|
||||||
* This is called widening the char.
|
|
||||||
*
|
|
||||||
* If STRICT is set, it will only allow widening the left to the right.
|
|
||||||
* This means you cannot `char a; int b; b = 15000; a = b;`
|
|
||||||
* As this would shrink the int and lose resolution.
|
|
||||||
*
|
|
||||||
* NOTE: THIS IS NOT THE DEFAULT BEHAVIOUR
|
|
||||||
* By default, you CAN shrink an int into a char, a la shifting down.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
int TypesCompatible(int* Left, int* Right, int STRICT) {
|
|
||||||
|
|
||||||
int LeftSize, RightSize;
|
|
||||||
|
|
||||||
// Same types are compatible. No shrinking required
|
|
||||||
if(*Left == *Right) {
|
|
||||||
*Left = *Right = 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
LeftSize = PrimitiveSize(*Left);
|
|
||||||
RightSize = PrimitiveSize(*Right);
|
|
||||||
|
|
||||||
|
|
||||||
// Types of size 0 are incompatible
|
|
||||||
if((LeftSize == 0) || (RightSize == 0))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
|
|
||||||
/* char x;
|
|
||||||
* int y;
|
|
||||||
* y = 15;
|
|
||||||
*
|
|
||||||
* x = y;
|
|
||||||
* x needs to be widened, y copied in, then x shrunk back down
|
|
||||||
* AKA, the left must be widened.
|
|
||||||
*/
|
|
||||||
if(LeftSize < RightSize) {
|
|
||||||
*Left = OP_WIDEN;
|
|
||||||
*Right = 0;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* char x;
|
|
||||||
* int y;
|
|
||||||
*
|
|
||||||
* x = 15;
|
|
||||||
*
|
|
||||||
* y = x;
|
|
||||||
* x must be widened to fit into y.
|
|
||||||
* if STRICT mode, this is not allowed.
|
|
||||||
* By default, this is valid.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
if(LeftSize > RightSize) {
|
|
||||||
if(STRICT)
|
|
||||||
return 0; // Not compatible if STRICT
|
|
||||||
|
|
||||||
*Left = 0;
|
|
||||||
*Right = OP_WIDEN;
|
|
||||||
return 1; // Compatible by default
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Any other cases left, by default, are compatible.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
*Left = *Right = 0;
|
|
||||||
return 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handles the declaration of a type of a variable.
|
* Handles the declaration of a type of a variable.
|
||||||
|
@ -208,7 +104,8 @@ struct ASTNode* ReturnStatement() {
|
||||||
ReturnType = Tree->ExprType;
|
ReturnType = Tree->ExprType;
|
||||||
FunctionType = Symbols[CurrentFunction].Type;
|
FunctionType = Symbols[CurrentFunction].Type;
|
||||||
|
|
||||||
if(!TypesCompatible(&ReturnType, &FunctionType, 0))
|
Tree = MutateType(Tree, FunctionType, 0);
|
||||||
|
if(!Tree)
|
||||||
Die("Returning a value of incorrect type for function");
|
Die("Returning a value of incorrect type for function");
|
||||||
|
|
||||||
|
|
||||||
|
@ -225,13 +122,18 @@ struct ASTNode* ReturnStatement() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Handles the assignment of variables.
|
* Handles Identifiers.
|
||||||
*
|
*
|
||||||
* You can assign variables with an assignment,
|
* This is called for any of:
|
||||||
|
* - Calling a function
|
||||||
|
* - Assigning an lvalue variable
|
||||||
|
* - Performing arithmetic on a variable
|
||||||
|
* - Performing arithmetic with the return values of function calls
|
||||||
|
*
|
||||||
|
* For the case where you're assigning an l-value;
|
||||||
|
* You can assign with another assignment,
|
||||||
* a statement, a function or a literal.
|
* a statement, a function or a literal.
|
||||||
*
|
*
|
||||||
* This means we need to do some recursive parsing.
|
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
struct ASTNode* ParseIdentifier() {
|
struct ASTNode* ParseIdentifier() {
|
||||||
|
@ -259,8 +161,9 @@ struct ASTNode* ParseIdentifier() {
|
||||||
LeftType = Left->ExprType;
|
LeftType = Left->ExprType;
|
||||||
RightType = Right->ExprType;
|
RightType = Right->ExprType;
|
||||||
|
|
||||||
if(!TypesCompatible(&LeftType, &RightType, 1))
|
Left = MutateType(Left, RightType, 0);
|
||||||
Die("Incompatible variable types");
|
if(!Left)
|
||||||
|
Die("Incompatible types in assignment");
|
||||||
|
|
||||||
if(LeftType)
|
if(LeftType)
|
||||||
Left = ConstructASTBranch(LeftType, Right->ExprType, Left, 0);
|
Left = ConstructASTBranch(LeftType, Right->ExprType, Left, 0);
|
||||||
|
@ -363,7 +266,8 @@ struct ASTNode* PrintStatement(void) {
|
||||||
LeftType = RET_INT;
|
LeftType = RET_INT;
|
||||||
RightType = Tree->ExprType;
|
RightType = Tree->ExprType;
|
||||||
|
|
||||||
if(!TypesCompatible(&LeftType, &RightType, 0))
|
Tree = MutateType(Tree, RightType, 0);
|
||||||
|
if(!Tree)
|
||||||
DieDecimal("Attempting to print an invalid type:", RightType);
|
DieDecimal("Attempting to print an invalid type:", RightType);
|
||||||
|
|
||||||
if(RightType)
|
if(RightType)
|
||||||
|
|
202
src/Types.c
Normal file
202
src/Types.c
Normal file
|
@ -0,0 +1,202 @@
|
||||||
|
|
||||||
|
/*************/
|
||||||
|
/*GEMWIRE */
|
||||||
|
/* ERYTHRO*/
|
||||||
|
/*************/
|
||||||
|
|
||||||
|
#include <Defs.h>
|
||||||
|
#include <Data.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn a token type into its appropriate
|
||||||
|
* primitive type.
|
||||||
|
*
|
||||||
|
* This is where we do redirections like:
|
||||||
|
* short -> s16
|
||||||
|
* long -> s64
|
||||||
|
* int -> s32
|
||||||
|
* char -> u8
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int PrimitiveSize(int Type) {
|
||||||
|
if(Type < RET_NONE || Type > PTR_VOID)
|
||||||
|
DieDecimal("Checking size of bad data type", Type);
|
||||||
|
|
||||||
|
return TypeSizes[Type];
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Given two types, determine if they are compatible.
|
||||||
|
*
|
||||||
|
* Depending on the value of STRICT, it will try to
|
||||||
|
* fit the right value into the left value.
|
||||||
|
*
|
||||||
|
* This is valid, for ie. a char into an int, as int is larger than char.
|
||||||
|
* This is called widening the char.
|
||||||
|
*
|
||||||
|
* If STRICT is set, it will only allow widening the left to the right.
|
||||||
|
* This means you cannot `char a; int b; b = 15000; a = b;`
|
||||||
|
* As this would shrink the int and lose resolution.
|
||||||
|
*
|
||||||
|
* NOTE: THIS IS NOT THE DEFAULT BEHAVIOUR
|
||||||
|
* By default, you CAN shrink an int into a char, a la shifting down.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
int TypesCompatible(int* Left, int* Right, int STRICT) {
|
||||||
|
|
||||||
|
int LeftSize, RightSize;
|
||||||
|
|
||||||
|
// Same types are compatible. No shrinking required
|
||||||
|
if(*Left == *Right) {
|
||||||
|
*Left = *Right = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
LeftSize = PrimitiveSize(*Left);
|
||||||
|
RightSize = PrimitiveSize(*Right);
|
||||||
|
|
||||||
|
|
||||||
|
// Types of size 0 are incompatible
|
||||||
|
if((LeftSize == 0) || (RightSize == 0))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
|
||||||
|
/* char x;
|
||||||
|
* int y;
|
||||||
|
* y = 15;
|
||||||
|
*
|
||||||
|
* x = y;
|
||||||
|
* x needs to be widened, y copied in, then x shrunk back down
|
||||||
|
* AKA, the left must be widened.
|
||||||
|
*/
|
||||||
|
if(LeftSize < RightSize) {
|
||||||
|
*Left = OP_WIDEN;
|
||||||
|
*Right = 0;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* char x;
|
||||||
|
* int y;
|
||||||
|
*
|
||||||
|
* x = 15;
|
||||||
|
*
|
||||||
|
* y = x;
|
||||||
|
* x must be widened to fit into y.
|
||||||
|
* if STRICT mode, this is not allowed.
|
||||||
|
* By default, this is valid.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(LeftSize > RightSize) {
|
||||||
|
if(STRICT)
|
||||||
|
return 0; // Not compatible if STRICT
|
||||||
|
|
||||||
|
*Left = 0;
|
||||||
|
*Right = OP_WIDEN;
|
||||||
|
return 1; // Compatible by default
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Any other cases left, by default, are compatible.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
*Left = *Right = 0;
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an operation on two types, we need to be able to
|
||||||
|
* determine if the operation is valid for both types,
|
||||||
|
* as well as modify the types if the operation is
|
||||||
|
* theoretically valid but requires some changes.
|
||||||
|
*
|
||||||
|
* An example of the latter is assigning an int literal into
|
||||||
|
* a char, or squeezing down the int into the char type.
|
||||||
|
*
|
||||||
|
* If the operation is not valid, this will return NULL.
|
||||||
|
* If the operaton is valid without changes, this will return Tree.
|
||||||
|
* If the operation is valid with changes, this will perform
|
||||||
|
* the changes and return the new tree.
|
||||||
|
*
|
||||||
|
* This also serves to consolidate some of the gross widening
|
||||||
|
* code that TypesCompatible led us to.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct ASTNode* MutateType(struct ASTNode* Tree, int RightType, int Operation) {
|
||||||
|
int LeftType;
|
||||||
|
int LeftSize, RightSize;
|
||||||
|
|
||||||
|
LeftType = Tree->ExprType;
|
||||||
|
|
||||||
|
|
||||||
|
if(TypeIsInt(LeftType) && TypeIsInt(RightType)) {
|
||||||
|
|
||||||
|
// Short-circuit for valid types
|
||||||
|
if(LeftType == RightType) {
|
||||||
|
return Tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
LeftSize = PrimitiveSize(LeftType);
|
||||||
|
RightSize = PrimitiveSize(RightType);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LeftSize > RightSize:
|
||||||
|
* char x = 15000;
|
||||||
|
*
|
||||||
|
* (The left branch of the tree contains the current AST)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
if(LeftSize > RightSize)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* RightSize > LeftSize:
|
||||||
|
* char x = 5;
|
||||||
|
* int y = x;
|
||||||
|
*
|
||||||
|
* We have to widen x into an int in order for this to be compatible
|
||||||
|
* BUT it is possible!
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(RightSize > LeftSize)
|
||||||
|
return ConstructASTBranch(OP_WIDEN, RightType, Tree, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Left branch pointers are compatible if we're not doing operations
|
||||||
|
if(TypeIsPtr(LeftType)) {
|
||||||
|
if(Operation == 0 && LeftType == RightType)
|
||||||
|
return Tree;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Otherwise, we can perform some scaling for pointer addition & subtraction
|
||||||
|
if(Operation == OP_ADD || Operation == OP_SUBTRACT) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Left int, right pointer:
|
||||||
|
* int x = 5;
|
||||||
|
* int* y;
|
||||||
|
*
|
||||||
|
* x = *(y + 1);
|
||||||
|
*/
|
||||||
|
|
||||||
|
if(TypeIsInt(LeftType) && TypeIsPtr(RightType)) {
|
||||||
|
RightSize = PrimitiveSize(ValueAt(RightType));
|
||||||
|
|
||||||
|
if(RightSize > 1)
|
||||||
|
return ConstructASTBranch(OP_SCALE, RightType, Tree, RightSize);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all else fails, we've constructed a combination of types that are not compatible.
|
||||||
|
// ie. left or right is a void.
|
||||||
|
// You cannot do pointer arithmetic on void type.
|
||||||
|
return NULL;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user