2021-03-30 16:31:37 +01:00
|
|
|
module compiler.typecheck.core;
|
2021-03-21 19:43:01 +00:00
|
|
|
|
2021-03-30 16:35:16 +01: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 18:13:39 +01:00
|
|
|
import gogga;
|
2021-03-30 17:02:13 +01:00
|
|
|
import compiler.parsing.core;
|
2021-03-31 11:54:34 +01:00
|
|
|
import compiler.typecheck.resolution;
|
2021-04-01 13:38:20 +01:00
|
|
|
import compiler.typecheck.exceptions;
|
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-03-23 19:35:13 +00:00
|
|
|
|
2021-03-29 18:13:39 +01: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);
|
|
|
|
|
|
|
|
/* TODO: Process class ? vars funcs ?*/
|
2021-04-07 12:11:36 +01:00
|
|
|
}
|
|
|
|
|
2021-04-15 16:26:14 +01:00
|
|
|
/* TODO: Idk, maybe change */
|
|
|
|
/* TODO: Just using for `checkDefinitionTypes` */
|
|
|
|
|
|
|
|
private void checkDefinitionTypes(Container c)
|
|
|
|
{
|
|
|
|
/* Check variable declarations */
|
|
|
|
Variable[] variables;
|
|
|
|
|
|
|
|
foreach (Statement statement; c.getStatements())
|
|
|
|
{
|
|
|
|
if (statement !is null && cast(Variable) statement)
|
|
|
|
{
|
|
|
|
variables ~= cast(Variable) statement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check function definitions */
|
|
|
|
Function[] functions;
|
|
|
|
|
|
|
|
foreach (Statement statement; c.getStatements())
|
|
|
|
{
|
|
|
|
if (statement !is null && cast(Function) statement)
|
|
|
|
{
|
|
|
|
functions ~= cast(Function) statement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check class inheritance types */
|
|
|
|
Clazz[] classes;
|
|
|
|
|
|
|
|
foreach (Statement statement; c.getStatements())
|
|
|
|
{
|
|
|
|
if (statement !is null && cast(Clazz) statement)
|
|
|
|
{
|
|
|
|
classes ~= cast(Clazz) statement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-01 14:39:06 +01:00
|
|
|
public void beginCheck()
|
2021-03-29 20:06:30 +01:00
|
|
|
{
|
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-01 14:49:02 +01:00
|
|
|
checkContainer(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
|
|
|
}
|
|
|
|
|
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
|
|
|
*/
|
|
|
|
private void checkContainer(Container c)
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* 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-01 19:52:36 +01:00
|
|
|
else if (cmp(c.getName(), entity.getName()) == 0)
|
2021-04-01 14:21:10 +01:00
|
|
|
{
|
2021-04-01 14:25:55 +01:00
|
|
|
throw new CollidingNameException(this, c, 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);
|
|
|
|
string containerNameFullPath = resolver.generateName(modulle, c);
|
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
|
|
|
{
|
|
|
|
checkContainer(possibleContainerEntity);
|
|
|
|
}
|
|
|
|
}
|
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-04-01 19:26:17 +01:00
|
|
|
if (statement !is null && cast(Clazz) statement)
|
2021-04-01 14:21:10 +01:00
|
|
|
{
|
2021-04-01 19:26:17 +01:00
|
|
|
entities ~= cast(Clazz) statement;
|
2021-04-01 14:21:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get all functions */
|
2021-04-01 19:26:17 +01:00
|
|
|
foreach (Statement statement; c.getStatements())
|
2021-04-01 14:21:10 +01:00
|
|
|
{
|
2021-04-01 19:26:17 +01:00
|
|
|
if (statement !is null && cast(Function) statement)
|
2021-04-01 14:21:10 +01:00
|
|
|
{
|
2021-04-01 19:26:17 +01:00
|
|
|
entities ~= cast(Function) statement;
|
2021-04-01 14:21:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Get all variables */
|
2021-04-01 19:26:17 +01:00
|
|
|
foreach (Statement statement; c.getStatements())
|
2021-04-01 14:21:10 +01:00
|
|
|
{
|
2021-04-01 19:26:17 +01:00
|
|
|
if (statement !is null && cast(Variable) statement)
|
2021-04-01 14:21:10 +01:00
|
|
|
{
|
2021-04-01 19:26:17 +01:00
|
|
|
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
|
|
|
{
|
|
|
|
/* 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(
|
|
|
|
modulle, c) ~ "\"");
|
2021-03-30 17:51:32 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-03-30 20:29:57 +01:00
|
|
|
/* Get the current container's parent container */
|
|
|
|
Container parentContainer = c.parentOf();
|
|
|
|
|
2021-03-30 20:54:27 +01:00
|
|
|
/* Don't allow a class to be named after it's container */
|
|
|
|
// if(!parentContainer)
|
|
|
|
// {
|
2021-04-01 19:26:17 +01:00
|
|
|
if (cmp(c.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(
|
|
|
|
modulle, c) ~ "\"");
|
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-04-01 19:26:17 +01:00
|
|
|
/* Test name colliding with container name (1/3) [module] */
|
2021-04-01 14:30:56 +01:00
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.parsing.core;
|
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
string sourceFile = "source/tlang/testing/collide_container_module1.t";
|
2021-04-01 14:30:56 +01:00
|
|
|
|
2021-04-01 19:26:17 +01: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-04-01 14:30:56 +01:00
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
string sourceCode = cast(string) fileBytes;
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
2021-04-01 19:52:36 +01:00
|
|
|
currentLexer.performLex();
|
2021-04-01 14:39:06 +01:00
|
|
|
|
2021-04-01 19:26:17 +01: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, "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);
|
|
|
|
}
|
2021-04-01 14:30:56 +01:00
|
|
|
}
|
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
|
|
|
|
|
|
|
|
/* Test name colliding with container name (2/3) [module, nested collider] */
|
2021-04-01 14:30:56 +01:00
|
|
|
unittest
|
|
|
|
{
|
2021-04-01 19:26:17 +01:00
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.parsing.core;
|
|
|
|
|
2021-04-01 19:52:36 +01:00
|
|
|
string sourceFile = "source/tlang/testing/collide_container_module2.t";
|
2021-04-01 19:26:17 +01: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-04-01 14:30:56 +01:00
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
string sourceCode = cast(string) fileBytes;
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
2021-04-01 19:52:36 +01:00
|
|
|
currentLexer.performLex();
|
2021-04-01 19:26:17 +01: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, "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 */
|
2021-04-01 19:53:31 +01:00
|
|
|
assert(false);
|
2021-04-01 19:26:17 +01:00
|
|
|
}
|
|
|
|
catch (CollidingNameException e)
|
|
|
|
{
|
2021-04-01 20:00:48 +01:00
|
|
|
/* 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 */
|
2021-04-01 19:53:31 +01:00
|
|
|
assert(e.defined == container);
|
2021-04-01 19:26:17 +01:00
|
|
|
}
|
2021-04-01 14:30:56 +01:00
|
|
|
}
|
|
|
|
|
2021-04-01 20:08:37 +01:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-02 17:45:59 +01:00
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-01 14:30:56 +01:00
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
/* 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);
|
2021-04-01 19:52:36 +01:00
|
|
|
currentLexer.performLex();
|
2021-04-01 19:26:17 +01: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, "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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-04-01 14:30:56 +01:00
|
|
|
|
2021-03-24 21:19:40 +00:00
|
|
|
unittest
|
|
|
|
{
|
|
|
|
/* TODO: Add some unit tests */
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
2021-03-30 17:02:13 +01:00
|
|
|
import compiler.parsing.core;
|
2021-03-24 21:19:40 +00:00
|
|
|
|
|
|
|
// isUnitTest = true;
|
|
|
|
|
|
|
|
string sourceFile = "source/tlang/testing/basic1.t";
|
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
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);
|
2021-04-01 19:52:36 +01:00
|
|
|
currentLexer.performLex();
|
|
|
|
|
2021-03-24 21:19:40 +00:00
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
gprintln("Collected " ~ to!(string)(currentLexer.getTokens()));
|
2021-03-24 21:19:40 +00:00
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
gprintln("Parsing tokens...");
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
Module modulle = parser.parse();
|
2021-03-24 21:19:40 +00:00
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
gprintln("Type checking and symbol resolution...");
|
|
|
|
try
|
|
|
|
{
|
|
|
|
TypeChecker typeChecker = new TypeChecker(modulle);
|
2021-03-24 21:19:40 +00:00
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
}
|
|
|
|
// 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);
|
2021-03-24 21:19:40 +00:00
|
|
|
|
2021-04-01 19:26:17 +01:00
|
|
|
// /* 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);
|
|
|
|
|
|
|
|
}
|