upload code

This commit is contained in:
Dominic 2022-10-16 20:14:55 +02:00
commit 3f507064ce
Signed by: msrd0
GPG key ID: DCC8C247452E98F9
111 changed files with 9258 additions and 0 deletions

37
.github/workflows/rust.yml vendored Normal file
View file

@ -0,0 +1,37 @@
name: Rust
on:
push:
branches: [main]
pull_request:
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
- name: Get Rust Version
id: rust-version
run: echo "::set-output name=version::$(cargo -V | head -n1 | awk '{print $2}')"
- uses: actions/cache@v3
with:
path: |
~/.cargo/git
~/.cargo/registry
target
key: ${{runner.os}}-rust-${{steps.rust-version.outputs.version}}
- run: cargo test
env:
RUST_BACKTRACE: 1
rustfmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@nightly
with:
components: rustfmt
- run: cargo fmt --all -- --check

17
.gitignore vendored Normal file
View file

@ -0,0 +1,17 @@
a.out
# rust/cargo
/target/
# git
*.orig
# emacs
*~
\#*#
.#*#
# intellij
/.idea/
*.ipr
*.iml

1402
Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

44
Cargo.toml Normal file
View file

@ -0,0 +1,44 @@
# -*- eval: (cargo-minor-mode 1) -*-
[workspace]
members = [".", "stdlib"]
[package]
name = "compiler"
version = "0.0.0"
publish = false
edition = "2021"
authors = ["Dominic Meiser <git@msrd0.de>"]
description = "Compiler for our symbolic heap-manipulating probabilistic programming language"
[[test]]
name = "integration"
path = "integration/main.rs"
harness = false
[build-dependencies]
anyhow = "1"
tar = "0.4"
vergen = { version = "7", features = ["git"], default-features = false }
[dependencies]
anyhow = "1"
ariadne = "0.1"
askama = { version = "0.11", default-features = false }
bat = { version = "0.21", features = ["regex-onig"], default-features = false }
clap = { version = "3", features = ["derive"] }
indexmap = "1"
prettyplease = "0.1"
proc-macro2 = { version = "1", features = ["span-locations"], default-features = false }
quote = { version = "1", default-features = false }
serde = { version = "1", features = ["derive"] }
syn = { version = "1", features = ["clone-impls", "extra-traits", "full", "parsing", "printing"], default-features = false }
tar = "0.4"
tempfile = "3"
[dev-dependencies]
lazy-regex = "2"
libtest = { version = "0.4", package = "libtest-mimic" }
[profile.release]
strip = true

116
README.md Normal file
View file

@ -0,0 +1,116 @@
# Compiler
This is a compiler for a heap-manipulating symbolic probabilistic programming language
that I'm designing as part of my bachelor thesis.
## Types
- `bool` boolean value
- `int` integer value (either 32 or 64 bit, depending on the target architecture)
- `()` empty tuple (also the default return value of functions, like `void` in C)
- `(..)` tuple
- `Rc<_>` reference-counting pointer
- `Option<_>` optional value, can be `None` or `Some(_)`
- `$ident`, `$ident<..>` custom type, must have previously been typedef'ed
## Syntax
```
$program => $typedefs $functions $inputs $main $outputs
$typedefs => $typedef $typedefs
| ε
$functions => $function $functions
| ε
$inputs => $input $inputs
| ε
$main => $stmt $block
$outputs => $output $outputs
| ε
$typedef => type $ident = $type;
| type $ident<$identlist> = $type;
$type => bool
| int
| ()
| ($typelist)
| ($typelist,)
| Rc<$type>
| Option<$type>
| $ident
| $ident<$typelist>
$typelist => $type | $type, $typelist
$function => fn $ident($params) { $block }
| fn $ident($params) -> $type { $block }
| fn $ident<$identlist>($params) { $block }
| fn $ident<$identlist>($params) -> $type { $block }
$params => $paramlist | ε
$paramlist => $ident: $type, $paramlist
| $ident: $type
$input => input!($ident);
$output => output!($identlist);
$stmt => let $ident: $type = $expr;
| $ident = $expr;
| $ifstmt
| { $block } [$float] { $block }
| while $ifcond { $block }
| { $block }
| return;
| return $expr;
| continue;
| break;
$block => $stmt $block
| ε
$ifstmt => if $ifcond { $block }
| if $ifcond { $block } else $ifstmt
| if $ifcond { $block } else { $block }
$ifcond => $expr
| let Some($ident) = $expr
$expr => $expr $op $expr
| !$expr
| -$expr
| *$expr
| ($exprs)
| ($exprlist,)
| $expr.$index
| Rc($expr)
| Some($expr)
| None
| $int
| $bool
| $ident($exprs)
| $ident<$typelist>($exprs)
$exprs => $exprlist | ε
$exprlist => $expr, $exprlist
| $expr
$op => + | - | * | /
| == | != | < | <= | > | >=
| && | ||
$ident matches all valid Rust identifiers
$identlist => $ident | $ident, $identlist
$bool => true | false
$index matches all non-negative integer values
$int matches all integer values
$float matches all floating-point values
```

24
build.rs Normal file
View file

@ -0,0 +1,24 @@
use std::{env, fs::File, path::PathBuf};
use vergen::{vergen, Config, ShaKind};
fn main() -> anyhow::Result<()> {
// provide version information
let mut cfg = Config::default();
let git = cfg.git_mut();
*git.branch_mut() = false;
*git.commit_count_mut() = true;
*git.sha_kind_mut() = ShaKind::Short;
vergen(cfg)?;
// include the stdlib crate with the compiler
let out_dir: PathBuf = env::var("OUT_DIR")?.parse()?;
let archive_path = out_dir.join("stdlib.tar");
println!("cargo:rustc-env=ARCHIVE_PATH={}", archive_path.display());
let mut archive = tar::Builder::new(File::create(&archive_path)?);
let stdlib_path = "stdlib";
println!("cargo:rerun-if-changed={stdlib_path}");
archive.append_dir_all(stdlib_path, stdlib_path)?;
archive.finish()?;
Ok(())
}

3
examples/dice.p3l Normal file
View file

@ -0,0 +1,3 @@
input!(dice);
{}
output!(dice);

262
examples/quicksort.p3l Normal file
View file

@ -0,0 +1,262 @@
type List<T> = Option<Rc<(T, List<T>)>>;
type Partition<T> = (List<T>, T, List<T>);
// Return a random number between 0 (inclusive) and
// max (exclusive).
fn random(max: int) -> int {
while true {
let value: int = 0;
let max_value: int = 0;
while max_value < max {
max_value = max_value * 2 + 1;
value = value * 2;
{ value = value + 1; } [0.5] {}
}
if value < max {
return value;
}
}
}
// Return true if the option is `None`.
fn is_none<O>(opt: O) -> bool {
if let Some(_value) = opt {
return false;
} else {
return true;
}
}
// Unwrap an option.
fn unwrap<T>(opt: Option<T>) -> T {
if let Some(value) = opt {
return value;
}
}
// Create a new, empty list.
// Complexity: O(1)
fn list_new<T>() -> List<T> {
return None;
}
// Push a new value to the front of the list.
// Complexity: O(1)
fn list_push_front<T>(list: List<T>, elem: T) -> List<T> {
return Some(Rc((elem, list)));
}
// Pop the value from the front of the list.
// Complexity: O(1)
fn list_pop_front<T>(list: List<T>) -> (T, List<T>) {
if let Some(front) = list {
return *front;
}
}
// Append a list to the end of this one.
// Complexity: O(n)
fn list_append<T>(list: List<T>, list2: List<T>) -> List<T> {
if is_none::<List<T>>(list) {
return list2;
}
let back: List<T> = list;
while let Some(front) = back {
let tail: List<T> = (*front).1;
if is_none::<List<T>>(tail) {
break;
}
back = tail;
}
if let Some(back) = back {
back.1 = list2;
return list;
}
// else unreachable - back is always a non-empty list
}
// Return the length of a list.
// Complexity: O(n)
fn list_len<T>(list: List<T>) -> int {
let len: int = 0;
while let Some(inner) = list {
len = len + 1;
list = (*inner).1;
}
return len;
}
// Swap the value of the two list elements.
fn list_swap<T>(list: List<T>, list2: List<T>) {
let list: Rc<(T, List<T>)> = unwrap::<Rc<(T, List<T>)>>(list);
let list2: Rc<(T, List<T>)> = unwrap::<Rc<(T, List<T>)>>(list2);
let tmp: T = (*list).0;
list.0 = (*list2).0;
list2.0 = tmp;
}
// Swap the n-th and last elements, and return its value.
fn list_swap_nth_last<T>(list: List<T>, n: int) -> T {
let i: int = 0;
let nth: List<T> = None;
while let Some(front) = list {
if i == n {
nth = list;
}
let tail: List<T> = (*front).1;
if is_none::<List<T>>(tail) {
let _unit: () = list_swap::<T>(nth, list);
return (*front).0;
}
list = tail;
i = i + 1;
}
}
// Return the next list element.
fn list_next<T>(list: List<T>) -> List<T> {
return (*unwrap::<Rc<(T, List<T>)>>(list)).1;
}
// Split the list at index n.
fn list_split<T>(list: List<T>, n: int) -> (List<T>, List<T>) {
if n == 0 {
return (None, list);
}
let list2: List<T> = list;
let i: int = 0;
while i < n - 1 {
list2 = list_next::<T>(list2);
i = i + 1;
}
if let Some(front) = list2 {
list2 = (*front).1;
front.1 = None;
}
return (list, list2);
}
// Increase swap count
fn inc_swaps(swaps_and_comps: Rc<(int, int)>) {
swaps_and_comps.0 = (*swaps_and_comps).0 + 1;
}
// Increase comparison count
fn inc_comps(swaps_and_comps: Rc<(int, int)>) {
swaps_and_comps.1 = (*swaps_and_comps).1 + 1;
}
// Randomised quicksort.
// Expected complexity: O(n log n)
fn quicksort(list: List<int>, swaps_and_comps: Rc<(int, int)>) -> List<int> {
if list_len::<int>(list) < 2 {
return list;
}
let partition: Partition<int> = partition(list, swaps_and_comps);
let list: List<int> = quicksort(partition.0, swaps_and_comps);
let pivot: int = partition.1;
let list2: List<int> = quicksort(partition.2, swaps_and_comps);
list2 = list_push_front::<int>(list2, pivot);
return list_append::<int>(list, list2);
}
// Partition a list into smaller, pivot, and larger elements
fn partition(list: List<int>, swaps_and_comps: Rc<(int, int)>) -> Partition<int> {
// choose some random element as pivot and move it to the last position
let list_len: int = list_len::<int>(list);
let pivot: int = list_swap_nth_last::<int>(list, random(list_len));
let _unit: () = inc_swaps(swaps_and_comps);
// this is our pivot position
let iter: List<int> = list;
let iter_idx: int = -1;
// move all elements smaller than our pivot to its left
let i: int = 0;
let iter2: List<int> = list;
while let Some(front) = iter2 {
// don't swap out the pivot already
if i != list_len - 1 {
let _unit: () = inc_comps(swaps_and_comps);
if (*front).0 <= pivot {
// move pivot index forward
if iter_idx >= 0 {
iter = list_next::<int>(iter);
}
iter_idx = iter_idx + 1;
// swap current element with pivot position
if iter_idx != i {
let _unit: () = list_swap::<int>(iter, iter2);
let _unit: () = inc_swaps(swaps_and_comps);
}
}
}
iter2 = (*front).1;
i = i + 1;
}
// move pivot to the correct pivot position
iter_idx = iter_idx + 1;
let _pivot: int = list_swap_nth_last::<int>(list, iter_idx);
let _unit: () = inc_swaps(swaps_and_comps);
// split the list into two
let lists: (List<int>, List<int>) = list_split::<int>(list, iter_idx);
let pop: (int, List<int>) = list_pop_front::<int>(lists.1);
return (lists.0, pop.0, pop.1);
}
// Assert that the list is sorted.
fn assert_sorted(list: List<int>) -> () {
if let Some(first) = list {
if assert_sorted_impl((*first).1, (*first).0) {
return ();
}
// else there's no path to a return statement - assert failed
} else {
return ();
}
}
fn assert_sorted_impl(list: List<int>, prev: int) -> bool {
while let Some(front) = list {
let curr: int = (*front).0;
if curr < prev {
return false;
}
prev = curr;
list = (*front).1;
}
return true;
}
input!(len);
// Create a random list.
let list: List<int> = list_new::<int>();
let i: int = 0;
while i < len {
list = list_push_front::<int>(list, random(len*2));
i = i + 1;
}
// Run quicksort.
let swaps_and_comps: Rc<(int, int)> = Rc((0, 0));
list = quicksort(list, swaps_and_comps);
let swaps: int = (*swaps_and_comps).0;
let comps: int = (*swaps_and_comps).1;
// Ensure the list is now sorted.
let _unit: () = assert_sorted(list);
output!(swaps);
output!(comps);

382
examples/skiplist-1-4.p3l Normal file
View file

@ -0,0 +1,382 @@
// 0: own level (lowest level is 0)
// 1: entry below this one (exists iff level is greater than 0)
// 2: entry to the right of this one
type SkipList<V> = (int, SkipListLink<V>, Option<SkipListNode<V>>);
type SkipListLink<V> = Option<Rc<SkipList<V>>>;
// 0: own level (lowest level is 0)
// 1: entry below this one (exists iff level is greater than 0)
// 2: entry to the right of this one
// 3: key of the column this entry belongs to
// 4: value of the column (only on the lowest level)
type SkipListNode<V> = Rc<(int, Option<SkipListNode<V>>, Option<SkipListNode<V>>, int, Option<V>)>;
type Stack<T> = Rc<(Option<(T, Stack<T>)>,)>;
// Unwrap an option.
fn unwrap<T>(opt: Option<T>) -> T {
if let Some(value) = opt {
return value;
}
}
// Create a new, empty stack
fn stack_new<T>() -> Stack<T> {
return Rc((None,));
}
// Push a new value to the stack.
fn stack_push<T>(stack: Stack<T>, value: T) {
stack.0 = Some((value, Rc(*stack)));
}
// Pop a value from the stack.
fn stack_pop<T>(stack: Stack<T>) -> Option<T> {
if let Some(top) = (*stack).0 {
stack.0 = (*top.1).0;
return Some(top.0);
}
return None;
}
// Return a new, empty skiplist.
fn skiplist_new<V>() -> SkipList<V> {
return (0, None, None);
}
// Return the current maximum level of the skiplist.
fn skiplist_level<V>(list: SkipList<V>) -> int {
return list.0;
}
// NOT PUBLIC API
// Insert a new value into the skiplist at the first position.
fn skiplist_insert_first<V>(
list: SkipList<V>,
new_lvl: int,
key: int,
value: V
) -> SkipList<V> {
// decompose list into levels
let levels: Stack<Option<SkipListNode<V>>> =
stack_new::<Option<SkipListNode<V>>>();
let _unit: () = stack_push::<Option<SkipListNode<V>>>(levels, list.2);
while let Some(next) = list.1 {
list = *next;
let _unit: () = stack_push::<Option<SkipListNode<V>>>(levels, list.2);
}
// create new list with just the lowest level
let lvl: int = 0;
let top: Option<Option<SkipListNode<V>>> =
stack_pop::<Option<SkipListNode<V>>>(levels);
let new_node: SkipListNode<V> = Rc((
0,
None,
unwrap::<Option<SkipListNode<V>>>(top),
key,
Some(value)
));
list = (0, None, Some(new_node));
// rebuild list up to its original level
while let Some(top) = stack_pop::<Option<SkipListNode<V>>>(levels) {
lvl = lvl + 1;
if new_lvl >= lvl {
new_node = Rc((lvl, Some(new_node), top, key, None));
} else {
let top: SkipListNode<V> = unwrap::<SkipListNode<V>>(top);
new_node = Rc((lvl, Some(new_node), (*top).2, (*top).3, None));
}
list = (lvl, Some(Rc(list)), Some(new_node));
}
// add new levels if necessary
while new_lvl > lvl {
lvl = lvl + 1;
new_node = Rc((lvl, Some(new_node), None, key, None));
list = (lvl, Some(Rc(list)), Some(new_node));
}
return list;
}
// NOT PUBLIC API
// Insert a new value into the skiplist that needs to be inserted after
// the current node. Will be called when entering a new level while
// searching for the insert position. Returns the level of the inserted
// node.
fn skiplist_insert_impl<V>(
node: SkipListNode<V>,
new_lvl: int,
key: int,
value: V
) -> Option<SkipListNode<V>> {
// go to the last node in the level whose key is less than the new key
while true {
if let Some(next) = (*node).2 {
if (*next).3 < key {
node = next;
continue;
}
}
break;
}
// when not at the lowest level, descend
if let Some(below) = (*node).1 {
let new_node: Option<SkipListNode<V>> =
skiplist_insert_impl::<V>(below, new_lvl, key, value);
if let Some(new_node) = new_node {
let node_lvl: int = (*node).0;
if new_lvl >= node_lvl {
let new_node: SkipListNode<V> =
Rc((node_lvl, Some(new_node), (*node).2, key, None));
node.2 = Some(new_node);
return Some(new_node);
}
}
return new_node;
}
// if we found a node with the exact key, just update the value
if (*node).3 == key {
node.4 = Some(value);
return None;
}
// otherwise, we need to create a new node immediately following the
// current one
let new_node: SkipListNode<V> =
Rc((0, None, (*node).2, key, Some(value)));
node.2 = Some(new_node);
return Some(new_node);
}
// NOT PUBLIC API
// Insert a new value with a set level.
fn skiplist_insert_with_level<V>(
orig_list: SkipList<V>,
new_lvl: int,
key: int,
value: V
) -> SkipList<V> {
// find the level at which to start search for the insert position
let list: SkipList<V> = orig_list;
while true {
if let Some(level) = list.2 {
if (*level).3 <= key {
break;
}
}
if let Some(below) = list.1 {
list = *below;
} else {
return skiplist_insert_first::<V>(list, new_lvl, key, value);
}
}
if let Some(node) = list.2 {
let new_node: Option<SkipListNode<V>> =
skiplist_insert_impl::<V>(node, new_lvl, key, value);
if let Some(new_node) = new_node {
let node_lvl: int = (*node).0;
// update levels above node_lvl if necessary
list = orig_list;
if new_lvl >= list.0 {
let new_node: SkipListNode<V> =
Rc((list.0, None, list.2, key, None));
list.2 = Some(new_node);
}
while let Some(below) = list.1 {
list = *below;
if list.0 <= node_lvl || list.0 < new_lvl {
break;
}
let new_node: SkipListNode<V> =
Rc((list.0, None, list.2, key, None));
list.2 = Some(new_node);
}
// add levels above the list level if necessary
list = orig_list;
while new_lvl > list.0 {
new_node = Rc((list.0, Some(new_node), None, key, None));
list = (list.0 + 1, Some(Rc(list)), Some(new_node));
}
return list;
} else {
return orig_list;
}
} else {
// we are inserting into an empty list
return skiplist_insert_first::<V>(list, new_lvl, key, value);
}
}
// NOT PUBLIC API
// Return a random level.
fn skiplist_random_level() -> int {
let lvl: int = 0;
while true {
{ lvl = lvl + 1; } [0.25] { return lvl; }
}
}
// Take the skiplist, insert a new value, and return the skiplist.
fn skiplist_insert<V>(
list: SkipList<V>,
key: int,
value: V
) -> SkipList<V> {
let lvl: int = skiplist_random_level();
return skiplist_insert_with_level::<V>(list, lvl, key, value);
}
// NOT PUBLIC API
// Find a value in the skiplist, starting the search at a specific node.
fn skiplist_get_impl<V>(
node: SkipListNode<V>,
key: int
) -> (Option<V>, int) {
let steps: int = 1;
if (*node).3 < key {
while true {
if let Some(next) = (*node).2 {
let next_key: int = (*next).3;
steps = steps + 1;
if next_key <= key {
node = next;
if next_key == key {
break;
} else {
continue;
}
}
}
if let Some(below) = (*node).1 {
node = below;
continue;
}
break;
}
}
if (*node).3 != key {
return (None, steps);
}
// the value is only stored at the lowest level
while let Some(below) = (*node).1 {
node = below;
}
return (Some(unwrap::<V>((*node).4)), steps);
}
// Find a value in the skiplist.
fn skiplist_get<V>(list: SkipList<V>, key: int) -> (Option<V>, int) {
let steps: int = 0;
while true {
if let Some(node) = list.2 {
steps = steps + 1;
if (*node).3 <= key {
let tmp: (Option<V>, int) = skiplist_get_impl::<V>(node, key);
return (tmp.0, steps + tmp.1);
}
}
if let Some(below) = list.1 {
list = *below;
continue;
}
return (None, steps);
}
}
// Assert that the skiplist contains a certain key.
fn assert_contains<V>(list: SkipList<V>, key: int) -> () {
if let Some(_value) = skiplist_get::<V>(list, key).0 {
return ();
}
}
// Return the length of the skiplist.
fn skiplist_len<V>(list: SkipList<V>) -> int {
// descend to the lowest level
while let Some(next) = list.1 {
list = *next;
}
// count the lowest level
let node: Option<SkipListNode<V>> = list.2;
let len: int = 0;
while let Some(next) = node {
len = len + 1;
node = (*next).2;
}
return len;
}
// Assert that the skiplist has the correct length.
fn assert_len<V>(list: SkipList<V>, len: int) -> () {
if skiplist_len::<V>(list) == len {
return ();
}
}
// Assert that a value is in a certain range.
fn assert_in_range(value: int, min_incl: int, max_excl: int) -> () {
if value >= min_incl && value < max_excl {
return ();
}
}
// Return a random number between 0 (inclusive) and
// max (exclusive).
fn random(max: int) -> int {
while true {
let value: int = 0;
let max_value: int = 0;
while max_value < max {
max_value = max_value * 2 + 1;
value = value * 2;
{ value = value + 1; } [0.25] {}
}
if value < max {
return value;
}
}
}
input!(len);
// create a new list with entries 0..len
let list: SkipList<()> = skiplist_new::<()>();
let i: int = 0;
while i < len {
list = skiplist_insert::<()>(list, i, ());
let _unit: () = assert_contains::<()>(list, i);
i = i + 1;
}
let _unit: () = assert_len::<()>(list, len);
// lookup a random value in the list
let key: int = random(len);
let _unit: () = assert_in_range(key, 0, len);
let lookup: (Option<()>, int) = skiplist_get::<()>(list, key);
let _unit: () = unwrap::<()>(lookup.0);
let steps: int = lookup.1;
let lvl: int = skiplist_level::<()>(list);
output!(len);
output!(lvl, steps);

