To sum it up simply: I like C and did it for fun. I also like namespaces.
If you enjoy programming in C, you'll definitely feel quite at home with this language.
- There'll probably always be more things to add to the standard library and now, more optimizations to add to the compiler (this is putting aside language features as well). Feel free to lend a hand! Any help would always be appreciated :^).
- See the LICENSE for what applies to this code (and code you would contribute).
- If something went terribly wrong, feel free to let me know and I'll try my best to rectify it.
- Most importantly, this project was a chance for me to build something from the ground up and learn how stuff I works. If you are reading this and want to contribute, I'd like to keep this spirit going and hope you feel the same way.
See the LICENSE file.
- CMake (>= 3.0)
- Make (>= 3.81)
- g++-9 (C++17)
- realpath
- GNU binutils
To compile spectre for your machine:
$ mkdir build
$ cd build
$ cmake -DCMAKE_CXX_COMPILER=g++-9 ..
$ make
This will setup and compile spectre for you.
The standard library only works on an actual 32-bit ARMV6L Linux system. If you don't have access to one, you'll need to emulate one.
First, you'll need to copy the libspectre
directory into your /usr/include
directory. The spectre compiler searches for system includes from the
/usr/include/libspectre
path. I'd recommend giving your current user full permissions over the folder during setup (you can change this later if you want).
Additionally, you'll need to add the spectre compiler to your $PATH
for this build script to work.
From here on out, I'll assume that you have full permissions to the /usr/include/libspectre
folder and spectre
is in your $PATH
.
Now, do the following inside the /usr/include/libspectre
folder:
$ make
This creates a temporary build directory (/usr/include/libspectre/build
), builds the standard library, and statically links everything into a libspectre.a
archive.
The libspectre.a
archive will automatically placed in the root of the standard library (/usr/include/libspectre
); this matches what the sp
script looks for when
linking your program with standard library functions.
You can remove the build/
folder now if you want.
Running assumes you've compiled the standard library from source in the last step (see the Config
section for more clarifications on this) and you're on a 32-bit Linux ARMV6L system.
Here's a sample Spectre program to print Hello World!
using syscalls.
import <"std/syscall">
import <"std/string">
using std::syscall::direct_write;
using std::string::strlen;
func int main() {
char* hello = "Hello World!\n";
direct_write(1, hello, strlen(hello));
return 0;
}
You could also try the more ergonomic printf function as well:
import <"std/io">
using std::io::printf;
func int main() {
printf("Hello World!\n");
return 0;
}
Save either snippet in a file called hello.sp
(or really whatever you want, I'll just assume hello.sp
from here on out).
Now, run the following:
$ spectre hello.sp
$ as -mfloat-abi=hard -mfpu=vfp hello.s -o hello.o
$ ld hello.o -o hello -L/usr/include/libspectre -l:libspectre.a
$ ./hello
This should print Hello World!
if everything went smoothly. Since these sets of commands are so common, there's a shortcut for doing this:
$ sp hello.sp
$ ./hello
Where the sp
command is just a bash script wrapper around the spectre
compiler.
If you don't have access to a physical ARMV6L machine, I would recommend using QEMU to emulate one. I've tested this on a QEMU 32-bit emulated CPU that has floating point support with a Raspbian Linux distribution. If you are looking for the old, MIPS backend, check out this README.
Inside the tests/
folder, there are two folders, work/
and ref_outputs/
. work/
contains tests you can automatically run (see below). ref_outputs/
contains the
reference outputs for the tests.
Once again, this assumes the standard library from before.
$ ./clear_outputs.sh
$ ./check_outputs
$ ./run.sh
This will run all the tests in each directory, pipe outputs and return values to files, and check the outputs of the tests to see if they match the reference outputs. These folders contain examples of practical spectre programs such as linked lists, ternary search trees, etc. So if you're confused about how to use spectre, this is a good place to start!
Inside the samples/
folder, you will see some simple spectre programs that display the power of the spectre programming language and standard library.
In addition to the tests, these samples are a great place to start seeing spectre in action in a practical setting.
These currently include things like a decimal to hexadecimal converter (and vice versa), a simple echo program, and more.
The more complex ones include:
- A full expression evaluator. All of C's binary operators are supported; multiple statements (separated by semicolons) are also supported as well.
This is in the
samples/expr_eval/
folder. - A simple ls clone; this includes some of the output provided by the traditional
ls -la
including displaying hidden files, file permissions, file sizes, etc. - A simple du clone; this is essentially the output of
du -b
on regular files.
- Should make the
sp
script more robust by just doing the entire thing in C++ so it'll handle more options as well. Currently, it takes all files as inputs and assumes your first file is the file with your main function. Should make testing more robust and intuitive (this includes updating all tests to include theThis is being accomplished with thetest.sp
include and automate checking for tests).tests/work/check_outputs.sh
script. See theTesting
section above.- Implement more optimizations (copy propagation, CSE, etc.).
- Implement pre-coloring and support register coalescing in
alloc_regs.{c,h}pp
. Also make the algorithm more efficient. (WIP: Optimized register allocation quite a bit but more can be done). - Look into linking calls dynamically; currently everything is statically linked which makes life easier, but dynamic linking should be supported.