Initial commit

This commit is contained in:
Jeehoon Kang
2020-03-17 17:31:16 +09:00
commit b929dc334d
54 changed files with 4368 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/target
**/*.rs.bk

349
Cargo.lock generated Normal file
View File

@@ -0,0 +1,349 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace"
version = "0.3.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)",
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "backtrace-sys"
version = "0.1.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cc"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "clap"
version = "2.33.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)",
"bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)",
"yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "either"
version = "1.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "failure"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)",
"failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure_derive"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
"synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "getrandom"
version = "0.1.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "hermit-abi"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "itertools"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "kecc"
version = "0.1.0"
dependencies = [
"clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)",
"lang-c 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "lang-c"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "libc"
version = "0.2.67"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "ppv-lite86"
version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_chacha"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_core"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "redox_syscall"
version = "0.1.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "remove_dir_all"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "rustc-demangle"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "syn"
version = "1.0.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "synstructure"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "tempfile"
version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
"rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)",
"redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)",
"remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-width"
version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "unicode-xid"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "vec_map"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi"
version = "0.3.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "yaml-rust"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata]
"checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
"checksum atty 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
"checksum backtrace 0.3.45 (registry+https://github.com/rust-lang/crates.io-index)" = "ad235dabf00f36301792cfe82499880ba54c6486be094d1047b02bacb67c14e8"
"checksum backtrace-sys 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "ca797db0057bae1a7aa2eef3283a874695455cecf08a43bfb8507ee0ebc1ed69"
"checksum bitflags 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
"checksum cc 1.0.50 (registry+https://github.com/rust-lang/crates.io-index)" = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd"
"checksum cfg-if 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
"checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9"
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
"checksum failure 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b"
"checksum failure_derive 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231"
"checksum getrandom 0.1.14 (registry+https://github.com/rust-lang/crates.io-index)" = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb"
"checksum hermit-abi 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "1010591b26bbfe835e9faeabeb11866061cc7dcebffd56ad7d0942d0e61aefd8"
"checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484"
"checksum lang-c 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "43d8e04e01e7e22312294e6aaa1e121192b103abf9408800fc20ee85c67ccc0f"
"checksum libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)" = "eb147597cdf94ed43ab7a9038716637d2d1bf2bc571da995d0028dec06bd3018"
"checksum ppv-lite86 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "74490b50b9fbe561ac330df47c08f3f33073d2d00c150f719147d7c54522fa1b"
"checksum proc-macro2 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6c09721c6781493a2a492a96b5a5bf19b65917fe6728884e7c44dd0c60ca3435"
"checksum quote 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2bdc6c187c65bca4260c9011c9e3132efe4909da44726bad24cf7572ae338d7f"
"checksum rand 0.7.3 (registry+https://github.com/rust-lang/crates.io-index)" = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
"checksum rand_chacha 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
"checksum rand_core 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
"checksum rand_hc 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
"checksum redox_syscall 0.1.56 (registry+https://github.com/rust-lang/crates.io-index)" = "2439c63f3f6139d1b57529d16bc3b8bb855230c8efcc5d3a896c8bea7c3b1e84"
"checksum remove_dir_all 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "4a83fa3702a688b9359eccba92d153ac33fd2e8462f9e0e3fdf155239ea7792e"
"checksum rustc-demangle 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783"
"checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
"checksum syn 1.0.16 (registry+https://github.com/rust-lang/crates.io-index)" = "123bd9499cfb380418d509322d7a6d52e5315f064fe4b3ad18a53d6b92c07859"
"checksum synstructure 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545"
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
"checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
"checksum unicode-width 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "caaa9d531767d1ff2150b9332433f32a24622147e5ebb1f26409d5da67afd479"
"checksum unicode-xid 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "826e7639553986605ec5979c7dd957c7895e93eabed50ab2ffa7f6128a75097c"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum wasi 0.9.0+wasi-snapshot-preview1 (registry+https://github.com/rust-lang/crates.io-index)" = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
"checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
"checksum yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "e66366e18dc58b46801afbf2ca7661a9f59cc8c5962c29892b6039b4f86fa992"

31
Cargo.toml Normal file
View File

@@ -0,0 +1,31 @@
[package]
name = "kecc"
version = "0.1.0"
authors = ["Chunmyong Park <chunmyong.park@kaist.ac.kr>", "Jeehoon Kang <jeehoon.kang@kaist.ac.kr>", "Hyunsu Kim <hyunsu.kim00@kaist.ac.kr>"]
edition = "2018"
default-run = "kecc"
description = "KAIST Educational C Compiler"
homepage = "https://github.com/kaist-cp/kecc"
repository = "https://github.com/kaist-cp/kecc"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "kecc"
path = "src/lib.rs"
[[bin]]
name = "kecc"
path = "bin/kecc.rs"
[[bin]]
name = "fuzz"
path = "bin/fuzz.rs"
[dependencies]
clap = { version = "2.33.0", features = ["yaml"] }
lang-c = "0.7.0"
itertools = "0.8"
failure = "0.1.6"
tempfile = "3.1.0"

43
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,43 @@
def setupRust() {
sh "rustup component add rustfmt clippy"
sh "rustup install nightly"
sh "cargo update"
sh "cargo"
}
pipeline {
agent {
docker {
image 'rust:latest'
}
}
stages {
stage('Rustfmt') {
steps {
setupRust()
sh "cargo fmt --all -- --check"
}
}
stage('Clippy') {
steps {
setupRust()
sh "cargo clippy --all"
}
}
stage('Build') {
steps {
setupRust()
sh "cargo build"
sh "cargo build --release"
}
}
stage('Test') {
steps {
setupRust()
sh "cargo test"
sh "cargo test --release"
}
}
}
}

63
README.md Normal file
View File

@@ -0,0 +1,63 @@
# KECC: KAIST Educational C Compiler
## Install
Install [rustup](https://rustup.rs/).
## Build
```sh
cargo build # debug build
cargo build --release # release build
```
## Run
```sh
cargo run -- -h # print options
cargo run -- -p examples/fibonacci.c # parse
cargo run -- -i examples/fibonacci.c # irgen
cargo run -- examples/fibonacci.c # compile
cargo run --release -- examples/fibonacci.c # compile with release build
```
## Test
```
cargo test # debug build test
cargo test --release # release build test
cargo test <test-name> # run a particular test
```
`<test-name>` can be `test_ast_print`, `ir_smoke`, ...
## Fuzzing
### Install
```sh
# Ubuntu 18.04 or higher
apt install -y make cmake python3
# MacOS
xcode-select install
brew install cmake python3
```
### Run
The following script generates 10 random test cases and tests your C AST printer:
```sh
python3 tests/fuzz.py --help # print options
python3 tests/fuzz.py --print -n10 # test C AST printer for 10 times
```
We use [Csmith](https://embed.cs.utah.edu/csmith/) to randomly generate C source codes. Csmith will
be automatically downloaded and built by the test script.

26
bin/fuzz.rs Normal file
View File

@@ -0,0 +1,26 @@
#[macro_use]
extern crate clap;
use clap::{crate_authors, crate_description, crate_version, App};
#[macro_use]
extern crate kecc;
use kecc::{Parse, Translate};
fn main() {
let yaml = load_yaml!("fuzz_cli.yml");
#[allow(deprecated)]
let matches = App::from_yaml(yaml)
.version(crate_version!())
.about(crate_description!())
.author(crate_authors!(", "))
.get_matches();
let input = matches.value_of("INPUT").unwrap();
let unit = ok_or_exit!(Parse::default().translate(&input), 1);
if matches.is_present("print") {
kecc::write_c_test(&unit);
return;
}
}

10
bin/fuzz_cli.yml Normal file
View File

@@ -0,0 +1,10 @@
name: fuzz
args:
- print:
short: p
long: print
help: Fuzzes C AST printer
- INPUT:
help: Sets the input file to use
required: true
index: 1

52
bin/kecc.rs Normal file
View File

@@ -0,0 +1,52 @@
#[macro_use]
extern crate clap;
use clap::{crate_authors, crate_description, crate_version, App};
#[macro_use]
extern crate kecc;
use kecc::{Codegen, Irgen, Optimize, Parse, Translate, O1};
fn main() {
let yaml = load_yaml!("kecc_cli.yml");
#[allow(deprecated)]
let matches = App::from_yaml(yaml)
.version(crate_version!())
.about(crate_description!())
.author(crate_authors!(", "))
.get_matches();
let input = matches.value_of("INPUT").unwrap();
let unit = ok_or_exit!(Parse::default().translate(&input), 1);
let output = matches.value_of("output").unwrap_or_else(|| "-");
let mut output: Box<dyn ::std::io::Write> = if output == "-" {
Box::new(::std::io::stdout())
} else {
Box::new(ok_or_exit!(::std::fs::File::open(output), 1))
};
if matches.is_present("print") {
kecc::write_c(&unit, &mut output).unwrap();
return;
}
let mut ir = match Irgen::default().translate(&unit) {
Ok(ir) => ir,
Err(irgen_error) => {
println!("{}", irgen_error);
return;
}
};
if matches.is_present("irgen") {
kecc::write_ir(&ir, &mut output).unwrap();
return;
}
if matches.is_present("optimize") {
O1::default().optimize(&mut ir);
}
let asm = ok_or_exit!(Codegen::default().translate(&ir), 1);
kecc::write_asm(&asm, &mut output);
}

24
bin/kecc_cli.yml Normal file
View File

@@ -0,0 +1,24 @@
name: kecc
args:
- print:
short: p
long: print
help: Prints the input file's AST
- irgen:
short: i
long: irgen
help: Generates IR
- optimize:
short: O
long: optimize
help: Optimizes IR
- output:
short: o
long: output
value_name: FILE
help: Sets the output file to use
takes_value: true
- INPUT:
help: Sets the input file to use
required: true
index: 1

2
bors.toml Normal file
View File

@@ -0,0 +1,2 @@
status = [ "continuous-integration/jenkins/branch" ]
delete_merged_branches = true

3
examples/alignof.c Normal file
View File

@@ -0,0 +1,3 @@
int main() {
return _Alignof(const int);
}

6
examples/bar.c Normal file
View File

@@ -0,0 +1,6 @@
int bar(int x, int y, int z){
int arith_mean = (x + y + z) / 3;
int ugly_mean = (((x + y) / 2) * 2 + z) / 3;
if (x == y) { return y; }
else { return z; }
}

6
examples/comma.c Normal file
View File

@@ -0,0 +1,6 @@
int main()
{
int y = 2;
int x = (y += 2, 2, y + 3);
return x;
}

6
examples/cond.c Normal file
View File

@@ -0,0 +1,6 @@
int main()
{
int y = 1;
int x = 0;
return (x == y) ? 2 : 5;
}

7
examples/fib2.c Normal file
View File

@@ -0,0 +1,7 @@
int (fibonacci)(int n) {
if (n < 2) {
return n;
}
return fibonacci(n - 2) + fibonacci(n - 1);
}

20
examples/fib3.c Normal file
View File

@@ -0,0 +1,20 @@
int fibonacci(int n) {
int i = 0;
int t1 = 0, t2 = 1, next_term = 0;
if (n < 2) {
return n;
}
for (i = 1; i < n; ++i) {
next_term = t1 + t2;
t1 = t2;
t2 = next_term;
}
return t2;
}
int main() {
return fibonacci(9);
}

22
examples/fib4.c Normal file
View File

@@ -0,0 +1,22 @@
int fibonacci(int n) {
int i = 0;
int t1 = 0, t2 = 1, next_term = 0;
if (n < 2) {
return n;
}
i = 1;
while (i < n) {
next_term = t1 + t2;
t1 = t2;
t2 = next_term;
++i;
}
return t2;
}
int main() {
return fibonacci(9);
}

22
examples/fib5.c Normal file
View File

@@ -0,0 +1,22 @@
int fibonacci(int n) {
int i = 0;
int t1 = 0, t2 = 1, next_term = 0;
if (n < 2) {
return n;
}
i = 1;
do {
next_term = t1 + t2;
t1 = t2;
t2 = next_term;
++i;
} while (i < n);
return t2;
}
int main() {
return fibonacci(9);
}

11
examples/fibonacci.c Normal file
View File

@@ -0,0 +1,11 @@
int fibonacci(int n) {
if (n < 2) {
return n;
}
return fibonacci(n - 2) + fibonacci(n - 1);
}
int main() {
return fibonacci(9);
}

8
examples/foo.c Normal file
View File

@@ -0,0 +1,8 @@
int foo(int x, int y, int z){
if (x == y) { return y; }
else { return z; }
}
int main() {
return foo(0, 1, -1);
}

9
examples/foo2.c Normal file
View File

@@ -0,0 +1,9 @@
int main() {
int i = 0;
for (int i = 0; i < 10; ++i) {
int i = 0;
int k = 0;
}
return 0;
}

13
examples/foo3.c Normal file
View File

@@ -0,0 +1,13 @@
int g = 10;
int foo(int, int k);
int main() {
int i = g;
return foo(i, i);
}
int foo(int i, int j) {
return i + j + g;
}

15
examples/foo4.c Normal file
View File

@@ -0,0 +1,15 @@
int foo(int i, int j, int k) {
return i + j + k;
}
int (* foo2())(int, int, int){
return foo;
}
int (* (* foo3())())(int, int, int){
return foo2;
}
int main() {
return foo3()()(2, 2, 2);
}

8
examples/negate.c Normal file
View File

@@ -0,0 +1,8 @@
int foo(int x, int y, int z){
if (!(x == y)) { return y; }
else { return z; }
}
int main() {
return foo(0, 1, -1);
}

15
examples/pointer.c Normal file
View File

@@ -0,0 +1,15 @@
int* foo(int *a){
return a;
}
int main(){
int a = 1;
int *p = &a;
int **p2 = &*&p;
int *p3 = *&p;
*&*foo(*p2) += 1;
*foo(p3) += 1;
return a;
}

7
examples/return_void.c Normal file
View File

@@ -0,0 +1,7 @@
void foo() {
}
int main() {
foo();
return 0;
}

4
examples/simple.c Normal file
View File

@@ -0,0 +1,4 @@
int main()
{
int x = 1;
}

9
examples/simple_for.c Normal file
View File

@@ -0,0 +1,9 @@
int main()
{
int i;
int sum = 0;
for (i = 0; i < 11; ++i) {
sum += i;
}
return sum;
}

7
examples/simple_if.c Normal file
View File

@@ -0,0 +1,7 @@
int (fibonacci)(int n) {
if (n < 2) {
n += 2;
}
return fibonacci(n - 2) + fibonacci(n - 1);
}

3
examples/sizeof.c Normal file
View File

@@ -0,0 +1,3 @@
int main() {
return sizeof(const int);
}

20
examples/switch.c Normal file
View File

@@ -0,0 +1,20 @@
int main() {
int a = 1;
int b = 0;
switch (a) {
case 0: {
b += 1;
break;
}
case 1: {
b += 2;
break;
}
default: {
b += 3;
break;
}
}
return b;
}

5
examples/temp.c Normal file
View File

@@ -0,0 +1,5 @@
int fibonacci(int n) {
while (n + n) {
return n;
}
}

26
examples/temp2.c Normal file
View File

@@ -0,0 +1,26 @@
int f(int i, int const a[const i]) {
int temp = 0;
const float temp2 = 0.f, temp3 = 0.f;
temp = sizeof(unsigned char);
temp = _Alignof(unsigned char);
struct color { int number; char name; } c;
c.name;
struct color *cp = &c;
cp->name;
for(int i = 0, j = 0; i < 10; ++i) {
break;
}
switch(temp) {
case 1: {
break;
}
default: {
break;
}
}
return temp;
}

1
rust-toolchain Normal file
View File

@@ -0,0 +1 @@
1.42.0

18
scripts/make-public.py Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env python3
"""Makes public skeleton.
"""
import os
import subprocess
import itertools
import argparse
import sys
import re
if __name__ == "__main__":
for fullname in os.listdir("src"):
(filename, ext) = os.path.splitext(fullname)
if ext == ".public":
os.rename(os.path.join("src", fullname), os.path.join("src", filename))

2
src/asm.rs Normal file
View File

@@ -0,0 +1,2 @@
/// TODO
pub struct Asm {}

543
src/assert_ast_equiv.rs Normal file
View File

@@ -0,0 +1,543 @@
#![allow(unused_variables)]
use lang_c::ast::*;
use lang_c::span::Node;
use std::ops::Deref;
use itertools::izip;
trait IsEquiv {
fn is_equiv(&self, other: &Self) -> bool;
}
impl<T: IsEquiv> IsEquiv for Node<T> {
fn is_equiv(&self, other: &Self) -> bool {
self.node.is_equiv(&other.node)
}
}
impl<T: IsEquiv> IsEquiv for Box<T> {
fn is_equiv(&self, other: &Self) -> bool {
self.deref().is_equiv(other.deref())
}
}
impl<T: IsEquiv> IsEquiv for &T {
fn is_equiv(&self, other: &Self) -> bool {
(*self).is_equiv(*other)
}
}
impl<T: IsEquiv> IsEquiv for Option<T> {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Some(lhs), Some(rhs)) => lhs.is_equiv(rhs),
(None, None) => true,
_ => false,
}
}
}
impl<T: IsEquiv> IsEquiv for Vec<T> {
fn is_equiv(&self, other: &Self) -> bool {
self.len() == other.len() && izip!(self, other).all(|(lhs, rhs)| lhs.is_equiv(rhs))
}
}
impl IsEquiv for TranslationUnit {
fn is_equiv(&self, other: &Self) -> bool {
self.0.is_equiv(&other.0)
}
}
impl IsEquiv for ExternalDeclaration {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Declaration(decl), Self::Declaration(other_decl)) => decl.is_equiv(other_decl),
(Self::FunctionDefinition(fdef), Self::FunctionDefinition(other_fdef)) => {
fdef.is_equiv(other_fdef)
}
_ => false,
}
}
}
impl IsEquiv for Declaration {
fn is_equiv(&self, other: &Self) -> bool {
self.specifiers.is_equiv(&other.specifiers) && self.declarators.is_equiv(&other.declarators)
}
}
impl IsEquiv for FunctionDefinition {
fn is_equiv(&self, other: &Self) -> bool {
self.specifiers.is_equiv(&other.specifiers)
&& self.declarator.is_equiv(&other.declarator)
&& self.declarations.is_equiv(&other.declarations)
&& self.statement.is_equiv(&other.statement)
}
}
impl IsEquiv for InitDeclarator {
fn is_equiv(&self, other: &Self) -> bool {
self.declarator.is_equiv(&other.declarator) && self.initializer.is_equiv(&other.initializer)
}
}
impl IsEquiv for Initializer {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Expression(expr), Self::Expression(other_expr)) => expr.is_equiv(other_expr),
_ => false,
}
}
}
impl IsEquiv for Declarator {
fn is_equiv(&self, other: &Self) -> bool {
self.kind.is_equiv(&other.kind) && self.derived.is_equiv(&other.derived)
}
}
impl IsEquiv for DeclaratorKind {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Identifier(identifier), Self::Identifier(other_identifier)) => {
identifier.node.name == other_identifier.node.name
}
(Self::Declarator(decl), Self::Declarator(other_decl)) => decl.is_equiv(&other_decl),
_ => false,
}
}
}
impl IsEquiv for DerivedDeclarator {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Pointer(pointer_qualifiers), Self::Pointer(other_pointer_qualifiers)) => {
pointer_qualifiers.is_equiv(other_pointer_qualifiers)
}
(Self::Array(array_decl), Self::Array(other_array_decl)) => {
let array_decl = &array_decl.node;
let other_array_decl = &other_array_decl.node;
array_decl.qualifiers.is_equiv(&other_array_decl.qualifiers)
&& array_decl.size.is_equiv(&other_array_decl.size)
}
(Self::Function(func_decl), Self::Function(other_func_decl)) => {
let params = &func_decl.node.parameters;
let other_params = &other_func_decl.node.parameters;
params.is_equiv(other_params)
}
(Self::KRFunction(kr_func_decl), Self::KRFunction(other_kr_func_decl)) => {
kr_func_decl.is_equiv(&other_kr_func_decl)
}
_ => false,
}
}
}
impl IsEquiv for PointerQualifier {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::TypeQualifier(type_qualifier), Self::TypeQualifier(other_type_qualifier)) => {
type_qualifier.is_equiv(other_type_qualifier)
}
_ => false,
}
}
}
impl IsEquiv for ArraySize {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Unknown, Self::Unknown) => true,
(Self::VariableUnknown, Self::VariableUnknown) => true,
(Self::VariableExpression(expr), Self::VariableExpression(other_expr)) => {
expr.is_equiv(&other_expr)
}
(Self::StaticExpression(expr), Self::StaticExpression(other_expr)) => {
expr.is_equiv(&other_expr)
}
_ => false,
}
}
}
impl IsEquiv for ParameterDeclaration {
fn is_equiv(&self, other: &Self) -> bool {
self.specifiers.is_equiv(&other.specifiers)
&& self
.declarator
.as_ref()
.map(|d| &d.node)
.is_equiv(&other.declarator.as_ref().map(|d| &d.node))
}
}
impl IsEquiv for Statement {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Labeled(stmt), Self::Labeled(other_stmt)) => {
stmt.node.label.is_equiv(&other_stmt.node.label)
&& stmt.node.statement.is_equiv(&other_stmt.node.statement)
}
(Self::Compound(items), Self::Compound(other_items)) => items.is_equiv(other_items),
(Self::Expression(expr), Self::Expression(other_expr)) => {
expr.as_ref().is_equiv(&other_expr.as_ref())
}
(Self::If(stmt), Self::If(other_stmt)) => {
let else_stmt = stmt.node.else_statement.as_ref();
let other_else_stmt = other_stmt.node.else_statement.as_ref();
stmt.node.condition.is_equiv(&other_stmt.node.condition)
&& stmt
.node
.then_statement
.is_equiv(&other_stmt.node.then_statement)
&& else_stmt.is_equiv(&other_else_stmt)
}
(Self::Switch(stmt), Self::Switch(other_stmt)) => {
stmt.node.expression.is_equiv(&other_stmt.node.expression)
&& stmt.node.statement.is_equiv(&other_stmt.node.statement)
}
(Self::While(stmt), Self::While(other_stmt)) => {
stmt.node.expression.is_equiv(&other_stmt.node.expression)
&& stmt.node.statement.is_equiv(&other_stmt.node.statement)
}
(Self::DoWhile(stmt), Self::DoWhile(other_stmt)) => {
stmt.node.statement.is_equiv(&other_stmt.node.statement)
&& stmt.node.expression.is_equiv(&other_stmt.node.expression)
}
(Self::For(stmt), Self::For(other_stmt)) => {
stmt.node.initializer.is_equiv(&other_stmt.node.initializer)
&& stmt
.node
.condition
.as_ref()
.is_equiv(&other_stmt.node.condition.as_ref())
&& stmt
.node
.step
.as_ref()
.is_equiv(&other_stmt.node.step.as_ref())
&& stmt.node.statement.is_equiv(&other_stmt.node.statement)
}
(Self::Goto(label), Self::Goto(other_label)) => label.is_equiv(other_label),
(Self::Continue, Self::Continue) => true,
(Self::Break, Self::Break) => true,
(Self::Return(expr), Self::Return(other_expr)) => expr.is_equiv(other_expr),
_ => false,
}
}
}
impl IsEquiv for Label {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Identifier(ident), Self::Identifier(other_ident)) => ident.is_equiv(other_ident),
(Self::Case(expr), Self::Case(other_expr)) => expr.is_equiv(other_expr),
(Self::Default, Self::Default) => true,
_ => false,
}
}
}
impl IsEquiv for Identifier {
fn is_equiv(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl IsEquiv for ForInitializer {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Empty, Self::Empty) => true,
(Self::Expression(expr), Self::Expression(other_expr)) => expr.is_equiv(other_expr),
(Self::Declaration(decl), Self::Declaration(other_decl)) => decl.is_equiv(other_decl),
_ => false,
}
}
}
impl IsEquiv for Expression {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Identifier(identifier), Self::Identifier(other_identifier)) => {
identifier.is_equiv(other_identifier)
}
(Self::Constant(constant), Self::Constant(other_constant)) => {
constant.is_equiv(other_constant)
}
(Self::StringLiteral(other_string_lit), Self::StringLiteral(string_lit)) => {
string_lit.is_equiv(other_string_lit)
}
(Self::Member(member), Self::Member(other_member)) => member.is_equiv(other_member),
(Self::Call(call), Self::Call(other_call)) => call.is_equiv(other_call),
(Self::SizeOf(typename), Self::SizeOf(other_typename)) => {
typename.is_equiv(other_typename)
}
(Self::AlignOf(typename), Self::AlignOf(other_typename)) => {
typename.is_equiv(other_typename)
}
(Self::UnaryOperator(unary), Self::UnaryOperator(other_unary)) => {
unary.node.operator.is_equiv(&other_unary.node.operator)
&& unary.node.operand.is_equiv(&other_unary.node.operand)
}
(Self::Cast(cast), Self::Cast(other_cast)) => {
cast.node.type_name.is_equiv(&other_cast.node.type_name)
&& cast.node.expression.is_equiv(&other_cast.node.expression)
}
(Self::BinaryOperator(binary), Self::BinaryOperator(other_binary)) => {
binary.node.lhs.is_equiv(&other_binary.node.lhs)
&& binary.node.operator.is_equiv(&other_binary.node.operator)
&& binary.node.rhs.is_equiv(&other_binary.node.rhs)
}
(Self::Conditional(conditional), Self::Conditional(other_conditional)) => {
conditional
.node
.condition
.is_equiv(&other_conditional.node.condition)
&& conditional
.node
.then_expression
.is_equiv(&other_conditional.node.then_expression)
&& conditional
.node
.else_expression
.is_equiv(&other_conditional.node.else_expression)
}
(Self::Comma(exprs), Self::Comma(other_exprs)) => {
exprs.as_ref().is_equiv(other_exprs.as_ref())
}
_ => false,
}
}
}
impl IsEquiv for TypeName {
fn is_equiv(&self, other: &Self) -> bool {
self.specifiers.is_equiv(&other.specifiers) && self.declarator.is_equiv(&other.declarator)
}
}
impl IsEquiv for SpecifierQualifier {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::TypeSpecifier(type_specifier), Self::TypeSpecifier(other_type_specifier)) => {
type_specifier.is_equiv(other_type_specifier)
}
(Self::TypeQualifier(type_qualifier), Self::TypeQualifier(other_type_qualifier)) => {
type_qualifier.is_equiv(other_type_qualifier)
}
_ => false,
}
}
}
impl IsEquiv for MemberExpression {
fn is_equiv(&self, other: &Self) -> bool {
self.expression.is_equiv(&other.expression)
&& self.operator.is_equiv(&other.operator)
&& self.identifier.is_equiv(&other.identifier)
}
}
impl IsEquiv for MemberOperator {
fn is_equiv(&self, other: &Self) -> bool {
self == other
}
}
impl IsEquiv for UnaryOperator {
fn is_equiv(&self, other: &Self) -> bool {
self == other
}
}
impl IsEquiv for BinaryOperator {
fn is_equiv(&self, other: &Self) -> bool {
self == other
}
}
impl IsEquiv for Constant {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Integer(integer), Self::Integer(other_integer)) => {
integer.base.is_equiv(&other_integer.base)
&& integer.number == other_integer.number
&& integer.suffix.is_equiv(&other_integer.suffix)
}
(Self::Float(float), Self::Float(other_float)) => {
float.base == other_float.base
&& float.number == other_float.number
&& float.suffix.is_equiv(&other_float.suffix)
}
(Self::Character(literal), Self::Character(other_literal)) => literal == other_literal,
_ => false,
}
}
}
impl IsEquiv for IntegerBase {
fn is_equiv(&self, other: &Self) -> bool {
self == other
}
}
impl IsEquiv for IntegerSuffix {
fn is_equiv(&self, other: &Self) -> bool {
self.unsigned == other.unsigned && self.size == other.size
}
}
impl IsEquiv for FloatSuffix {
fn is_equiv(&self, other: &Self) -> bool {
self.imaginary == other.imaginary && self.format == other.format
}
}
impl IsEquiv for StringLiteral {
fn is_equiv(&self, other: &Self) -> bool {
self == other
}
}
impl IsEquiv for BlockItem {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Declaration(decl), Self::Declaration(other_decl)) => decl.is_equiv(other_decl),
(Self::Statement(statement), Self::Statement(other_statement)) => {
statement.is_equiv(other_statement)
}
_ => false,
}
}
}
impl IsEquiv for DeclarationSpecifier {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(
Self::StorageClass(storage_class_spec),
Self::StorageClass(other_storage_class_spec),
) => storage_class_spec.is_equiv(other_storage_class_spec),
(Self::TypeSpecifier(type_specifier), Self::TypeSpecifier(other_type_specifier)) => {
type_specifier.is_equiv(other_type_specifier)
}
(Self::TypeQualifier(type_qualifier), Self::TypeQualifier(other_type_qualifier)) => {
type_qualifier.is_equiv(other_type_qualifier)
}
_ => false,
}
}
}
impl IsEquiv for StorageClassSpecifier {
fn is_equiv(&self, other: &Self) -> bool {
self == other
}
}
impl IsEquiv for TypeSpecifier {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Void, Self::Void) => true,
(Self::Char, Self::Char) => true,
(Self::Short, Self::Short) => true,
(Self::Int, Self::Int) => true,
(Self::Long, Self::Long) => true,
(Self::Float, Self::Float) => true,
(Self::Double, Self::Double) => true,
(Self::Signed, Self::Signed) => true,
(Self::Unsigned, Self::Unsigned) => true,
(Self::Bool, Self::Bool) => true,
(Self::Struct(struct_type), Self::Struct(other_struct_type)) => {
struct_type.is_equiv(other_struct_type)
}
(Self::Enum(enum_type), Self::Enum(other_enum_type)) => {
enum_type.is_equiv(other_enum_type)
}
(Self::TypedefName(identifier), Self::TypedefName(other_identifier)) => {
identifier.is_equiv(other_identifier)
}
_ => false,
}
}
}
impl IsEquiv for StructType {
fn is_equiv(&self, other: &Self) -> bool {
self.declarations.is_equiv(&other.declarations)
&& self.kind.is_equiv(&other.kind)
&& self.identifier.is_equiv(&other.identifier)
}
}
impl IsEquiv for StructKind {
fn is_equiv(&self, other: &Self) -> bool {
self == other
}
}
impl IsEquiv for StructDeclaration {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Field(struct_field), Self::Field(other_struct_field)) => {
struct_field.is_equiv(other_struct_field)
}
_ => false,
}
}
}
impl IsEquiv for StructField {
fn is_equiv(&self, other: &Self) -> bool {
self.specifiers.is_equiv(&other.specifiers) && self.declarators.is_equiv(&other.declarators)
}
}
impl IsEquiv for StructDeclarator {
fn is_equiv(&self, other: &Self) -> bool {
self.declarator.is_equiv(&other.declarator) && self.bit_width.is_equiv(&other.bit_width)
}
}
impl IsEquiv for EnumType {
fn is_equiv(&self, other: &Self) -> bool {
self.identifier.is_equiv(&other.identifier) && self.enumerators.is_equiv(&other.enumerators)
}
}
impl IsEquiv for Enumerator {
fn is_equiv(&self, other: &Self) -> bool {
self.identifier.is_equiv(&other.identifier) && self.expression.is_equiv(&other.expression)
}
}
impl IsEquiv for TypeQualifier {
fn is_equiv(&self, other: &Self) -> bool {
match (self, other) {
(Self::Const, Self::Const) => true,
_ => false,
}
}
}
impl IsEquiv for CallExpression {
fn is_equiv(&self, other: &Self) -> bool {
self.callee.is_equiv(&other.callee) && self.arguments.is_equiv(&other.arguments)
}
}
pub fn assert_ast_equiv(lhs: &TranslationUnit, rhs: &TranslationUnit) {
if !lhs.is_equiv(rhs) {
panic!(
r#"assertion failed: `(left.is_equiv(right))`
left: `{:?}`,
right: `{:?}`"#,
lhs, rhs
)
}
}

15
src/codegen.rs Normal file
View File

@@ -0,0 +1,15 @@
use crate::asm::Asm;
use crate::ir;
use crate::Translate;
#[derive(Default)]
pub struct Codegen {}
impl Translate<ir::TranslationUnit> for Codegen {
type Target = Asm;
type Error = ();
fn translate(&mut self, _source: &ir::TranslationUnit) -> Result<Self::Target, Self::Error> {
unimplemented!()
}
}

1096
src/ir.rs Normal file

File diff suppressed because it is too large Load Diff

26
src/irgen.rs Normal file
View File

@@ -0,0 +1,26 @@
use std::fmt;
use lang_c::ast::*;
use crate::*;
#[derive(Default)]
pub struct Irgen {}
#[derive(Debug, PartialEq)]
pub struct IrgenError {}
impl fmt::Display for IrgenError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "IrgenError")
}
}
impl Translate<TranslationUnit> for Irgen {
type Target = ir::TranslationUnit;
type Error = IrgenError;
fn translate(&mut self, _unit: &TranslationUnit) -> Result<Self::Target, Self::Error> {
todo!("homework 2")
}
}

37
src/lib.rs Normal file
View File

@@ -0,0 +1,37 @@
#![deny(warnings)]
mod utils;
pub mod asm;
pub mod ir;
mod codegen;
mod irgen;
mod optimize;
mod parse;
pub mod run_ir;
mod write_asm;
mod write_base;
mod write_c;
mod write_ir;
pub mod assert_ast_equiv;
pub mod write_c_test;
pub use utils::*;
pub use asm::Asm;
pub use codegen::Codegen;
pub use irgen::Irgen;
pub use optimize::{O0, O1};
pub use parse::Parse;
pub use utils::{Optimize, Repeat, Translate};
pub use write_asm::write_asm;
pub use write_c::write_c;
pub use write_ir::write_ir;
pub use assert_ast_equiv::assert_ast_equiv;
pub use write_c_test::write_c_test;

31
src/optimize.rs Normal file
View File

@@ -0,0 +1,31 @@
use crate::ir;
use crate::{Optimize, Repeat};
#[derive(Default)]
pub struct O0 {}
#[derive(Default)]
pub struct Mem2reg {}
#[derive(Default)]
pub struct Gvn {}
pub type O1 = Repeat<(Mem2reg, Gvn)>;
impl Optimize<ir::TranslationUnit> for O0 {
fn optimize(&mut self, _code: &mut ir::TranslationUnit) -> bool {
false
}
}
impl Optimize<ir::TranslationUnit> for Mem2reg {
fn optimize(&mut self, _code: &mut ir::TranslationUnit) -> bool {
unimplemented!()
}
}
impl Optimize<ir::TranslationUnit> for Gvn {
fn optimize(&mut self, _code: &mut ir::TranslationUnit) -> bool {
unimplemented!()
}
}

551
src/parse.rs Normal file
View File

@@ -0,0 +1,551 @@
use std::ops::Deref;
use std::path::Path;
use lang_c::ast::*;
use lang_c::driver::{parse, Config, Error as ParseError};
use lang_c::span::Node;
use crate::Translate;
#[derive(Debug)]
pub enum Error {
ParseError(ParseError),
Unsupported,
}
#[derive(Default)]
pub struct Parse {}
impl<P: AsRef<Path>> Translate<P> for Parse {
type Target = TranslationUnit;
type Error = Error;
fn translate(&mut self, source: &P) -> Result<Self::Target, Self::Error> {
let config = Config::default();
let ast = parse(&config, source).map_err(Error::ParseError)?;
let unit = ast.unit;
unit.assert_supported();
Ok(unit)
}
}
trait AssertSupported {
fn assert_supported(&self);
}
impl<T: AssertSupported> AssertSupported for Node<T> {
fn assert_supported(&self) {
self.node.assert_supported();
}
}
impl<T: AssertSupported> AssertSupported for Option<T> {
fn assert_supported(&self) {
if let Some(this) = self {
this.assert_supported();
}
}
}
impl<T: AssertSupported> AssertSupported for Box<T> {
fn assert_supported(&self) {
self.deref().assert_supported();
}
}
impl<T: AssertSupported> AssertSupported for Vec<T> {
fn assert_supported(&self) {
self.iter().for_each(AssertSupported::assert_supported);
}
}
impl<T: AssertSupported> AssertSupported for [T] {
fn assert_supported(&self) {
self.iter().for_each(AssertSupported::assert_supported);
}
}
impl AssertSupported for TranslationUnit {
fn assert_supported(&self) {
self.0.assert_supported();
}
}
impl AssertSupported for ExternalDeclaration {
fn assert_supported(&self) {
match self {
Self::Declaration(decl) => decl.assert_supported(),
Self::StaticAssert(_) => panic!("ExternalDeclaration::StaticAssert"),
Self::FunctionDefinition(fdef) => fdef.assert_supported(),
}
}
}
impl AssertSupported for Declaration {
fn assert_supported(&self) {
self.specifiers.assert_supported();
self.declarators.assert_supported();
}
}
impl AssertSupported for FunctionDefinition {
fn assert_supported(&self) {
self.specifiers.assert_supported();
self.declarator.assert_supported();
self.declarations.is_empty();
self.statement.assert_supported();
}
}
impl AssertSupported for DeclarationSpecifier {
fn assert_supported(&self) {
match self {
Self::StorageClass(_) => panic!("DeclarationSpecifier::StorageClass"),
Self::TypeSpecifier(type_specifier) => type_specifier.assert_supported(),
Self::TypeQualifier(type_qualifier) => type_qualifier.assert_supported(),
Self::Function(_) => panic!("DeclarationSpecifier::Function"),
Self::Alignment(_) => panic!("DeclarationSpecifier::Alignment"),
Self::Extension(_) => panic!("DeclarationSpecifier::Extension"),
}
}
}
impl AssertSupported for TypeSpecifier {
fn assert_supported(&self) {
match self {
Self::Void => (),
Self::Char => (),
Self::Short => (),
Self::Int => (),
Self::Long => (),
Self::Float => (),
Self::Double => (),
Self::Signed => (),
Self::Unsigned => (),
Self::Bool => (),
Self::Complex => panic!("TypeSpecifier::Complex"),
Self::Atomic(_) => panic!("TypeSpecifier::Atomic"),
Self::Struct(struct_type) => struct_type.assert_supported(),
Self::Enum(_) => panic!("TypeSpecifier::Enum"),
Self::TypedefName(_) => panic!("TypeSpecifier::TypedefName"),
Self::TypeOf(_) => panic!("TypeSpecifier::TypeOf"),
Self::TS18661Float(_) => panic!("TypeSpecifier::TS18661Float"),
}
}
}
impl AssertSupported for StructType {
fn assert_supported(&self) {
self.kind.assert_supported();
self.declarations.assert_supported();
}
}
impl AssertSupported for StructDeclaration {
fn assert_supported(&self) {
match self {
Self::Field(field) => field.assert_supported(),
Self::StaticAssert(_) => panic!("StructDeclaration::StaticAssert"),
}
}
}
impl AssertSupported for StructField {
fn assert_supported(&self) {
self.specifiers.assert_supported();
self.declarators.assert_supported();
}
}
impl AssertSupported for StructDeclarator {
fn assert_supported(&self) {
self.declarator.assert_supported();
assert_eq!(true, self.bit_width.is_none());
}
}
impl AssertSupported for StructKind {
fn assert_supported(&self) {
match self {
Self::Struct => (),
Self::Union => panic!("StructKind::Union"),
}
}
}
impl AssertSupported for AlignmentSpecifier {
fn assert_supported(&self) {
match self {
Self::Type(typename) => typename.assert_supported(),
Self::Constant(_) => panic!(AlignmentSpecifier::Constant),
}
}
}
impl AssertSupported for InitDeclarator {
fn assert_supported(&self) {
self.declarator.assert_supported();
self.initializer.assert_supported();
}
}
impl AssertSupported for Initializer {
fn assert_supported(&self) {
match self {
Self::Expression(expr) => expr.assert_supported(),
Self::List(_) => panic!("Initializer::List"),
}
}
}
impl AssertSupported for Declarator {
fn assert_supported(&self) {
self.kind.assert_supported();
self.derived.assert_supported();
self.extensions.is_empty();
}
}
impl AssertSupported for DerivedDeclarator {
fn assert_supported(&self) {
match self {
Self::Pointer(pointer_qualifiers) => pointer_qualifiers.assert_supported(),
Self::Array(array_decl) => array_decl.assert_supported(),
Self::Function(func_decl) => func_decl.assert_supported(),
// Support when K&R function has no parameter
Self::KRFunction(kr_func_decl) => assert_eq!(true, kr_func_decl.is_empty()),
}
}
}
impl AssertSupported for PointerQualifier {
fn assert_supported(&self) {
match self {
Self::TypeQualifier(type_qualifier) => type_qualifier.assert_supported(),
Self::Extension(_) => panic!("PointerQualifier::Extension"),
}
}
}
impl AssertSupported for ArrayDeclarator {
fn assert_supported(&self) {
self.qualifiers.assert_supported();
self.size.assert_supported();
}
}
impl AssertSupported for TypeQualifier {
fn assert_supported(&self) {
match self {
Self::Const => (),
_ => panic!("TypeQualifier::_"),
}
}
}
impl AssertSupported for ArraySize {
fn assert_supported(&self) {
match self {
Self::VariableExpression(expr) => expr.assert_supported(),
_ => panic!("ArraySize::_"),
}
}
}
impl AssertSupported for FunctionDeclarator {
fn assert_supported(&self) {
self.parameters.assert_supported();
assert_eq!(self.ellipsis, Ellipsis::None);
}
}
impl AssertSupported for ParameterDeclaration {
fn assert_supported(&self) {
self.specifiers.assert_supported();
self.declarator.assert_supported();
self.extensions.is_empty();
}
}
impl AssertSupported for DeclaratorKind {
fn assert_supported(&self) {
match self {
Self::Abstract => panic!("DeclaratorKind::Abstract"),
Self::Identifier(_) => (),
Self::Declarator(decl) => decl.assert_supported(),
}
}
}
impl AssertSupported for BlockItem {
fn assert_supported(&self) {
match self {
Self::Declaration(decl) => decl.assert_supported(),
Self::StaticAssert(_) => panic!("BlockItem::StaticAssert"),
Self::Statement(stmt) => stmt.assert_supported(),
}
}
}
impl AssertSupported for ForInitializer {
fn assert_supported(&self) {
match self {
Self::Empty => (),
Self::Expression(expr) => expr.assert_supported(),
Self::Declaration(decl) => decl.assert_supported(),
Self::StaticAssert(_) => panic!("ForInitializer::StaticAssert"),
}
}
}
impl AssertSupported for Statement {
fn assert_supported(&self) {
match self {
Self::Labeled(_) => panic!("Statement::Labeled"),
Self::Compound(items) => items.assert_supported(),
Self::Expression(expr) => expr.assert_supported(),
Self::If(stmt) => {
stmt.node.condition.assert_supported();
stmt.node.then_statement.assert_supported();
stmt.node.else_statement.assert_supported();
}
Self::Switch(stmt) => stmt.assert_supported(),
Self::While(stmt) => {
stmt.node.expression.assert_supported();
stmt.node.statement.assert_supported();
}
Self::DoWhile(stmt) => {
stmt.node.statement.assert_supported();
stmt.node.expression.assert_supported();
}
Self::For(stmt) => {
stmt.node.initializer.assert_supported();
stmt.node.condition.assert_supported();
stmt.node.step.assert_supported();
stmt.node.statement.assert_supported();
}
Self::Goto(_) => panic!("Statement::Goto"),
Self::Continue | Self::Break => (),
Self::Return(expr) => expr.assert_supported(),
Self::Asm(_) => panic!("Statement::Asm"),
}
}
}
impl AssertSupported for SwitchStatement {
fn assert_supported(&self) {
self.expression.assert_supported();
let items = if let Statement::Compound(items) = &self.statement.node {
items
} else {
panic!("`Statement` in the `switch` is unsupported except `Statement::Compound`")
};
for item in items {
let stmt = if let BlockItem::Statement(stmt) = &item.node {
&stmt.node
} else {
panic!(
"`BlockItem` in the `Statement::Compound` of the `switch` \
is unsupported except `BlockItem::Statement`"
)
};
let stmt_in_label = if let Statement::Labeled(label_stmt) = stmt {
label_stmt.node.label.assert_supported();
&label_stmt.node.statement.node
} else {
panic!(
"`BlockItem::Statement` in the `Statement::Compound` of the `switch` \
is unsupported except `Statement::Labeled`"
)
};
let items = if let Statement::Compound(items) = stmt_in_label {
items
} else {
panic!("`Statement` in the `label` is unsupported except `Statement::Compound`")
};
// Split last and all the rest of the elements of the `Compound` items
let (last, items) = items
.split_last()
.unwrap_or_else(|| panic!("`Statement::Compound` has no item"));
for item in items {
match &item.node {
BlockItem::Declaration(decl) => decl.assert_supported(),
BlockItem::StaticAssert(_) => panic!("BlockItem::StaticAssert"),
BlockItem::Statement(stmt) => {
assert_ne!(
&stmt.node,
&Statement::Break,
"`BlockItem::Statement` in the `Statement::Compound` of the \
`label` should not be `Statement::Break` except the last one"
);
stmt.assert_supported();
}
}
}
// The last element of the `items` must be `Statement::Break`
let stmt = if let BlockItem::Statement(stmt) = &last.node {
&stmt.node
} else {
panic!(
"`BlockItem` in the `Statement::Compound` of the `label` \
is unsupported except `BlockItem::Statement`"
)
};
assert_eq!(
stmt,
&Statement::Break,
"the last `BlockItem` in the `Statement::Compound` \
of the `label` must be `Statement::Break`"
);
}
}
}
impl AssertSupported for Expression {
fn assert_supported(&self) {
match self {
Self::Identifier(_) => (),
Self::Constant(constant) => constant.assert_supported(),
Self::StringLiteral(_) => panic!("Expression:StringLiteral"),
Self::GenericSelection(_) => panic!("Expression:GenericSelection"),
Self::Member(member) => member.assert_supported(),
Self::Call(call) => call.assert_supported(),
Self::CompoundLiteral(_) => panic!("Expression::CompoundLiteral"),
Self::SizeOf(typename) => typename.assert_supported(),
Self::AlignOf(typename) => typename.assert_supported(),
Self::UnaryOperator(unary) => unary.assert_supported(),
Self::Cast(cast) => cast.assert_supported(),
Self::BinaryOperator(binary) => binary.assert_supported(),
Self::Conditional(conditional) => conditional.assert_supported(),
Self::Comma(exprs) => exprs.assert_supported(),
Self::OffsetOf(_) => panic!("Expression::OffsetOf"),
Self::VaArg(_) => panic!("Expression::VaArg"),
Self::Statement(_) => panic!("Expression::Statement"),
}
}
}
impl AssertSupported for Label {
fn assert_supported(&self) {
match self {
Self::Identifier(_) => panic!("Label::Identifier"),
Self::Case(_) => (),
Self::Default => (),
}
}
}
impl AssertSupported for MemberExpression {
fn assert_supported(&self) {
self.expression.assert_supported();
}
}
impl AssertSupported for CallExpression {
fn assert_supported(&self) {
self.callee.assert_supported();
self.arguments.assert_supported();
}
}
impl AssertSupported for TypeName {
fn assert_supported(&self) {
self.specifiers.assert_supported();
self.declarator.assert_supported();
}
}
impl AssertSupported for SpecifierQualifier {
fn assert_supported(&self) {
match self {
Self::TypeSpecifier(type_specifier) => type_specifier.assert_supported(),
Self::TypeQualifier(type_qualifier) => type_qualifier.assert_supported(),
}
}
}
impl AssertSupported for UnaryOperatorExpression {
fn assert_supported(&self) {
self.operator.assert_supported();
self.operand.assert_supported();
}
}
impl AssertSupported for CastExpression {
fn assert_supported(&self) {
self.type_name.assert_supported();
self.expression.assert_supported();
}
}
impl AssertSupported for BinaryOperatorExpression {
fn assert_supported(&self) {
self.operator.assert_supported();
self.lhs.assert_supported();
self.rhs.assert_supported();
}
}
impl AssertSupported for Constant {
fn assert_supported(&self) {
match self {
Self::Integer(integer) => integer.assert_supported(),
Self::Float(float) => float.assert_supported(),
Self::Character(_) => (),
}
}
}
impl AssertSupported for Integer {
fn assert_supported(&self) {
assert_eq!(false, self.suffix.imaginary);
}
}
impl AssertSupported for Float {
fn assert_supported(&self) {
assert_eq!(self.base, FloatBase::Decimal);
self.suffix.format.assert_supported();
assert_eq!(false, self.suffix.imaginary);
}
}
impl AssertSupported for FloatFormat {
fn assert_supported(&self) {
match self {
Self::Float => (),
Self::Double => (),
Self::LongDouble => (),
Self::TS18661Format(_) => panic!("TS18861"),
}
}
}
impl AssertSupported for UnaryOperator {
fn assert_supported(&self) {
if let Self::SizeOf = self {
panic!("UnaryOperaotr::SizeOf")
}
}
}
impl AssertSupported for BinaryOperator {
fn assert_supported(&self) {}
}
impl AssertSupported for ConditionalExpression {
fn assert_supported(&self) {
self.condition.assert_supported();
self.then_expression.assert_supported();
self.else_expression.assert_supported();
}
}

592
src/run_ir.rs Normal file
View File

@@ -0,0 +1,592 @@
use crate::ir::*;
use crate::*;
use failure::Fail;
use std::collections::HashMap;
use std::mem;
use itertools::izip;
// TODO: the variants of Value will be added in the future
#[derive(Debug, PartialEq, Clone)]
pub enum Value {
Undef,
Unit,
Int(i32),
Float(f32),
Bool(bool),
Pointer { bid: Option<usize>, offset: usize },
}
impl Value {
#[inline]
fn pointer(bid: Option<usize>, offset: usize) -> Self {
Self::Pointer { bid, offset }
}
#[inline]
fn get_bool(self) -> Option<bool> {
if let Value::Bool(value) = self {
Some(value)
} else {
None
}
}
#[inline]
fn get_pointer(self) -> Option<(Option<usize>, usize)> {
if let Value::Pointer { bid, offset } = self {
Some((bid, offset))
} else {
None
}
}
#[inline]
fn nullptr() -> Self {
Self::Pointer {
bid: None,
offset: 0,
}
}
#[inline]
fn default_from_dtype(dtype: &Dtype) -> Self {
match dtype {
// TODO: consider `Unit` value in the future
ir::Dtype::Unit { .. } => todo!(),
ir::Dtype::Int { width, .. } => match width {
32 => Self::Int(i32::default()),
_ => todo!("other cases will be covered"),
},
ir::Dtype::Float { .. } => Self::Float(f32::default()),
ir::Dtype::Pointer { .. } => Self::nullptr(),
ir::Dtype::Function { .. } => panic!("function types do not have a default value"),
}
}
}
#[derive(Debug, PartialEq, Fail)]
pub enum InterpreterError {
#[fail(display = "current block is unreachable")]
Unreachable,
#[fail(display = "ir has no main function")]
NoMainFunction,
#[fail(display = "ir has no function definition of {} function", func_name)]
NoFunctionDefinition { func_name: String },
#[fail(
display = "{}:{}:{} / Undef value cannot be used as an operand",
func_name, bid, iid
)]
Undef {
func_name: String,
bid: BlockId,
iid: usize,
},
}
#[derive(Debug, PartialEq, Clone)]
struct Pc {
pub bid: BlockId,
pub iid: usize,
}
impl Pc {
fn new(bid: BlockId) -> Pc {
Pc { bid, iid: 0 }
}
fn increment(&mut self) {
self.iid += 1;
}
}
#[derive(Debug, PartialEq, Clone)]
struct RegisterMap {
inner: HashMap<RegisterId, Value>,
}
impl RegisterMap {
fn new() -> Self {
Self {
inner: HashMap::new(),
}
}
}
#[derive(Default, Debug, PartialEq, Clone)]
/// Bidirectional map between the name of a global variable and memory box id
struct GlobalMap {
/// Map name of a global variable to memory box id
///
/// Since IR treats global variable as `Constant::GlobalVariable`,
/// the interpreter should be able to generate pointer values by infer 'bid'
/// from the 'name' of the global variable.
var_to_bid: HashMap<String, usize>,
/// Map memory box id to the name of a global variable
///
/// When a function call occurs, the interpreter should be able to find `name` of the function
/// from `bid` of the `callee` which is a function pointer.
bid_to_var: HashMap<usize, String>,
}
impl GlobalMap {
/// Create a bi-directional mapping between `var` and `bid`.
fn insert(&mut self, var: String, bid: usize) -> Result<(), InterpreterError> {
if self.var_to_bid.insert(var.clone(), bid).is_some() {
panic!("variable name should be unique in IR")
}
if self.bid_to_var.insert(bid, var).is_some() {
panic!("`bid` is connected to only one `var`")
}
Ok(())
}
fn get_bid(&self, var: &str) -> Option<usize> {
self.var_to_bid.get(var).cloned()
}
fn get_var(&self, bid: usize) -> Option<String> {
self.bid_to_var.get(&bid).cloned()
}
}
#[derive(Debug, PartialEq, Clone)]
struct StackFrame<'i> {
pub pc: Pc,
pub registers: RegisterMap,
pub func_name: String,
pub func_def: &'i FunctionDefinition,
}
impl<'i> StackFrame<'i> {
fn new(bid: BlockId, func_name: String, func_def: &'i FunctionDefinition) -> Self {
StackFrame {
pc: Pc::new(bid),
registers: RegisterMap::new(),
func_name,
func_def,
}
}
}
mod calculator {
use super::Value;
use lang_c::ast;
pub fn calculate_binary_operator_expression(
op: &ast::BinaryOperator,
lhs: Value,
rhs: Value,
) -> Result<Value, ()> {
match (op, lhs, rhs) {
(_, Value::Undef, _) => Err(()),
(_, _, Value::Undef) => Err(()),
(ast::BinaryOperator::Plus, Value::Int(lhs), Value::Int(rhs)) => {
Ok(Value::Int(lhs + rhs))
}
(ast::BinaryOperator::Minus, Value::Int(lhs), Value::Int(rhs)) => {
Ok(Value::Int(lhs - rhs))
}
(ast::BinaryOperator::Equals, Value::Int(lhs), Value::Int(rhs)) => {
Ok(Value::Bool(lhs == rhs))
}
(ast::BinaryOperator::NotEquals, Value::Int(lhs), Value::Int(rhs)) => {
Ok(Value::Bool(lhs != rhs))
}
(ast::BinaryOperator::Less, Value::Int(lhs), Value::Int(rhs)) => {
Ok(Value::Bool(lhs < rhs))
}
_ => todo!(),
}
}
pub fn calculate_unary_operator_expression(
op: &ast::UnaryOperator,
operand: Value,
) -> Result<Value, ()> {
match (op, operand) {
(_, Value::Undef) => Err(()),
(ast::UnaryOperator::Plus, Value::Int(value)) => Ok(Value::Int(value)),
(ast::UnaryOperator::Minus, Value::Int(value)) => Ok(Value::Int(-value)),
(ast::UnaryOperator::Negate, Value::Bool(value)) => Ok(Value::Bool(!value)),
_ => todo!(),
}
}
}
// TODO: allocation fields will be added in the future
// TODO: program fields will be added in the future
#[derive(Debug, PartialEq)]
struct State<'i> {
/// A data structure that maps each global variable to a pointer value
/// When function call occurs, `registers` can be initialized by `global_registers`
pub global_map: GlobalMap,
pub stack_frame: StackFrame<'i>,
pub stack: Vec<StackFrame<'i>>,
// TODO: memory type should change to Vec<Vec<Byte>>
pub memory: Vec<Vec<Value>>,
pub ir: &'i TranslationUnit,
}
impl<'i> State<'i> {
fn new(ir: &'i TranslationUnit, args: Vec<Value>) -> Result<State, InterpreterError> {
// Interpreter starts with the main function
let func_name = String::from("main");
let func = ir
.decls
.get(&func_name)
.ok_or_else(|| InterpreterError::NoMainFunction)?;
let (_, func_def) = func
.get_function()
.ok_or_else(|| InterpreterError::NoMainFunction)?;
let func_def = func_def
.as_ref()
.ok_or_else(|| InterpreterError::NoFunctionDefinition {
func_name: func_name.clone(),
})?;
// Create State
let mut state = State {
global_map: GlobalMap::default(),
stack_frame: StackFrame::new(func_def.bid_init.clone(), func_name, func_def),
stack: Vec::new(),
memory: Vec::new(),
ir,
};
state.alloc_global_variable()?;
// Initialize state with main function and args
state.pass_arguments(args)?;
state.alloc_local_variable()?;
Ok(state)
}
fn alloc_global_variable(&mut self) -> Result<(), InterpreterError> {
for (name, decl) in &self.ir.decls {
// Memory allocation
let bid = self.alloc_memory(&decl.dtype())?;
self.global_map.insert(name.clone(), bid)?;
// Initialize allocated memory space
match decl {
Declaration::Variable { dtype, initializer } => {
let value = if let Some(constant) = initializer {
self.constant_to_value(constant.clone())
} else {
Value::default_from_dtype(dtype)
};
self.memory[bid][0] = value;
}
// If functin declaration, skip initialization
Declaration::Function { .. } => (),
}
}
Ok(())
}
fn pass_arguments(&mut self, args: Vec<Value>) -> Result<(), InterpreterError> {
for (i, value) in args.iter().enumerate() {
self.register_write(RegisterId::arg(i), value.clone());
}
Ok(())
}
fn alloc_local_variable(&mut self) -> Result<(), InterpreterError> {
// add alloc register
for (id, allocation) in self.stack_frame.func_def.allocations.iter().enumerate() {
let bid = self.alloc_memory(&allocation)?;
let ptr = Value::pointer(Some(bid), 0);
let rid = RegisterId::local("".to_string(), id);
self.register_write(rid, ptr)
}
Ok(())
}
fn alloc_memory(&mut self, dtype: &Dtype) -> Result<usize, InterpreterError> {
// TODO: memory block will be handled as Vec<Byte>
let memory_block = match dtype {
Dtype::Unit { .. } => vec![],
Dtype::Int { width, .. } => match width {
32 => vec![Value::Undef],
_ => todo!(),
},
Dtype::Float { .. } => todo!(),
Dtype::Pointer { .. } => vec![Value::Undef],
Dtype::Function { .. } => vec![],
};
self.memory.push(memory_block);
Ok(self.memory.len() - 1)
}
fn preprocess_args(
&self,
signature: &FunctionSignature,
args: &[Operand],
) -> Result<Vec<Value>, InterpreterError> {
// Check that the dtype of each args matches the expected
if !(args.len() == signature.params.len()
&& izip!(args, &signature.params).all(|(a, d)| a.dtype().is_compatible(d)))
{
panic!("dtype of args and params must be compatible")
}
args.iter()
.map(|a| self.get_value(a.clone()))
.collect::<Result<Vec<_>, _>>()
}
fn step(&mut self) -> Result<Option<Value>, InterpreterError> {
let block = self
.stack_frame
.func_def
.blocks
.get(&self.stack_frame.pc.bid.clone())
.expect("block matched with `bid` must be exist");
if block.instructions.len() == self.stack_frame.pc.iid {
self.interpret_block_exit(&block.exit)
} else {
let instr = block
.instructions
.get(self.stack_frame.pc.iid)
.expect("instruction matched with `iid` must be exist");
self.interpret_instruction(instr)
}
}
fn run(&mut self) -> Result<Value, InterpreterError> {
loop {
if let Some(value) = self.step()? {
// TODO: Before return, free memory allocated in a function
// restore previous state
let prev_stack_frame = some_or!(self.stack.pop(), {
return Ok(value);
});
self.stack_frame = prev_stack_frame;
// create temporary register to write return value
let register =
RegisterId::temp(self.stack_frame.pc.bid.clone(), self.stack_frame.pc.iid);
self.register_write(register, value);
self.stack_frame.pc.increment();
}
}
}
fn interpret_block_exit(
&mut self,
block_exit: &BlockExit,
) -> Result<Option<Value>, InterpreterError> {
match block_exit {
BlockExit::Jump { bid } => {
self.stack_frame.pc = Pc::new(bid.clone());
Ok(None)
}
BlockExit::ConditionalJump {
condition,
bid_then,
bid_else,
} => {
let value = self.get_value(condition.clone())?;
let value = value.get_bool().expect("`condition` must be `Value::Bool`");
self.stack_frame.pc = Pc::new(if value {
bid_then.clone()
} else {
bid_else.clone()
});
Ok(None)
}
BlockExit::Switch {
value,
default,
cases,
} => {
let value = self.get_value(value.clone())?;
// TODO: consider different integer `width` in the future
let bid_next = cases
.iter()
.find(|(c, _)| value == self.constant_to_value(c.clone()))
.map(|(_, bid)| bid.clone())
.unwrap_or_else(|| default.clone());
self.stack_frame.pc = Pc::new(bid_next);
Ok(None)
}
BlockExit::Return { value } => Ok(Some(self.get_value(value.clone())?)),
BlockExit::Unreachable => Err(InterpreterError::Unreachable),
}
}
fn interpret_instruction(
&mut self,
instruction: &Instruction,
) -> Result<Option<Value>, InterpreterError> {
let result = match instruction {
Instruction::BinOp { op, lhs, rhs, .. } => {
let lhs = self.get_value(lhs.clone())?;
let rhs = self.get_value(rhs.clone())?;
calculator::calculate_binary_operator_expression(&op, lhs, rhs).map_err(|_| {
InterpreterError::Undef {
func_name: self.stack_frame.func_name.clone(),
bid: self.stack_frame.pc.bid.clone(),
iid: self.stack_frame.pc.iid,
}
})?
}
Instruction::UnaryOp { op, operand, .. } => {
let operand = self.get_value(operand.clone())?;
calculator::calculate_unary_operator_expression(&op, operand).map_err(|_| {
InterpreterError::Undef {
func_name: self.stack_frame.func_name.clone(),
bid: self.stack_frame.pc.bid.clone(),
iid: self.stack_frame.pc.iid,
}
})?
}
Instruction::Store { ptr, value, .. } => {
let ptr = self.get_value(ptr.clone())?;
let value = self.get_value(value.clone())?;
self.memory_store(ptr, value)?;
Value::Unit
}
Instruction::Load { ptr, .. } => {
let ptr = self.get_value(ptr.clone())?;
self.memory_load(ptr)?
}
Instruction::Call { callee, args, .. } => {
let ptr = self.get_value(callee.clone())?;
// Get function name from pointer
let (bid, _) = ptr.get_pointer().expect("`ptr` must be `Value::Pointer`");
let bid = bid.expect("pointer for global variable must have bid value");
let callee_name = self
.global_map
.get_var(bid)
.expect("bid must have relation with global variable");
let func = self
.ir
.decls
.get(&callee_name)
.expect("function must be declared before being called");
let (func_signature, func_def) = func
.get_function()
.expect("`func` must be function declaration");
let func_def =
func_def
.as_ref()
.ok_or_else(|| InterpreterError::NoFunctionDefinition {
func_name: callee_name.clone(),
})?;
let args = self.preprocess_args(func_signature, args)?;
let stack_frame = StackFrame::new(func_def.bid_init.clone(), callee_name, func_def);
let prev_stack_frame = mem::replace(&mut self.stack_frame, stack_frame);
self.stack.push(prev_stack_frame);
// Initialize state with function obtained by callee and args
self.pass_arguments(args)?;
self.alloc_local_variable()?;
return Ok(None);
}
_ => todo!("{:?} will be supported in the future", instruction),
};
let register = RegisterId::temp(self.stack_frame.pc.bid.clone(), self.stack_frame.pc.iid);
self.register_write(register, result);
self.stack_frame.pc.increment();
Ok(None)
}
fn get_value(&self, operand: Operand) -> Result<Value, InterpreterError> {
match &operand {
Operand::Constant(value) => Ok(self.constant_to_value(value.clone())),
Operand::Register { rid, .. } => Ok(self.register_read(rid.clone())),
}
}
fn constant_to_value(&self, value: Constant) -> Value {
match value {
Constant::Unit => Value::Unit,
// TODO: consider `width` and `is_signed` in the future
Constant::Int { value, .. } => Value::Int(value as i32),
Constant::Float { value, .. } => Value::Float(value as f32),
Constant::GlobalVariable { name, .. } => {
let bid = self
.global_map
.get_bid(&name)
.expect("The name matching `bid` must exist.");
// Generate appropriate pointer from `bid`
Value::Pointer {
bid: Some(bid),
offset: 0,
}
}
}
}
fn register_write(&mut self, rid: RegisterId, value: Value) {
let _ = self.stack_frame.registers.inner.insert(rid, value);
}
fn register_read(&self, rid: RegisterId) -> Value {
self.stack_frame
.registers
.inner
.get(&rid)
.cloned()
.expect("`rid` must be assigned before it can be used")
}
fn memory_store(&mut self, pointer: Value, value: Value) -> Result<(), InterpreterError> {
let (bid, offset) = pointer
.get_pointer()
.expect("`pointer` must be `Value::Pointer` to access memory");
let bid = bid.expect("write to memory using constant value address is not allowed");
self.memory[bid][offset] = value;
Ok(())
}
fn memory_load(&self, pointer: Value) -> Result<Value, InterpreterError> {
let (bid, offset) = pointer
.get_pointer()
.expect("`pointer` must be `Value::Pointer` to access memory");
let bid = bid.expect("read from memory using constant value address is not allowed");
Ok(self.memory[bid][offset].clone())
}
}
#[inline]
pub fn run_ir(ir: &TranslationUnit, args: Vec<Value>) -> Result<Value, InterpreterError> {
let mut init_state = State::new(ir, args)?;
init_state.run()
}

81
src/utils.rs Normal file
View File

@@ -0,0 +1,81 @@
#[macro_export]
/// Ok or executing the given expression.
macro_rules! ok_or {
($e:expr, $err:expr) => {{
match $e {
Ok(r) => r,
Err(_) => $err,
}
}};
}
#[macro_export]
/// Some or executing the given expression.
macro_rules! some_or {
($e:expr, $err:expr) => {{
match $e {
Some(r) => r,
None => $err,
}
}};
}
#[macro_export]
/// Ok or exiting the process.
macro_rules! ok_or_exit {
($e:expr, $code:expr) => {{
match $e {
Ok(r) => r,
Err(e) => {
eprintln!("{:?}", e);
::std::process::exit($code);
}
}
}};
}
#[macro_export]
/// Ok or exiting the process.
macro_rules! some_or_exit {
($e:expr, $code:expr) => {{
match $e {
Some(r) => r,
None => ::std::process::exit($code),
}
}};
}
pub trait Translate<S> {
type Target;
type Error;
fn translate(&mut self, source: &S) -> Result<Self::Target, Self::Error>;
}
pub trait Optimize<T> {
fn optimize(&mut self, code: &mut T) -> bool;
}
#[derive(Default)]
pub struct Repeat<O> {
inner: O,
}
impl<T, O1: Optimize<T>, O2: Optimize<T>> Optimize<T> for (O1, O2) {
fn optimize(&mut self, code: &mut T) -> bool {
let changed1 = self.0.optimize(code);
let changed2 = self.1.optimize(code);
changed1 || changed2
}
}
impl<T, O: Optimize<T>> Optimize<T> for Repeat<O> {
fn optimize(&mut self, code: &mut T) -> bool {
if !self.inner.optimize(code) {
return false;
}
while self.inner.optimize(code) {}
true
}
}

5
src/write_asm.rs Normal file
View File

@@ -0,0 +1,5 @@
use crate::asm::Asm;
pub fn write_asm(_asm: &Asm, _write: &mut dyn ::std::io::Write) {
unimplemented!();
}

18
src/write_base.rs Normal file
View File

@@ -0,0 +1,18 @@
use std::io::{Result, Write};
#[inline]
pub fn write_indent(indent: usize, write: &mut dyn Write) -> Result<()> {
write!(write, "{}", " ".repeat(indent))
}
pub trait WriteLine {
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()>;
}
pub trait WriteString {
fn write_string(&self) -> String;
}
pub trait WriteOp {
fn write_operation(&self) -> String;
}

51
src/write_c.rs Normal file
View File

@@ -0,0 +1,51 @@
use lang_c::ast::*;
use lang_c::span::Node;
use std::io::{Result, Write};
use std::ops::Deref;
use crate::write_base::*;
impl<T: WriteLine> WriteLine for Node<T> {
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
self.node.write_line(indent, write)
}
}
impl<T: WriteString> WriteString for Node<T> {
fn write_string(&self) -> String {
self.node.write_string()
}
}
impl<T: WriteString> WriteString for Box<T> {
fn write_string(&self) -> String {
self.deref().write_string()
}
}
impl<T: WriteString> WriteString for &T {
fn write_string(&self) -> String {
(*self).write_string()
}
}
impl<T: WriteString> WriteString for Option<T> {
fn write_string(&self) -> String {
if let Some(this) = self {
this.write_string()
} else {
"".to_string()
}
}
}
impl WriteLine for TranslationUnit {
fn write_line(&self, _indent: usize, _write: &mut dyn Write) -> Result<()> {
todo!("homework 1")
}
}
pub fn write_c(unit: &TranslationUnit, write: &mut dyn Write) -> Result<()> {
unit.write_line(0, write)
}

20
src/write_c_test.rs Normal file
View File

@@ -0,0 +1,20 @@
use lang_c::ast::*;
use std::fs::File;
use tempfile::tempdir;
use crate::*;
pub fn write_c_test(unit: &TranslationUnit) {
let temp_dir = tempdir().expect("temp dir creation failed");
let temp_file_path = temp_dir.path().join("temp.c");
let mut temp_file = File::create(&temp_file_path).unwrap();
write_c(&unit, &mut temp_file).unwrap();
let new_unit = Parse::default()
.translate(&temp_file_path.as_path())
.expect("parse failed while parsing file from implemented printer");
drop(temp_file);
assert_ast_equiv(&unit, &new_unit);
temp_dir.close().expect("temp dir deletion failed");
}

224
src/write_ir.rs Normal file
View File

@@ -0,0 +1,224 @@
use crate::ir::*;
use std::io::{Result, Write};
use crate::write_base::*;
use crate::*;
use lang_c::ast;
impl WriteLine for TranslationUnit {
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
write_indent(indent, write)?;
writeln!(write, "<variable list>")?;
writeln!(write)?;
for (name, decl) in &self.decls {
let _ = some_or!(decl.get_variable(), continue);
(name, decl).write_line(indent, write)?;
}
writeln!(write)?;
writeln!(write)?;
write_indent(indent, write)?;
writeln!(write, "<function list>")?;
writeln!(write)?;
for (name, decl) in &self.decls {
let _ = some_or!(decl.get_function(), continue);
(name, decl).write_line(indent, write)?;
}
Ok(())
}
}
impl WriteLine for (&String, &Declaration) {
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
let name = self.0;
let decl = self.1;
match decl {
Declaration::Variable { dtype, .. } => {
writeln!(write, "{} = {}", name, dtype)?;
}
Declaration::Function {
signature,
definition,
} => {
let declaration = format!(
"{} @{}({})",
signature.ret,
name,
signature
.params
.iter()
.map(|d| d.to_string())
.collect::<Vec<_>>()
.join(", "),
);
match definition.as_ref() {
Some(defintion) => {
// print meta data for function
writeln!(
write,
"; function meta data:\n; bid_init: {}\n; allocations: {}",
defintion.bid_init,
defintion
.allocations
.iter()
.enumerate()
.map(|(i, a)| format!("{}:{}", i, a))
.collect::<Vec<_>>()
.join(", ")
)?;
// print function definition
writeln!(write, "define {} {{", declaration)?;
for (id, block) in &defintion.blocks {
writeln!(write, "; <BoxId> {}", id)?;
(id, block).write_line(indent + 1, write)?;
writeln!(write)?;
}
writeln!(write, "}}")?;
writeln!(write)?;
}
None => {
// print declaration line only
writeln!(write, "declare {}", declaration)?;
writeln!(write)?;
}
}
}
}
Ok(())
}
}
impl WriteLine for (&BlockId, &Block) {
fn write_line(&self, indent: usize, write: &mut dyn Write) -> Result<()> {
for (i, instr) in self.1.instructions.iter().enumerate() {
write_indent(indent, write)?;
writeln!(
write,
"{}:{} = {}",
RegisterId::temp(self.0.clone(), i),
instr.dtype(),
instr.write_string()
)?;
}
write_indent(indent, write)?;
writeln!(write, "{}", self.1.exit.write_string())?;
Ok(())
}
}
impl WriteString for Instruction {
fn write_string(&self) -> String {
match self {
Instruction::BinOp { op, lhs, rhs, .. } => format!(
"{} {} {}",
op.write_operation(),
lhs.write_string(),
rhs.write_string()
),
Instruction::UnaryOp { op, operand, .. } => {
format!("{} {}", op.write_operation(), operand.write_string(),)
}
Instruction::Store { ptr, value } => {
format!("store {} {}", value.write_string(), ptr.write_string())
}
Instruction::Load { ptr } => format!("load {}", ptr.write_string()),
Instruction::Call { callee, args, .. } => format!(
"call {}({})",
callee,
args.iter()
.map(WriteString::write_string)
.collect::<Vec<_>>()
.join(", ")
),
Instruction::TypeCast {
value,
target_dtype,
} => format!("typecast {} to {}", value.write_string(), target_dtype),
}
}
}
impl WriteString for Operand {
fn write_string(&self) -> String {
format!("{}:{}", self, self.dtype())
}
}
impl WriteOp for ast::BinaryOperator {
fn write_operation(&self) -> String {
match self {
Self::Multiply => "mul",
Self::Divide => "div",
Self::Modulo => "mod",
Self::Plus => "add",
Self::Minus => "sub",
Self::Equals => "cmp eq",
Self::NotEquals => "cmp ne",
Self::Less => "cmp lt",
Self::LessOrEqual => "cmp le",
Self::Greater => "cmp gt",
Self::GreaterOrEqual => "cmp ge",
_ => todo!(),
}
.to_string()
}
}
impl WriteOp for ast::UnaryOperator {
fn write_operation(&self) -> String {
match self {
Self::Minus => "minus",
_ => todo!(),
}
.to_string()
}
}
impl WriteString for BlockExit {
fn write_string(&self) -> String {
match self {
BlockExit::Jump { bid } => format!("j {}", bid),
BlockExit::ConditionalJump {
condition,
bid_then,
bid_else,
} => format!(
"br {}, {}, {}",
condition.write_string(),
bid_then,
bid_else
),
BlockExit::Switch {
value,
default,
cases,
} => format!(
"switch {}, default: {} [\n{}\n ]",
value.write_string(),
default,
cases
.iter()
.map(|(v, b)| format!(" {}:{}, {}", v, v.dtype(), b))
.collect::<Vec<_>>()
.join("\n")
),
BlockExit::Return { value } => format!("ret {}", value.write_string()),
BlockExit::Unreachable => "<unreachable>\t\t\t\t; error state".to_string(),
}
}
}
pub fn write_ir(ir: &TranslationUnit, write: &mut dyn Write) -> Result<()> {
ir.write_line(0, write)
}

2
tests/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/platform.info
/csmith-*

21
tests/ast_printer_test.rs Normal file
View File

@@ -0,0 +1,21 @@
use std::path::Path;
use kecc::*;
#[test]
fn ast_printer_test() {
let mut parse = Parse::default();
let dir_path = Path::new("examples/");
let dir = dir_path.read_dir().expect("read_dir call failed");
for entry in dir {
let test_file = ok_or!(entry, continue);
let test_unit = parse.translate(&test_file.path().as_path()).expect(
&format!(
"parse failed {:?}",
test_file.path().into_os_string().to_str().unwrap()
)
.to_owned(),
);
write_c_test(&test_unit);
}
}

112
tests/fuzz.py Normal file
View File

@@ -0,0 +1,112 @@
#!/usr/bin/env python3
"""Fuzzes KECC.
For customization, one may restrict/loosen the replacement rule by adding/deleting the pair into
below `REPLACE_DICT`.
"""
import os
import subprocess
import itertools
import argparse
import sys
import re
REPLACE_DICT = {
"#include \"csmith.h\"": "",
"volatile ": "",
"uint16_t": "unsigned int",
"uint32_t": "unsigned int",
"int16_t": "int",
"int32_t": "int",
"uint": "unsigned int",
}
CSMITH_DIR = "csmith-2.3.0"
def install_csmith(tests_dir, bin_file):
global CSMITH_DIR
csmith_root_dir = os.path.join(tests_dir, CSMITH_DIR)
if not os.path.exists(bin_file):
subprocess.Popen(["curl", "https://embed.cs.utah.edu/csmith/" + CSMITH_DIR + ".tar.gz", "-o", CSMITH_DIR + ".tar.gz"], cwd=tests_dir).communicate()
subprocess.Popen(["tar", "xzvf", CSMITH_DIR + ".tar.gz"], cwd=tests_dir).communicate()
subprocess.Popen("cmake .; make -j", shell=True, cwd=csmith_root_dir).communicate()
else:
print("Using the existing csmith...")
def generate(tests_dir, bin_file, runtime, file_name):
"""Feeding options to built Csmith to randomly generate test case.
For generality, I disabled most of the features that are enabled by default.
FYI, please take a look at `-h` flag. By adding or deleting one of `--blah-blah...`
in `options` list below, csmith will be able to generate corresponding test case.
A developer may customize the options to meet one's needs for testing.
"""
global CSMITH_DIR
options = ["--no-builtins", "--no-safe-math", "--no-unions"]
args = [bin_file] + options
dst_path = os.path.join(runtime, file_name)
with open(dst_path, 'w') as f_dst:
subprocess.Popen(args, cwd=tests_dir, stdout=f_dst).wait()
f_dst.flush()
return dst_path
def preprocess(src_path, file_name):
"""Preprocessing test case to fit in kecc parser specification.
It resolves an issue that arbitrarily included header file to hinder parsing.
"""
global REPLACE_DICT, CSMITH_DIR
with open(src_path, 'r') as src:
src = str(src.read())
for _from, _to in REPLACE_DICT.items():
src = src.replace(_from, _to)
with open(os.path.join(os.path.dirname(src_path), file_name), 'w') as dst:
dst.write(str(src))
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Fuzzing KECC.')
parser.add_argument('-n', '--num', type=int, help='The number of tests')
parser.add_argument('-p', '--print', action='store_true', help='Fuzzing C AST printer')
args = parser.parse_args()
if args.print:
cargo_arg = "-p"
else:
raise "Specify fuzzing argument"
tests_dir = os.path.abspath(os.path.dirname(__file__))
csmith_bin = os.path.abspath(os.path.join(tests_dir, CSMITH_DIR, "src/csmith"))
csmith_runtime = os.path.abspath(os.path.join(tests_dir, CSMITH_DIR, "runtime/"))
install_csmith(tests_dir, csmith_bin)
# Run cargo test infinitely
TEST_FAIL_TOKEN = "test result: FAILED."
raw_test_file = "raw_test.c"
test_file = "test.c"
try:
if args.num is None:
print("Fuzzing with infinitely many test cases. Please press [ctrl+C] to break.")
iterator = itertools.count(0)
else:
print("Fuzzing with {} test cases.".format(args.num))
iterator = range(args.num)
for i in iterator:
print("Test case #{}".format(i))
preprocess(generate(tests_dir, csmith_bin, csmith_runtime, raw_test_file), test_file)
args = ["cargo", "run", "--release", "--bin", "fuzz", "--", cargo_arg, os.path.join(csmith_runtime, test_file)]
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, cwd=tests_dir)
test_result = str(proc.stdout.read())
if TEST_FAIL_TOKEN in test_result:
sys.exit("[Error] Test failed. Check `{}` that failed to parse.\nTo investigate, run '{}` in terminal."
.format(os.path.join(csmith_runtime, test_file), " ".join(args)))
except KeyboardInterrupt:
proc.terminate()
print("\n[KeyboardInterrupt] Test ended")

68
tests/ir_test.rs Normal file
View File

@@ -0,0 +1,68 @@
use kecc::run_ir::*;
use kecc::*;
use std::path::Path;
// TODO: cover all examples in the future
#[test]
fn ir_interpreter_test() {
// Test toy example
assert_eq!(run_example("examples/foo.c"), Ok(Value::Int(-1)));
// Test toy example with negate unary operator
assert_eq!(run_example("examples/negate.c"), Ok(Value::Int(1)));
// Test fibonacci function with for-loop
assert_eq!(run_example("examples/fib3.c"), Ok(Value::Int(34)));
// Test fibonacci function with while-loop
assert_eq!(run_example("examples/fib4.c"), Ok(Value::Int(34)));
// Test fibonacci function with do-while-loop
assert_eq!(run_example("examples/fib5.c"), Ok(Value::Int(34)));
// Test fibonacci function with recursive function call
assert_eq!(run_example("examples/fibonacci.c"), Ok(Value::Int(34)));
// Test example with global variable
assert_eq!(run_example("examples/foo3.c"), Ok(Value::Int(30)));
// Test example with comma expressions
assert_eq!(run_example("examples/comma.c"), Ok(Value::Int(7)));
// Test example with complex function call
assert_eq!(run_example("examples/foo4.c"), Ok(Value::Int(6)));
// Test example with pointer
assert_eq!(run_example("examples/pointer.c"), Ok(Value::Int(3)));
// Test example with sizeof
assert_eq!(run_example("examples/sizeof.c"), Ok(Value::Int(4)));
// Test example with alignof
assert_eq!(run_example("examples/alignof.c"), Ok(Value::Int(4)));
// Test example with simple for statement
assert_eq!(run_example("examples/simple_for.c"), Ok(Value::Int(55)));
// Test example with conditional expression
assert_eq!(run_example("examples/cond.c"), Ok(Value::Int(5)));
// Test example with switch statement
assert_eq!(run_example("examples/switch.c"), Ok(Value::Int(2)));
}
fn run_example(example_path: &str) -> Result<Value, InterpreterError> {
let example_path = Path::new(example_path);
let unit = Parse::default()
.translate(&example_path)
.expect("parse failed");
let ir = Irgen::default()
.translate(&unit)
.expect("failed to generate ir");
// TODO: consider command line arguments in the future
// TODO: randomly generate argument values
let args = Vec::new();
run_ir(&ir, args)
}