Skip to content

Commit

Permalink
Enhance REPL and error handling as well as documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
dvdvgt committed Aug 12, 2021
1 parent 82dabae commit 65a1736
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 51 deletions.
11 changes: 3 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 1 addition & 9 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
[package]
name = "sasl"
version = "0.1.0"
version = "1.0.0"
authors = ["Lars Vogtmann", "David Voigt <david.voigt1998@gmail.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
phf = { version = "0.8.0", features = ["macros"] }
dot = "0.1.4"
clap = "3.0.0-beta.2"

[dev-dependencies]
#criterion = "0.3.4"

#[[bench]]
#name = "bench_lexer"
#harness = false
18 changes: 15 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ SASL is a purely functional programming language developed by David Turner in 19
- Purely functional programming paradigm

The compiler is divided into two sections: the frontend and the backend. The frontend contains the lexer, parser and the implementation of the abstract syntax tree which is used throughout the project. The backend contains the compiler and a virtual machine for evaluation. \
Furthermore a simple parser for the [Graphviz DOT DSL](https://graphviz.org/) is included which can be used for debugging and visualizing the abstract syntax tree.
Furthermore a simple parser for the [Graphviz DOT DSL](https://graphviz.org/) is included which can be used for debugging and visualizing the abstract syntax tree. \
The compiler was implemented following the languages specifications listed [here](https://db.inf.uni-tuebingen.de/staticfiles/teaching/ss16/sasl2016.pdf).

# Installation
1. If you already have Rust installed you can skip this part. Otherwise you may want to look at [how to install Rust](https://www.rust-lang.org/learn/get-started).
Expand All @@ -30,16 +31,17 @@ As previously mentioned this project contains a small parser for the DOT languag
# Usage
```
$ sasl --help
SASL-rs 0.0.1
SASL-rs 1.0.0
David Voigt <david.voigt@student.uni-tuebingen.de>
Lars Vogtmann <lars.vogtmann@studen.uni-tuebingen.de
Compiler for the SASL functional programming language written in Rust.
A compiler for the SASL functional programming language written in Rust.

USAGE:
sasl [FLAGS] [OPTIONS]

FLAGS:
-h, --help Prints help information
-o Activate optimizations.
-v Output tokens as well as the AST. Useful for debugging.
-V, --version Prints version information

Expand Down Expand Up @@ -116,6 +118,16 @@ The compiler features a simple REPL environment which can be started by just run
.
take 50 sieve
```
5. It's possible to import other SASL files by just specifiying the relative path to the file
to import.
```
use prelude
take 2 [1, 2, 3]
```
There has to be `prelude.sasl` file within the same directory the file importing is in. It is also possible to import SASL files from subdirectories by specifiying the relative path including subdirectories. Imports from root directories are not (yet) supported.
# Benchmarks
The following benchmarks were conducted using [Hyperfine](https://github.com/sharkdp/hyperfine):
Expand Down
2 changes: 1 addition & 1 deletion reference-bin/fibonacci.sasl
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,4 @@ def last xs =
if (tl xs) = nil then hd xs
else last (tl xs)
.
take 20 fibs
take 50 fibs
6 changes: 5 additions & 1 deletion reference-bin/quicksort.sasl
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
use test/test2/prelude
use ackermann
use sieve

def quicksort xs =
if xs = nil then []
else concat (concat lhs [pivot]) rhs
Expand All @@ -22,4 +26,4 @@ def concat xs ys =
(if ys = nil then xs
else (hd xs) : concat (tl xs) (ys))
.
quicksort [3,1,4,1,5,9,7]
[quicksort [3,1,4,1,5,9,7], length [1,2,3], ackermann 3 2, take 5 sieve]
17 changes: 1 addition & 16 deletions src/backend/abstractor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl<'a> Abstractor<'a> {
#[cfg(test)]
mod tests {
use super::*;
use crate::frontend::{ast::Ast, lexer::Lexer, parser::Parser, visualize::Visualizer};
use crate::frontend::{ast::Ast, lexer::Lexer, parser::Parser};

fn parse_to_ast(code: &str) -> Ast {
Parser::new(Lexer::new(code, None).tokenize().unwrap())
Expand All @@ -223,12 +223,6 @@ mod tests {
false
}

fn compile_(code: &str) -> Ast {
let mut ast = parse_to_ast(code);
compile(&mut ast).unwrap();
ast
}

#[test]
fn test_check_recursion() {
let mut ast = parse_to_ast("x where f x = g x; g y = f y");
Expand All @@ -241,15 +235,6 @@ mod tests {
assert!(check_recursion(ast.body));
}

#[test]
fn test_multiple_where() {
let mut vis = Visualizer::new("g", false);
let ast = compile_("f where f = g; g = 1");
vis.visualize_ast(&ast);
println!("After compilation: {:?}", &ast);
vis.write_to_pdf("test.pdf");
}

#[test]
fn test_free_occurences_global_def() {
let mut ast = parse_to_ast(
Expand Down
11 changes: 11 additions & 0 deletions src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,14 @@
//! This module contains all modules needed for the backend of the compiler.
//!
//! The backend features two (three counting optimizations) stages:
//! 1. Introducing combinators into the AST and removing all ocurences of parameters and
//! local where definitions. This stage is handled by the `Abstractor`.
//! 2. Evaluating the program in a virtual machine. This stage is handled by the `ReductionMachine`.
//! The virtual machine simplifies the AST step by step adhering the rules given in the language
//! specifications for the combinators. The VM also applys optimization rules which can lead to
//! performance increase with up to ~40% quicker execution time. The optimizations can be activated
//! by setting the `-o` flag. (There's really no reason not to do that.)
pub mod abstractor;
pub mod reduction;
pub mod utils;
41 changes: 31 additions & 10 deletions src/backend/reduction.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
//! This module contains the implementation of a virtual machine used for evaluating a SASL program.
//!
//! The reduction machine also has all the optimizations built in so evaluating and optimizing are not
//! two seperate stages but one and the same.
use crate::frontend::token::Type;
use crate::{
Expand Down Expand Up @@ -765,19 +768,37 @@ mod tests {

let result = evaluate("1 / 3 > 0.33");
assert_eq!(result, "true");

assert_eq!(evaluate("hd [1,2,3]"), "1");
assert_eq!(evaluate("tl [1,2,3]"), "[2, 3]");
}

#[test]
fn test_complex_programs() {
let program = std::fs::read_to_string("reference-bin/example.sasl").unwrap();
assert_eq!(evaluate(&program), "5050");

let program = std::fs::read_to_string("reference-bin/sieve.sasl").unwrap();
assert_eq!(evaluate(&program), "[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]");

let program = "f where f = g + 1; g = 1";
assert_eq!(evaluate(&program), "2");
let program = "f where g = 1; f = g + 1";
assert_eq!(evaluate(&program), "2");
// mutual recursion
let prog = "f where f = g + 1; g = 1";
assert_eq!(evaluate(prog), "2");
let prog = "f where g = 1; f = g + 1";
assert_eq!(evaluate(prog), "2");

// recursive global definition
let prog = "def factorial n = if n = 0 then 1 else n * factorial (n-1) . factorial 5";
assert_eq!(evaluate(prog), "120");

// Lazy evaluation with infinite lists
let prog = "def naturalNums = go 1 where go n = n : go (n+1)\
def take xs n = if (n = 0) or (tail = nil) then nil else head : take tail (n - 1)\
where tail = tl xs; head = hd xs
.\
take naturalNums 5";
assert_eq!(evaluate(prog), "[1, 2, 3, 4, 5]");

// Function as first class citizien
let prog = "def flip f x y = f y x def div x y = x / y . [flip div 2 10, div 10 2]";
assert_eq!(evaluate(prog), "[5, 5]");

// Currying of functions
let prog = "def plus x y = x + y def incr = plus 1 . incr 1";
assert_eq!(evaluate(prog), "2");
}
}
2 changes: 1 addition & 1 deletion src/frontend/visualize/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
//! needs to installed on your system. It is available with most GNU/Linux package manager.
//!
//! Example:
//! ```rust
//! ```rust, no_run
//! use sasl::frontend::{visualize::Visualizer, parser::Parser, lexer::Lexer};
//!
//! let mut viz = Visualizer::new("test_graph", true);
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,8 @@ impl<'a> Runner<'a> {
match reductor.reduce() {
Ok(_) => match reductor.print_result() {
Err(e) => eprintln!("\u{1b}[31m{}\u{1b}[0m", e),
_ => {
println!("{}", reductor.print_result().unwrap());
Ok(s) => {
println!("{}", s);
println!("\ntook \u{1b}[32;40m{:.2?}\u{1b}[0m", start.elapsed());
self.prompt_defs = ast.global_defs.clone();
}
Expand Down

0 comments on commit 65a1736

Please sign in to comment.