diff --git a/source/tlang/compiler/codegen/emit/dgen.d b/source/tlang/compiler/codegen/emit/dgen.d index a6d7fba3..eb0331e7 100644 --- a/source/tlang/compiler/codegen/emit/dgen.d +++ b/source/tlang/compiler/codegen/emit/dgen.d @@ -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 ""; } @@ -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 +#include +int main() +{ + test(); + + + return 0; }`); } diff --git a/source/tlang/compiler/parsing/core.d b/source/tlang/compiler/parsing/core.d index b4258583..39e50e6e 100644 --- a/source/tlang/compiler/parsing/core.d +++ b/source/tlang/compiler/parsing/core.d @@ -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() ~ "'"); diff --git a/source/tlang/compiler/symbols/check.d b/source/tlang/compiler/symbols/check.d index 244cf3f9..1813f54d 100644 --- a/source/tlang/compiler/symbols/check.d +++ b/source/tlang/compiler/symbols/check.d @@ -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); diff --git a/source/tlang/compiler/symbols/data.d b/source/tlang/compiler/symbols/data.d index f73e1c1a..0e9fedd9 100644 --- a/source/tlang/compiler/symbols/data.d +++ b/source/tlang/compiler/symbols/data.d @@ -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()~")]"; + } } \ No newline at end of file diff --git a/source/tlang/compiler/typecheck/core.d b/source/tlang/compiler/typecheck/core.d index 0c4e1182..66c70738 100644 --- a/source/tlang/compiler/typecheck/core.d +++ b/source/tlang/compiler/typecheck/core.d @@ -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) */ diff --git a/source/tlang/compiler/typecheck/dependency/core.d b/source/tlang/compiler/typecheck/dependency/core.d index 45b3bd6f..9bf895b3 100644 --- a/source/tlang/compiler/typecheck/dependency/core.d +++ b/source/tlang/compiler/typecheck/dependency/core.d @@ -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; } diff --git a/source/tlang/testing/simple_extern.t b/source/tlang/testing/simple_extern.t new file mode 100644 index 00000000..02bf836e --- /dev/null +++ b/source/tlang/testing/simple_extern.t @@ -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; +} \ No newline at end of file diff --git a/texterned.c b/texterned.c new file mode 100644 index 00000000..0db06103 --- /dev/null +++ b/texterned.c @@ -0,0 +1 @@ +int kak;