2021-03-30 15:31:37 +00:00
module compiler.typecheck.core ;
2021-03-21 19:43:01 +00:00
2021-03-30 15:35:16 +00:00
import compiler.symbols.check ;
import compiler.symbols.data ;
2021-03-21 19:43:01 +00:00
import std.conv : to ;
2021-03-24 21:19:40 +00:00
import std.string ;
import std.stdio ;
2021-03-29 17:13:39 +00:00
import gogga ;
2021-03-30 16:02:13 +00:00
import compiler.parsing.core ;
2021-03-31 10:54:34 +00:00
import compiler.typecheck.resolution ;
2021-04-01 12:38:20 +00:00
import compiler.typecheck.exceptions ;
2021-04-23 12:19:46 +00:00
import compiler.symbols.typing.core ;
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 19:06:30 +00:00
private Module modulle ;
2021-03-21 19:43:01 +00:00
2021-03-31 10:54:34 +00:00
/* The name resolver */
private Resolver resolver ;
2021-03-31 19:15:28 +00:00
public Module getModule ( )
{
return modulle ;
}
2021-03-29 19:06:30 +00:00
this ( Module modulle )
2021-03-21 19:43:01 +00:00
{
2021-03-29 19:06:30 +00:00
this . modulle = modulle ;
2021-03-31 10:54:34 +00:00
resolver = new Resolver ( this ) ;
2021-06-04 11:39:11 +00:00
/* TODO: Module check?!?!? */
2021-06-06 14:52:47 +00:00
initTrees ( ) ;
2021-06-04 11:39:11 +00:00
}
2021-03-23 19:35:13 +00:00
2021-06-04 11:39:11 +00:00
import compiler.typecheck.visitor ;
private VTreeNode root ;
2021-06-06 14:52:47 +00:00
/ * *
* Root of reliance ( dependency ) tree
* /
import compiler.typecheck.reliance ;
private RelianceNode relianceRootNode ;
private void initTrees ( )
2021-06-04 11:39:11 +00:00
{
root = new VTreeNode ( modulle ) ;
2021-06-06 14:52:47 +00:00
/* Create a reliance node with no dependancies for the module */
relianceRootNode = new RelianceNode ( modulle ) ;
2021-03-29 17:13:39 +00:00
}
2021-06-04 09:55:22 +00:00
2021-06-04 11:39:11 +00:00
// private Statement[] visistedStatements;
public void visit ( VTreeNode level , Statement statement )
2021-06-04 09:55:22 +00:00
{
2021-06-04 11:39:11 +00:00
// visistedStatements ~= statement;
level . addChild ( new VTreeNode ( statement ) ) ;
2021-06-04 09:55:22 +00:00
}
2021-06-04 11:39:11 +00:00
public VTreeNode hasVisited ( Statement statementInQuestion )
2021-06-04 09:55:22 +00:00
{
2021-06-04 11:39:11 +00:00
return root . isInTree ( statementInQuestion ) ;
2021-06-04 09:55:22 +00:00
}
2021-04-07 11:11:36 +00: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 15:26:14 +00:00
/* Check declaration and definition types */
checkDefinitionTypes ( modulle ) ;
2021-04-07 11:11:36 +00:00
/* TODO: Implement me */
2021-04-09 13:56:15 +00:00
checkClassInherit ( modulle ) ;
2021-08-15 20:23:17 +00: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
*
* /
2021-06-09 17:45:57 +00:00
import compiler.typecheck.dependency ;
DNodeGenerator dNodeGenerator = new DNodeGenerator ( this ) ;
2021-08-15 20:33:22 +00:00
DNode rootNode = dNodeGenerator . generate ( ) ; /* TODO: This should make it acyclic */
2021-08-15 20:23:17 +00:00
2021-08-15 20:33:22 +00:00
/* Print the tree */
2021-08-15 20:23:17 +00:00
string tree = rootNode . print ( ) ;
gprintln ( tree ) ;
2021-10-25 14:55:43 +00:00
/* Get the action-list (linearised bottom up graph) */
DNode [ ] actionList = rootNode . poes ;
gprintln ( "Action list: " ~ to ! ( string ) ( actionList ) ) ;
foreach ( DNode node ; actionList )
{
gprintln ( "Process: " ~ to ! ( string ) ( node ) ) ;
2021-10-25 15:03:36 +00:00
/ * *
* Now depending on thr DNode type we should
* place ambiguous intems on stack then
* move on , let the next process then
* pop the stack and then consume it
* for checking ( typewise we can get
* information out of it ) , then when
* done we should probably pop - the other
* guy off and push something that resembles
* an emmitable onto a EmitStack
* /
/* TODO: typecheck(node) */
/* TODO: emit(node) */
2021-10-25 14:55:43 +00:00
}
2021-10-25 14:54:06 +00:00
2021-08-15 20:23:17 +00:00
/ * *
* TODO : What ' s next ?
*
* 1. Fetch the tree from the DNodeGenerator
* /
2021-06-09 17:45:57 +00:00
2021-06-07 13:56:43 +00:00
2021-04-07 11:11:36 +00:00
}
2021-04-23 12:07:19 +00: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 15:58:27 +00:00
{
2021-04-23 12:07:19 +00:00
Type foundType ;
2021-04-15 15:58:27 +00:00
2021-04-23 12:07:19 +00:00
/* Check if the type is built-in */
2021-04-26 08:01:53 +00:00
foundType = getBuiltInType ( typeString ) ;
/* If it isn't then check for a type (resolve it) */
if ( ! foundType )
2021-04-23 12:07:19 +00:00
{
foundType = cast ( Type ) resolver . resolveBest ( c , typeString ) ;
}
2021-04-26 08:01:53 +00:00
2021-04-23 12:07:19 +00:00
return foundType ;
2021-04-15 15:58:27 +00:00
}
2021-06-04 09:27:56 +00:00
private void checkClass ( Clazz clazz )
{
gprintln ( "Checking class now..." ) ;
/* TODO: Get all typed entities */
/* TODO: Check things ithin */
checkTypedEntitiesTypeNames ( clazz ) ;
}
2021-06-06 14:52:47 +00:00
2021-06-06 15:15:48 +00:00
2021-06-06 14:52:47 +00:00
2021-04-23 12:14:09 +00:00
/ * *
* Checks all TypedEntity ( s ) ( so Variables and Functions )
* such that their types ( variable type / return type ) are
* valid type names
2021-06-04 09:31:44 +00:00
*
* This is called on a Container
2021-06-04 10:04:58 +00:00
*
* TODO : Should we also do expression parsing or rather do another call for that
* mmmmh
2021-04-23 12:14:09 +00:00
* /
2021-06-04 11:39:11 +00:00
// private VTreeNode currentNode;
2021-04-23 12:14:09 +00:00
private void checkTypedEntitiesTypeNames ( Container c )
2021-04-15 15:26:14 +00:00
{
2021-06-06 15:56:31 +00:00
/* This VTreeNode */
VTreeNode thisNode ;
2021-06-04 11:39:11 +00:00
2021-06-06 15:56:31 +00:00
if ( c = = modulle )
{
thisNode = root ;
}
else
{
/* Create a VTreeNode for this Statement */
assert ( cast ( Statement ) c ) ;
thisNode = new VTreeNode ( cast ( Statement ) c ) ;
2021-06-04 11:39:11 +00:00
2021-06-06 15:56:31 +00:00
gprintln ( "dsdf" ) ;
2021-06-04 11:39:11 +00:00
2021-06-06 15:56:31 +00:00
/* Get my parent's VTreeNode */
/* TODO: This parent of, ah we should make functions be containers, like we gonna need that for constutcor processing etc, and fucntions, mutual recursion there too */
Statement cS = cast ( Statement ) c ;
VTreeNode parentNode = hasVisited ( cast ( Statement ) cS . parentOf ( ) ) ;
2021-06-04 11:39:11 +00:00
2021-06-06 15:56:31 +00:00
gprintln ( "dsdf" ) ;
gprintln ( parentNode ) ;
gprintln ( c ) ;
2021-06-04 11:39:11 +00:00
2021-06-06 15:56:31 +00:00
/* TODO: My grand resolver fuuuuck the parent is not in da tree */
/* TODO: Static classes moment */
2021-06-04 11:42:26 +00:00
2021-06-06 15:56:31 +00:00
/* TODO: We should do this recursively rather, because we exit it is fine technically so the tree will be valid */
2021-06-04 11:39:11 +00:00
2021-06-06 15:56:31 +00:00
/* Child-self to parent VTreeNode */
parentNode . addChild ( thisNode ) ;
gprintln ( "dsdf" ) ;
2021-06-06 14:52:47 +00:00
2021-06-06 15:56:31 +00:00
}
2021-06-04 11:39:11 +00:00
2021-06-06 15:56:31 +00:00
TypedEntity [ ] typedEntities ;
2021-04-15 15:26:14 +00:00
2021-06-06 15:56:31 +00:00
foreach ( Statement statement ; c . getStatements ( ) )
{
if ( statement ! is null & & cast ( TypedEntity ) statement )
{
typedEntities ~ = cast ( TypedEntity ) statement ;
}
}
2021-04-15 15:26:14 +00:00
2021-06-06 15:56:31 +00:00
/* Attempt to resolve the types of the variables */
foreach ( TypedEntity typedEntity ; typedEntities )
{
/* TypedEntity's type */
string typeString = typedEntity . getType ( ) ;
2021-04-15 15:58:27 +00:00
2021-06-06 15:56:31 +00:00
/* TODO: Resolve type here (either built-in or class type) */
Type type = getType ( c , typeString ) ;
2021-04-23 12:07:19 +00:00
2021-06-06 15:56:31 +00:00
/* Make sure type is valid */
if ( ! type )
{
Parser . expect ( "Invalid type \"" ~ typeString ~ "\"" ) ;
}
2021-04-23 12:09:05 +00:00
2021-06-06 15:56:31 +00:00
gprintln ( "Type: " ~ to ! ( string ) ( type ) ) ;
2021-06-04 09:27:56 +00:00
2021-06-06 15:56:31 +00:00
/* TODO: Visit it (mark it as such) */
VTreeNode thisEntity = new VTreeNode ( typedEntity ) ;
2021-06-04 11:39:11 +00:00
2021-06-04 09:27:56 +00:00
2021-06-06 15:56:31 +00:00
/* TODO: Check type here */
2021-06-04 09:27:56 +00:00
2021-06-06 15:56:31 +00:00
/* If it is primitive then no further checking */
if ( cast ( Number ) type )
{
/* TODO: Mark it as ready-for-reference */
2021-06-04 11:39:11 +00:00
2021-06-06 15:56:31 +00:00
/* TODO: Expression checking */
thisNode . addChild ( thisEntity ) ;
2021-06-04 09:57:25 +00:00
2021-06-06 15:56:31 +00:00
}
else
{
/* If it is a Class type */
if ( cast ( Clazz ) type )
{
Clazz clazzType = cast ( Clazz ) type ;
/* TODO: Check constructor */
/* TODO: We need to start marking things */
/* TODO: Do actual checks here now */
/* TODO: If the type is of the current class we are in then it is fine? */
if ( clazzType = = c )
{
gprintln ( "Container we are in matches type of TypedEdntity being processed" ) ;
/* TODO: In that case mark the entity as fine */
thisNode . addChild ( thisEntity ) ;
// clazzType.mark();
}
/* If the type is visited already (good for rwcuasiev case mutal class references) */
else if ( hasVisited ( clazzType ) )
{
/* TODO: This could actually solve the abive too? */
/* This is basically saying the TypedEntity's type is a CLass that has been visited so we can assume it is safe to add */
/* We don't wanna visit it again (as stackoevrflow) from mutaul recursion then */
thisNode . addChild ( thisEntity ) ;
}
else
{
/* TODO: Also make it fine? mmuutal recusive refernce */
/* TODO: Got it, we NEED a dependency tree, to know chihs is being processed previosuly */
/* TODO: Now check this class and follow it's path */
checkClass ( clazzType ) ;
}
2021-06-04 09:31:44 +00:00
2021-06-06 15:56:31 +00:00
}
}
2021-04-23 12:07:19 +00:00
2021-06-06 15:56:31 +00:00
}
2021-06-06 14:52:47 +00:00
2021-06-06 15:56:31 +00:00
// /**
// * Get all classes
// */
// Clazz[] classes;
// foreach (Statement statement; c.getStatements())
// {
// if (statement !is null && cast(Clazz) statement)
// {
// classes ~= cast(Clazz) statement;
// }
// }
2021-06-06 14:52:47 +00:00
2021-06-06 15:56:31 +00:00
// /**
// * TODO: Here I am testing on dependeny constrtuction
// * 1. Only for classes
// * 2. Only for their static member (assignment is assumed not to happen)
// */
// foreach(Clazz currentClass; classes)
// {
// gprintln("DependencyConstruction: Class Container found '"~to!(string)(currentClass)~"'");
2021-06-06 14:52:47 +00:00
2021-06-06 15:56:31 +00:00
// /* Mark this as reliant on modulle (then) */
2021-06-06 14:52:47 +00:00
2021-06-06 15:56:31 +00:00
// /* Check recursively */
// checkClass_DepTest(currentClass);
// }
2021-06-06 14:52:47 +00:00
2021-04-15 15:58:27 +00:00
}
2021-06-06 14:52:47 +00:00
2021-06-06 15:15:48 +00:00
/ * *
* Dependency encountering
*
* TODO : Move to own module
2021-06-06 15:56:31 +00:00
// */
// public string[][string] deps;
// public void encounter(string entityName, string dependentOn)
// {
// deps[entityName] ~= dependentOn;
// gprintln("[Encounter] Entity: \""~entityName~"\" set to be dependent on \""~dependentOn~"\"");
// }
// private void checkClass_DepTest(Clazz c)
// {
// /**
// * Get all static entities in class
// */
// Entity[] staticMembers;
// foreach (Statement statement; c.getStatements())
// {
// if (statement !is null && cast(Entity) statement)
// {
// Entity member = cast(Entity)statement;
// if(member.getModifierType() == InitScope.STATIC)
// {
// staticMembers ~= cast(Entity) statement;
// }
// }
// }
// gprintln("Static members: "~to!(string)(staticMembers));
// /**
// * Processes all Class definitions (first, ordered)
// * Fucntions and Variables (TODO: I cannot recall that ordering)
// */
// foreach(Entity staticMember; staticMembers)
// {
// /**
// * Handle static member classes (Class)
// */
// if(cast(Clazz)staticMember)
// {
// /* The class must be dependent on the current class */
// gprintln("fdhjdfshjfd");
// /* Full path of thing depending on something else */
// string dependee = resolver.generateName(modulle, staticMember);
// /* Full path of the thing it is dependent on */
// string dependency = resolver.generateName(modulle, c);
2021-06-06 15:33:14 +00:00
2021-06-06 15:56:31 +00:00
// /* Add this to the dependency list fpr the current dependent staticMemberType */
// encounter(dependee, dependency);
// /* If the static member is a class then apply the logic recursively to it */
// Clazz staticMemberClass = cast(Clazz)staticMember;
// checkTypedEntitiesTypeNames(staticMemberClass);
// }
// /**
// * Handle static member functions/variables (Function/Variable)
// */
// else if(cast(TypedEntity)staticMember)
// {
// /* Typed static member */
// TypedEntity typedStaticMember = cast(TypedEntity)staticMember;
// /* Get the Type of the member */
// Type staticMemberType = getType(c, typedStaticMember.getType());
// /* Full path of thing depending on something else */
// string dependee = resolver.generateName(modulle, typedStaticMember);
// /* Full path of the thing it is dependent on */
// string dependency = resolver.generateName(modulle, staticMemberType);
2021-06-06 15:33:14 +00:00
2021-06-06 15:56:31 +00:00
// /* Add this to the dependency list fpr the current dependent staticMemberType */
// encounter(dependee, dependency);
// }
// else
// {
// assert(false);
// }
// }
// }
2021-06-06 14:52:47 +00:00
2021-04-23 12:14:09 +00:00
/* TODO: TYpeEntity check sepeare */
/* TODO: Parsing within function etc. */
2021-04-15 15:58:27 +00:00
private void checkDefinitionTypes ( Container c )
{
2021-04-23 12:14:09 +00:00
/* Check variables and functions (TypedEntities) declarations */
checkTypedEntitiesTypeNames ( c ) ;
2021-04-15 15:58:27 +00:00
2021-04-23 12:14:09 +00:00
2021-04-15 15:26:14 +00: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 15:58:45 +00:00
/ * *
* Begins the type checking process
* /
2021-04-01 13:39:06 +00:00
public void beginCheck ( )
2021-03-29 19:06:30 +00:00
{
2021-04-01 13:32:05 +00: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 15:54:26 +00:00
checkContainerCollision ( modulle ) ; /* TODO: Rename checkContainerCollision */
2021-04-01 13:21:10 +00:00
2021-04-01 12:09:30 +00:00
/* TODO: Now that everything is defined, no collision */
/* TODO: Do actual type checking and declarations */
2021-04-09 13:56:15 +00:00
dependencyCheck ( ) ;
2021-03-29 19:06:30 +00:00
}
2021-03-30 19:07:04 +00:00
private void checkClassInherit ( Container c )
2021-03-30 18:05:16 +00:00
{
2021-03-30 19:07:04 +00:00
/* Get all types (Clazz so far) */
Clazz [ ] classTypes ;
2021-04-01 18:26:17 +00:00
foreach ( Statement statement ; c . getStatements ( ) )
2021-03-30 19:07:04 +00:00
{
2021-04-01 18:26:17 +00:00
if ( statement ! is null & & cast ( Clazz ) statement )
2021-03-30 19:07:04 +00:00
{
2021-04-01 18:26:17 +00:00
classTypes ~ = cast ( Clazz ) statement ;
2021-03-30 19:07:04 +00:00
}
}
/* Process each Clazz */
2021-04-01 18:26:17 +00:00
foreach ( Clazz clazz ; classTypes )
2021-03-30 19:07:04 +00:00
{
2021-04-01 18:26:17 +00:00
/* Get the current class's parent */
2021-03-30 19:07:04 +00:00
string [ ] parentClasses = clazz . getInherit ( ) ;
2021-04-01 18:26:17 +00:00
gprintln ( "Class: " ~ clazz . getName ( ) ~ ": ParentInheritList: " ~ to ! (
string ) ( parentClasses ) ) ;
2021-03-30 19:07:04 +00:00
/* Try resolve all of these */
2021-04-01 18:26:17 +00:00
foreach ( string parent ; parentClasses )
2021-03-30 19:07:04 +00:00
{
/* Find the named entity */
2021-03-30 20:04:11 +00:00
Entity namedEntity ;
/* Check if the name is rooted */
string [ ] dotPath = split ( parent , '.' ) ;
gprintln ( dotPath . length ) ;
2021-04-01 06:47:44 +00:00
/* Resolve the name */
namedEntity = resolver . resolveBest ( c , parent ) ;
2021-03-30 19:07:04 +00:00
/* If the entity exists */
2021-04-01 18:26:17 +00:00
if ( namedEntity )
2021-03-30 19:07:04 +00:00
{
/* Check if it is a Class, if so non-null */
2021-04-01 18:26:17 +00:00
Clazz parentEntity = cast ( Clazz ) namedEntity ;
2021-03-30 19:07:04 +00:00
/* Only inherit from class or (TODO: interfaces) */
2021-04-01 18:26:17 +00:00
if ( parentEntity )
2021-03-30 19:07:04 +00:00
{
/* Make sure it is not myself */
2021-04-01 18:26:17 +00:00
if ( parentEntity ! = clazz )
2021-03-30 19:07:04 +00:00
{
/* TODO: Add loop checking here */
}
else
{
Parser . expect ( "Cannot inherit from self" ) ;
}
}
/* Error */
2021-04-01 18:26:17 +00:00
else
2021-03-30 19:07:04 +00:00
{
Parser . expect ( "Can only inherit from classes" ) ;
}
}
/* If the entity doesn't exist then it is an error */
else
{
2021-04-01 18:26:17 +00:00
Parser . expect ( "Could not find any entity named " ~ parent ) ;
2021-03-30 19:07:04 +00:00
}
}
}
2021-03-30 18:05:16 +00:00
2021-03-30 19:07:04 +00:00
/* Once processing is done, apply recursively */
2021-04-01 18:26:17 +00:00
foreach ( Clazz clazz ; classTypes )
2021-03-30 18:05:16 +00:00
{
2021-03-30 19:07:04 +00:00
checkClassInherit ( clazz ) ;
2021-03-30 18:05:16 +00:00
}
2021-03-30 19:07:04 +00:00
2021-04-01 18:26:17 +00:00
}
2021-03-30 18:05:16 +00:00
private void checkClasses ( Container c )
{
/ * *
* Make sure no duplicate types ( classes ) defined
2021-03-30 19:07:04 +00:00
* within same Container
2021-03-30 18:05:16 +00:00
* /
checkClassNames ( c ) ;
/ * *
* Now that everything is neat and tidy
2021-03-30 19:07:04 +00:00
* let ' s check class properties like inheritance
* names
2021-03-30 18:05:16 +00:00
* /
2021-03-30 19:07:04 +00:00
checkClassInherit ( c ) ;
2021-03-30 18:05:16 +00:00
}
2021-04-01 12:43:49 +00:00
public Resolver getResolver ( )
{
return resolver ;
}
2021-03-30 18:05:16 +00:00
2021-04-01 13:21:10 +00:00
/ * *
2021-04-01 13:30:02 +00: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 13:21:10 +00:00
* /
2021-04-29 15:54:26 +00:00
private void checkContainerCollision ( Container c )
2021-04-01 13:21:10 +00:00
{
2021-04-21 19:14:11 +00: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 13:21:10 +00:00
/ * *
* Get all Entities of the Container with order Clazz , Function , Variable
* /
Entity [ ] entities = getContainerMembers ( c ) ;
2021-04-01 18:26:17 +00:00
gprintln ( "checkContainer(C): " ~ to ! ( string ) ( entities ) ) ;
2021-04-01 13:21:10 +00:00
2021-04-01 18:26:17 +00:00
foreach ( Entity entity ; entities )
2021-04-01 13:21:10 +00:00
{
2021-04-01 18:52:36 +00: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 13:21:10 +00:00
/ * *
* If the current entity ' s name matches the container then error
* /
2021-04-21 19:14:11 +00:00
else if ( cmp ( containerEntity . getName ( ) , entity . getName ( ) ) = = 0 )
2021-04-01 13:21:10 +00:00
{
2021-04-21 19:14:11 +00:00
throw new CollidingNameException ( this , containerEntity , entity , c ) ;
2021-04-01 13:21:10 +00:00
}
/ * *
* If there are conflicting names within the current container
* ( this takes precedence into account based on how `entities`
* is generated )
* /
2021-04-01 18:26:17 +00:00
else if ( findPrecedence ( c , entity . getName ( ) ) ! = entity )
2021-04-01 13:21:10 +00:00
{
2021-04-01 18:26:17 +00:00
throw new CollidingNameException ( this , findPrecedence ( c ,
entity . getName ( ) ) , entity , c ) ;
2021-04-01 13:21:10 +00:00
}
2021-04-01 13:26:17 +00:00
/ * *
* Otherwise this Entity is fine
* /
else
{
string fullPath = resolver . generateName ( modulle , entity ) ;
2021-04-21 19:14:11 +00:00
string containerNameFullPath = resolver . generateName ( modulle , containerEntity ) ;
2021-04-01 18:26:17 +00:00
gprintln ( "Entity \"" ~ fullPath
~ "\" is allowed to be defined within container \""
~ containerNameFullPath ~ "\"" ) ;
2021-04-01 13:26:17 +00:00
/ * *
* Check if this Entity is a Container , if so , then
* apply the same round of checks within it
* /
2021-04-01 18:26:17 +00:00
Container possibleContainerEntity = cast ( Container ) entity ;
if ( possibleContainerEntity )
2021-04-01 13:26:17 +00:00
{
2021-04-29 15:54:26 +00:00
checkContainerCollision ( possibleContainerEntity ) ;
2021-04-01 13:26:17 +00:00
}
}
2021-04-01 13:21:10 +00:00
}
}
/ * *
* Returns container members in order of
* Clazz , Function , Variable
* /
private Entity [ ] getContainerMembers ( Container c )
{
/* Entities */
Entity [ ] entities ;
/* Get all classes */
2021-04-01 18:26:17 +00:00
foreach ( Statement statement ; c . getStatements ( ) )
2021-04-01 13:21:10 +00:00
{
2021-04-01 18:26:17 +00:00
if ( statement ! is null & & cast ( Clazz ) statement )
2021-04-01 13:21:10 +00:00
{
2021-04-01 18:26:17 +00:00
entities ~ = cast ( Clazz ) statement ;
2021-04-01 13:21:10 +00:00
}
}
/* Get all functions */
2021-04-01 18:26:17 +00:00
foreach ( Statement statement ; c . getStatements ( ) )
2021-04-01 13:21:10 +00:00
{
2021-04-01 18:26:17 +00:00
if ( statement ! is null & & cast ( Function ) statement )
2021-04-01 13:21:10 +00:00
{
2021-04-01 18:26:17 +00:00
entities ~ = cast ( Function ) statement ;
2021-04-01 13:21:10 +00:00
}
}
/* Get all variables */
2021-04-01 18:26:17 +00:00
foreach ( Statement statement ; c . getStatements ( ) )
2021-04-01 13:21:10 +00:00
{
2021-04-01 18:26:17 +00:00
if ( statement ! is null & & cast ( Variable ) statement )
2021-04-01 13:21:10 +00:00
{
2021-04-01 18:26:17 +00:00
entities ~ = cast ( Variable ) statement ;
2021-04-01 13:21:10 +00:00
}
}
return entities ;
}
2021-04-01 13:49:02 +00:00
/ * *
* Finds the first occurring Entity with the provided
* name based on Classes being searched , then Functions
* and lastly Variables
* /
2021-04-01 13:21:10 +00:00
public Entity findPrecedence ( Container c , string name )
{
2021-04-01 18:26:17 +00:00
foreach ( Entity entity ; getContainerMembers ( c ) )
2021-04-01 13:21:10 +00:00
{
/* If we find matching entity names */
2021-04-01 18:26:17 +00:00
if ( cmp ( entity . getName ( ) , name ) = = 0 )
2021-04-01 13:21:10 +00:00
{
return entity ;
}
}
return null ;
}
2021-04-01 06:47:44 +00:00
2021-03-30 18:05:16 +00:00
/ * *
* Starting from a Container c this makes sure
* that all classes defined within that container
* do no clash name wise
2021-04-01 13:21:10 +00: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 18:05:16 +00:00
* /
private void checkClassNames ( Container c )
2021-03-30 16:51:32 +00:00
{
2021-04-21 19:14:11 +00: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 16:51:32 +00:00
/* Get all types (Clazz so far) */
Clazz [ ] classTypes ;
2021-04-01 18:26:17 +00:00
foreach ( Statement statement ; c . getStatements ( ) )
2021-03-30 16:51:32 +00:00
{
2021-04-01 18:26:17 +00:00
if ( statement ! is null & & cast ( Clazz ) statement )
2021-03-30 16:51:32 +00:00
{
2021-04-01 18:26:17 +00:00
classTypes ~ = cast ( Clazz ) statement ;
2021-03-30 16:51:32 +00:00
}
}
/* Declare each type */
2021-04-01 18:26:17 +00:00
foreach ( Clazz clazz ; classTypes )
2021-03-30 16:51:32 +00:00
{
2021-04-01 06:51:55 +00:00
// gprintln("Name: "~resolver.generateName(modulle, clazz));
2021-03-30 16:51:32 +00: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 18:05:16 +00: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 16:51:32 +00:00
* /
2021-04-01 18:26:17 +00:00
if ( resolver . resolveUp ( c , clazz . getName ( ) ) ! = clazz )
2021-03-30 16:51:32 +00:00
{
2021-04-01 18:26:17 +00: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 19:14:11 +00:00
modulle , containerEntity ) ~ "\"" ) ;
2021-03-30 16:51:32 +00:00
}
else
{
2021-03-30 19:29:57 +00:00
/* Get the current container's parent container */
2021-04-21 19:14:11 +00:00
Container parentContainer = containerEntity . parentOf ( ) ;
2021-03-30 19:29:57 +00:00
2021-03-30 19:54:27 +00:00
/* Don't allow a class to be named after it's container */
// if(!parentContainer)
// {
2021-04-21 19:14:11 +00:00
if ( cmp ( containerEntity . getName ( ) , clazz . getName ( ) ) = = 0 )
2021-04-01 06:56:06 +00:00
{
2021-04-01 18:26:17 +00:00
Parser . expect ( "Class \"" ~ resolver . generateName ( modulle ,
clazz ) ~ "\" cannot be defined within container with same name, \"" ~ resolver . generateName (
2021-04-21 19:14:11 +00:00
modulle , containerEntity ) ~ "\"" ) ;
2021-04-01 06:56:06 +00:00
}
2021-04-01 13:21:10 +00:00
/* TODO: Loop througn Container ENtitys here */
/* Make sure that when we call findPrecedence(entity) == current entity */
2021-03-30 19:54:27 +00:00
// }
2021-03-30 19:29:57 +00:00
2021-03-30 19:36:35 +00: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 19:29:57 +00:00
2021-03-30 19:36:35 +00:00
// if(!clazzAbove)
// {
2021-03-30 19:29:57 +00:00
2021-03-30 19:36:35 +00:00
// }
// else
// {
// Parser.expect("Name in use abpve us, bad"~to!(string)(clazz));
// }
2021-03-30 19:29:57 +00: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 18:05:16 +00: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 18:26:17 +00:00
2021-03-30 18:05:16 +00:00
// Parser.expect("Class with name "~clazz.getName()~" defined in class "~c.getName());
2021-04-01 18:26:17 +00:00
2021-03-30 18:05:16 +00:00
// }
2021-03-30 16:51:32 +00:00
}
}
/ * *
* TODO : Now we should loop through each class and do the same
* so we have all types defined
* /
2021-04-01 12:09:30 +00:00
//gprintln("Defined classes: "~to!(string)(Program.getAllOf(new Clazz(""), cast(Statement[])marked)));
2021-04-01 18:26:17 +00:00
2021-03-30 16:51:32 +00: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 18:26:17 +00:00
foreach ( Clazz clazz ; classTypes )
2021-03-30 16:51:32 +00:00
{
2021-04-01 18:26:17 +00:00
gprintln ( "Check recursive " ~ to ! ( string ) ( clazz ) , DebugType . WARNING ) ;
2021-03-30 18:05:16 +00:00
2021-03-30 16:51:32 +00:00
/* Check the current class's types within */
2021-03-30 18:05:16 +00:00
checkClassNames ( clazz ) ;
2021-03-30 16:51:32 +00:00
2021-03-30 19:07:04 +00:00
// checkClassInherit(clazz);
2021-03-30 16:51:32 +00: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-06-08 08:52:22 +00:00
// /* Test name colliding with container name (1/3) [module] */
// unittest
// {
// import std.file;
// import std.stdio;
// import compiler.lexer;
// import compiler.parsing.core;
// 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();
// /* 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);
// }
// }
// /* Test name colliding with container name (2/3) [module, nested collider] */
// unittest
// {
// import std.file;
// import std.stdio;
// import compiler.lexer;
// import compiler.parsing.core;
// 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();
// /* 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);
// }
// }
// /* Test name colliding with container name (3/3) [container (non-module), nested collider] */
// unittest
// {
// import std.file;
// import std.stdio;
// import compiler.lexer;
// import compiler.parsing.core;
// string sourceFile = "source/tlang/testing/collide_container_non_module.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, "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);
// }
// }
// /* Test name colliding with member */
// unittest
// {
// import std.file;
// import std.stdio;
// import compiler.lexer;
// import compiler.parsing.core;
// string sourceFile = "source/tlang/testing/collide_member.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 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);
// }
// }
// /* Test name colliding with member (check that the member defined is class (precendence test)) */
// unittest
// {
// import std.file;
// import std.stdio;
// import compiler.lexer;
// import compiler.parsing.core;
// string sourceFile = "source/tlang/testing/precedence_collision_test.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 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);
// }
// }
// /* Test name colliding with container name (1/2) */
// unittest
// {
// import std.file;
// import std.stdio;
// import compiler.lexer;
// import compiler.parsing.core;
// string sourceFile = "source/tlang/testing/collide_container.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();
// /* 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);
// }
// }
// unittest
// {
// /* TODO: Add some unit tests */
// import std.file;
// import std.stdio;
// import compiler.lexer;
// import compiler.parsing.core;
// // isUnitTest = true;
// string sourceFile = "source/tlang/testing/basic1.t";
// gprintln("Reading source file '" ~ sourceFile ~ "' ...");
// 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();
// gprintln("Performing tokenization on '" ~ sourceFile ~ "' ...");
// /* TODO: Open source file */
// string sourceCode = cast(string) fileBytes;
// // string sourceCode = "hello \"world\"|| ";
// //string sourceCode = "hello \"world\"||"; /* TODO: Implement this one */
// // string sourceCode = "hello;";
// Lexer currentLexer = new Lexer(sourceCode);
// currentLexer.performLex();
2021-04-01 18:52:36 +00:00
2021-03-24 21:19:40 +00:00
2021-06-08 08:52:22 +00:00
// gprintln("Collected " ~ to!(string)(currentLexer.getTokens()));
// gprintln("Parsing tokens...");
// Parser parser = new Parser(currentLexer.getTokens());
// Module modulle = parser.parse();
// gprintln("Type checking and symbol resolution...");
// try
// {
// TypeChecker typeChecker = new TypeChecker(modulle);
// }
// // catch(CollidingNameException e)
// // {
// // gprintln(e.msg, DebugType.ERROR);
// // //gprintln("Stack trace:\n"~to!(string)(e.info));
// // }
// catch (TypeCheckerException e)
// {
// gprintln(e.msg, DebugType.ERROR);
// }
// /* Test first-level resolution */
// // assert(cmp(typeChecker.isValidEntity(modulle.getStatements(), "clazz1").getName(), "clazz1")==0);
// // /* Test n-level resolution */
// // assert(cmp(typeChecker.isValidEntity(modulle.getStatements(), "clazz_2_1.clazz_2_2").getName(), "clazz_2_2")==0);
// // assert(cmp(typeChecker.isValidEntity(modulle.getStatements(), "clazz_2_1.clazz_2_2.j").getName(), "j")==0);
// // assert(cmp(typeChecker.isValidEntity(modulle.getStatements(), "clazz_2_1.clazz_2_2.clazz_2_2_1").getName(), "clazz_2_2_1")==0);
// // assert(cmp(typeChecker.isValidEntity(modulle.getStatements(), "clazz_2_1.clazz_2_2").getName(), "clazz_2_2")==0);
// // /* Test invalid access to j treating it as a Container (whilst it is a Variable) */
// // assert(typeChecker.isValidEntity(modulle.getStatements(), "clazz_2_1.clazz_2_2.j.p") is null);
// }