Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/mustafaquraish/cup
simple, C-like compiled programming language
https://github.com/mustafaquraish/cup
compiler language
Last synced: 3 months ago
JSON representation
simple, C-like compiled programming language
- Host: GitHub
- URL: https://github.com/mustafaquraish/cup
- Owner: mustafaquraish
- License: mit
- Created: 2022-01-30T06:09:49.000Z (almost 3 years ago)
- Default Branch: master
- Last Pushed: 2022-08-24T03:23:36.000Z (about 2 years ago)
- Last Synced: 2024-07-24T11:56:10.894Z (3 months ago)
- Topics: compiler, language
- Language: Rust
- Homepage:
- Size: 669 KB
- Stars: 295
- Watchers: 8
- Forks: 11
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# CUP: C(ompiler) U(nder) P(rogress)
A badly named, in-progress programming language just to learn how these things work. Wait, doesn't everyone write a compiler when they're bored?
Currently, the language is comparable to C, with some syntax changes inspired by Rust (that also make it a little easier to parse). The compiler outputs assembly code in `yasm` format, so you will need [yasm](https://yasm.tortall.net/) and a linker of your choice to compile it. The included Makefile and scripts use `ld`. (Alternatively, you can use `nasm`, but you will have to change the command being run in `compiler/main.cup` and `meta/bootstrap.sh`)
Only linux and macOS (only on x86_64) are supported.
## Building
### Tools
Make sure you have `yasm` and `ld` installed, and located on your `PATH`.
### Compiling
The reference implementation of the compiler is written in CUP, so you'll need to use the pre-compiled YASM files to get the initial executable. You should be able to run the command below to create the `./build/cupcc` compiler:
```bash
$ ./meta/bootstrap.sh
```
Compile a program (and optionally run it) using:
```bash
$ ./build/cupcc /path/to/program.cup -o prog
$ ./prog 1 2 3 4
# OR
$ ./build/cupcc /path/to/program.cup -o prog -r 1 2 3 4
```
Make sure to not have the executable name end in `.yasm` or `.o`, since there are some temporary files created during compilation.---
## Code Samples
### Hello World
Some common functions you'll want are located in `std/common.cup`
```rust
import "std/common.cup";fn main(arc: int, argv: char**): int {
putsln("Hello, world!");
return 0;
}
```### Variables
Variables are strongly typed. You can either declare them with a type, or they can be inferred if there is an initial assignment.
```rust
fn main() {
let x: int = 5; // Explicity define the type
let y = 6; // Infer the type
let z = x + y; // Add them, and infer the type
}
```### Pointers and arrays
```rust
fn main() {
let x: int[10]; // An array of 10 ints (initializers not supported)
let y: int* = x; // Automatically decays to a pointer when passed or assigned
let z = y; // type(z) == int* also works
let a = x[0]; // Access the first element (`a` is an int)
let b = *(x+1); // Access the second element (can use pointer arithmetic)
}
```### Structs / Unions / Enums
```rust
// For now, enums just generate constant values with sequential numbers.
// They aren't a "type" on their own.
enum Type {
TypeInt,
TypeFloat,
TypeChar,
}struct Variable {
typ: int; // Can't use `Type` here, because it's not a type
value: union { // Anonymous nested structures are allowed.
as_int: int;
as_char: char;
as_ptr: Variable*; // Can recursively define types.
};
};fn main() {
let x: Variable; // No struct initializers yet
x.typ = TypeInt;
x.value.as_int = 5;
}
```### Methods for Structs/Unions
```rust
struct Value {
x: int;
};method Value::inc(amount: int) {
// self (pointer) is implicitly passed in
self.x = self.x + amount;
}method Value::print() {
print(self.x);
}fn main() {
let v: Value;
let v_ptr = &v;v.x = 0;
// Call methods using `::`
v::inc(10);
v_ptr::print(); // Also works for pointers
}
```### File I/O
For now, the file I/O is very inspired by C, but it's wrapped using methods for the `File` object. Optionally, you can use the raw syscalls (which behave like C), to deal with file descriptors manually. However it's preferred to use the `File` object as it's more convenient and also provides buffered writes.
A simple implementation of `cat` is:
```rust
import "std/file.cup";fn main(argc: int, argv: char**) {
for (let i = 1; i < argc; ++i) {
let file = fopen(argv[i], 'r');
defer file::close(); // Close the file at the end of the block (in each iteration)let buf: char[1024];
let n = file::read(buf, 1024); // use file-specific functions
while (n > 0) {
write(0, buf, n); // Use raw system calls
n = file::read(buf, 1024);
}
// file closed here because of defer
}
}
```---
Want some more examples? Check out the [examples](examples/) directory, or the [compiler](compiler/) directory, which contains the implementation of the compiler itself.