382
examples/skiplist-3-8.p3l Normal file
View file

@ -0,0 +1,382 @@
// 0: own level (lowest level is 0)
// 1: entry below this one (exists iff level is greater than 0)
// 2: entry to the right of this one
type SkipList<V> = (int, SkipListLink<V>, Option<SkipListNode<V>>);
type SkipListLink<V> = Option<Rc<SkipList<V>>>;
// 0: own level (lowest level is 0)
// 1: entry below this one (exists iff level is greater than 0)
// 2: entry to the right of this one
// 3: key of the column this entry belongs to
// 4: value of the column (only on the lowest level)
type SkipListNode<V> = Rc<(int, Option<SkipListNode<V>>, Option<SkipListNode<V>>, int, Option<V>)>;
type Stack<T> = Rc<(Option<(T, Stack<T>)>,)>;
// Unwrap an option.
fn unwrap<T>(opt: Option<T>) -> T {
if let Some(value) = opt {
return value;
}
}
// Create a new, empty stack
fn stack_new<T>() -> Stack<T> {
return Rc((None,));
}
// Push a new value to the stack.
fn stack_push<T>(stack: Stack<T>, value: T) {
stack.0 = Some((value, Rc(*stack)));
}
// Pop a value from the stack.
fn stack_pop<T>(stack: Stack<T>) -> Option<T> {
if let Some(top) = (*stack).0 {
stack.0 = (*top.1).0;
return Some(top.0);
}
return None;
}
// Return a new, empty skiplist.
fn skiplist_new<V>() -> SkipList<V> {
return (0, None, None);
}
// Return the current maximum level of the skiplist.
fn skiplist_level<V>(list: SkipList<V>) -> int {
return list.0;
}
// NOT PUBLIC API
// Insert a new value into the skiplist at the first position.
fn skiplist_insert_first<V>(
list: SkipList<V>,
new_lvl: int,
key: int,
value: V
) -> SkipList<V> {
// decompose list into levels
let levels: Stack<Option<SkipListNode<V>>> =
stack_new::<Option<SkipListNode<V>>>();
let _unit: () = stack_push::<Option<SkipListNode<V>>>(levels, list.2);
while let Some(next) = list.1 {
list = *next;
let _unit: () = stack_push::<Option<SkipListNode<V>>>(levels, list.2);
}
// create new list with just the lowest level
let lvl: int = 0;
let top: Option<Option<SkipListNode<V>>> =
stack_pop::<Option<SkipListNode<V>>>(levels);
let new_node: SkipListNode<V> = Rc((
0,
None,
unwrap::<Option<SkipListNode<V>>>(top),
key,
Some(value)
));
list = (0, None, Some(new_node));
// rebuild list up to its original level
while let Some(top) = stack_pop::<Option<SkipListNode<V>>>(levels) {
lvl = lvl + 1;
if new_lvl >= lvl {
new_node = Rc((lvl, Some(new_node), top, key, None));
} else {
let top: SkipListNode<V> = unwrap::<SkipListNode<V>>(top);
new_node = Rc((lvl, Some(new_node), (*top).2, (*top).3, None));
}
list = (lvl, Some(Rc(list)), Some(new_node));
}
// add new levels if necessary
while new_lvl > lvl {
lvl = lvl + 1;
new_node = Rc((lvl, Some(new_node), None, key, None));
list = (lvl, Some(Rc(list)), Some(new_node));
}
return list;
}
// NOT PUBLIC API
// Insert a new value into the skiplist that needs to be inserted after
// the current node. Will be called when entering a new level while
// searching for the insert position. Returns the level of the inserted
// node.
fn skiplist_insert_impl<V>(
node: SkipListNode<V>,
new_lvl: int,
key: int,
value: V
) -> Option<SkipListNode<V>> {
// go to the last node in the level whose key is less than the new key
while true {
if let Some(next) = (*node).2 {
if (*next).3 < key {
node = next;
continue;
}
}
break;
}
// when not at the lowest level, descend
if let Some(below) = (*node).1 {
let new_node: Option<SkipListNode<V>> =
skiplist_insert_impl::<V>(below, new_lvl, key, value);
if let Some(new_node) = new_node {
let node_lvl: int = (*node).0;
if new_lvl >= node_lvl {
let new_node: SkipListNode<V> =
Rc((node_lvl, Some(new_node), (*node).2, key, None));
node.2 = Some(new_node);
return Some(new_node);
}
}
return new_node;
}
// if we found a node with the exact key, just update the value
if (*node).3 == key {
node.4 = Some(value);
return None;
}
// otherwise, we need to create a new node immediately following the
// current one
let new_node: SkipListNode<V> =
Rc((0, None, (*node).2, key, Some(value)));
node.2 = Some(new_node);
return Some(new_node);
}
// NOT PUBLIC API
// Insert a new value with a set level.
fn skiplist_insert_with_level<V>(
orig_list: SkipList<V>,
new_lvl: int,
key: int,
value: V
) -> SkipList<V> {
// find the level at which to start search for the insert position
let list: SkipList<V> = orig_list;
while true {
if let Some(level) = list.2 {
if (*level).3 <= key {
break;
}
}
if let Some(below) = list.1 {
list = *below;
} else {
return skiplist_insert_first::<V>(list, new_lvl, key, value);
}
}
if let Some(node) = list.2 {
let new_node: Option<SkipListNode<V>> =
skiplist_insert_impl::<V>(node, new_lvl, key, value);
if let Some(new_node) = new_node {
let node_lvl: int = (*node).0;
// update levels above node_lvl if necessary
list = orig_list;
if new_lvl >= list.0 {
let new_node: SkipListNode<V> =
Rc((list.0, None, list.2, key, None));
list.2 = Some(new_node);
}
while let Some(below) = list.1 {
list = *below;
if list.0 <= node_lvl || list.0 < new_lvl {
break;
}
let new_node: SkipListNode<V> =
Rc((list.0, None, list.2, key, None));
list.2 = Some(new_node);
}
// add levels above the list level if necessary
list = orig_list;
while new_lvl > list.0 {
new_node = Rc((list.0, Some(new_node), None, key, None));
list = (list.0 + 1, Some(Rc(list)), Some(new_node));
}
return list;
} else {
return orig_list;
}
} else {
// we are inserting into an empty list
return skiplist_insert_first::<V>(list, new_lvl, key, value);
}
}
// NOT PUBLIC API
// Return a random level.
fn skiplist_random_level() -> int {
let lvl: int = 0;
while true {
{ lvl = lvl + 1; } [0.375] { return lvl; }
}
}
// Take the skiplist, insert a new value, and return the skiplist.
fn skiplist_insert<V>(
list: SkipList<V>,
key: int,
value: V
) -> SkipList<V> {
let lvl: int = skiplist_random_level();
return skiplist_insert_with_level::<V>(list, lvl, key, value);
}
// NOT PUBLIC API
// Find a value in the skiplist, starting the search at a specific node.
fn skiplist_get_impl<V>(
node: SkipListNode<V>,
key: int
) -> (Option<V>, int) {
let steps: int = 1;
if (*node).3 < key {
while true {
if let Some(next) = (*node).2 {
let next_key: int = (*next).3;
steps = steps + 1;
if next_key <= key {
node = next;
if next_key == key {
break;
} else {
continue;
}
}
}
if let Some(below) = (*node).1 {
node = below;
continue;
}
break;
}
}
if (*node).3 != key {
return (None, steps);
}
// the value is only stored at the lowest level
while let Some(below) = (*node).1 {
node = below;
}
return (Some(unwrap::<V>((*node).4)), steps);
}
// Find a value in the skiplist.
fn skiplist_get<V>(list: SkipList<V>, key: int) -> (Option<V>, int) {
let steps: int = 0;
while true {
if let Some(node) = list.2 {
steps = steps + 1;
if (*node).3 <= key {
let tmp: (Option<V>, int) = skiplist_get_impl::<V>(node, key);
return (tmp.0, steps + tmp.1);
}
}
if let Some(below) = list.1 {
list = *below;
continue;
}
return (None, steps);
}
}
// Assert that the skiplist contains a certain key.
fn assert_contains<V>(list: SkipList<V>, key: int) -> () {
if let Some(_value) = skiplist_get::<V>(list, key).0 {
return ();
}
}
// Return the length of the skiplist.
fn skiplist_len<V>(list: SkipList<V>) -> int {
// descend to the lowest level
while let Some(next) = list.1 {
list = *next;
}
// count the lowest level
let node: Option<SkipListNode<V>> = list.2;
let len: int = 0;
while let Some(next) = node {
len = len + 1;
node = (*next).2;
}
return len;
}
// Assert that the skiplist has the correct length.
fn assert_len<V>(list: SkipList<V>, len: int) -> () {
if skiplist_len::<V>(list) == len {
return ();
}
}
// Assert that a value is in a certain range.
fn assert_in_range(value: int, min_incl: int, max_excl: int) -> () {
if value >= min_incl && value < max_excl {
return ();
}
}
// Return a random number between 0 (inclusive) and
// max (exclusive).
fn random(max: int) -> int {
while true {
let value: int = 0;
let max_value: int = 0;
while max_value < max {
max_value = max_value * 2 + 1;
value = value * 2;
{ value = value + 1; } [0.375] {}
}
if value < max {
return value;
}
}
}
input!(len);
// create a new list with entries 0..len
let list: SkipList<()> = skiplist_new::<()>();
let i: int = 0;
while i < len {
list = skiplist_insert::<()>(list, i, ());
let _unit: () = assert_contains::<()>(list, i);
i = i + 1;
}
let _unit: () = assert_len::<()>(list, len);
// lookup a random value in the list
let key: int = random(len);
let _unit: () = assert_in_range(key, 0, len);
let lookup: (Option<()>, int) = skiplist_get::<()>(list, key);
let _unit: () = unwrap::<()>(lookup.0);
let steps: int = lookup.1;
let lvl: int = skiplist_level::<()>(list);
output!(len);
output!(lvl, steps);

382
examples/skiplist.p3l Normal file
View file

@ -0,0 +1,382 @@
// 0: own level (lowest level is 0)
// 1: entry below this one (exists iff level is greater than 0)
// 2: entry to the right of this one
type SkipList<V> = (int, SkipListLink<V>, Option<SkipListNode<V>>);
type SkipListLink<V> = Option<Rc<SkipList<V>>>;
// 0: own level (lowest level is 0)
// 1: entry below this one (exists iff level is greater than 0)
// 2: entry to the right of this one
// 3: key of the column this entry belongs to
// 4: value of the column (only on the lowest level)
type SkipListNode<V> = Rc<(int, Option<SkipListNode<V>>, Option<SkipListNode<V>>, int, Option<V>)>;
type Stack<T> = Rc<(Option<(T, Stack<T>)>,)>;
// Unwrap an option.
fn unwrap<T>(opt: Option<T>) -> T {
if let Some(value) = opt {
return value;
}
}
// Create a new, empty stack
fn stack_new<T>() -> Stack<T> {
return Rc((None,));
}
// Push a new value to the stack.
fn stack_push<T>(stack: Stack<T>, value: T) {
stack.0 = Some((value, Rc(*stack)));
}
// Pop a value from the stack.
fn stack_pop<T>(stack: Stack<T>) -> Option<T> {
if let Some(top) = (*stack).0 {
stack.0 = (*top.1).0;
return Some(top.0);
}
return None;
}
// Return a new, empty skiplist.
fn skiplist_new<V>() -> SkipList<V> {
return (0, None, None);
}
// Return the current maximum level of the skiplist.
fn skiplist_level<V>(list: SkipList<V>) -> int {
return list.0;
}
// NOT PUBLIC API
// Insert a new value into the skiplist at the first position.
fn skiplist_insert_first<V>(
list: SkipList<V>,
new_lvl: int,
key: int,
value: V
) -> SkipList<V> {
// decompose list into levels
let levels: Stack<Option<SkipListNode<V>>> =
stack_new::<Option<SkipListNode<V>>>();
let _unit: () = stack_push::<Option<SkipListNode<V>>>(levels, list.2);
while let Some(next) = list.1 {
list = *next;
let _unit: () = stack_push::<Option<SkipListNode<V>>>(levels, list.2);
}
// create new list with just the lowest level
let lvl: int = 0;
let top: Option<Option<SkipListNode<V>>> =
stack_pop::<Option<SkipListNode<V>>>(levels);
let new_node: SkipListNode<V> = Rc((
0,
None,
unwrap::<Option<SkipListNode<V>>>(top),
key,
Some(value)
));
list = (0, None, Some(new_node));
// rebuild list up to its original level
while let Some(top) = stack_pop::<Option<SkipListNode<V>>>(levels) {
lvl = lvl + 1;
if new_lvl >= lvl {
new_node = Rc((lvl, Some(new_node), top, key, None));
} else {
let top: SkipListNode<V> = unwrap::<SkipListNode<V>>(top);
new_node = Rc((lvl, Some(new_node), (*top).2, (*top).3, None));
}
list = (lvl, Some(Rc(list)), Some(new_node));
}
// add new levels if necessary
while new_lvl > lvl {
lvl = lvl + 1;
new_node = Rc((lvl, Some(new_node), None, key, None));
list = (lvl, Some(Rc(list)), Some(new_node));
}
return list;
}
// NOT PUBLIC API
// Insert a new value into the skiplist that needs to be inserted after
// the current node. Will be called when entering a new level while
// searching for the insert position. Returns the level of the inserted
// node.
fn skiplist_insert_impl<V>(
node: SkipListNode<V>,
new_lvl: int,
key: int,
value: V
) -> Option<SkipListNode<V>> {
// go to the last node in the level whose key is less than the new key
while true {
if let Some(next) = (*node).2 {
if (*next).3 < key {
node = next;
continue;
}
}
break;
}
// when not at the lowest level, descend
if let Some(below) = (*node).1 {
let new_node: Option<SkipListNode<V>> =
skiplist_insert_impl::<V>(below, new_lvl, key, value);
if let Some(new_node) = new_node {
let node_lvl: int = (*node).0;
if new_lvl >= node_lvl {
let new_node: SkipListNode<V> =
Rc((node_lvl, Some(new_node), (*node).2, key, None));
node.2 = Some(new_node);
return Some(new_node);
}
}
return new_node;
}
// if we found a node with the exact key, just update the value
if (*node).3 == key {
node.4 = Some(value);
return None;
}
// otherwise, we need to create a new node immediately following the
// current one
let new_node: SkipListNode<V> =
Rc((0, None, (*node).2, key, Some(value)));
node.2 = Some(new_node);
return Some(new_node);
}
// NOT PUBLIC API
// Insert a new value with a set level.
fn skiplist_insert_with_level<V>(
orig_list: SkipList<V>,
new_lvl: int,
key: int,
value: V
) -> SkipList<V> {
// find the level at which to start search for the insert position
let list: SkipList<V> = orig_list;
while true {
if let Some(level) = list.2 {
if (*level).3 <= key {
break;
}
}
if let Some(below) = list.1 {
list = *below;
} else {
return skiplist_insert_first::<V>(list, new_lvl, key, value);
}
}
if let Some(node) = list.2 {
let new_node: Option<SkipListNode<V>> =
skiplist_insert_impl::<V>(node, new_lvl, key, value);
if let Some(new_node) = new_node {
let node_lvl: int = (*node).0;
// update levels above node_lvl if necessary
list = orig_list;
if new_lvl >= list.0 {
let new_node: SkipListNode<V> =
Rc((list.0, None, list.2, key, None));
list.2 = Some(new_node);
}
while let Some(below) = list.1 {
list = *below;
if list.0 <= node_lvl || list.0 < new_lvl {
break;
}
let new_node: SkipListNode<V> =
Rc((list.0, None, list.2, key, None));
list.2 = Some(new_node);
}
// add levels above the list level if necessary
list = orig_list;
while new_lvl > list.0 {
new_node = Rc((list.0, Some(new_node), None, key, None));
list = (list.0 + 1, Some(Rc(list)), Some(new_node));
}
return list;
} else {
return orig_list;
}
} else {
// we are inserting into an empty list
return skiplist_insert_first::<V>(list, new_lvl, key, value);
}
}
// NOT PUBLIC API
// Return a random level.
fn skiplist_random_level() -> int {
let lvl: int = 0;
while true {
{ lvl = lvl + 1; } [0.5] { return lvl; }
}
}
// Take the skiplist, insert a new value, and return the skiplist.
fn skiplist_insert<V>(
list: SkipList<V>,
key: int,
value: V
) -> SkipList<V> {
let lvl: int = skiplist_random_level();
return skiplist_insert_with_level::<V>(list, lvl, key, value);
}
// NOT PUBLIC API
// Find a value in the skiplist, starting the search at a specific node.
fn skiplist_get_impl<V>(
node: SkipListNode<V>,
key: int
) -> (Option<V>, int) {
let steps: int = 1;
if (*node).3 < key {
while true {
if let Some(next) = (*node).2 {
let next_key: int = (*next).3;
steps = steps + 1;
if next_key <= key {
node = next;
if next_key == key {
break;
} else {
continue;
}
}
}
if let Some(below) = (*node).1 {
node = below;
continue;
}
break;
}
}
if (*node).3 != key {
return (None, steps);
}
// the value is only stored at the lowest level
while let Some(below) = (*node).1 {
node = below;
}
return (Some(unwrap::<V>((*node).4)), steps);
}
// Find a value in the skiplist.
fn skiplist_get<V>(list: SkipList<V>, key: int) -> (Option<V>, int) {
let steps: int = 0;
while true {
if let Some(node) = list.2 {
steps = steps + 1;
if (*node).3 <= key {
let tmp: (Option<V>, int) = skiplist_get_impl::<V>(node, key);
return (tmp.0, steps + tmp.1);
}
}
if let Some(below) = list.1 {
list = *below;
continue;
}
return (None, steps);
}
}
// Assert that the skiplist contains a certain key.
fn assert_contains<V>(list: SkipList<V>, key: int) -> () {
if let Some(_value) = skiplist_get::<V>(list, key).0 {
return ();
}
}
// Return the length of the skiplist.
fn skiplist_len<V>(list: SkipList<V>) -> int {
// descend to the lowest level
while let Some(next) = list.1 {
list = *next;
}
// count the lowest level
let node: Option<SkipListNode<V>> = list.2;
let len: int = 0;
while let Some(next) = node {
len = len + 1;
node = (*next).2;
}
return len;
}
// Assert that the skiplist has the correct length.
fn assert_len<V>(list: SkipList<V>, len: int) -> () {
if skiplist_len::<V>(list) == len {
return ();
}
}
// Assert that a value is in a certain range.
fn assert_in_range(value: int, min_incl: int, max_excl: int) -> () {
if value >= min_incl && value < max_excl {
return ();
}
}
// Return a random number between 0 (inclusive) and
// max (exclusive).
fn random(max: int) -> int {
while true {
let value: int = 0;
let max_value: int = 0;
while max_value < max {
max_value = max_value * 2 + 1;
value = value * 2;
{ value = value + 1; } [0.5] {}
}
if value < max {
return value;
}
}
}
input!(len);
// create a new list with entries 0..len
let list: SkipList<()> = skiplist_new::<()>();
let i: int = 0;
while i < len {
list = skiplist_insert::<()>(list, i, ());
let _unit: () = assert_contains::<()>(list, i);
i = i + 1;
}
let _unit: () = assert_len::<()>(list, len);
// lookup a random value in the list
let key: int = random(len);
let _unit: () = assert_in_range(key, 0, len);
let lookup: (Option<()>, int) = skiplist_get::<()>(list, key);
let _unit: () = unwrap::<()>(lookup.0);
let steps: int = lookup.1;
let lvl: int = skiplist_level::<()>(list);
output!(len);
output!(lvl, steps);

