├── src └── lib.rs ├── tests ├── bool.rkt ├── num.rkt ├── if7.rkt ├── if8.rkt ├── if5.rkt ├── if6.rkt ├── incrdecr1.rkt ├── incrdecr2.rkt ├── if1.rkt ├── if2.rkt ├── incrdecr3.rkt ├── if3.rkt ├── if4.rkt └── correctness_test.rs ├── .gitignore ├── language ├── src │ ├── ast.rs │ ├── lib.rs │ ├── dtypes.rs │ └── parser.rs └── Cargo.toml ├── compiler ├── src │ ├── lib.rs │ ├── asm.rs │ └── compile.rs └── Cargo.toml ├── runtime ├── src │ └── main.rs ├── Cargo.toml ├── build.rs └── Cargo.lock ├── notes.md ├── Cargo.toml ├── interpreter ├── Cargo.toml ├── src │ ├── main.rs │ └── lib.rs └── Cargo.lock ├── README.md └── Cargo.lock /src/lib.rs: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /tests/bool.rkt: -------------------------------------------------------------------------------- 1 | #f 2 | -------------------------------------------------------------------------------- /tests/num.rkt: -------------------------------------------------------------------------------- 1 | 42 2 | -------------------------------------------------------------------------------- /tests/if7.rkt: -------------------------------------------------------------------------------- 1 | (if 0 1 2) 2 | -------------------------------------------------------------------------------- /tests/if8.rkt: -------------------------------------------------------------------------------- 1 | (if 7 1 2) 2 | -------------------------------------------------------------------------------- /tests/if5.rkt: -------------------------------------------------------------------------------- 1 | (if #f 1 2) 2 | -------------------------------------------------------------------------------- /tests/if6.rkt: -------------------------------------------------------------------------------- 1 | (if #t 1 2) 2 | -------------------------------------------------------------------------------- /tests/incrdecr1.rkt: -------------------------------------------------------------------------------- 1 | (add1 0) 2 | -------------------------------------------------------------------------------- /tests/incrdecr2.rkt: -------------------------------------------------------------------------------- 1 | (sub1 120) 2 | -------------------------------------------------------------------------------- /tests/if1.rkt: -------------------------------------------------------------------------------- 1 | (if (zero? 0) (add1 2) 4) 2 | -------------------------------------------------------------------------------- /tests/if2.rkt: -------------------------------------------------------------------------------- 1 | (if (zero? 1) (add1 2) 4) 2 | -------------------------------------------------------------------------------- /tests/incrdecr3.rkt: -------------------------------------------------------------------------------- 1 | (add1 (add1 (add1 42))) 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/target 2 | **/*.rs.bk 3 | **/*.s 4 | -------------------------------------------------------------------------------- /tests/if3.rkt: -------------------------------------------------------------------------------- 1 | (if (zero? (if (zero? (sub1 1)) 1 0)) (add1 2) 4) 2 | -------------------------------------------------------------------------------- /tests/if4.rkt: -------------------------------------------------------------------------------- 1 | (if (zero? (add1 0)) (add1 2) (if (zero? (sub1 1)) 1 0)) 2 | -------------------------------------------------------------------------------- /language/src/ast.rs: -------------------------------------------------------------------------------- 1 | pub enum Expr { 2 | EInt(u64), 3 | EBool(bool), 4 | EAdd1(Box), 5 | ESub1(Box), 6 | EZeroh(Box), 7 | EIf(Box, Box, Box) 8 | } 9 | -------------------------------------------------------------------------------- /compiler/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod asm; 2 | mod compile; 3 | 4 | extern crate language; 5 | use language::ast::Expr; 6 | 7 | use crate::asm::Asm; 8 | 9 | pub fn compile(e: Expr) -> Vec { 10 | compile::compile(e) 11 | } 12 | -------------------------------------------------------------------------------- /runtime/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate language; 2 | 3 | use language::dtypes::Val; 4 | 5 | extern "C" { 6 | fn entry() -> Val; 7 | } 8 | 9 | fn main() { 10 | let result = unsafe { entry() }; 11 | println!("{}", result.to_string()); 12 | } 13 | -------------------------------------------------------------------------------- /language/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "language" 3 | version = "0.1.0" 4 | authors = ["Sankha Narayan Guria "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "language" 9 | path = "src/lib.rs" 10 | 11 | [dependencies] 12 | nom = "*" 13 | -------------------------------------------------------------------------------- /compiler/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "compiler" 3 | version = "0.1.0" 4 | authors = ["Sankha Narayan Guria "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "compiler" 9 | path = "src/lib.rs" 10 | 11 | [dependencies] 12 | language = { path = "../language" } 13 | -------------------------------------------------------------------------------- /notes.md: -------------------------------------------------------------------------------- 1 | Plan to make this an educational resource. The order of proceeding through this would be: 2 | 3 | * Setup infra -> build tooling, compile language of numbers, interpret it 4 | * increment/decrement of numbers, IR 5 | * conditionals 6 | * multiple types, show bugs because of unhandled types 7 | * type checking 8 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "scheme-compiler" 3 | version = "0.1.0" 4 | authors = ["Sankha Narayan Guria "] 5 | edition = "2018" 6 | 7 | [lib] 8 | name = "compiler" 9 | path = "src/lib.rs" 10 | 11 | [dependencies] 12 | glob = "*" 13 | language = { path = "language" } 14 | interpreter = { path = "interpreter" } 15 | -------------------------------------------------------------------------------- /interpreter/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "interpreter" 3 | version = "0.1.0" 4 | authors = ["Sankha Narayan Guria "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | language = { path = "../language" } 11 | 12 | [lib] 13 | name = "interpreter" 14 | path = "src/lib.rs" -------------------------------------------------------------------------------- /runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runtime" 3 | version = "0.1.0" 4 | authors = ["Sankha Narayan Guria "] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [build-dependencies] 10 | cc = "1.0.3" 11 | language = { path = "../language" } 12 | compiler = { path = "../compiler" } 13 | 14 | [dependencies] 15 | language = { path = "../language" } 16 | -------------------------------------------------------------------------------- /language/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate nom; 2 | 3 | pub mod ast; 4 | pub mod parser; 5 | pub mod dtypes; 6 | 7 | use std::fs; 8 | 9 | pub fn parse_from_file(filename: &str) -> ast::Expr { 10 | let src = fs::read_to_string(filename).unwrap(); 11 | match parser::parse(&src) { 12 | Ok((rem, expr)) => { 13 | if rem.is_empty() { 14 | expr 15 | } else { 16 | panic!(format!("expected empty string, got {}", rem)) 17 | } 18 | }, 19 | Err(err) => panic!(err.to_string()) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /interpreter/src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate language; 2 | 3 | mod lib; 4 | 5 | use std::env; 6 | use std::fs; 7 | use language::parser; 8 | 9 | fn main() { 10 | let src = match env::var("SRC") { 11 | Ok(src) => src, 12 | _ => panic!("no source files in SRC environment variable") 13 | }; 14 | 15 | let prog = fs::read_to_string(src).unwrap(); 16 | match parser::parse(&prog) { 17 | Ok((rem, expr)) => { 18 | if rem.is_empty() { 19 | println!("{}", lib::interp(expr).to_string()) 20 | } else { 21 | panic!(format!("expected empty string, got {}", rem)) 22 | } 23 | }, 24 | Err(err) => panic!(err.to_string()) 25 | }; 26 | } 27 | -------------------------------------------------------------------------------- /interpreter/src/lib.rs: -------------------------------------------------------------------------------- 1 | use language::ast::Expr; 2 | use language::ast::Expr::*; 3 | use language::dtypes::Val; 4 | 5 | pub fn interp(e: Expr) -> Val { 6 | match e { 7 | EInt(n) => n.into(), 8 | EBool(b) => b.into(), 9 | EAdd1(e0) => interp(*e0) + 1.into(), 10 | ESub1(e0) => interp(*e0) - 1.into(), 11 | EZeroh(e0) => (interp(*e0) == 0.into()).into(), 12 | EIf(e0, e1, e2) => { 13 | let b = interp(*e0); 14 | if b.is_truthy() { 15 | interp(*e1) 16 | } else { 17 | interp(*e2) 18 | } 19 | } 20 | } 21 | } 22 | 23 | pub fn to_human_str(r: Val) -> String { 24 | format!("{}\n", r.to_string()) 25 | } 26 | -------------------------------------------------------------------------------- /runtime/build.rs: -------------------------------------------------------------------------------- 1 | extern crate cc; 2 | extern crate compiler; 3 | 4 | use std::env; 5 | use std::fs::File; 6 | use std::io::prelude::*; 7 | use std::path::Path; 8 | 9 | fn main() { 10 | let src = match env::var("SRC") { 11 | Ok(src) => src, 12 | _ => return 13 | }; 14 | println!("cargo:rerun-if-env-changed=SRC"); 15 | 16 | let ast = language::parse_from_file(&src); 17 | let compiled = compiler::asm::asm_to_string(compiler::compile(ast)); 18 | 19 | let path = Path::new("compiled.s"); 20 | let mut file = File::create(&path).unwrap(); 21 | file.write_all(compiled.as_bytes()).unwrap(); 22 | 23 | cc::Build::new() 24 | .file("compiled.s") 25 | .compile("compiledlib"); 26 | } 27 | -------------------------------------------------------------------------------- /tests/correctness_test.rs: -------------------------------------------------------------------------------- 1 | extern crate glob; 2 | extern crate language; 3 | extern crate interpreter; 4 | 5 | use std::str; 6 | use std::fs; 7 | use std::process::Command; 8 | use glob::glob; 9 | 10 | #[test] 11 | fn compile_interp_consistent() { 12 | for entry in glob("tests/*.rkt").unwrap() { 13 | let src = fs::canonicalize(entry.unwrap()).unwrap().display().to_string(); 14 | let ast = language::parse_from_file(&src); 15 | let mut child = Command::new("cargo") 16 | .current_dir("runtime") 17 | .arg("build") 18 | .env("SRC", &src) 19 | .spawn().expect("failed to build binary"); 20 | let ecode = child.wait().expect("failed to wait on compilation"); 21 | assert!(ecode.success()); 22 | let output = Command::new("runtime/target/debug/runtime").output().expect("failed to execute process"); 23 | let cout = str::from_utf8(&output.stdout).unwrap(); 24 | let iout = interpreter::to_human_str(interpreter::interp(ast)); 25 | assert_eq!(iout, cout, "{}", src); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /compiler/src/asm.rs: -------------------------------------------------------------------------------- 1 | use language::dtypes::Val; 2 | 3 | pub enum Register { 4 | Rax 5 | } 6 | 7 | pub struct Rep(u64); 8 | 9 | impl Into for Val { 10 | fn into(self) -> Rep { 11 | Rep(self.0) 12 | } 13 | } 14 | 15 | impl Register { 16 | pub fn as_str(&self) -> &str { 17 | match *self { 18 | Register::Rax => "%rax" 19 | } 20 | } 21 | } 22 | 23 | pub enum Asm { 24 | Label(String), 25 | Movq(Rep, Register), 26 | Addq(Rep, Register), 27 | Subq(Rep, Register), 28 | Cmpq(Rep, Register), 29 | Jmp(String), 30 | Jne(String), 31 | Je(String), 32 | Shrq(u64, Register), 33 | Ret 34 | } 35 | 36 | impl Asm { 37 | pub fn to_string(&self) -> String { 38 | match &*self { 39 | Asm::Label(l) => format!("{}:", l), 40 | Asm::Movq(val, ref r) => format!("\tmovq ${}, {}", val.0, r.as_str()), 41 | Asm::Addq(val, ref r) => format!("\taddq ${}, {}", val.0, r.as_str()), 42 | Asm::Subq(val, ref r) => format!("\tsubq ${}, {}", val.0, r.as_str()), 43 | Asm::Cmpq(val, ref r) => format!("\tcmpq ${}, {}", val.0, r.as_str()), 44 | Asm::Jmp(label) => format!("\tjmp {}", label), 45 | Asm::Jne(label) => format!("\tjne {}", label), 46 | Asm::Je(label) => format!("\tje {}", label), 47 | Asm::Shrq(val, ref r) => format!("\tshrq ${}, {}", val, r.as_str()), 48 | Asm::Ret => "ret".to_string() 49 | } 50 | } 51 | } 52 | 53 | pub fn asm_to_string(asm: Vec) -> String { 54 | let instructions = asm.into_iter() 55 | .map(|instr| instr.to_string()) 56 | .collect::>() 57 | .join("\n"); 58 | format!(".text 59 | .globl _entry 60 | _entry: 61 | {} 62 | ", instructions) 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Scheme Compiler 2 | 3 | This is an experiment to build a compiler for a Scheme that compiles directly down to x86-64 assembly. This is inspired by the incremental approach taken in [CMSC430](https://www.cs.umd.edu/class/fall2019/cmsc430/) class at the University of Maryland. 4 | 5 | This project is just to see how the incremental approach to compiler development is in a typed language, the aim is not to be a production grade compiler for Scheme. 6 | 7 | ## Usage 8 | 9 | You need a working Rust setup on your machine. A C compiler is needed as well (this is to convert assembly to object code only). 10 | 11 | ### Compile & Run 12 | 13 | ```bash 14 | $ cd runtime 15 | $ SRC=filename.rkt cargo build # Compile the filename.rkt file into the binary target/debug/runtime 16 | $ ./target/debug/runtime # Run the binary 17 | ``` 18 | 19 | ### Interpret 20 | 21 | There is an interpreter to check if the compiled code is behaving correctly. To run the interpreter: 22 | 23 | ```bash 24 | $ cd interpreter 25 | $ cargo build 26 | $ SRC=filename.rkt ./target/debug/interpreter 27 | ``` 28 | 29 | ### Testing 30 | 31 | Many sample scheme programs are given in `tests` directory. To test them use: 32 | 33 | ```bash 34 | $ cargo test 35 | ``` 36 | 37 | The above script runs the compiler and interpreter on all the provided sample program and checks if their outputs are consistent. 38 | 39 | ## Differences from the class 40 | 41 | * Use a strongly typed language for developing the compiler. Rust was a natural choice here, because compiling and linking against assembly fits nicely with its build system. Rust is a perfect fit for systems programming, making it a good choice to write a compile code's runtime system. Use of typed AST, typed assembly code and exhaustive pattern matching helps the compiler catch many bugs during development. 42 | * [Planned] Use an IR for possible compiler optimizations instead of directly compiling to assembly. 43 | * [Planned] Build a working garbage collector for memory management. The compiler designed in class never deallocates memory. 44 | 45 | ## Source code reference 46 | 47 | * `compiler`: Transforms the parsed Scheme program into assembly. 48 | * `interpreter`: Runs an interpreter over the provided source program and reports the output. 49 | * `language`: Contains the parser and AST definitions for our language. 50 | * `runtime`: Contains the runtime support for our compiled code. 51 | -------------------------------------------------------------------------------- /language/src/dtypes.rs: -------------------------------------------------------------------------------- 1 | use std::ops::Add; 2 | use std::ops::Sub; 3 | 4 | #[repr(C)] 5 | pub struct Val(pub u64); 6 | 7 | #[derive(PartialEq, Debug)] 8 | pub enum TypeTag { 9 | IntTag, 10 | BoolTag 11 | } 12 | 13 | pub const PADDING: u64 = 1; 14 | 15 | impl Val { 16 | pub fn type_tag(&self) -> TypeTag { 17 | let mask = (1 << PADDING) - 1; 18 | match self.0 & mask { 19 | 0 => TypeTag::IntTag, 20 | 1 => TypeTag::BoolTag, 21 | _ => unreachable!() 22 | } 23 | } 24 | 25 | pub fn to_u64(&self) -> u64 { 26 | self.0 >> PADDING 27 | } 28 | 29 | pub fn to_bool(&self) -> bool { 30 | if (self.0 >> PADDING) > 0 { 31 | true 32 | } else { 33 | false 34 | } 35 | } 36 | 37 | pub fn to_string(&self) -> String { 38 | match self.type_tag() { 39 | TypeTag::IntTag => self.to_u64().to_string(), 40 | TypeTag::BoolTag => if self.to_bool() { 41 | "#t".to_string() 42 | } else { 43 | "#f".to_string() 44 | } 45 | } 46 | } 47 | 48 | pub fn is_truthy(&self) -> bool { 49 | match self.type_tag() { 50 | TypeTag::IntTag => self.to_u64() != 0, 51 | TypeTag::BoolTag => self.to_bool() 52 | } 53 | } 54 | } 55 | 56 | impl Into for u64 { 57 | fn into(self) -> Val { 58 | Val((self << PADDING) | TypeTag::IntTag as u64) 59 | } 60 | } 61 | 62 | impl Into for bool { 63 | fn into(self) -> Val { 64 | if self { 65 | Val((1 << PADDING) | TypeTag::BoolTag as u64) 66 | } else { 67 | Val((0 << PADDING) | TypeTag::BoolTag as u64) 68 | } 69 | } 70 | } 71 | 72 | impl Add for Val { 73 | type Output = Self; 74 | 75 | fn add(self, other: Self) -> Self { 76 | assert_eq!(self.type_tag(), TypeTag::IntTag); 77 | assert_eq!(other.type_tag(), TypeTag::IntTag); 78 | (self.to_u64() + other.to_u64()).into() 79 | } 80 | } 81 | 82 | impl Sub for Val { 83 | type Output = Self; 84 | 85 | fn sub(self, other: Self) -> Self { 86 | assert_eq!(self.type_tag(), TypeTag::IntTag); 87 | assert_eq!(other.type_tag(), TypeTag::IntTag); 88 | (self.to_u64() - other.to_u64()).into() 89 | } 90 | } 91 | 92 | impl PartialEq for Val { 93 | fn eq(&self, other: &Self) -> bool { 94 | self.0 == other.0 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /compiler/src/compile.rs: -------------------------------------------------------------------------------- 1 | use language::ast::Expr; 2 | use language::ast::Expr::*; 3 | use language::dtypes::{Val, PADDING}; 4 | use crate::asm::Asm; 5 | use crate::asm::Asm::*; 6 | use crate::asm::Register::*; 7 | 8 | struct Context { 9 | gensym_count: u64 10 | } 11 | 12 | pub fn compile(e: Expr) -> Vec { 13 | let mut ctx = Context { gensym_count: 0 }; 14 | let mut compiled = compile_expr(&mut ctx, e); 15 | compiled.push(Ret); 16 | compiled 17 | } 18 | 19 | fn compile_expr(ctx: &mut Context, e: Expr) -> Vec { 20 | match e { 21 | EInt(n) => compile_int(ctx, n), 22 | EBool(b) => compile_bool(ctx, b), 23 | EAdd1(e) => compile_add1(ctx, e), 24 | ESub1(e) => compile_sub1(ctx, e), 25 | EZeroh(e) => compile_zeroh(ctx, e), 26 | EIf(e0, e1, e2) => compile_if(ctx, e0, e1, e2) 27 | } 28 | } 29 | 30 | fn compile_int(_ctx: &mut Context, n: u64) -> Vec { 31 | let v: Val = n.into(); 32 | vec![Movq(v.into(), Rax)] 33 | } 34 | 35 | fn compile_bool(_ctx: &mut Context, b: bool) -> Vec { 36 | let v: Val = b.into(); 37 | vec![Movq(v.into(), Rax)] 38 | } 39 | 40 | fn compile_add1(ctx: &mut Context, e0: Box) -> Vec { 41 | let mut c0 = compile_expr(ctx, *e0); 42 | let v: Val = 1.into(); 43 | c0.extend(vec![Addq(v.into(), Rax)]); 44 | c0 45 | } 46 | 47 | fn compile_sub1(ctx: &mut Context, e0: Box) -> Vec { 48 | let mut c0 = compile_expr(ctx, *e0); 49 | let v: Val = 1.into(); 50 | c0.extend(vec![Subq(v.into(), Rax)]); 51 | c0 52 | } 53 | 54 | fn compile_zeroh(ctx: &mut Context, e0: Box) -> Vec { 55 | let mut c0 = compile_expr(ctx, *e0); 56 | let l0 = gensym(ctx, "zeroh"); 57 | let t: Val = true.into(); 58 | let f: Val = false.into(); 59 | let z: Val = 0.into(); 60 | 61 | c0.extend(vec![Cmpq(z.into(), Rax), 62 | Movq(f.into(), Rax), 63 | Jne(l0.clone()), 64 | Movq(t.into(), Rax), 65 | Label(l0)]); 66 | c0 67 | } 68 | 69 | fn compile_if(ctx: &mut Context, e0: Box, e1: Box, e2: Box) -> Vec { 70 | let c0 = compile_expr(ctx, *e0); 71 | let c1 = compile_expr(ctx, *e1); 72 | let c2 = compile_expr(ctx, *e2); 73 | let l0 = gensym(ctx, "if"); 74 | let l1 = gensym(ctx, "if"); 75 | let z: Val = 0.into(); 76 | 77 | // we handle falsy values by right shifting away the type tag 78 | // falsy values of every type will be 0 when this is done 79 | vec![c0, 80 | vec![Shrq(PADDING, Rax), 81 | Cmpq(z.into(), Rax), 82 | Je(l0.clone())], 83 | c1, 84 | vec![Jmp(l1.clone())], 85 | vec![Label(l0)], 86 | c2, 87 | vec![Label(l1)]] 88 | .into_iter().flatten().collect() 89 | } 90 | 91 | fn gensym(ctx: &mut Context, prefix: &str) -> String { 92 | ctx.gensym_count += 1; 93 | format!("{}{}", prefix, ctx.gensym_count) 94 | } 95 | -------------------------------------------------------------------------------- /language/src/parser.rs: -------------------------------------------------------------------------------- 1 | use nom::{IResult, 2 | character::streaming::digit1, 3 | character::streaming::multispace0, 4 | character::streaming::multispace1, 5 | character::streaming::char, 6 | sequence::terminated, 7 | sequence::preceded, 8 | sequence::tuple, 9 | bytes::streaming::tag, 10 | branch::alt, 11 | combinator::map_res}; 12 | 13 | use crate::ast::Expr; 14 | use crate::ast::Expr::*; 15 | 16 | pub fn parse(input: &str) -> IResult<&str, Expr> { 17 | terminated(parse_expr, nom::character::complete::multispace0)(input) 18 | } 19 | 20 | fn to_int(input: &str) -> Result { 21 | input.parse::().map(|n| EInt(n)) 22 | } 23 | 24 | fn parse_sexpr(input: &str) -> IResult<&str, Expr> { 25 | preceded(tag("("), 26 | preceded(multispace0, 27 | terminated( 28 | terminated( 29 | alt((parse_add1, 30 | parse_sub1, 31 | parse_zeroh, 32 | parse_if)), 33 | multispace0), 34 | tag(")"))))(input) 35 | } 36 | 37 | fn parse_expr(input: &str) -> IResult<&str, Expr> { 38 | alt((parse_int, parse_bool, parse_sexpr))(input) 39 | } 40 | 41 | fn parse_true(input: &str) -> IResult<&str, Expr> { 42 | map_res(char('t'), to_bool)(input) 43 | } 44 | 45 | fn parse_false(input: &str) -> IResult<&str, Expr> { 46 | map_res(char('f'), to_bool)(input) 47 | } 48 | 49 | fn parse_bool(input: &str) -> IResult<&str, Expr> { 50 | preceded(tag("#"), alt((parse_true, parse_false)))(input) 51 | } 52 | 53 | fn to_bool(input: char) -> Result { 54 | Ok(match input { 55 | 't' => EBool(true), 56 | 'f' => EBool(false), 57 | _ => unreachable!() 58 | }) 59 | } 60 | 61 | fn to_add1(e: Expr) -> Result { 62 | Ok(EAdd1(Box::new(e))) 63 | } 64 | 65 | fn to_sub1(e: Expr) -> Result { 66 | Ok(ESub1(Box::new(e))) 67 | } 68 | 69 | fn to_if(e: (Expr, Expr, Expr)) -> Result { 70 | Ok(EIf(Box::new(e.0), Box::new(e.1), Box::new(e.2))) 71 | } 72 | 73 | fn parse_add1(input: &str) -> IResult<&str, Expr> { 74 | map_res(preceded(tag("add1"), 75 | preceded(multispace0, 76 | parse_expr)), to_add1)(input) 77 | } 78 | 79 | fn parse_sub1(input: &str) -> IResult<&str, Expr> { 80 | map_res(preceded(tag("sub1"), 81 | preceded(multispace0, 82 | parse_expr)), to_sub1)(input) 83 | } 84 | 85 | fn parse_zeroh(input: &str) -> IResult<&str, Expr> { 86 | map_res(preceded(tag("zero?"), 87 | preceded(multispace0, 88 | parse_expr)), to_zeroh)(input) 89 | } 90 | 91 | fn parse_int(input: &str) -> IResult<&str, Expr> { 92 | map_res(digit1, to_int)(input) 93 | } 94 | 95 | fn to_zeroh(e: Expr) -> Result { 96 | Ok(EZeroh(Box::new(e))) 97 | } 98 | 99 | fn parse_if(input: &str) -> IResult<&str, Expr> { 100 | map_res(preceded(tag("if"), 101 | preceded(multispace1, tuple((parse_expr, 102 | preceded(multispace1, parse_expr), 103 | preceded(multispace1, parse_expr))))), to_if)(input) 104 | } 105 | 106 | -------------------------------------------------------------------------------- /interpreter/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "arrayvec" 5 | version = "0.4.12" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" 8 | dependencies = [ 9 | "nodrop", 10 | ] 11 | 12 | [[package]] 13 | name = "bitflags" 14 | version = "1.2.1" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 17 | 18 | [[package]] 19 | name = "cfg-if" 20 | version = "0.1.9" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 23 | 24 | [[package]] 25 | name = "interpreter" 26 | version = "0.1.0" 27 | dependencies = [ 28 | "language", 29 | ] 30 | 31 | [[package]] 32 | name = "language" 33 | version = "0.1.0" 34 | dependencies = [ 35 | "nom", 36 | ] 37 | 38 | [[package]] 39 | name = "lexical-core" 40 | version = "0.6.7" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "f86d66d380c9c5a685aaac7a11818bdfa1f733198dfd9ec09c70b762cd12ad6f" 43 | dependencies = [ 44 | "arrayvec", 45 | "bitflags", 46 | "cfg-if", 47 | "rustc_version", 48 | "ryu", 49 | "static_assertions", 50 | ] 51 | 52 | [[package]] 53 | name = "memchr" 54 | version = "2.3.3" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 57 | 58 | [[package]] 59 | name = "nodrop" 60 | version = "0.1.14" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" 63 | 64 | [[package]] 65 | name = "nom" 66 | version = "5.1.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" 69 | dependencies = [ 70 | "lexical-core", 71 | "memchr", 72 | "version_check", 73 | ] 74 | 75 | [[package]] 76 | name = "rustc_version" 77 | version = "0.2.3" 78 | source = "registry+https://github.com/rust-lang/crates.io-index" 79 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 80 | dependencies = [ 81 | "semver", 82 | ] 83 | 84 | [[package]] 85 | name = "ryu" 86 | version = "1.0.3" 87 | source = "registry+https://github.com/rust-lang/crates.io-index" 88 | checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" 89 | 90 | [[package]] 91 | name = "semver" 92 | version = "0.9.0" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 95 | dependencies = [ 96 | "semver-parser", 97 | ] 98 | 99 | [[package]] 100 | name = "semver-parser" 101 | version = "0.7.0" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 104 | 105 | [[package]] 106 | name = "static_assertions" 107 | version = "0.3.4" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" 110 | 111 | [[package]] 112 | name = "version_check" 113 | version = "0.9.1" 114 | source = "registry+https://github.com/rust-lang/crates.io-index" 115 | checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" 116 | -------------------------------------------------------------------------------- /runtime/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "arrayvec" 5 | version = "0.4.12" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" 8 | dependencies = [ 9 | "nodrop", 10 | ] 11 | 12 | [[package]] 13 | name = "bitflags" 14 | version = "1.2.1" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 17 | 18 | [[package]] 19 | name = "cc" 20 | version = "1.0.50" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "95e28fa049fda1c330bcf9d723be7663a899c4679724b34c81e9f5a326aab8cd" 23 | 24 | [[package]] 25 | name = "cfg-if" 26 | version = "0.1.9" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 29 | 30 | [[package]] 31 | name = "compiler" 32 | version = "0.1.0" 33 | dependencies = [ 34 | "language", 35 | ] 36 | 37 | [[package]] 38 | name = "language" 39 | version = "0.1.0" 40 | dependencies = [ 41 | "nom", 42 | ] 43 | 44 | [[package]] 45 | name = "lexical-core" 46 | version = "0.6.7" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "f86d66d380c9c5a685aaac7a11818bdfa1f733198dfd9ec09c70b762cd12ad6f" 49 | dependencies = [ 50 | "arrayvec", 51 | "bitflags", 52 | "cfg-if", 53 | "rustc_version", 54 | "ryu", 55 | "static_assertions", 56 | ] 57 | 58 | [[package]] 59 | name = "memchr" 60 | version = "2.3.3" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 63 | 64 | [[package]] 65 | name = "nodrop" 66 | version = "0.1.14" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" 69 | 70 | [[package]] 71 | name = "nom" 72 | version = "5.1.1" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" 75 | dependencies = [ 76 | "lexical-core", 77 | "memchr", 78 | "version_check", 79 | ] 80 | 81 | [[package]] 82 | name = "runtime" 83 | version = "0.1.0" 84 | dependencies = [ 85 | "cc", 86 | "compiler", 87 | "language", 88 | ] 89 | 90 | [[package]] 91 | name = "rustc_version" 92 | version = "0.2.3" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 95 | dependencies = [ 96 | "semver", 97 | ] 98 | 99 | [[package]] 100 | name = "ryu" 101 | version = "1.0.3" 102 | source = "registry+https://github.com/rust-lang/crates.io-index" 103 | checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" 104 | 105 | [[package]] 106 | name = "semver" 107 | version = "0.9.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 110 | dependencies = [ 111 | "semver-parser", 112 | ] 113 | 114 | [[package]] 115 | name = "semver-parser" 116 | version = "0.7.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 119 | 120 | [[package]] 121 | name = "static_assertions" 122 | version = "0.3.4" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" 125 | 126 | [[package]] 127 | name = "version_check" 128 | version = "0.9.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" 131 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "arrayvec" 5 | version = "0.4.12" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | checksum = "cd9fd44efafa8690358b7408d253adf110036b88f55672a933f01d616ad9b1b9" 8 | dependencies = [ 9 | "nodrop", 10 | ] 11 | 12 | [[package]] 13 | name = "bitflags" 14 | version = "1.2.1" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" 17 | 18 | [[package]] 19 | name = "cfg-if" 20 | version = "0.1.9" 21 | source = "registry+https://github.com/rust-lang/crates.io-index" 22 | checksum = "b486ce3ccf7ffd79fdeb678eac06a9e6c09fc88d33836340becb8fffe87c5e33" 23 | 24 | [[package]] 25 | name = "glob" 26 | version = "0.3.0" 27 | source = "registry+https://github.com/rust-lang/crates.io-index" 28 | checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" 29 | 30 | [[package]] 31 | name = "interpreter" 32 | version = "0.1.0" 33 | dependencies = [ 34 | "language", 35 | ] 36 | 37 | [[package]] 38 | name = "language" 39 | version = "0.1.0" 40 | dependencies = [ 41 | "nom", 42 | ] 43 | 44 | [[package]] 45 | name = "lexical-core" 46 | version = "0.6.7" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "f86d66d380c9c5a685aaac7a11818bdfa1f733198dfd9ec09c70b762cd12ad6f" 49 | dependencies = [ 50 | "arrayvec", 51 | "bitflags", 52 | "cfg-if", 53 | "rustc_version", 54 | "ryu", 55 | "static_assertions", 56 | ] 57 | 58 | [[package]] 59 | name = "memchr" 60 | version = "2.3.3" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" 63 | 64 | [[package]] 65 | name = "nodrop" 66 | version = "0.1.14" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" 69 | 70 | [[package]] 71 | name = "nom" 72 | version = "5.1.1" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "0b471253da97532da4b61552249c521e01e736071f71c1a4f7ebbfbf0a06aad6" 75 | dependencies = [ 76 | "lexical-core", 77 | "memchr", 78 | "version_check", 79 | ] 80 | 81 | [[package]] 82 | name = "rustc_version" 83 | version = "0.2.3" 84 | source = "registry+https://github.com/rust-lang/crates.io-index" 85 | checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" 86 | dependencies = [ 87 | "semver", 88 | ] 89 | 90 | [[package]] 91 | name = "ryu" 92 | version = "1.0.3" 93 | source = "registry+https://github.com/rust-lang/crates.io-index" 94 | checksum = "535622e6be132bccd223f4bb2b8ac8d53cda3c7a6394944d3b2b33fb974f9d76" 95 | 96 | [[package]] 97 | name = "scheme-compiler" 98 | version = "0.1.0" 99 | dependencies = [ 100 | "glob", 101 | "interpreter", 102 | "language", 103 | ] 104 | 105 | [[package]] 106 | name = "semver" 107 | version = "0.9.0" 108 | source = "registry+https://github.com/rust-lang/crates.io-index" 109 | checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" 110 | dependencies = [ 111 | "semver-parser", 112 | ] 113 | 114 | [[package]] 115 | name = "semver-parser" 116 | version = "0.7.0" 117 | source = "registry+https://github.com/rust-lang/crates.io-index" 118 | checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" 119 | 120 | [[package]] 121 | name = "static_assertions" 122 | version = "0.3.4" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "7f3eb36b47e512f8f1c9e3d10c2c1965bc992bd9cdb024fa581e2194501c83d3" 125 | 126 | [[package]] 127 | name = "version_check" 128 | version = "0.9.1" 129 | source = "registry+https://github.com/rust-lang/crates.io-index" 130 | checksum = "078775d0255232fb988e6fccf26ddc9d1ac274299aaedcedce21c6f72cc533ce" 131 | --------------------------------------------------------------------------------