Command-line

- All compilation stages now make use of the `Compiler` object

Compiler

- Added new exception type `CompilerException` complete with a sub-type enum, `CompilerError`
- `getConfig()` will now throw a `CompilerException` when a key is not found, rather than return false (which didn't work under different template types anyways)
- Implemented `hasConfig()` to check for the existence of a key in the configuration sub-system's key-value store
- The `Compiler` object now stores the `Token[] tokens` generated from the call to `doLex()`
- The `Compiler` object now stores the resulting container (`Module`) generated from the call to `doParse()`
- Set default symbol mapping technique to the hashmapper technique
- Implemented `dolex()` for performing tokenization, it will create and store a `Lexer` instance and the produced `Token[] tokens` into the `Compiler` object
- Added `getTokens()` to fetch the tokens generated by `doLex()`
- Implemented `doParse()`, `doTypeCheck()` and `doEmit()` in a similiar fashion to `doLex()`
- Implemented `getMdoule()` to get the container (`Module`) generated by `doParse()`
- Implemented `compile()` which calls `doLex()`, then `doParse()`, then `doTypeCheck()` and finally `doEmit()`

CodeEmitter

- The `CodeEmitter` constructor now takes in an instance of the chosen `SymbolMapper`

DGen

- Switched to the instance of the `mapper` inheited from the `CodeMapper` parent class for any `symbolMap` calls required
- Use the inherited `TypeChecker` instance and not an instance of it provided by `Context`

SymbolMapper

- Reworked this class into an abstract class which must have its children implement a `symbolMap(Entity)` interface, this provides us pluggable mapping techniques

HashMapper

- Moved hashing symbol-mapping technique into `HashMapper`

Lebanese

- Created a kind-of `SymbolMapper` which, unlike `HashMapper`, produces human-redable-yet-valid C symbols (by replacing the `.`'s with `_`'s)

TypeChecker

- Removed code for setting now-nonexistent `SymbolMapper.tc`
- Removed code for setting now-nonexistent `Context.tc`

Context

- Removed `static TypeChecker tc` field
This commit is contained in:
Tristan B. Velloza Kildaire 2023-01-23 20:44:35 +02:00
parent a8a88c86e1
commit d30ffb7684
9 changed files with 240 additions and 83 deletions

View File

@ -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)
{

View File

@ -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;
}
/**

View File

@ -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 <type> <parameter-name (symbol mapped)>

View File

@ -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);
}

View File

@ -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);

View File

@ -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 `_<underscored>`
string symbolName = replace(absoluteFullPath, ".", "_");
symbolName="t_"~symbolName;
return symbolName;
}
}

View File

@ -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 */

View File

@ -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);

View File

@ -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;