View file

@ -0,0 +1,7 @@
Error: No such field
╭─[assignment-bool-field.txt:2:5]
2 │ foo.bar = true;
· ─┬─
· ╰─── bool is a primitive type and does not have any fields
───╯

View file

@ -0,0 +1,2 @@
let foo: bool = true;
foo.bar = true;

View file

@ -0,0 +1,7 @@
Error: No such field
╭─[assignment-bool-index.txt:2:5]
2 │ foo.0 = true;
· ┬
· ╰── bool is a primitive type and does not have any fields
───╯

View file

@ -0,0 +1,2 @@
let foo: bool = true;
foo.0 = true;

View file

@ -0,0 +1,7 @@
Error: No such field
╭─[assignment-int-field.txt:2:5]
2 │ foo.bar = 1;
· ─┬─
· ╰─── int is a primitive type and does not have any fields
───╯

View file

@ -0,0 +1,2 @@
let foo: int = 1;
foo.bar = 1;

View file

@ -0,0 +1,7 @@
Error: No such field
╭─[assignment-int-index.txt:2:5]
2 │ foo.0 = 1;
· ┬
· ╰── int is a primitive type and does not have any fields
───╯

View file

@ -0,0 +1,2 @@
let foo: int = 1;
foo.0 = 1;

View file

@ -0,0 +1,9 @@
Error: No such field
╭─[assignment-tuple-field.txt:2:5]
2 │ foo.first = 0;
· ──┬──
· ╰──── tuples don't have named fields
·
· Help: To access the first element in a tuple, use `.0`
───╯

View file

@ -0,0 +1,2 @@
let foo: (int,) = (1,);
foo.first = 0;

View file

@ -0,0 +1,9 @@
Error: No such field
╭─[assignment-tuple-out-of-bounds.txt:2:5]
2 │ foo.2 = 0;
· ┬
· ╰── this tuple does not have enough elements
·
· Note: The first element in a tuple has index 0
───╯

View file

@ -0,0 +1,2 @@
let foo: (int,) = (1,);
foo.2 = 0;

View file

@ -0,0 +1,11 @@
Error: Too few arguments
╭─[expected-2-found-1-generic.txt:4:17]
2 │ fn foo<V>(foo: V, bar: V) {}
· ─┬─
· ╰─── This function takes 2 arguments,
·
4 │ let _unit: () = foo::<int>(1);
· ─┬─
· ╰─── but only 1 arguments were supplied
───╯

View file

@ -0,0 +1,4 @@
// This function does nothing.
fn foo<V>(foo: V, bar: V) {}
let _unit: () = foo::<int>(1);

View file

@ -0,0 +1,11 @@
Error: Too few arguments
╭─[expected-2-found-1.txt:4:17]
2 │ fn foo(foo: int, bar: int) {}
· ─┬─
· ╰─── This function takes 2 arguments,
·
4 │ let _unit: () = foo(1);
· ─┬─
· ╰─── but only 1 arguments were supplied
───╯

View file

@ -0,0 +1,4 @@
// This function does nothing.
fn foo(foo: int, bar: int) {}
let _unit: () = foo(1);

View file

@ -0,0 +1,10 @@
Error: Type Mismatch
╭─[assignment-bool-int.txt:2:7]
1 │ let foo: bool = true;
· ──┬─
· ╰─── Expected type `bool`,
2 │ foo = 0;
· ┬
· ╰── but expression is of type `int`
───╯

View file

@ -0,0 +1,2 @@
let foo: bool = true;
foo = 0;

View file

@ -0,0 +1,10 @@
Error: Type Mismatch
╭─[assignment-rc-tuple-bool-int.txt:2:9]
1 │ let foo: Rc<(bool,)> = Rc((true,));
· ──┬─
· ╰─── Expected type `bool`,
2 │ foo.0 = 0;
· ┬
· ╰── but expression is of type `int`
───╯

View file

@ -0,0 +1,2 @@
let foo: Rc<(bool,)> = Rc((true,));
foo.0 = 0;

View file

@ -0,0 +1,10 @@
Error: Type Mismatch
╭─[assignment-rc-tuple-option-rc.txt:2:9]
1 │ let foo: Rc<(Option<bool>,)> = Rc((None,));
· ───┬──
· ╰──── Expected type `Option<bool>`,
2 │ foo.0 = Rc(true);
· ─┬
· ╰── but expression is of type `Rc<_>`
───╯

View file

@ -0,0 +1,2 @@
let foo: Rc<(Option<bool>,)> = Rc((None,));
foo.0 = Rc(true);

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-bool-int-arithmetic.txt:1:17]
1 │ let foo: bool = 1 + 2;
· ──┬─ ──┬──
· ╰─────────── Expected type `bool`,
· │
· ╰──── but expression is of type `int`
───╯

View file

@ -0,0 +1 @@
let foo: bool = 1 + 2;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-bool-int-negation.txt:2:17]
2 │ let foo: bool = -one;
· ──┬─ ┬
· ╰─────── Expected type `bool`,
· │
· ╰── but expression is of type `int`
───╯

View file

@ -0,0 +1,2 @@
let one: int = 1;
let foo: bool = -one;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-bool-int-tuple-bool-bool-tuple.txt:1:31]
1 │ let foo: (bool, int) = (true, true);
· ─┬─ ──┬─
· ╰────────────────── Expected type `int`,
· │
· ╰─── but expression is of type `bool`
───╯

View file

@ -0,0 +1 @@
let foo: (bool, int) = (true, true);

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-bool-int.txt:1:17]
1 │ let foo: bool = 0;
· ──┬─ ┬
· ╰─────── Expected type `bool`,
· │
· ╰── but expression is of type `int`
───╯

View file

@ -0,0 +1 @@
let foo: bool = 0;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-int-bool-conjunction.txt:1:16]
1 │ let foo: int = true && true;
· ─┬─ ──────┬─────
· ╰────────────────── Expected type `int`,
· │
· ╰─────── but expression is of type `bool`
───╯

View file

@ -0,0 +1 @@
let foo: int = true && true;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-int-bool-negation.txt:2:16]
2 │ let foo: int = !yes;
· ─┬─ ┬
· ╰─────── Expected type `int`,
· │
· ╰── but expression is of type `bool`
───╯

View file

@ -0,0 +1,2 @@
let yes: bool = true;
let foo: int = !yes;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-int-bool.txt:1:16]
1 │ let foo: int = true;
· ─┬─ ──┬─
· ╰────────── Expected type `int`,
· │
· ╰─── but expression is of type `bool`
───╯

View file

@ -0,0 +1 @@
let foo: int = true;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-int-int-comparison.txt:1:16]
1 │ let foo: int = 1 == 2;
· ─┬─ ───┬──
· ╰──────────── Expected type `int`,
· │
· ╰──── but expression is of type `bool`
───╯

View file

@ -0,0 +1 @@
let foo: int = 1 == 2;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-int-tuple-bool-tuple.txt:1:20]
1 │ let foo: (int,) = (true,);
· ─┬─ ──┬─
· ╰───────────── Expected type `int`,
· │
· ╰─── but expression is of type `bool`
───╯

View file

@ -0,0 +1 @@
let foo: (int,) = (true,);

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[declaration-unit-one-tuple.txt:1:15]
1 │ let foo: () = (1,);
· ─┬ ──┬─
· ╰───────── Expected tuple with 0 elements,
· │
· ╰─── but expression has 1 elements
───╯

View file

@ -0,0 +1 @@
let foo: () = (1,);

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[expression-arithmetic-int-bool.txt:1:20]
1 │ let foo: int = 1 + true;
· ┬ ──┬─
· ╰─────── Expected type `int`,
· │
· ╰─── but expression is of type `bool`
───╯

View file

@ -0,0 +1 @@
let foo: int = 1 + true;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[expression-comparison-int-bool.txt:1:22]
1 │ let foo: bool = 1 == true;
· ─┬ ──┬─
· ╰─────── Expected type `int`,
· │
· ╰─── but expression is of type `bool`
───╯

View file

@ -0,0 +1 @@
let foo: bool = 1 == true;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[expression-conjunction-bool-int.txt:1:25]
1 │ let foo: bool = true || 1;
· ─┬ ┬
· ╰──── Expected type `bool`,
· │
· ╰── but expression is of type `int`
───╯

View file

@ -0,0 +1 @@
let foo: bool = true || 1;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[expression-int-0.txt:2:16]
2 │ let bar: int = foo.0;
· ──┬─┬
· ╰──── Expression is of type `int`,
· │
· ╰── but expected type `(_, ..)`
───╯

View file

@ -0,0 +1,2 @@
let foo: int = 1;
let bar: int = foo.0;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[expression-minus-bool.txt:1:17]
1 │ let foo: int = -true;
· ┬──┬─
· ╰────── Expected type `int`,
· │
· ╰─── but expression is of type `bool`
───╯

View file

@ -0,0 +1 @@
let foo: int = -true;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[expression-not-int.txt:1:18]
1 │ let foo: bool = !0;
· ┬┬
· ╰─── Expected type `bool`,
· │
· ╰── but expression is of type `int`
───╯

View file

@ -0,0 +1 @@
let foo: bool = !0;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[expression-unit-0.txt:1:16]
1 │ let foo: int = ().0;
· ─┬┬─
· ╰──── Found tuple with 0 elements,
· │
· ╰─── but expression requires at least 1 elements
───╯

View file

@ -0,0 +1 @@
let foo: int = ().0;

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[if-condition-int.txt:1:4]
1 │ if 0 {}
· ─┬ ┬
· ╰──── Expected type `bool`,
· │
· ╰── but expression is of type `int`
───╯

View file

@ -0,0 +1 @@
if 0 {}

View file

@ -0,0 +1,9 @@
Error: Type Mismatch
╭─[while-condition-int.txt:1:7]
1 │ while 0 {}
· ──┬── ┬
· ╰────── Expected type `bool`,
· │
· ╰── but expression is of type `int`
───╯

View file

@ -0,0 +1 @@
while 0 {}

View file

@ -0,0 +1,7 @@
Error: Unknown type
╭─[declaration.txt:1:10]
1 │ let foo: Foo = 0;
· ─┬─
· ╰─── This type has not been defined
───╯

View file

@ -0,0 +1 @@
let foo: Foo = 0;

116
integration/main.rs Normal file
View file

@ -0,0 +1,116 @@
#![warn(rust_2018_idioms)]
#![deny(elided_lifetimes_in_paths)]
#![forbid(unsafe_code)]
use compiler::{
compile::{compile, CompileResult},
diagnostic::{Diagnostic, Source}
};
use lazy_regex::regex_replace_all;
use libtest::{run_tests, Arguments, Outcome, Test};
use std::{
fs::{self, File},
io::{Read as _, Write as _},
panic::catch_unwind,
path::{Path, PathBuf}
};
struct TestData {
path: PathBuf
}
fn run_test(data: &TestData) -> anyhow::Result<()> {
let mut file = File::open(&data.path)?;
let mut input = String::new();
file.read_to_string(&mut input)?;
drop(file);
let compile_result = match catch_unwind(|| compile(&input)) {
Ok(result) => result,
Err(e) => match e.downcast_ref::<String>() {
Some(str) => anyhow::bail!("The compiler paniced: {str}"),
None => match e.downcast_ref::<&str>() {
Some(str) => anyhow::bail!("The compiler paniced: {str}"),
None => anyhow::bail!("The compiler paniced")
}
}
};
let msg = match compile_result {
Ok(CompileResult::Ok { .. }) => anyhow::bail!("Code compiled successfully"),
Ok(CompileResult::InternalErr(code, err)) => {
let mut buf = Vec::new();
Diagnostic::ice(err).write(Source::new(&code), &mut buf)?;
String::from_utf8(buf).unwrap()
},
Err(err) => {
let filename = data.path.file_name().unwrap().to_str().unwrap();
let mut buf = Vec::new();
err.write(Source::new(&input).with_filename(filename), &mut buf)?;
String::from_utf8(buf).unwrap()
}
};
// remove colors from the msg
let msg = regex_replace_all!("\x1B\\[[^m]+m", &msg, |_| "");
let mut path = data.path.clone();
path.set_extension("out");
if !path.exists() {
let mut file = File::create(&path)?;
write!(file, "{msg}")?;
Ok(())
} else {
let mut file = File::open(&path)?;
let mut buf = String::new();
file.read_to_string(&mut buf)?;
drop(file);
if msg == buf {
Ok(())
} else {
Err(anyhow::anyhow!(
"Error: Expected output\n\n{buf}\n\nActual output\n\n{msg}"
))
}
}
}
fn add_tests_from_dir<P>(tests: &mut Vec<Test<TestData>>, path: P) -> anyhow::Result<()>
where
P: AsRef<Path>
{
for file in fs::read_dir(path)? {
let file = file?;
let path = file.path();
let ty = file.file_type()?;
if ty.is_dir() {
add_tests_from_dir(tests, &path)?;
} else if ty.is_file()
&& path.extension().map(|ext| ext == "txt").unwrap_or(false)
{
tests.push(Test {
name: path.display().to_string(),
kind: "".into(),
is_ignored: false,
is_bench: false,
data: TestData { path }
});
}
}
Ok(())
}
fn main() -> anyhow::Result<()> {
let args = Arguments::from_args();
let mut tests = Vec::new();
add_tests_from_dir(&mut tests, "integration/fail")?;
run_tests(&args, tests, |test| match run_test(&test.data) {
Ok(()) => Outcome::Passed,
Err(err) => Outcome::Failed {
msg: Some(format!("{err:?}"))
}
})
.exit();
}

35
rustfmt.toml Normal file
View file

@ -0,0 +1,35 @@
edition = "2021"
max_width = 90
newline_style = "Unix"
unstable_features = true
# always use tabs.
hard_tabs = true
tab_spaces = 4
# commas inbetween but not after
match_block_trailing_comma = true
trailing_comma = "Never"
# fix my imports for me
imports_granularity = "Crate"
group_imports = "One"
# format everything
format_code_in_doc_comments = true
format_macro_matchers = true
# don't keep outdated syntax
use_field_init_shorthand = true
use_try_shorthand = true
# condense Struct { _, _ } to Struct { .. }
condense_wildcard_suffixes = true
# prefer foo(Bar { \n }) over foo(\nBar { \n }\n)
overflow_delimited_expr = true
# I wish there was a way to allow 0..n but not a + 1..b + 2
# However, the later looks so terible that I use spaces everywhere
# https://github.com/rust-lang/rustfmt/issues/3367
spaces_around_ranges = true

30
src/ast/assignment.rs Normal file
View file

@ -0,0 +1,30 @@
use super::Expression;
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
Member, Token
};
#[derive(Clone, Debug)]
pub struct Assignment {
pub lhs: Punctuated<Member, Token![.]>,
pub eq_token: Token![=],
pub rhs: Expression,
pub semi_token: Token![;]
}
impl Parse for Assignment {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let lhs = Punctuated::parse_separated_nonempty(input)?;
if lhs.trailing_punct() {
bail!(lhs.span() => "Expected ident after fullstop");
}
Ok(Self {
lhs,
eq_token: input.parse()?,
rhs: input.parse()?,
semi_token: input.parse()?
})
}
}

84
src/ast/conditional.rs Normal file
View file

@ -0,0 +1,84 @@
use super::{Block, Expression};
use proc_macro2::{Ident, Span};
use syn::{
parse::{Parse, ParseStream},
token::{Brace, Paren},
Token
};
#[derive(Clone, Debug)]
pub enum Condition {
Bool(Expression),
LetSome {
let_token: Token![let],
some_span: Span,
paren_token: Paren,
ident: Ident,
eq_token: Token![=],
expr: Expression
}
}
impl Parse for Condition {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(if input.peek(Token![let]) {
let content;
Self::LetSome {
let_token: input.parse()?,
some_span: {
let some: Ident = input.parse()?;
if some != "Some" {
bail!(some.span() => "Expected `Some`");
}
some.span()
},
paren_token: syn::parenthesized!(content in input),
ident: content.parse()?,
eq_token: input.parse()?,
expr: input.parse()?
}
} else {
Self::Bool(input.parse()?)
})
}
}
#[derive(Clone, Debug)]
pub enum ElseBranch {
If(Box<If>),
Block(Block)
}
impl Parse for ElseBranch {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(if input.peek(Token![if]) {
Self::If(input.parse()?)
} else if input.peek(Brace) {
Self::Block(input.parse()?)
} else {
bail!(input.span() => "Expected `if` or block");
})
}
}
#[derive(Clone, Debug)]
pub struct If {
pub if_token: Token![if],
pub condition: Condition,
pub then_branch: Block,
pub else_branch: Option<(Token![else], ElseBranch)>
}
impl Parse for If {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
if_token: input.parse()?,
condition: input.parse()?,
then_branch: input.parse()?,
else_branch: input
.peek(Token![else])
.then(|| syn::Result::Ok((input.parse()?, input.parse()?)))
.transpose()?
})
}
}

