203 lines
4.8 KiB
C
203 lines
4.8 KiB
C
|
|
||
|
/*************/
|
||
|
/*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;
|
||
|
}
|