Meta processor
The meta processor is a mechanism that acts somewhat like a shim
(something you shove in-between two things) between the parser and the
typechecker. Therefore because the parser provides us with an AST tree
rooted in a Module
of which is then passed to the type checker it
would imply that the place where the meta processor fits in is something
that consumes the AST tree, applies manipulations to its nodes or
completely replaces some, and then finally returns to let the type
checker begin its process.
Examples of things which require AST manipulation are:
- Type aliases
size_t
andssize_t
need to be resolved to their concrete types
- Macros
- Macros such as
sizeof(<type>)
need to be replaced with aNumberLiteral
with the value that is equal to the bit-width (in bytes) of the type<type>
- Macros such as
Meta API
There are some core interfaces which various Statement
(s) (parser
nodes) can implement in order to be able to be manipulated by the
meta-processor, we describe these in this section. These are all defined
in source/tlang/compiler/symbols/mcro.d
.
the MStatementSearchable
Anything which implements this has the ability to search for objects of the provided type, and return a list of them.
TODO: Method table (required methods to implement)
Method name | Return type | Description |
---|---|---|
search( TypeInfo_Class ) |
Statement[] |
Searches for all objects of the given type and returns an array of them. Only if the given type is equal to or sub-of Statement |
TODO: Add an example of it being used here please
the MStatementReplaceable
Anything which implements this has the ability to, given an object x
,
return a ref x
to it hence allowing us to replace it.
FIXME: This description is wrong and wrong in the code, no more
ref
stuff is ysed AT ALL, and hasn’t been for a long time anyways, so
that comment needs to get updated
Method name | Return type | Description |
---|---|---|
replace( Statement, Statement) |
bool |
Replace a given Statement (the first argument) with another Statement (the second argument), returns true if the replacement is successful, false otherwise |
TODO: Add an example of it being used here please
the MTypeRewritable
TODO: Description
Method name | Return type | Description |
---|---|---|
getType() |
string |
Gets this entity’s type |
setType(string) |
void |
Sets this entity’s type to the provided one |
TODO: Add an example of it being used here please
the MCloneable
NOTE: This one isn’t even used yet anywhere that I know of, hence do not document yet
Anything which implements this can make a full deep clone of itself.
Method name | Return type | Description |
---|---|---|
clone() |
Statement |
Returns the deeply cloned Statement |
TODO: Add an example of it being used here please
the MetaProcessor
The MetaProcessor
is the actual processing facility which can apply
different types of AST manipulations to a given Container
. It is run
prior to type checking but after parsing. This makes sense as we want to
do AST node manipulation just after the parse tree has been
constructed, then we can pass the changed program to the TypeChecker
and it wouldn’t know any different. For all the type checker knows, this
is just the original program.
Method name | Return type | Description |
---|---|---|
process(Container) |
void |
Processes the various types of meta statements in the provided Container |
doTypeAlias( Container, Statement) |
void |
Performs the replacement of type aliases such as size_t , ssize_t |
typeRewrite( MTypeRewritable) |
void |
Updates any type fields in TypedEntity s (these are the only ones really implementing the MTypeRewritable interface) |
getConcreteType( string) |
string |
Given an assumed type alias this will try resolve it to its concrete type |
isTypeAlias(string) |
bool |
Given an assumed type alias this checks if it is a type alias |
isSystemType(string) |
bool |
Checks if the given type is a system alias (so, is it size_t /ssize_t ) |
getSystemType(string) |
string |
Resolves size_t /ssize_t to their concrete types using the CompilerConfig |
AST processing
The process(Container)
method is the entry point to the whole
meta-processor engine and it is called by the TypeChecker
by passing
in the parsed Module
instance such that the meta-proessing and AST
manipulation can be applied to the entire program tree.
This method is recursive in that what it first does is apply the AST
manipulations, that you will see in the next sectiom, to each body
Statement
of the current container. Right at the end after all these
manipulations have taken place we then check if the current Statement
is a kind-of Container
, if so then we recurse by calling
process(childContainer)
on the child container. Therefore reaching the
depths of the AST tree.
Type alias replacement
The first step which is applied is to replace all type aliases with
their concrete values. This is accomplished by calling the
doTypeAlias(Container, Statement)
method from the current container
with the Statement
we have iterated to.
This does two checks:
- Is the current statement
MTypeRewritable
-compatible?- If so then it means that the statement is some sort of
TypedEntity
which has a type field - This field is then updated by calling
setType(string)
with the new concrete type
- If so then it means that the statement is some sort of
- Is the current statement
MStatementSearchable
andMStatementReplaceable
?- This means that the statement can be searched and have parts of it replaced
- A search is normally done via the
Container
, however, for any AST node of typeIdentExpression
- We search for
IdentExpression
because, for example if you have1+sizeof(size_t)
, then the identity expression (named variable) would be that ofsize_t
within the argument tosizeof(...)
. - We can then replace the
IdentExpression
there with one referring to the concrete type - The concrete type is retrieved by calling
getConcreteType(string)
(with"size_t"
in this case)
Macro replacement
To support macros such as sizeof(<type>)
we need to be able to find
where they occur and then, no matter how deep in the AST tree, replace
them with some other node (in this example an IntegerLiteral
) which
makes sense. We make heavy use of the MStatementSearchable
(for
searching) and the MStatementReplaceable
(for replacing)
interfaces as part of this process.
The method of replacement is to examine the current Statement
being
iterated on in the call to process(Container)
, we then check the
following things:
- Is the current statement both
MStatementSearchable
andMStatementReplaceable
compatible? - If so, search for all
FunctionCall
AST nodes - Of all the
FunctionCall
AST node(s) found:- If the name of the function being called is
sizeof
? - If their is only a single argument passed to said function call?
- If the argument passed is an
IdentExpression
?
- If the name of the function being called is
- If the above are all true, then we proceed to do:
- Extract the
IdentExpression
’s name field - Pass this to
sizeOf_Literalize(string)
- Now replace this
IdentExpression
with theIntegerLiteral
returned bysizeOf_Literalize(string)
- Extract the
The replacement is done from the current container, and we pass it the
FunctionCall
(the sizeof<type>
) we found as the first argument (what
to search for) and then the second argument is our newly created
IntegerLiteral
which is what will replace the spot that FunctionCall
occupies in the AST tree.