31
src/ast/declaration.rs Normal file
View file

@ -0,0 +1,31 @@
use super::{Expression, Type};
use proc_macro2::Ident;
use syn::{
parse::{Parse, ParseStream},
Token
};
#[derive(Clone, Debug)]
pub struct Declaration {
pub let_token: Token![let],
pub ident: Ident,
pub colon_token: Token![:],
pub ty: Type,
pub eq_token: Token![=],
pub expr: Expression,
pub semi_token: Token![;]
}
impl Parse for Declaration {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
let_token: input.parse()?,
ident: input.parse()?,
colon_token: input.parse()?,
ty: input.parse()?,
eq_token: input.parse()?,
expr: input.parse()?,
semi_token: input.parse()?
})
}
}

433
src/ast/expression/mod.rs Normal file
View file

@ -0,0 +1,433 @@
use super::{Generics, Type};
use proc_macro2::{Ident, Span};
use std::fmt::{self, Debug, Display, Formatter};
use syn::{
punctuated::Punctuated, spanned::Spanned, token::Paren, Index, LitBool, LitInt, Token
};
mod parse;
#[derive(Clone, Copy, Debug)]
pub enum ArithmeticOp {
Add(Token![+]),
Sub(Token![-]),
Mul(Token![*]),
Div(Token![/])
}
impl ArithmeticOp {
pub fn is_add_or_sub_op(self) -> bool {
matches!(self, Self::Add(_) | Self::Sub(_))
}
pub fn is_mul_or_div_op(self) -> bool {
matches!(self, Self::Mul(_) | Self::Div(_))
}
pub fn char(self) -> char {
match self {
ArithmeticOp::Add(_) => '+',
ArithmeticOp::Sub(_) => '-',
ArithmeticOp::Mul(_) => '*',
ArithmeticOp::Div(_) => '/'
}
}
}
impl Spanned for ArithmeticOp {
fn span(&self) -> Span {
match self {
Self::Add(token) => token.span,
Self::Sub(token) => token.span,
Self::Mul(token) => token.span,
Self::Div(token) => token.span
}
}
}
impl Display for ArithmeticOp {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.char())
}
}
#[derive(Clone, Copy, Debug)]
pub enum ComparisonOp {
Eq(Token![==]),
Neq(Token![!=]),
Lt(Token![<]),
Le(Token![<=]),
Gt(Token![>]),
Ge(Token![>=])
}
impl ComparisonOp {
fn str(self) -> &'static str {
match self {
ComparisonOp::Eq(_) => "==",
ComparisonOp::Neq(_) => "!=",
ComparisonOp::Lt(_) => "<",
ComparisonOp::Le(_) => "<=",
ComparisonOp::Gt(_) => ">",
ComparisonOp::Ge(_) => ">="
}
}
}
impl Spanned for ComparisonOp {
fn span(&self) -> Span {
match self {
Self::Eq(token) => token.span(),
Self::Neq(token) => token.span(),
Self::Lt(token) => token.span,
Self::Le(token) => token.span(),
Self::Gt(token) => token.span,
Self::Ge(token) => token.span()
}
}
}
impl Display for ComparisonOp {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(self.str())
}
}
#[derive(Clone, Copy, Debug)]
pub enum ConjunctionOp {
And(Token![&&]),
Or(Token![||])
}
impl ConjunctionOp {
fn str(self) -> &'static str {
match self {
ConjunctionOp::And(_) => "&&",
ConjunctionOp::Or(_) => "||"
}
}
}
impl Spanned for ConjunctionOp {
fn span(&self) -> Span {
match self {
Self::And(token) => token.span(),
Self::Or(token) => token.span()
}
}
}
impl Display for ConjunctionOp {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str(self.str())
}
}
#[derive(Clone)]
pub enum Expression {
/// An expression enclosed in parentheses.
Term {
paren_token: Paren,
expr: Box<Expression>
},
/// Variable access.
Ident(Ident),
/// A boolean literal.
Bool(LitBool),
/// A number literal.
Literal(LitInt),
/// A boolean negation (`!expr`).
BoolNegation {
not_token: Token![!],
expr: Box<Expression>
},
/// An integer negation (`-expr`).
IntNegation {
minus_token: Token![-],
expr: Box<Expression>
},
/// An arithmetic expression.
Arithmetic {
lhs: Box<Expression>,
op: ArithmeticOp,
rhs: Box<Expression>
},
/// A comparison.
Comparison {
lhs: Box<Expression>,
op: ComparisonOp,
rhs: Box<Expression>
},
/// A conjunction or disjunction.
Conjunction {
lhs: Box<Expression>,
op: ConjunctionOp,
rhs: Box<Expression>
},
/// A tuple constructor.
TupleCtor {
paren_token: Paren,
elems: Punctuated<Expression, Token![,]>
},
/// A tuple index expression.
TupleIndex {
expr: Box<Expression>,
dot_token: Token![.],
index: Index
},
/// An `Rc` constructor.
RcCtor {
span: Span,
paren_token: Paren,
expr: Box<Expression>
},
/// An `Option` constructor.
OptionCtor {
span: Span,
expr: Option<(Paren, Box<Expression>)>
},
/// A deref expression.
Deref {
star_token: Token![*],
expr: Box<Expression>
},
/// A function call.
FnCall {
ident: Ident,
generics: Option<Generics<Type>>,
paren_token: Paren,
inputs: Punctuated<Expression, Token![,]>
}
}
impl Expression {
/// Replace the rightmost part of this expression with the return value
/// of the callback. If self is not a binary expression, or if the filter
/// callback returns false, calls the callback with self.
fn replace_rightmost<F, C>(self, filter: F, callback: C) -> Self
where
F: Fn(&Self) -> bool,
C: FnOnce(Self) -> Self
{
if !filter(&self) {
return callback(self);
}
match self {
Self::Arithmetic { lhs, op, rhs } => Self::Arithmetic {
lhs,
op,
rhs: Box::new(rhs.replace_rightmost(filter, callback))
},
Self::Comparison { lhs, op, rhs } => Self::Comparison {
lhs,
op,
rhs: Box::new(rhs.replace_rightmost(filter, callback))
},
Self::Conjunction { lhs, op, rhs } => Self::Conjunction {
lhs,
op,
rhs: Box::new(rhs.replace_rightmost(filter, callback))
},
this => callback(this)
}
}
}
impl Spanned for Expression {
fn span(&self) -> Span {
match self {
Self::Term { paren_token, .. } => paren_token.span,
Self::Ident(ident) => ident.span(),
Self::Bool(bool) => bool.span(),
Self::Literal(lit) => lit.span(),
Self::BoolNegation { not_token, .. } => not_token.span,
Self::IntNegation { minus_token, .. } => minus_token.span,
Self::Arithmetic { lhs, op, rhs } => lhs
.span()
.join(op.span())
.unwrap()
.join(rhs.span())
.unwrap(),
Self::Comparison { lhs, op, rhs } => lhs
.span()
.join(op.span())
.unwrap()
.join(rhs.span())
.unwrap(),
Self::Conjunction { lhs, op, rhs } => lhs
.span()
.join(op.span())
.unwrap()
.join(rhs.span())
.unwrap(),
Self::TupleCtor { paren_token, .. } => paren_token.span,
Self::TupleIndex {
expr,
dot_token,
index
} => expr
.span()
.join(dot_token.span())
.unwrap()
.join(index.span())
.unwrap(),
Self::RcCtor { span, .. } => *span,
Self::OptionCtor { span, .. } => *span,
Self::Deref { star_token, expr } => {
star_token.span.join(expr.span()).unwrap()
},
Self::FnCall { ident, .. } => ident.span()
}
}
}
impl Debug for Expression {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Term { expr, .. } => f
.debug_struct("Expression::Term")
.field("expr", &expr)
.finish_non_exhaustive(),
Self::Ident(ident) => {
f.debug_tuple("Expression::Ident").field(ident).finish()
},
Self::Bool(lit) => {
f.debug_tuple("Expression::Bool").field(&lit.value).finish()
},
Self::Literal(lit) => f
.debug_tuple("Expression::Literal")
.field(&lit.base10_parse::<isize>())
.finish(),
Self::BoolNegation { expr, .. } => f
.debug_struct("Expression::BoolNegation")
.field("expr", &expr)
.finish_non_exhaustive(),
Self::IntNegation { expr, .. } => f
.debug_struct("Expression::IntNegation")
.field("expr", &expr)
.finish_non_exhaustive(),
Self::Arithmetic { lhs, op, rhs } => f
.debug_struct("Expression::Arithmetic")
.field("lhs", &lhs)
.field("op", &op.char())
.field("rhs", &rhs)
.finish(),
Self::Comparison { lhs, op, rhs } => f
.debug_struct("Expression::Comparison")
.field("lhs", &lhs)
.field("op", &op.str())
.field("rhs", &rhs)
.finish(),
Self::Conjunction { lhs, op, rhs } => f
.debug_struct("Expression::Conjunction")
.field("lhs", &lhs)
.field("op", &op.str())
.field("rhs", &rhs)
.finish(),
Self::TupleCtor { elems, .. } => f
.debug_struct("Expression::TupleCtor")
.field("elems", &elems)
.finish_non_exhaustive(),
Self::TupleIndex { expr, index, .. } => f
.debug_struct("Expression::TupleCtor")
.field("expr", &expr)
.field("index", &index.index)
.finish_non_exhaustive(),
Self::RcCtor { expr, .. } => f
.debug_struct("Expression::RcCtor")
.field("expr", &expr)
.finish_non_exhaustive(),
Self::OptionCtor { expr, .. } => f
.debug_struct("Expression::OptionCtor")
.field("expr", &expr.as_ref().map(|(_, expr)| expr))
.finish_non_exhaustive(),
Self::Deref { expr, .. } => f
.debug_struct("Expression::Deref")
.field("expr", &expr)
.finish_non_exhaustive(),
Self::FnCall { ident, .. } => f
.debug_struct("Expression::FnCall")
.field("ident", &ident)
.finish_non_exhaustive()
}
}
}
impl Display for Expression {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Term { expr, .. } => write!(f, "({{ {expr} }})"),
Self::Ident(ident) => {
write!(f, "{ident}")
},
Self::Bool(lit) => match lit.value() {
true => f.write_str("true"),
false => f.write_str("false")
},
Self::Literal(lit) => f.write_str(lit.base10_digits()),
Self::BoolNegation { expr, .. } => write!(f, "!{{ {expr} }}"),
Self::IntNegation { expr, .. } => write!(f, "-{{ {expr} }}"),
Self::Arithmetic { lhs, op, rhs } => {
write!(f, "{{ {lhs} }} {op} {{ {rhs} }}")
},
Self::Comparison { lhs, op, rhs } => {
write!(f, "{{ {lhs} }} {op} {{ {rhs} }}")
},
Self::Conjunction { lhs, op, rhs } => {
write!(f, "{{ {lhs} }} {op} {{ {rhs} }}")
},
Self::TupleCtor { elems, .. } => {
f.write_str("(")?;
for elem in elems.iter() {
write!(f, "{{ {elem} }}, ")?;
}
f.write_str(")")
},
Self::TupleIndex { expr, index, .. } => {
write!(f, "{{ {expr} }}.{}", index.index)
},
Self::RcCtor { expr, .. } => write!(f, "Rc({{ {expr} }})"),
Self::OptionCtor { expr: None, .. } => write!(f, "None"),
Self::OptionCtor {
expr: Some((_, expr)),
..
} => write!(f, "Some({{ {expr} }})"),
Self::Deref { expr, .. } => write!(f, "*{{ {expr} }}"),
Self::FnCall {
ident,
generics,
inputs,
..
} => {
write!(f, "{ident}")?;
if let Some(g) = generics {
write!(f, "::{g}")?;
}
f.write_str("(")?;
for arg in inputs.iter() {
write!(f, "{{ {arg} }}, ")?;
}
f.write_str(")")
}
}
}
}
#[cfg(test)]
mod tests;

300
src/ast/expression/parse.rs Normal file
View file

@ -0,0 +1,300 @@
use super::{ArithmeticOp, ComparisonOp, ConjunctionOp, Expression};
use proc_macro2::Ident;
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
token::Paren,
Index, LitBool, LitInt, Token
};
macro_rules! parse_next_op {
($left:ident, $input:ident,Self:: $fn:ident, $op:ident:: $variant:ident) => {
(
Self::$fn(
$left,
$op::$variant($input.parse()?),
Self::parse_first($input)?
),
false
)
};
}
macro_rules! reorder_expr {
($lhs:ident, $op:ident, $rhs:ident => Self::$new_variant:ident {
$(Self::$variant:ident {
$lhs_lhs:ident,
$lhs_op:ident,
$lhs_rhs:ident
} $(if $condition:expr)?),*
}) => {{
// This method will not be called when there are not variants in the macro
// Also, the variables in the pattern can only be used by the if condition
#[allow(dead_code, unused_variables)]
fn filter(expr: &Expression) -> bool {
match expr {
$(Expression::$variant {
lhs: $lhs_lhs,
op: $lhs_op,
rhs: $lhs_rhs
} $(if $condition)? => true,)*
_ => false
}
}
match $lhs {
$(Self::$variant {
lhs: $lhs_lhs,
op: $lhs_op,
rhs: $lhs_rhs
} $(if $condition)? => {
Self::$variant {
lhs: $lhs_lhs,
op: $lhs_op,
rhs: $lhs_rhs
}.replace_rightmost(filter, |lhs_rhs| {
Self::$new_variant {
lhs: Box::new(lhs_rhs),
op: $op,
rhs: Box::new($rhs)
}
})
},)*
lhs => Self::$new_variant {
lhs: Box::new(lhs),
op: $op,
rhs: Box::new($rhs)
}
}
}};
}
impl Expression {
/// Parse the first expression from the input. Might not be the full
/// expression.
fn parse_first(input: ParseStream<'_>) -> syn::Result<Self> {
if input.peek(Paren) {
let content;
let paren_token = syn::parenthesized!(content in input);
let elems = Punctuated::parse_terminated(&content)?;
return Ok(if elems.len() == 1 && !elems.trailing_punct() {
// parenthezised expression, not a tuple
Self::Term {
paren_token,
expr: Box::new(elems.into_iter().next().unwrap())
}
} else {
// tuple
Self::TupleCtor { paren_token, elems }
});
}
if input.peek(LitBool) {
return Ok(Self::Bool(input.parse()?));
}
if input.peek(LitInt) {
return Ok(Self::Literal(input.parse()?));
}
if input.peek(Token![!]) {
return Ok(Self::BoolNegation {
not_token: input.parse()?,
expr: Box::new(Self::parse_first(input)?)
});
}
if input.peek(Token![-]) {
return Ok(Self::IntNegation {
minus_token: input.parse()?,
expr: Box::new(Self::parse_first(input)?)
});
}
if input.peek(Token![*]) {
return Ok(Self::Deref {
star_token: input.parse()?,
expr: Box::new(Self::parse_first(input)?)
});
}
let ident: Ident = input.parse()?;
if input.peek(Token![.]) {
let mut left = Self::Ident(ident);
while input.peek(Token![.]) {
left = Self::tuple_index(left, input.parse()?, input.parse()?);
}
return Ok(left);
}
Ok(if ident == "Rc" {
let content;
let paren_token = syn::parenthesized!(content in input);
Self::RcCtor {
span: ident.span(),
paren_token,
expr: Box::new(content.parse()?)
}
} else if ident == "Some" {
let content;
let paren_token = syn::parenthesized!(content in input);
Self::OptionCtor {
span: ident.span(),
expr: Some((paren_token, Box::new(content.parse()?)))
}
} else if ident == "None" {
Self::OptionCtor {
span: ident.span(),
expr: None
}
} else if input.peek(Paren) {
let content;
let paren_token = syn::parenthesized!(content in input);
let inputs = Punctuated::parse_terminated(&content)?;
Self::FnCall {
ident,
generics: None,
paren_token,
inputs
}
} else if input.peek(Token![::]) {
let _turbofish: Token![::] = input.parse()?;
let generics = Some(input.parse()?);
let content;
let paren_token = syn::parenthesized!(content in input);
let inputs = Punctuated::parse_terminated(&content)?;
Self::FnCall {
ident,
generics,
paren_token,
inputs
}
} else {
Self::Ident(ident)
})
}
// this is the strongest binding binary operator, and since all unary
// operators are left of their argument we don't need to take them
// into account
fn mul_or_div_op(lhs: Self, op: ArithmeticOp, rhs: Self) -> Self {
debug_assert!(op.is_mul_or_div_op());
reorder_expr!(lhs, op, rhs => Self::Arithmetic {
// ¦a + b¦ * c = a + (b * c)
Self::Arithmetic { lhs_lhs, lhs_op, lhs_rhs } if lhs_op.is_add_or_sub_op(),
// ¦a == b¦ * c = a == (b * c)
Self::Comparison { lhs_lhs, lhs_op, lhs_rhs },
// ¦a && b¦ * c = a && (b * c)
// (this is essential to allow comparison operators to be parsed next)
Self::Conjunction { lhs_lhs, lhs_op, lhs_rhs }
})
}
// this is the 2nd strongest binding binary operator, after mul or div and
// before any of the boolean operators
fn add_or_sub_op(lhs: Self, op: ArithmeticOp, rhs: Self) -> Self {
debug_assert!(op.is_add_or_sub_op());
reorder_expr!(lhs, op, rhs => Self::Arithmetic {
// ¦a == b¦ * c = a == (b * c)
Self::Comparison { lhs_lhs, lhs_op, lhs_rhs },
// ¦a && b¦ * c = a && (b * c)
// (this is essential to allow comparison operators to be parsed next)
Self::Conjunction { lhs_lhs, lhs_op, lhs_rhs }
})
}
// this is the 3rd strongest binding binary operator, after both arithmetic
// operators
fn comparison_op(lhs: Self, op: ComparisonOp, rhs: Self) -> Self {
reorder_expr!(lhs, op, rhs => Self::Comparison {
// ¦a && b¦ == c = a && (b == c)
Self::Conjunction { lhs_lhs, lhs_op, lhs_rhs }
})
}
// this is the least strongest binding binary operator
fn conjunction_op(lhs: Self, op: ConjunctionOp, rhs: Self) -> Self {
reorder_expr!(lhs, op, rhs => Self::Conjunction {})
}
// tuple indexing binds stronger than unary operators
fn tuple_index(left: Self, dot_token: Token![.], index: Index) -> Self {
match left {
Self::BoolNegation { not_token, expr } => Self::BoolNegation {
not_token,
expr: Box::new(Self::tuple_index(*expr, dot_token, index))
},
Self::IntNegation { minus_token, expr } => Self::IntNegation {
minus_token,
expr: Box::new(Self::tuple_index(*expr, dot_token, index))
},
Self::Deref { star_token, expr } => Self::Deref {
star_token,
expr: Box::new(Self::tuple_index(*expr, dot_token, index))
},
left => Self::TupleIndex {
expr: Box::new(left),
dot_token,
index
}
}
}
fn parse_next(left: Self, input: ParseStream<'_>) -> syn::Result<(Self, bool)> {
if input.is_empty() {
return Ok((left, true));
}
Ok(if input.peek(Token![.]) {
(
Self::tuple_index(left, input.parse()?, input.parse()?),
false
)
} else if input.peek(Token![+]) {
parse_next_op!(left, input, Self::add_or_sub_op, ArithmeticOp::Add)
} else if input.peek(Token![-]) {
parse_next_op!(left, input, Self::add_or_sub_op, ArithmeticOp::Sub)
} else if input.peek(Token![*]) {
parse_next_op!(left, input, Self::mul_or_div_op, ArithmeticOp::Mul)
} else if input.peek(Token![/]) {
parse_next_op!(left, input, Self::mul_or_div_op, ArithmeticOp::Div)
} else if input.peek(Token![==]) {
parse_next_op!(left, input, Self::comparison_op, ComparisonOp::Eq)
} else if input.peek(Token![!=]) {
parse_next_op!(left, input, Self::comparison_op, ComparisonOp::Neq)
} else if input.peek(Token![<=]) {
parse_next_op!(left, input, Self::comparison_op, ComparisonOp::Le)
} else if input.peek(Token![<]) {
parse_next_op!(left, input, Self::comparison_op, ComparisonOp::Lt)
} else if input.peek(Token![>=]) {
parse_next_op!(left, input, Self::comparison_op, ComparisonOp::Ge)
} else if input.peek(Token![>]) {
parse_next_op!(left, input, Self::comparison_op, ComparisonOp::Gt)
} else if input.peek(Token![&&]) {
parse_next_op!(left, input, Self::conjunction_op, ConjunctionOp::And)
} else if input.peek(Token![||]) {
parse_next_op!(left, input, Self::conjunction_op, ConjunctionOp::Or)
} else {
(left, true)
})
}
}
impl Parse for Expression {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut expr = Self::parse_first(input)?;
loop {
let last;
(expr, last) = Self::parse_next(expr, input)?;
if last {
break;
}
}
Ok(expr)
}
}

