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;
|
2022-12-11 15:33:18 +00:00
|
|
|
import compiler.typecheck.dependency.core;
|
|
|
|
import compiler.codegen.instruction;
|
|
|
|
import std.container.slist;
|
2022-12-19 13:37:55 +00:00
|
|
|
import std.algorithm : reverse;
|
2021-03-21 19:43:01 +00:00
|
|
|
|
|
|
|
/**
|
2021-03-24 21:19:40 +00:00
|
|
|
* The Parser only makes sure syntax
|
|
|
|
* is adhered to (and, well, partially)
|
|
|
|
* as it would allow string+string
|
|
|
|
* for example
|
|
|
|
*
|
2021-03-21 19:43:01 +00:00
|
|
|
*/
|
|
|
|
public final class TypeChecker
|
|
|
|
{
|
2021-03-29 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-03-23 19:35:13 +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-11-10 14:17:42 +00:00
|
|
|
|
2022-12-11 16:18:50 +00:00
|
|
|
// Allow Context class access to the type checker (used in Instruction where only Context is available)
|
|
|
|
Context.tc = this;
|
|
|
|
|
2022-12-12 08:58:58 +00:00
|
|
|
// Allow the SymbolMapper class to access the type checker
|
|
|
|
import compiler.codegen.mapper : SymbolMapper;
|
|
|
|
SymbolMapper.tc = this;
|
|
|
|
|
2021-11-10 14:17:42 +00:00
|
|
|
// DNodeGenerator.staticTC = this;
|
2022-12-11 16:18:50 +00:00
|
|
|
|
2021-11-10 14:17:42 +00:00
|
|
|
|
2021-06-09 17:45:57 +00:00
|
|
|
DNodeGenerator dNodeGenerator = new DNodeGenerator(this);
|
2023-01-20 14:43:04 +00:00
|
|
|
|
|
|
|
/* Generate the dependency tree */
|
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
|
|
|
|
2023-01-20 14:43:04 +00:00
|
|
|
/* Perform the linearization to the dependency tree */
|
|
|
|
rootNode.performLinearization();
|
|
|
|
|
2021-08-15 20:33:22 +00:00
|
|
|
/* Print the tree */
|
2023-01-20 14:43:04 +00:00
|
|
|
string tree = rootNode.getTree();
|
2021-08-15 20:23:17 +00:00
|
|
|
gprintln(tree);
|
|
|
|
|
2021-10-25 14:55:43 +00:00
|
|
|
/* Get the action-list (linearised bottom up graph) */
|
2023-01-20 14:43:04 +00:00
|
|
|
DNode[] actionList = rootNode.getLinearizedNodes();
|
2022-10-01 14:35:23 +00:00
|
|
|
doTypeCheck(actionList);
|
2022-02-20 14:21:29 +00:00
|
|
|
printTypeQueue();
|
2022-10-01 14:35:23 +00:00
|
|
|
|
2021-10-25 20:31:07 +00:00
|
|
|
/**
|
2022-12-14 17:49:08 +00:00
|
|
|
* After processing globals executions the instructions will
|
|
|
|
* be placed into `codeQueue`, therefore copy them from the temporary
|
|
|
|
* scratchpad queue into `globalCodeQueue`.
|
|
|
|
*
|
|
|
|
* Then clean the codeQueue for next use
|
|
|
|
*/
|
|
|
|
foreach(Instruction curGlobInstr; codeQueue)
|
|
|
|
{
|
|
|
|
globalCodeQueue~=curGlobInstr;
|
|
|
|
}
|
|
|
|
codeQueue.clear();
|
|
|
|
assert(codeQueue.empty() == true);
|
|
|
|
|
|
|
|
//FIXME: Look at this, ffs why is it static
|
|
|
|
//Clear tree/linearized version (todo comment)
|
|
|
|
DNode.poes=[];
|
|
|
|
|
|
|
|
/* Grab functionData ??? */
|
|
|
|
FunctionData[string] functionDefinitions = grabFunctionDefs();
|
|
|
|
gprintln("Defined functions: "~to!(string)(functionDefinitions));
|
|
|
|
|
|
|
|
foreach(FunctionData funcData; functionDefinitions.values)
|
|
|
|
{
|
|
|
|
assert(codeQueue.empty() == true);
|
|
|
|
|
2023-01-20 14:43:04 +00:00
|
|
|
/* Generate the dependency tree */
|
2022-12-14 17:49:08 +00:00
|
|
|
DNode funcNode = funcData.generate();
|
2023-01-20 14:43:04 +00:00
|
|
|
|
|
|
|
/* Perform the linearization to the dependency tree */
|
|
|
|
funcNode.performLinearization();
|
|
|
|
|
|
|
|
/* Get the action-list (linearised bottom up graph) */
|
|
|
|
DNode[] actionListFunc = funcNode.getLinearizedNodes();
|
2022-12-14 17:49:08 +00:00
|
|
|
|
|
|
|
//TODO: Would this not mess with our queues?
|
|
|
|
doTypeCheck(actionListFunc);
|
|
|
|
printTypeQueue();
|
2023-01-20 14:43:04 +00:00
|
|
|
gprintln(funcNode.getTree());
|
2022-12-14 17:49:08 +00:00
|
|
|
|
|
|
|
// The current code queue would be the function's body instructions
|
|
|
|
// a.k.a. the `codeQueue`
|
|
|
|
// functionBodies[funcData.name] = codeQueue;
|
|
|
|
|
|
|
|
|
|
|
|
// The call to `doTypeCheck()` above adds to this queue
|
|
|
|
// so we should clean it out before the next run
|
|
|
|
//
|
|
|
|
// NOTE: Static allocations in? Well, we don't clean init queue
|
|
|
|
// so is it fine then? We now have seperate dependency trees,
|
|
|
|
// we should make checking methods that check the `initQueue`
|
|
|
|
// whenever we come past a `ClassStaticNode` for example
|
|
|
|
// codeQueue.clear();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy over the function code queue into
|
|
|
|
* the function code queue respective key.
|
|
|
|
*
|
|
|
|
* Then clear the scratchpad code queue
|
|
|
|
*/
|
|
|
|
functionBodyCodeQueues[funcData.name]=[];
|
|
|
|
foreach(Instruction curFuncInstr; codeQueue)
|
|
|
|
{
|
|
|
|
//TODO: Think about class funcs? Nah
|
|
|
|
functionBodyCodeQueues[funcData.name]~=curFuncInstr;
|
|
|
|
gprintln("FuncDef ("~funcData.name~"): Adding body instruction: "~to!(string)(curFuncInstr));
|
|
|
|
}
|
|
|
|
codeQueue.clear();
|
|
|
|
|
|
|
|
// Clear the linearization for the next round
|
|
|
|
DNode.poes=[];
|
|
|
|
|
|
|
|
gprintln("FUNCDEF DONE: "~to!(string)(functionBodyCodeQueues[funcData.name]));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-11 15:33:18 +00:00
|
|
|
}
|
|
|
|
|
2022-12-14 17:49:08 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Function definitions
|
|
|
|
*
|
|
|
|
* Holds their action lists which are to be used for the
|
|
|
|
* (later) emitting of their X-lang emit code
|
|
|
|
*/
|
|
|
|
//FUnctionDeifnition should couple `linearizedList` but `functionEntity`
|
|
|
|
// private FunctionDefinition[string] functionDefinitions2; //TODO: Use this
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Concrete queues
|
|
|
|
*
|
|
|
|
* These queues below are finalized and not used as a scratchpad.
|
|
|
|
*
|
|
|
|
* 1. Global code queue
|
|
|
|
* - This accounts for the globals needing to be executed
|
|
|
|
* 2. Function body code queues
|
|
|
|
* - This accounts for (every) function definition's code queue
|
|
|
|
*/
|
|
|
|
private Instruction[] globalCodeQueue;
|
|
|
|
private Instruction[][string] functionBodyCodeQueues;
|
|
|
|
|
|
|
|
public Instruction[] getGlobalCodeQueue()
|
|
|
|
{
|
|
|
|
return globalCodeQueue;
|
|
|
|
}
|
|
|
|
|
|
|
|
public Instruction[][string] getFunctionBodyCodeQueues()
|
|
|
|
{
|
|
|
|
return functionBodyCodeQueues;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Main code queue (used for temporary passes) */
|
|
|
|
private SList!(Instruction) codeQueue; //TODO: Rename to `currentCodeQueue`
|
2021-10-26 15:17:53 +00:00
|
|
|
|
2022-10-15 18:36:18 +00:00
|
|
|
/* Initialization queue */
|
|
|
|
private SList!(Instruction) initQueue;
|
|
|
|
|
2022-12-14 17:49:08 +00:00
|
|
|
|
|
|
|
//TODO: CHange to oneshot in the function
|
|
|
|
public Instruction[] getInitQueue()
|
2022-12-11 15:37:27 +00:00
|
|
|
{
|
2022-12-14 17:49:08 +00:00
|
|
|
Instruction[] initQueueConcrete;
|
|
|
|
|
|
|
|
foreach(Instruction currentInstruction; initQueue)
|
|
|
|
{
|
|
|
|
initQueueConcrete~=currentInstruction;
|
|
|
|
}
|
|
|
|
|
|
|
|
return initQueueConcrete;
|
2022-12-11 15:37:27 +00:00
|
|
|
}
|
|
|
|
|
2022-10-15 19:57:03 +00:00
|
|
|
/* Adds an initialization instruction to the initialization queue (at the back) */
|
2022-10-15 18:36:18 +00:00
|
|
|
public void addInit(Instruction initInstruction)
|
|
|
|
{
|
|
|
|
initQueue.insertAfter(initQueue[], initInstruction);
|
|
|
|
}
|
|
|
|
|
2022-10-15 19:57:03 +00:00
|
|
|
/*
|
|
|
|
* Prints the current contents of the init-queue
|
|
|
|
*/
|
|
|
|
public void printInitQueue()
|
|
|
|
{
|
|
|
|
import std.range : walkLength;
|
|
|
|
ulong i = 0;
|
|
|
|
foreach(Instruction instruction; initQueue)
|
|
|
|
{
|
|
|
|
gprintln("InitQueue: "~to!(string)(i+1)~"/"~to!(string)(walkLength(initQueue[]))~": "~instruction.toString());
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-15 18:36:18 +00:00
|
|
|
/* Adds an instruction to the front of code queue */
|
2021-10-26 15:17:53 +00:00
|
|
|
public void addInstr(Instruction inst)
|
2021-10-26 18:32:47 +00:00
|
|
|
{
|
|
|
|
codeQueue.insert(inst);
|
|
|
|
}
|
|
|
|
|
2022-10-15 18:36:18 +00:00
|
|
|
/* Adds an instruction to the back of the code queue */
|
2021-10-26 18:32:47 +00:00
|
|
|
public void addInstrB(Instruction inst)
|
2021-10-26 15:17:53 +00:00
|
|
|
{
|
|
|
|
codeQueue.insertAfter(codeQueue[], inst);
|
|
|
|
}
|
|
|
|
|
2022-10-15 18:36:18 +00:00
|
|
|
/* Removes the instruction at the front of the code queue and returns it */
|
2021-10-26 15:17:53 +00:00
|
|
|
public Instruction popInstr()
|
|
|
|
{
|
2021-10-26 18:32:47 +00:00
|
|
|
Instruction poppedInstr;
|
|
|
|
|
|
|
|
if(!codeQueue.empty)
|
|
|
|
{
|
|
|
|
poppedInstr = codeQueue.front();
|
|
|
|
codeQueue.removeFront();
|
|
|
|
}
|
|
|
|
|
2021-10-26 15:17:53 +00:00
|
|
|
return poppedInstr;
|
|
|
|
}
|
2021-11-11 10:30:40 +00:00
|
|
|
|
2022-12-19 13:37:55 +00:00
|
|
|
/* Pops from the tail of the code queue and returns it */
|
|
|
|
public Instruction tailPopInstr()
|
|
|
|
{
|
|
|
|
Instruction poppedInstr;
|
|
|
|
|
|
|
|
if(!codeQueue.empty)
|
|
|
|
{
|
|
|
|
// Perhaps there is a nicer way to tail popping
|
|
|
|
codeQueue.reverse();
|
|
|
|
poppedInstr = codeQueue.front();
|
|
|
|
codeQueue.removeFront();
|
|
|
|
codeQueue.reverse();
|
|
|
|
}
|
|
|
|
|
|
|
|
return poppedInstr;
|
|
|
|
}
|
|
|
|
|
2021-11-11 10:30:40 +00:00
|
|
|
public bool isInstrEmpty()
|
|
|
|
{
|
|
|
|
return codeQueue.empty;
|
|
|
|
}
|
2021-10-26 15:17:53 +00:00
|
|
|
|
2022-12-14 17:49:08 +00:00
|
|
|
// public Instruction[] getCodeQueue()
|
|
|
|
// {
|
|
|
|
// Instruction[] codeQueueConcrete;
|
|
|
|
|
|
|
|
// foreach(Instruction currentInstruction; codeQueue)
|
|
|
|
// {
|
|
|
|
// codeQueueConcrete~=currentInstruction;
|
|
|
|
// }
|
|
|
|
|
|
|
|
// return codeQueueConcrete;
|
|
|
|
// }
|
2021-10-27 07:51:13 +00:00
|
|
|
|
2021-10-26 15:17:53 +00:00
|
|
|
/*
|
|
|
|
* Prints the current contents of the code-queue
|
|
|
|
*/
|
|
|
|
public void printCodeQueue()
|
|
|
|
{
|
|
|
|
import std.range : walkLength;
|
|
|
|
ulong i = 0;
|
|
|
|
foreach(Instruction instruction; codeQueue)
|
|
|
|
{
|
2021-10-27 07:25:11 +00:00
|
|
|
gprintln(to!(string)(i+1)~"/"~to!(string)(walkLength(codeQueue[]))~": "~instruction.toString());
|
2021-10-26 15:17:53 +00:00
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-18 12:32:45 +00:00
|
|
|
/*
|
|
|
|
* Prints the current contents of the code-queue
|
|
|
|
*/
|
|
|
|
public void printTypeQueue()
|
|
|
|
{
|
|
|
|
import std.range : walkLength;
|
|
|
|
ulong i = 0;
|
|
|
|
foreach(Type instruction; typeStack)
|
|
|
|
{
|
|
|
|
gprintln("TypeQueue: "~to!(string)(i+1)~"/"~to!(string)(walkLength(typeStack[]))~": "~instruction.toString());
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* There are several types and comparing them differs
|
|
|
|
*/
|
|
|
|
private bool isSameType(Type type1, Type type2)
|
|
|
|
{
|
|
|
|
bool same = false;
|
|
|
|
|
2022-04-07 23:12:52 +00:00
|
|
|
|
|
|
|
|
2022-02-18 12:32:45 +00:00
|
|
|
/* Handling for Integers */
|
|
|
|
if(typeid(type1) == typeid(type2) && cast(Integer)type1 !is null)
|
|
|
|
{
|
|
|
|
Integer i1 = cast(Integer)type1, i2 = cast(Integer)type2;
|
|
|
|
|
|
|
|
/* Both same size? */
|
|
|
|
if(i1.getSize() == i2.getSize())
|
|
|
|
{
|
|
|
|
/* Matching signedness ? */
|
2022-04-07 23:12:52 +00:00
|
|
|
same = i1.isSigned() == i2.isSigned();
|
2022-02-18 12:32:45 +00:00
|
|
|
}
|
|
|
|
/* Size mismatch */
|
|
|
|
else
|
|
|
|
{
|
2022-04-07 23:12:52 +00:00
|
|
|
same = false;
|
2022-02-18 12:32:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-04-07 23:12:52 +00:00
|
|
|
gprintln("isSameType("~to!(string)(type1)~","~to!(string)(type2)~"): "~to!(string)(same), DebugType.ERROR);
|
2022-02-18 12:32:45 +00:00
|
|
|
return same;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-10-25 20:31:07 +00:00
|
|
|
|
|
|
|
private SList!(Type) typeStack;
|
|
|
|
|
2021-10-26 15:17:53 +00:00
|
|
|
|
2022-02-20 12:24:05 +00:00
|
|
|
/**
|
|
|
|
* Adds a Type to the type queue right at the beginning
|
|
|
|
* of it
|
|
|
|
*/
|
2021-10-25 20:31:07 +00:00
|
|
|
private void addType(Type typeName)
|
2022-02-20 12:24:05 +00:00
|
|
|
{
|
|
|
|
typeStack.insert(typeName);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a Type to the type queue right at the end
|
|
|
|
* of it
|
|
|
|
*/
|
|
|
|
private void addTypeB(Type typeName)
|
2021-10-25 20:31:07 +00:00
|
|
|
{
|
|
|
|
typeStack.insertAfter(typeStack[], typeName);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Type popType()
|
|
|
|
{
|
|
|
|
Type typeCur = typeStack.front();
|
|
|
|
|
|
|
|
typeStack.removeFront();
|
|
|
|
|
|
|
|
return typeCur;
|
|
|
|
}
|
|
|
|
|
2022-02-20 10:09:10 +00:00
|
|
|
public bool isTypesEmpty()
|
|
|
|
{
|
|
|
|
return typeStack.empty;
|
|
|
|
}
|
|
|
|
|
2021-10-25 20:31:07 +00:00
|
|
|
public void typeCheckThing(DNode dnode)
|
|
|
|
{
|
2021-10-26 15:17:53 +00:00
|
|
|
gprintln("typeCheckThing(): "~dnode.toString());
|
|
|
|
|
2021-10-25 20:31:07 +00:00
|
|
|
/* ExpressionDNodes */
|
2021-11-09 17:16:51 +00:00
|
|
|
if(cast(compiler.typecheck.dependency.expression.ExpressionDNode)dnode)
|
2021-10-25 20:31:07 +00:00
|
|
|
{
|
2021-11-09 17:16:51 +00:00
|
|
|
compiler.typecheck.dependency.expression.ExpressionDNode expDNode = cast(compiler.typecheck.dependency.expression.ExpressionDNode)dnode;
|
2021-10-25 20:31:07 +00:00
|
|
|
|
|
|
|
Statement statement = expDNode.getEntity();
|
|
|
|
gprintln("Hdfsfdjfds"~to!(string)(statement));
|
|
|
|
|
|
|
|
/* Dependent on the type of Statement */
|
|
|
|
|
|
|
|
if(cast(NumberLiteral)statement)
|
|
|
|
{
|
|
|
|
/* TODO: For now */
|
2021-10-26 15:17:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Typechecking
|
2022-08-11 08:26:30 +00:00
|
|
|
*
|
2022-08-19 09:42:19 +00:00
|
|
|
* If the number literal contains a `.` then it is a float
|
|
|
|
* else if is an int (NOTE: This may need to be more specific
|
|
|
|
* with literal encoders down the line)
|
2021-10-26 15:17:53 +00:00
|
|
|
*/
|
2022-08-11 08:26:30 +00:00
|
|
|
NumberLiteral numLit = cast(NumberLiteral)statement;
|
|
|
|
import std.string : indexOf;
|
|
|
|
bool isFloat = indexOf(numLit.getNumber(), ".") > -1;
|
|
|
|
gprintln("NUMBER LIT: isFloat: "~to!(string)(isFloat));
|
|
|
|
addType(getType(modulle, isFloat ? "float" : "int"));
|
2021-10-26 15:17:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Codegen
|
2022-08-11 08:26:30 +00:00
|
|
|
*
|
|
|
|
* TODO: We just assume (for integers) byte size 4?
|
2022-08-19 09:42:19 +00:00
|
|
|
*
|
|
|
|
* Generate the correct value instruction depending
|
|
|
|
* on the number literal's type
|
2021-10-26 15:17:53 +00:00
|
|
|
*/
|
2022-08-11 08:26:30 +00:00
|
|
|
Value valInstr;
|
|
|
|
|
2022-08-19 09:42:19 +00:00
|
|
|
/* Generate a LiteralValue (Integer literal) */
|
2022-08-11 08:26:30 +00:00
|
|
|
if(!isFloat)
|
|
|
|
{
|
|
|
|
ulong i = to!(ulong)((cast(NumberLiteral)statement).getNumber());
|
|
|
|
LiteralValue litValInstr = new LiteralValue(i, 4);
|
|
|
|
|
|
|
|
valInstr = litValInstr;
|
|
|
|
}
|
2022-08-19 09:42:19 +00:00
|
|
|
/* Generate a LiteralValueFloat (Floating point literal) */
|
2022-08-11 08:26:30 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
double i = to!(float)((cast(NumberLiteral)statement).getNumber());
|
|
|
|
LiteralValueFloat litValInstr = new LiteralValueFloat(i, 4);
|
|
|
|
|
|
|
|
valInstr = litValInstr;
|
|
|
|
}
|
|
|
|
|
|
|
|
addInstr(valInstr);
|
2021-10-25 20:31:07 +00:00
|
|
|
}
|
2022-07-25 17:15:27 +00:00
|
|
|
/* String literal */
|
2021-10-25 20:31:07 +00:00
|
|
|
else if(cast(StringExpression)statement)
|
|
|
|
{
|
2022-07-25 17:15:27 +00:00
|
|
|
gprintln("Typecheck(): String literal processing...");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Add the char* type as string literals should be
|
|
|
|
* interned
|
|
|
|
*/
|
2022-07-26 07:59:27 +00:00
|
|
|
addType(getType(modulle, "char*"));
|
2022-07-25 17:30:40 +00:00
|
|
|
|
|
|
|
/**
|
2022-07-26 08:07:03 +00:00
|
|
|
* Add the instruction and pass the literal to it
|
2022-07-25 17:30:40 +00:00
|
|
|
*/
|
2022-07-26 07:59:27 +00:00
|
|
|
StringExpression strExp = cast(StringExpression)statement;
|
|
|
|
string strLit = strExp.getStringLiteral();
|
|
|
|
gprintln("String literal: `"~strLit~"`");
|
|
|
|
StringLiteral strLitInstr = new StringLiteral(strLit);
|
2022-07-25 17:30:40 +00:00
|
|
|
addInstr(strLitInstr);
|
2022-07-25 17:15:27 +00:00
|
|
|
|
|
|
|
gprintln("Typecheck(): String literal processing... [done]");
|
2021-10-25 20:31:07 +00:00
|
|
|
}
|
|
|
|
else if(cast(VariableExpression)statement)
|
|
|
|
{
|
2022-04-13 07:50:24 +00:00
|
|
|
|
|
|
|
gprintln("Yaa, it's rewind time");
|
2021-10-25 20:31:07 +00:00
|
|
|
auto g = cast(VariableExpression)statement;
|
2022-10-13 11:21:35 +00:00
|
|
|
|
|
|
|
/* FIXME: It would seem that g.getContext() is returning null, so within function body's context is not being set */
|
|
|
|
gprintln("VarExp: "~g.getName());
|
|
|
|
gprintln(g.getContext());
|
2021-10-25 20:31:07 +00:00
|
|
|
auto gVar = cast(TypedEntity)resolver.resolveBest(g.getContext().getContainer(), g.getName());
|
2022-10-14 18:44:33 +00:00
|
|
|
gprintln("gVar nullity?: "~to!(string)(gVar is null));
|
|
|
|
|
|
|
|
/* TODO; Above crashes when it is a container, eish baba - from dependency generation with `TestClass.P.h` */
|
2021-10-26 15:17:53 +00:00
|
|
|
|
|
|
|
string variableName = resolver.generateName(modulle, gVar);
|
2022-04-13 07:50:24 +00:00
|
|
|
|
|
|
|
gprintln("VarName: "~variableName);
|
2022-10-14 18:44:33 +00:00
|
|
|
gprintln("Halo");
|
2022-04-13 07:50:24 +00:00
|
|
|
|
|
|
|
gprintln("Yaa, it's rewind time1: "~to!(string)(gVar.getType()));
|
|
|
|
gprintln("Yaa, it's rewind time2: "~to!(string)(gVar.getContext()));
|
2021-10-25 20:31:07 +00:00
|
|
|
|
|
|
|
/* TODO: Above TYpedEntity check */
|
|
|
|
/* TODO: still wip the expresison parser */
|
|
|
|
|
|
|
|
/* TODO: TYpe needs ansatz too `.updateName()` call */
|
|
|
|
addType(getType(gVar.getContext().getContainer(), gVar.getType()));
|
2021-10-26 15:17:53 +00:00
|
|
|
|
2022-04-13 07:50:24 +00:00
|
|
|
gprintln("Yaa, it's rewind time");
|
|
|
|
|
2021-10-26 15:17:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Codegen
|
|
|
|
*
|
|
|
|
* FIXME: Add type info, length
|
2022-12-13 09:50:54 +00:00
|
|
|
*
|
|
|
|
* 1. Generate the instruction
|
|
|
|
* 2. Set the Context of it to where the VariableExpression occurred
|
2021-10-26 15:17:53 +00:00
|
|
|
*/
|
|
|
|
FetchValueVar fVV = new FetchValueVar(variableName, 4);
|
2022-12-13 09:50:54 +00:00
|
|
|
fVV.context = g.getContext();
|
|
|
|
|
|
|
|
|
2021-10-26 15:17:53 +00:00
|
|
|
addInstr(fVV);
|
|
|
|
}
|
|
|
|
// else if(cast()) !!!! Continue here
|
|
|
|
else if(cast(BinaryOperatorExpression)statement)
|
|
|
|
{
|
2021-10-26 20:03:48 +00:00
|
|
|
BinaryOperatorExpression binOpExp = cast(BinaryOperatorExpression)statement;
|
|
|
|
SymbolType binOperator = binOpExp.getOperator();
|
2022-04-07 23:12:52 +00:00
|
|
|
|
2021-10-26 20:03:48 +00:00
|
|
|
|
2021-10-26 15:17:53 +00:00
|
|
|
/**
|
|
|
|
* Typechecking (TODO)
|
|
|
|
*/
|
2022-02-20 14:21:29 +00:00
|
|
|
Type vRhsType = popType();
|
|
|
|
Type vLhsType = popType();
|
2022-02-20 14:37:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO:
|
|
|
|
* Types must either BE THE SAME or BE COMPATIBLE
|
|
|
|
*/
|
|
|
|
if(isSameType(vLhsType, vRhsType))
|
|
|
|
{
|
|
|
|
/* Left type + Right type = left/right type (just use left - it doesn't matter) */
|
|
|
|
addType(vLhsType);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gprintln("Binary operator expression requires both types be same, but got '"~vRhsType.toString()~"' and '"~vLhsType.toString()~"'", DebugType.ERROR);
|
|
|
|
assert(false);
|
|
|
|
}
|
2022-02-20 14:21:29 +00:00
|
|
|
|
2021-10-26 15:17:53 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Codegen
|
|
|
|
*
|
|
|
|
* Retrieve the two Value Instructions
|
2021-10-26 19:04:47 +00:00
|
|
|
*
|
2021-10-26 19:15:26 +00:00
|
|
|
* They would be placed as if they were on stack
|
|
|
|
* hence we need to burger-flip them around (swap)
|
2021-10-26 15:17:53 +00:00
|
|
|
*/
|
|
|
|
Instruction vRhsInstr = popInstr();
|
2021-10-26 19:15:26 +00:00
|
|
|
Instruction vLhsInstr = popInstr();
|
2021-10-26 15:17:53 +00:00
|
|
|
|
2021-10-26 20:03:48 +00:00
|
|
|
BinOpInstr addInst = new BinOpInstr(vLhsInstr, vRhsInstr, binOperator);
|
2021-10-26 15:17:53 +00:00
|
|
|
addInstr(addInst);
|
2021-10-25 20:31:07 +00:00
|
|
|
}
|
2022-04-12 08:54:04 +00:00
|
|
|
/* Unary operator expressions */
|
|
|
|
else if(cast(UnaryOperatorExpression)statement)
|
|
|
|
{
|
|
|
|
UnaryOperatorExpression unaryOpExp = cast(UnaryOperatorExpression)statement;
|
|
|
|
SymbolType unaryOperator = unaryOpExp.getOperator();
|
|
|
|
|
|
|
|
|
2022-04-13 07:50:24 +00:00
|
|
|
|
|
|
|
|
2022-04-12 08:54:04 +00:00
|
|
|
/**
|
|
|
|
* Typechecking (TODO)
|
|
|
|
*/
|
|
|
|
Type expType = popType();
|
|
|
|
|
|
|
|
/* TODO: Ad type check for operator */
|
2022-04-13 07:50:24 +00:00
|
|
|
|
|
|
|
/* If the unary operation is an arithmetic one */
|
|
|
|
if(unaryOperator == SymbolType.ADD || unaryOperator == SymbolType.SUB)
|
|
|
|
{
|
|
|
|
/* TODO: I guess any type fr */
|
|
|
|
}
|
|
|
|
/* If pointer dereference */
|
|
|
|
else if(unaryOperator == SymbolType.STAR)
|
|
|
|
{
|
2023-01-14 10:39:37 +00:00
|
|
|
gprintln("Type popped: "~to!(string)(expType));
|
|
|
|
|
|
|
|
// Okay, so yes, we would pop `ptr`'s type as `int*` which is correct
|
|
|
|
// but now, we must a.) ensure that IS the case and b.)
|
|
|
|
// push the type of `<type>` with one star less on as we are derefrencing `ptr`
|
|
|
|
Type derefPointerType;
|
|
|
|
if(cast(Pointer)expType)
|
|
|
|
{
|
|
|
|
Pointer pointerType = cast(Pointer)expType;
|
|
|
|
|
|
|
|
// Get the type being referred to
|
|
|
|
Type referredType = pointerType.getReferredType();
|
|
|
|
|
|
|
|
addType(referredType);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gprintln("You cannot dereference a type that is not a pointer type!", DebugType.ERROR);
|
|
|
|
assert(false);
|
|
|
|
}
|
2022-04-13 07:50:24 +00:00
|
|
|
}
|
|
|
|
/* If pointer create `&` */
|
|
|
|
else if(unaryOperator == SymbolType.AMPERSAND)
|
|
|
|
{
|
|
|
|
/**
|
2023-01-12 11:31:42 +00:00
|
|
|
* If the type popped from the stack was `<type>` then push
|
|
|
|
* a new type onto the stack which is `<type>*`
|
2022-04-13 07:50:24 +00:00
|
|
|
*/
|
2023-01-12 08:53:48 +00:00
|
|
|
Type ptrType = new Pointer(expType);
|
|
|
|
addType(ptrType);
|
2022-04-13 07:50:24 +00:00
|
|
|
}
|
|
|
|
/* This should never occur */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gprintln("UnaryOperatorExpression: This should NEVER happen: "~to!(string)(unaryOperator), DebugType.ERROR);
|
|
|
|
assert(false);
|
|
|
|
}
|
2022-04-12 08:54:04 +00:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Codegen
|
|
|
|
*
|
|
|
|
* Retrieve the instruction
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
Instruction expInstr = popInstr();
|
2023-01-12 11:31:42 +00:00
|
|
|
|
|
|
|
// TODO: For type checking and semantics we should be checking WHAT is being ampersanded
|
|
|
|
// ... as in we should only be allowing Ident's to be ampersanded, not, for example, literals
|
|
|
|
// ... such a check can be accomplished via runtime type information of the instruction above
|
2022-04-13 07:50:24 +00:00
|
|
|
|
2022-04-12 08:54:04 +00:00
|
|
|
|
|
|
|
UnaryOpInstr addInst = new UnaryOpInstr(expInstr, unaryOperator);
|
2023-01-14 10:39:37 +00:00
|
|
|
gprintln("Made unaryop instr: "~to!(string)(addInst));
|
2022-04-12 08:54:04 +00:00
|
|
|
addInstr(addInst);
|
|
|
|
}
|
2022-02-15 13:04:16 +00:00
|
|
|
/* Function calls */
|
|
|
|
else if(cast(FunctionCall)statement)
|
|
|
|
{
|
2022-02-20 14:21:29 +00:00
|
|
|
// gprintln("FuncCall hehe (REMOVE AFTER DONE)");
|
2022-02-20 13:35:34 +00:00
|
|
|
// printTypeQueue();
|
2022-02-18 12:32:45 +00:00
|
|
|
|
|
|
|
FunctionCall funcCall = cast(FunctionCall)statement;
|
|
|
|
|
|
|
|
/* TODO: Look up func def to know when popping stops (types-based delimiting) */
|
|
|
|
Function func = cast(Function)resolver.resolveBest(modulle, funcCall.getName());
|
|
|
|
assert(func);
|
2022-12-17 12:00:16 +00:00
|
|
|
VariableParameter[] paremeters = func.getParams();
|
2022-02-20 12:24:05 +00:00
|
|
|
|
|
|
|
|
|
|
|
/* TODO: Pass in FUnction, so we get function's body for calling too */
|
|
|
|
FuncCallInstr funcCallInstr = new FuncCallInstr(func.getName(), paremeters.length);
|
2022-04-07 23:12:52 +00:00
|
|
|
gprintln("Name of func call: "~func.getName(), DebugType.ERROR);
|
2022-02-18 12:32:45 +00:00
|
|
|
|
|
|
|
/* If there are paremeters for this function (as per definition) */
|
|
|
|
if(!paremeters.length)
|
|
|
|
{
|
2022-04-07 23:12:52 +00:00
|
|
|
gprintln("No parameters for deez nuts: "~func.getName(), DebugType.ERROR);
|
2022-02-18 12:32:45 +00:00
|
|
|
}
|
|
|
|
/* Pop all args per type */
|
|
|
|
else
|
|
|
|
{
|
2022-02-20 14:05:15 +00:00
|
|
|
ulong parmCount = paremeters.length-1;
|
2022-04-07 23:12:52 +00:00
|
|
|
gprintln("Kachow: "~to!(string)(parmCount),DebugType.ERROR);
|
2022-02-20 14:05:15 +00:00
|
|
|
|
2022-02-18 12:32:45 +00:00
|
|
|
while(!isInstrEmpty())
|
|
|
|
{
|
|
|
|
Instruction instr = popInstr();
|
|
|
|
|
|
|
|
Value valueInstr = cast(Value)instr;
|
|
|
|
|
|
|
|
|
|
|
|
/* Must be a value instruction */
|
|
|
|
if(valueInstr && parmCount!=-1)
|
|
|
|
{
|
|
|
|
/* TODO: Determine type and match up */
|
|
|
|
gprintln("Yeah");
|
|
|
|
gprintln(valueInstr);
|
|
|
|
Type argType = popType();
|
2022-02-20 13:35:34 +00:00
|
|
|
// gprintln(argType);
|
2022-02-18 12:32:45 +00:00
|
|
|
|
|
|
|
Variable parameter = paremeters[parmCount];
|
2022-02-20 13:35:34 +00:00
|
|
|
// gprintln(parameter);
|
|
|
|
|
2022-02-18 12:32:45 +00:00
|
|
|
|
|
|
|
Type parmType = getType(func.parentOf(), parameter.getType());
|
2022-02-20 13:35:34 +00:00
|
|
|
// gprintln("FuncCall(Actual): "~argType.getName());
|
|
|
|
// gprintln("FuncCall(Formal): "~parmType.getName());
|
|
|
|
// gprintln("FuncCall(Actual): "~valueInstr.toString());
|
2022-02-18 12:32:45 +00:00
|
|
|
|
|
|
|
|
2022-02-20 13:35:34 +00:00
|
|
|
// printTypeQueue();
|
2022-02-18 12:32:45 +00:00
|
|
|
/* Match up types */
|
|
|
|
//if(argType == parmType)
|
|
|
|
if(isSameType(argType, parmType))
|
|
|
|
{
|
|
|
|
gprintln("Match type");
|
2022-02-20 13:35:34 +00:00
|
|
|
|
|
|
|
/* Add the instruction into the FunctionCallInstr */
|
|
|
|
funcCallInstr.setEvalInstr(parmCount, valueInstr);
|
|
|
|
gprintln(funcCallInstr.getEvaluationInstructions());
|
2022-02-18 12:32:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
printCodeQueue();
|
|
|
|
gprintln("Wrong actual argument type for function call", DebugType.ERROR);
|
|
|
|
gprintln("Cannot pass value of type '"~argType.getName()~"' to function accepting '"~parmType.getName()~"'", DebugType.ERROR);
|
|
|
|
assert(false);
|
|
|
|
}
|
2022-02-20 13:35:34 +00:00
|
|
|
|
|
|
|
parmCount--;
|
2022-02-18 12:32:45 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Push it back */
|
|
|
|
addInstr(instr);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-02-20 12:24:05 +00:00
|
|
|
|
2022-02-18 12:32:45 +00:00
|
|
|
|
|
|
|
|
2022-02-15 13:04:16 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO:
|
|
|
|
*
|
2022-12-16 12:53:33 +00:00
|
|
|
* 1. Create FuncCallInstr
|
2022-02-16 05:31:22 +00:00
|
|
|
* 2. Evaluate args and process them?! wait done elsewhere yeah!!!
|
2022-02-15 13:04:16 +00:00
|
|
|
* 3. Pop arts into here
|
|
|
|
* 4. AddInstr(combining those args)
|
|
|
|
* 5. DOne
|
|
|
|
*/
|
2022-12-16 12:53:33 +00:00
|
|
|
funcCallInstr.context = funcCall.getContext();
|
|
|
|
|
2022-02-18 12:32:45 +00:00
|
|
|
addInstr(funcCallInstr);
|
|
|
|
addType(getType(func.parentOf(), func.getType()));
|
2022-02-15 13:04:16 +00:00
|
|
|
}
|
2023-01-14 16:40:08 +00:00
|
|
|
/* Type cast operator */
|
|
|
|
else if(cast(CastedExpression)statement)
|
|
|
|
{
|
|
|
|
CastedExpression castedExpression = cast(CastedExpression)statement;
|
|
|
|
gprintln("Context: "~to!(string)(castedExpression.context));
|
|
|
|
gprintln("ParentOf: "~to!(string)(castedExpression.parentOf()));
|
|
|
|
Type castToType = getType(castedExpression.context.container, castedExpression.getToType());
|
|
|
|
|
|
|
|
/* Pop the type associated with the embedded expression */
|
|
|
|
Type typeBeingCasted = popType(); // TODO: Is there anything we would want to do with this?
|
|
|
|
gprintln("TypeCast [FromType: "~to!(string)(typeBeingCasted)~", ToType: "~to!(string)(castToType)~"]");
|
|
|
|
|
|
|
|
/* Push the type to cast to onto the stack such that we typify the associated instruction */
|
|
|
|
addType(castToType);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Codegen
|
|
|
|
*
|
|
|
|
* 1. Pop off the current value instruction corresponding to the embedding
|
|
|
|
* 2. Create a new CastedValueInstruction instruction
|
|
|
|
* 3. Set the context
|
|
|
|
* 4. Add to front of code queue
|
|
|
|
*/
|
|
|
|
Value uncastedInstruction = cast(Value)popInstr();
|
|
|
|
assert(uncastedInstruction);
|
|
|
|
|
|
|
|
printCodeQueue();
|
|
|
|
printTypeQueue();
|
|
|
|
|
|
|
|
CastedValueInstruction castedValueInstruction = new CastedValueInstruction(uncastedInstruction, castToType);
|
|
|
|
castedValueInstruction.context = castedExpression.context;
|
|
|
|
|
|
|
|
addInstr(castedValueInstruction);
|
|
|
|
}
|
2021-10-25 20:31:07 +00:00
|
|
|
}
|
2021-10-26 18:32:47 +00:00
|
|
|
/* VariableAssigbmentDNode */
|
2021-11-09 17:16:51 +00:00
|
|
|
else if(cast(compiler.typecheck.dependency.variables.VariableAssignmentNode)dnode)
|
2021-10-26 18:32:47 +00:00
|
|
|
{
|
2021-11-09 17:16:51 +00:00
|
|
|
import compiler.typecheck.dependency.variables;
|
2022-12-12 11:12:03 +00:00
|
|
|
|
2021-10-26 18:32:47 +00:00
|
|
|
/* Get the variable's name */
|
|
|
|
string variableName;
|
2021-11-09 17:16:51 +00:00
|
|
|
VariableAssignmentNode varAssignDNode = cast(compiler.typecheck.dependency.variables.VariableAssignmentNode)dnode;
|
2021-10-26 18:32:47 +00:00
|
|
|
Variable assignTo = (cast(VariableAssignment)varAssignDNode.getEntity()).getVariable();
|
|
|
|
variableName = resolver.generateName(modulle, assignTo);
|
2022-10-14 16:01:35 +00:00
|
|
|
gprintln("VariableAssignmentNode: "~to!(string)(variableName));
|
2021-10-26 18:32:47 +00:00
|
|
|
|
2022-12-12 11:12:03 +00:00
|
|
|
/* Get the Context of the Variable Assigmnent */
|
|
|
|
Context variableAssignmentContext = (cast(VariableAssignment)varAssignDNode.getEntity()).context;
|
|
|
|
|
2022-10-15 18:24:23 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* FIXME: Now with ClassStaticAllocate we will have wrong instructoins for us
|
|
|
|
* ontop of the stack (at the beginning of the queue), I think this leads us
|
|
|
|
* to potentially opping wrong thing off - we should filter pop perhaps
|
|
|
|
*/
|
|
|
|
|
2021-10-26 18:32:47 +00:00
|
|
|
/**
|
|
|
|
* Codegen
|
|
|
|
*
|
|
|
|
* 1. Get the variable's name
|
|
|
|
* 2. Pop Value-instruction
|
|
|
|
* 3. Generate VarAssignInstruction with Value-instruction
|
2022-12-12 11:12:03 +00:00
|
|
|
* 4. Set the VarAssignInstr's Context to that of the Variable assigning to
|
2021-10-26 18:32:47 +00:00
|
|
|
*/
|
|
|
|
Instruction valueInstr = popInstr();
|
2022-10-15 18:24:23 +00:00
|
|
|
gprintln("VaribleAssignmentNode(): Just popped off valInstr?: "~to!(string)(valueInstr), DebugType.WARNING);
|
2022-02-15 13:04:16 +00:00
|
|
|
gprintln(valueInstr is null);/*TODO: FUnc calls not implemented? Then is null for simple_1.t */
|
2021-10-26 18:32:47 +00:00
|
|
|
VariableAssignmentInstr varAssInstr = new VariableAssignmentInstr(variableName, valueInstr);
|
2022-12-12 11:12:03 +00:00
|
|
|
varAssInstr.context = variableAssignmentContext;
|
|
|
|
|
2022-12-19 13:37:55 +00:00
|
|
|
addInstr(varAssInstr);
|
2021-10-27 19:12:00 +00:00
|
|
|
}
|
2021-11-10 14:42:15 +00:00
|
|
|
/* TODO: Add support */
|
|
|
|
/**
|
|
|
|
* TODO: We need to emit different code dependeing on variable declaration TYPE
|
|
|
|
* We could use context for this, ClassVariableDec vs ModuleVariableDec
|
|
|
|
*/
|
|
|
|
else if(cast(compiler.typecheck.dependency.variables.StaticVariableDeclaration)dnode)
|
2021-10-27 19:12:00 +00:00
|
|
|
{
|
2021-11-10 15:15:27 +00:00
|
|
|
/* TODO: Add skipping if context is within a class */
|
|
|
|
/* We need to wait for class static node, to do an InitInstruction (static init) */
|
|
|
|
/* It probably makes sense , IDK, we need to allocate both classes */
|
|
|
|
|
2021-10-27 19:12:00 +00:00
|
|
|
/**
|
|
|
|
* Codegen
|
|
|
|
*
|
|
|
|
* Emit a variable declaration instruction
|
|
|
|
*/
|
|
|
|
Variable variablePNode = cast(Variable)dnode.getEntity();
|
2022-10-14 16:01:35 +00:00
|
|
|
gprintln("HELLO FELLA");
|
2021-10-27 19:12:00 +00:00
|
|
|
string variableName = resolver.generateName(modulle, variablePNode);
|
2022-12-19 13:37:55 +00:00
|
|
|
gprintln("HELLO FELLA (name): "~variableName);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// CHeck if this variable declaration has an assignment attached
|
|
|
|
VariableAssignmentInstr assignmentInstr;
|
|
|
|
if(variablePNode.getAssignment())
|
|
|
|
{
|
|
|
|
Instruction poppedInstr = popInstr();
|
|
|
|
assignmentInstr = cast(VariableAssignmentInstr)poppedInstr;
|
|
|
|
assert(assignmentInstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-01-15 10:36:54 +00:00
|
|
|
Type variableDeclarationType = getType(variablePNode.context.container, variablePNode.getType());
|
|
|
|
|
|
|
|
VariableDeclaration varDecInstr = new VariableDeclaration(variableName, 4, variableDeclarationType, assignmentInstr);
|
2021-10-27 19:12:00 +00:00
|
|
|
|
2021-11-09 13:50:04 +00:00
|
|
|
/* NEW CODE (9th November 2021) Set the context */
|
|
|
|
varDecInstr.context = variablePNode.context;
|
|
|
|
|
2021-11-11 10:30:40 +00:00
|
|
|
|
2022-10-15 15:15:03 +00:00
|
|
|
addInstrB(varDecInstr);
|
2021-10-27 19:12:00 +00:00
|
|
|
|
|
|
|
|
2022-12-19 13:37:55 +00:00
|
|
|
|
2021-10-27 19:12:00 +00:00
|
|
|
|
2021-10-26 18:32:47 +00:00
|
|
|
}
|
2022-10-13 12:34:47 +00:00
|
|
|
/* TODO: Add class init, see #8 */
|
2021-11-10 15:15:27 +00:00
|
|
|
else if(cast(compiler.typecheck.dependency.classes.classStaticDep.ClassStaticNode)dnode)
|
|
|
|
{
|
2022-10-15 18:25:38 +00:00
|
|
|
/* Extract the class node and create a static allocation instruction out of it */
|
|
|
|
Clazz clazzPNode = cast(Clazz)dnode.getEntity();
|
|
|
|
string clazzName = resolver.generateName(modulle, clazzPNode);
|
|
|
|
ClassStaticInitAllocate clazzStaticInitAllocInstr = new ClassStaticInitAllocate(clazzName);
|
2021-11-10 15:15:27 +00:00
|
|
|
|
2022-10-15 18:36:18 +00:00
|
|
|
/* Add this static initialization to the list of global allocations required */
|
|
|
|
addInit(clazzStaticInitAllocInstr);
|
2021-11-10 15:15:27 +00:00
|
|
|
}
|
2021-11-10 15:00:56 +00:00
|
|
|
/* It will pop a bunch of shiiit */
|
2021-10-27 18:57:30 +00:00
|
|
|
/* TODO: ANy statement */
|
2021-11-09 17:16:51 +00:00
|
|
|
else if(cast(compiler.typecheck.dependency.core.DNode)dnode)
|
2021-10-27 18:57:30 +00:00
|
|
|
{
|
|
|
|
/* TODO: Get the STatement */
|
|
|
|
Statement statement = dnode.getEntity();
|
|
|
|
|
2022-12-14 17:49:08 +00:00
|
|
|
gprintln("Generic DNode typecheck(): Begin (examine: "~to!(string)(dnode)~" )");
|
|
|
|
|
2021-10-27 18:57:30 +00:00
|
|
|
|
|
|
|
/* VariableAssignmentStdAlone */
|
|
|
|
if(cast(VariableAssignmentStdAlone)statement)
|
|
|
|
{
|
|
|
|
VariableAssignmentStdAlone vasa = cast(VariableAssignmentStdAlone)statement;
|
|
|
|
string variableName = vasa.getVariableName();
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Codegen
|
|
|
|
*
|
|
|
|
* 1. Get the variable's name
|
|
|
|
* 2. Pop Value-instruction
|
|
|
|
* 3. Generate VarAssignInstruction with Value-instruction
|
|
|
|
*/
|
|
|
|
Instruction valueInstr = popInstr();
|
|
|
|
VariableAssignmentInstr vAInstr = new VariableAssignmentInstr(variableName, valueInstr);
|
2022-12-12 19:10:58 +00:00
|
|
|
|
|
|
|
/* Set the VariableAssigmmentInstruction's context to that of the stdalone entity */
|
|
|
|
vAInstr.context = vasa.getContext();
|
|
|
|
|
2021-10-27 18:57:30 +00:00
|
|
|
addInstrB(vAInstr);
|
2022-12-19 13:37:55 +00:00
|
|
|
|
|
|
|
gprintln("VariableAssignmentStdAlone", DebugType.ERROR);
|
2021-10-27 18:57:30 +00:00
|
|
|
}
|
2022-12-17 17:02:14 +00:00
|
|
|
/**
|
|
|
|
* Return statement (ReturnStmt)
|
|
|
|
*/
|
|
|
|
else if(cast(ReturnStmt)statement)
|
|
|
|
{
|
|
|
|
ReturnStmt returnStatement = cast(ReturnStmt)statement;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Codegen
|
|
|
|
*
|
|
|
|
* 1. Pop the expression on the stack
|
|
|
|
* 2. Create a new ReturnInstruction with the expression instruction
|
|
|
|
* embedded in it
|
|
|
|
* 3. Set the Context of the instruction
|
|
|
|
* 4. Add this instruction back
|
|
|
|
*/
|
|
|
|
Value returnExpressionInstr = cast(Value)popInstr();
|
|
|
|
assert(returnExpressionInstr);
|
|
|
|
ReturnInstruction returnInstr = new ReturnInstruction(returnExpressionInstr);
|
|
|
|
returnInstr.context = returnStatement.getContext();
|
|
|
|
addInstrB(returnInstr);
|
|
|
|
}
|
2022-12-19 13:37:55 +00:00
|
|
|
/**
|
|
|
|
* If statement (IfStatement)
|
|
|
|
*/
|
|
|
|
else if(cast(IfStatement)statement)
|
|
|
|
{
|
|
|
|
IfStatement ifStatement = cast(IfStatement)statement;
|
|
|
|
BranchInstruction[] branchInstructions;
|
|
|
|
|
|
|
|
/* Get the if statement's branches */
|
|
|
|
Branch[] branches = ifStatement.getBranches();
|
|
|
|
assert(branches.length > 0);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 1. These would be added stack wise, so we need to pop them like backwards
|
|
|
|
* 2. Then a reversal at the end (generated instructions list)
|
|
|
|
*
|
|
|
|
* FIXME: EIther used siggned or the hack below lmao, out of boounds
|
|
|
|
*/
|
|
|
|
for(ulong branchIdx = branches.length-1; true; branchIdx--)
|
|
|
|
{
|
|
|
|
Branch branch = branches[branchIdx];
|
|
|
|
|
|
|
|
// Pop off an expression instruction (if it exists)
|
|
|
|
Value branchConditionInstr;
|
|
|
|
if(branch.hasCondition())
|
|
|
|
{
|
|
|
|
Instruction instr = popInstr();
|
|
|
|
gprintln("BranchIdx: "~to!(string)(branchIdx));
|
|
|
|
gprintln("Instr is: "~to!(string)(instr));
|
|
|
|
branchConditionInstr = cast(Value)instr;
|
|
|
|
assert(branchConditionInstr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get the number of body instructions to pop
|
|
|
|
ulong bodyCount = branch.getBody().length;
|
|
|
|
ulong i = 0;
|
|
|
|
Instruction[] bodyInstructions;
|
|
|
|
|
|
|
|
|
|
|
|
while(i < bodyCount)
|
|
|
|
{
|
|
|
|
Instruction bodyInstr = tailPopInstr();
|
|
|
|
bodyInstructions~=bodyInstr;
|
|
|
|
|
|
|
|
gprintln("tailPopp'd("~to!(string)(i)~"/"~to!(string)(bodyCount-1)~"): "~to!(string)(bodyInstr));
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reverse the body instructions (correct ordering)
|
|
|
|
bodyInstructions=reverse(bodyInstructions);
|
|
|
|
|
|
|
|
// Create the branch instruction (coupling the condition instruction and body instructions)
|
|
|
|
branchInstructions~=new BranchInstruction(branchConditionInstr, bodyInstructions);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if(branchIdx == 0)
|
|
|
|
{
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-01-04 10:03:50 +00:00
|
|
|
// Reverse the list to be in the correct order (it was computed backwards)
|
2022-12-19 13:37:55 +00:00
|
|
|
branchInstructions=reverse(branchInstructions);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Code gen
|
|
|
|
*
|
|
|
|
* 1. Create the IfStatementInstruction containing the BranchInstruction[](s)
|
|
|
|
* 2. Set the context
|
|
|
|
* 3. Add the instruction
|
|
|
|
*/
|
|
|
|
IfStatementInstruction ifStatementInstruction = new IfStatementInstruction(branchInstructions);
|
|
|
|
ifStatementInstruction.context = ifStatement.getContext();
|
|
|
|
addInstrB(ifStatementInstruction);
|
|
|
|
|
|
|
|
gprintln("If!");
|
|
|
|
}
|
2023-01-04 10:03:50 +00:00
|
|
|
/**
|
|
|
|
* While loop (WhileLoop)
|
|
|
|
*/
|
|
|
|
else if(cast(WhileLoop)statement)
|
|
|
|
{
|
|
|
|
WhileLoop whileLoop = cast(WhileLoop)statement;
|
|
|
|
|
2023-01-11 08:43:29 +00:00
|
|
|
// FIXME: Do-while loops are still being considered in terms of dependency construction
|
|
|
|
if(whileLoop.isDoWhile)
|
|
|
|
{
|
|
|
|
gprintln("Still looking at dependency construction in this thing (do while loops )");
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
|
2023-01-04 10:03:50 +00:00
|
|
|
Branch branch = whileLoop.getBranch();
|
|
|
|
|
|
|
|
/* The condition `Value` instruction should be on the stack */
|
|
|
|
Value valueInstrCondition = cast(Value)popInstr();
|
|
|
|
assert(valueInstrCondition);
|
|
|
|
|
|
|
|
/* Process the body of the while-loop with tail-popping followed by a reverse */
|
|
|
|
Instruction[] bodyInstructions;
|
|
|
|
ulong bodyLen = branch.getBody().length;
|
|
|
|
ulong bodyIdx = 0;
|
|
|
|
|
|
|
|
while(bodyIdx < bodyLen)
|
|
|
|
{
|
|
|
|
Instruction bodyInstr = tailPopInstr();
|
|
|
|
bodyInstructions~=bodyInstr;
|
|
|
|
bodyIdx++;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reverse the list to be in the correct order (it was computed backwards)
|
|
|
|
bodyInstructions=reverse(bodyInstructions);
|
|
|
|
|
|
|
|
|
|
|
|
// Create a branch instruction coupling the condition instruction + body instructions (in corrected order)
|
|
|
|
BranchInstruction branchInstr = new BranchInstruction(valueInstrCondition, bodyInstructions);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Code gen
|
|
|
|
*
|
|
|
|
* 1. Create the WhileLoopInstruction containing the BranchInstruction
|
|
|
|
* 2. Set the context
|
|
|
|
* 3. Add the instruction
|
|
|
|
*/
|
|
|
|
WhileLoopInstruction whileLoopInstruction = new WhileLoopInstruction(branchInstr);
|
|
|
|
whileLoopInstruction.context = whileLoop.getContext();
|
|
|
|
addInstrB(whileLoopInstruction);
|
|
|
|
}
|
2023-01-11 08:43:29 +00:00
|
|
|
/**
|
|
|
|
* For loop (ForLoop)
|
|
|
|
*/
|
|
|
|
else if(cast(ForLoop)statement)
|
|
|
|
{
|
|
|
|
ForLoop forLoop = cast(ForLoop)statement;
|
|
|
|
|
|
|
|
/* Pop-off the Value-instruction for the condition */
|
|
|
|
Value valueInstrCondition = cast(Value)popInstr();
|
|
|
|
assert(valueInstrCondition);
|
|
|
|
|
|
|
|
/* Calculate the number of instructions representing the body to tailPopInstr() */
|
|
|
|
ulong bodyTailPopNumber = forLoop.getBranch().getStatements().length;
|
|
|
|
gprintln("bodyTailPopNumber: "~to!(string)(bodyTailPopNumber));
|
|
|
|
|
|
|
|
/* Pop off the body instructions, then reverse final list */
|
|
|
|
Instruction[] bodyInstructions;
|
|
|
|
for(ulong idx = 0; idx < bodyTailPopNumber; idx++)
|
|
|
|
{
|
|
|
|
bodyInstructions ~= tailPopInstr();
|
|
|
|
}
|
|
|
|
bodyInstructions = reverse(bodyInstructions);
|
|
|
|
|
|
|
|
// Create a branch instruction coupling the condition instruction + body instructions (in corrected order)
|
|
|
|
BranchInstruction branchInstr = new BranchInstruction(valueInstrCondition, bodyInstructions);
|
|
|
|
|
|
|
|
|
|
|
|
/* If there is a pre-run instruction */
|
|
|
|
Instruction preRunInstruction;
|
|
|
|
if(forLoop.hasPreRunStatement())
|
|
|
|
{
|
|
|
|
preRunInstruction = tailPopInstr();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Code gen
|
|
|
|
*
|
|
|
|
* 1. Create the ForLoopInstruction containing the BranchInstruction and
|
|
|
|
* preRunInstruction
|
|
|
|
* 2. Set the context
|
|
|
|
* 3. Add the instruction
|
|
|
|
*/
|
|
|
|
ForLoopInstruction forLoopInstruction = new ForLoopInstruction(branchInstr, preRunInstruction);
|
|
|
|
forLoopInstruction.context = forLoop.context;
|
|
|
|
addInstrB(forLoopInstruction);
|
|
|
|
}
|
2022-12-19 13:37:55 +00:00
|
|
|
/* Branch */
|
|
|
|
else if(cast(Branch)statement)
|
|
|
|
{
|
|
|
|
Branch branch = cast(Branch)statement;
|
|
|
|
|
|
|
|
gprintln("Look at that y'all, cause this is it: "~to!(string)(branch));
|
|
|
|
}
|
2023-01-12 08:53:48 +00:00
|
|
|
/**
|
|
|
|
* Dereferencing pointer assignment statement (PointerDereferenceAssignment)
|
|
|
|
*/
|
|
|
|
else if(cast(PointerDereferenceAssignment)statement)
|
|
|
|
{
|
|
|
|
PointerDereferenceAssignment ptrDerefAss = cast(PointerDereferenceAssignment)statement;
|
|
|
|
|
|
|
|
/* Pop off the pointer dereference expression instruction (LHS) */
|
|
|
|
Value lhsPtrExprInstr = cast(Value)popInstr();
|
|
|
|
assert(lhsPtrExprInstr);
|
|
|
|
|
|
|
|
/* Pop off the assignment instruction (RHS expression) */
|
|
|
|
Value rhsExprInstr = cast(Value)popInstr();
|
|
|
|
assert(rhsExprInstr);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Code gen
|
|
|
|
*
|
|
|
|
* 1. Create the PointerDereferenceAssignmentInstruction containing the `lhsPtrExprInstr`
|
2023-01-12 11:31:42 +00:00
|
|
|
* and `rhsExprInstr`. Also set the pointer depth.
|
2023-01-12 08:53:48 +00:00
|
|
|
* 2. Set the context
|
|
|
|
* 3. Add the instruction
|
|
|
|
*/
|
|
|
|
PointerDereferenceAssignmentInstruction pointerDereferenceAssignmentInstruction = new PointerDereferenceAssignmentInstruction(lhsPtrExprInstr, rhsExprInstr, ptrDerefAss.getDerefCount());
|
|
|
|
pointerDereferenceAssignmentInstruction.context = ptrDerefAss.context;
|
|
|
|
addInstrB(pointerDereferenceAssignmentInstruction);
|
|
|
|
}
|
2023-01-13 08:49:47 +00:00
|
|
|
/**
|
|
|
|
* Discard statement (DiscardStatement)
|
|
|
|
*/
|
|
|
|
else if(cast(DiscardStatement)statement)
|
|
|
|
{
|
|
|
|
DiscardStatement discardStatement = cast(DiscardStatement)statement;
|
|
|
|
|
|
|
|
/* Pop off a Value instruction */
|
|
|
|
Value exprInstr = cast(Value)popInstr();
|
|
|
|
assert(exprInstr);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Code gen
|
|
|
|
*
|
|
|
|
* 1. Create the DiscardInstruction containing the Value instruction
|
|
|
|
* `exprInstr`
|
|
|
|
* 2. Set the context
|
|
|
|
* 3. Add the instruction
|
|
|
|
*/
|
|
|
|
DiscardInstruction discardInstruction = new DiscardInstruction(exprInstr);
|
|
|
|
discardInstruction.context = discardStatement.context;
|
|
|
|
addInstrB(discardInstruction);
|
|
|
|
}
|
2022-09-18 19:52:11 +00:00
|
|
|
/* Case of no matches */
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gprintln("NO MATCHES FIX ME FOR: "~to!(string)(statement), DebugType.WARNING);
|
|
|
|
}
|
2021-10-27 18:57:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-10-25 20:31:07 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2022-10-15 20:03:50 +00:00
|
|
|
/**
|
|
|
|
* Perform type-checking and code-generation
|
|
|
|
* on the provided linearized dependency tree
|
|
|
|
*/
|
2021-10-25 20:31:07 +00:00
|
|
|
private void doTypeCheck(DNode[] actionList)
|
|
|
|
{
|
2022-11-26 14:26:51 +00:00
|
|
|
/* Print the action list provided to us */
|
2021-10-25 14:55:43 +00:00
|
|
|
gprintln("Action list: "~to!(string)(actionList));
|
2022-11-26 14:26:51 +00:00
|
|
|
|
|
|
|
/**
|
2022-11-26 14:28:27 +00:00
|
|
|
* Loop through each dependency-node in the action list
|
2022-11-26 14:26:51 +00:00
|
|
|
* and perform the type-checking/code generation
|
|
|
|
*/
|
2021-10-25 14:55:43 +00:00
|
|
|
foreach(DNode node; actionList)
|
|
|
|
{
|
|
|
|
gprintln("Process: "~to!(string)(node));
|
2021-10-25 15:03:36 +00:00
|
|
|
|
2021-10-26 15:17:53 +00:00
|
|
|
/* Print the code queue each time */
|
2022-09-18 19:52:11 +00:00
|
|
|
gprintln("sdfhjkhdsfjhfdsj 1");
|
2021-10-26 15:17:53 +00:00
|
|
|
printCodeQueue();
|
2022-09-18 19:52:11 +00:00
|
|
|
gprintln("sdfhjkhdsfjhfdsj 2");
|
2021-10-26 15:17:53 +00:00
|
|
|
|
2022-11-26 14:26:51 +00:00
|
|
|
/* Type-check/code-gen this node */
|
2021-10-27 19:12:00 +00:00
|
|
|
typeCheckThing(node);
|
2022-11-26 14:31:33 +00:00
|
|
|
writeln("--------------");
|
2021-10-25 14:55:43 +00:00
|
|
|
}
|
2021-10-26 18:32:47 +00:00
|
|
|
|
2022-11-26 14:31:33 +00:00
|
|
|
|
|
|
|
writeln("\n################# Results from type-checking/code-generation #################\n");
|
|
|
|
|
2022-10-15 19:57:03 +00:00
|
|
|
|
|
|
|
/* Print the init queue */
|
|
|
|
gprintln("<<<<< FINAL ALLOCATE QUEUE >>>>>");
|
|
|
|
printInitQueue();
|
|
|
|
|
|
|
|
/* Print the code queue */
|
2021-10-26 18:32:47 +00:00
|
|
|
gprintln("<<<<< FINAL CODE QUEUE >>>>>");
|
2022-10-15 19:57:03 +00:00
|
|
|
printCodeQueue();
|
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 */
|
2022-07-25 17:15:27 +00:00
|
|
|
foundType = getBuiltInType(this, typeString);
|
2021-04-26 08:01:53 +00:00
|
|
|
|
|
|
|
/* 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
|
|
|
}
|
|
|
|
|
2023-01-12 21:34:24 +00:00
|
|
|
// TODO: What actually is the point of this? It literally generates a `Class[]`
|
|
|
|
// ... and then tosses it after returning. (See issue "Dead code tracking" #83)
|
2021-04-15 15:58:27 +00:00
|
|
|
private void checkDefinitionTypes(Container c)
|
|
|
|
{
|
2021-04-23 12:14:09 +00:00
|
|
|
/* Check variables and functions (TypedEntities) declarations */
|
2021-11-02 07:32:52 +00:00
|
|
|
// checkTypedEntitiesTypeNames(c);
|
2021-04-15 15:58:27 +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
|
|
|
{
|
2023-01-15 18:48:40 +00:00
|
|
|
/* Process all pseudo entities of the given module */
|
|
|
|
processPseudoEntities(modulle);
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-01-15 18:48:40 +00:00
|
|
|
private void processPseudoEntities(Container c)
|
|
|
|
{
|
|
|
|
/* Collect all `extern` declarations */
|
|
|
|
ExternStmt[] externDeclarations;
|
|
|
|
foreach(Statement curStatement; c.getStatements())
|
|
|
|
{
|
|
|
|
if(cast(ExternStmt)curStatement)
|
|
|
|
{
|
|
|
|
externDeclarations ~= cast(ExternStmt)curStatement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: We could remove them from the container too, means less loops in dependency/core.d
|
|
|
|
|
|
|
|
/* Add each Entity to the container */
|
|
|
|
foreach(ExternStmt curExternStmt; externDeclarations)
|
|
|
|
{
|
|
|
|
SymbolType externType = curExternStmt.getExternType();
|
|
|
|
string externalSymbolName = curExternStmt.getExternalName();
|
|
|
|
Entity pseudoEntity = curExternStmt.getPseudoEntity();
|
|
|
|
|
|
|
|
/* Set the embedded pseudo entity's parent to that of the container */
|
|
|
|
pseudoEntity.parentTo(c);
|
|
|
|
|
|
|
|
c.addStatements([pseudoEntity]);
|
|
|
|
|
|
|
|
assert(this.getResolver().resolveBest(c, externalSymbolName));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-03-30 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
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2021-10-27 13:52:43 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* TODO: Create a version of the below function that possibly
|
|
|
|
* returns the list of Statement[]s ordered like below but
|
|
|
|
* via a weighting system rather
|
|
|
|
*/
|
|
|
|
public Statement[] getContainerMembers_W(Container c)
|
|
|
|
{
|
|
|
|
/* Statements */
|
|
|
|
Statement[] statements;
|
|
|
|
|
|
|
|
/* TODO: Implement me */
|
|
|
|
|
|
|
|
return statements;
|
|
|
|
}
|
|
|
|
|
2021-04-01 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-10-27 18:57:30 +00:00
|
|
|
if (statement !is null && cast(Entity) statement)
|
2021-04-01 13:21:10 +00:00
|
|
|
{
|
2021-10-27 18:57:30 +00:00
|
|
|
entities ~= cast(Entity) statement;
|
2021-04-01 13:21:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-27 18:57:30 +00:00
|
|
|
// /* Get all classes */
|
|
|
|
// foreach (Statement statement; c.getStatements())
|
|
|
|
// {
|
|
|
|
// if (statement !is null && cast(Clazz) statement)
|
|
|
|
// {
|
|
|
|
// entities ~= cast(Clazz) statement;
|
|
|
|
// }
|
|
|
|
// }
|
2021-04-01 13:21:10 +00:00
|
|
|
|
2021-10-27 18:57:30 +00:00
|
|
|
// /* Get all functions */
|
|
|
|
// foreach (Statement statement; c.getStatements())
|
|
|
|
// {
|
|
|
|
// if (statement !is null && cast(Function) statement)
|
|
|
|
// {
|
|
|
|
// entities ~= cast(Function) statement;
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// /* Get all variables */
|
|
|
|
// foreach (Statement statement; c.getStatements())
|
|
|
|
// {
|
|
|
|
// if (statement !is null && cast(Variable) statement)
|
|
|
|
// {
|
|
|
|
// entities ~= cast(Variable) statement;
|
|
|
|
// }
|
|
|
|
// }
|
2021-04-01 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-11-02 07:34:40 +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();
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:34:40 +00:00
|
|
|
/* Shouldn't reach here, collision exception MUST occur */
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
catch (CollidingNameException e)
|
|
|
|
{
|
|
|
|
/* Make sure the member y.y collided with root container (module) y */
|
|
|
|
assert(e.defined == container);
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 08:52:22 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
2021-11-02 07:34:40 +00:00
|
|
|
/* Test name colliding with container name (2/3) [module, nested collider] */
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
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();
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:34:40 +00:00
|
|
|
/* Shouldn't reach here, collision exception MUST occur */
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
catch (CollidingNameException e)
|
|
|
|
{
|
|
|
|
/* Make sure the member y.a.b.c.y collided with root container (module) y */
|
|
|
|
assert(e.defined == container);
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:34:57 +00:00
|
|
|
/* Test name colliding with container name (3/3) [container (non-module), nested collider] */
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.parsing.core;
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:34:57 +00:00
|
|
|
string sourceFile = "source/tlang/testing/collide_container_non_module.t";
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:34:57 +00:00
|
|
|
File sourceFileFile;
|
|
|
|
sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
|
|
|
|
ulong fileSize = sourceFileFile.size();
|
|
|
|
byte[] fileBytes;
|
|
|
|
fileBytes.length = fileSize;
|
|
|
|
fileBytes = sourceFileFile.rawRead(fileBytes);
|
|
|
|
sourceFileFile.close();
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:34:57 +00:00
|
|
|
string sourceCode = cast(string) fileBytes;
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
currentLexer.performLex();
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:34:57 +00:00
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
TypeChecker typeChecker = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
/* Setup testing variables */
|
|
|
|
Entity container = typeChecker.getResolver().resolveBest(typeChecker.getModule, "a.b.c");
|
|
|
|
Entity colliderMember = typeChecker.getResolver().resolveBest(typeChecker.getModule, "a.b.c.c");
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
/* Perform test */
|
|
|
|
typeChecker.beginCheck();
|
|
|
|
|
|
|
|
/* Shouldn't reach here, collision exception MUST occur */
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
catch (CollidingNameException e)
|
|
|
|
{
|
|
|
|
/* Make sure the member a.b.c.c collided with a.b.c container */
|
|
|
|
assert(e.defined == container);
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:27 +00:00
|
|
|
/* Test name colliding with member */
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.parsing.core;
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:27 +00:00
|
|
|
string sourceFile = "source/tlang/testing/collide_member.t";
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:27 +00:00
|
|
|
File sourceFileFile;
|
|
|
|
sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
|
|
|
|
ulong fileSize = sourceFileFile.size();
|
|
|
|
byte[] fileBytes;
|
|
|
|
fileBytes.length = fileSize;
|
|
|
|
fileBytes = sourceFileFile.rawRead(fileBytes);
|
|
|
|
sourceFileFile.close();
|
|
|
|
|
|
|
|
string sourceCode = cast(string) fileBytes;
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
currentLexer.performLex();
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
TypeChecker typeChecker = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
/* Setup testing variables */
|
|
|
|
Entity memberFirst = typeChecker.getResolver().resolveBest(typeChecker.getModule, "a.b");
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
/* Perform test */
|
|
|
|
typeChecker.beginCheck();
|
|
|
|
|
|
|
|
/* Shouldn't reach here, collision exception MUST occur */
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
catch (CollidingNameException e)
|
|
|
|
{
|
|
|
|
/* Make sure the member a.b.c.c collided with a.b.c container */
|
|
|
|
assert(e.attempted != memberFirst);
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:48 +00:00
|
|
|
/* Test name colliding with member (check that the member defined is class (precendence test)) */
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.parsing.core;
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:48 +00:00
|
|
|
string sourceFile = "source/tlang/testing/precedence_collision_test.t";
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:48 +00:00
|
|
|
File sourceFileFile;
|
|
|
|
sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
|
|
|
|
ulong fileSize = sourceFileFile.size();
|
|
|
|
byte[] fileBytes;
|
|
|
|
fileBytes.length = fileSize;
|
|
|
|
fileBytes = sourceFileFile.rawRead(fileBytes);
|
|
|
|
sourceFileFile.close();
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:48 +00:00
|
|
|
string sourceCode = cast(string) fileBytes;
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
currentLexer.performLex();
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:48 +00:00
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
TypeChecker typeChecker = new TypeChecker(modulle);
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:35:48 +00:00
|
|
|
/* Setup testing variables */
|
|
|
|
Entity ourClassA = typeChecker.getResolver().resolveBest(typeChecker.getModule, "a");
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
/* Perform test */
|
|
|
|
typeChecker.beginCheck();
|
|
|
|
|
|
|
|
/* Shouldn't reach here, collision exception MUST occur */
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
catch (CollidingNameException e)
|
|
|
|
{
|
|
|
|
/* Make sure the member attempted was Variable and defined was Clazz */
|
|
|
|
assert(cast(Variable)e.attempted);
|
|
|
|
assert(cast(Clazz)e.defined);
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 08:52:22 +00:00
|
|
|
|
|
|
|
|
2021-11-02 07:36:15 +00:00
|
|
|
/* Test name colliding with container name (1/2) */
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.parsing.core;
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:36:15 +00:00
|
|
|
string sourceFile = "source/tlang/testing/collide_container.t";
|
2021-06-08 08:52:22 +00:00
|
|
|
|
2021-11-02 07:36:15 +00:00
|
|
|
File sourceFileFile;
|
|
|
|
sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
|
|
|
|
ulong fileSize = sourceFileFile.size();
|
|
|
|
byte[] fileBytes;
|
|
|
|
fileBytes.length = fileSize;
|
|
|
|
fileBytes = sourceFileFile.rawRead(fileBytes);
|
|
|
|
sourceFileFile.close();
|
|
|
|
|
|
|
|
string sourceCode = cast(string) fileBytes;
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
currentLexer.performLex();
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
TypeChecker typeChecker = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
/* Setup testing variables */
|
|
|
|
Entity container = typeChecker.getResolver().resolveBest(typeChecker.getModule, "y");
|
|
|
|
Entity colliderMember = typeChecker.getResolver().resolveBest(typeChecker.getModule, "y.y");
|
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
/* Perform test */
|
|
|
|
typeChecker.beginCheck();
|
|
|
|
|
|
|
|
/* Shouldn't reach here, collision exception MUST occur */
|
|
|
|
assert(false);
|
|
|
|
}
|
|
|
|
catch (CollidingNameException e)
|
|
|
|
{
|
|
|
|
/* Make sure the member y.y collided with root container (module) y */
|
|
|
|
assert(e.defined == container);
|
|
|
|
}
|
2022-01-20 08:43:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Test name colliding with container name (1/2) */
|
2023-01-13 09:13:14 +00:00
|
|
|
// TODO: Re-enable this when we take a look at the `discards` - for now discards at module level are not allowed
|
|
|
|
// ... therefore this unittest fails - otherwise it would have normally passed
|
|
|
|
// unittest
|
|
|
|
// {
|
|
|
|
// import std.file;
|
|
|
|
// import std.stdio;
|
|
|
|
// import compiler.lexer;
|
|
|
|
// import compiler.parsing.core;
|
|
|
|
|
|
|
|
// string sourceFile = "source/tlang/testing/typecheck/simple_dependence_correct7.t";
|
|
|
|
|
|
|
|
// File sourceFileFile;
|
|
|
|
// sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
|
|
|
|
// ulong fileSize = sourceFileFile.size();
|
|
|
|
// byte[] fileBytes;
|
|
|
|
// fileBytes.length = fileSize;
|
|
|
|
// fileBytes = sourceFileFile.rawRead(fileBytes);
|
|
|
|
// sourceFileFile.close();
|
|
|
|
|
|
|
|
// string sourceCode = cast(string) fileBytes;
|
|
|
|
// Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
// currentLexer.performLex();
|
|
|
|
|
|
|
|
// Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
// Module modulle = parser.parse();
|
|
|
|
// TypeChecker typeChecker = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
// /* Perform test */
|
|
|
|
// typeChecker.beginCheck();
|
|
|
|
|
|
|
|
// /* TODO: Insert checks here */
|
|
|
|
// }
|
2022-04-12 09:11:54 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Code generation and typechecking
|
|
|
|
*
|
|
|
|
* Testing file: `simple_function_call.t`
|
|
|
|
*/
|
|
|
|
unittest
|
|
|
|
{
|
|
|
|
import std.file;
|
|
|
|
import std.stdio;
|
|
|
|
import compiler.lexer;
|
|
|
|
import compiler.parsing.core;
|
|
|
|
|
|
|
|
string sourceFile = "source/tlang/testing/typecheck/simple_function_call.t";
|
|
|
|
|
|
|
|
File sourceFileFile;
|
|
|
|
sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */
|
|
|
|
ulong fileSize = sourceFileFile.size();
|
|
|
|
byte[] fileBytes;
|
|
|
|
fileBytes.length = fileSize;
|
|
|
|
fileBytes = sourceFileFile.rawRead(fileBytes);
|
|
|
|
sourceFileFile.close();
|
|
|
|
|
|
|
|
string sourceCode = cast(string) fileBytes;
|
|
|
|
Lexer currentLexer = new Lexer(sourceCode);
|
|
|
|
currentLexer.performLex();
|
|
|
|
|
|
|
|
Parser parser = new Parser(currentLexer.getTokens());
|
|
|
|
Module modulle = parser.parse();
|
|
|
|
TypeChecker typeChecker = new TypeChecker(modulle);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Perform test */
|
|
|
|
typeChecker.beginCheck();
|
|
|
|
|
|
|
|
/* TODO: Actually test generated code queue */
|
2021-11-09 17:16:51 +00:00
|
|
|
}
|