2023-02-05 18:21:26 +00:00
module tlang.compiler.typecheck.core ;
2021-03-21 19:43:01 +00:00
2023-02-05 18:21:26 +00:00
import tlang.compiler.symbols.check ;
import tlang.compiler.symbols.data ;
2023-02-05 10:47:27 +00:00
import std.conv : to , ConvException ;
2021-03-24 21:19:40 +00:00
import std.string ;
import std.stdio ;
2021-03-29 18:13:39 +01:00
import gogga ;
2023-02-05 18:21:26 +00:00
import tlang.compiler.parsing.core ;
import tlang.compiler.typecheck.resolution ;
import tlang.compiler.typecheck.exceptions ;
import tlang.compiler.symbols.typing.core ;
import tlang.compiler.typecheck.dependency.core ;
import tlang.compiler.codegen.instruction ;
2022-12-11 15:33:18 +00:00
import std.container.slist ;
2022-12-19 13:37:55 +00:00
import std.algorithm : reverse ;
2021-03-21 19:43:01 +00:00
/ * *
2021-03-24 21:19:40 +00:00
* The Parser only makes sure syntax
* is adhered to ( and , well , partially )
* as it would allow string + string
* for example
*
2021-03-21 19:43:01 +00:00
* /
public final class TypeChecker
{
2021-03-29 20:06:30 +01:00
private Module modulle ;
2021-03-21 19:43:01 +00:00
2021-03-31 11:54:34 +01:00
/* The name resolver */
private Resolver resolver ;
2021-03-31 20:15:28 +01:00
public Module getModule ( )
{
return modulle ;
}
2021-03-29 20:06:30 +01:00
this ( Module modulle )
2021-03-21 19:43:01 +00:00
{
2021-03-29 20:06:30 +01:00
this . modulle = modulle ;
2021-03-31 11:54:34 +01:00
resolver = new Resolver ( this ) ;
2021-06-04 12:39:11 +01:00
/* TODO: Module check?!?!? */
}
2021-03-23 19:35:13 +00:00
2021-04-07 12:11:36 +01:00
/ * *
* I guess this should be called rather
* when processing assignments but I also
* think we need something like it for
* class initializations first rather than
* variable expressions in assignments
* ( which should probably use some other
* function to check that then )
* /
public void dependencyCheck ( )
{
2021-04-15 16:26:14 +01:00
/* Check declaration and definition types */
checkDefinitionTypes ( modulle ) ;
2021-04-07 12:11:36 +01:00
/* TODO: Implement me */
2021-04-09 14:56:15 +01:00
checkClassInherit ( modulle ) ;
2021-08-15 21:23:17 +01:00
/ * *
* Dependency tree generation
*
* Currently this generates a dependency tree
* just for the module , the tree must be run
* through after wards to make it
* non - cyclic
*
* /
2022-12-11 16:18:50 +00:00
2021-11-10 14:17:42 +00:00
2021-06-09 18:45:57 +01:00
DNodeGenerator dNodeGenerator = new DNodeGenerator ( this ) ;
2023-01-20 14:43:04 +00:00
/* Generate the dependency tree */
2021-08-15 21:33:22 +01:00
DNode rootNode = dNodeGenerator . generate ( ) ; /* TODO: This should make it acyclic */
2021-08-15 21:23:17 +01:00
2023-01-20 14:43:04 +00:00
/* Perform the linearization to the dependency tree */
rootNode . performLinearization ( ) ;
2021-08-15 21:33:22 +01:00
/* Print the tree */
2023-01-20 14:43:04 +00:00
string tree = rootNode . getTree ( ) ;
2021-08-15 21:23:17 +01:00
gprintln ( tree ) ;
2021-10-25 15:55:43 +01:00
/* Get the action-list (linearised bottom up graph) */
2023-01-20 14:43:04 +00:00
DNode [ ] actionList = rootNode . getLinearizedNodes ( ) ;
2023-02-01 13:05:31 +00:00
doTypeCheck ( actionList ) ;
2022-10-01 15:35:23 +01:00
2021-10-25 21:31:07 +01:00
/ * *
2022-12-14 17:49:08 +00:00
* After processing globals executions the instructions will
* be placed into `codeQueue` , therefore copy them from the temporary
* scratchpad queue into `globalCodeQueue` .
*
* Then clean the codeQueue for next use
* /
foreach ( Instruction curGlobInstr ; codeQueue )
{
globalCodeQueue ~ = curGlobInstr ;
}
codeQueue . clear ( ) ;
assert ( codeQueue . empty ( ) = = true ) ;
/* Grab functionData ??? */
FunctionData [ string ] functionDefinitions = grabFunctionDefs ( ) ;
gprintln ( "Defined functions: " ~ to ! ( string ) ( functionDefinitions ) ) ;
foreach ( FunctionData funcData ; functionDefinitions . values )
{
assert ( codeQueue . empty ( ) = = true ) ;
2023-01-20 14:43:04 +00:00
/* Generate the dependency tree */
2022-12-14 17:49:08 +00:00
DNode funcNode = funcData . generate ( ) ;
2023-01-20 14:43:04 +00:00
/* Perform the linearization to the dependency tree */
funcNode . performLinearization ( ) ;
/* Get the action-list (linearised bottom up graph) */
DNode [ ] actionListFunc = funcNode . getLinearizedNodes ( ) ;
2022-12-14 17:49:08 +00:00
//TODO: Would this not mess with our queues?
doTypeCheck ( actionListFunc ) ;
2023-01-20 14:43:04 +00:00
gprintln ( funcNode . getTree ( ) ) ;
2022-12-14 17:49:08 +00:00
// The current code queue would be the function's body instructions
// a.k.a. the `codeQueue`
// functionBodies[funcData.name] = codeQueue;
// The call to `doTypeCheck()` above adds to this queue
// so we should clean it out before the next run
//
// NOTE: Static allocations in? Well, we don't clean init queue
// so is it fine then? We now have seperate dependency trees,
// we should make checking methods that check the `initQueue`
// whenever we come past a `ClassStaticNode` for example
// codeQueue.clear();
/ * *
* Copy over the function code queue into
* the function code queue respective key .
*
* Then clear the scratchpad code queue
* /
functionBodyCodeQueues [ funcData . name ] = [ ] ;
foreach ( Instruction curFuncInstr ; codeQueue )
{
//TODO: Think about class funcs? Nah
functionBodyCodeQueues [ funcData . name ] ~ = curFuncInstr ;
gprintln ( "FuncDef (" ~ funcData . name ~ "): Adding body instruction: " ~ to ! ( string ) ( curFuncInstr ) ) ;
}
codeQueue . clear ( ) ;
gprintln ( "FUNCDEF DONE: " ~ to ! ( string ) ( functionBodyCodeQueues [ funcData . name ] ) ) ;
}
2022-12-11 15:33:18 +00:00
}
2022-12-14 17:49:08 +00:00
/ * *
* Function definitions
*
* Holds their action lists which are to be used for the
* ( later ) emitting of their X - lang emit code
* /
//FUnctionDeifnition should couple `linearizedList` but `functionEntity`
// private FunctionDefinition[string] functionDefinitions2; //TODO: Use this
/ * *
* Concrete queues
*
* These queues below are finalized and not used as a scratchpad .
*
* 1. Global code queue
* - This accounts for the globals needing to be executed
* 2. Function body code queues
* - This accounts for ( every ) function definition ' s code queue
* /
private Instruction [ ] globalCodeQueue ;
private Instruction [ ] [ string ] functionBodyCodeQueues ;
public Instruction [ ] getGlobalCodeQueue ( )
{
return globalCodeQueue ;
}
public Instruction [ ] [ string ] getFunctionBodyCodeQueues ( )
{
return functionBodyCodeQueues ;
}
/* Main code queue (used for temporary passes) */
private SList ! ( Instruction ) codeQueue ; //TODO: Rename to `currentCodeQueue`
2021-10-26 16:17:53 +01:00
2022-10-15 19:36:18 +01:00
/* Initialization queue */
private SList ! ( Instruction ) initQueue ;
2022-12-14 17:49:08 +00:00
//TODO: CHange to oneshot in the function
public Instruction [ ] getInitQueue ( )
2022-12-11 15:37:27 +00:00
{
2022-12-14 17:49:08 +00:00
Instruction [ ] initQueueConcrete ;
foreach ( Instruction currentInstruction ; initQueue )
{
initQueueConcrete ~ = currentInstruction ;
}
return initQueueConcrete ;
2022-12-11 15:37:27 +00:00
}
2022-10-15 20:57:03 +01:00
/* Adds an initialization instruction to the initialization queue (at the back) */
2022-10-15 19:36:18 +01:00
public void addInit ( Instruction initInstruction )
{
initQueue . insertAfter ( initQueue [ ] , initInstruction ) ;
}
2022-10-15 20:57:03 +01:00
/ *
* Prints the current contents of the init - queue
* /
public void printInitQueue ( )
{
import std.range : walkLength ;
ulong i = 0 ;
foreach ( Instruction instruction ; initQueue )
{
gprintln ( "InitQueue: " ~ to ! ( string ) ( i + 1 ) ~ "/" ~ to ! ( string ) ( walkLength ( initQueue [ ] ) ) ~ ": " ~ instruction . toString ( ) ) ;
i + + ;
}
}
2022-10-15 19:36:18 +01:00
/* Adds an instruction to the front of code queue */
2021-10-26 16:17:53 +01:00
public void addInstr ( Instruction inst )
2021-10-26 19:32:47 +01:00
{
codeQueue . insert ( inst ) ;
}
2022-10-15 19:36:18 +01:00
/* Adds an instruction to the back of the code queue */
2021-10-26 19:32:47 +01:00
public void addInstrB ( Instruction inst )
2021-10-26 16:17:53 +01:00
{
codeQueue . insertAfter ( codeQueue [ ] , inst ) ;
}
2022-10-15 19:36:18 +01:00
/* Removes the instruction at the front of the code queue and returns it */
2021-10-26 16:17:53 +01:00
public Instruction popInstr ( )
{
2021-10-26 19:32:47 +01:00
Instruction poppedInstr ;
if ( ! codeQueue . empty )
{
poppedInstr = codeQueue . front ( ) ;
codeQueue . removeFront ( ) ;
}
2021-10-26 16:17:53 +01:00
return poppedInstr ;
}
2021-11-11 10:30:40 +00:00
2022-12-19 13:37:55 +00:00
/* Pops from the tail of the code queue and returns it */
public Instruction tailPopInstr ( )
{
Instruction poppedInstr ;
if ( ! codeQueue . empty )
{
// Perhaps there is a nicer way to tail popping
codeQueue . reverse ( ) ;
poppedInstr = codeQueue . front ( ) ;
codeQueue . removeFront ( ) ;
codeQueue . reverse ( ) ;
}
return poppedInstr ;
}
2021-11-11 10:30:40 +00:00
public bool isInstrEmpty ( )
{
return codeQueue . empty ;
}
2021-10-26 16:17:53 +01:00
2022-12-14 17:49:08 +00:00
// public Instruction[] getCodeQueue()
// {
// Instruction[] codeQueueConcrete;
// foreach(Instruction currentInstruction; codeQueue)
// {
// codeQueueConcrete~=currentInstruction;
// }
// return codeQueueConcrete;
// }
2021-10-27 08:51:13 +01:00
2021-10-26 16:17:53 +01:00
/ *
* Prints the current contents of the code - queue
* /
public void printCodeQueue ( )
{
import std.range : walkLength ;
ulong i = 0 ;
foreach ( Instruction instruction ; codeQueue )
{
2021-10-27 08:25:11 +01:00
gprintln ( to ! ( string ) ( i + 1 ) ~ "/" ~ to ! ( string ) ( walkLength ( codeQueue [ ] ) ) ~ ": " ~ instruction . toString ( ) ) ;
2021-10-26 16:17:53 +01:00
i + + ;
}
}
2022-02-18 12:32:45 +00:00
/ * *
* There are several types and comparing them differs
* /
private bool isSameType ( Type type1 , Type type2 )
{
bool same = false ;
2023-04-17 15:50:11 +01:00
/* Handling for pointers */
if ( typeid ( type1 ) = = typeid ( type2 ) & & cast ( Pointer ) type1 ! is null )
{
Pointer p1 = cast ( Pointer ) type1 , p2 = cast ( Pointer ) type2 ;
/* Now check that both of their referred types are the same */
return isSameType ( p1 . getReferredType ( ) , p2 . getReferredType ( ) ) ;
}
2022-02-18 12:32:45 +00:00
/* Handling for Integers */
2023-04-17 15:50:11 +01:00
else if ( typeid ( type1 ) = = typeid ( type2 ) & & cast ( Integer ) type1 ! is null )
2022-02-18 12:32:45 +00:00
{
Integer i1 = cast ( Integer ) type1 , i2 = cast ( Integer ) type2 ;
/* Both same size? */
if ( i1 . getSize ( ) = = i2 . getSize ( ) )
{
/* Matching signedness ? */
2022-04-08 00:12:52 +01:00
same = i1 . isSigned ( ) = = i2 . isSigned ( ) ;
2022-02-18 12:32:45 +00:00
}
/* Size mismatch */
else
{
2022-04-08 00:12:52 +01:00
same = false ;
2022-02-18 12:32:45 +00:00
}
}
2022-04-08 00:12:52 +01:00
gprintln ( "isSameType(" ~ to ! ( string ) ( type1 ) ~ "," ~ to ! ( string ) ( type2 ) ~ "): " ~ to ! ( string ) ( same ) , DebugType . ERROR ) ;
2022-02-18 12:32:45 +00:00
return same ;
}
2023-02-04 12:37:40 +00:00
/ * *
* Given a type to try coerce towards and a literal value
* instruction , this will check whether the literal itself
* is within the range whereby it may be coerced
*
* Params :
* variableType = the type to try coercing towards
* assignmentInstruction = the literal to apply a range
* check to
* /
private bool isCoercibleRange ( Type toType , Value literalInstr )
{
// You should only be calling this on either a `LiteralValue`
// ... or a `LiteralValueFloat` instruction
// TODO: Add support for UnaryOpInstr (where the inner type is then)
// ... one of the above
assert ( cast ( LiteralValue ) literalInstr | | cast ( LiteralValueFloat ) literalInstr | | cast ( UnaryOpInstr ) literalInstr ) ;
// LiteralValue (integer literal instructions)
if ( cast ( LiteralValue ) literalInstr )
{
2023-02-05 10:27:47 +00:00
LiteralValue integerLiteral = cast ( LiteralValue ) literalInstr ;
string literal = integerLiteral . getLiteralValue ( ) ;
2023-02-04 12:37:40 +00:00
2023-02-05 12:13:15 +00:00
// NOTE (X-platform): For cross-platform sake we should change the `ulong` to `size_t`
ulong literalValue = to ! ( ulong ) ( literal ) ;
2023-02-05 10:47:27 +00:00
2023-02-05 12:13:15 +00:00
if ( isSameType ( toType , getType ( null , "ubyte" ) ) )
{
if ( literalValue > = 0 & & literalValue < = 255 )
2023-02-05 10:47:27 +00:00
{
2023-02-05 12:13:15 +00:00
// Valid coercion
return true ;
2023-02-05 10:47:27 +00:00
}
2023-02-05 12:13:15 +00:00
else
2023-02-05 10:27:47 +00:00
{
2023-02-05 12:13:15 +00:00
// Invalid coercion
return false ;
2023-02-05 10:27:47 +00:00
}
2023-02-05 12:13:15 +00:00
}
else if ( isSameType ( toType , getType ( null , "ushort" ) ) )
{
if ( literalValue > = 0 & & literalValue < = 65_535 )
2023-02-05 10:27:47 +00:00
{
2023-02-05 12:13:15 +00:00
// Valid coercion
return true ;
2023-02-05 10:27:47 +00:00
}
2023-02-05 12:13:15 +00:00
else
2023-02-05 10:47:27 +00:00
{
2023-02-05 12:13:15 +00:00
// Invalid coercion
return false ;
}
}
else if ( isSameType ( toType , getType ( null , "uint" ) ) )
{
if ( literalValue > = 0 & & literalValue < = 4_294_967_295 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
2023-02-05 10:47:27 +00:00
}
}
2023-02-05 12:13:15 +00:00
else if ( isSameType ( toType , getType ( null , "ulong" ) ) )
2023-02-05 10:47:27 +00:00
{
2023-02-05 18:21:26 +00:00
if ( literalValue > = 0 & & literalValue < = 18_446_744_073_709_551_615 )
2023-02-05 12:13:15 +00:00
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
2023-02-05 10:27:47 +00:00
}
2023-02-06 19:49:25 +00:00
// Handling for signed bytes [0, 127]
else if ( isSameType ( toType , getType ( null , "byte" ) ) )
{
if ( literalValue > = 0 & & literalValue < = 127 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
}
// Handling for signed shorts [0, 32_767]
else if ( isSameType ( toType , getType ( null , "short" ) ) )
{
if ( literalValue > = 0 & & literalValue < = 32_767 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
}
// Handling for signed integers [0, 2_147_483_647]
else if ( isSameType ( toType , getType ( null , "int" ) ) )
{
if ( literalValue > = 0 & & literalValue < = 2_147_483_647 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
}
// Handling for signed longs [0, 9_223_372_036_854_775_807]
else if ( isSameType ( toType , getType ( null , "long" ) ) )
{
if ( literalValue > = 0 & & literalValue < = 9_223_372_036_854_775_807 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
}
2023-02-04 12:37:40 +00:00
}
// LiteralValue (integer literal instructions)
else if ( cast ( LiteralValueFloat ) literalInstr )
{
2023-02-05 18:21:26 +00:00
2023-02-04 12:37:40 +00:00
}
// Unary operator
else
{
UnaryOpInstr unaryOpLiteral = cast ( UnaryOpInstr ) literalInstr ;
assert ( unaryOpLiteral . getOperator ( ) = = SymbolType . SUB ) ;
Value operandInstr = unaryOpLiteral . getOperand ( ) ;
// LiteralValue (integer literal instructions) with subtraction infront
2023-02-06 19:49:25 +00:00
if ( cast ( LiteralValue ) operandInstr )
2023-02-04 12:37:40 +00:00
{
2023-02-06 19:49:25 +00:00
LiteralValue theLiteral = cast ( LiteralValue ) operandInstr ;
// Then the actual literal will be `-<value>`
string negativeLiteral = "-" ~ theLiteral . getLiteralValue ( ) ;
gprintln ( "Negated literal: " ~ negativeLiteral ) ;
2023-02-04 12:37:40 +00:00
2023-02-06 19:49:25 +00:00
// NOTE (X-platform): For cross-platform sake we should change the `long` to `ssize_t`
long literalValue = to ! ( long ) ( negativeLiteral ) ;
if ( isSameType ( toType , getType ( null , "byte" ) ) )
{
if ( literalValue > = - 128 & & literalValue < = 127 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
}
else if ( isSameType ( toType , getType ( null , "short" ) ) )
{
if ( literalValue > = - 32_768 & & literalValue < = 32_767 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
}
else if ( isSameType ( toType , getType ( null , "int" ) ) )
{
if ( literalValue > = - 2_147_483_648 & & literalValue < = 2_147_483_647 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
}
else if ( isSameType ( toType , getType ( null , "long" ) ) )
{
if ( literalValue > = - 9_223_372_036_854_775_808 & & literalValue < = 9_223_372_036_854_775_807 )
{
// Valid coercion
return true ;
}
else
{
// Invalid coercion
return false ;
}
}
2023-02-04 12:37:40 +00:00
}
// LiteralValue (integer literal instructions) with subtraction infront
else
{
}
}
2023-02-05 10:47:27 +00:00
return false ;
2023-02-04 12:37:40 +00:00
}
/ * *
* Attempts to perform coercion of the provided Value - instruction
* with respect to the provided variable type .
*
* This should only be called if the types do not match .
* This will update the provided instruction ' s type - field
*
* Params :
* variableType = the type to attempt coercing the instruction to
* assignmentInstruction = instruction to coerce
* /
private void attemptCoercion ( Type variableType , Value assignmentInstruction )
{
2023-02-06 19:49:25 +00:00
gprintln ( "VibeCheck?" ) ;
2023-02-04 12:37:40 +00:00
/* Extract the type of the assignment instruction */
Type assignmentType = assignmentInstruction . getInstrType ( ) ;
// If it is a LiteralValue (integer literal) (support for issue #94)
if ( cast ( LiteralValue ) assignmentInstruction )
{
// TODO: Add a check for if these types are both atleast integral (as in the Variable's type)
// ... THEN (TODO): Check if range makes sense
bool isIntegral = ! ( cast ( Integer ) variableType is null ) ; // Integrality check
if ( isIntegral )
{
bool isCoercible = isCoercibleRange ( variableType , assignmentInstruction ) ; // TODO: Range check
if ( isCoercible )
{
// TODO: Coerce here by changing the embedded instruction's type (I think this makes sense)
// ... as during code emit that is what will be hoisted out and checked regarding its type
// NOTE: Referrring to same type should not be a problem (see #96 Question 1)
assignmentInstruction . setInstrType ( variableType ) ;
}
else
{
throw new TypeMismatchException ( this , variableType , assignmentType , "Not coercible (range violation)" ) ;
}
}
else
{
throw new TypeMismatchException ( this , variableType , assignmentType , "Not coercible (lacking integral var type)" ) ;
}
}
// If it is a LiteralValueFloat (support for issue #94)
else if ( cast ( LiteralValueFloat ) assignmentInstruction )
{
gprintln ( "Coercion not yet supported for floating point literals" , DebugType . ERROR ) ;
assert ( false ) ;
}
// Unary operator (specifically with a minus)
else if ( cast ( UnaryOpInstr ) assignmentInstruction )
{
UnaryOpInstr unaryOpInstr = cast ( UnaryOpInstr ) assignmentInstruction ;
if ( unaryOpInstr . getOperator ( ) = = SymbolType . SUB )
{
Value operandInstr = unaryOpInstr . getOperand ( ) ;
// If it is a negative LiteralValue (integer literal)
if ( cast ( LiteralValue ) operandInstr )
{
2023-02-06 19:49:25 +00:00
bool isIntegral = ! ( cast ( Integer ) variableType is null ) ;
if ( isIntegral )
{
LiteralValue literalValue = cast ( LiteralValue ) operandInstr ;
bool isCoercible = isCoercibleRange ( variableType , assignmentInstruction ) ; // TODO: Range check
if ( isCoercible )
{
// TODO: Coerce here by changing the embedded instruction's type (I think this makes sense)
// ... as during code emit that is what will be hoisted out and checked regarding its type
// NOTE: Referrring to same type should not be a problem (see #96 Question 1)
assignmentInstruction . setInstrType ( variableType ) ;
}
else
{
throw new TypeMismatchException ( this , variableType , assignmentType , "Not coercible (range violation)" ) ;
}
// TODO: Implement things here
// gprintln("Please implement coercing checking for negative integer literals", DebugType.ERROR);
// assert(false);
}
2023-02-04 12:37:40 +00:00
}
// If it is a negative LiteralValueFloat (floating-point literal)
else if ( cast ( LiteralValueFloat ) operandInstr )
{
gprintln ( "Coercion not yet supported for floating point literals" , DebugType . ERROR ) ;
assert ( false ) ;
}
// If anything else is embedded
else
{
throw new TypeMismatchException ( this , variableType , assignmentType , "Not coercible (lacking integral var type)" ) ;
}
}
else
{
throw new TypeMismatchException ( this , variableType , assignmentType , "Cannot coerce a non minus unary operation" ) ;
}
}
else
{
throw new TypeMismatchException ( this , variableType , assignmentType , "Not coercible (lacking integral var type)" ) ;
}
}
2023-04-17 15:50:11 +01:00
/ * *
* Given two Value - based instructions this will firstly check if
* at least one of the two is of type Pointer , then checks if the
* remaining instruction is an of type Integer - the remaining instruction
* will then be coerced into a pointer .
*
* If both are Pointers , neither are pointers or one or the other is
* a Pointer and another is non - Integer then nothing will be coerced .
* and this function is effectively a no - op .
*
* Params :
* vInstr1 = the first instruction
* vInstr2 = the second instruction
* /
private void attemptPointerAriehmeticCoercion ( Value vInstr1 , Value vInstr2 )
{
// Get the types of `vInstr1` and `vInstr2` respectively
Type t1 = vInstr1 . getInstrType ( ) ;
Type t2 = vInstr2 . getInstrType ( ) ;
// TODO: Check if T1 is a pointer and then if T2 is an integer make it a pointer
if ( cast ( Pointer ) t1 & & cast ( Integer ) t2 )
{
Pointer t1Ptr = cast ( Pointer ) t1 ;
Type coercedType = new Pointer ( t1Ptr . getReferredType ( ) ) ;
vInstr2 . setInstrType ( coercedType ) ;
}
// TODO: Else check if T2 is a pointer and then if T1 is an integer and make it a pointer
else if ( cast ( Pointer ) t2 & & cast ( Integer ) t2 )
{
Pointer t2Ptr = cast ( Pointer ) t2 ;
Type coercedType = new Pointer ( t2Ptr . getReferredType ( ) ) ;
vInstr1 . setInstrType ( coercedType ) ;
}
else if ( cast ( Pointer ) t1 & & cast ( Pointer ) t2 )
{
// Do nothing
// TODO: Remove this branch
}
else
{
// Do nothing
}
}
2021-10-25 21:31:07 +01:00
2022-02-20 10:09:10 +00:00
2021-10-25 21:31:07 +01:00
public void typeCheckThing ( DNode dnode )
{
2021-10-26 16:17:53 +01:00
gprintln ( "typeCheckThing(): " ~ dnode . toString ( ) ) ;
2021-10-25 21:31:07 +01:00
/* ExpressionDNodes */
2023-02-05 18:21:26 +00:00
if ( cast ( tlang . compiler . typecheck . dependency . expression . ExpressionDNode ) dnode )
2021-10-25 21:31:07 +01:00
{
2023-02-05 18:21:26 +00:00
tlang . compiler . typecheck . dependency . expression . ExpressionDNode expDNode = cast ( tlang . compiler . typecheck . dependency . expression . ExpressionDNode ) dnode ;
2021-10-25 21:31:07 +01:00
Statement statement = expDNode . getEntity ( ) ;
gprintln ( "Hdfsfdjfds" ~ to ! ( string ) ( statement ) ) ;
/* Dependent on the type of Statement */
if ( cast ( NumberLiteral ) statement )
{
2021-10-26 16:17:53 +01:00
/ * *
* Codegen
2022-08-11 09:26:30 +01:00
*
* TODO : We just assume ( for integers ) byte size 4 ?
2022-08-19 10:42:19 +01:00
*
* Generate the correct value instruction depending
* on the number literal ' s type
2021-10-26 16:17:53 +01:00
* /
2022-08-11 09:26:30 +01:00
Value valInstr ;
2023-01-29 12:13:04 +00:00
/* Generate a LiteralValue (IntegerLiteral) */
if ( cast ( IntegerLiteral ) statement )
2022-08-11 09:26:30 +01:00
{
2023-01-29 12:13:04 +00:00
IntegerLiteral integerLitreal = cast ( IntegerLiteral ) statement ;
2023-01-30 17:08:48 +00:00
/ * *
* Determine the type of this value instruction by finding
* the encoding of the integer literal ( part of doing issue # 94 )
* /
Type literalEncodingType ;
if ( integerLitreal . getEncoding ( ) = = IntegerLiteralEncoding . SIGNED_INTEGER )
{
literalEncodingType = getType ( modulle , "int" ) ;
}
2023-02-05 12:13:15 +00:00
else if ( integerLitreal . getEncoding ( ) = = IntegerLiteralEncoding . UNSIGNED_INTEGER )
{
literalEncodingType = getType ( modulle , "uint" ) ;
}
else if ( integerLitreal . getEncoding ( ) = = IntegerLiteralEncoding . SIGNED_LONG )
{
literalEncodingType = getType ( modulle , "long" ) ;
}
else if ( integerLitreal . getEncoding ( ) = = IntegerLiteralEncoding . UNSIGNED_LONG )
{
literalEncodingType = getType ( modulle , "ulong" ) ;
}
2023-01-30 17:08:48 +00:00
assert ( literalEncodingType ) ;
2023-01-29 12:13:04 +00:00
// TODO: Insert getEncoding stuff here
2023-01-30 17:08:48 +00:00
LiteralValue litValInstr = new LiteralValue ( integerLitreal . getNumber ( ) , literalEncodingType ) ;
2022-08-11 09:26:30 +01:00
valInstr = litValInstr ;
2023-01-29 12:13:04 +00:00
// TODO: Insert get encoding stuff here
2022-08-11 09:26:30 +01:00
}
2023-01-29 12:13:04 +00:00
/* Generate a LiteralValueFloat (FloatingLiteral) */
2022-08-11 09:26:30 +01:00
else
{
2023-01-29 12:13:04 +00:00
FloatingLiteral floatLiteral = cast ( FloatingLiteral ) statement ;
2023-01-30 17:08:48 +00:00
gprintln ( "We haven't sorted ouyt literal encoding for floating onts yet (null below hey!)" , DebugType . ERROR ) ;
Type bruhType = null ;
assert ( bruhType ) ;
LiteralValueFloat litValInstr = new LiteralValueFloat ( floatLiteral . getNumber ( ) , bruhType ) ;
2022-08-11 09:26:30 +01:00
valInstr = litValInstr ;
2023-01-29 12:13:04 +00:00
// TODO: Insert get encoding stuff here
2022-08-11 09:26:30 +01:00
}
addInstr ( valInstr ) ;
2021-10-25 21:31:07 +01:00
}
2022-07-25 18:15:27 +01:00
/* String literal */
2021-10-25 21:31:07 +01:00
else if ( cast ( StringExpression ) statement )
{
2022-07-25 18:15:27 +01:00
gprintln ( "Typecheck(): String literal processing..." ) ;
/ * *
* Add the char * type as string literals should be
* interned
* /
2023-02-01 12:45:55 +00:00
gprintln ( "Please implement strings" , DebugType . ERROR ) ;
// assert(false);
// addType(getType(modulle, "char*"));
2022-07-25 18:30:40 +01:00
2023-02-01 12:45:55 +00:00
// /**
// * Add the instruction and pass the literal to it
// */
// StringExpression strExp = cast(StringExpression)statement;
// string strLit = strExp.getStringLiteral();
// gprintln("String literal: `"~strLit~"`");
// StringLiteral strLitInstr = new StringLiteral(strLit);
// addInstr(strLitInstr);
// gprintln("Typecheck(): String literal processing... [done]");
2021-10-25 21:31:07 +01:00
}
else if ( cast ( VariableExpression ) statement )
{
2022-04-13 08:50:24 +01:00
gprintln ( "Yaa, it's rewind time" ) ;
2021-10-25 21:31:07 +01:00
auto g = cast ( VariableExpression ) statement ;
2022-10-13 12:21:35 +01:00
/* FIXME: It would seem that g.getContext() is returning null, so within function body's context is not being set */
gprintln ( "VarExp: " ~ g . getName ( ) ) ;
gprintln ( g . getContext ( ) ) ;
2021-10-25 21:31:07 +01:00
auto gVar = cast ( TypedEntity ) resolver . resolveBest ( g . getContext ( ) . getContainer ( ) , g . getName ( ) ) ;
2022-10-14 19:44:33 +01:00
gprintln ( "gVar nullity?: " ~ to ! ( string ) ( gVar is null ) ) ;
/* TODO; Above crashes when it is a container, eish baba - from dependency generation with `TestClass.P.h` */
2021-10-26 16:17:53 +01:00
string variableName = resolver . generateName ( modulle , gVar ) ;
2022-04-13 08:50:24 +01:00
gprintln ( "VarName: " ~ variableName ) ;
2022-10-14 19:44:33 +01:00
gprintln ( "Halo" ) ;
2022-04-13 08:50:24 +01:00
gprintln ( "Yaa, it's rewind time1: " ~ to ! ( string ) ( gVar . getType ( ) ) ) ;
gprintln ( "Yaa, it's rewind time2: " ~ to ! ( string ) ( gVar . getContext ( ) ) ) ;
2021-10-25 21:31:07 +01:00
/* TODO: Above TYpedEntity check */
/* TODO: still wip the expresison parser */
/* TODO: TYpe needs ansatz too `.updateName()` call */
2023-02-01 12:32:29 +00:00
Type variableType = getType ( gVar . getContext ( ) . getContainer ( ) , gVar . getType ( ) ) ;
2021-10-26 16:17:53 +01:00
2022-04-13 08:50:24 +01:00
gprintln ( "Yaa, it's rewind time" ) ;
2021-10-26 16:17:53 +01:00
/ * *
* Codegen
*
* FIXME : Add type info , length
2022-12-13 09:50:54 +00:00
*
* 1. Generate the instruction
* 2. Set the Context of it to where the VariableExpression occurred
2021-10-26 16:17:53 +01:00
* /
FetchValueVar fVV = new FetchValueVar ( variableName , 4 ) ;
2023-02-03 14:13:40 +00:00
fVV . setContext ( g . getContext ( ) ) ;
2022-12-13 09:50:54 +00:00
2021-10-26 16:17:53 +01:00
addInstr ( fVV ) ;
2023-02-01 12:32:29 +00:00
/* The type of a FetchValueInstruction is the type of the variable being fetched */
2023-02-01 12:55:23 +00:00
fVV . setInstrType ( variableType ) ;
2021-10-26 16:17:53 +01:00
}
// else if(cast()) !!!! Continue here
else if ( cast ( BinaryOperatorExpression ) statement )
{
2021-10-26 21:03:48 +01:00
BinaryOperatorExpression binOpExp = cast ( BinaryOperatorExpression ) statement ;
SymbolType binOperator = binOpExp . getOperator ( ) ;
2023-02-01 12:32:29 +00:00
2021-10-26 21:03:48 +01:00
2021-10-26 16:17:53 +01:00
/ * *
2023-02-01 12:32:29 +00:00
* Codegen / Type checking
*
* Retrieve the two Value Instructions
*
* They would be placed as if they were on stack
* hence we need to burger - flip them around ( swap )
2021-10-26 16:17:53 +01:00
* /
2023-02-01 12:32:29 +00:00
Value vRhsInstr = cast ( Value ) popInstr ( ) ;
Value vLhsInstr = cast ( Value ) popInstr ( ) ;
2023-04-17 15:50:11 +01:00
/ * *
* Attempt to coerce the types of both instructions if one is
* a pointer and another is an integer , else do nothing
* /
attemptPointerAriehmeticCoercion ( vLhsInstr , vRhsInstr ) ;
2023-02-01 12:55:23 +00:00
Type vRhsType = vRhsInstr . getInstrType ( ) ;
Type vLhsType = vLhsInstr . getInstrType ( ) ;
2022-02-20 14:37:45 +00:00
2023-04-17 15:50:11 +01:00
2022-02-20 14:37:45 +00:00
/ * *
2023-02-01 12:32:29 +00:00
* TODO
2022-02-20 14:37:45 +00:00
* Types must either BE THE SAME or BE COMPATIBLE
* /
2023-02-01 12:45:55 +00:00
Type chosenType ;
2022-02-20 14:37:45 +00:00
if ( isSameType ( vLhsType , vRhsType ) )
{
/* Left type + Right type = left/right type (just use left - it doesn't matter) */
2023-02-01 12:45:55 +00:00
chosenType = vLhsType ;
2022-02-20 14:37:45 +00:00
}
else
{
2023-02-11 10:09:09 +00:00
throw new TypeMismatchException ( this , vLhsType , vRhsType , "Binary operator expression requires both types be same" ) ;
2022-02-20 14:37:45 +00:00
}
2022-02-20 14:21:29 +00:00
2021-10-26 21:03:48 +01:00
BinOpInstr addInst = new BinOpInstr ( vLhsInstr , vRhsInstr , binOperator ) ;
2021-10-26 16:17:53 +01:00
addInstr ( addInst ) ;
2023-02-01 12:32:29 +00:00
/* Set the Value instruction's type */
2023-02-01 12:55:23 +00:00
addInst . setInstrType ( chosenType ) ;
2021-10-25 21:31:07 +01:00
}
2022-04-12 09:54:04 +01:00
/* Unary operator expressions */
else if ( cast ( UnaryOperatorExpression ) statement )
{
UnaryOperatorExpression unaryOpExp = cast ( UnaryOperatorExpression ) statement ;
SymbolType unaryOperator = unaryOpExp . getOperator ( ) ;
2023-02-01 12:32:29 +00:00
/* The type of the eventual UnaryOpInstr */
Type unaryOpType ;
2022-04-13 08:50:24 +01:00
2022-04-12 09:54:04 +01:00
/ * *
* Typechecking ( TODO )
* /
2023-02-01 12:32:29 +00:00
Value expInstr = cast ( Value ) popInstr ( ) ;
2023-02-01 12:55:23 +00:00
Type expType = expInstr . getInstrType ( ) ;
2022-04-12 09:54:04 +01:00
/* TODO: Ad type check for operator */
2022-04-13 08:50:24 +01:00
/* If the unary operation is an arithmetic one */
if ( unaryOperator = = SymbolType . ADD | | unaryOperator = = SymbolType . SUB )
{
/* TODO: I guess any type fr */
2023-02-06 19:49:25 +00:00
2023-02-11 10:03:25 +00:00
if ( unaryOperator = = SymbolType . SUB )
{
// TODO: Note below is a legitimately good question, given a type
// ... <valueType>, what does applying a `-` infront of it (`-<valueType>`)
// ... mean in terms of its type?
//
// ... Does it remain the same type? We ask because of literal encoding.
// ... I believe the best way forward would be specifically to handle
// ... cases where `cast(LiteralValue)expInstr` is true here - just
// ... as we had the special handling for it in `NumberLiteral` statements
// ... before.
if ( cast ( LiteralValue ) expInstr )
{
LiteralValue literalValue = cast ( LiteralValue ) expInstr ;
string literalValueStr = literalValue . getLiteralValue ( ) ;
ulong literalValueNumber = to ! ( ulong ) ( literalValueStr ) ; // TODO: Add a conv check for overflow
if ( literalValueNumber > = 9_223_372_036_854_775_808 )
{
// TODO: I don't think we are meant to be doing the below, atleast for coercive cases
// TODO: make this error nicer
// throw new TypeCheckerException(this, TypeCheckerException.TypecheckError.GENERAL_ERROR, "Cannot represent -"~literalValueStr~" as too big");
}
// TODO: Check case of literal being 9223372036854775808 or above
// ... and having a `-` infront of it, then disallow
// TODO: Remove the below (just for now)
unaryOpType = expType ;
}
else
{
// Else just copy the tyoe of the expInstr over
unaryOpType = expType ;
}
}
else
{
// Else just copy the tyoe of the expInstr over
unaryOpType = expType ;
}
2022-04-13 08:50:24 +01:00
}
/* If pointer dereference */
else if ( unaryOperator = = SymbolType . STAR )
{
2023-01-14 10:39:37 +00:00
gprintln ( "Type popped: " ~ to ! ( string ) ( expType ) ) ;
// Okay, so yes, we would pop `ptr`'s type as `int*` which is correct
// but now, we must a.) ensure that IS the case and b.)
// push the type of `<type>` with one star less on as we are derefrencing `ptr`
Type derefPointerType ;
if ( cast ( Pointer ) expType )
{
Pointer pointerType = cast ( Pointer ) expType ;
// Get the type being referred to
Type referredType = pointerType . getReferredType ( ) ;
2023-02-01 12:32:29 +00:00
unaryOpType = referredType ;
2023-01-14 10:39:37 +00:00
}
else
{
2023-02-11 10:13:08 +00:00
throw new TypeCheckerException ( this , TypeCheckerException . TypecheckError . GENERAL_ERROR , "You cannot dereference a type that is not a pointer type!" ) ;
2023-01-14 10:39:37 +00:00
}
2022-04-13 08:50:24 +01:00
}
/* If pointer create `&` */
else if ( unaryOperator = = SymbolType . AMPERSAND )
{
/ * *
2023-01-12 11:31:42 +00:00
* If the type popped from the stack was `<type>` then push
* a new type onto the stack which is `<type>*`
2022-04-13 08:50:24 +01:00
* /
2023-01-12 08:53:48 +00:00
Type ptrType = new Pointer ( expType ) ;
2023-02-01 12:32:29 +00:00
unaryOpType = ptrType ;
2022-04-13 08:50:24 +01:00
}
/* This should never occur */
else
{
gprintln ( "UnaryOperatorExpression: This should NEVER happen: " ~ to ! ( string ) ( unaryOperator ) , DebugType . ERROR ) ;
assert ( false ) ;
}
2022-04-12 09:54:04 +01:00
2023-02-01 12:32:29 +00:00
2023-01-12 11:31:42 +00:00
// TODO: For type checking and semantics we should be checking WHAT is being ampersanded
// ... as in we should only be allowing Ident's to be ampersanded, not, for example, literals
// ... such a check can be accomplished via runtime type information of the instruction above
2022-04-13 08:50:24 +01:00
2022-04-12 09:54:04 +01:00
UnaryOpInstr addInst = new UnaryOpInstr ( expInstr , unaryOperator ) ;
2023-01-14 10:39:37 +00:00
gprintln ( "Made unaryop instr: " ~ to ! ( string ) ( addInst ) ) ;
2022-04-12 09:54:04 +01:00
addInstr ( addInst ) ;
2023-02-01 12:32:29 +00:00
2023-02-01 12:55:23 +00:00
addInst . setInstrType ( unaryOpType ) ;
2022-04-12 09:54:04 +01:00
}
2022-02-15 13:04:16 +00:00
/* Function calls */
else if ( cast ( FunctionCall ) statement )
{
2022-02-20 14:21:29 +00:00
// gprintln("FuncCall hehe (REMOVE AFTER DONE)");
2022-02-18 12:32:45 +00:00
FunctionCall funcCall = cast ( FunctionCall ) statement ;
/* TODO: Look up func def to know when popping stops (types-based delimiting) */
Function func = cast ( Function ) resolver . resolveBest ( modulle , funcCall . getName ( ) ) ;
assert ( func ) ;
2022-12-17 12:00:16 +00:00
VariableParameter [ ] paremeters = func . getParams ( ) ;
2022-02-20 12:24:05 +00:00
/* TODO: Pass in FUnction, so we get function's body for calling too */
FuncCallInstr funcCallInstr = new FuncCallInstr ( func . getName ( ) , paremeters . length ) ;
2022-04-08 00:12:52 +01:00
gprintln ( "Name of func call: " ~ func . getName ( ) , DebugType . ERROR ) ;
2022-02-18 12:32:45 +00:00
/* If there are paremeters for this function (as per definition) */
if ( ! paremeters . length )
{
2022-04-08 00:12:52 +01:00
gprintln ( "No parameters for deez nuts: " ~ func . getName ( ) , DebugType . ERROR ) ;
2022-02-18 12:32:45 +00:00
}
/* Pop all args per type */
else
{
2022-02-20 14:05:15 +00:00
ulong parmCount = paremeters . length - 1 ;
2022-04-08 00:12:52 +01:00
gprintln ( "Kachow: " ~ to ! ( string ) ( parmCount ) , DebugType . ERROR ) ;
2022-02-20 14:05:15 +00:00
2022-02-18 12:32:45 +00:00
while ( ! isInstrEmpty ( ) )
{
Instruction instr = popInstr ( ) ;
Value valueInstr = cast ( Value ) instr ;
/* Must be a value instruction */
if ( valueInstr & & parmCount ! = - 1 )
{
/* TODO: Determine type and match up */
gprintln ( "Yeah" ) ;
gprintln ( valueInstr ) ;
2023-02-01 12:55:23 +00:00
Type argType = valueInstr . getInstrType ( ) ;
2022-02-20 13:35:34 +00:00
// gprintln(argType);
2022-02-18 12:32:45 +00:00
Variable parameter = paremeters [ parmCount ] ;
2022-02-20 13:35:34 +00:00
// gprintln(parameter);
2022-02-18 12:32:45 +00:00
Type parmType = getType ( func . parentOf ( ) , parameter . getType ( ) ) ;
2022-02-20 13:35:34 +00:00
// gprintln("FuncCall(Actual): "~argType.getName());
// gprintln("FuncCall(Formal): "~parmType.getName());
// gprintln("FuncCall(Actual): "~valueInstr.toString());
2022-02-18 12:32:45 +00:00
/* Match up types */
//if(argType == parmType)
if ( isSameType ( argType , parmType ) )
{
gprintln ( "Match type" ) ;
2022-02-20 13:35:34 +00:00
/* Add the instruction into the FunctionCallInstr */
funcCallInstr . setEvalInstr ( parmCount , valueInstr ) ;
gprintln ( funcCallInstr . getEvaluationInstructions ( ) ) ;
2022-02-18 12:32:45 +00:00
}
else
{
printCodeQueue ( ) ;
gprintln ( "Wrong actual argument type for function call" , DebugType . ERROR ) ;
gprintln ( "Cannot pass value of type '" ~ argType . getName ( ) ~ "' to function accepting '" ~ parmType . getName ( ) ~ "'" , DebugType . ERROR ) ;
2023-02-11 10:19:44 +00:00
throw new TypeMismatchException ( this , parmType , argType , "The actual argument's type does not match that of the function's parameter type" ) ;
2022-02-18 12:32:45 +00:00
}
2022-02-20 13:35:34 +00:00
parmCount - - ;
2022-02-18 12:32:45 +00:00
}
else
{
2023-02-11 10:16:11 +00:00
// TODO: This should enver happen, see book and remove soon (see Cleanup: Remove any pushbacks #101)
2022-02-18 12:32:45 +00:00
/* Push it back */
addInstr ( instr ) ;
break ;
}
}
}
2022-02-20 12:24:05 +00:00
2022-02-18 12:32:45 +00:00
2022-02-15 13:04:16 +00:00
/ * *
* TODO :
*
2022-12-16 12:53:33 +00:00
* 1. Create FuncCallInstr
2022-02-16 05:31:22 +00:00
* 2. Evaluate args and process them ? ! wait done elsewhere yeah ! ! !
2022-02-15 13:04:16 +00:00
* 3. Pop arts into here
* 4. AddInstr ( combining those args )
* 5. DOne
* /
2023-02-03 14:13:40 +00:00
funcCallInstr . setContext ( funcCall . getContext ( ) ) ;
2022-12-16 12:53:33 +00:00
2022-02-18 12:32:45 +00:00
addInstr ( funcCallInstr ) ;
2023-02-01 12:32:29 +00:00
/* Set the Value instruction's type */
Type funcCallInstrType = getType ( func . parentOf ( ) , func . getType ( ) ) ;
2023-02-01 12:55:23 +00:00
funcCallInstr . setInstrType ( funcCallInstrType ) ;
2022-02-15 13:04:16 +00:00
}
2023-01-14 16:40:08 +00:00
/* Type cast operator */
else if ( cast ( CastedExpression ) statement )
{
CastedExpression castedExpression = cast ( CastedExpression ) statement ;
gprintln ( "Context: " ~ to ! ( string ) ( castedExpression . context ) ) ;
gprintln ( "ParentOf: " ~ to ! ( string ) ( castedExpression . parentOf ( ) ) ) ;
2023-02-01 12:32:29 +00:00
/* Extract the type that the cast is casting towards */
2023-01-14 16:40:08 +00:00
Type castToType = getType ( castedExpression . context . container , castedExpression . getToType ( ) ) ;
/ * *
* Codegen
*
* 1. Pop off the current value instruction corresponding to the embedding
* 2. Create a new CastedValueInstruction instruction
* 3. Set the context
* 4. Add to front of code queue
* /
Value uncastedInstruction = cast ( Value ) popInstr ( ) ;
assert ( uncastedInstruction ) ;
2023-02-01 12:32:29 +00:00
/* Extract the type of the expression being casted */
2023-02-01 13:03:29 +00:00
Type typeBeingCasted = uncastedInstruction . getInstrType ( ) ;
2023-02-01 12:32:29 +00:00
gprintln ( "TypeCast [FromType: " ~ to ! ( string ) ( typeBeingCasted ) ~ ", ToType: " ~ to ! ( string ) ( castToType ) ~ "]" ) ;
2023-01-14 16:40:08 +00:00
printCodeQueue ( ) ;
2023-02-01 12:32:29 +00:00
// TODO: Remove the `castToType` argument, this should be solely based off of the `.type` (as set below)
2023-01-14 16:40:08 +00:00
CastedValueInstruction castedValueInstruction = new CastedValueInstruction ( uncastedInstruction , castToType ) ;
2023-02-03 14:13:40 +00:00
castedValueInstruction . setContext ( castedExpression . context ) ;
2023-01-14 16:40:08 +00:00
addInstr ( castedValueInstruction ) ;
2023-02-01 12:32:29 +00:00
/* The type of the cats expression is that of the type it casts to */
2023-02-01 12:55:23 +00:00
castedValueInstruction . setInstrType ( castToType ) ;
2023-01-14 16:40:08 +00:00
}
2021-10-25 21:31:07 +01:00
}
2021-10-26 19:32:47 +01:00
/* VariableAssigbmentDNode */
2023-02-05 18:21:26 +00:00
else if ( cast ( tlang . compiler . typecheck . dependency . variables . VariableAssignmentNode ) dnode )
2021-10-26 19:32:47 +01:00
{
2023-02-05 18:21:26 +00:00
import tlang.compiler.typecheck.dependency.variables ;
2022-12-12 11:12:03 +00:00
2021-10-26 19:32:47 +01:00
/* Get the variable's name */
string variableName ;
2023-02-05 18:21:26 +00:00
VariableAssignmentNode varAssignDNode = cast ( tlang . compiler . typecheck . dependency . variables . VariableAssignmentNode ) dnode ;
2021-10-26 19:32:47 +01:00
Variable assignTo = ( cast ( VariableAssignment ) varAssignDNode . getEntity ( ) ) . getVariable ( ) ;
variableName = resolver . generateName ( modulle , assignTo ) ;
2022-10-14 17:01:35 +01:00
gprintln ( "VariableAssignmentNode: " ~ to ! ( string ) ( variableName ) ) ;
2021-10-26 19:32:47 +01:00
2022-12-12 11:12:03 +00:00
/* Get the Context of the Variable Assigmnent */
Context variableAssignmentContext = ( cast ( VariableAssignment ) varAssignDNode . getEntity ( ) ) . context ;
2022-10-15 19:24:23 +01:00
/ * *
* FIXME : Now with ClassStaticAllocate we will have wrong instructoins for us
* ontop of the stack ( at the beginning of the queue ) , I think this leads us
* to potentially opping wrong thing off - we should filter pop perhaps
* /
2021-10-26 19:32:47 +01:00
/ * *
* Codegen
*
* 1. Get the variable ' s name
* 2. Pop Value - instruction
* 3. Generate VarAssignInstruction with Value - instruction
2022-12-12 11:12:03 +00:00
* 4. Set the VarAssignInstr ' s Context to that of the Variable assigning to
2021-10-26 19:32:47 +01:00
* /
2023-01-30 17:08:48 +00:00
Instruction instr = popInstr ( ) ;
assert ( instr ) ;
Value valueInstr = cast ( Value ) instr ;
assert ( valueInstr ) ;
2022-10-15 19:24:23 +01:00
gprintln ( "VaribleAssignmentNode(): Just popped off valInstr?: " ~ to ! ( string ) ( valueInstr ) , DebugType . WARNING ) ;
2023-01-30 17:08:48 +00:00
2023-02-01 13:03:29 +00:00
Type rightHandType = valueInstr . getInstrType ( ) ;
2023-01-30 17:08:48 +00:00
gprintln ( "RightHandType (assignment): " ~ to ! ( string ) ( rightHandType ) ) ;
2023-02-01 12:32:29 +00:00
2023-01-30 17:08:48 +00:00
2022-02-15 13:04:16 +00:00
gprintln ( valueInstr is null ) ; /*TODO: FUnc calls not implemented? Then is null for simple_1.t */
2021-10-26 19:32:47 +01:00
VariableAssignmentInstr varAssInstr = new VariableAssignmentInstr ( variableName , valueInstr ) ;
2023-02-03 14:13:40 +00:00
varAssInstr . setContext ( variableAssignmentContext ) ;
2023-02-01 12:32:29 +00:00
// NOTE: No need setting `varAssInstr.type` as the type if in `getEmbeddedInstruction().type`
2022-12-12 11:12:03 +00:00
2022-12-19 13:37:55 +00:00
addInstr ( varAssInstr ) ;
2021-10-27 20:12:00 +01:00
}
2021-11-10 14:42:15 +00:00
/* TODO: Add support */
/ * *
* TODO : We need to emit different code dependeing on variable declaration TYPE
* We could use context for this , ClassVariableDec vs ModuleVariableDec
* /
2023-02-05 18:21:26 +00:00
else if ( cast ( tlang . compiler . typecheck . dependency . variables . StaticVariableDeclaration ) dnode )
2021-10-27 20:12:00 +01:00
{
2021-11-10 15:15:27 +00:00
/* TODO: Add skipping if context is within a class */
/* We need to wait for class static node, to do an InitInstruction (static init) */
/* It probably makes sense , IDK, we need to allocate both classes */
2021-10-27 20:12:00 +01:00
/ * *
* Codegen
*
* Emit a variable declaration instruction
* /
Variable variablePNode = cast ( Variable ) dnode . getEntity ( ) ;
2022-10-14 17:01:35 +01:00
gprintln ( "HELLO FELLA" ) ;
2021-10-27 20:12:00 +01:00
string variableName = resolver . generateName ( modulle , variablePNode ) ;
2022-12-19 13:37:55 +00:00
gprintln ( "HELLO FELLA (name): " ~ variableName ) ;
2023-01-30 17:08:48 +00:00
Type variableDeclarationType = getType ( variablePNode . context . container , variablePNode . getType ( ) ) ;
2022-12-19 13:37:55 +00:00
2023-02-04 10:41:30 +00:00
// Check if this variable declaration has an assignment attached
Value assignmentInstr ;
2022-12-19 13:37:55 +00:00
if ( variablePNode . getAssignment ( ) )
{
Instruction poppedInstr = popInstr ( ) ;
2023-02-01 12:32:29 +00:00
assert ( poppedInstr ) ;
2023-01-30 17:08:48 +00:00
2023-02-04 10:41:30 +00:00
// Obtain the value instruction of the variable assignment
// ... along with the assignment's type
assignmentInstr = cast ( Value ) poppedInstr ;
assert ( assignmentInstr ) ;
Type assignmentType = assignmentInstr . getInstrType ( ) ;
2023-02-01 12:32:29 +00:00
2023-01-30 17:08:48 +00:00
// TODO: We should add a typecheck here where we update the type of the valInstr if it is of
// ... type NumberLiteral and coerce it to the variable referred to by the VariableAssignment
// ... see issue #94 part on "Coercion"
// If the types match then everything is fine
if ( isSameType ( variableDeclarationType , assignmentType ) )
{
gprintln ( "Variable's declared type ('" ~ to ! ( string ) ( variableDeclarationType ) ~ "') matches that of assignment expression's type ('" ~ to ! ( string ) ( assignmentType ) ~ "')" ) ;
}
// If the types do not match
else
{
2023-02-04 12:37:40 +00:00
// Then attempt coercion
attemptCoercion ( variableDeclarationType , assignmentInstr ) ;
2023-01-30 17:08:48 +00:00
}
2022-12-19 13:37:55 +00:00
}
2023-02-04 12:37:40 +00:00
/* Generate a variable declaration instruction and add it to the codequeue */
2023-01-15 10:36:54 +00:00
VariableDeclaration varDecInstr = new VariableDeclaration ( variableName , 4 , variableDeclarationType , assignmentInstr ) ;
2023-02-03 14:13:40 +00:00
varDecInstr . setContext ( variablePNode . context ) ;
2022-10-15 16:15:03 +01:00
addInstrB ( varDecInstr ) ;
2021-10-26 19:32:47 +01:00
}
2022-10-13 13:34:47 +01:00
/* TODO: Add class init, see #8 */
2023-02-05 18:21:26 +00:00
else if ( cast ( tlang . compiler . typecheck . dependency . classes . classStaticDep . ClassStaticNode ) dnode )
2021-11-10 15:15:27 +00:00
{
2022-10-15 19:25:38 +01:00
/* Extract the class node and create a static allocation instruction out of it */
Clazz clazzPNode = cast ( Clazz ) dnode . getEntity ( ) ;
string clazzName = resolver . generateName ( modulle , clazzPNode ) ;
ClassStaticInitAllocate clazzStaticInitAllocInstr = new ClassStaticInitAllocate ( clazzName ) ;
2021-11-10 15:15:27 +00:00
2022-10-15 19:36:18 +01:00
/* Add this static initialization to the list of global allocations required */
addInit ( clazzStaticInitAllocInstr ) ;
2021-11-10 15:15:27 +00:00
}
2021-11-10 15:00:56 +00:00
/* It will pop a bunch of shiiit */
2021-10-27 19:57:30 +01:00
/* TODO: ANy statement */
2023-02-05 18:21:26 +00:00
else if ( cast ( tlang . compiler . typecheck . dependency . core . DNode ) dnode )
2021-10-27 19:57:30 +01:00
{
/* TODO: Get the STatement */
Statement statement = dnode . getEntity ( ) ;
2022-12-14 17:49:08 +00:00
gprintln ( "Generic DNode typecheck(): Begin (examine: " ~ to ! ( string ) ( dnode ) ~ " )" ) ;
2021-10-27 19:57:30 +01:00
/* VariableAssignmentStdAlone */
if ( cast ( VariableAssignmentStdAlone ) statement )
{
VariableAssignmentStdAlone vasa = cast ( VariableAssignmentStdAlone ) statement ;
string variableName = vasa . getVariableName ( ) ;
2023-02-04 12:37:40 +00:00
/* Extract information about the variable declaration of the avriable being assigned to */
Context variableContext = vasa . getContext ( ) ;
Variable variable = cast ( Variable ) resolver . resolveBest ( variableContext . container , variableName ) ;
Type variableDeclarationType = getType ( variableContext . container , variable . getType ( ) ) ;
2021-10-27 19:57:30 +01:00
/ * *
* Codegen
*
* 1. Get the variable ' s name
* 2. Pop Value - instruction
* 3. Generate VarAssignInstruction with Value - instruction
* /
2023-01-30 17:08:48 +00:00
Instruction instr = popInstr ( ) ;
assert ( instr ) ;
2023-02-04 12:37:40 +00:00
Value assignmentInstr = cast ( Value ) instr ;
assert ( assignmentInstr ) ;
2023-01-30 17:08:48 +00:00
2023-02-04 12:37:40 +00:00
Type assignmentType = assignmentInstr . getInstrType ( ) ;
2023-01-30 17:08:48 +00:00
assert ( assignmentType ) ;
2023-02-04 12:37:40 +00:00
if ( isSameType ( variableDeclarationType , assignmentType ) )
{
gprintln ( "Variable's declared type ('" ~ to ! ( string ) ( variableDeclarationType ) ~ "') matches that of assignment expression's type ('" ~ to ! ( string ) ( assignmentType ) ~ "')" ) ;
}
// If the type's do not match
else
{
// Then attempt coercion
attemptCoercion ( variableDeclarationType , assignmentInstr ) ;
}
2022-12-12 19:10:58 +00:00
2023-02-04 12:37:40 +00:00
/* Generate a variable assignment instruction and add it to the codequeue */
VariableAssignmentInstr vAInstr = new VariableAssignmentInstr ( variableName , assignmentInstr ) ;
2023-02-03 14:13:40 +00:00
vAInstr . setContext ( vasa . getContext ( ) ) ;
2021-10-27 19:57:30 +01:00
addInstrB ( vAInstr ) ;
}
2022-12-17 17:02:14 +00:00
/ * *
* Return statement ( ReturnStmt )
* /
else if ( cast ( ReturnStmt ) statement )
{
ReturnStmt returnStatement = cast ( ReturnStmt ) statement ;
/ * *
* Codegen
*
* 1. Pop the expression on the stack
* 2. Create a new ReturnInstruction with the expression instruction
* embedded in it
* 3. Set the Context of the instruction
* 4. Add this instruction back
* /
Value returnExpressionInstr = cast ( Value ) popInstr ( ) ;
assert ( returnExpressionInstr ) ;
ReturnInstruction returnInstr = new ReturnInstruction ( returnExpressionInstr ) ;
2023-02-03 14:13:40 +00:00
returnInstr . setContext ( returnStatement . getContext ( ) ) ;
2022-12-17 17:02:14 +00:00
addInstrB ( returnInstr ) ;
}
2022-12-19 13:37:55 +00:00
/ * *
* If statement ( IfStatement )
* /
else if ( cast ( IfStatement ) statement )
{
IfStatement ifStatement = cast ( IfStatement ) statement ;
BranchInstruction [ ] branchInstructions ;
/* Get the if statement's branches */
Branch [ ] branches = ifStatement . getBranches ( ) ;
assert ( branches . length > 0 ) ;
/ * *
* 1. These would be added stack wise , so we need to pop them like backwards
* 2. Then a reversal at the end ( generated instructions list )
*
* FIXME : EIther used siggned or the hack below lmao , out of boounds
* /
for ( ulong branchIdx = branches . length - 1 ; true ; branchIdx - - )
{
Branch branch = branches [ branchIdx ] ;
// Pop off an expression instruction (if it exists)
Value branchConditionInstr ;
if ( branch . hasCondition ( ) )
{
Instruction instr = popInstr ( ) ;
gprintln ( "BranchIdx: " ~ to ! ( string ) ( branchIdx ) ) ;
gprintln ( "Instr is: " ~ to ! ( string ) ( instr ) ) ;
branchConditionInstr = cast ( Value ) instr ;
assert ( branchConditionInstr ) ;
}
// Get the number of body instructions to pop
ulong bodyCount = branch . getBody ( ) . length ;
ulong i = 0 ;
Instruction [ ] bodyInstructions ;
while ( i < bodyCount )
{
Instruction bodyInstr = tailPopInstr ( ) ;
bodyInstructions ~ = bodyInstr ;
gprintln ( "tailPopp'd(" ~ to ! ( string ) ( i ) ~ "/" ~ to ! ( string ) ( bodyCount - 1 ) ~ "): " ~ to ! ( string ) ( bodyInstr ) ) ;
i + + ;
}
// Reverse the body instructions (correct ordering)
bodyInstructions = reverse ( bodyInstructions ) ;
// Create the branch instruction (coupling the condition instruction and body instructions)
branchInstructions ~ = new BranchInstruction ( branchConditionInstr , bodyInstructions ) ;
if ( branchIdx = = 0 )
{
break ;
}
}
2023-01-04 10:03:50 +00:00
// Reverse the list to be in the correct order (it was computed backwards)
2022-12-19 13:37:55 +00:00
branchInstructions = reverse ( branchInstructions ) ;
/ * *
* Code gen
*
* 1. Create the IfStatementInstruction containing the BranchInstruction [ ] ( s )
* 2. Set the context
* 3. Add the instruction
* /
IfStatementInstruction ifStatementInstruction = new IfStatementInstruction ( branchInstructions ) ;
2023-02-03 14:13:40 +00:00
ifStatementInstruction . setContext ( ifStatement . getContext ( ) ) ;
2022-12-19 13:37:55 +00:00
addInstrB ( ifStatementInstruction ) ;
gprintln ( "If!" ) ;
}
2023-01-04 10:03:50 +00:00
/ * *
* While loop ( WhileLoop )
* /
else if ( cast ( WhileLoop ) statement )
{
WhileLoop whileLoop = cast ( WhileLoop ) statement ;
2023-01-11 08:43:29 +00:00
// FIXME: Do-while loops are still being considered in terms of dependency construction
if ( whileLoop . isDoWhile )
{
gprintln ( "Still looking at dependency construction in this thing (do while loops )" ) ;
assert ( false ) ;
}
2023-01-04 10:03:50 +00:00
Branch branch = whileLoop . getBranch ( ) ;
/* The condition `Value` instruction should be on the stack */
Value valueInstrCondition = cast ( Value ) popInstr ( ) ;
assert ( valueInstrCondition ) ;
/* Process the body of the while-loop with tail-popping followed by a reverse */
Instruction [ ] bodyInstructions ;
ulong bodyLen = branch . getBody ( ) . length ;
ulong bodyIdx = 0 ;
while ( bodyIdx < bodyLen )
{
Instruction bodyInstr = tailPopInstr ( ) ;
bodyInstructions ~ = bodyInstr ;
bodyIdx + + ;
}
// Reverse the list to be in the correct order (it was computed backwards)
bodyInstructions = reverse ( bodyInstructions ) ;
// Create a branch instruction coupling the condition instruction + body instructions (in corrected order)
BranchInstruction branchInstr = new BranchInstruction ( valueInstrCondition , bodyInstructions ) ;
/ * *
* Code gen
*
* 1. Create the WhileLoopInstruction containing the BranchInstruction
* 2. Set the context
* 3. Add the instruction
* /
WhileLoopInstruction whileLoopInstruction = new WhileLoopInstruction ( branchInstr ) ;
2023-02-03 14:13:40 +00:00
whileLoopInstruction . setContext ( whileLoop . getContext ( ) ) ;
2023-01-04 10:03:50 +00:00
addInstrB ( whileLoopInstruction ) ;
}
2023-01-11 08:43:29 +00:00
/ * *
* For loop ( ForLoop )
* /
else if ( cast ( ForLoop ) statement )
{
ForLoop forLoop = cast ( ForLoop ) statement ;
/* Pop-off the Value-instruction for the condition */
Value valueInstrCondition = cast ( Value ) popInstr ( ) ;
assert ( valueInstrCondition ) ;
/* Calculate the number of instructions representing the body to tailPopInstr() */
ulong bodyTailPopNumber = forLoop . getBranch ( ) . getStatements ( ) . length ;
gprintln ( "bodyTailPopNumber: " ~ to ! ( string ) ( bodyTailPopNumber ) ) ;
/* Pop off the body instructions, then reverse final list */
Instruction [ ] bodyInstructions ;
for ( ulong idx = 0 ; idx < bodyTailPopNumber ; idx + + )
{
bodyInstructions ~ = tailPopInstr ( ) ;
}
bodyInstructions = reverse ( bodyInstructions ) ;
// Create a branch instruction coupling the condition instruction + body instructions (in corrected order)
BranchInstruction branchInstr = new BranchInstruction ( valueInstrCondition , bodyInstructions ) ;
/* If there is a pre-run instruction */
Instruction preRunInstruction ;
if ( forLoop . hasPreRunStatement ( ) )
{
preRunInstruction = tailPopInstr ( ) ;
}
/ * *
* Code gen
*
* 1. Create the ForLoopInstruction containing the BranchInstruction and
* preRunInstruction
* 2. Set the context
* 3. Add the instruction
* /
ForLoopInstruction forLoopInstruction = new ForLoopInstruction ( branchInstr , preRunInstruction ) ;
2023-02-03 14:13:40 +00:00
forLoopInstruction . setContext ( forLoop . context ) ;
2023-01-11 08:43:29 +00:00
addInstrB ( forLoopInstruction ) ;
}
2022-12-19 13:37:55 +00:00
/* Branch */
else if ( cast ( Branch ) statement )
{
Branch branch = cast ( Branch ) statement ;
gprintln ( "Look at that y'all, cause this is it: " ~ to ! ( string ) ( branch ) ) ;
}
2023-01-12 08:53:48 +00:00
/ * *
* Dereferencing pointer assignment statement ( PointerDereferenceAssignment )
* /
else if ( cast ( PointerDereferenceAssignment ) statement )
{
PointerDereferenceAssignment ptrDerefAss = cast ( PointerDereferenceAssignment ) statement ;
/* Pop off the pointer dereference expression instruction (LHS) */
Value lhsPtrExprInstr = cast ( Value ) popInstr ( ) ;
assert ( lhsPtrExprInstr ) ;
/* Pop off the assignment instruction (RHS expression) */
Value rhsExprInstr = cast ( Value ) popInstr ( ) ;
assert ( rhsExprInstr ) ;
/ * *
* Code gen
*
* 1. Create the PointerDereferenceAssignmentInstruction containing the `lhsPtrExprInstr`
2023-01-12 11:31:42 +00:00
* and `rhsExprInstr` . Also set the pointer depth .
2023-01-12 08:53:48 +00:00
* 2. Set the context
* 3. Add the instruction
* /
PointerDereferenceAssignmentInstruction pointerDereferenceAssignmentInstruction = new PointerDereferenceAssignmentInstruction ( lhsPtrExprInstr , rhsExprInstr , ptrDerefAss . getDerefCount ( ) ) ;
2023-02-03 14:13:40 +00:00
pointerDereferenceAssignmentInstruction . setContext ( ptrDerefAss . context ) ;
2023-01-12 08:53:48 +00:00
addInstrB ( pointerDereferenceAssignmentInstruction ) ;
}
2023-01-13 08:49:47 +00:00
/ * *
* Discard statement ( DiscardStatement )
* /
else if ( cast ( DiscardStatement ) statement )
{
DiscardStatement discardStatement = cast ( DiscardStatement ) statement ;
/* Pop off a Value instruction */
Value exprInstr = cast ( Value ) popInstr ( ) ;
assert ( exprInstr ) ;
/ * *
* Code gen
*
* 1. Create the DiscardInstruction containing the Value instruction
* `exprInstr`
* 2. Set the context
* 3. Add the instruction
* /
DiscardInstruction discardInstruction = new DiscardInstruction ( exprInstr ) ;
2023-02-03 14:13:40 +00:00
discardInstruction . setContext ( discardStatement . context ) ;
2023-01-13 08:49:47 +00:00
addInstrB ( discardInstruction ) ;
}
2022-09-18 20:52:11 +01:00
/* Case of no matches */
else
{
gprintln ( "NO MATCHES FIX ME FOR: " ~ to ! ( string ) ( statement ) , DebugType . WARNING ) ;
}
2021-10-27 19:57:30 +01:00
}
2021-10-25 21:31:07 +01:00
}
2022-10-15 21:03:50 +01:00
/ * *
* Perform type - checking and code - generation
* on the provided linearized dependency tree
* /
2021-10-25 21:31:07 +01:00
private void doTypeCheck ( DNode [ ] actionList )
{
2022-11-26 14:26:51 +00:00
/* Print the action list provided to us */
2021-10-25 15:55:43 +01:00
gprintln ( "Action list: " ~ to ! ( string ) ( actionList ) ) ;
2022-11-26 14:26:51 +00:00
/ * *
2022-11-26 14:28:27 +00:00
* Loop through each dependency - node in the action list
2022-11-26 14:26:51 +00:00
* and perform the type - checking / code generation
* /
2021-10-25 15:55:43 +01:00
foreach ( DNode node ; actionList )
{
gprintln ( "Process: " ~ to ! ( string ) ( node ) ) ;
2021-10-25 16:03:36 +01:00
2021-10-26 16:17:53 +01:00
/* Print the code queue each time */
2022-09-18 20:52:11 +01:00
gprintln ( "sdfhjkhdsfjhfdsj 1" ) ;
2021-10-26 16:17:53 +01:00
printCodeQueue ( ) ;
2022-09-18 20:52:11 +01:00
gprintln ( "sdfhjkhdsfjhfdsj 2" ) ;
2021-10-26 16:17:53 +01:00
2022-11-26 14:26:51 +00:00
/* Type-check/code-gen this node */
2021-10-27 20:12:00 +01:00
typeCheckThing ( node ) ;
2022-11-26 14:31:33 +00:00
writeln ( "--------------" ) ;
2021-10-25 15:55:43 +01:00
}
2021-10-26 19:32:47 +01:00
2022-11-26 14:31:33 +00:00
writeln ( "\n################# Results from type-checking/code-generation #################\n" ) ;
2022-10-15 20:57:03 +01:00
/* Print the init queue */
gprintln ( "<<<<< FINAL ALLOCATE QUEUE >>>>>" ) ;
printInitQueue ( ) ;
/* Print the code queue */
2021-10-26 19:32:47 +01:00
gprintln ( "<<<<< FINAL CODE QUEUE >>>>>" ) ;
2022-10-15 20:57:03 +01:00
printCodeQueue ( ) ;
2021-04-07 12:11:36 +01:00
}
2021-04-23 13:07:19 +01:00
/ * *
* Given a type as a string this
* returns the actual type
*
* If not found then null is returned
* /
public Type getType ( Container c , string typeString )
2021-04-15 16:58:27 +01:00
{
2021-04-23 13:07:19 +01:00
Type foundType ;
2021-04-15 16:58:27 +01:00
2021-04-23 13:07:19 +01:00
/* Check if the type is built-in */
2022-07-25 18:15:27 +01:00
foundType = getBuiltInType ( this , typeString ) ;
2021-04-26 09:01:53 +01:00
/* If it isn't then check for a type (resolve it) */
if ( ! foundType )
2021-04-23 13:07:19 +01:00
{
foundType = cast ( Type ) resolver . resolveBest ( c , typeString ) ;
}
2021-04-26 09:01:53 +01:00
2021-04-23 13:07:19 +01:00
return foundType ;
2021-04-15 16:58:27 +01:00
}
2023-01-12 21:34:24 +00:00
// TODO: What actually is the point of this? It literally generates a `Class[]`
// ... and then tosses it after returning. (See issue "Dead code tracking" #83)
2021-04-15 16:58:27 +01:00
private void checkDefinitionTypes ( Container c )
{
2021-04-23 13:14:09 +01:00
/* Check variables and functions (TypedEntities) declarations */
2021-11-02 07:32:52 +00:00
// checkTypedEntitiesTypeNames(c);
2021-04-15 16:58:27 +01:00
2021-04-15 16:26:14 +01:00
/* Check class inheritance types */
Clazz [ ] classes ;
foreach ( Statement statement ; c . getStatements ( ) )
{
if ( statement ! is null & & cast ( Clazz ) statement )
{
classes ~ = cast ( Clazz ) statement ;
}
}
}
2021-04-29 16:58:45 +01:00
/ * *
* Begins the type checking process
* /
2021-04-01 14:39:06 +01:00
public void beginCheck ( )
2021-03-29 20:06:30 +01:00
{
2023-01-15 18:48:40 +00:00
/* Process all pseudo entities of the given module */
processPseudoEntities ( modulle ) ;
2021-04-01 14:32:05 +01:00
/ * *
* Make sure there are no name collisions anywhere
* in the Module with an order of precedence of
* Classes being declared before Functions and
* Functions before Variables
* /
2021-04-29 16:54:26 +01:00
checkContainerCollision ( modulle ) ; /* TODO: Rename checkContainerCollision */
2021-04-01 14:21:10 +01:00
2021-04-01 13:09:30 +01:00
/* TODO: Now that everything is defined, no collision */
/* TODO: Do actual type checking and declarations */
2021-04-09 14:56:15 +01:00
dependencyCheck ( ) ;
2021-03-29 20:06:30 +01:00
}
2023-01-15 18:48:40 +00:00
private void processPseudoEntities ( Container c )
{
/* Collect all `extern` declarations */
ExternStmt [ ] externDeclarations ;
foreach ( Statement curStatement ; c . getStatements ( ) )
{
if ( cast ( ExternStmt ) curStatement )
{
externDeclarations ~ = cast ( ExternStmt ) curStatement ;
}
}
// TODO: We could remove them from the container too, means less loops in dependency/core.d
/* Add each Entity to the container */
foreach ( ExternStmt curExternStmt ; externDeclarations )
{
SymbolType externType = curExternStmt . getExternType ( ) ;
string externalSymbolName = curExternStmt . getExternalName ( ) ;
Entity pseudoEntity = curExternStmt . getPseudoEntity ( ) ;
/* Set the embedded pseudo entity's parent to that of the container */
pseudoEntity . parentTo ( c ) ;
c . addStatements ( [ pseudoEntity ] ) ;
assert ( this . getResolver ( ) . resolveBest ( c , externalSymbolName ) ) ;
}
}
2021-03-30 20:07:04 +01:00
private void checkClassInherit ( Container c )
2021-03-30 19:05:16 +01:00
{
2021-03-30 20:07:04 +01:00
/* Get all types (Clazz so far) */
Clazz [ ] classTypes ;
2021-04-01 19:26:17 +01:00
foreach ( Statement statement ; c . getStatements ( ) )
2021-03-30 20:07:04 +01:00
{
2021-04-01 19:26:17 +01:00
if ( statement ! is null & & cast ( Clazz ) statement )
2021-03-30 20:07:04 +01:00
{
2021-04-01 19:26:17 +01:00
classTypes ~ = cast ( Clazz ) statement ;
2021-03-30 20:07:04 +01:00
}
}
/* Process each Clazz */
2021-04-01 19:26:17 +01:00
foreach ( Clazz clazz ; classTypes )
2021-03-30 20:07:04 +01:00
{
2021-04-01 19:26:17 +01:00
/* Get the current class's parent */
2021-03-30 20:07:04 +01:00
string [ ] parentClasses = clazz . getInherit ( ) ;
2021-04-01 19:26:17 +01:00
gprintln ( "Class: " ~ clazz . getName ( ) ~ ": ParentInheritList: " ~ to ! (
string ) ( parentClasses ) ) ;
2021-03-30 20:07:04 +01:00
/* Try resolve all of these */
2021-04-01 19:26:17 +01:00
foreach ( string parent ; parentClasses )
2021-03-30 20:07:04 +01:00
{
/* Find the named entity */
2021-03-30 21:04:11 +01:00
Entity namedEntity ;
/* Check if the name is rooted */
string [ ] dotPath = split ( parent , '.' ) ;
gprintln ( dotPath . length ) ;
2021-04-01 07:47:44 +01:00
/* Resolve the name */
namedEntity = resolver . resolveBest ( c , parent ) ;
2021-03-30 20:07:04 +01:00
/* If the entity exists */
2021-04-01 19:26:17 +01:00
if ( namedEntity )
2021-03-30 20:07:04 +01:00
{
/* Check if it is a Class, if so non-null */
2021-04-01 19:26:17 +01:00
Clazz parentEntity = cast ( Clazz ) namedEntity ;
2021-03-30 20:07:04 +01:00
/* Only inherit from class or (TODO: interfaces) */
2021-04-01 19:26:17 +01:00
if ( parentEntity )
2021-03-30 20:07:04 +01:00
{
/* Make sure it is not myself */
2021-04-01 19:26:17 +01:00
if ( parentEntity ! = clazz )
2021-03-30 20:07:04 +01:00
{
/* TODO: Add loop checking here */
}
else
{
Parser . expect ( "Cannot inherit from self" ) ;
}
}
/* Error */
2021-04-01 19:26:17 +01:00
else
2021-03-30 20:07:04 +01:00
{
Parser . expect ( "Can only inherit from classes" ) ;
}
}
/* If the entity doesn't exist then it is an error */
else
{
2021-04-01 19:26:17 +01:00
Parser . expect ( "Could not find any entity named " ~ parent ) ;
2021-03-30 20:07:04 +01:00
}
}
}
2021-03-30 19:05:16 +01:00
2021-03-30 20:07:04 +01:00
/* Once processing is done, apply recursively */
2021-04-01 19:26:17 +01:00
foreach ( Clazz clazz ; classTypes )
2021-03-30 19:05:16 +01:00
{
2021-03-30 20:07:04 +01:00
checkClassInherit ( clazz ) ;
2021-03-30 19:05:16 +01:00
}
2021-03-30 20:07:04 +01:00
2021-04-01 19:26:17 +01:00
}
2021-03-30 19:05:16 +01:00
private void checkClasses ( Container c )
{
/ * *
* Make sure no duplicate types ( classes ) defined
2021-03-30 20:07:04 +01:00
* within same Container
2021-03-30 19:05:16 +01:00
* /
checkClassNames ( c ) ;
/ * *
* Now that everything is neat and tidy
2021-03-30 20:07:04 +01:00
* let ' s check class properties like inheritance
* names
2021-03-30 19:05:16 +01:00
* /
2021-03-30 20:07:04 +01:00
checkClassInherit ( c ) ;
2021-03-30 19:05:16 +01:00
}
2021-04-01 13:43:49 +01:00
public Resolver getResolver ( )
{
return resolver ;
}
2021-03-30 19:05:16 +01:00
2021-04-01 14:21:10 +01:00
/ * *
2021-04-01 14:30:02 +01:00
* Given a Container `c` this will check all
* members of said Container and make sure
* none of them have a name that conflicts
* with any other member in said Container
* nor uses the same name AS the Container
* itself .
*
* Errors are printed when a member has a name
* of a previously defined member
*
* Errors are printed if the memeber shares a
* name with the container
*
* If the above 2 are false then a last check
* happens to check if the current Entity
* that just passed these checks is itself a
* Container , if not , then we do nothing and
* go onto processing the next Entity that is
* a member of Container `c` ( we stay at the
* same level ) , HOWEVER if so , we then recursively
* call `checkContainer` on said Entity and the
* logic above applies again
2021-04-01 14:21:10 +01:00
* /
2021-04-29 16:54:26 +01:00
private void checkContainerCollision ( Container c )
2021-04-01 14:21:10 +01:00
{
2021-04-21 20:14:11 +01:00
/ * *
* TODO : Always make sure this holds
*
* All objects that implement Container so far
* are also Entities ( hence they have a name )
* /
Entity containerEntity = cast ( Entity ) c ;
assert ( containerEntity ) ;
2021-04-01 14:21:10 +01:00
/ * *
* Get all Entities of the Container with order Clazz , Function , Variable
* /
Entity [ ] entities = getContainerMembers ( c ) ;
2021-04-01 19:26:17 +01:00
gprintln ( "checkContainer(C): " ~ to ! ( string ) ( entities ) ) ;
2021-04-01 14:21:10 +01:00
2021-04-01 19:26:17 +01:00
foreach ( Entity entity ; entities )
2021-04-01 14:21:10 +01:00
{
2021-04-01 19:52:36 +01:00
/ * *
* Absolute root Container ( in other words , the Module )
* can not be used
* /
if ( cmp ( modulle . getName ( ) , entity . getName ( ) ) = = 0 )
{
throw new CollidingNameException ( this , modulle , entity , c ) ;
}
2021-04-01 14:21:10 +01:00
/ * *
* If the current entity ' s name matches the container then error
* /
2021-04-21 20:14:11 +01:00
else if ( cmp ( containerEntity . getName ( ) , entity . getName ( ) ) = = 0 )
2021-04-01 14:21:10 +01:00
{
2021-04-21 20:14:11 +01:00
throw new CollidingNameException ( this , containerEntity , entity , c ) ;
2021-04-01 14:21:10 +01:00
}
/ * *
* If there are conflicting names within the current container
* ( this takes precedence into account based on how `entities`
* is generated )
* /
2021-04-01 19:26:17 +01:00
else if ( findPrecedence ( c , entity . getName ( ) ) ! = entity )
2021-04-01 14:21:10 +01:00
{
2021-04-01 19:26:17 +01:00
throw new CollidingNameException ( this , findPrecedence ( c ,
entity . getName ( ) ) , entity , c ) ;
2021-04-01 14:21:10 +01:00
}
2021-04-01 14:26:17 +01:00
/ * *
* Otherwise this Entity is fine
* /
else
{
string fullPath = resolver . generateName ( modulle , entity ) ;
2021-04-21 20:14:11 +01:00
string containerNameFullPath = resolver . generateName ( modulle , containerEntity ) ;
2021-04-01 19:26:17 +01:00
gprintln ( "Entity \"" ~ fullPath
~ "\" is allowed to be defined within container \""
~ containerNameFullPath ~ "\"" ) ;
2021-04-01 14:26:17 +01:00
/ * *
* Check if this Entity is a Container , if so , then
* apply the same round of checks within it
* /
2021-04-01 19:26:17 +01:00
Container possibleContainerEntity = cast ( Container ) entity ;
if ( possibleContainerEntity )
2021-04-01 14:26:17 +01:00
{
2021-04-29 16:54:26 +01:00
checkContainerCollision ( possibleContainerEntity ) ;
2021-04-01 14:26:17 +01:00
}
}
2021-04-01 14:21:10 +01:00
}
}
2021-10-27 14:52:43 +01:00
/ * *
* TODO : Create a version of the below function that possibly
* returns the list of Statement [ ] s ordered like below but
* via a weighting system rather
* /
public Statement [ ] getContainerMembers_W ( Container c )
{
/* Statements */
Statement [ ] statements ;
/* TODO: Implement me */
return statements ;
}
2021-04-01 14:21:10 +01:00
/ * *
* Returns container members in order of
* Clazz , Function , Variable
* /
private Entity [ ] getContainerMembers ( Container c )
{
/* Entities */
Entity [ ] entities ;
/* Get all classes */
2021-04-01 19:26:17 +01:00
foreach ( Statement statement ; c . getStatements ( ) )
2021-04-01 14:21:10 +01:00
{
2021-10-27 19:57:30 +01:00
if ( statement ! is null & & cast ( Entity ) statement )
2021-04-01 14:21:10 +01:00
{
2021-10-27 19:57:30 +01:00
entities ~ = cast ( Entity ) statement ;
2021-04-01 14:21:10 +01:00
}
}
2021-10-27 19:57:30 +01:00
// /* Get all classes */
// foreach (Statement statement; c.getStatements())
// {
// if (statement !is null && cast(Clazz) statement)
// {
// entities ~= cast(Clazz) statement;
// }
// }
2021-04-01 14:21:10 +01:00
2021-10-27 19:57:30 +01:00
// /* Get all functions */
// foreach (Statement statement; c.getStatements())
// {
// if (statement !is null && cast(Function) statement)
// {
// entities ~= cast(Function) statement;
// }
// }
// /* Get all variables */
// foreach (Statement statement; c.getStatements())
// {
// if (statement !is null && cast(Variable) statement)
// {
// entities ~= cast(Variable) statement;
// }
// }
2021-04-01 14:21:10 +01:00
return entities ;
}
2021-04-01 14:49:02 +01:00
/ * *
* Finds the first occurring Entity with the provided
* name based on Classes being searched , then Functions
* and lastly Variables
* /
2021-04-01 14:21:10 +01:00
public Entity findPrecedence ( Container c , string name )
{
2021-04-01 19:26:17 +01:00
foreach ( Entity entity ; getContainerMembers ( c ) )
2021-04-01 14:21:10 +01:00
{
/* If we find matching entity names */
2021-04-01 19:26:17 +01:00
if ( cmp ( entity . getName ( ) , name ) = = 0 )
2021-04-01 14:21:10 +01:00
{
return entity ;
}
}
return null ;
}
2021-04-01 07:47:44 +01:00
2021-03-30 19:05:16 +01:00
/ * *
* Starting from a Container c this makes sure
* that all classes defined within that container
* do no clash name wise
2021-04-01 14:21:10 +01:00
*
* Make this general , so it checks all Entoties
* within container , starting first with classes
* then it should probably mark them , this will
* be so we can then loop through all entities
* including classes , of container c and for
* every entity we come across in c we make
* sure it doesn ' t have a name of something that
* is marked
2021-03-30 19:05:16 +01:00
* /
private void checkClassNames ( Container c )
2021-03-30 17:51:32 +01:00
{
2021-04-21 20:14:11 +01:00
/ * *
* TODO : Always make sure this holds
*
* All objects that implement Container so far
* are also Entities ( hence they have a name )
* /
Entity containerEntity = cast ( Entity ) c ;
assert ( containerEntity ) ;
2021-03-30 17:51:32 +01:00
/* Get all types (Clazz so far) */
Clazz [ ] classTypes ;
2021-04-01 19:26:17 +01:00
foreach ( Statement statement ; c . getStatements ( ) )
2021-03-30 17:51:32 +01:00
{
2021-04-01 19:26:17 +01:00
if ( statement ! is null & & cast ( Clazz ) statement )
2021-03-30 17:51:32 +01:00
{
2021-04-01 19:26:17 +01:00
classTypes ~ = cast ( Clazz ) statement ;
2021-03-30 17:51:32 +01:00
}
}
/* Declare each type */
2021-04-01 19:26:17 +01:00
foreach ( Clazz clazz ; classTypes )
2021-03-30 17:51:32 +01:00
{
2021-04-01 07:51:55 +01:00
// gprintln("Name: "~resolver.generateName(modulle, clazz));
2021-03-30 17:51:32 +01:00
/ * *
* Check if the first class found with my name is the one being
* processed , if so then it is fine , if not then error , it has
* been used ( that identifier ) already
2021-03-30 19:05:16 +01:00
*
* TODO : We cann add a check here to not allow containerName = = clazz
* TODO : Call resolveUp as we can then stop class1 . class1 . class1
* Okay top would resolve first part but class1 . class2 . class1
* would not be caught by that
*
* TODO : This will meet inner clazz1 first , we need to do another check
2021-03-30 17:51:32 +01:00
* /
2021-04-01 19:26:17 +01:00
if ( resolver . resolveUp ( c , clazz . getName ( ) ) ! = clazz )
2021-03-30 17:51:32 +01:00
{
2021-04-01 19:26:17 +01:00
Parser . expect ( "Cannot define class \"" ~ resolver . generateName ( modulle ,
clazz ) ~ "\" as one with same name, \"" ~ resolver . generateName ( modulle ,
resolver . resolveUp ( c , clazz . getName ( ) ) ) ~ "\" exists in container \"" ~ resolver . generateName (
2021-04-21 20:14:11 +01:00
modulle , containerEntity ) ~ "\"" ) ;
2021-03-30 17:51:32 +01:00
}
else
{
2021-03-30 20:29:57 +01:00
/* Get the current container's parent container */
2021-04-21 20:14:11 +01:00
Container parentContainer = containerEntity . parentOf ( ) ;
2021-03-30 20:29:57 +01:00
2021-03-30 20:54:27 +01:00
/* Don't allow a class to be named after it's container */
// if(!parentContainer)
// {
2021-04-21 20:14:11 +01:00
if ( cmp ( containerEntity . getName ( ) , clazz . getName ( ) ) = = 0 )
2021-04-01 07:56:06 +01:00
{
2021-04-01 19:26:17 +01:00
Parser . expect ( "Class \"" ~ resolver . generateName ( modulle ,
clazz ) ~ "\" cannot be defined within container with same name, \"" ~ resolver . generateName (
2021-04-21 20:14:11 +01:00
modulle , containerEntity ) ~ "\"" ) ;
2021-04-01 07:56:06 +01:00
}
2021-04-01 14:21:10 +01:00
/* TODO: Loop througn Container ENtitys here */
/* Make sure that when we call findPrecedence(entity) == current entity */
2021-03-30 20:54:27 +01:00
// }
2021-03-30 20:29:57 +01:00
2021-03-30 20:36:35 +01:00
/* TODO: We allow shaddowing so below is disabled */
/* TODO: We should however use the below for dot-less resolution */
// /* Find the name starting in upper cotainer */
// Entity clazzAbove = resolveUp(parentContainer, clazz.getName());
2021-03-30 20:29:57 +01:00
2021-03-30 20:36:35 +01:00
// if(!clazzAbove)
// {
2021-03-30 20:29:57 +01:00
2021-03-30 20:36:35 +01:00
// }
// else
// {
// Parser.expect("Name in use abpve us, bad"~to!(string)(clazz));
// }
2021-03-30 20:29:57 +01:00
/ * If the Container ' s parent container is Module then we can have
/* TODO: Check that it doesn;t equal any class up the chain */
/* TODO: Exclude Module from this */
2021-03-30 19:05:16 +01:00
// /* Still check if there is something with our name above us */
// Container parentContainer = c.parentOf();
// /* If at this level container we find duplicate */
// if(resolveUp(parentContainer, clazz.getName()))
// {
2021-04-01 19:26:17 +01:00
2021-03-30 19:05:16 +01:00
// Parser.expect("Class with name "~clazz.getName()~" defined in class "~c.getName());
2021-04-01 19:26:17 +01:00
2021-03-30 19:05:16 +01:00
// }
2021-03-30 17:51:32 +01:00
}
}
/ * *
* TODO : Now we should loop through each class and do the same
* so we have all types defined
* /
2021-04-01 13:09:30 +01:00
//gprintln("Defined classes: "~to!(string)(Program.getAllOf(new Clazz(""), cast(Statement[])marked)));
2021-04-01 19:26:17 +01:00
2021-03-30 17:51:32 +01:00
/ * *
* By now we have confirmed that within the current container
* there are no classes defined with the same name
*
* We now check each Class recursively , once we are done
* we mark the class entity as "ready" ( may be referenced )
* /
2021-04-01 19:26:17 +01:00
foreach ( Clazz clazz ; classTypes )
2021-03-30 17:51:32 +01:00
{
2021-04-01 19:26:17 +01:00
gprintln ( "Check recursive " ~ to ! ( string ) ( clazz ) , DebugType . WARNING ) ;
2021-03-30 19:05:16 +01:00
2021-03-30 17:51:32 +01:00
/* Check the current class's types within */
2021-03-30 19:05:16 +01:00
checkClassNames ( clazz ) ;
2021-03-30 17:51:32 +01:00
2021-03-30 20:07:04 +01:00
// checkClassInherit(clazz);
2021-03-30 17:51:32 +01:00
}
/*Now we should loop through each class */
/* Once outerly everything is defined we can then handle class inheritance names */
/* We can also then handle refereces between classes */
// gprintln("checkTypes: ")
}
2021-03-24 21:19:40 +00:00
/* Test name resolution */
unittest
{
//assert()
}
}
2021-11-02 07:34:40 +00:00
/* Test name colliding with container name (1/3) [module] */
unittest
{
import std.file ;
import std.stdio ;
2023-02-05 18:21:26 +00:00
import tlang.compiler.lexer.core ;
import tlang.compiler.parsing.core ;
2021-11-02 07:34:40 +00:00
string sourceFile = "source/tlang/testing/collide_container_module1.t" ;
File sourceFileFile ;
sourceFileFile . open ( sourceFile ) ; /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile . size ( ) ;
byte [ ] fileBytes ;
fileBytes . length = fileSize ;
fileBytes = sourceFileFile . rawRead ( fileBytes ) ;
sourceFileFile . close ( ) ;
string sourceCode = cast ( string ) fileBytes ;
Lexer currentLexer = new Lexer ( sourceCode ) ;
currentLexer . performLex ( ) ;
Parser parser = new Parser ( currentLexer . getTokens ( ) ) ;
Module modulle = parser . parse ( ) ;
TypeChecker typeChecker = new TypeChecker ( modulle ) ;
/* Setup testing variables */
Entity container = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "y" ) ;
Entity colliderMember = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "y.y" ) ;
try
{
/* Perform test */
typeChecker . beginCheck ( ) ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:34:40 +00:00
/* Shouldn't reach here, collision exception MUST occur */
assert ( false ) ;
}
catch ( CollidingNameException e )
{
/* Make sure the member y.y collided with root container (module) y */
assert ( e . defined = = container ) ;
}
}
2021-06-08 09:52:22 +01:00
2021-11-02 07:34:40 +00:00
/* Test name colliding with container name (2/3) [module, nested collider] */
unittest
{
import std.file ;
import std.stdio ;
2023-02-05 18:21:26 +00:00
import tlang.compiler.lexer.core ;
import tlang.compiler.parsing.core ;
2021-11-02 07:34:40 +00:00
string sourceFile = "source/tlang/testing/collide_container_module2.t" ;
File sourceFileFile ;
sourceFileFile . open ( sourceFile ) ; /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile . size ( ) ;
byte [ ] fileBytes ;
fileBytes . length = fileSize ;
fileBytes = sourceFileFile . rawRead ( fileBytes ) ;
sourceFileFile . close ( ) ;
string sourceCode = cast ( string ) fileBytes ;
Lexer currentLexer = new Lexer ( sourceCode ) ;
currentLexer . performLex ( ) ;
Parser parser = new Parser ( currentLexer . getTokens ( ) ) ;
Module modulle = parser . parse ( ) ;
TypeChecker typeChecker = new TypeChecker ( modulle ) ;
/* Setup testing variables */
Entity container = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "y" ) ;
Entity colliderMember = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "y.a.b.c.y" ) ;
try
{
/* Perform test */
typeChecker . beginCheck ( ) ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:34:40 +00:00
/* Shouldn't reach here, collision exception MUST occur */
assert ( false ) ;
}
catch ( CollidingNameException e )
{
/* Make sure the member y.a.b.c.y collided with root container (module) y */
assert ( e . defined = = container ) ;
}
}
2021-06-08 09:52:22 +01:00
2021-11-02 07:34:57 +00:00
/* Test name colliding with container name (3/3) [container (non-module), nested collider] */
unittest
{
import std.file ;
import std.stdio ;
2023-02-05 18:21:26 +00:00
import tlang.compiler.lexer.core ;
import tlang.compiler.parsing.core ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:34:57 +00:00
string sourceFile = "source/tlang/testing/collide_container_non_module.t" ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:34:57 +00:00
File sourceFileFile ;
sourceFileFile . open ( sourceFile ) ; /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile . size ( ) ;
byte [ ] fileBytes ;
fileBytes . length = fileSize ;
fileBytes = sourceFileFile . rawRead ( fileBytes ) ;
sourceFileFile . close ( ) ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:34:57 +00:00
string sourceCode = cast ( string ) fileBytes ;
Lexer currentLexer = new Lexer ( sourceCode ) ;
currentLexer . performLex ( ) ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:34:57 +00:00
Parser parser = new Parser ( currentLexer . getTokens ( ) ) ;
Module modulle = parser . parse ( ) ;
TypeChecker typeChecker = new TypeChecker ( modulle ) ;
/* Setup testing variables */
Entity container = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "a.b.c" ) ;
Entity colliderMember = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "a.b.c.c" ) ;
try
{
/* Perform test */
typeChecker . beginCheck ( ) ;
/* Shouldn't reach here, collision exception MUST occur */
assert ( false ) ;
}
catch ( CollidingNameException e )
{
/* Make sure the member a.b.c.c collided with a.b.c container */
assert ( e . defined = = container ) ;
}
}
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:27 +00:00
/* Test name colliding with member */
unittest
{
import std.file ;
import std.stdio ;
2023-02-05 18:21:26 +00:00
import tlang.compiler.lexer.core ;
import tlang.compiler.parsing.core ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:27 +00:00
string sourceFile = "source/tlang/testing/collide_member.t" ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:27 +00:00
File sourceFileFile ;
sourceFileFile . open ( sourceFile ) ; /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile . size ( ) ;
byte [ ] fileBytes ;
fileBytes . length = fileSize ;
fileBytes = sourceFileFile . rawRead ( fileBytes ) ;
sourceFileFile . close ( ) ;
string sourceCode = cast ( string ) fileBytes ;
Lexer currentLexer = new Lexer ( sourceCode ) ;
currentLexer . performLex ( ) ;
Parser parser = new Parser ( currentLexer . getTokens ( ) ) ;
Module modulle = parser . parse ( ) ;
TypeChecker typeChecker = new TypeChecker ( modulle ) ;
/* Setup testing variables */
Entity memberFirst = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "a.b" ) ;
try
{
/* Perform test */
typeChecker . beginCheck ( ) ;
/* Shouldn't reach here, collision exception MUST occur */
assert ( false ) ;
}
catch ( CollidingNameException e )
{
/* Make sure the member a.b.c.c collided with a.b.c container */
assert ( e . attempted ! = memberFirst ) ;
}
}
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:48 +00:00
/* Test name colliding with member (check that the member defined is class (precendence test)) */
unittest
{
import std.file ;
import std.stdio ;
2023-02-05 18:21:26 +00:00
import tlang.compiler.lexer.core ;
import tlang.compiler.parsing.core ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:48 +00:00
string sourceFile = "source/tlang/testing/precedence_collision_test.t" ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:48 +00:00
File sourceFileFile ;
sourceFileFile . open ( sourceFile ) ; /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile . size ( ) ;
byte [ ] fileBytes ;
fileBytes . length = fileSize ;
fileBytes = sourceFileFile . rawRead ( fileBytes ) ;
sourceFileFile . close ( ) ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:48 +00:00
string sourceCode = cast ( string ) fileBytes ;
Lexer currentLexer = new Lexer ( sourceCode ) ;
currentLexer . performLex ( ) ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:48 +00:00
Parser parser = new Parser ( currentLexer . getTokens ( ) ) ;
Module modulle = parser . parse ( ) ;
TypeChecker typeChecker = new TypeChecker ( modulle ) ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:35:48 +00:00
/* Setup testing variables */
Entity ourClassA = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "a" ) ;
try
{
/* Perform test */
typeChecker . beginCheck ( ) ;
/* Shouldn't reach here, collision exception MUST occur */
assert ( false ) ;
}
catch ( CollidingNameException e )
{
/* Make sure the member attempted was Variable and defined was Clazz */
assert ( cast ( Variable ) e . attempted ) ;
assert ( cast ( Clazz ) e . defined ) ;
}
}
2021-06-08 09:52:22 +01:00
2021-11-02 07:36:15 +00:00
/* Test name colliding with container name (1/2) */
unittest
{
import std.file ;
import std.stdio ;
2023-02-05 18:21:26 +00:00
import tlang.compiler.lexer.core ;
import tlang.compiler.parsing.core ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:36:15 +00:00
string sourceFile = "source/tlang/testing/collide_container.t" ;
2021-06-08 09:52:22 +01:00
2021-11-02 07:36:15 +00:00
File sourceFileFile ;
sourceFileFile . open ( sourceFile ) ; /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile . size ( ) ;
byte [ ] fileBytes ;
fileBytes . length = fileSize ;
fileBytes = sourceFileFile . rawRead ( fileBytes ) ;
sourceFileFile . close ( ) ;
string sourceCode = cast ( string ) fileBytes ;
Lexer currentLexer = new Lexer ( sourceCode ) ;
currentLexer . performLex ( ) ;
Parser parser = new Parser ( currentLexer . getTokens ( ) ) ;
Module modulle = parser . parse ( ) ;
TypeChecker typeChecker = new TypeChecker ( modulle ) ;
/* Setup testing variables */
Entity container = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "y" ) ;
Entity colliderMember = typeChecker . getResolver ( ) . resolveBest ( typeChecker . getModule , "y.y" ) ;
try
{
/* Perform test */
typeChecker . beginCheck ( ) ;
/* Shouldn't reach here, collision exception MUST occur */
assert ( false ) ;
}
catch ( CollidingNameException e )
{
/* Make sure the member y.y collided with root container (module) y */
assert ( e . defined = = container ) ;
}
2022-01-20 08:43:12 +00:00
}
/* Test name colliding with container name (1/2) */
2023-01-13 09:13:14 +00:00
// TODO: Re-enable this when we take a look at the `discards` - for now discards at module level are not allowed
// ... therefore this unittest fails - otherwise it would have normally passed
// unittest
// {
// import std.file;
// import std.stdio;
// import compiler.lexer;
// import compiler.parsing.core;
// string sourceFile = "source/tlang/testing/typecheck/simple_dependence_correct7.t";
// File sourceFileFile;
// sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
// ulong fileSize = sourceFileFile.size();
// byte[] fileBytes;
// fileBytes.length = fileSize;
// fileBytes = sourceFileFile.rawRead(fileBytes);
// sourceFileFile.close();
// string sourceCode = cast(string) fileBytes;
// Lexer currentLexer = new Lexer(sourceCode);
// currentLexer.performLex();
// Parser parser = new Parser(currentLexer.getTokens());
// Module modulle = parser.parse();
// TypeChecker typeChecker = new TypeChecker(modulle);
// /* Perform test */
// typeChecker.beginCheck();
// /* TODO: Insert checks here */
// }
2022-04-12 10:11:54 +01:00
/ * *
* Code generation and typechecking
*
* Testing file : `simple_function_call.t`
* /
unittest
{
import std.file ;
import std.stdio ;
2023-02-05 18:21:26 +00:00
import tlang.compiler.lexer.core ;
import tlang.compiler.parsing.core ;
2022-04-12 10:11:54 +01:00
string sourceFile = "source/tlang/testing/typecheck/simple_function_call.t" ;
File sourceFileFile ;
sourceFileFile . open ( sourceFile ) ; /* TODO: Error handling with ANY file I/O */
ulong fileSize = sourceFileFile . size ( ) ;
byte [ ] fileBytes ;
fileBytes . length = fileSize ;
fileBytes = sourceFileFile . rawRead ( fileBytes ) ;
sourceFileFile . close ( ) ;
string sourceCode = cast ( string ) fileBytes ;
Lexer currentLexer = new Lexer ( sourceCode ) ;
currentLexer . performLex ( ) ;
Parser parser = new Parser ( currentLexer . getTokens ( ) ) ;
Module modulle = parser . parse ( ) ;
TypeChecker typeChecker = new TypeChecker ( modulle ) ;
/* Perform test */
typeChecker . beginCheck ( ) ;
/* TODO: Actually test generated code queue */
2021-11-09 17:16:51 +00:00
}