259
src/ast/expression/tests.rs Normal file
View file

@ -0,0 +1,259 @@
use super::*;
#[track_caller]
fn parse_expr(input: &str) -> Expression {
println!("Input: {input}");
syn::parse_str(input).expect("Failed to parse input")
}
#[track_caller]
fn assert_ident(expr: Expression, name: &str) {
match expr {
Expression::Ident(ident) if ident == name => {},
expr => panic!("Expected `{name}`, found `{expr}`")
}
}
#[track_caller]
fn assert_bool(expr: Expression, value: bool) {
match expr {
Expression::Bool(lit) if lit.value() == value => {},
expr => panic!("Expected `{value}`, found `{expr}`")
}
}
#[track_caller]
fn assert_int(expr: Expression, value: isize) {
match expr {
Expression::Literal(lit)
if lit
.base10_parse::<isize>()
.map(|lit| lit == value)
.unwrap_or(false) => {},
expr => panic!("Expected `{value}`, found `{expr}`")
}
}
#[track_caller]
fn assert_bool_negation(expr: Expression) -> Expression {
match expr {
Expression::BoolNegation { expr, .. } => *expr,
expr => {
panic!("Expected `!{{ .. }}`, found `{expr}`")
}
}
}
#[track_caller]
fn assert_int_negation(expr: Expression) -> Expression {
match expr {
Expression::IntNegation { expr, .. } => *expr,
expr => {
panic!("Expected `-{{ .. }}`, found `{expr}`")
}
}
}
#[track_caller]
fn assert_arithmetic(expr: Expression, op_char: char) -> (Expression, Expression) {
match expr {
Expression::Arithmetic { lhs, op, rhs } if op.char() == op_char => (*lhs, *rhs),
expr => panic!("Expected `{{ .. }} {op_char} {{ .. }}`, found `{expr}`")
}
}
#[track_caller]
fn assert_comparison(expr: Expression, op_str: &str) -> (Expression, Expression) {
match expr {
Expression::Comparison { lhs, op, rhs } if op.str() == op_str => (*lhs, *rhs),
expr => panic!("Expected `{{ .. }} {op_str} {{ .. }}`, found `{expr}`")
}
}
#[track_caller]
fn assert_conjunction(expr: Expression, op_str: &str) -> (Expression, Expression) {
match expr {
Expression::Conjunction { lhs, op, rhs } if op.str() == op_str => (*lhs, *rhs),
expr => panic!("Expected `{{ .. }} {op_str} {{ .. }}`, found `{expr}`")
}
}
#[track_caller]
fn assert_tuple_index(expr: Expression, idx: u32) -> Expression {
match expr {
Expression::TupleIndex { expr, index, .. } if index.index == idx => *expr,
expr => panic!("Expected `{{ .. }}.{idx}`, found `{expr}`")
}
}
#[track_caller]
fn assert_deref(expr: Expression) -> Expression {
match expr {
Expression::Deref { expr, .. } => *expr,
expr => panic!("*{{ .. }}`, found `{expr}`")
}
}
#[test]
fn parse_true() {
let expr = parse_expr("true");
assert_bool(expr, true);
}
#[test]
fn parse_false() {
let expr = parse_expr("false");
assert_bool(expr, false);
}
#[test]
fn parse_zero() {
let expr = parse_expr("0");
assert_int(expr, 0);
}
#[test]
fn parse_ten() {
let expr = parse_expr("10");
assert_int(expr, 10);
}
#[test]
fn parse_minus_ten() {
let expr = parse_expr("-10");
assert_int(expr, -10);
}
#[test]
fn parse_not_true() {
let expr = parse_expr("!true");
let expr = assert_bool_negation(expr);
assert_bool(expr, true);
}
#[test]
fn parse_minus_minus_ten() {
let expr = parse_expr("--10");
let expr = assert_int_negation(expr);
assert_int(expr, -10);
}
#[test]
fn parse_one_plus_two() {
let expr = parse_expr("1 + 2");
let (lhs, rhs) = assert_arithmetic(expr, '+');
assert_int(lhs, 1);
assert_int(rhs, 2);
}
#[test]
fn parse_one_plus_two_times_three() {
let expr = parse_expr("1 + 2 * 3");
let (lhs, rhs) = assert_arithmetic(expr, '+');
assert_int(lhs, 1);
let (lhs, rhs) = assert_arithmetic(rhs, '*');
assert_int(lhs, 2);
assert_int(rhs, 3);
}
#[test]
fn parse_one_times_two_plus_three_times_four() {
let expr = parse_expr("1 * 2 + 3 * 4");
let (lhs, rhs) = assert_arithmetic(expr, '+');
{
let (lhs, rhs) = assert_arithmetic(lhs, '*');
assert_int(lhs, 1);
assert_int(rhs, 2);
}
{
let (lhs, rhs) = assert_arithmetic(rhs, '*');
assert_int(lhs, 3);
assert_int(rhs, 4);
}
}
#[test]
fn parse_one_times_two_eq_three_plus_four() {
let expr = parse_expr("1 * 2 == 3 + 4");
let (lhs, rhs) = assert_comparison(expr, "==");
{
let (lhs, rhs) = assert_arithmetic(lhs, '*');
assert_int(lhs, 1);
assert_int(rhs, 2);
}
{
let (lhs, rhs) = assert_arithmetic(rhs, '+');
assert_int(lhs, 3);
assert_int(rhs, 4);
}
}
#[test]
fn parse_true_and_one_plus_two_eq_three_times_four() {
let expr = parse_expr("true && 1 + 2 == 3 * 4");
let (lhs, rhs) = assert_conjunction(expr, "&&");
assert_bool(lhs, true);
let (lhs, rhs) = assert_comparison(rhs, "==");
{
let (lhs, rhs) = assert_arithmetic(lhs, '+');
assert_int(lhs, 1);
assert_int(rhs, 2);
}
{
let (lhs, rhs) = assert_arithmetic(rhs, '*');
assert_int(lhs, 3);
assert_int(rhs, 4);
}
}
#[test]
fn parse_not_true_and_false() {
let expr = parse_expr("!true && false");
let (lhs, rhs) = assert_conjunction(expr, "&&");
let lhs = assert_bool_negation(lhs);
assert_bool(lhs, true);
assert_bool(rhs, false);
}
#[test]
fn parse_not_ident_0_and_false() {
let expr = parse_expr("!foo.0 && false");
let (lhs, rhs) = assert_conjunction(expr, "&&");
let lhs = assert_bool_negation(lhs);
let lhs = assert_tuple_index(lhs, 0);
assert_ident(lhs, "foo");
assert_bool(rhs, false);
}
#[test]
fn parse_deref_ident() {
let expr = parse_expr("*foo");
let expr = assert_deref(expr);
assert_ident(expr, "foo");
}
#[test]
fn parse_deref_ident_0() {
let expr = parse_expr("*foo.0");
let expr = assert_deref(expr);
let expr = assert_tuple_index(expr, 0);
assert_ident(expr, "foo");
}
#[test]
fn parse_deref_ident_eq_one() {
let expr = parse_expr("*foo == 1");
let (lhs, rhs) = assert_comparison(expr, "==");
let lhs = assert_deref(lhs);
assert_ident(lhs, "foo");
assert_int(rhs, 1);
}
#[test]
fn parse_zero_eq_foo_0() {
let expr = parse_expr("0 == foo.0");
let (lhs, rhs) = assert_comparison(expr, "==");
assert_int(lhs, 0);
let rhs = assert_tuple_index(rhs, 0);
assert_ident(rhs, "foo");
}

54
src/ast/function.rs Normal file
View file

@ -0,0 +1,54 @@
use super::{Block, Generics, Type};
use proc_macro2::Ident;
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
token::Paren,
Token
};
#[derive(Clone, Debug)]
pub struct FnArg {
pub ident: Ident,
pub colon_token: Token![:],
pub ty: Type
}
impl Parse for FnArg {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
ident: input.parse()?,
colon_token: input.parse()?,
ty: input.parse()?
})
}
}
#[derive(Clone, Debug)]
pub struct Fn {
pub fn_token: Token![fn],
pub ident: Ident,
pub generics: Option<Generics<Ident>>,
pub paren_token: Paren,
pub inputs: Punctuated<FnArg, Token![,]>,
pub output: Option<(Token![->], Type)>,
pub block: Block
}
impl Parse for Fn {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let content;
Ok(Self {
fn_token: input.parse()?,
ident: input.parse()?,
generics: input.peek(Token![<]).then(|| input.parse()).transpose()?,
paren_token: syn::parenthesized!(content in input),
inputs: Punctuated::parse_terminated(&content)?,
output: input
.peek(Token![->])
.then(|| syn::Result::Ok((input.parse()?, input.parse()?)))
.transpose()?,
block: input.parse()?
})
}
}

53
src/ast/generics.rs Normal file
View file

@ -0,0 +1,53 @@
use std::{
fmt::{self, Display, Formatter},
hash::{Hash, Hasher}
};
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
token::{Comma, Gt, Lt}
};
#[derive(Clone, Debug)]
pub struct Generics<T> {
pub lt_token: Lt,
pub params: Punctuated<T, Comma>,
pub gt_token: Gt
}
impl<T: Display> Display for Generics<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.write_str("<")?;
for (i, param) in self.params.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
write!(f, "{param}")?;
}
f.write_str(">")
}
}
impl<T: PartialEq> PartialEq for Generics<T> {
fn eq(&self, other: &Self) -> bool {
self.params == other.params
}
}
impl<T: Eq> Eq for Generics<T> {}
impl<T: Hash> Hash for Generics<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.params.hash(state);
}
}
impl<T: Parse> Parse for Generics<T> {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
lt_token: input.parse()?,
params: Punctuated::parse_separated_nonempty(input)?,
gt_token: input.parse()?
})
}
}

94
src/ast/input_output.rs Normal file
View file

@ -0,0 +1,94 @@
use proc_macro2::{Ident, Span};
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
token::{Bang, Paren, Semi},
Token
};
#[derive(Debug)]
pub struct InputOutputMacro<C> {
pub span: Span,
pub not_token: Bang,
pub paren_token: Paren,
pub content: C,
pub semi_token: Semi
}
#[derive(Debug)]
pub struct Input {
pub span: Span,
pub not_token: Token![!],
pub paren_token: Paren,
pub ident: Ident,
pub semi_token: Token![;]
}
impl From<InputOutputMacro<Ident>> for Input {
fn from(this: InputOutputMacro<Ident>) -> Self {
Self {
span: this.span,
not_token: this.not_token,
paren_token: this.paren_token,
ident: this.content,
semi_token: this.semi_token
}
}
}
#[derive(Debug)]
pub struct Output {
pub span: Span,
pub not_token: Token![!],
pub paren_token: Paren,
pub idents: Punctuated<Ident, Token![,]>,
pub semi_token: Token![;]
}
impl From<InputOutputMacro<Punctuated<Ident, Token![,]>>> for Output {
fn from(this: InputOutputMacro<Punctuated<Ident, Token![,]>>) -> Self {
Self {
span: this.span,
not_token: this.not_token,
paren_token: this.paren_token,
idents: this.content,
semi_token: this.semi_token
}
}
}
fn parse<I, F>(
input: ParseStream<'_>,
macro_ident: &str,
content_parser: F
) -> syn::Result<InputOutputMacro<I>>
where
F: FnOnce(ParseStream<'_>) -> syn::Result<I>
{
let content;
Ok(InputOutputMacro {
span: {
let ident: Ident = input.parse()?;
if ident != macro_ident {
bail!(ident.span() => "Expected `{macro_ident}`")
}
ident.span()
},
not_token: input.parse()?,
paren_token: syn::parenthesized!(content in input),
content: content_parser(&content)?,
semi_token: input.parse()?
})
}
impl Parse for Input {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
parse(input, "input", Parse::parse).map(Into::into)
}
}
impl Parse for Output {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
parse(input, "output", Punctuated::parse_separated_nonempty).map(Into::into)
}
}

184
src/ast/mod.rs Normal file
View file

@ -0,0 +1,184 @@
use crate::verbose;
use proc_macro2::Ident;
use syn::{
parse::{Parse, ParseStream},
token::{Brace, Bracket},
LitFloat, Token
};
mod assignment;
mod conditional;
mod declaration;
mod expression;
mod function;
mod generics;
mod input_output;
mod ret;
mod ty;
mod typedef;
mod while_loop;
pub use assignment::Assignment;
pub use conditional::{Condition, ElseBranch, If};
pub use declaration::Declaration;
pub use expression::{ArithmeticOp, ComparisonOp, Expression};
pub use function::{Fn, FnArg};
pub use generics::Generics;
pub use input_output::{Input, Output};
pub use ret::Return;
pub use ty::{Type, TypeCustom, TypePredef, TypePrimitive, TypeTuple};
pub use typedef::TypeDef;
pub use while_loop::{Break, Continue, While};
#[derive(Debug)]
pub struct Program {
pub types: Vec<TypeDef>,
pub functions: Vec<Fn>,
pub inputs: Vec<Input>,
pub stmts: Vec<Statement>,
pub outputs: Vec<Output>
}
fn peek_macro(input: ParseStream<'_>, macro_ident: &str) -> bool {
let fork = input.fork();
let next = fork.parse::<Ident>();
let next2 = fork.parse::<Token![!]>();
matches!((next, next2), (Ok(ident), Ok(_)) if ident == macro_ident)
}
impl Parse for Program {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let mut types = Vec::new();
while input.peek(Token![type]) {
if verbose() {
eprint!("[ast] Parsing typedef ...");
}
types.push(input.parse()?);
if verbose() {
eprintln!("done");
}
}
let mut functions = Vec::new();
while input.peek(Token![fn]) {
if verbose() {
eprint!("[ast] Parsing function ...");
}
functions.push(input.parse()?);
if verbose() {
eprintln!("done");
}
}
let mut inputs = Vec::new();
while peek_macro(input, "input") {
if verbose() {
eprint!("[ast] Parsing input!() ...");
}
inputs.push(input.parse()?);
if verbose() {
eprintln!("done");
}
}
let mut stmts = Vec::new();
while !input.is_empty() && !peek_macro(input, "output") {
if verbose() {
eprint!("[ast] Parsing statement ...");
}
stmts.push(input.parse()?);
if verbose() {
eprintln!("done");
}
}
let mut outputs = Vec::new();
while !input.is_empty() {
if verbose() {
eprint!("[ast] Parsing output!() ...");
}
outputs.push(input.parse()?);
if verbose() {
eprintln!("done");
}
}
if verbose() {
eprintln!("[ast] Done parsing");
}
Ok(Self {
types,
functions,
inputs,
stmts,
outputs
})
}
}
#[derive(Clone, Debug)]
pub struct Block {
pub stmts: Vec<Statement>
}
impl Parse for Block {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let content;
syn::braced!(content in input);
let mut stmts = Vec::new();
while !content.is_empty() {
stmts.push(content.parse()?);
}
Ok(Self { stmts })
}
}
#[derive(Clone, Debug)]
pub enum Statement {
Decl(Declaration),
Assign(Assignment),
If(If),
CoinFlip { head: Block, prob: f32, tail: Block },
While(While),
Block(Block),
Return(Return),
Continue(Continue),
Break(Break)
}
impl Parse for Statement {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(if input.peek(Token![let]) {
Self::Decl(input.parse()?)
} else if input.peek(Token![if]) {
Self::If(input.parse()?)
} else if input.peek(Token![while]) {
Self::While(input.parse()?)
} else if input.peek(Token![return]) {
Self::Return(input.parse()?)
} else if input.peek(Token![continue]) {
Self::Continue(input.parse()?)
} else if input.peek(Token![break]) {
Self::Break(input.parse()?)
} else if input.peek(Brace) {
let block = input.parse()?;
if input.peek(Bracket) {
let content;
syn::bracketed!(content in input);
let prob: LitFloat = content.parse()?;
let tail = input.parse()?;
Self::CoinFlip {
head: block,
prob: prob.base10_parse()?,
tail
}
} else {
Self::Block(block)
}
} else if input.peek(syn::Ident) {
Self::Assign(input.parse()?)
} else {
bail!(input.span() => "Unexpected token");
})
}
}

24
src/ast/ret.rs Normal file
View file

@ -0,0 +1,24 @@
use super::Expression;
use syn::{
parse::{Parse, ParseStream},
Token
};
#[derive(Clone, Debug)]
pub struct Return {
pub return_token: Token![return],
pub expr: Option<Expression>,
pub semi_token: Token![;]
}
impl Parse for Return {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
return_token: input.parse()?,
expr: (!input.peek(Token![;]))
.then(|| input.parse())
.transpose()?,
semi_token: input.parse()?
})
}
}

432
src/ast/ty.rs Normal file
View file

