Today we will add a relatively simple feature that will be very useful for a future extension: type declarations.
Our current version of tiny has the concept of variable declaration, where a name is introduced in the program to represent a variable value type.
〈declaration〉 → var 〈identifier〉 : 〈type〉 ;
For example, in the code below:
After this variable declaration, the name x can be used as a variable: inside an expression or in the left hand side of an assignment.
But what if we were able to declare things that are not only variables, like types? Associate to a name a type so we can use the type where a type is expected? This is what we are doing today: we are introducing type declarations.
First we will generalize a bit the 〈declaration〉 rules to encompass 〈variable-declaration〉 and 〈type-declaration〉.
If a variable declaration introduce a variable name, a type declaration introduces a type name. The same rules we use for variable names apply for type declaration names. We need a few restrictions though. The 〈identifier〉 of a type declaration cannot appear in the 〈type〉 of its own 〈type-declaration〉 (e.g. type T : T; is not valid). A type name can only be used where a type is expected, this means that it cannot be used inside an expression or the left hand side of an assignment. Finally, a name can either be a variable name or a type name but not both.
The interpretation of using a type name inside a 〈type〉 is simple: it denotes the 〈type〉 of the corresponding 〈type-declaration〉 of that type name.
With all that knowledge, we can start implementing type names.
We are introducing a new token type. This is easy, we just add it to our list of token keywords.
Our existing lexer machinery will do the rest.
This part is as usual a bit more involved. First we need to recognize a new declaration.
When parsing a statement, if we see a token type it means that a type-declaration starts.
The implementation is pretty straightforward...
... except for a detail: we need to create a type name. This means that the scope of names will contain two different kinds of names: variable names and type names. So before we can continue we will need to be able to distinguish the different kinds of names.
This is not very complicated, though, it is just a matter of extending or Symbol class with a SymbolKind field.
Now it is mandatory to specify the kind of Symbol when we create it, so parse_variable_declaration and query_variable in tiny-parser.cc will have to be updated.
Now we can complete the implementation of parse_type_declaration that we left halfways above.
The implementation is pretty identical to parse_variable_declaration (we could of course refactor the code to avoid some duplication here) but instead of a variable name we create a type name. In GCC a declaration of a type is represented using a node with tree code TYPE_DECL. That node can then be used in the TREE_TYPE of any expression or declaration (including another TYPE_DECL).
Once a type has been declared we want to use its type name. The only place where we can currently use a type name in tiny is in 〈type〉 so we will need to update parse_type. This will require a query_type function that we will see later.
We will also allow the remaining part of parse_type work to work with an erroneous type in case query_type fails.
This uses a new function called query_type similar to query_variable that does the same query in the lookup but checks the name is a type name.
We can try our new extension.
Admittedly this new extension does not look very interesting now but it will be when we add record types to the language.