- Added new symbol types `EXTERN`, `EXTERN_EFUNC` and `EXTERN_EVAR` and related back-mappings

Parser

- `parseFuncDef()` now accepts a default argument (set to `true`) on whether to expect a body for a function or not, in the not case expect a semi-colon - this helps with extern support
- Likewise because `parseFuncDef(bool wantsBody = true)` is called by `parseTypedDeclaration()` we have added same argument to `parseTypedDeclaration(bool wantsBody = true)`
- Ensure we pass the parameter from `parseTypedDeclaration()` into `parseFuncDef(bool)` in function definition case
- Implemented `parseExtern()` for extern support
- `parse()` supports `SymbolType.EXTERN` now

Data

- Added `ExternStmt` to represent the parser node derived from a call to `parseExtern()`
- The `Entity` parser node type now has an `isExternal()` flag to know if the entity is marked for `extern` link time or TLang internal time (default)

Typechecker

- Implemented `processPseudoEntities(Container)` which loops through the given container and finds all extern statements and then extracts those nodes, parents them to the given container and marks them as external (pseudo-handling support)
- Added first call inside `beginCheck()` to be a call to `processPseudoEntities(modulle)`

Dependency

- Added useless no-op check for `ExternStmt` - it does nothing

DGen

- In `emitFunctionSignature()`, prepend the string `extern ` to the signatur if the given `Function` entity is marked as external (`isExternal()` is true)
- In `emitFunctionDefinitions()` do not emit a function body at all (or anything, no signature) if the `Function` is marked as external (`isExternal()` is true)
- Added entry point test for `simple_extern.t`
This commit is contained in:
Tristan B. Velloza Kildaire 2023-01-15 20:48:40 +02:00
parent d3e15e7a2f
commit 2abb28bcaf
7 changed files with 251 additions and 25 deletions

View File

@ -492,6 +492,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)~">";
}
@ -646,6 +648,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;
}
@ -674,26 +682,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()
@ -778,6 +795,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

@ -854,7 +854,7 @@ public final class Parser
VariableParameter[] params;
}
private funcDefPair parseFuncDef()
private funcDefPair parseFuncDef(bool wantsBody = true)
{
gprintln("parseFuncDef(): Enter", DebugType.WARNING);
@ -938,11 +938,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 +1304,7 @@ public final class Parser
return retExpression[0];
}
private TypedEntity parseTypedDeclaration()
private TypedEntity parseTypedDeclaration(bool wantsBody = true)
{
gprintln("parseTypedDeclaration(): Enter", DebugType.WARNING);
@ -1336,7 +1346,7 @@ public final class Parser
gprintln("ParseTypedDec: SymbolType=" ~ to!(string)(symbolType));
if (symbolType == SymbolType.LBRACE)
{
funcDefPair pair = parseFuncDef();
funcDefPair pair = parseFuncDef(wantsBody);
generated = new Function(identifier, type, pair.bodyStatements, pair.params);
@ -1767,6 +1777,53 @@ 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 false
pseudoEntity = parseTypedDeclaration(false);
}
/* External variable symbol */
else if(externType == SymbolType.EXTERN_EVAR)
{
// TODO: Implement me
assert(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 +1916,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) */
@ -1425,6 +1461,8 @@ public final class TypeChecker
foreach (Entity entity; entities)
{
gprintln("checkEntty: "~to!(string)(entity is null));
/**
* Absolute root Container (in other words, the Module)
* can not be used
@ -1468,6 +1506,7 @@ public final class TypeChecker
Container possibleContainerEntity = cast(Container) entity;
if (possibleContainerEntity)
{
gprintln("checkContainer(c): Recursing on: "~to!(string)(possibleContainerEntity));
checkContainerCollision(possibleContainerEntity);
}
}
@ -1503,6 +1542,7 @@ public final class TypeChecker
/* Get all classes */
foreach (Statement statement; c.getStatements())
{
gprintln("getContainerMember: "~to!(string)(statement));
if (statement !is null && cast(Entity) statement)
{
entities ~= cast(Entity) statement;

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,9 @@
module simple_extern;
extern efunc uint write(uint fd, ubyte* buffer, uint count);
void test()
{
ubyte* buff;
discard write(cast(uint)0, buff, cast(uint)1001);
}