tlang/source/tlang/compiler/core.d

280 lines
7.8 KiB
D

module compiler.core;
import gogga;
import std.conv : to;
import compiler.lexer.core;
import std.stdio : File;
import compiler.parsing.core;
import compiler.symbols.check;
import compiler.symbols.data;
import compiler.typecheck.core;
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;
import compiler.configuration : CompilerConfiguration, ConfigEntry;
// TODO: Add configentry unittests
public enum CompilerError
{
LEX_NOT_PERFORMED,
NO_TOKENS,
PARSE_NOT_YET_PERFORMED,
TYPECHECK_NOT_YET_PERFORMED,
CONFIG_ERROR,
CONFIG_KEY_NOT_FOUND,
CONFIG_TYPE_ERROR,
CONFIG_DUPLICATE_ENTRY
}
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 Compiler
{
/* The input source code */
private string inputSource;
/* 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;
/* The chosen code emitter to use */
private CodeEmitter emitter;
private File emitOutFile;
/* The configuration */
private CompilerConfiguration config;
/* TODO: Make the default config */
private void defaultConfig()
{
/* Enable Behaviour-C fixes */
config.addConfig(ConfigEntry("behavec:preinline_args", true));
/* Enable pretty code generation for DGen */
config.addConfig(ConfigEntry("dgen:pretty_code", true));
/* Enable entry point test generation for DGen */
config.addConfig(ConfigEntry("dgen:emit_entrypoint_test", true));
/* Set the mapping to hashing of entity names (TODO: This should be changed before release) */
config.addConfig(ConfigEntry("emit:mapper", "hashmapper"));
}
public CompilerConfiguration getConfig()
{
return config;
}
/**
* Create a new compiler instance to compile the given
* source code
* Params:
* sourceCode = the source code to compile
*/
this(string sourceCode, File emitOutFile)
{
this.inputSource = sourceCode;
this.emitOutFile = emitOutFile;
this.config = new CompilerConfiguration();
/* Enable the default config */
defaultConfig();
}
/* Setup the lexer and begin lexing */
public void doLex()
{
/* Setup the lexer and begin lexing */
this.lexer = new Lexer(inputSource);
this.lexer.performLex();
this.tokens = this.lexer.getTokens();
}
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);
}
this.typeChecker = new TypeChecker(modulle);
/* Perform typechecking/codegen */
this.typeChecker.beginCheck();
}
/* 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("emit:mapper").getText();
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();
}
}
/**
* Performs compilation of the provided module(s)
*
* Params:
* sourceFiles = The module(s) to perform compilation with
*/
void beginCompilation(string[] sourceFiles)
{
/* TODO: Begin compilation process, take in data here */
gprintln("Compiling files "~to!(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 */
ulong fileSize = sourceFileFile.size();
byte[] fileBytes;
fileBytes.length = fileSize;
fileBytes = sourceFileFile.rawRead(fileBytes);
sourceFileFile.close();
/* The file to output to */
File outFile;
outFile.open("tlangout.c", "w");
/* Create a new compiler */
Compiler compiler = new Compiler(cast(string)fileBytes, outFile);
/* Perform the compilation */
compiler.compile();
}
}
unittest
{
// TODO: Add tests here for our `simple_<x>.t` tests or put them in DGen, I think here is better
// FIXME: Crashes and I think because too fast or actually bad state? Maybe something is not being
// cleared, I believe this may be what is happening
// ... see issue #88
// ... UPDATE: It seems to be any unit test..... mhhhh.
// string[] testFiles = ["source/tlang/testing/simple_while.t"
// ];
// // "source/tlang/testing/simple_functions.t",
// // "source/tlang/testing/simple_while.t",
// // "source/tlang/testing/simple_for_loops.t",
// // "source/tlang/testing/simple_cast.t",
// // "source/tlang/testing/simple_conditionals.t",
// // "source/tlang/testing/nested_conditionals.t",
// // "source/tlang/testing/simple_discard.t"
// foreach(string testFile; testFiles)
// {
// beginCompilation([testFile]);
// }
}