Check
- 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:
parent
d3e15e7a2f
commit
2abb28bcaf
|
@ -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,6 +682,9 @@ 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`
|
||||
|
||||
// If the Entity is NOT external then emit the signature+body
|
||||
if(!functionEntity.isExternal())
|
||||
{
|
||||
// Emit the function signature
|
||||
file.writeln(generateSignature(functionEntity));
|
||||
|
||||
|
@ -695,6 +706,12 @@ public final class DCodeEmitter : CodeEmitter
|
|||
// 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
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}`);
|
||||
}
|
||||
|
|
|
@ -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
|
|||
}
|
||||
}
|
||||
|
||||
/* 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();
|
||||
}
|
||||
/* 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() ~ "'");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
@ -896,3 +910,40 @@ 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()~")]";
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
Loading…
Reference in New Issue