Merge branch 'extern_symbols' into vardec_varass_dependency

This commit is contained in:
Tristan B. Velloza Kildaire 2023-01-20 14:52:14 +02:00
commit 8edc03e3f3
8 changed files with 397 additions and 100 deletions

View File

@ -127,23 +127,33 @@ public final class DCodeEmitter : CodeEmitter
gprintln("Wazza contect: "~to!(string)(context.container));
auto typedEntityVariable = context.tc.getResolver().resolveBest(context.getContainer(), varAs.varName); //TODO: Remove `auto`
string renamedSymbol = SymbolMapper.symbolLookup(typedEntityVariable);
// If we are needed as part of a VariabvleDeclaration-with-assignment
if(varDecWantsConsumeVarAss)
/* If it is not external */
if(!typedEntityVariable.isExternal())
{
// Generate the code to emit (only the RHS of the = sign)
string emitCode = transform(varAs.data);
string renamedSymbol = SymbolMapper.symbolLookup(typedEntityVariable);
// Reset flag
varDecWantsConsumeVarAss = false;
// If we are needed as part of a VariabvleDeclaration-with-assignment
if(varDecWantsConsumeVarAss)
{
// Generate the code to emit (only the RHS of the = sign)
string emitCode = transform(varAs.data);
return emitCode;
// Reset flag
varDecWantsConsumeVarAss = false;
return emitCode;
}
return renamedSymbol~" = "~transform(varAs.data)~";";
}
/* If it is external */
else
{
return typedEntityVariable.getName()~" = "~transform(varAs.data)~";";
}
return renamedSymbol~" = "~transform(varAs.data)~";";
}
/* VariableDeclaration */
else if(cast(VariableDeclaration)instruction)
@ -155,36 +165,44 @@ public final class DCodeEmitter : CodeEmitter
Variable typedEntityVariable = cast(Variable)context.tc.getResolver().resolveBest(context.getContainer(), varDecInstr.varName); //TODO: Remove `auto`
//NOTE: We should remove all dots from generated symbol names as it won't be valid C (I don't want to say C because
// a custom CodeEmitter should be allowed, so let's call it a general rule)
//
//simple_variables.x -> simple_variables_x
//NOTE: We may need to create a symbol table actually and add to that and use that as these names
//could get out of hand (too long)
// NOTE: Best would be identity-mapping Entity's to a name
string renamedSymbol = SymbolMapper.symbolLookup(typedEntityVariable);
// Check to see if this declaration has an assignment attached
if(typedEntityVariable.getAssignment())
/* If the variable is not external */
if(!typedEntityVariable.isExternal())
{
// Set flag to expect different transform generation for VariableAssignment
varDecWantsConsumeVarAss = true;
//NOTE: We should remove all dots from generated symbol names as it won't be valid C (I don't want to say C because
// a custom CodeEmitter should be allowed, so let's call it a general rule)
//
//simple_variables.x -> simple_variables_x
//NOTE: We may need to create a symbol table actually and add to that and use that as these names
//could get out of hand (too long)
// NOTE: Best would be identity-mapping Entity's to a name
string renamedSymbol = SymbolMapper.symbolLookup(typedEntityVariable);
// Fetch the variable assignment instruction
// gprintln("Before crash: "~to!(string)(getCurrentInstruction()));
// nextInstruction();
// Instruction varAssInstr = getCurrentInstruction();
VariableAssignmentInstr varAssInstr = varDecInstr.getAssignmentInstr();
// Generate the code to emit
return typeTransform(cast(Type)varDecInstr.varType)~" "~renamedSymbol~" = "~transform(varAssInstr)~";";
// Check to see if this declaration has an assignment attached
if(typedEntityVariable.getAssignment())
{
// Set flag to expect different transform generation for VariableAssignment
varDecWantsConsumeVarAss = true;
// Fetch the variable assignment instruction
// gprintln("Before crash: "~to!(string)(getCurrentInstruction()));
// nextInstruction();
// Instruction varAssInstr = getCurrentInstruction();
VariableAssignmentInstr varAssInstr = varDecInstr.getAssignmentInstr();
// Generate the code to emit
return typeTransform(cast(Type)varDecInstr.varType)~" "~renamedSymbol~" = "~transform(varAssInstr)~";";
}
return typeTransform(cast(Type)varDecInstr.varType)~" "~renamedSymbol~";";
}
/* If the variable is external */
else
{
return "extern "~typeTransform(cast(Type)varDecInstr.varType)~" "~typedEntityVariable.getName()~";";
}
return typeTransform(cast(Type)varDecInstr.varType)~" "~renamedSymbol~";";
}
/* LiteralValue */
else if(cast(LiteralValue)instruction)
@ -205,12 +223,21 @@ public final class DCodeEmitter : CodeEmitter
Variable typedEntityVariable = cast(Variable)context.tc.getResolver().resolveBest(context.getContainer(), fetchValueVarInstr.varName); //TODO: Remove `auto`
//TODO: THis is giving me kak (see issue #54), it's generating name but trying to do it for the given container, relative to it
//TODO: We might need a version of generateName that is like generatenamebest (currently it acts like generatename, within)
/* If it is not external */
if(!typedEntityVariable.isExternal())
{
//TODO: THis is giving me kak (see issue #54), it's generating name but trying to do it for the given container, relative to it
//TODO: We might need a version of generateName that is like generatenamebest (currently it acts like generatename, within)
string renamedSymbol = SymbolMapper.symbolLookup(typedEntityVariable);
string renamedSymbol = SymbolMapper.symbolLookup(typedEntityVariable);
return renamedSymbol;
return renamedSymbol;
}
/* If it is external */
else
{
return typedEntityVariable.getName();
}
}
/* BinOpInstr */
else if(cast(BinOpInstr)instruction)
@ -499,6 +526,8 @@ public final class DCodeEmitter : CodeEmitter
return emit;
}
// TODO: MAAAAN we don't even have this yet
// else if(cast(StringExpression))
return "<TODO: Base emit: "~to!(string)(instruction)~">";
}
@ -658,6 +687,12 @@ public final class DCodeEmitter : CodeEmitter
// )
signature~=")";
// If the function is marked as external then place `extern` infront
if(func.isExternal())
{
signature = "extern "~signature;
}
return signature;
}
@ -686,26 +721,35 @@ public final class DCodeEmitter : CodeEmitter
//TODO: And what about methods defined in classes? Those should technically be here too
Function functionEntity = cast(Function)typeChecker.getResolver().resolveBest(typeChecker.getModule(), functionName); //TODO: Remove `auto`
// Emit the function signature
file.writeln(generateSignature(functionEntity));
// Emit opening curly brace
file.writeln(getCharacter(SymbolType.OCURLY));
// Emit body
while(hasInstructions())
// If the Entity is NOT external then emit the signature+body
if(!functionEntity.isExternal())
{
Instruction curFuncBodyInstr = getCurrentInstruction();
// Emit the function signature
file.writeln(generateSignature(functionEntity));
string emit = transform(curFuncBodyInstr);
gprintln("emitFunctionDefinition("~functionName~"): Emit: "~emit);
file.writeln("\t"~emit);
nextInstruction();
// Emit opening curly brace
file.writeln(getCharacter(SymbolType.OCURLY));
// Emit body
while(hasInstructions())
{
Instruction curFuncBodyInstr = getCurrentInstruction();
string emit = transform(curFuncBodyInstr);
gprintln("emitFunctionDefinition("~functionName~"): Emit: "~emit);
file.writeln("\t"~emit);
nextInstruction();
}
// Emit closing curly brace
file.writeln(getCharacter(SymbolType.CCURLY));
}
// If the Entity IS external then don't emit anything as the signature would have been emitted via a prorotype earlier with `emitPrototypes()`
else
{
// Do nothing
}
// Emit closing curly brace
file.writeln(getCharacter(SymbolType.CCURLY));
}
private void emitCodeQueue()
@ -790,6 +834,20 @@ int main()
assert(t_87bc875d0b65f741b69fb100a0edebc7 == 4);
assert(retValue == 6);
return 0;
}`);
}
else if(cmp(typeChecker.getModule().getName(), "simple_extern") == 0)
{
file.writeln(`
#include<stdio.h>
#include<assert.h>
int main()
{
test();
return 0;
}`);
}

View File

@ -450,6 +450,24 @@ public final class Parser
{
previousToken();
ret = parseTypedDeclaration();
/* If it is a function definition, then do nothing */
if(cast(Function)ret)
{
// The ending `}` would have already been consumed
}
/* If it is a variable declaration then */
else if(cast(Variable)ret)
{
/* Expect a semicolon and consume it */
expect(SymbolType.SEMICOLON, getCurrentToken());
nextToken();
}
/* This should never happen */
else
{
assert(false);
}
}
/* Assignment */
else if(type == SymbolType.ASSIGN)
@ -462,7 +480,6 @@ public final class Parser
{
gprintln(getCurrentToken);
expect("Error expected ( for var/func def");
}
@ -854,7 +871,7 @@ public final class Parser
VariableParameter[] params;
}
private funcDefPair parseFuncDef()
private funcDefPair parseFuncDef(bool wantsBody = true)
{
gprintln("parseFuncDef(): Enter", DebugType.WARNING);
@ -938,11 +955,21 @@ public final class Parser
}
}
expect(SymbolType.OCURLY, getCurrentToken());
/* If a body is required then allow it */
if(wantsBody)
{
expect(SymbolType.OCURLY, getCurrentToken());
/* Parse the body (and it leaves ONLY when it gets the correct symbol, no expect needed) */
statements = parseBody();
nextToken();
/* Parse the body (and it leaves ONLY when it gets the correct symbol, no expect needed) */
statements = parseBody();
nextToken();
}
/* If no body is requested */
else
{
expect(SymbolType.SEMICOLON, getCurrentToken());
}
gprintln("ParseFuncDef: Parameter count: " ~ to!(string)(parameterCount));
gprintln("parseFuncDef(): Leave", DebugType.WARNING);
@ -1294,7 +1321,7 @@ public final class Parser
return retExpression[0];
}
private TypedEntity parseTypedDeclaration()
private TypedEntity parseTypedDeclaration(bool wantsBody = true, bool allowVarDec = true, bool allowFuncDef = true)
{
gprintln("parseTypedDeclaration(): Enter", DebugType.WARNING);
@ -1336,63 +1363,88 @@ public final class Parser
gprintln("ParseTypedDec: SymbolType=" ~ to!(string)(symbolType));
if (symbolType == SymbolType.LBRACE)
{
funcDefPair pair = parseFuncDef();
// Only continue is function definitions are allowed
if(allowFuncDef)
{
/* Will consume the `}` (or `;` if wantsBody-false) */
funcDefPair pair = parseFuncDef(wantsBody);
generated = new Function(identifier, type, pair.bodyStatements, pair.params);
import std.stdio;
writeln(to!(string)((cast(Function)generated).getVariables()));
generated = new Function(identifier, type, pair.bodyStatements, pair.params);
import std.stdio;
writeln(to!(string)((cast(Function)generated).getVariables()));
// Parent the parameters of the function to the Function
parentToContainer(cast(Container)generated, cast(Statement[])pair.params);
// Parent the parameters of the function to the Function
parentToContainer(cast(Container)generated, cast(Statement[])pair.params);
// Parent the statements that make up the function to the Function
parentToContainer(cast(Container)generated, pair.bodyStatements);
// Parent the statements that make up the function to the Function
parentToContainer(cast(Container)generated, pair.bodyStatements);
}
else
{
expect("Function definitions not allowed");
}
}
/* Check for semi-colon (var dec) */
else if (symbolType == SymbolType.SEMICOLON)
{
nextToken();
gprintln("ParseTypedDec: VariableDeclaration: (Type: " ~ type ~ ", Identifier: " ~ identifier ~ ")",
DebugType.WARNING);
// Only continue if variable declarations are allowed
if(allowVarDec)
{
gprintln("Semi: "~to!(string)(getCurrentToken()));
gprintln("Semi: "~to!(string)(getCurrentToken()));
gprintln("ParseTypedDec: VariableDeclaration: (Type: " ~ type ~ ", Identifier: " ~ identifier ~ ")",
DebugType.WARNING);
generated = new Variable(type, identifier);
generated = new Variable(type, identifier);
}
else
{
expect("Variables declarations are not allowed.");
}
}
/* Check for `=` (var dec) */
else if (symbolType == SymbolType.ASSIGN)
{
nextToken();
// Only continue if variable declarations are allowed
if(allowVarDec)
{
// Only continue if assignments are allowed
if(wantsBody)
{
/* Consume the `=` token */
nextToken();
/* Now parse an expression */
Expression expression = parseExpression();
/* Now parse an expression */
Expression expression = parseExpression();
VariableAssignment varAssign = new VariableAssignment(expression);
VariableAssignment varAssign = new VariableAssignment(expression);
/**
* The symbol that returned us from `parseExpression` must
* be a semi-colon
*/
expect(SymbolType.SEMICOLON, getCurrentToken());
gprintln("ParseTypedDec: VariableDeclarationWithAssingment: (Type: "
~ type ~ ", Identifier: " ~ identifier ~ ")", DebugType.WARNING);
Variable variable = new Variable(type, identifier);
variable.addAssignment(varAssign);
nextToken();
varAssign.setVariable(variable);
gprintln("ParseTypedDec: VariableDeclarationWithAssingment: (Type: "
~ type ~ ", Identifier: " ~ identifier ~ ")", DebugType.WARNING);
Variable variable = new Variable(type, identifier);
variable.addAssignment(varAssign);
varAssign.setVariable(variable);
generated = variable;
generated = variable;
}
else
{
expect("Variable assignments+declarations are not allowed.");
}
}
else
{
expect("Variables declarations are not allowed.");
}
}
else
{
expect("Expected one of the following: (, ; or =");
}
/* TODO: If we outta tokens we should not call this */
// gprintln(getCurrentToken());
gprintln("parseTypedDeclaration(): Leave", DebugType.WARNING);
return generated;
@ -1767,6 +1819,55 @@ public final class Parser
return new FunctionCall(functionName, arguments);
}
private ExternStmt parseExtern()
{
ExternStmt externStmt;
/* Consume the `extern` token */
nextToken();
/* Expect the next token to be either `efunc` or `evariable` */
SymbolType externType = getSymbolType(getCurrentToken());
nextToken();
/* Pseudo-entity */
Entity pseudoEntity;
/* External function symbol */
if(externType == SymbolType.EXTERN_EFUNC)
{
// TODO: (For one below)(we should also disallow somehow assignment) - evar
// We now parse function definition but with `wantsBody` set to false
// indicating no body should be allowed.
pseudoEntity = parseTypedDeclaration(false, false, true);
}
/* External variable symbol */
else if(externType == SymbolType.EXTERN_EVAR)
{
// We now parse a variable declaration but with the `wantsBody` set to false
// indicating no assignment should be allowed.
pseudoEntity = parseTypedDeclaration(false, true, false);
}
/* Anything else is invalid */
else
{
expect("Expected either extern function (efunc) or extern variable (evar)");
}
/* Expect a semicolon to end it all and then consume it */
expect(SymbolType.SEMICOLON, getCurrentToken());
nextToken();
externStmt = new ExternStmt(pseudoEntity, externType);
/* Mark the Entity as external */
pseudoEntity.makeExternal();
return externStmt;
}
/* Almost like parseBody but has more */
/**
* TODO: For certain things like `parseClass` we should
@ -1859,6 +1960,13 @@ public final class Parser
/* Add the struct definition to the program */
modulle.addStatement(ztruct);
}
/* If it is an extern */
else if(symbol == SymbolType.EXTERN)
{
ExternStmt externStatement = parseExtern();
modulle.addStatement(externStatement);
}
else
{
expect("parse(): Unknown '" ~ tok.getToken() ~ "'");

View File

@ -59,6 +59,9 @@ public enum SymbolType
GREATER_THAN_OR_EQUALS,
SMALLER_THAN_OR_EQUALS,
CAST,
EXTERN,
EXTERN_EFUNC,
EXTERN_EVAR,
UNKNOWN
}
@ -317,6 +320,21 @@ public SymbolType getSymbolType(Token tokenIn)
{
return SymbolType.DELETE;
}
/* efunc keyword */
else if(cmp(token, "efunc") == 0)
{
return SymbolType.EXTERN_EFUNC;
}
/* evar keyword */
else if(cmp(token, "evar") == 0)
{
return SymbolType.EXTERN_EVAR;
}
/* extern keyword */
else if(cmp(token, "extern") == 0)
{
return SymbolType.EXTERN;
}
/* module keyword */
else if(cmp(token, "module") == 0)
{
@ -527,6 +545,10 @@ public string getCharacter(SymbolType symbolIn)
{
return "&";
}
else if(symbolIn == SymbolType.SEMICOLON)
{
return ";";
}
else
{
gprintln("getCharacter: No back-mapping for "~to!(string)(symbolIn), DebugType.ERROR);

View File

@ -169,9 +169,23 @@ public class Entity : Statement
/* Name of the entity (class's name, function's name, variable's name) */
protected string name;
this(string name)
/* If this entity is extern'd */
private bool isExternalEntity;
this(string name, bool isExternalEntity = false)
{
this.name = name;
this.isExternalEntity = isExternalEntity;
}
public bool isExternal()
{
return isExternalEntity;
}
public void makeExternal()
{
isExternalEntity = true;
}
public void setAccessorType(AccessorType accessorType)
@ -895,4 +909,41 @@ public final class DiscardStatement : Statement
{
return "[DiscardStatement: (Exp: "~expression.toString()~")]";
}
}
public final class ExternStmt : Statement
{
// Pseudo entity created
private Entity pseudoEntity;
private SymbolType externType;
this(Entity pseudoEntity, SymbolType externType)
{
// External symbols are either external functions or external variables
assert(externType == SymbolType.EXTERN_EFUNC || externType == SymbolType.EXTERN_EVAR);
this.pseudoEntity = pseudoEntity;
this.externType = externType;
}
public string getExternalName()
{
return pseudoEntity.getName();
}
public SymbolType getExternType()
{
return externType;
}
public Entity getPseudoEntity()
{
return pseudoEntity;
}
public override string toString()
{
return "[ExternStatement: (Symbol name: "~getExternalName()~")]";
}
}

View File

@ -1273,6 +1273,9 @@ public final class TypeChecker
*/
public void beginCheck()
{
/* Process all pseudo entities of the given module */
processPseudoEntities(modulle);
/**
* Make sure there are no name collisions anywhere
* in the Module with an order of precedence of
@ -1286,6 +1289,39 @@ public final class TypeChecker
dependencyCheck();
}
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));
}
}
private void checkClassInherit(Container c)
{
/* Get all types (Clazz so far) */

View File

@ -1535,6 +1535,15 @@ public class DNodeGenerator
return discardStatementDNode;
}
/**
* Extern statement (ExternStmt)
*/
else if(cast(ExternStmt)entity)
{
/* We don't do anything, this is to be handled in typechecker pre-run */
/* NOTE: If anything we ought to remove these ExternSTmt nodes during such a process */
return null;
}
return null;
}

View File

@ -0,0 +1,12 @@
module simple_extern;
extern efunc uint write(uint fd, ubyte* buffer, uint count);
extern evar int kak;
void test()
{
ubyte* buff;
discard write(cast(uint)0, buff, cast(uint)1001);
kak = kak + 1;
}

1
texterned.c Normal file
View File

@ -0,0 +1 @@
int kak;