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-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 20:06:30 +01:00
|
|
|
writeln("Got globals: "~to!(string)(Program.getAllOf(new Variable(null, null), modulle.getStatements())));
|
|
|
|
writeln("Got functions: "~to!(string)(Program.getAllOf(new Function(null, null, null, null), modulle.getStatements())));
|
|
|
|
writeln("Got classes: "~to!(string)(Program.getAllOf(new Clazz(null), modulle.getStatements())));
|
2021-03-21 20:01:28 +00:00
|
|
|
|
2021-03-24 21:19:40 +00:00
|
|
|
// nameResolution;
|
|
|
|
// writeln("Res:",isValidEntity(program.getStatements(), "clazz1"));
|
|
|
|
// writeln("Res:",isValidEntity(program.getStatements(), "clazz_2_1.clazz_2_2"));
|
|
|
|
|
2021-03-30 08:56:37 +01:00
|
|
|
|
|
|
|
/* Test getEntity on Module */
|
2021-03-30 20:54:27 +01:00
|
|
|
// gprintln("getEntity: myModule.x: "~to!(string)(getEntity(modulle, "myModule.x")));
|
|
|
|
// gprintln("getEntity: x: "~to!(string)(getEntity(modulle, "x")));
|
2021-03-30 08:56:37 +01:00
|
|
|
|
2021-03-30 20:54:27 +01:00
|
|
|
// /* Test getEntity on Class */
|
|
|
|
// Container clazzEntity = cast(Container)getEntity(modulle, "clazz1");
|
|
|
|
// gprintln("getEntity: clazz1.k: "~to!(string)(getEntity(clazzEntity, "clazz1.k")));
|
|
|
|
// gprintln("getEntity: k: "~to!(string)(getEntity(clazzEntity, "k")));
|
|
|
|
// clazzEntity = cast(Container)getEntity(modulle, "myModule.clazz1");
|
|
|
|
// gprintln("getEntity: clazz1.k: "~to!(string)(getEntity(clazzEntity, "clazz1.k")));
|
|
|
|
// gprintln("getEntity: k: "~to!(string)(getEntity(clazzEntity, "myModule.clazz1.k")));
|
2021-03-30 08:56:37 +01:00
|
|
|
|
2021-03-27 13:27:14 +00:00
|
|
|
//process();
|
2021-03-29 20:06:30 +01:00
|
|
|
beginCheck();
|
2021-03-29 18:13:39 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private string[] validNames;
|
|
|
|
|
2021-03-29 20:06:30 +01:00
|
|
|
private bool declareName()
|
2021-03-29 18:13:39 +01:00
|
|
|
{
|
2021-03-29 20:06:30 +01:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
private bool isName(string nameTest)
|
|
|
|
{
|
|
|
|
foreach(string name; validNames)
|
2021-03-29 18:13:39 +01:00
|
|
|
{
|
2021-03-29 20:06:30 +01:00
|
|
|
if(cmp(nameTest, name) == 0)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void declareName(string name)
|
|
|
|
{
|
|
|
|
validNames ~= name;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void beginCheck()
|
|
|
|
{
|
|
|
|
// checkIt(modulle.getStatements(), modulle.getName());
|
2021-03-30 08:56:37 +01:00
|
|
|
checkIt(modulle);
|
2021-03-29 20:06:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
private void checkClass(Clazz clazz)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-03-30 09:09:55 +01:00
|
|
|
/* List of known (marked) objects */
|
|
|
|
private Entity[] marked;
|
2021-03-29 20:06:30 +01:00
|
|
|
|
2021-03-30 09:09:55 +01:00
|
|
|
public bool isMarkedEntity(Entity entityTest)
|
2021-03-29 20:06:30 +01:00
|
|
|
{
|
2021-03-30 09:09:55 +01:00
|
|
|
foreach(Entity entity; marked)
|
2021-03-29 20:06:30 +01:00
|
|
|
{
|
|
|
|
if(entity == entityTest)
|
|
|
|
{
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2021-03-30 09:09:55 +01:00
|
|
|
public void markEntity(Entity entity)
|
2021-03-29 20:06:30 +01:00
|
|
|
{
|
2021-03-30 09:09:55 +01:00
|
|
|
marked ~= entity;
|
2021-03-29 20:06:30 +01:00
|
|
|
}
|
|
|
|
|
2021-03-29 20:26:16 +01:00
|
|
|
/* Returns index or 64 1 bits */
|
|
|
|
private ulong getStatementIndex(Statement[] statements, Statement statement)
|
|
|
|
{
|
|
|
|
for(ulong i = 0; i < statements.length; i++)
|
|
|
|
{
|
|
|
|
if(statement == statements[i])
|
|
|
|
{
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2021-03-30 15:20:47 +01:00
|
|
|
|
|
|
|
public bool isMarkedByName(Container c, string name)
|
|
|
|
{
|
|
|
|
return isMarkedEntity(getEntity(c, name));
|
|
|
|
}
|
|
|
|
|
2021-03-31 11:54:34 +01:00
|
|
|
|
2021-03-30 22:22:37 +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;
|
|
|
|
|
|
|
|
foreach(Statement statement; c.getStatements())
|
|
|
|
{
|
|
|
|
if(statement !is null && cast(Clazz)statement)
|
|
|
|
{
|
|
|
|
classTypes ~= cast(Clazz)statement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Process each Clazz */
|
|
|
|
foreach(Clazz clazz; classTypes)
|
|
|
|
{
|
|
|
|
/* Get the current class's parent */
|
|
|
|
string[] parentClasses = clazz.getInherit();
|
|
|
|
gprintln("Class: "~clazz.getName()~": ParentInheritList: "~to!(string)(parentClasses));
|
|
|
|
|
|
|
|
/* Try resolve all of these */
|
|
|
|
foreach(string parent; parentClasses)
|
|
|
|
{
|
|
|
|
/* 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);
|
|
|
|
|
|
|
|
/* If the name is rooted resolve the name top-down */
|
|
|
|
if(dotPath.length > 1)
|
|
|
|
{
|
|
|
|
namedEntity = getEntity(modulle, parent);
|
|
|
|
}
|
|
|
|
/* If the name is not rooted resolve the name bottom up */
|
|
|
|
else
|
|
|
|
{
|
2021-03-31 11:54:34 +01:00
|
|
|
namedEntity = resolver.resolveUp(c, parent);
|
2021-03-30 21:04:11 +01:00
|
|
|
}
|
|
|
|
|
2021-03-31 11:54:34 +01:00
|
|
|
namedEntity=resolver.resolveBest(c, parent);
|
2021-03-30 20:07:04 +01:00
|
|
|
|
|
|
|
/* If the entity exists */
|
|
|
|
if(namedEntity)
|
|
|
|
{
|
|
|
|
/* Check if it is a Class, if so non-null */
|
|
|
|
Clazz parentEntity = cast(Clazz)namedEntity;
|
|
|
|
|
|
|
|
/* Only inherit from class or (TODO: interfaces) */
|
|
|
|
if(parentEntity)
|
|
|
|
{
|
|
|
|
/* Make sure it is not myself */
|
|
|
|
if(parentEntity != clazz)
|
|
|
|
{
|
|
|
|
/* TODO: Add loop checking here */
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Parser.expect("Cannot inherit from self");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* Error */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Parser.expect("Can only inherit from classes");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/* If the entity doesn't exist then it is an error */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
Parser.expect("Could not find any entity named "~parent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-03-30 19:05:16 +01:00
|
|
|
|
2021-03-30 20:07:04 +01:00
|
|
|
/* Once processing is done, apply recursively */
|
|
|
|
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-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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Starting from a Container c this makes sure
|
|
|
|
* that all classes defined within that container
|
|
|
|
* do no clash name wise
|
|
|
|
*/
|
|
|
|
private void checkClassNames(Container c)
|
2021-03-30 17:51:32 +01:00
|
|
|
{
|
|
|
|
/* Get all types (Clazz so far) */
|
|
|
|
Clazz[] classTypes;
|
|
|
|
|
|
|
|
foreach(Statement statement; c.getStatements())
|
|
|
|
{
|
|
|
|
if(statement !is null && cast(Clazz)statement)
|
|
|
|
{
|
|
|
|
classTypes ~= cast(Clazz)statement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Declare each type */
|
|
|
|
foreach(Clazz clazz; classTypes)
|
|
|
|
{
|
|
|
|
/**
|
|
|
|
* 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-03-30 20:54:27 +01:00
|
|
|
gprintln("ddddsffsad");
|
2021-03-31 11:54:34 +01:00
|
|
|
if(resolver.resolveUp(c, clazz.getName()) != clazz)
|
2021-03-30 17:51:32 +01:00
|
|
|
{
|
2021-03-30 19:05:16 +01:00
|
|
|
Parser.expect("Error defining class with same name in same container: ClassTried: "~clazz.getName()~", Container: "~c.getName());
|
2021-03-30 17:51:32 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-03-30 20:54:27 +01:00
|
|
|
gprintln("dshfgjhdsj");
|
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-03-30 20:29:57 +01:00
|
|
|
if(cmp(c.getName(), clazz.getName()) == 0)
|
|
|
|
{
|
2021-03-30 20:54:27 +01:00
|
|
|
Parser.expect("Class cannot be defined with same name as containing entity");
|
2021-03-30 20:29:57 +01:00
|
|
|
}
|
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()))
|
|
|
|
// {
|
|
|
|
|
|
|
|
// Parser.expect("Class with name "~clazz.getName()~" defined in class "~c.getName());
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
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
|
|
|
|
*/
|
|
|
|
gprintln("Defined classes: "~to!(string)(Program.getAllOf(new Clazz(""), cast(Statement[])marked)));
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
|
|
|
*/
|
|
|
|
foreach(Clazz clazz; classTypes)
|
|
|
|
{
|
2021-03-30 19:05:16 +01:00
|
|
|
gprintln("Check recursive "~to!(string)(clazz), DebugType.WARNING);
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2021-03-30 19:05:16 +01:00
|
|
|
|
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: ")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO clazz_21_211 , crashes */
|
|
|
|
|
2021-03-30 08:56:37 +01:00
|
|
|
private void checkIt(Container c)
|
2021-03-29 20:06:30 +01:00
|
|
|
{
|
2021-03-30 08:56:37 +01:00
|
|
|
//gprintln("Processing at path/level: "~path, DebugType.WARNING);
|
|
|
|
|
2021-03-30 17:51:32 +01:00
|
|
|
/* First we define types (so classes) */
|
2021-03-30 20:54:27 +01:00
|
|
|
gprintln("dd");
|
2021-03-30 19:05:16 +01:00
|
|
|
checkClasses(c);
|
2021-03-30 17:51:32 +01:00
|
|
|
|
2021-03-30 08:56:37 +01:00
|
|
|
Statement[] statements = c.getStatements();
|
|
|
|
string path = c.getName();
|
2021-03-29 20:06:30 +01:00
|
|
|
|
|
|
|
foreach(Statement statement; statements)
|
|
|
|
{
|
|
|
|
/* If the statement is a COntainer */
|
|
|
|
if(cast(Container)statement)
|
|
|
|
{
|
|
|
|
Container container = cast(Container)statement;
|
|
|
|
string name = path~"."~container.getName();
|
|
|
|
/* TODO: Implement */
|
|
|
|
//checkIt()
|
|
|
|
}
|
2021-03-29 18:13:39 +01:00
|
|
|
/* If the statement is a variable declaration */
|
2021-03-29 20:06:30 +01:00
|
|
|
else if(cast(Variable)statement)
|
2021-03-29 18:13:39 +01:00
|
|
|
{
|
2021-03-29 20:06:30 +01:00
|
|
|
Variable variable = cast(Variable)statement;
|
|
|
|
gprintln("Declaring variable"~variable.getName());
|
|
|
|
|
2021-03-30 17:02:13 +01:00
|
|
|
/**
|
|
|
|
* To check if a name is taken we check if the current one equals the
|
|
|
|
* first match (if so, then declare, if not, then taken)
|
|
|
|
*/
|
|
|
|
if(getEntity(c, variable.getName()) != variable)
|
|
|
|
{
|
|
|
|
Parser.expect("Duplicate name tried to be declared");
|
|
|
|
}
|
|
|
|
|
2021-03-29 20:06:30 +01:00
|
|
|
/* Check if this variable has an expression, if so check that */
|
|
|
|
if(variable.getAssignment())
|
|
|
|
{
|
|
|
|
VariableAssignment varAssign = variable.getAssignment();
|
|
|
|
|
2021-03-30 17:51:32 +01:00
|
|
|
/* TODO: Do what D does, only allow assignment of constants */
|
|
|
|
/* TODO: For assignments at global only allow constants */
|
|
|
|
|
2021-03-29 20:06:30 +01:00
|
|
|
Expression expression = varAssign.getExpression();
|
2021-03-30 09:09:55 +01:00
|
|
|
string type = expression.evaluateType(this, c);
|
|
|
|
|
|
|
|
if(!type.length)
|
|
|
|
{
|
|
|
|
Parser.expect("Expression type fetch failed: "~variable.getName());
|
|
|
|
}
|
|
|
|
gprintln("ExpressionTYpe in VarAssign: "~type);
|
2021-03-30 17:02:13 +01:00
|
|
|
|
|
|
|
|
2021-03-29 20:06:30 +01:00
|
|
|
}
|
2021-03-30 17:02:13 +01:00
|
|
|
|
|
|
|
/* Set the variable as declared */
|
|
|
|
markEntity(variable);
|
2021-03-29 18:13:39 +01:00
|
|
|
}
|
2021-03-30 17:51:32 +01:00
|
|
|
/* If the statement is a function */
|
|
|
|
else if(cast(Function)statement)
|
|
|
|
{
|
|
|
|
Function func = cast(Function)statement;
|
|
|
|
}
|
2021-03-29 18:13:39 +01:00
|
|
|
}
|
2021-03-27 13:27:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* List of currently declared variables
|
|
|
|
*/
|
|
|
|
private Entity[] declaredVars;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialization order
|
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Example:
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* int a;
|
|
|
|
* int b = a;
|
|
|
|
* int c = b;
|
|
|
|
* Reversing must not work
|
|
|
|
*
|
|
|
|
* Only time it can is if the path is to something in a class as those should
|
|
|
|
* be initialized all before variables
|
|
|
|
*/
|
|
|
|
private void process(Statement[] statements)
|
|
|
|
{
|
|
|
|
/* Go through each entity and check them */
|
|
|
|
|
|
|
|
/* TODO: Starting with x, if `int x = clacc.class.class.i` */
|
|
|
|
/* TODO: Then we getPath from the assignment aexpressiona nd eval it */
|
|
|
|
/**
|
|
|
|
* TODO: The variable there cannot rely on x without it being initted, hence
|
|
|
|
* need for global list of declared variables
|
|
|
|
*/
|
2021-03-24 21:19:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Test name resolution */
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
//assert()
|
|
|
|
}
|
|
|
|
|
|
|
|
/* TODO: We need a duplicate detector, maybe do this in Parser, in `parseBody` */
|
|
|
|
|
2021-03-29 21:20:47 +01:00
|
|
|
|
|
|
|
// public Entity isValidEntityTop(string path)
|
|
|
|
// {
|
|
|
|
// /* module.x same as x */
|
|
|
|
// if(cmp(path, "") == 0)
|
|
|
|
// }
|
|
|
|
|
2021-03-29 21:27:48 +01:00
|
|
|
|
|
|
|
/* TODO: Do elow functio n */
|
|
|
|
/* TODO: I also need something to get all entities with same name */
|
|
|
|
public bool entityCmp(Entity lhs, Entity rhs)
|
|
|
|
{
|
|
|
|
/* TODO: Depends on Entity */
|
|
|
|
/* If lhs and rhs are variables then if lhs came before rhs this is true */
|
2021-03-30 08:56:37 +01:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a Container like a Module or Class and a path
|
|
|
|
* this will search from said container to find the Entity
|
|
|
|
* at the given path
|
|
|
|
*
|
|
|
|
* If you give it class_1 and path class_1.x or x
|
|
|
|
* they both should return the same Entity
|
|
|
|
*/
|
|
|
|
public Entity getEntity(Container container, string path)
|
|
|
|
{
|
|
|
|
/* Get the Container's name */
|
|
|
|
string containerName = container.getName();
|
|
|
|
|
|
|
|
/* Check to see if the first item is the container's name */
|
|
|
|
string[] pathItems = split(path, '.');
|
|
|
|
if(cmp(pathItems[0], containerName) == 0)
|
|
|
|
{
|
|
|
|
/* If so, then remove it */
|
|
|
|
path = path[indexOf(path, '.')+1..path.length];
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Search for the Entity */
|
|
|
|
return isValidEntity(container.getStatements(), path);
|
2021-03-29 21:27:48 +01:00
|
|
|
}
|
|
|
|
|
2021-03-24 21:19:40 +00:00
|
|
|
/* Path: clazz_2_1.class_2_2 */
|
|
|
|
public Entity isValidEntity(Statement[] startingPoint, string path)
|
|
|
|
{ /* The entity found with the matching name at the end of the path */
|
|
|
|
// Entity foundEntity;
|
|
|
|
|
|
|
|
/* Go through each Statement and look for Entity's */
|
|
|
|
foreach(Statement curStatement; startingPoint)
|
|
|
|
{
|
|
|
|
/* Only look for Entitys */
|
|
|
|
if(cast(Entity)curStatement !is null)
|
|
|
|
{
|
|
|
|
/* Current entity */
|
|
|
|
Entity curEntity = cast(Entity)curStatement;
|
|
|
|
|
|
|
|
/* Make sure the root of path matches current entity */
|
|
|
|
string[] name = split(path, ".");
|
|
|
|
|
|
|
|
/* If root does not match current entity, skip */
|
|
|
|
if(cmp(name[0], curEntity.getName()) != 0)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// writeln("warren g had to regulate");
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the name fully matches this entity's name
|
|
|
|
*
|
|
|
|
* If so, return it, a match has been found
|
|
|
|
*/
|
|
|
|
if(cmp(path, curEntity.getName()) == 0)
|
|
|
|
{
|
|
|
|
return curEntity;
|
|
|
|
}
|
|
|
|
/**
|
|
|
|
* Or recurse
|
|
|
|
*/
|
|
|
|
else
|
|
|
|
{
|
|
|
|
string newPath = path[indexOf(path, '.')+1..path.length];
|
|
|
|
|
|
|
|
/* In this case it must be some sort of container */
|
|
|
|
if(cast(Container)curEntity)
|
|
|
|
{
|
|
|
|
Container curContainer = cast(Container)curEntity;
|
|
|
|
|
|
|
|
/* Get statements */
|
|
|
|
Statement[] containerStatements = curContainer.getStatements();
|
|
|
|
|
|
|
|
/* TODO: Consider accessors? Here, Parser, where? */
|
|
|
|
|
|
|
|
return isValidEntity(containerStatements, newPath);
|
|
|
|
}
|
|
|
|
/* If not, error , semantics */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null;
|
2021-03-21 19:43:01 +00:00
|
|
|
}
|
2021-03-23 19:35:13 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* This function will walk, recursively, through
|
|
|
|
* each Statement at the top-level and generate
|
|
|
|
* names of declared items in a global array
|
|
|
|
*
|
|
|
|
* This is top-level, iterative then recursive within
|
|
|
|
* each iteration
|
|
|
|
*
|
|
|
|
* The point of this is to know of all symbols
|
|
|
|
* that exist so that we can do a second pass
|
|
|
|
* and see if symbols in use (declaration does
|
|
|
|
* not count as "use") are infact valid references
|
|
|
|
*/
|
|
|
|
public void nameResolution()
|
|
|
|
{
|
|
|
|
string[] names;
|
|
|
|
|
2021-03-29 20:06:30 +01:00
|
|
|
foreach(Statement statement; Program.getAllOf(new Statement(), modulle.getStatements()))
|
2021-03-23 19:35:13 +00:00
|
|
|
{
|
|
|
|
/* TODO: Add container name */
|
2021-03-24 21:19:40 +00:00
|
|
|
/* TODO: Make sure all Entity type */
|
|
|
|
string containerName = (cast(Entity)statement).getName();
|
|
|
|
names ~= containerName;
|
|
|
|
string[] receivedNameSet = resolveNames(containerName, statement);
|
|
|
|
names ~= receivedNameSet;
|
2021-03-23 19:35:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-03-24 21:19:40 +00:00
|
|
|
private string[] resolveNames(string root, Statement statement)
|
2021-03-23 19:35:13 +00:00
|
|
|
{
|
2021-03-24 21:19:40 +00:00
|
|
|
/* If the statement is a variable then return */
|
|
|
|
if(typeid(statement) == typeid(Variable))
|
|
|
|
{
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
/* If it is a class */
|
|
|
|
else if(typeid(statement) == typeid(Clazz))
|
|
|
|
{
|
|
|
|
/* Get class's identifiers */
|
|
|
|
|
|
|
|
}
|
2021-03-23 19:35:13 +00:00
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void check()
|
|
|
|
{
|
|
|
|
checkDuplicateTopLevel();
|
|
|
|
|
|
|
|
/* TODO: Process globals */
|
|
|
|
/* TODO: Process classes */
|
|
|
|
/* TODO: Process functions */
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Ensures that at the top-level there are no duplicate names
|
|
|
|
*/
|
|
|
|
private bool checkDuplicateTopLevel()
|
|
|
|
{
|
|
|
|
import misc.utils;
|
|
|
|
|
|
|
|
/* List of names travsersed so far */
|
|
|
|
string[] names;
|
|
|
|
|
|
|
|
/* Add all global variables */
|
2021-03-29 20:06:30 +01:00
|
|
|
foreach(Variable variable; Program.getAllOf(new Variable(null, null), modulle.getStatements()))
|
2021-03-23 19:35:13 +00:00
|
|
|
{
|
|
|
|
string name = variable.getName();
|
|
|
|
|
|
|
|
if(isPresent(names, name))
|
|
|
|
{
|
|
|
|
Parser.expect("Bruh duplicate var"~name);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
names ~= variable.getName();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
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";
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 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();
|
|
|
|
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
|
2021-03-29 20:06:30 +01:00
|
|
|
Module modulle = parser.parse();
|
2021-03-24 21:19:40 +00:00
|
|
|
|
2021-03-29 20:06:30 +01:00
|
|
|
TypeChecker typeChecker = new TypeChecker(modulle);
|
2021-03-24 21:19:40 +00:00
|
|
|
typeChecker.check();
|
|
|
|
|
|
|
|
/* Test first-level resolution */
|
2021-03-29 20:06:30 +01:00
|
|
|
assert(cmp(typeChecker.isValidEntity(modulle.getStatements(), "clazz1").getName(), "clazz1")==0);
|
2021-03-24 21:19:40 +00:00
|
|
|
|
|
|
|
/* Test n-level resolution */
|
2021-03-29 20:06:30 +01:00
|
|
|
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
|
|
|
|
|
|
|
/* Test invalid access to j treating it as a Container (whilst it is a Variable) */
|
2021-03-29 20:06:30 +01:00
|
|
|
assert(typeChecker.isValidEntity(modulle.getStatements(), "clazz_2_1.clazz_2_2.j.p") is null);
|
2021-03-24 21:19:40 +00:00
|
|
|
|
|
|
|
|
2021-03-21 19:43:01 +00:00
|
|
|
}
|