diff --git a/source/tlang/commandline/commands.d b/source/tlang/commandline/commands.d index 40447e4..e430db2 100644 --- a/source/tlang/commandline/commands.d +++ b/source/tlang/commandline/commands.d @@ -15,6 +15,7 @@ import compiler.lexer : Lexer, Token; import compiler.parsing.core : Parser; import compiler.typecheck.core : TypeChecker; import gogga; +import compiler.compiler : Compiler; //TODO: Re-order the definitions below so that they appear with compile first, then lex, parse, ..., help @@ -67,11 +68,10 @@ struct lexCommand file.close(); /* Begin lexing process */ - Lexer lexer = new Lexer(sourceText); - lexer.performLex(); - + Compiler compiler = new Compiler(sourceText, File()); + compiler.doLex(); writeln("=== Tokens ===\n"); - writeln(lexer.getTokens()); + writeln(compiler.getTokens()); } catch(TError t) { @@ -107,17 +107,15 @@ struct parseCommand file.close(); /* Begin lexing process */ - Lexer lexer = new Lexer(sourceText); - lexer.performLex(); - - Token[] tokens = lexer.getTokens(); + Compiler compiler = new Compiler(sourceText, File()); + compiler.doLex(); writeln("=== Tokens ===\n"); - writeln(tokens); + writeln(compiler.getTokens()); - // TODO: Catch exception - Parser parser = new Parser(tokens); + /* Perform parsing */ + compiler.doParse(); // TODO: Do something with the returned module - auto modulel = parser.parse(); + auto modulel = compiler.getModule(); } catch(TError t) { @@ -152,22 +150,18 @@ struct typecheckCommand file.close(); /* Begin lexing process */ - Lexer lexer = new Lexer(sourceText); - lexer.performLex(); - - Token[] tokens = lexer.getTokens(); + Compiler compiler = new Compiler(sourceText, File()); + compiler.doLex(); writeln("=== Tokens ===\n"); - writeln(tokens); + writeln(compiler.getTokens()); - // TODO: Catch exception - Parser parser = new Parser(tokens); + /* Perform parsing */ + compiler.doParse(); // TODO: Do something with the returned module - auto modulel = parser.parse(); + auto modulel = compiler.getModule(); - //TODO: collect results here - //TODO: catch exceptions - TypeChecker typeChecker = new TypeChecker(modulel); - typeChecker.beginCheck(); + /* Perform typechecking/codegen */ + compiler.doTypeCheck(); } catch(TError t) { diff --git a/source/tlang/compiler/codegen/emit/core.d b/source/tlang/compiler/codegen/emit/core.d index 6016d9e..a189884 100644 --- a/source/tlang/compiler/codegen/emit/core.d +++ b/source/tlang/compiler/codegen/emit/core.d @@ -11,6 +11,7 @@ import std.range : walkLength; import gogga; import std.conv : to; import compiler.compiler : CompilerConfiguration; +import compiler.codegen.mapper.core : SymbolMapper; /** * TODO: Perhaps have an interface that can emit(Context/Parent, Statement) @@ -23,6 +24,7 @@ public abstract class CodeEmitter protected TypeChecker typeChecker; protected File file; protected CompilerConfiguration config; + protected SymbolMapper mapper; /** * The selected queue is the queue to be used @@ -127,7 +129,8 @@ public abstract class CodeEmitter return functionBodyInstrs.keys(); } - this(TypeChecker typeChecker, File file, CompilerConfiguration config) + // TODO: Add allow for custom symbol mapper, use an interface or rather base class mechanism for it + this(TypeChecker typeChecker, File file, CompilerConfiguration config, SymbolMapper mapper) { this.typeChecker = typeChecker; @@ -141,6 +144,8 @@ public abstract class CodeEmitter this.file = file; this.config = config; + + this.mapper = mapper; } /** diff --git a/source/tlang/compiler/codegen/emit/dgen.d b/source/tlang/compiler/codegen/emit/dgen.d index eb0331e..5a80b7a 100644 --- a/source/tlang/compiler/codegen/emit/dgen.d +++ b/source/tlang/compiler/codegen/emit/dgen.d @@ -13,7 +13,7 @@ import std.range : walkLength; import std.string : wrap; import std.process : spawnProcess, Pid, ProcessException, wait; import compiler.typecheck.dependency.core : Context, FunctionData, DNode; -import compiler.codegen.mapper : SymbolMapper; +import compiler.codegen.mapper.core : SymbolMapper; import compiler.symbols.data : SymbolType, Variable, Function, VariableParameter; import compiler.symbols.check : getCharacter; import misc.utils : Stack; @@ -28,9 +28,10 @@ public final class DCodeEmitter : CodeEmitter private bool varDecWantsConsumeVarAss = false; - this(TypeChecker typeChecker, File file, CompilerConfiguration config) + // NOTE: In future store the mapper in the config please + this(TypeChecker typeChecker, File file, CompilerConfiguration config, SymbolMapper mapper) { - super(typeChecker, file, config); + super(typeChecker, file, config, mapper); } private ulong transformDepth = 0; @@ -125,13 +126,13 @@ public final class DCodeEmitter : CodeEmitter gprintln("Is ContextNull?: "~to!(string)(context is null)); gprintln("Wazza contect: "~to!(string)(context.container)); - auto typedEntityVariable = context.tc.getResolver().resolveBest(context.getContainer(), varAs.varName); //TODO: Remove `auto` + auto typedEntityVariable = typeChecker.getResolver().resolveBest(context.getContainer(), varAs.varName); //TODO: Remove `auto` /* If it is not external */ if(!typedEntityVariable.isExternal()) { - string renamedSymbol = SymbolMapper.symbolLookup(typedEntityVariable); + string renamedSymbol = mapper.symbolLookup(typedEntityVariable); // If we are needed as part of a VariabvleDeclaration-with-assignment @@ -163,7 +164,7 @@ public final class DCodeEmitter : CodeEmitter VariableDeclaration varDecInstr = cast(VariableDeclaration)instruction; Context context = varDecInstr.getContext(); - Variable typedEntityVariable = cast(Variable)context.tc.getResolver().resolveBest(context.getContainer(), varDecInstr.varName); //TODO: Remove `auto` + Variable typedEntityVariable = cast(Variable)typeChecker.getResolver().resolveBest(context.getContainer(), varDecInstr.varName); //TODO: Remove `auto` /* If the variable is not external */ if(!typedEntityVariable.isExternal()) @@ -175,7 +176,7 @@ public final class DCodeEmitter : CodeEmitter //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); + string renamedSymbol = mapper.symbolLookup(typedEntityVariable); // Check to see if this declaration has an assignment attached @@ -221,7 +222,7 @@ public final class DCodeEmitter : CodeEmitter FetchValueVar fetchValueVarInstr = cast(FetchValueVar)instruction; Context context = fetchValueVarInstr.getContext(); - Variable typedEntityVariable = cast(Variable)context.tc.getResolver().resolveBest(context.getContainer(), fetchValueVarInstr.varName); //TODO: Remove `auto` + Variable typedEntityVariable = cast(Variable)typeChecker.getResolver().resolveBest(context.getContainer(), fetchValueVarInstr.varName); //TODO: Remove `auto` /* If it is not external */ if(!typedEntityVariable.isExternal()) @@ -229,7 +230,7 @@ public final class DCodeEmitter : CodeEmitter //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 = mapper.symbolLookup(typedEntityVariable); return renamedSymbol; } @@ -259,7 +260,7 @@ public final class DCodeEmitter : CodeEmitter Context context = funcCallInstr.getContext(); assert(context); - Function functionToCall = cast(Function)context.tc.getResolver().resolveBest(context.getContainer(), funcCallInstr.functionName); //TODO: Remove `auto` + Function functionToCall = cast(Function)typeChecker.getResolver().resolveBest(context.getContainer(), funcCallInstr.functionName); //TODO: Remove `auto` // TODO: SymbolLookup? @@ -669,7 +670,7 @@ public final class DCodeEmitter : CodeEmitter // Generate the symbol-mapped names for the parameters Variable typedEntityVariable = cast(Variable)typeChecker.getResolver().resolveBest(func, currentParameter.getName()); //TODO: Remove `auto` - string renamedSymbol = SymbolMapper.symbolLookup(typedEntityVariable); + string renamedSymbol = mapper.symbolLookup(typedEntityVariable); // Generate diff --git a/source/tlang/compiler/codegen/mapper/core.d b/source/tlang/compiler/codegen/mapper/core.d new file mode 100644 index 0000000..4dd7868 --- /dev/null +++ b/source/tlang/compiler/codegen/mapper/core.d @@ -0,0 +1,25 @@ +module compiler.codegen.mapper.core; + +import compiler.typecheck.core; +import compiler.symbols.data; +import std.conv : to; +import gogga; + +/** + * SymbolMapper + * + * Maps Entity's to consistent but unique symbol + * names (strings) + */ +public class SymbolMapper +{ + // Used to map names to entities + protected TypeChecker tc; + + this(TypeChecker tc) + { + this.tc = tc; + } + + public abstract string symbolLookup(Entity entityIn); +} \ No newline at end of file diff --git a/source/tlang/compiler/codegen/mapper.d b/source/tlang/compiler/codegen/mapper/hashmapper.d similarity index 67% rename from source/tlang/compiler/codegen/mapper.d rename to source/tlang/compiler/codegen/mapper/hashmapper.d index 9811a7d..5c927f8 100644 --- a/source/tlang/compiler/codegen/mapper.d +++ b/source/tlang/compiler/codegen/mapper/hashmapper.d @@ -1,28 +1,16 @@ -module compiler.codegen.mapper; +module compiler.codegen.mapper.hashmapper; +import compiler.codegen.mapper.core : SymbolMapper; import compiler.typecheck.core; import compiler.symbols.data; -import std.conv : to; -import gogga; -/** - * SymbolMapper - * - * Maps Entity's to consistent but unique symbol - * names (strings) - */ -public final class SymbolMapper +public final class HashMapper : SymbolMapper { - // Used to map names to entities - public static TypeChecker tc; - // Entity map - // private string[Entity] symbolMap; - - // this(TypeChecker tc) - // { - // this.tc = tc; - // } + this(TypeChecker tc) + { + super(tc); + } /** * Given an Entity this will generate a unique (but consistent) @@ -34,7 +22,7 @@ public final class SymbolMapper * * Returns: The symbol hash */ - public static string symbolLookup(Entity entityIn) + public override string symbolLookup(Entity entityIn) { // Generate the absolute full path of the Entity string absoluteFullPath = tc.getResolver().generateNameBest(entityIn); diff --git a/source/tlang/compiler/codegen/mapper/lebanese.d b/source/tlang/compiler/codegen/mapper/lebanese.d new file mode 100644 index 0000000..f956733 --- /dev/null +++ b/source/tlang/compiler/codegen/mapper/lebanese.d @@ -0,0 +1,34 @@ +module compiler.codegen.mapper.lebanese; + +import compiler.codegen.mapper.core : SymbolMapper; +import compiler.typecheck.core; +import compiler.symbols.data; +import std.string : replace; + +public final class LebaneseMapper : SymbolMapper +{ + this(TypeChecker tc) + { + super(tc); + } + + /** + * Maps given Entity's name to a version whereby all the + * `.`'s are placed by underscores preceded by a `t_` + * + * Params: + * entityIn = the Entity to map + * Returns: A string of the mapped symbol + */ + public override string symbolLookup(Entity entityIn) + { + // Generate the absolute full path of the Entity + string absoluteFullPath = tc.getResolver().generateNameBest(entityIn); + + // Generate the name as `_` + string symbolName = replace(absoluteFullPath, ".", "_"); + symbolName="t_"~symbolName; + + return symbolName; + } +} \ No newline at end of file diff --git a/source/tlang/compiler/compiler.d b/source/tlang/compiler/compiler.d index fa745d1..19dbe9a 100644 --- a/source/tlang/compiler/compiler.d +++ b/source/tlang/compiler/compiler.d @@ -12,6 +12,32 @@ import compiler.typecheck.exceptions; import core.stdc.stdlib; import compiler.codegen.emit.core; import compiler.codegen.emit.dgen; +import misc.exceptions : TError; +import compiler.codegen.mapper.core : SymbolMapper; +import compiler.codegen.mapper.hashmapper : HashMapper; +import compiler.codegen.mapper.lebanese : LebaneseMapper; +import std.string : cmp; + +public enum CompilerError +{ + LEX_NOT_PERFORMED, + NO_TOKENS, + PARSE_NOT_YET_PERFORMED, + TYPECHECK_NOT_YET_PERFORMED, + CONFIG_ERROR, + CONFIG_KEY_NOT_FOUND +} + +public final class CompilerException : TError +{ + private CompilerError errType; + + this(CompilerError errType, string msg = "") + { + super("CompilerError("~to!(string)(errType)~")"~(msg.length ? ": "~msg : "")); + this.errType = errType; + } +} public class CompilerConfiguration { @@ -31,11 +57,17 @@ public class CompilerConfiguration } else { - // TODO: Change to a TError - // throw new Exception("Key not found"); - return false; + throw new CompilerException(CompilerError.CONFIG_KEY_NOT_FOUND); } } + + public bool hasConfig(string key) + { + string[] keys = config.keys(); + import std.algorithm.searching : canFind; + + return canFind(keys, key); + } } public class Compiler @@ -46,8 +78,12 @@ public class Compiler /* The lexer */ private Lexer lexer; + /* The lexed tokens */ + private Token[] tokens; + /* The parser */ private Parser parser; + private Module modulle; /* The typechecker/code generator */ private TypeChecker typeChecker; @@ -70,6 +106,9 @@ public class Compiler /* Enable entry point test generation for DGen */ config.setConfig("dgen_emit_entrypoint_test", true); + + /* Set the mapping to hashing of entity names (TODO: This should be changed before release) */ + config.setConfig("emit:mapper", "hashmapper"); } @@ -91,36 +130,115 @@ public class Compiler defaultConfig(); } - public void compile() + /* Setup the lexer and begin lexing */ + public void doLex() { - // TODO: Add each step of the pipeline here - /* Setup the lexer and begin lexing */ this.lexer = new Lexer(inputSource); this.lexer.performLex(); - - /* Extract the tokens */ - Token[] tokens = lexer.getTokens(); - gprintln("Collected "~to!(string)(tokens)); - /* Spawn a new parser with the provided tokens */ - this.parser = new Parser(tokens); + this.tokens = this.lexer.getTokens(); + } - /* The parsed Module */ - Module modulle = parser.parse(); + public Token[] getTokens() + { + if(this.lexer is null) + { + throw new CompilerException(CompilerError.LEX_NOT_PERFORMED); + } + + return tokens; + } + + /* Spawn a new parser with the provided tokens */ + public void doParse() + { + Token[] lexedTokens = getTokens(); + + if(lexedTokens.length == 0) + { + throw new CompilerException(CompilerError.NO_TOKENS); + } + else + { + /* Spawn a new parser with the provided tokens */ + this.parser = new Parser(lexedTokens); + + modulle = parser.parse(); + } + } + + public Module getModule() + { + return modulle; + } + + /** + * Spawn a new typechecker/codegenerator on the module + * and perform type checking and code generation + */ + public void doTypeCheck() + { + if(this.parser is null) + { + throw new CompilerException(CompilerError.PARSE_NOT_YET_PERFORMED); + } - /* Spawn a new typechecker/codegenerator on the module */ this.typeChecker = new TypeChecker(modulle); /* Perform typechecking/codegen */ this.typeChecker.beginCheck(); + } - /* Perform code emitting */ - this.emitter = new DCodeEmitter(typeChecker, emitOutFile, config); + /* Perform code emitting */ + public void doEmit() + { + if(typeChecker is null) + { + throw new CompilerException(CompilerError.TYPECHECK_NOT_YET_PERFORMED); + } + + if(!config.hasConfig("emit:mapper")) + { + throw new CompilerException(CompilerError.CONFIG_ERROR, "Missing a symbol mapper"); + } + + SymbolMapper mapper; + string mapperType = config.getConfig!(string)("emit:mapper"); + + if(cmp(mapperType, "hashmapper") == 0) + { + mapper = new HashMapper(typeChecker); + } + else if(cmp(mapperType, "lebanese") == 0) + { + mapper = new LebaneseMapper(typeChecker); + } + else + { + throw new CompilerException(CompilerError.CONFIG_ERROR, "Invalid mapper type '"~mapperType~"'"); + } + + this.emitter = new DCodeEmitter(typeChecker, emitOutFile, config, mapper); emitter.emit(); // Emit the code emitOutFile.close(); // Flush (perform the write() syscall) emitter.finalize(); // Call CC on the file containing generated C code } + + public void compile() + { + /* Setup the lexer, perform the tokenization and obtain the tokens */ + doLex(); + + /* Setup the parser with the provided tokens and perform parsing */ + doParse(); + + /* Spawn a new typechecker/codegenerator on the module and perform type checking */ + doTypeCheck(); + + /* Perform code emitting */ + doEmit(); + } } /** @@ -137,6 +255,7 @@ void beginCompilation(string[] sourceFiles) foreach(string sourceFile; sourceFiles) { /* Read in the source code */ + // TODO: THis below code is used so many times, for heavens-sake please make a helper function for it gprintln("Reading source file '"~sourceFile~"' ..."); File sourceFileFile; sourceFileFile.open(sourceFile); /* TODO: Error handling with ANY file I/O */ diff --git a/source/tlang/compiler/typecheck/core.d b/source/tlang/compiler/typecheck/core.d index a69e0f3..0046b2f 100644 --- a/source/tlang/compiler/typecheck/core.d +++ b/source/tlang/compiler/typecheck/core.d @@ -68,15 +68,6 @@ public final class TypeChecker * non-cyclic * */ - - // Allow Context class access to the type checker (used in Instruction where only Context is available) - Context.tc = this; - - // Allow the SymbolMapper class to access the type checker - import compiler.codegen.mapper : SymbolMapper; - SymbolMapper.tc = this; - - // DNodeGenerator.staticTC = this; DNodeGenerator dNodeGenerator = new DNodeGenerator(this); diff --git a/source/tlang/compiler/typecheck/dependency/core.d b/source/tlang/compiler/typecheck/dependency/core.d index 49f17e7..73ae2e5 100644 --- a/source/tlang/compiler/typecheck/dependency/core.d +++ b/source/tlang/compiler/typecheck/dependency/core.d @@ -27,7 +27,7 @@ import compiler.typecheck.dependency.exceptions : DependencyException, Dependenc public final class Context { // Required for cases where we need the functionality of the type checker - static TypeChecker tc; + // static TypeChecker tc; InitScope initScope; Container container;