@ -0,0 +1,432 @@
use super::Generics;
use proc_macro2::{Ident, Span};
use std::{
borrow::Cow,
fmt::{self, Display, Formatter, Write as _},
hash::{Hash, Hasher}
};
use syn::{
parse::{Parse, ParseStream},
punctuated::Punctuated,
spanned::Spanned,
token::Paren,
Token
};
#[derive(Clone, Debug)]
pub struct TypePrimitive {
pub span: Span
}
impl Default for TypePrimitive {
fn default() -> Self {
Self {
span: Span::call_site()
}
}
}
impl Hash for TypePrimitive {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl Spanned for TypePrimitive {
fn span(&self) -> Span {
self.span
}
}
#[derive(Clone, Debug, Default)]
pub struct TypeTuple {
pub paren_token: Paren,
pub elems: Punctuated<Type, Token![,]>
}
impl PartialEq for TypeTuple {
fn eq(&self, other: &Self) -> bool {
let len = self.elems.len();
if len != other.elems.len() {
return false;
}
for i in 0 .. len {
if self.elems[i] != other.elems[i] {
return false;
}
}
true
}
}
impl Hash for TypeTuple {
fn hash<H: Hasher>(&self, state: &mut H) {
// Punctuated implements Hash by hashing the inner Vec,
// and Token![] types have an empty hash function
self.elems.hash(state);
}
}
impl Spanned for TypeTuple {
fn span(&self) -> Span {
self.paren_token.span
}
}
#[derive(Clone, Debug)]
pub struct TypePredef {
pub span: Span,
pub lt_token: Token![<],
pub inner_ty: Box<Type>,
pub gt_token: Token![>]
}
impl PartialEq for TypePredef {
fn eq(&self, other: &Self) -> bool {
self.inner_ty == other.inner_ty
}
}
impl Hash for TypePredef {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner_ty.hash(state);
}
}
impl Spanned for TypePredef {
fn span(&self) -> Span {
self.span
}
}
#[derive(Clone, Debug)]
pub struct TypeCustom {
pub ident: Ident,
pub generics: Option<Generics<Type>>
}
impl PartialEq for TypeCustom {
fn eq(&self, other: &Self) -> bool {
self.ident == other.ident && self.generics == other.generics
}
}
impl Hash for TypeCustom {
fn hash<H: Hasher>(&self, state: &mut H) {
// Ident implements Hash by hashing its string representation
self.ident.hash(state);
self.generics.hash(state);
}
}
impl Spanned for TypeCustom {
fn span(&self) -> Span {
self.ident.span()
}
}
#[derive(Clone, Debug, Hash)]
pub enum Type {
Bool(TypePrimitive),
Int(TypePrimitive),
Tuple(TypeTuple),
Rc(TypePredef),
Option(TypePredef),
Custom(TypeCustom),
/// This type is only used when expanding expressions, it is
/// never parsed from input.
Any
}
impl Display for Type {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
Self::Bool(_) => f.write_str("bool"),
Self::Int(_) => f.write_str("int"),
Self::Tuple(tuple) => {
f.write_str("(")?;
for (i, ty) in tuple.elems.iter().enumerate() {
if i > 0 {
f.write_str(" ")?;
}
write!(f, "{ty},")?;
}
f.write_str(")")
},
Self::Rc(rc) => write!(f, "Rc<{}>", rc.inner_ty),
Self::Option(opt) => write!(f, "Option<{}>", opt.inner_ty),
Self::Custom(custom) => {
write!(f, "{}", custom.ident)?;
if let Some(g) = &custom.generics {
write!(f, "{g}")?;
}
Ok(())
},
Self::Any => f.write_str("_")
}
}
}
impl Type {
/// Returns a mangled string representation of this type. Panics
/// if this type contains `_`.
pub fn mangled(&self) -> Cow<'static, str> {
match self {
Self::Bool(_) => "B".into(),
Self::Int(_) => "I".into(),
Self::Tuple(tuple) => {
let mut buf = format!("T{}", tuple.elems.len());
for ty in tuple.elems.iter() {
write!(buf, "_{}", ty.mangled()).unwrap();
}
buf.into()
},
Self::Rc(rc) => format!("R{}", rc.inner_ty.mangled()).into(),
Self::Option(opt) => format!("O{}", opt.inner_ty.mangled()).into(),
Self::Custom(custom) => {
let ident = custom.ident.to_string();
let mut buf = format!("C{}_{}", ident.len(), ident);
if let Some(g) = &custom.generics {
write!(buf, "_G{}", g.params.len()).unwrap();
for param in g.params.iter() {
write!(buf, "_{}", param.mangled()).unwrap()
}
}
buf.into()
},
Self::Any => panic!("Cannot mangle type containing _")
}
}
}
impl From<Type> for Cow<'_, Type> {
fn from(this: Type) -> Self {
Cow::Owned(this)
}
}
impl<'a> From<&'a Type> for Cow<'a, Type> {
fn from(this: &'a Type) -> Self {
Cow::Borrowed(this)
}
}
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Bool(_), Self::Bool(_)) | (Self::Int(_), Self::Int(_)) => true,
(Self::Tuple(this), Self::Tuple(other)) => this == other,
(Self::Rc(this), Self::Rc(other)) => this == other,
(Self::Option(this), Self::Option(other)) => this == other,
(Self::Custom(this), Self::Custom(other)) => this == other,
(Self::Any, _) | (_, Self::Any) => true,
_ => false
}
}
}
impl Eq for Type {}
impl Type {
/// Return the inner type for `Rc` and `Option`. Panics otherwise.
pub fn inner_ty(&self) -> &Type {
match self {
Self::Rc(this) | Self::Option(this) => &this.inner_ty,
_ => panic!("This type does not have an inner type")
}
}
/// Return the inner type for `Rc` and `Option`. Panics otherwise.
pub fn into_inner_ty(self) -> Type {
match self {
Self::Rc(this) | Self::Option(this) => *this.inner_ty,
_ => panic!("This type does not have an inner type")
}
}
/// Return the n-th inner type for tuples. Panics if self is not a tuple or
/// if n is out of bounds.
pub fn nth_ty(&self, n: usize) -> &Type {
match self {
Self::Tuple(tuple) => tuple.elems.iter().nth(n).expect("out of bounds"),
_ => panic!("This type is not a tuple")
}
}
}
impl Parse for Type {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
if input.peek(Paren) {
let content;
let paren_token = syn::parenthesized!(content in input);
return Ok(Self::Tuple(TypeTuple {
paren_token,
elems: Punctuated::parse_terminated(&content)?
}));
}
let ident: Ident = input.parse()?;
if ident == "bool" {
return Ok(Self::Bool(TypePrimitive { span: ident.span() }));
} else if ident == "int" {
return Ok(Self::Int(TypePrimitive { span: ident.span() }));
}
if !input.peek(Token![<]) {
if ident == "Rc" || ident == "Option" {
bail!(ident.span() => "Missing generic type parameter")
}
return Ok(Self::Custom(TypeCustom {
ident,
generics: None
}));
}
let generics: Generics<Type> = input.parse()?;
if ident == "Rc" || ident == "Option" {
let mut iter = generics.params.into_iter();
let inner_ty = Box::new(iter.next().unwrap());
if let Some(p) = iter.next() {
bail!(p.span() => "Too many generic type paramaters");
}
let predef = TypePredef {
span: ident.span(),
lt_token: generics.lt_token,
inner_ty,
gt_token: generics.gt_token
};
return Ok(if ident == "Rc" {
Self::Rc(predef)
} else {
Self::Option(predef)
});
}
Ok(Self::Custom(TypeCustom {
ident,
generics: Some(generics)
}))
}
}
impl Spanned for Type {
fn span(&self) -> Span {
match self {
Self::Bool(this) | Self::Int(this) => this.span(),
Self::Tuple(this) => this.span(),
Self::Rc(this) | Self::Option(this) => this.span(),
Self::Custom(this) => this.span(),
Self::Any => Span::call_site()
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn parse_bool() {
let ty: Type = syn::parse_str("bool").unwrap();
assert!(matches!(ty, Type::Bool(_)));
}
#[test]
fn parse_int() {
let ty: Type = syn::parse_str("int").unwrap();
assert!(matches!(ty, Type::Int(_)));
}
#[test]
fn parse_unit() {
let ty: Type = syn::parse_str("()").unwrap();
assert!(matches!(ty, Type::Tuple(_)));
match ty {
Type::Tuple(tuple) => assert!(tuple.elems.is_empty()),
_ => unreachable!()
}
}
#[test]
fn parse_tuple_1() {
let ty: Type = syn::parse_str("(int)").unwrap();
assert!(matches!(ty, Type::Tuple(_)));
match ty {
Type::Tuple(tuple) => {
let mut iter = tuple.elems.into_iter();
assert!(matches!(iter.next(), Some(Type::Int(_))));
assert!(iter.next().is_none());
},
_ => unreachable!()
}
}
#[test]
fn parse_tuple_2() {
let ty: Type = syn::parse_str("(int, Option<int>)").unwrap();
assert!(matches!(ty, Type::Tuple(_)));
match ty {
Type::Tuple(tuple) => {
let mut iter = tuple.elems.into_iter();
assert!(matches!(iter.next(), Some(Type::Int(_))));
assert!(matches!(iter.next(), Some(Type::Option(_))));
assert!(iter.next().is_none());
},
_ => unreachable!()
}
}
#[test]
fn parse_rc() {
let ty: Type = syn::parse_str("Rc<int>").unwrap();
assert!(matches!(ty, Type::Rc(_)));
match ty {
Type::Rc(rc) => assert!(matches!(*rc.inner_ty, Type::Int(_))),
_ => unreachable!()
}
}
#[test]
fn parse_option() {
let ty: Type = syn::parse_str("Option<int>").unwrap();
assert!(matches!(ty, Type::Option(_)));
match ty {
Type::Option(option) => assert!(matches!(*option.inner_ty, Type::Int(_))),
_ => unreachable!()
}
}
#[test]
fn parse_custom_without_generics() {
let ty: Type = syn::parse_str("Foo").unwrap();
assert!(matches!(ty, Type::Custom(_)));
match ty {
Type::Custom(custom) => {
assert_eq!(custom.ident, "Foo");
assert!(custom.generics.is_none());
},
_ => unreachable!()
}
}
#[test]
fn parse_custom_with_generics() {
let ty: Type = syn::parse_str("Foo<int>").unwrap();
assert!(matches!(ty, Type::Custom(_)));
match ty {
Type::Custom(custom) => {
assert_eq!(custom.ident, "Foo");
assert!(custom.generics.is_some());
match custom.generics {
Some(generics) => {
let mut iter = generics.params.into_iter();
assert!(matches!(iter.next(), Some(Type::Int(_))));
assert!(iter.next().is_none());
},
None => unreachable!()
}
},
_ => unreachable!()
}
}
}

44
src/ast/typedef.rs Normal file
View file

@ -0,0 +1,44 @@
use super::{Generics, Type};
use proc_macro2::{Ident, Span};
use syn::{
parse::{Parse, ParseStream},
spanned::Spanned,
Token
};
#[derive(Debug)]
pub struct TypeDef {
pub type_token: Token![type],
pub ident: Ident,
pub generics: Option<Generics<Ident>>,
pub eq_token: Token![=],
pub ty: Type,
pub semi_token: Token![;]
}
impl Parse for TypeDef {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
let type_token = input.parse()?;
let ident = input.parse()?;
let mut generics = None;
if input.peek(Token![<]) {
generics = Some(input.parse()?);
}
Ok(Self {
type_token,
ident,
generics,
eq_token: input.parse()?,
ty: input.parse()?,
semi_token: input.parse()?
})
}
}
impl Spanned for TypeDef {
fn span(&self) -> Span {
self.ident.span()
}
}

52
src/ast/while_loop.rs Normal file
View file

@ -0,0 +1,52 @@
use super::{Block, Condition};
use syn::{
parse::{Parse, ParseStream},
Token
};
#[derive(Clone, Debug)]
pub struct While {
pub while_token: Token![while],
pub condition: Condition,
pub loop_body: Block
}
impl Parse for While {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
while_token: input.parse()?,
condition: input.parse()?,
loop_body: input.parse()?
})
}
}
#[derive(Clone, Debug)]
pub struct Continue {
pub continue_token: Token![continue],
pub semi_token: Token![;]
}
impl Parse for Continue {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
continue_token: input.parse()?,
semi_token: input.parse()?
})
}
}
#[derive(Clone, Debug)]
pub struct Break {
pub break_token: Token![break],
pub semi_token: Token![;]
}
impl Parse for Break {
fn parse(input: ParseStream<'_>) -> syn::Result<Self> {
Ok(Self {
break_token: input.parse()?,
semi_token: input.parse()?
})
}
}

100
src/compile/assign.rs Normal file
View file

@ -0,0 +1,100 @@
use super::{expr::expand_expr_with_type, resolve_ty::resolve_tycustom, State};
use crate::{
ast::{Assignment, Expression, Type},
diagnostic::Diagnostic,
verbose
};
use std::{borrow::Cow, iter::Peekable};
use syn::{spanned::Spanned, Member};
fn cont<I>(
state: &State<'_>,
current_ty: &Type,
mut remaining: Peekable<I>,
expr: Expression
) -> Result<(Cow<'static, str>, Cow<'static, str>), Diagnostic>
where
I: Iterator<Item = Member>
{
if remaining.peek().is_none() {
if verbose() {
eprintln!("[assign] expanding assignment with type {current_ty} and expression {expr}");
}
let expr = expand_expr_with_type(state, current_ty, expr)?;
return Ok((" = __temp_value;".into(), expr));
}
Ok(match current_ty {
Type::Bool(_) => {
return Err(Diagnostic::no_such_field(
remaining.next().unwrap().span(),
"bool is a primitive type and does not have any fields"
));
},
Type::Int(_) => {
return Err(Diagnostic::no_such_field(
remaining.next().unwrap().span(),
"int is a primitive type and does not have any fields"
));
},
Type::Tuple(tuple) => {
let index = match remaining.next().unwrap() {
Member::Named(named) => {
return Err(Diagnostic::no_such_field(
named.span(),
"tuples don't have named fields"
)
.with_help("To access the first element in a tuple, use `.0`"));
},
Member::Unnamed(index) if index.index as usize >= tuple.elems.len() => {
return Err(Diagnostic::no_such_field(
index.span(),
"this tuple does not have enough elements"
)
.with_note("The first element in a tuple has index 0"));
},
Member::Unnamed(index) => index.index as usize
};
let (path, expr) = cont(state, current_ty.nth_ty(index), remaining, expr)?;
(format!(".{index}{path}").into(), expr)
},
Type::Rc(rc) => {
let (path, expr) = cont(state, &rc.inner_ty, remaining, expr)?;
(
format!(".with_mut(|__temp| {{ __temp{path} }});").into(),
expr
)
},
Type::Option(_) => {
return Err(Diagnostic::no_such_field(
remaining.next().unwrap().span(),
"Option does not have any fields"
));
},
Type::Custom(custom) => {
let ty = resolve_tycustom(state, custom)?;
return cont(state, &ty, remaining, expr);
},
Type::Any => unimplemented!()
})
}
pub(super) fn expand_assign(
state: &State<'_>,
assign: Assignment
) -> Result<String, Diagnostic> {
let mut lhs = assign.lhs.into_iter().peekable();
let first = match lhs.next().unwrap() {
Member::Named(ident) => ident,
Member::Unnamed(_) => unreachable!()
};
let first_ty = state.var(&first)?;
let (path, expr) = cont(state, first_ty, lhs, assign.rhs)?;
Ok(format!("{{ let __temp_value = {expr}; {first}{path} }}",))
}

View file

@ -0,0 +1,83 @@
use super::{
expand_block,
expect_ty::expect_option,
expr::{expand_expr, expand_expr_with_type},
State
};
use crate::{
ast::{Condition, ElseBranch, If, Type, TypePrimitive, While},
diagnostic::Diagnostic
};
use proc_macro2::Span;
use std::borrow::Cow;
use syn::Token;
pub(super) fn expand_condition(
state: &mut State<'_>,
condition: Condition,
if_span: Span
) -> Result<Cow<'static, str>, Diagnostic> {
Ok(match condition {
Condition::Bool(expr) => expand_expr_with_type(
state,
&Type::Bool(TypePrimitive { span: if_span }),
expr
)?,
Condition::LetSome {
some_span,
ident,
expr,
..
} => {
let expr = expand_expr(state, expr)?;
expect_option(&expr.ty, expr.expr_span, some_span)?;
let buf = format!("let ::stdlib::Option::Some(mut {ident}) = {}", expr.code);
let expr_ty = expr.ty.into_owned();
state.add_variable(ident, expr_ty.into_inner_ty());
buf.into()
}
})
}
fn expand_else_branch(
state: &State<'_>,
else_branch: Option<(Token![else], ElseBranch)>
) -> Result<String, Diagnostic> {
let mut buf = String::new();
if let Some((_, else_branch)) = else_branch {
buf += " else ";
match else_branch {
ElseBranch::If(else_if) => {
buf += "{";
buf += &expand_if(state, *else_if)?;
buf += "}";
},
ElseBranch::Block(block) => {
buf += &expand_block(state, block)?;
}
}
}
Ok(buf)
}
pub(super) fn expand_if(state: &State<'_>, if_expr: If) -> Result<String, Diagnostic> {
let mut sub = state.substate();
let condition = expand_condition(&mut sub, if_expr.condition, if_expr.if_token.span)?;
let block = expand_block(&sub, if_expr.then_branch)?;
let mut buf = format!("if {condition} {block}");
buf += &expand_else_branch(state, if_expr.else_branch)?;
Ok(buf)
}
pub(super) fn expand_while(
state: &State<'_>,
while_expr: While
) -> Result<String, Diagnostic> {
let mut sub = state.substate().into_loop();
let condition =
expand_condition(&mut sub, while_expr.condition, while_expr.while_token.span)?;
let block = expand_block(&sub, while_expr.loop_body)?;
Ok(format!("while {condition} {block}"))
}

19
src/compile/decl.rs Normal file
View file

@ -0,0 +1,19 @@
use super::{
expr::expand_expr_with_type, resolve_ty::resolve_ty, ty::expand_type, State
};
use crate::{ast::Declaration, diagnostic::Diagnostic};
pub(super) fn expand_decl(
state: &mut State<'_>,
decl: Declaration
) -> Result<String, Diagnostic> {
let expr = expand_expr_with_type(state, &decl.ty, decl.expr)?;
let ident = decl.ident;
let ty = expand_type(state, &decl.ty)?;
let buf = format!("let mut {ident}: {ty} = {expr};");
state.add_variable(
ident,
resolve_ty(&decl.ty, state.generic_types()).into_owned()
);
Ok(buf)
}

157
src/compile/expect_ty.rs Normal file
View file

@ -0,0 +1,157 @@
use crate::{ast::Type, diagnostic::Diagnostic};
use proc_macro2::Span;
use syn::spanned::Spanned as _;
pub(super) fn expect_ty(
expected_ty: &Type,
expression_ty: &Type,
expression_span: Span
) -> Result<(), Diagnostic> {
if expected_ty == expression_ty {
Ok(())
} else {
Err(Diagnostic::type_mismatch(
expected_ty,
expected_ty.span(),
expression_ty,
expression_span
))
}
}
pub(super) fn expect_bool(
expr_ty: &Type,
expr_span: Span,
why_span: Span
) -> Result<(), Diagnostic> {
match expr_ty {
Type::Bool(_) => Ok(()),
_ => Err(Diagnostic::type_mismatch(
"bool", why_span, expr_ty, expr_span
))
}
}
pub(super) fn expect_int(
expr_ty: &Type,
expr_span: Span,
why_span: Span
) -> Result<(), Diagnostic> {
match expr_ty {
Type::Int(_) => Ok(()),
_ => Err(Diagnostic::type_mismatch(
"int", why_span, expr_ty, expr_span
))
}
}
pub(super) fn expect_tuple_exact(
ty: &Type,
elems_len: usize,
expr_span: Span,
why_span: Span
) -> Result<(), Diagnostic> {
match ty {
Type::Tuple(tuple) if tuple.elems.len() == elems_len => Ok(()),
Type::Tuple(tuple) => Err(Diagnostic::new(
expr_span,
"Type Mismatch",
format!("but expression has {elems_len} elements")
)
.with_label(
ty.span(),
format!("Expected tuple with {} elements,", tuple.elems.len())
)),
_ => Err(Diagnostic::type_mismatch(
ty,
expr_span,
format!("({})", ", ".repeat(elems_len)),
why_span
))
}
}
pub(super) fn expect_tuple_at_least(
ty: &Type,
elems_len: usize,
expr_span: Span,
why_span: Span
) -> Result<(), Diagnostic> {
match ty {
Type::Tuple(tuple) if tuple.elems.len() >= elems_len => Ok(()),
Type::Tuple(tuple) => Err(Diagnostic::new(
expr_span,
"Type Mismatch",
format!("but expression requires at least {elems_len} elements")
)
.with_label(
ty.span(),
format!("Found tuple with {} elements,", tuple.elems.len())
)),
_ => Err(Diagnostic::type_mismatch(
format!("({}..)", "_, ".repeat(elems_len)),
why_span,
ty,
expr_span
))
}
}
pub(super) fn expect_rc(
expr_ty: &Type,
expr_span: Span,
why_span: Span
) -> Result<(), Diagnostic> {
match expr_ty {
Type::Rc(_) => Ok(()),
_ => Err(Diagnostic::type_mismatch(
"Rc<_>", why_span, expr_ty, expr_span
))
}
}
pub(super) fn expect_option(
expr_ty: &Type,
expr_span: Span,
why_span: Span
) -> Result<(), Diagnostic> {
match expr_ty {
Type::Option(_) => Ok(()),
_ => Err(Diagnostic::type_mismatch(
"Option<_>",
why_span,
expr_ty,
expr_span
))
}
}
pub(super) fn expect_rc_ctor(
expected_ty: &Type,
expr_span: Span
) -> Result<(), Diagnostic> {
match expected_ty {
Type::Rc(_) => Ok(()),
_ => Err(Diagnostic::type_mismatch(
expected_ty,
expected_ty.span(),
"Rc<_>",
expr_span
))
}
}
pub(super) fn expect_option_ctor(
expected_ty: &Type,
expr_span: Span
) -> Result<(), Diagnostic> {
match expected_ty {
Type::Option(_) => Ok(()),
_ => Err(Diagnostic::type_mismatch(
expected_ty,
expected_ty.span(),
"Option<_>",
expr_span
))
}
}

