I’ve been tinkering around with mpc and llvm recently - just to satisfy a few simple questions:
- How easy is it to use mpc?
- How easy is the LLVM c api to use?
- How easy is it to connect a parser generator to an LLVM Module?
So given the above aims, I’ve embarked on creating my own stupid little language, that I’m calling ‘neil‘ - Not Exactly an Intermediate Language. It is utterly by chance that the name ‘neil‘ happens to be my own name, if you’ve believe that gratuitous lie!
A ‘neil‘ program currently consists of one allowed type - a 32 bit integer named i32, function definitions, function calls, and being able to return the results of one function from another. In short, it is (currently, and most likely permanently) a really stupid language.
An example legal ‘neil' program is:
So lets get right into how we construct this grammar in mpc. The mpc grammar for our ‘neil‘ language is as follows:
It’s a very simple grammar, I looked at the mpc - smallc example to work out what to do. To parse using mpc, I have the following function:
With this, we can then iterate through a successfully parsed ‘neil‘ input using the:
type that mpc provides. The struct elements are as follows:
- tag - the string name of the ast node’s type
- contents - what the ast node is pointing at in the original source input
- state - the line information for the ast node
- children_num - the length of the children array
- children - an array of AST node’s that are children of the current node
Next up, we need to create what we need from LLVM to produce a module for the current file. I’m using the LLVM C API because I’ve never used it before, having only ever used the C++ API in my day to day stuff at work, so I thought it would be useful to have a look at it.
First up we need an LLVM Module - this is an encapsulation of a ‘neil‘ input file for our uses. I use:
To get a module to work with. Then, for each AST node that is a procedure, I create a corresponding LLVM function with:
(Note I’m cheating for now because I know my functions return i32, and take no params, don’t do this in production code!).
Then, since I have no control flow within my functions, I can create one basic block to hold the body of the function, and an IR builder to help us make the instructions within the basic block, with:
And with this we can begin to parse the body of the functions!
I parse the return statement (the only allowed statement within our functions), and check if it returns a literal or the result of a call to a function:
And the result is:
From the example ‘neil‘ file I gave above.
TL;DR mpc is pretty cool, the LLVM C API is very easy to use, and I’ll be fleshing out my ‘neil‘ language in future blog posts once I try handling a more complicated input grammar.
PS the full source for the example is below: