Basic compiler frontend using LLVM as backend written without C macros (includes and simple defines don't count 😊) so it is easy to read and understand.
Create and refine a most basic compiler using LLVM as backend that can easily be used as a starting point for any kind of compiler and can also be used in learning the compiler construction basics.
Feel free to open issues if you feel any part of the code could be better documented.
If you install Clang to C:\Program Files\LLVM\ the VS project should just work. Otherwise you will have to change project library path under linker options. Also in case you get a lot of linker errors try changing Runtime Library in project options, under C/C++ -> Code Generation, to /MD or /MT.
You could also try to setup official Clang build from llvm.org but I found it is not as straightforward as the unofficial build I linked above.
# sudo apt-get install llvm)# sudo apt-get install libz-dev)You can use clangCompile.sh or gccCompile.sh scripts to compile the compiler. You will get the binary output in the bin/ subdirectory.
Language at the moment supports:
The goal of this project is not to create a perfect new language but to try and create a perfect compiler for minimal possible language.
| C char | Summus char |
|---|---|
| 'a' | @a |
| 'z' | @z |
| '\n' | @\n |
| '\t' | @\t |
| '@' | @@ |
| '\x20' | @\x20 |
| '\20' | @\16 |
| '\' | @\ |
| ' ' | @ |
Once you build summus compiler you can use these commands with it:
summus inputfile.smm -o outfile.ll to compile given smm file to LLVM assembly which will be written in given ll filesummus -pp1 inputfile.smm | dot -Tsvg -oast.svg to generate image of AST tree if you have GraphViz installed (pp1 stands for print pass 1 and it supports pp1, pp2 and pp3)Here are some useful commands you can run on that output ll file:
clang -x ir -o test.exe test.ll to make native executable from ll fileclang -c -x ir -o test.o test.ll to make native object file from ll filellvm-objdump.exe -disassemble test.o to get native disassembly of object fileAlso if you want to experiment and discover what kind of llvm code needs to be written for certain constructs in C and C++ you can write the code you want to compile in a test.cpp file and then run:
clang -S -emit-llvm test.cpp -o test.ll to compile any cpp code to llvm assemblyllc -march=cpp test.ll -o llvmtest.cpp to convert llvm assembly to cpp file of llvm API calls that generate that assemblyFor this you need llc built with cpp option which isn't always included in binary builds of LLVM so you may need to compile LLVM manually in order to get it.
Compiler source is in compiler directory but it also uses smmgvpass from utility folder:
ibscommon just contains some common C compiler directives or pragmasibsallocator contains implementation of custom memory allocatoribsdictionary contains implementation of custom key-value store where multiple values can be pushed and popup under the same keysmmmsgs contains code that collects error and warning messages from compiler and can output themsmmlexer contains code that transforms input file text into a sequence of tokens, parsing numbers, keywords, symbols etc.smmparser contains code that parses the sequence of tokens from lexer and builds Abstract Syntax Tree (AST) doing some validations on the waysmmtypeinference does further validations and infers type of expressions and variables based on basic elements of expressionssmmsempass does further validations and propagates the biggest infered type down toward basic elements of expressionssmmllvmcodegen goes through now valid AST and generates LLVM module which it then outputs as LLVM assemblysmmgvpass from utility folder goes through AST and prints it in a form that GraphViz can then parse and generate an image of it as you can see in ast.svg fileTest folder contains code and samples for automatic tests
AllTests is entry point for running testsCuTest is small C unit testing framework from http://cutest.sourceforge.net/smmlexertests contains unit tests for lexersmmparsertests contains unit tests for parser which use the 3 files bellow to process samples from tests/samples directorysmmastwritter will write AST of a sample to ast file in easy to parse but still human readable format if such file doesn't already existsmmastreader will read AST from ast file if it already existssmmastmatcher will compare AST generated from parsing the sample with the one read from corresponding ast file and report if there are any differencesYou can see the exact changes mentioned bellow in a commit called "Adds support for if and while statements" from January 14th 2017.
if condition then statement; else statement; // statement can also be { block }if, then and else.if node. Thus I get AST representation of if statement.if node itself and call processExpression and processStatement in order to print nodes for its condition and body.summus -pp1 inputfile.smm | dot -Tsvg -oast.svg to generate an image of AST tree.summus -pp2 inputfile.smm | dot -Tsvg -oast.svg and the same with -pp3 parameter to see how AST looks after each pass.then body, for else body if it is given and for code that comes after if statement. If root of condition node is logical and or or node I call processAndOrInstr function directly because I just need conditional jumps it generates while I call processExpression for all other types of nodes. If I also called processExpression for logical nodes I would get a node that represents a resulting value of the condition (which is true or false) and then generate additional conditional jump based on that value which creates some extra instructions that I don't need. After this I position instruction building in then body block and call processStatement to generate instructions for it. At the end I add a branch instruction which jumps to end block. If there is an else body I position builder in its block and again call processStatement after which I also add a branch instruction that jumps to end block. At the end I position the builder in end block so the rest of code is generated in it.This is where I stopped and left the rest of the work for the reader. The next step is to test different kind of invalid code and see how summus compiler handles it like missing then and similar. Once you are happy how it does that you need to write automatic tests for it:
for statement[start:end] for slicing and ~ for contcatenating79 commits
C
97.0%
LLVM
2.6%
Basic compiler frontend using LLVM as backend written without C macros (includes and simple defines don't count 😊) so it is easy to read and understand.
Create and refine a most basic compiler using LLVM as backend that can easily be used as a starting point for any kind of compiler and can also be used in learning the compiler construction basics.
Feel free to open issues if you feel any part of the code could be better documented.
If you install Clang to C:\Program Files\LLVM\ the VS project should just work. Otherwise you will have to change project library path under linker options. Also in case you get a lot of linker errors try changing Runtime Library in project options, under C/C++ -> Code Generation, to /MD or /MT.
You could also try to setup official Clang build from llvm.org but I found it is not as straightforward as the unofficial build I linked above.
# sudo apt-get install llvm)# sudo apt-get install libz-dev)You can use clangCompile.sh or gccCompile.sh scripts to compile the compiler. You will get the binary output in the bin/ subdirectory.
Language at the moment supports:
The goal of this project is not to create a perfect new language but to try and create a perfect compiler for minimal possible language.
| C char | Summus char |
|---|---|
| 'a' | @a |
| 'z' | @z |
| '\n' | @\n |
| '\t' | @\t |
| '@' | @@ |
| '\x20' | @\x20 |
| '\20' | @\16 |
| '\' | @\ |
| ' ' | @ |
Once you build summus compiler you can use these commands with it:
summus inputfile.smm -o outfile.ll to compile given smm file to LLVM assembly which will be written in given ll filesummus -pp1 inputfile.smm | dot -Tsvg -oast.svg to generate image of AST tree if you have GraphViz installed (pp1 stands for print pass 1 and it supports pp1, pp2 and pp3)Here are some useful commands you can run on that output ll file:
clang -x ir -o test.exe test.ll to make native executable from ll fileclang -c -x ir -o test.o test.ll to make native object file from ll filellvm-objdump.exe -disassemble test.o to get native disassembly of object fileAlso if you want to experiment and discover what kind of llvm code needs to be written for certain constructs in C and C++ you can write the code you want to compile in a test.cpp file and then run:
clang -S -emit-llvm test.cpp -o test.ll to compile any cpp code to llvm assemblyllc -march=cpp test.ll -o llvmtest.cpp to convert llvm assembly to cpp file of llvm API calls that generate that assemblyFor this you need llc built with cpp option which isn't always included in binary builds of LLVM so you may need to compile LLVM manually in order to get it.
Compiler source is in compiler directory but it also uses smmgvpass from utility folder:
ibscommon just contains some common C compiler directives or pragmasibsallocator contains implementation of custom memory allocatoribsdictionary contains implementation of custom key-value store where multiple values can be pushed and popup under the same keysmmmsgs contains code that collects error and warning messages from compiler and can output themsmmlexer contains code that transforms input file text into a sequence of tokens, parsing numbers, keywords, symbols etc.smmparser contains code that parses the sequence of tokens from lexer and builds Abstract Syntax Tree (AST) doing some validations on the waysmmtypeinference does further validations and infers type of expressions and variables based on basic elements of expressionssmmsempass does further validations and propagates the biggest infered type down toward basic elements of expressionssmmllvmcodegen goes through now valid AST and generates LLVM module which it then outputs as LLVM assemblysmmgvpass from utility folder goes through AST and prints it in a form that GraphViz can then parse and generate an image of it as you can see in ast.svg fileTest folder contains code and samples for automatic tests
AllTests is entry point for running testsCuTest is small C unit testing framework from http://cutest.sourceforge.net/smmlexertests contains unit tests for lexersmmparsertests contains unit tests for parser which use the 3 files bellow to process samples from tests/samples directorysmmastwritter will write AST of a sample to ast file in easy to parse but still human readable format if such file doesn't already existsmmastreader will read AST from ast file if it already existssmmastmatcher will compare AST generated from parsing the sample with the one read from corresponding ast file and report if there are any differencesYou can see the exact changes mentioned bellow in a commit called "Adds support for if and while statements" from January 14th 2017.
if condition then statement; else statement; // statement can also be { block }if, then and else.if node. Thus I get AST representation of if statement.if node itself and call processExpression and processStatement in order to print nodes for its condition and body.summus -pp1 inputfile.smm | dot -Tsvg -oast.svg to generate an image of AST tree.summus -pp2 inputfile.smm | dot -Tsvg -oast.svg and the same with -pp3 parameter to see how AST looks after each pass.then body, for else body if it is given and for code that comes after if statement. If root of condition node is logical and or or node I call processAndOrInstr function directly because I just need conditional jumps it generates while I call processExpression for all other types of nodes. If I also called processExpression for logical nodes I would get a node that represents a resulting value of the condition (which is true or false) and then generate additional conditional jump based on that value which creates some extra instructions that I don't need. After this I position instruction building in then body block and call processStatement to generate instructions for it. At the end I add a branch instruction which jumps to end block. If there is an else body I position builder in its block and again call processStatement after which I also add a branch instruction that jumps to end block. At the end I position the builder in end block so the rest of code is generated in it.This is where I stopped and left the rest of the work for the reader. The next step is to test different kind of invalid code and see how summus compiler handles it like missing then and similar. Once you are happy how it does that you need to write automatic tests for it:
for statement[start:end] for slicing and ~ for contcatenating79 commits
C
97.0%
LLVM
2.6%