304
src/compile/expr.rs Normal file
View file

@ -0,0 +1,304 @@
use super::{
expect_ty::{
expect_bool, expect_int, expect_option_ctor, expect_rc, expect_rc_ctor,
expect_tuple_at_least, expect_tuple_exact, expect_ty
},
resolve_ty::{resolve_ty, resolve_tycustom},
State
};
use crate::{
ast::{Expression, Type, TypePredef, TypeTuple},
diagnostic::Diagnostic
};
use proc_macro2::Span;
use std::borrow::Cow;
use syn::{punctuated::Punctuated, spanned::Spanned};
pub(super) struct ExpandedExpression<'a> {
pub(super) code: Cow<'static, str>,
pub(super) expr_span: Span,
pub(super) ty: Cow<'a, Type>
}
impl<'a, C, S, T> From<(C, S, T)> for ExpandedExpression<'a>
where
C: Into<Cow<'static, str>>,
S: Spanned,
T: Into<Cow<'a, Type>>
{
fn from((code, expr_span, ty): (C, S, T)) -> Self {
Self {
code: code.into(),
expr_span: expr_span.span(),
ty: ty.into()
}
}
}
pub(super) fn expand_expr<'a>(
state: &'a State<'_>,
expr: Expression
) -> Result<ExpandedExpression<'a>, Diagnostic> {
let span = expr.span();
let expanded: ExpandedExpression<'a> = match expr {
Expression::Term { expr, .. } => {
let expr = expand_expr(state, *expr)?;
(format!("({})", expr.code), span, expr.ty).into()
},
Expression::Ident(ident) => (
format!("::core::clone::Clone::clone(&{ident})"),
span,
state.var(&ident)?
)
.into(),
Expression::Bool(lit) => {
let code = match lit.value {
true => "true",
false => "false"
};
(code, span, Type::Bool(Default::default())).into()
},
Expression::Literal(lit) => {
(lit.to_string(), span, Type::Int(Default::default())).into()
},
Expression::BoolNegation { not_token, expr } => {
let bool = Type::Bool(Default::default());
let expr = expand_expr(state, *expr)?;
expect_bool(&expr.ty, expr.expr_span, not_token.span)?;
(format!("!({})", expr.code), span, bool).into()
},
Expression::IntNegation { minus_token, expr } => {
let int = Type::Int(Default::default());
let expr = expand_expr(state, *expr)?;
expect_int(&expr.ty, expr.expr_span, minus_token.span)?;
(format!("-({})", expr.code), span, int).into()
},
Expression::Arithmetic { lhs, op, rhs } => {
let int = Type::Int(Default::default());
let lhs = expand_expr(state, *lhs)?;
expect_int(&lhs.ty, lhs.expr_span, op.span())?;
let rhs = expand_expr(state, *rhs)?;
expect_int(&rhs.ty, rhs.expr_span, op.span())?;
(format!("{} {op} {}", lhs.code, rhs.code), span, int).into()
},
Expression::Comparison { lhs, op, rhs } => {
let bool = Type::Bool(Default::default());
let lhs = expand_expr(state, *lhs)?;
expect_int(&lhs.ty, lhs.expr_span, op.span())?;
let rhs = expand_expr(state, *rhs)?;
expect_int(&rhs.ty, rhs.expr_span, op.span())?;
(format!("{} {op} {}", lhs.code, rhs.code), span, bool).into()
},
Expression::Conjunction { lhs, op, rhs } => {
let bool = Type::Bool(Default::default());
let lhs = expand_expr(state, *lhs)?;
expect_bool(&lhs.ty, lhs.expr_span, op.span())?;
let rhs = expand_expr(state, *rhs)?;
expect_bool(&rhs.ty, rhs.expr_span, op.span())?;
(format!("{} {op} {}", lhs.code, rhs.code), span, bool).into()
},
Expression::TupleCtor { paren_token, elems } => {
let mut ty_elems = Punctuated::new();
let mut buf = "(".to_owned();
for v in elems.into_iter() {
let elem = expand_expr(state, v)?;
buf += &elem.code;
buf += ", ";
ty_elems.push(elem.ty.into_owned());
}
buf += ")";
let tuple = Type::Tuple(TypeTuple {
paren_token,
elems: ty_elems
});
(buf, span, tuple).into()
},
Expression::TupleIndex { expr, index, .. } => {
let left = expand_expr(state, *expr)?;
let idx = index.index as usize;
expect_tuple_at_least(&left.ty, idx + 1, span, index.span())?;
(
format!("{}.{}", left.code, index.index),
span,
resolve_ty(left.ty.nth_ty(idx), state.generic_types()).into_owned()
)
.into()
},
Expression::RcCtor { span, expr, .. } => {
let inner = expand_expr(state, *expr)?;
let rc = Type::Rc(TypePredef {
span,
lt_token: Default::default(),
inner_ty: Box::new(
resolve_ty(&inner.ty, state.generic_types()).into_owned()
),
gt_token: Default::default()
});
(format!("::stdlib::Rc::new({})", inner.code), span, rc).into()
},
Expression::OptionCtor { span, expr: None } => {
let option = Type::Option(TypePredef {
span,
lt_token: Default::default(),
inner_ty: Box::new(Type::Any),
gt_token: Default::default()
});
("::stdlib::Option::None", span, option).into()
},
Expression::OptionCtor {
span,
expr: Some((_, expr))
} => {
let inner = expand_expr(state, *expr)?;
let option = Type::Option(TypePredef {
span,
lt_token: Default::default(),
inner_ty: Box::new(
resolve_ty(&inner.ty, state.generic_types()).into_owned()
),
gt_token: Default::default()
});
(
format!("::stdlib::Option::Some({})", inner.code),
span,
option
)
.into()
},
Expression::Deref { star_token, expr } => {
let rc = expand_expr(state, *expr)?;
expect_rc(&rc.ty, rc.expr_span, star_token.span)?;
(
format!("::stdlib::Rc::into_inner({})", rc.code),
span,
rc.ty.inner_ty().to_owned()
)
.into()
},
Expression::FnCall {
ident,
generics,
inputs,
..
} => {
let fun = state.fun(&ident, &generics)?;
let fun_inputs_len = fun.inputs_len();
let inputs_len = inputs.len();
if fun_inputs_len > inputs_len {
return Err(Diagnostic::new(
ident.span(),
"Too few arguments",
format!("but only {inputs_len} arguments were supplied")
)
.with_label(
fun.span(),
format!("This function takes {fun_inputs_len} arguments,")
));
}
let mut buf = format!("{}(", fun.ident);
for (input_ty, input_expr) in fun.inputs().zip(inputs.iter()) {
let resolved_ty = resolve_ty(&input_ty, state.generic_types());
buf += &expand_expr_with_type(state, &resolved_ty, input_expr.clone())?;
buf += ", ";
}
buf += ")";
(buf, span, fun.output().into_owned()).into()
}
};
let expanded_ty = resolve_ty(&expanded.ty, state.generic_types());
let expanded_ty: &Type = &expanded_ty;
Ok(if matches!(expanded_ty, Type::Custom(_)) {
let mut ty = expanded.ty.into_owned();
let mut buf = expanded.code.into_owned();
while let Type::Custom(custom) = ty {
buf = format!("{}::into_inner({buf})", custom.ident);
ty = resolve_tycustom(state, &custom)?.into_owned();
}
(buf, expanded.expr_span, ty).into()
} else {
expanded
})
}
pub(super) fn expand_expr_with_type(
state: &State<'_>,
ty: &Type,
expr: Expression
) -> Result<Cow<'static, str>, Diagnostic> {
let ty = resolve_ty(ty, state.generic_types());
let ty: &Type = &ty;
// special case: custom outer type, requires wrapping in new
if let Type::Custom(custom) = ty {
let inner_ty = resolve_tycustom(state, custom)?;
let ident = &custom.ident;
let expr = expand_expr_with_type(state, &inner_ty, expr)?;
return Ok(format!("{ident}::new({expr})").into());
}
Ok(match expr {
// special case: possibly custom type(s) inside a term
Expression::Term { expr, .. } => {
let expr = expand_expr_with_type(state, ty, *expr)?;
format!("({expr})").into()
},
// special case: possibly custom type(s) inside a tuple
Expression::TupleCtor {
paren_token, elems, ..
} => {
expect_tuple_exact(ty, elems.len(), paren_token.span, ty.span())?;
let mut buf = "(".to_owned();
for (n, v) in elems.into_iter().enumerate() {
let elem = expand_expr_with_type(state, ty.nth_ty(n), v)?;
buf += &elem;
buf += ", ";
}
buf += ")";
buf.into()
},
// special case: possibly custom type inside an Rc
Expression::RcCtor { span, expr, .. } => {
expect_rc_ctor(ty, span)?;
let inner = expand_expr_with_type(state, ty.inner_ty(), *expr)?;
format!("::stdlib::Rc::new({inner})").into()
},
// special case: possibly custom type inside a Some
Expression::OptionCtor {
span,
expr: Some((_, expr))
} => {
expect_option_ctor(ty, span)?;
let inner = expand_expr_with_type(state, ty.inner_ty(), *expr)?;
format!("::stdlib::Option::Some({inner})").into()
},
// otherwise, just expand the expression and expect its type
_ => {
let expr = expand_expr(state, expr)?;
expect_ty(ty, &expr.ty, expr.expr_span)?;
expr.code
}
})
}

66
src/compile/function.rs Normal file
View file

@ -0,0 +1,66 @@
use super::{expand_stmt, resolve_ty::resolve_ty, ty::expand_type, State};
use crate::{
ast::{Fn, Generics, Type, TypeTuple},
diagnostic::Diagnostic
};
use std::fmt::Write as _;
pub(super) fn expand_function(
buf: &mut String,
state: &State<'_>,
fun: &Fn,
generics: Option<Generics<Type>>,
ident: String
) -> Result<(), Diagnostic> {
let mut sub = state.substate();
if let Some((fun_generics, inst_generics)) = fun
.generics
.as_ref()
.and_then(|fg| generics.as_ref().map(|ig| (fg, ig)))
{
for (ident, ty) in fun_generics.params.iter().zip(inst_generics.params.iter()) {
sub.add_ty(ident.clone(), ty.clone());
}
}
// function signature
*buf += "#[allow(non_snake_case)]\n";
write!(buf, "fn {ident}").unwrap();
*buf += "(";
for arg in &fun.inputs {
write!(buf, "mut {}: {}, ", arg.ident, expand_type(&sub, &arg.ty)?).unwrap();
sub.add_variable(
arg.ident.clone(),
resolve_ty(&arg.ty, sub.generic_types()).into_owned()
);
}
*buf += ")";
if let Some((_, out)) = fun.output.as_ref() {
write!(buf, " -> {}", expand_type(&sub, out)?).unwrap();
sub.set_return_ty(out.clone());
} else {
sub.set_return_ty(Type::Tuple(TypeTuple::default()))
}
// function body
*buf += " {\n";
for s in &fun.block.stmts {
*buf += &expand_stmt(&mut sub, s.clone())?;
}
if fun.output.is_some() {
*buf += "\n#[allow(unreachable_code)]\n{\n";
write!(buf, "::stdlib::fn_no_return({:?})", fun.ident.to_string()).unwrap();
for arg in &fun.inputs {
write!(
buf,
".add_input(::core::stringify!({ident}), &{ident})",
ident = arg.ident
)
.unwrap();
}
*buf += ".panic()\n}\n";
}
*buf += "}\n";
Ok(())
}

276
src/compile/mod.rs Normal file
View file

@ -0,0 +1,276 @@
use crate::{
ast::{Block, Expression, Program, Return, Statement, Type},
diagnostic::Diagnostic,
verbose
};
use askama::Template;
use std::{borrow::Cow, fmt::Write as _};
mod assign;
mod conditional;
mod decl;
mod expect_ty;
mod expr;
mod function;
mod resolve_ty;
mod state;
mod ty;
use assign::expand_assign;
use conditional::{expand_if, expand_while};
use decl::expand_decl;
use expect_ty::expect_tuple_exact;
use expr::expand_expr_with_type;
use function::expand_function;
use state::State;
use ty::expand_type;
pub enum CompileResult {
Ok {
main_rs: syn::File,
funs_rs: syn::File,
types_rs: syn::File
},
InternalErr(String, syn::Error)
}
mod tpl {
use crate::ast::{Generics, Output};
use askama::Template;
use proc_macro2::Ident;
use std::borrow::Cow;
mod filters {
use std::fmt::Display;
pub fn if_some<T: Display>(opt: &Option<T>) -> askama::Result<String> {
Ok(if let Some(value) = opt.as_ref() {
value.to_string()
} else {
String::new()
})
}
}
#[derive(Template)]
#[template(path = "main.rs.j2", escape = "none")]
pub(super) struct MainRs<'a> {
pub(super) inputs: Vec<Input<'a>>,
pub(super) outputs: &'a [Output]
}
#[derive(Template)]
#[template(path = "types.rs.j2", escape = "none")]
pub(super) struct TypesRs<'a> {
pub(super) typedefs: Vec<TypeDef<'a>>
}
pub(super) struct TypeDef<'a> {
pub(super) ident: &'a Ident,
pub(super) generics: Option<&'a Generics<Ident>>,
pub(super) inner_ty: Cow<'static, str>
}
impl<'a> TypeDef<'a> {
pub(super) fn new(
ident: &'a Ident,
generics: Option<&'a Generics<Ident>>,
inner_ty: Cow<'static, str>
) -> Self {
Self {
ident,
generics,
inner_ty
}
}
}
pub(super) struct Input<'a> {
pub(super) ident: &'a Ident,
short_long: &'static str,
quote: char
}
impl<'a> Input<'a> {
pub(super) fn new(ident: &'a Ident) -> Self {
let (short_long, quote) = match ident.to_string() {
str if str.len() == 1 && str != "h" && str != "n" && str != "v" => {
("short", '\'')
},
_ => ("long", '"')
};
Self {
ident,
short_long,
quote
}
}
}
}
#[allow(clippy::write_with_newline)]
pub fn compile(input: &str) -> Result<CompileResult, Diagnostic> {
let program: Program = syn::parse_str(input)?;
let mut state = State::new(program.types, program.functions);
let main_rs = tpl::MainRs {
inputs: program
.inputs
.iter()
.map(|arg| tpl::Input::new(&arg.ident))
.collect(),
outputs: &program.outputs
};
let main_rs = main_rs.render().unwrap();
let types_rs = tpl::TypesRs {
typedefs: state
.types()
.into_iter()
.map(|ty| {
Ok(tpl::TypeDef::new(
&ty.ident,
ty.generics.as_ref(),
expand_type(&state, &ty.ty)?
))
})
.collect::<Result<Vec<_>, Diagnostic>>()?
};
let types_rs = types_rs.render().unwrap();
let mut buf = String::new();
buf += "#![no_implicit_prelude]\n";
buf += "#![allow(unused_mut, while_true)]\n\n";
buf += "use crate::types::*;\n\n";
buf += "pub(crate) fn run(crate::Values {\n";
for input in program.inputs {
write!(buf, " mut {},\n", input.ident).unwrap();
state.add_variable(input.ident, Type::Int(Default::default()));
}
buf += "}: crate::Values) -> crate::Output {\n";
for s in program.stmts {
buf += " ";
buf += &expand_stmt(&mut state, s)?;
buf += "\n";
}
buf += " crate::Output {\n";
for output in program.outputs {
for ident in output.idents {
write!(buf, " {ident}: ").unwrap();
buf += &expand_expr_with_type(
&state,
&Type::Int(Default::default()),
Expression::Ident(ident)
)?;
buf += ",\n";
}
}
buf += " }\n";
buf += "}\n\n";
while let Some(idx) = state.next_function_instance() {
state.with_function_instance(idx, |fun, generics, ident| {
if verbose() {
eprint!("[compile] Expanding function instance {ident} ...");
}
let res = expand_function(&mut buf, &state, fun, generics, ident);
if verbose() {
eprintln!("done");
}
res
})?;
}
Ok(CompileResult::Ok {
main_rs: match syn::parse_file(&main_rs) {
Ok(main_rs) => main_rs,
Err(err) => return Ok(CompileResult::InternalErr(main_rs, err))
},
funs_rs: match syn::parse_file(&buf) {
Ok(funs_rs) => funs_rs,
Err(err) => return Ok(CompileResult::InternalErr(buf, err))
},
types_rs: match syn::parse_file(&types_rs) {
Ok(types_rs) => types_rs,
Err(err) => return Ok(CompileResult::InternalErr(types_rs, err))
}
})
}
fn expand_stmt(
state: &mut State<'_>,
stmt: Statement
) -> Result<Cow<'static, str>, Diagnostic> {
Ok(match stmt {
Statement::Decl(decl) => expand_decl(state, decl)?.into(),
Statement::Assign(assign) => expand_assign(state, assign)?.into(),
Statement::If(if_expr) => expand_if(state, if_expr)?.into(),
Statement::CoinFlip { head, prob, tail } => {
let mut buf = format!("match ::stdlib::Coin::flip({}) {{", prob);
buf += "::stdlib::Coin::Head => ";
buf += &expand_block(state, head)?;
buf += ", ::stdlib::Coin::Tail => ";
buf += &expand_block(state, tail)?;
buf += "}";
buf.into()
},
Statement::While(while_expr) => expand_while(state, while_expr)?.into(),
Statement::Block(block) => expand_block(state, block)?.into(),
Statement::Return(Return {
return_token, expr, ..
}) => {
let return_ty = state.return_ty().ok_or_else(|| {
Diagnostic::new(
return_token.span,
"Cannot return",
"Return statements may only be used in function bodies"
)
})?;
match expr {
Some(expr) => {
format!("return {};", expand_expr_with_type(state, return_ty, expr)?)
.into()
},
None => {
expect_tuple_exact(
return_ty,
0,
return_token.span,
return_token.span
)?;
"return;".into()
}
}
},
Statement::Continue(cont) => {
if !state.is_loop() {
return Err(Diagnostic::new(
cont.continue_token.span,
"Not a loop",
"You may only use `continue` in a loop body"
));
}
"continue;".into()
},
Statement::Break(brake) => {
if !state.is_loop() {
return Err(Diagnostic::new(
brake.break_token.span,
"Not a loop",
"You may only use `break` in a loop body"
));
}
"break;".into()
}
})
}
fn expand_block(state: &State<'_>, block: Block) -> Result<String, Diagnostic> {
let mut sub = state.substate();
let mut buf = "{".to_owned();
for s in block.stmts {
buf += &expand_stmt(&mut sub, s)?;
}
buf += "}";
Ok(buf)
}

126
src/compile/resolve_ty.rs Normal file
View file

