You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
413 lines
13 KiB
D
413 lines
13 KiB
D
module tlang.compiler.typecheck.meta;
|
|
|
|
import tlang.compiler.symbols.data : Statement, TypedEntity, Function, FunctionCall, IdentExpression;
|
|
import tlang.compiler.symbols.expressions : Expression, IntegerLiteral, IntegerLiteralEncoding;
|
|
import tlang.compiler.symbols.typing.core;
|
|
import tlang.compiler.symbols.containers : Container;
|
|
import tlang.compiler.symbols.mcro;
|
|
import tlang.compiler.typecheck.core;
|
|
import gogga;
|
|
import std.conv : to;
|
|
import tlang.compiler.configuration;
|
|
|
|
/**
|
|
* The `MetaProcessor` is used to do a pass over a `Container`
|
|
* to process any macro and macro-like entities
|
|
*/
|
|
public class MetaProcessor
|
|
{
|
|
private TypeChecker tc;
|
|
private bool isMetaEnabled;
|
|
private CompilerConfiguration compilerConfig;
|
|
|
|
/**
|
|
* Constructs a new `MetaProcessor` for the purposes of
|
|
* modifying the AST tree before the typechecker traverses
|
|
* it
|
|
*
|
|
* Params:
|
|
* tc = the `TypeChecker` instance to process
|
|
* isMetaEnabled = `true` if to perform meta processing, otherwise `false`
|
|
*/
|
|
this(TypeChecker tc, bool isMetaEnabled)
|
|
{
|
|
this.tc = tc;
|
|
this.isMetaEnabled = isMetaEnabled;
|
|
this.compilerConfig = tc.getConfig();
|
|
}
|
|
|
|
/**
|
|
* Analyzes the provided `Container` and searches for any `Macro`-like
|
|
* parse-nodes to process
|
|
*/
|
|
public void process(Container container)
|
|
{
|
|
/* Only apply meta-processing if enabled */
|
|
if(!isMetaEnabled)
|
|
{
|
|
return;
|
|
}
|
|
|
|
/* Get all statements */
|
|
Statement[] stmts = container.getStatements();
|
|
|
|
foreach(Statement curStmt; stmts)
|
|
{
|
|
gprintln("MetaProcessor: Examining AST node '"~curStmt.toString()~"'...");
|
|
|
|
// Perform replacement of all type alises to concrete types, such as `size_t`
|
|
doTypeAlias(container, curStmt);
|
|
|
|
/**
|
|
* Search for any `sizeof(<ident_type>)` expressions
|
|
* and replace them with a `NumberLiteral`
|
|
*/
|
|
if(cast(MStatementSearchable)curStmt && cast(MStatementReplaceable)curStmt)
|
|
{
|
|
MStatementSearchable searchableStmt = cast(MStatementSearchable)curStmt;
|
|
Statement[] foundStmts = searchableStmt.search(FunctionCall.classinfo);
|
|
gprintln("Nah fr");
|
|
|
|
foreach(Statement curFoundStmt; foundStmts)
|
|
{
|
|
FunctionCall curFuncCall = cast(FunctionCall)curFoundStmt;
|
|
|
|
if(curFuncCall.getName() == "sizeof")
|
|
{
|
|
gprintln("Elo");
|
|
Expression[] arguments = curFuncCall.getCallArguments();
|
|
if(arguments.length == 1)
|
|
{
|
|
IdentExpression potentialIdentExp = cast(IdentExpression)arguments[0];
|
|
if(potentialIdentExp)
|
|
{
|
|
string typeName = potentialIdentExp.getName();
|
|
IntegerLiteral replacementStmt = sizeOf_Literalize(typeName);
|
|
gprintln("sizeof: Replace '"~curFoundStmt.toString()~"' with '"~replacementStmt.toString()~"'");
|
|
|
|
/* Traverse down from the `Container` we are process()'ing and apply the replacement */
|
|
MStatementReplaceable containerRepl = cast(MStatementReplaceable)container;
|
|
containerRepl.replace(curFoundStmt, replacementStmt);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Throw an exception here that an ident_type should be present as the argument
|
|
gprintln("The argument to `sizeof` should be an ident", DebugType.ERROR);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: Throw an exception here as only 1 argument is allowed
|
|
gprintln("To use the `sizeof` macro you require a single argument to be passed to it", DebugType.ERROR);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If the current statement is a Container then recurse
|
|
*
|
|
* This will help us do the following:
|
|
*
|
|
* 1. Type re-writing of
|
|
* a. Functions (Parameters and Body as both make up its Statement[])
|
|
*/
|
|
if(cast(Container)curStmt)
|
|
{
|
|
process(cast(Container)curStmt);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Re-writes the types for things such as `size_t`, `ssize_t` and so forth
|
|
*
|
|
* Params:
|
|
* statement = the `MTypeRewritable` to apply re-writing to
|
|
*/
|
|
private void typeRewrite(MTypeRewritable statement)
|
|
{
|
|
/* Applies re-write to Variable's declared type and Function's return type */
|
|
string type = statement.getType();
|
|
|
|
/* Only re-write if type alias */
|
|
if(isTypeAlias(type))
|
|
{
|
|
/* Get the concrete type of `type` */
|
|
string concreteType = getConcreteType(type);
|
|
|
|
/* Rewrite the type */
|
|
statement.setType(concreteType);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Performs the replacement of type alieses such as `size_t`, `ssize_t`
|
|
* and so forth with their concrete type
|
|
*
|
|
* Params:
|
|
* container = the current `Container` being processsed
|
|
* curStmt = the current `Statement` to consider
|
|
*/
|
|
private void doTypeAlias(Container container, Statement curStmt)
|
|
{
|
|
/**
|
|
* Apply type-rewriting to any `MTypeRewritable` AST node
|
|
* (a.k.a. a node which contains a type and can have it set)
|
|
*
|
|
* NOTE: This is just for the "type" fields in AST nodes,
|
|
* we should have some full recursive re-writer.
|
|
*
|
|
* An example of why is for supporting something like:
|
|
*
|
|
* `sizeof(size_t)` <- currently is not supported by this
|
|
*/
|
|
if(cast(MTypeRewritable)curStmt)
|
|
{
|
|
typeRewrite(cast(MTypeRewritable)curStmt);
|
|
}
|
|
|
|
/**
|
|
* Here we will also search for any `IdentExpression`
|
|
* which contains `size_t`, `ssize_t` etc. and replace
|
|
* them
|
|
*/
|
|
if(cast(MStatementSearchable)curStmt && cast(MStatementReplaceable)curStmt)
|
|
{
|
|
MStatementSearchable searchableStmt = cast(MStatementSearchable)curStmt;
|
|
IdentExpression[] foundStmts = cast(IdentExpression[])searchableStmt.search(IdentExpression.classinfo);
|
|
|
|
// TODO: Implement me
|
|
// gprintln("IdentExpressions found: "~to!(string)(foundStmts));
|
|
|
|
/**
|
|
* Loop through all `IdentExpression`s and find any
|
|
* occurence of `size_t`/`ssize_t` and replace those
|
|
* with the concrete type
|
|
*/
|
|
foreach(IdentExpression identExp; foundStmts)
|
|
{
|
|
string identName = identExp.getName();
|
|
|
|
/* Determine if this is a type alias? */
|
|
if(isTypeAlias(identName))
|
|
{
|
|
// Determine the concrete type
|
|
string concereteType = getConcreteType(identName);
|
|
gprintln("Found type alias '"~identName~"' which concretely is '"~concereteType~"'");
|
|
|
|
// Replace with concrete type
|
|
container.replace(identExp, new IdentExpression(concereteType));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private IntegerLiteral sizeOf_Literalize(string typeName)
|
|
{
|
|
IntegerLiteral literal = new IntegerLiteral("TODO_LITERAL_GOES_HERESIZEOF_REPLACEMENT", IntegerLiteralEncoding.UNSIGNED_INTEGER);
|
|
|
|
// TODO: Via typechecker determine size with a lookup
|
|
Type type = tc.getType(tc.getModule(), typeName);
|
|
|
|
/* Calculated type size */
|
|
ulong typeSize = 0;
|
|
|
|
/**
|
|
* Calculate stack array size
|
|
*
|
|
* Algo: `<componentType>.size * stackArraySize`
|
|
*/
|
|
if(cast(StackArray)type)
|
|
{
|
|
StackArray stackArrayType = cast(StackArray)type;
|
|
ulong arrayLength = stackArrayType.getAllocatedSize();
|
|
Type componentType = stackArrayType.getComponentType();
|
|
ulong componentTypeSize = 0;
|
|
|
|
// FIXME: Later, when the Dependency Genrator supports more advanced component types,
|
|
// ... we will need to support this - for now assume that `componentType` is primitive
|
|
if(cast(Number)componentType)
|
|
{
|
|
Number numberType = cast(Number)componentType;
|
|
componentTypeSize = numberType.getSize();
|
|
}
|
|
|
|
typeSize = componentTypeSize*arrayLength;
|
|
}
|
|
/**
|
|
* Calculate the size of `Number`-based types
|
|
*/
|
|
else if(cast(Number)type)
|
|
{
|
|
Number numberType = cast(Number)type;
|
|
typeSize = numberType.getSize();
|
|
}
|
|
|
|
// TODO: We may eed toupdate Type so have bitwidth or only do this
|
|
// for basic types - in which case I guess we should throw an exception
|
|
// here.
|
|
// ulong typeSize =
|
|
|
|
|
|
|
|
/* Update the `Sizeof` kind-of-`IntegerLiteral` with the new size */
|
|
literal.setNumber(to!(string)(typeSize));
|
|
|
|
return literal;
|
|
}
|
|
|
|
/**
|
|
* Transforms the type alias into its concrete type.
|
|
*
|
|
* This method incorporates defensive programming in
|
|
* that it will only apply the transformation IF
|
|
* the provided type alias is infact a type alias,
|
|
* otherwise it performs an identity transformation
|
|
* and returns the "alias" untouched.
|
|
*
|
|
* Params:
|
|
* typeAlias = the potential type alias
|
|
* Returns: the concrete type, or `typeAlias` if
|
|
* not an alias
|
|
*/
|
|
private string getConcreteType(string typeAlias)
|
|
{
|
|
/* Check if this is a system type alias? If so, transform */
|
|
if(isSystemType(typeAlias))
|
|
{
|
|
return getSystemType(typeAlias);
|
|
}
|
|
// TODO: Add user-defined type alias support here
|
|
/* Else, return the "alias" untouched */
|
|
else
|
|
{
|
|
return typeAlias;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if the given type is a type alias.
|
|
*
|
|
* Params:
|
|
* typeAlias = the type to check
|
|
* Returns: `true` if it is an alias, `false` otherwise
|
|
*/
|
|
private bool isTypeAlias(string typeAlias)
|
|
{
|
|
/* If this a system type alias? */
|
|
if(isSystemType(typeAlias))
|
|
{
|
|
return true;
|
|
}
|
|
// TODO: Support for user-defined type aliases
|
|
/* Otherwise, not a type alias */
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Determines if the given type is a system type alias
|
|
*
|
|
* Params:
|
|
* typeAlias = the type to check
|
|
* Returns: `true` if system type alias, `false` otherwise
|
|
*/
|
|
private bool isSystemType(string typeAlias)
|
|
{
|
|
/* `size_t`/`ssize_t` system type aliases */
|
|
if(typeAlias == "size_t" || typeAlias == "ssize_t")
|
|
{
|
|
return true;
|
|
}
|
|
/* Else, not a system type alias */
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Given a type alias (think `size_t`/`ssize_t` for example) this will
|
|
* look up in the compiler's configuration what that size should be
|
|
* resolved to
|
|
*
|
|
* Params:
|
|
* typeAlias = the system type alias to lookup
|
|
* Returns: the concrete type
|
|
*/
|
|
private string getSystemType(string typeAlias)
|
|
{
|
|
/* Determine machine's width */
|
|
ulong maxWidth = compilerConfig.getConfig("types:max_width").getNumber();
|
|
|
|
string maxType;
|
|
|
|
if(maxWidth == 1)
|
|
{
|
|
if(typeAlias == "size_t")
|
|
{
|
|
return "ubyte";
|
|
}
|
|
else if(typeAlias == "ssize_t")
|
|
{
|
|
return "byte";
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
else if(maxWidth == 2)
|
|
{
|
|
if(typeAlias == "size_t")
|
|
{
|
|
return "ushort";
|
|
}
|
|
else if(typeAlias == "ssize_t")
|
|
{
|
|
return "short";
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
else if(maxWidth == 4)
|
|
{
|
|
if(typeAlias == "size_t")
|
|
{
|
|
return "uint";
|
|
}
|
|
else if(typeAlias == "ssize_t")
|
|
{
|
|
return "int";
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
else if(maxWidth == 8)
|
|
{
|
|
if(typeAlias == "size_t")
|
|
{
|
|
return "ulong";
|
|
}
|
|
else if(typeAlias == "ssize_t")
|
|
{
|
|
return "long";
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert(false);
|
|
}
|
|
}
|
|
} |