- Corrected import path for `compiler.lexer` -> `compiler.lexer.core`

Parser

- Corrected import path for `compiler.lexer` -> `compiler.lexer.core`

TypeChecker

- Corrected import path for `compiler.lexer` -> `compiler.lexer.core`

Compiler

- Moved configuration code outside of it
- Renamed to `compiler.core`

DGen

- Check for any object files to link in, if so append them to the `cc` call

Lexer

- Moved from `compiler.lexer` to `compiler.lexer.core`

Configuration

- Overhauled configuration system

Mapper

- Added definition to `SymbolMappingTechnique`

Command-line

- Migrated to new configuration system
- Migrated `SymbolMappingTechnique` to Mapper module
- Added support for specifying object fils to link in using the `-ll` flag`

Tests

- Added `file_io.c` for testing `simple_extern.t` with `extern_test.sh`
- Added `extern_test.sh` for testing `simple_extern.t`
This commit is contained in:
Tristan B. Velloza Kildaire 2023-01-28 18:12:49 +02:00
parent a5d1617d15
commit d548a066a6
14 changed files with 299 additions and 94 deletions

13
extern_test.sh Executable file
View File

@ -0,0 +1,13 @@
#1/bin/sh
# Compile C to object file as library to link in
gcc source/tlang/testing/file_io.c -c -o file_io.o
# Compile T to C, then compile C and link with other object file into a final object file
./tlang compile source/tlang/testing/simple_extern.t -sm HASHMAPPER -et true -pg true -ll file_io.o
# Run the tlang file
./tlang.out
# Run (with strace) to see it
strace -e trace=write ./tlang.out

View File

@ -8,22 +8,18 @@ module commandline.commands;
import jcli;
import std.stdio;
import compiler.compiler : beginCompilation;
import misc.exceptions : TError;
import std.exception : ErrnoException;
import compiler.lexer : Lexer, Token;
import compiler.lexer.core : Lexer, Token;
import compiler.parsing.core : Parser;
import compiler.typecheck.core : TypeChecker;
import gogga;
import compiler.compiler : Compiler;
import compiler.core : Compiler, beginCompilation;
import compiler.configuration : ConfigEntry;
import std.conv : to;
//TODO: Re-order the definitions below so that they appear with compile first, then lex, parse, ..., help
import compiler.codegen.mapper.core : SymbolMappingTechnique;
public enum SymbolMappingTechnique
{
hashmapper,
lebanese
}
//TODO: Re-order the definitions below so that they appear with compile first, then lex, parse, ..., help
public enum VerbosityLevel
{
@ -49,7 +45,7 @@ mixin template BaseCommand()
void BaseCommandInit(Compiler compiler)
{
// Set the verbosity level
compiler.getConfig().setConfig("verbosity", debugLevel);
compiler.getConfig().addConfig(ConfigEntry("verbosity", debugLevel));
}
}
@ -80,20 +76,22 @@ mixin template EmitBase()
@ArgNamed("library-link|ll", "Paths to any object files to ,ink in during the linking phase")
@(ArgConfig.optional)
@(ArgConfig.aggregate)
string[] bruh;
}
void EmitBaseInit(Compiler compiler)
{
// Set the symbol mapper technique
compiler.getConfig().setConfig("emit:mapper", symbolTechnique);
compiler.getConfig().addConfig(ConfigEntry("emit:mapper", symbolTechnique));
// Set whether pretty-printed code should be generated
compiler.getConfig().setConfig("dgen:pretty_code", prettyPrintCodeGen);
compiler.getConfig().addConfig(ConfigEntry("dgen:pretty_code", prettyPrintCodeGen));
// Set whether or not to enable the entry point testing code
compiler.getConfig().setConfig("dgen:emit_entrypoint_test", entrypointTestEmit);
compiler.getConfig().addConfig(ConfigEntry("dgen:emit_entrypoint_test", entrypointTestEmit));
// Set the paths to the object files to link in
compiler.getConfig().addConfig(ConfigEntry("linker:link_files", bruh));
}
}

View File

@ -10,7 +10,7 @@ import compiler.codegen.instruction : Instruction;
import std.range : walkLength;
import gogga;
import std.conv : to;
import compiler.compiler : CompilerConfiguration;
import compiler.configuration : CompilerConfiguration;
import compiler.codegen.mapper.core : SymbolMapper;
/**

View File

@ -18,7 +18,7 @@ import compiler.symbols.data : SymbolType, Variable, Function, VariableParameter
import compiler.symbols.check : getCharacter;
import misc.utils : Stack;
import compiler.symbols.typing.core : Type, Primitive, Integer, Void, Pointer;
import compiler.compiler : CompilerConfiguration;
import compiler.configuration : CompilerConfiguration;
public final class DCodeEmitter : CodeEmitter
{
@ -41,7 +41,7 @@ public final class DCodeEmitter : CodeEmitter
string tabStr;
/* Only generate tabs if enabled in compiler config */
if(config.getConfig!(bool)("dgen:pretty_code"))
if(config.getConfig("dgen:pretty_code").getBoolean())
{
for(ulong i = 0; i < count; i++)
{
@ -554,7 +554,7 @@ public final class DCodeEmitter : CodeEmitter
// If enabled (default: yes) then emit entry point (TODO: change later)
if(config.getConfig!(bool)("dgen:emit_entrypoint_test"))
if(config.getConfig("dgen:emit_entrypoint_test").getBoolean())
{
//TODO: Emit main (entry point)
emitEntryPoint();
@ -878,7 +878,21 @@ int main()
try
{
//NOTE: Change to system compiler (maybe, we need to choose a good C compiler)
Pid ccPID = spawnProcess(["clang", "-o", "tlang.out", file.name()]);
string[] compileArgs = ["clang", "-o", "tlang.out", file.name()];
// Check for object files to be linked in
string[] objectFilesLink;
if(config.hasConfig("linker:link_files"))
{
objectFilesLink = config.getConfig("linker:link_files").getArray();
gprintln("Object files to be linked in: "~to!(string)(objectFilesLink));
}
else
{
gprintln("No files to link in");
}
Pid ccPID = spawnProcess(compileArgs~objectFilesLink);
int code = wait(ccPID);

View File

@ -23,3 +23,9 @@ public class SymbolMapper
public abstract string symbolLookup(Entity entityIn);
}
public enum SymbolMappingTechnique : string
{
HASHMAPPER = "hashmapper",
LEBANESE = "lebanese"
}

View File

@ -0,0 +1,202 @@
module compiler.configuration;
import compiler.core : CompilerException, CompilerError;
import std.string : cmp;
private union ConfigValue
{
ulong number;
bool boolean;
string text;
string[] textArray;
}
public enum ConfigType
{
NUMBER,
BOOLEAN,
TEXT,
TEXT_ARRAY
}
public struct ConfigEntry
{
private string name;
private ConfigValue value;
private ConfigType type;
private this(string entryName, ConfigType entryType)
{
this.name = entryName;
this.type = entryType;
}
this(VType)(string entryName, VType valType)
{
this(entryName, ConfigType.TEXT);
value.text = to!(string)(valType);
}
this(string entryName, ulong entryValue)
{
this(entryName, ConfigType.NUMBER);
value.number = entryValue;
}
this(string entryName, bool entryValue)
{
this(entryName, ConfigType.BOOLEAN);
value.boolean = entryValue;
}
this(string entryName, string entryValue)
{
this(entryName, ConfigType.TEXT);
value.text = entryValue;
}
this(string entryName, string[] entryValue)
{
this(entryName, ConfigType.TEXT_ARRAY);
value.textArray = entryValue;
}
public ulong getNumber()
{
if(type == ConfigType.NUMBER)
{
return value.number;
}
else
{
throw new CompilerException(CompilerError.CONFIG_TYPE_ERROR, "Type mismatch for key '"~name~"'");
}
}
public bool getBoolean()
{
if(type == ConfigType.BOOLEAN)
{
return value.boolean;
}
else
{
throw new CompilerException(CompilerError.CONFIG_TYPE_ERROR, "Type mismatch for key '"~name~"'");
}
}
public string getText()
{
if(type == ConfigType.TEXT)
{
return value.text;
}
else
{
throw new CompilerException(CompilerError.CONFIG_TYPE_ERROR, "Type mismatch for key '"~name~"'");
}
}
public string[] getArray()
{
if(type == ConfigType.TEXT_ARRAY)
{
return value.textArray;
}
else
{
throw new CompilerException(CompilerError.CONFIG_TYPE_ERROR, "Type mismatch for key '"~name~"'");
}
}
public string getName()
{
return name;
}
public ConfigType getType()
{
return type;
}
}
/**
* TODO: Add a union and then also a get funciton that
* interprets per each for us (using templatescan be automated
* maybe)
* Implement this!
*/
/**
* TODO: Implement a union below as the value
* of tye key-value pair
*/
public class CompilerConfiguration
{
private ConfigEntry[] entries;
public void addConfig(ConfigEntry entry)
{
// If duplicate then update entry
if(hasConfig(entry.getName()))
{
updateConfig(entry);
}
// Else, add a new entry
else
{
entries ~= entry;
}
}
private void updateConfig(ConfigEntry newEntry)
{
for(ulong i = 0; i < entries.length; i++)
{
if(cmp(entries[i].getName(), newEntry.getName()) == 0)
{
if(entries[i].getType() == newEntry.getType())
{
entries[i] = newEntry;
break;
}
else
{
throw new CompilerException(CompilerError.CONFIG_TYPE_ERROR, "Tried updating an entry to a different type");
}
}
}
}
public ConfigEntry getConfig(string key)
{
ConfigEntry foundEntry;
if(hasConfig_internal(key, foundEntry))
{
return foundEntry;
}
else
{
throw new CompilerException(CompilerError.CONFIG_KEY_NOT_FOUND);
}
}
private bool hasConfig_internal(string key, ref ConfigEntry foundEntry)
{
foreach(ConfigEntry curEntry; entries)
{
if(cmp(curEntry.getName(), key) == 0)
{
foundEntry = curEntry;
return true;
}
}
return false;
}
public bool hasConfig(string key)
{
ConfigEntry _discard;
return hasConfig_internal(key, _discard);
}
}

View File

@ -1,8 +1,8 @@
module compiler.compiler;
module compiler.core;
import gogga;
import std.conv : to;
import compiler.lexer;
import compiler.lexer.core;
import std.stdio : File;
import compiler.parsing.core;
import compiler.symbols.check;
@ -17,6 +17,9 @@ 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
{
@ -25,7 +28,9 @@ public enum CompilerError
PARSE_NOT_YET_PERFORMED,
TYPECHECK_NOT_YET_PERFORMED,
CONFIG_ERROR,
CONFIG_KEY_NOT_FOUND
CONFIG_KEY_NOT_FOUND,
CONFIG_TYPE_ERROR,
CONFIG_DUPLICATE_ENTRY
}
public final class CompilerException : TError
@ -39,47 +44,6 @@ public final class CompilerException : TError
}
}
public class ConfigObject
{
}
public class ConfigList : ConfigObject
{
private ConfigObject[] list;
}
public class CompilerConfiguration
{
private string[string] config;
public void setConfig(VType)(string key, VType value)
{
config[key] = to!(string)(value);
}
public VType getConfig(VType)(string key)
{
import std.algorithm : canFind;
if(hasConfig(key))
{
return to!(VType)(config[key]);
}
else
{
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
{
/* The input source code */
@ -109,16 +73,16 @@ public class Compiler
private void defaultConfig()
{
/* Enable Behaviour-C fixes */
config.setConfig("behavec:preinline_args", true);
config.addConfig(ConfigEntry("behavec:preinline_args", true));
/* Enable pretty code generation for DGen */
config.setConfig("dgen:pretty_code", true);
config.addConfig(ConfigEntry("dgen:pretty_code", true));
/* Enable entry point test generation for DGen */
config.setConfig("dgen:emit_entrypoint_test", true);
config.addConfig(ConfigEntry("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");
config.addConfig(ConfigEntry("emit:mapper", "hashmapper"));
}
public CompilerConfiguration getConfig()
@ -217,7 +181,7 @@ public class Compiler
}
SymbolMapper mapper;
string mapperType = config.getConfig!(string)("emit:mapper");
string mapperType = config.getConfig("emit:mapper").getText();
if(cmp(mapperType, "hashmapper") == 0)
{
@ -312,3 +276,4 @@ unittest
// beginCompilation([testFile]);
// }
}

View File

@ -1,4 +1,4 @@
module compiler.lexer;
module compiler.lexer.core;
import std.container.slist;
import gogga;

View File

@ -5,7 +5,7 @@ import std.conv : to;
import std.string : isNumeric, cmp;
import compiler.symbols.check;
import compiler.symbols.data;
import compiler.lexer : Token;
import compiler.lexer.core : Token;
import core.stdc.stdlib;
import misc.exceptions : TError;
import compiler.parsing.exceptions;
@ -1990,7 +1990,7 @@ unittest
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
string sourceCode = `
module myModule;
@ -2029,7 +2029,7 @@ unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.typecheck.core;
string sourceCode = `
@ -2196,7 +2196,7 @@ class myClass2
unittest
{
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.typecheck.core;
@ -2264,7 +2264,7 @@ void function()
unittest
{
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.typecheck.core;
@ -2346,7 +2346,7 @@ int myFunction(int i, int j)
unittest
{
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.typecheck.core;
@ -2448,7 +2448,7 @@ void function()
unittest
{
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.typecheck.core;
string sourceCode = `
@ -2548,7 +2548,7 @@ int thing()
unittest
{
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.typecheck.core;
@ -2671,7 +2671,7 @@ void function()
unittest
{
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.typecheck.core;

View File

@ -1,11 +1,10 @@
module compiler.parsing.exceptions;
import compiler.parsing.core;
import compiler.lexer;
import misc.exceptions;
import compiler.symbols.check;
import compiler.symbols.data;
import compiler.lexer : Token;
import compiler.lexer.core : Token;
import std.conv : to;
public class ParserException : TError

View File

@ -1,6 +1,6 @@
module compiler.symbols.check;
import compiler.lexer : Token;
import compiler.lexer.core : Token;
import std.conv : to;
import std.string : isNumeric, cmp;
import misc.utils;

View File

@ -1740,7 +1740,7 @@ unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.parsing.core;
string sourceFile = "source/tlang/testing/collide_container_module1.t";
@ -1787,7 +1787,7 @@ unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.parsing.core;
string sourceFile = "source/tlang/testing/collide_container_module2.t";
@ -1832,7 +1832,7 @@ unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.parsing.core;
string sourceFile = "source/tlang/testing/collide_container_non_module.t";
@ -1877,7 +1877,7 @@ unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.parsing.core;
string sourceFile = "source/tlang/testing/collide_member.t";
@ -1921,7 +1921,7 @@ unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.parsing.core;
string sourceFile = "source/tlang/testing/precedence_collision_test.t";
@ -1967,7 +1967,7 @@ unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.parsing.core;
string sourceFile = "source/tlang/testing/collide_container.t";
@ -2052,7 +2052,7 @@ unittest
{
import std.file;
import std.stdio;
import compiler.lexer;
import compiler.lexer.core;
import compiler.parsing.core;
string sourceFile = "source/tlang/testing/typecheck/simple_function_call.t";

View File

@ -0,0 +1,8 @@
#include<unistd.h>
int ctr = 2;
unsigned int doWrite(unsigned int fd, unsigned char* buffer, unsigned int count)
{
write(fd, buffer, count+ctr);
}

View File

@ -1,12 +1,12 @@
module simple_extern;
extern efunc uint write(uint fd, ubyte* buffer, uint count);
extern evar int kak;
extern efunc uint doWrite(uint fd, ubyte* buffer, uint count);
extern evar int ctr;
void test()
{
ubyte* buff;
discard write(cast(uint)0, buff, cast(uint)1001);
ctr = ctr + 1;
kak = kak + 1;
ubyte* buff;
discard doWrite(cast(uint)0, buff, cast(uint)1001);
}