@ -0,0 +1,126 @@
use super::State;
use crate::{
ast::{Generics, Type, TypeCustom, TypePredef, TypeTuple},
diagnostic::Diagnostic
};
use proc_macro2::Ident;
use std::{borrow::Cow, collections::HashMap};
pub(super) trait GenericsMap {
type Out;
fn get(&self, ident: &Ident) -> Option<Self::Out>;
}
impl<'a> GenericsMap for (&Generics<Ident>, &'a Generics<Type>) {
type Out = &'a Type;
fn get(&self, ident: &Ident) -> Option<Self::Out> {
self.0
.params
.iter()
.position(|param| param == ident)
.map(|index| self.1.params.iter().nth(index).unwrap())
}
}
impl<'a> GenericsMap for &'a HashMap<Ident, Type> {
type Out = &'a Type;
fn get(&self, ident: &Ident) -> Option<Self::Out> {
HashMap::get(self, ident)
}
}
impl<T: GenericsMap> GenericsMap for Option<T> {
type Out = T::Out;
fn get(&self, ident: &Ident) -> Option<Self::Out> {
self.as_ref().and_then(|this| this.get(ident))
}
}
impl<T, U> GenericsMap for (T, U)
where
T: GenericsMap,
U: GenericsMap<Out = T::Out>
{
type Out = T::Out;
fn get(&self, ident: &Ident) -> Option<Self::Out> {
self.0.get(ident).or_else(|| self.1.get(ident))
}
}
fn resolve_typredef<'a, T>(ty: &'a TypePredef, generics: T) -> TypePredef
where
T: GenericsMap<Out = &'a Type> + Copy
{
TypePredef {
span: ty.span,
lt_token: ty.lt_token,
inner_ty: Box::new(resolve_ty(&ty.inner_ty, generics).into_owned()),
gt_token: ty.gt_token
}
}
pub(super) fn resolve_ty<'a, T>(ty: &'a Type, generics: T) -> Cow<'a, Type>
where
T: GenericsMap<Out = &'a Type> + Copy
{
match ty {
Type::Bool(_) | Type::Int(_) => ty.into(),
Type::Tuple(tuple) => Type::Tuple(TypeTuple {
paren_token: tuple.paren_token,
elems: tuple
.elems
.iter()
.map(|elem| resolve_ty(elem, generics).into_owned())
.collect()
})
.into(),
Type::Rc(rc) => Type::Rc(resolve_typredef(rc, generics)).into(),
Type::Option(option) => Type::Option(resolve_typredef(option, generics)).into(),
Type::Custom(custom) => {
if let Some(resolved_ty) = generics.get(&custom.ident) {
resolved_ty.into()
} else {
Type::Custom(TypeCustom {
ident: custom.ident.clone(),
generics: custom.generics.as_ref().map(|g| Generics {
lt_token: g.lt_token,
params: g
.params
.iter()
.map(|ty| resolve_ty(ty, generics).into_owned())
.collect(),
gt_token: g.gt_token
})
})
.into()
}
},
Type::Any => Type::Any.into()
}
}
pub(super) fn resolve_tycustom<'a, 'b: 'a>(
state: &'a State<'b>,
custom: &'a TypeCustom
) -> Result<Cow<'a, Type>, Diagnostic> {
if custom.generics.is_none() {
if let Some(resolved_ty) = state.generic_types().get(&custom.ident) {
return Ok(resolved_ty.into());
}
}
let inner_ty = state.ty(&custom.ident)?;
Ok(resolve_ty(
&inner_ty.ty,
inner_ty.generics.as_ref().and_then(|def_generics| {
custom
.generics
.as_ref()
.map(|generics| (def_generics, generics))
})
))
}

399
src/compile/state.rs Normal file
View file

@ -0,0 +1,399 @@
use super::resolve_ty::{resolve_ty, GenericsMap};
use crate::{
ast::{Fn, Generics, Type, TypeDef, TypeTuple},
diagnostic::Diagnostic,
verbose
};
use indexmap::IndexMap;
use proc_macro2::{Ident, Span};
use std::{
borrow::Cow,
cell::RefCell,
collections::{HashMap, HashSet},
fmt::Write as _
};
use syn::spanned::Spanned;
fn mangled_fn_name(function: &Fn, generics: &Option<Generics<Type>>) -> String {
let mut ident = format!("fun_{}", function.ident);
if let Some(g) = generics {
for ty in &g.params {
write!(ident, "_{}", ty.mangled()).unwrap();
}
}
ident
}
struct FunctionMap {
/// A list of function definitions.
functions: Vec<Fn>,
/// A mapping of function identifier to the function definition index.
idents: HashMap<Ident, usize>,
/// A list of generics for this function that are being used with the
/// mangled name of the function.
used_generics: RefCell<Vec<IndexMap<Option<Generics<Type>>, String>>>,
/// A list of function instances that have been emitted already.
emitted_generics: Vec<HashSet<usize>>
}
impl FunctionMap {
/// Create a new function map with these available functions of which
/// none have any instances that are being used.
fn new(functions: Vec<Fn>) -> Self {
let idents = functions
.iter()
.enumerate()
.map(|(i, fun)| (fun.ident.clone(), i))
.collect();
let len = functions.len();
Self {
functions,
idents,
used_generics: RefCell::new(vec![IndexMap::new(); len]),
emitted_generics: vec![HashSet::new(); len]
}
}
/// Create a new, empty function map.
fn new_empty() -> Self {
Self {
functions: Vec::new(),
idents: HashMap::new(),
used_generics: RefCell::new(Vec::new()),
emitted_generics: Vec::new()
}
}
/// Get an instance of a function and return its mangled name. If no
/// function with said ident exists, `None` is being returned.
fn get(
&self,
ident: &Ident,
generics: &Option<Generics<Type>>
) -> Option<(&Fn, String)> {
let idx = *self.idents.get(ident)?;
if !self
.used_generics
.borrow()
.get(idx)
.unwrap()
.contains_key(generics)
{
self.used_generics
.borrow_mut()
.get_mut(idx)
.unwrap()
.insert(
generics.clone(),
mangled_fn_name(&self.functions[idx], generics)
);
}
Some((
&self.functions[idx],
self.used_generics
.borrow()
.get(idx)
.unwrap()
.get(generics)
.unwrap()
.clone()
))
}
}
pub(super) struct FunctionIndex {
fun_idx: usize,
inst_idx: usize
}
pub(super) struct State<'a> {
/// The parent state, or `None`.
parent: Option<&'a State<'a>>,
/// True if this substate represents a loop body.
is_loop: bool,
/// A mapping of custom type identifier to the corresponding typedef.
types: HashMap<Ident, TypeDef>,
/// A mapping of generic ident to concrete instance. Used only for
/// function.
generic_types: HashMap<Ident, Type>,
/// When the state represents a function, this stores the return
/// type of the function.
return_ty: Option<Type>,
/// All the functions, which is a bit more complex since we need to
/// track the generic instances that are being used in the program.
functions: FunctionMap,
/// A mapping of variable identifier to the type of the variable.
variables: HashMap<Ident, Type>
}
impl State<'static> {
pub(super) fn new(types: Vec<TypeDef>, functions: Vec<Fn>) -> Self {
Self {
parent: None,
is_loop: false,
types: types.into_iter().map(|ty| (ty.ident.clone(), ty)).collect(),
generic_types: HashMap::new(),
return_ty: None,
functions: FunctionMap::new(functions),
variables: HashMap::new()
}
}
pub(super) fn types(&self) -> impl Iterator<Item = &TypeDef> {
if self.parent.is_some() {
panic!("This function may only be called on the root state");
}
self.types.values()
}
pub(super) fn next_function_instance(&mut self) -> Option<FunctionIndex> {
if self.parent.is_some() {
panic!("This function may only be called on the root state");
}
for (fun_idx, instances) in
self.functions.used_generics.get_mut().iter().enumerate()
{
for (inst_idx, _) in instances.iter().enumerate() {
if self.functions.emitted_generics[fun_idx].contains(&inst_idx) {
continue;
}
self.functions.emitted_generics[fun_idx].insert(inst_idx);
return Some(FunctionIndex { fun_idx, inst_idx });
}
}
None
}
pub(super) fn with_function_instance<F, T>(
&self,
idx: FunctionIndex,
callback: F
) -> T
where
F: FnOnce(&Fn, Option<Generics<Type>>, String) -> T
{
if self.parent.is_some() {
panic!("This function may only be called on the root state");
}
let used_generics = self.functions.used_generics.borrow();
let fun = &self.functions.functions[idx.fun_idx];
let (generics, ident) = used_generics
.get(idx.fun_idx)
.unwrap()
.get_index(idx.inst_idx)
.unwrap();
let generics = generics.clone();
let ident = ident.clone();
// since the callback might need to add another function instance
// or do a lookup, we cannot keep the borrow past this point.
drop(used_generics);
callback(fun, generics, ident)
}
}
impl<'a> State<'a> {
pub(super) fn is_loop(&self) -> bool {
self.is_loop || self.parent.map(State::is_loop).unwrap_or(false)
}
pub(super) fn into_loop(mut self) -> Self {
self.is_loop = true;
self
}
pub(super) fn ty(&self, ident: &Ident) -> Result<&TypeDef, Diagnostic> {
if let Some(ty) = self.types.get(ident) {
return Ok(ty);
}
match self.parent {
Some(parent) => parent.ty(ident),
None => Err(Diagnostic::new(
ident.span(),
"Unknown type",
"This type has not been defined"
))
}
}
fn has_generic_types(&self) -> bool {
!self.generic_types.is_empty()
|| self
.parent
.map(|parent| parent.has_generic_types())
.unwrap_or(false)
}
pub(super) fn generic_types(&self) -> &HashMap<Ident, Type> {
if !self.generic_types.is_empty() {
return &self.generic_types;
}
match self.parent.as_ref() {
Some(parent) => parent.generic_types(),
None => &self.generic_types
}
}
pub(super) fn add_ty(&mut self, ident: Ident, ty: Type) {
if self.generic_types.is_empty() {
// ensure that only one (sub)state in the chain has generic types
if let Some(parent) = self.parent.as_ref() {
if parent.has_generic_types() {
panic!("One of the parent state already has generic types");
}
}
}
self.generic_types.insert(ident, ty);
}
pub(super) fn return_ty(&self) -> Option<&Type> {
self.return_ty
.as_ref()
.or_else(|| self.parent.and_then(|parent| parent.return_ty()))
}
pub(super) fn set_return_ty(&mut self, ty: Type) {
if self.return_ty().is_some() {
panic!(
"This state or one of its parent states already has a return type set"
);
}
self.return_ty = Some(ty);
}
pub(super) fn fun(
&self,
ident: &Ident,
generics: &Option<Generics<Type>>
) -> Result<FunctionInstance<'_>, Diagnostic> {
let generics = generics.as_ref().map(|generics| Generics {
lt_token: generics.lt_token,
params: generics
.params
.iter()
.map(|param| resolve_ty(param, self.generic_types()).into_owned())
.collect(),
gt_token: generics.gt_token
});
if let Some((fun, fun_ident)) = self.functions.get(ident, &generics) {
match (fun.generics.as_ref(), generics.as_ref()) {
(None, None) => {},
(Some(fun_generics), Some(generics))
if fun_generics.params.len() == generics.params.len() => {},
_ => return Err(Diagnostic::new(
ident.span(),
"Mismatched generics",
"The generics at the call site differ with the function declaration"
))
};
return Ok(FunctionInstance {
fun,
generics,
ident: fun_ident
});
}
match self.parent {
Some(parent) => parent.fun(ident, &generics),
None => Err(Diagnostic::new(
ident.span(),
"Unknown function",
"This function has not been defined"
))
}
}
pub(super) fn var(&self, ident: &Ident) -> Result<&Type, Diagnostic> {
if let Some(ty) = self.variables.get(ident) {
return Ok(ty);
}
match self.parent {
Some(parent) => parent.var(ident),
None => Err(Diagnostic::new(
ident.span(),
"No such variable",
"Variable not defined in scope"
))
}
}
pub(super) fn add_variable(&mut self, ident: Ident, ty: Type) {
if verbose() {
eprintln!("[state] adding variable `{ident}` with type `{ty}`");
}
self.variables.insert(ident, ty);
}
pub(super) fn substate<'b>(&'b self) -> State<'b>
where
'a: 'b
{
State {
parent: Some(self),
is_loop: false,
types: HashMap::new(),
generic_types: HashMap::new(),
return_ty: None,
functions: FunctionMap::new_empty(),
variables: HashMap::new()
}
}
}
pub(super) struct FunctionInstance<'a> {
fun: &'a Fn,
generics: Option<Generics<Type>>,
pub ident: String
}
impl Spanned for FunctionInstance<'_> {
fn span(&self) -> Span {
self.fun.ident.span()
}
}
impl FunctionInstance<'_> {
pub(super) fn inputs_len(&self) -> usize {
self.fun.inputs.len()
}
fn generics(&self) -> impl GenericsMap<Out = &Type> + Copy {
self.fun
.generics
.as_ref()
.and_then(|fg| self.generics.as_ref().map(|ig| (fg, ig)))
}
pub(super) fn inputs(&self) -> impl Iterator<Item = Cow<'_, Type>> {
let generics = self.generics();
self.fun
.inputs
.iter()
.map(move |arg| resolve_ty(&arg.ty, generics))
}
pub(super) fn output(&self) -> Cow<'_, Type> {
let generics = self.generics();
self.fun
.output
.as_ref()
.map(|(_, ty)| resolve_ty(ty, generics))
.unwrap_or_else(|| Type::Tuple(TypeTuple::default()).into())
}
}

61
src/compile/ty.rs Normal file
View file

@ -0,0 +1,61 @@
use super::State;
use crate::{ast::Type, diagnostic::Diagnostic};
use std::borrow::Cow;
pub(super) fn expand_type(
state: &State<'_>,
ty: &Type
) -> Result<Cow<'static, str>, Diagnostic> {
Ok(match ty {
Type::Bool(_) => "::stdlib::bool".into(),
Type::Int(_) => "::stdlib::int".into(),
Type::Tuple(tuple) => {
let mut buf = "(".to_owned();
for elem in &tuple.elems {
buf += &expand_type(state, elem)?;
buf += ", "
}
buf += ")";
buf.into()
},
Type::Rc(rc) => {
format!("::stdlib::Rc<{}>", expand_type(state, &rc.inner_ty)?).into()
},
Type::Option(option) => format!(
"::stdlib::Option<{}>",
expand_type(state, &option.inner_ty)?
)
.into(),
Type::Custom(custom) => {
if let Some(ty) = state.generic_types().get(&custom.ident) {
if let Some(g) = custom.generics.as_ref() {
return Err(Diagnostic::new(
g.lt_token.span,
"Invalid Type",
"This generic type parameter cannot accept generics"
));
}
expand_type(state, ty)?
} else {
let mut buf = format!("{}", custom.ident);
if let Some(g) = &custom.generics {
buf += "::<";
for (i, ty) in g.params.iter().enumerate() {
if i > 0 {
buf += ", ";
}
buf += &expand_type(state, ty)?;
}
buf += ">";
}
buf.into()
}
},
Type::Any => unimplemented!()
})
}

220
src/diagnostic.rs Normal file
View file

@ -0,0 +1,220 @@
use ariadne::{Label, Report, ReportKind};
use proc_macro2::{LineColumn, Span};
use std::{
fmt::{Debug, Display},
io::{self, Write}
};
#[derive(Clone, Copy, Eq, PartialEq)]
pub struct Source<'a> {
pub code: &'a str,
pub filename: Option<&'a str>
}
impl<'a> Source<'a> {
pub fn new(code: &'a str) -> Self {
Self {
code,
filename: None
}
}
pub fn with_filename(mut self, filename: &'a str) -> Self {
self.filename = Some(filename);
self
}
fn offset(&self, at: LineColumn) -> usize {
let line_offset: usize = self
.code
.split('\n')
.take(at.line - 1)
.map(|line| line.len() + 1)
.sum();
line_offset + at.column
}
}
pub struct SourceSpan<'a> {
source: Source<'a>,
start: usize,
end: usize
}
impl<'a> ariadne::Span for SourceSpan<'a> {
type SourceId = Source<'a>;
fn source(&self) -> &Self::SourceId {
&self.source
}
fn start(&self) -> usize {
self.start
}
fn end(&self) -> usize {
self.end
}
}
struct SourceCache {
source: ariadne::Source
}
impl SourceCache {
fn new(source: Source<'_>) -> Self {
Self {
source: ariadne::Source::from(source.code)
}
}
}
impl ariadne::Cache<Source<'_>> for SourceCache {
fn fetch(&mut self, _: &Source<'_>) -> Result<&ariadne::Source, Box<dyn Debug>> {
Ok(&self.source)
}
fn display<'a>(&self, id: &'a Source<'_>) -> Option<Box<dyn Display + 'a>> {
id.filename
.map(|filename| Box::new(filename) as Box<dyn Display + 'a>)
}
}
pub struct Diagnostic {
span: Span,
msg: String,
labels: Vec<(Span, String)>,
note: Option<String>,
help: Option<String>
}
impl Diagnostic {
pub fn new<T, M>(span: Span, topic: T, msg: M) -> Self
where
T: Into<String>,
M: Into<String>
{
Self {
span,
msg: topic.into(),
labels: vec![(span, msg.into())],
note: None,
help: None
}
}
pub fn with_label<M>(mut self, span: Span, msg: M) -> Self
where
M: Into<String>
{
self.labels.push((span, msg.into()));
self
}
pub fn with_note<N>(mut self, note: N) -> Self
where
N: Into<String>
{
self.note = Some(note.into());
self
}
pub fn with_help<H>(mut self, help: H) -> Self
where
H: Into<String>
{
self.help = Some(help.into());
self
}
pub fn no_such_field<M>(span: Span, msg: M) -> Self
where
M: Into<String>
{
Self::new(span, "No such field", msg)
}
pub fn type_mismatch<T, U>(
expected_ty: T,
expected_span: Span,
expression_ty: U,
expression_span: Span
) -> Self
where
T: Display,
U: Display
{
Self {
span: expression_span,
msg: "Type Mismatch".into(),
labels: if expected_span.start() < expression_span.start() {
vec![
(expected_span, format!("Expected type `{expected_ty}`,")),
(
expression_span,
format!("but expression is of type `{expression_ty}`")
),
]
} else {
vec![
(
expression_span,
format!("Expression is of type `{expression_ty}`,")
),
(expected_span, format!("but expected type `{expected_ty}`")),
]
},
note: None,
help: None
}
}
pub fn ice(err: syn::Error) -> Self {
Self::new(err.span(), "Internal Compiler Error", err.to_string()).with_note(
"The compiler produced invalid intermediate Rust code. This is a bug."
)
}
}
impl From<syn::Error> for Diagnostic {
fn from(err: syn::Error) -> Self {
Self::new(err.span(), "Syntax Error", err.to_string())
}
}
impl Diagnostic {
fn into_report(self, source: Source<'_>) -> Report<SourceSpan<'_>> {
let start_offset = source.offset(self.span.start());
let mut report =
Report::build(ReportKind::Error, source, start_offset).with_message(self.msg);
for (label_span, label_msg) in self.labels {
report.add_label(
Label::new(SourceSpan {
source,
start: source.offset(label_span.start()),
end: source.offset(label_span.end())
})
.with_message(label_msg)
);
}
if let Some(note) = self.note {
report.set_note(note);
}
if let Some(help) = self.help {
report.set_help(help);
}
report.finish()
}
pub fn write<W>(self, source: Source<'_>, w: W) -> io::Result<()>
where
W: Write
{
self.into_report(source).write(SourceCache::new(source), w)
}
pub fn eprint(self, source: Source<'_>) -> io::Result<()> {
self.into_report(source).eprint(SourceCache::new(source))
}
}

Some files were not shown because too many files have changed in this diff Show more