├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── README.md ├── build.sh ├── examples ├── fibonacci.ulisp ├── plus-two.ulisp └── sub-two.ulisp ├── rust-toolchain └── src ├── backend ├── llvm │ ├── mod.rs │ └── scope │ │ ├── mod.rs │ │ └── tests.rs ├── mod.rs └── x86.rs ├── main.rs └── parser └── mod.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /target 3 | **/*.rs.bk 4 | *.asm 5 | *.ll 6 | *.o 7 | *.out 8 | *.s 9 | .vscode -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "ansi_term" 5 | version = "0.11.0" 6 | source = "registry+https://github.com/rust-lang/crates.io-index" 7 | dependencies = [ 8 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 9 | ] 10 | 11 | [[package]] 12 | name = "atty" 13 | version = "0.2.11" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | dependencies = [ 16 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 17 | "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", 18 | "winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", 19 | ] 20 | 21 | [[package]] 22 | name = "bitflags" 23 | version = "1.0.4" 24 | source = "registry+https://github.com/rust-lang/crates.io-index" 25 | 26 | [[package]] 27 | name = "clap" 28 | version = "2.33.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | dependencies = [ 31 | "ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 32 | "atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", 33 | "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", 34 | "strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", 35 | "textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", 36 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 37 | "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", 38 | ] 39 | 40 | [[package]] 41 | name = "heck" 42 | version = "0.3.1" 43 | source = "registry+https://github.com/rust-lang/crates.io-index" 44 | dependencies = [ 45 | "unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", 46 | ] 47 | 48 | [[package]] 49 | name = "libc" 50 | version = "0.2.51" 51 | source = "registry+https://github.com/rust-lang/crates.io-index" 52 | 53 | [[package]] 54 | name = "proc-macro2" 55 | version = "0.4.27" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | dependencies = [ 58 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 59 | ] 60 | 61 | [[package]] 62 | name = "quote" 63 | version = "0.6.11" 64 | source = "registry+https://github.com/rust-lang/crates.io-index" 65 | dependencies = [ 66 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 67 | ] 68 | 69 | [[package]] 70 | name = "redox_syscall" 71 | version = "0.1.54" 72 | source = "registry+https://github.com/rust-lang/crates.io-index" 73 | 74 | [[package]] 75 | name = "redox_termios" 76 | version = "0.1.1" 77 | source = "registry+https://github.com/rust-lang/crates.io-index" 78 | dependencies = [ 79 | "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", 80 | ] 81 | 82 | [[package]] 83 | name = "strsim" 84 | version = "0.8.0" 85 | source = "registry+https://github.com/rust-lang/crates.io-index" 86 | 87 | [[package]] 88 | name = "structopt" 89 | version = "0.2.17" 90 | source = "registry+https://github.com/rust-lang/crates.io-index" 91 | dependencies = [ 92 | "clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)", 93 | "structopt-derive 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 94 | ] 95 | 96 | [[package]] 97 | name = "structopt-derive" 98 | version = "0.2.17" 99 | source = "registry+https://github.com/rust-lang/crates.io-index" 100 | dependencies = [ 101 | "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", 102 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 103 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 104 | "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", 105 | ] 106 | 107 | [[package]] 108 | name = "syn" 109 | version = "0.15.30" 110 | source = "registry+https://github.com/rust-lang/crates.io-index" 111 | dependencies = [ 112 | "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", 113 | "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", 114 | "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", 115 | ] 116 | 117 | [[package]] 118 | name = "termion" 119 | version = "1.5.1" 120 | source = "registry+https://github.com/rust-lang/crates.io-index" 121 | dependencies = [ 122 | "libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)", 123 | "redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)", 124 | "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", 125 | ] 126 | 127 | [[package]] 128 | name = "textwrap" 129 | version = "0.11.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | dependencies = [ 132 | "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", 133 | ] 134 | 135 | [[package]] 136 | name = "ulisp" 137 | version = "0.3.0" 138 | dependencies = [ 139 | "structopt 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", 140 | ] 141 | 142 | [[package]] 143 | name = "unicode-segmentation" 144 | version = "1.2.1" 145 | source = "registry+https://github.com/rust-lang/crates.io-index" 146 | 147 | [[package]] 148 | name = "unicode-width" 149 | version = "0.1.5" 150 | source = "registry+https://github.com/rust-lang/crates.io-index" 151 | 152 | [[package]] 153 | name = "unicode-xid" 154 | version = "0.1.0" 155 | source = "registry+https://github.com/rust-lang/crates.io-index" 156 | 157 | [[package]] 158 | name = "vec_map" 159 | version = "0.8.1" 160 | source = "registry+https://github.com/rust-lang/crates.io-index" 161 | 162 | [[package]] 163 | name = "winapi" 164 | version = "0.3.7" 165 | source = "registry+https://github.com/rust-lang/crates.io-index" 166 | dependencies = [ 167 | "winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 168 | "winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", 169 | ] 170 | 171 | [[package]] 172 | name = "winapi-i686-pc-windows-gnu" 173 | version = "0.4.0" 174 | source = "registry+https://github.com/rust-lang/crates.io-index" 175 | 176 | [[package]] 177 | name = "winapi-x86_64-pc-windows-gnu" 178 | version = "0.4.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | 181 | [metadata] 182 | "checksum ansi_term 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" 183 | "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" 184 | "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" 185 | "checksum clap 2.33.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5067f5bb2d80ef5d68b4c87db81601f0b75bca627bc2ef76b141d7b846a3c6d9" 186 | "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" 187 | "checksum libc 0.2.51 (registry+https://github.com/rust-lang/crates.io-index)" = "bedcc7a809076656486ffe045abeeac163da1b558e963a31e29fbfbeba916917" 188 | "checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" 189 | "checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" 190 | "checksum redox_syscall 0.1.54 (registry+https://github.com/rust-lang/crates.io-index)" = "12229c14a0f65c4f1cb046a3b52047cdd9da1f4b30f8a39c5063c8bae515e252" 191 | "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" 192 | "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" 193 | "checksum structopt 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c767a8971f53d7324583085deee2e230903be09e52fb27df9af94c5cb2b43c31" 194 | "checksum structopt-derive 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "c57a30c87454ced2186f62f940e981746e8cbbe026d52090c8c4352b636f8235" 195 | "checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2" 196 | "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" 197 | "checksum textwrap 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" 198 | "checksum unicode-segmentation 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "aa6024fc12ddfd1c6dbc14a80fa2324d4568849869b779f6bd37e5e4c03344d1" 199 | "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" 200 | "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" 201 | "checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a" 202 | "checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770" 203 | "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 204 | "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 205 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ulisp" 3 | version = "0.3.0" 4 | authors = ["Jorge Luis Pérez "] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | structopt = "0.2" -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ulisp 2 | 3 | An implementation of @phil_eaton compilers basic notes in Rust 4 | 5 | http://notes.eatonphil.com/compiler-basics-lisp-to-assembly.html 6 | 7 | The parser module is ispired in the Peter Norvig Lisp parser 8 | 9 | http://norvig.com/lispy.html 10 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | mkdir -p build/ 3 | cargo run --release -- input.ulisp > build/out.asm 4 | nasm -f elf64 build/out.asm 5 | gcc -o build/out build/out.o 6 | -------------------------------------------------------------------------------- /examples/fibonacci.ulisp: -------------------------------------------------------------------------------- 1 | (module 2 | (def fib (n) 3 | (if (< n 2) 4 | n 5 | (+ (fib (- n 1)) (fib (- n 2)))) 6 | ) 7 | 8 | (def main () 9 | (fib 8)) 10 | ) 11 | -------------------------------------------------------------------------------- /examples/plus-two.ulisp: -------------------------------------------------------------------------------- 1 | (module 2 | (def plus-two (a b) 3 | (+ a (+ b 2))) 4 | 5 | (def main () 6 | (plus-two 3 (plus-two 1 1))) 7 | ) -------------------------------------------------------------------------------- /examples/sub-two.ulisp: -------------------------------------------------------------------------------- 1 | (module 2 | (def sub-two (a b) 3 | (- a (- b 2))) 4 | 5 | (def main () 6 | (sub-two 3 (sub-two 1 1))) 7 | ) -------------------------------------------------------------------------------- /rust-toolchain: -------------------------------------------------------------------------------- 1 | stable -------------------------------------------------------------------------------- /src/backend/llvm/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::backend::Backend; 2 | use crate::parser::Expression; 3 | use scope::safe_name; 4 | use std::collections::HashMap; 5 | use std::fs; 6 | use std::io::Write; 7 | use std::process::Command; 8 | use std::rc::Rc; 9 | 10 | pub use scope::Scope; 11 | 12 | mod scope; 13 | 14 | type PrimitiveFunction = Rc, &mut Scope) -> ()>; 15 | 16 | struct LLVM { 17 | output: String, 18 | primitive_functions: HashMap, 19 | } 20 | 21 | impl Backend for LLVM { 22 | type S = Scope; 23 | 24 | fn emit(&mut self, depth: usize, code: T) 25 | where 26 | T: Into, 27 | { 28 | let mut indent = String::with_capacity(depth); 29 | for _ in 0..depth { 30 | indent.push_str("\t"); 31 | } 32 | let s: String = code.into(); 33 | self.output.push_str(&format!("{}{}\n", indent, s.clone())); 34 | } 35 | 36 | fn compile(&mut self, ast: &Expression) -> String { 37 | let mut scope = Scope::new(); 38 | let destination = scope.symbol(None); 39 | self.compile_expression(ast, Some(&destination), &mut scope); 40 | self.output.clone() 41 | } 42 | 43 | fn compile_expression( 44 | &mut self, 45 | arg: &Expression, 46 | destination: Option<&str>, 47 | scope: &mut Scope, 48 | ) { 49 | match arg { 50 | Expression::List(_vec) => { 51 | let (function, args) = split_function(arg); 52 | //let dest = scope.symbol(); 53 | self.compile_call(&function, args, destination, scope); 54 | //self.compile_call(&function, args, Some(&dest), scope); 55 | return; 56 | } 57 | Expression::Symbol(symbol) => { 58 | if let Some(name) = scope.get(symbol) { 59 | self.emit( 60 | 1, 61 | format!("%{} = add i32 %{}, 0", destination.unwrap(), name), 62 | ); 63 | } else { 64 | panic!( 65 | "Attempt to reference undefined variable or unsupported literal: {} ", 66 | symbol 67 | ); 68 | }; 69 | } 70 | Expression::Integer(int) => { 71 | self.emit(1, format!("%{} = add i32 {}, 0", destination.unwrap(), int)); 72 | } 73 | Expression::Float(_float) => { 74 | unimplemented!(); 75 | } 76 | Expression::Boolean(_boolean) => { 77 | unimplemented!(); 78 | } 79 | } 80 | } 81 | 82 | fn compile_call( 83 | &mut self, 84 | function: &str, 85 | args: &[Expression], 86 | destination: Option<&str>, 87 | scope: &mut Scope, 88 | ) { 89 | if let Some(fun) = self.get_primitive_function(function) { 90 | let mut scope = scope; 91 | (*fun)(self, args, destination, &mut scope); 92 | return; 93 | } 94 | 95 | let valid_function = if let Some(f) = scope.get(function) { 96 | f 97 | } else { 98 | panic!("Attempt to call undefined function: {}", function); 99 | }; 100 | 101 | let safe_args = args 102 | .iter() 103 | .map(|arg| { 104 | let sym = scope.symbol(None); 105 | self.compile_expression(arg, Some(&sym), scope); 106 | format!("i32 %{}", sym) 107 | }) 108 | .fold("".to_string(), |acc, s| { 109 | if acc == "" { 110 | s.to_string() 111 | } else { 112 | format!("{}, {}", acc, s) 113 | } 114 | }); 115 | 116 | self.emit( 117 | 1, 118 | format!( 119 | "%{} = call i32 @{}({})", 120 | destination.unwrap(), 121 | valid_function, 122 | safe_args 123 | ), 124 | ); 125 | } 126 | 127 | fn compile_define( 128 | &mut self, 129 | args: &[Expression], 130 | _destination: Option<&str>, 131 | scope: &mut Scope, 132 | ) { 133 | let (name, params, body) = split_def_expression(args); 134 | // Add this function to outer scope 135 | let safe_name = scope.register(name); 136 | // Copy outer scope so parameter mappings aren't exposed in outer scope. 137 | let mut child_scope = scope.copy(); 138 | 139 | let safe_params = params 140 | .iter() 141 | .map(|param| { 142 | if let Expression::Symbol(param_name) = param { 143 | child_scope.register(param_name.to_string()) 144 | } else { 145 | panic!("") 146 | } 147 | }) 148 | .fold("".to_string(), |acc, s| { 149 | if acc == "" { 150 | format!("i32 %{}", s) 151 | } else { 152 | format!("{}, i32 %{}", acc, s) 153 | } 154 | }); 155 | 156 | self.emit(0, format!("define i32 @{}({}) {{", safe_name, safe_params)); 157 | 158 | let ret = child_scope.symbol(None); 159 | self.compile_expression(body, Some(&ret), &mut child_scope); 160 | 161 | self.emit(1, format!("ret i32 %{}", ret)); 162 | self.emit(0, "}\n"); 163 | } 164 | 165 | fn compile_module( 166 | &mut self, 167 | args: &[Expression], 168 | _destination: Option<&str>, 169 | scope: &mut Scope, 170 | ) { 171 | for expression in args { 172 | self.compile_expression(expression, None, scope); 173 | } 174 | } 175 | 176 | fn build(&mut self, asm: String, input: &str, output: &str) { 177 | let asmfile = &format!("{}.ll", input); 178 | self.write_asm(asmfile, asm); 179 | 180 | let objfile = self.run_assembler(asmfile, &input); 181 | self.run_linker(&objfile, &output); 182 | } 183 | } 184 | 185 | impl LLVM { 186 | fn new() -> Self { 187 | let primitive_functions = { 188 | let mut m = HashMap::::new(); 189 | m.insert("def".to_string(), Rc::new(Self::compile_define)); 190 | m.insert("module".to_string(), Rc::new(Self::compile_module)); 191 | m.insert("+".to_string(), Self::compile_operation("add")); 192 | m.insert("-".to_string(), Self::compile_operation("sub")); 193 | m.insert("*".to_string(), Self::compile_operation("mul")); 194 | m.insert("<".to_string(), Self::compile_operation("icmp slt")); 195 | m.insert("if".to_string(), Self::compile_if()); 196 | m 197 | }; 198 | let output = String::new(); 199 | 200 | LLVM { 201 | primitive_functions, 202 | output, 203 | } 204 | } 205 | 206 | fn get_primitive_function(&mut self, name: &str) -> Option { 207 | match self.primitive_functions.get(name) { 208 | Some(pf) => Some(pf.clone()), 209 | None => None, 210 | } 211 | } 212 | 213 | fn compile_operation(operation: T) -> PrimitiveFunction 214 | where 215 | T: Into + Clone, 216 | { 217 | let c = move |backend: &mut LLVM, 218 | expressions: &[Expression], 219 | destination: Option<&str>, 220 | scope: &mut Scope| { 221 | let exp1 = &expressions[0]; 222 | let exp2 = &expressions[1]; 223 | 224 | let arg1 = scope.symbol(None); 225 | let arg2 = scope.symbol(None); 226 | 227 | backend.compile_expression(exp1, Some(&arg1), scope); 228 | backend.compile_expression(exp2, Some(&arg2), scope); 229 | backend.emit( 230 | 1, 231 | format!( 232 | "%{} = {} i32 %{}, %{}", 233 | destination.unwrap(), 234 | operation.clone().into(), 235 | arg1, 236 | arg2 237 | ), 238 | ); 239 | }; 240 | Rc::new(c) 241 | } 242 | 243 | fn compile_if() -> PrimitiveFunction { 244 | let c = move |backend: &mut LLVM, 245 | expressions: &[Expression], 246 | destination: Option<&str>, 247 | scope: &mut Scope| { 248 | let test_var = scope.symbol(None); 249 | let result = scope.symbol(Some("ifresult")); 250 | // Space for result 251 | backend.emit(1, format!("%{} = alloca i32, align 4", result)); 252 | 253 | let test = &expressions[0]; 254 | let then_block = &expressions[1]; 255 | let else_block = &expressions[2]; 256 | 257 | backend.compile_expression(test, Some(&test_var), scope); 258 | let true_label = scope.symbol(Some("iftrue")); 259 | let false_label = scope.symbol(Some("iffalse")); 260 | 261 | backend.emit( 262 | 1, 263 | format!( 264 | "br i1 %{}, label %{}, label %{}", 265 | test_var, true_label, false_label 266 | ), 267 | ); 268 | 269 | // Compile true section 270 | backend.emit(0, format!("{}:", true_label)); 271 | let tmp1 = scope.symbol(None); 272 | backend.compile_expression(then_block, Some(&tmp1), scope); 273 | backend.emit(1, format!("store i32 %{}, i32* %{}, align 4", tmp1, result)); 274 | 275 | let end_label = scope.symbol(Some("ifend")); 276 | backend.emit(1, format!("br label %{}", end_label)); 277 | backend.emit(0, format!("{}:", false_label)); 278 | 279 | // Compile false section 280 | let tmp2 = scope.symbol(None); 281 | backend.compile_expression(else_block, Some(&tmp2), scope); 282 | backend.emit(1, format!("store i32 %{}, i32* %{}, align 4", tmp2, result)); 283 | backend.emit(1, format!("br label %{}", end_label)); 284 | 285 | // Clean up 286 | backend.emit(0, format!("{}:", end_label)); 287 | backend.emit( 288 | 1, 289 | format!( 290 | "%{} = load i32, i32* %{}, align 4", 291 | destination.unwrap(), 292 | result 293 | ), 294 | ); 295 | }; 296 | Rc::new(c) 297 | } 298 | 299 | fn write_asm(&mut self, output: &str, asm: String) { 300 | let mut output = fs::File::create(output).expect("failed open output file"); 301 | output 302 | .write_all(asm.as_bytes()) 303 | .expect("failed write output file"); 304 | } 305 | 306 | fn run_assembler(&mut self, asmfile: &str, codefile: &str) -> String { 307 | let objfile = format!("{}.s", codefile); 308 | Command::new("llc") 309 | .arg("-o") 310 | .arg(&objfile) 311 | .arg(asmfile) 312 | .output() 313 | .expect("failed to run llc"); 314 | objfile 315 | } 316 | 317 | fn run_linker(&mut self, objfile: &str, binary: &str) { 318 | Command::new("gcc") 319 | .arg("-o") 320 | .arg(binary) 321 | .arg(objfile) 322 | .output() 323 | .expect("failed to run gcc"); 324 | } 325 | } 326 | 327 | fn split_function(list: &Expression) -> (String, &[Expression]) { 328 | if let Expression::List(vec) = list { 329 | if let Expression::Symbol(name) = &vec[0] { 330 | return (safe_name(name), vec.split_at(1).1); 331 | } else { 332 | panic!("First list item is not a symbol"); 333 | } 334 | } else { 335 | panic!("Expression is not a list item"); 336 | } 337 | } 338 | 339 | fn split_def_expression(args: &[Expression]) -> (String, &Vec, &Expression) { 340 | ( 341 | if let Expression::Symbol(name) = &args[0] { 342 | safe_name(name) 343 | } else { 344 | panic!("First item must be a symbol in def statement"); 345 | }, 346 | if let Expression::List(vec) = &args[1] { 347 | vec 348 | } else { 349 | panic!("Second item must be a list in def statement"); 350 | }, 351 | if let Expression::List(_) = &args[2] { 352 | &args[2] 353 | } else { 354 | panic!("Third item must be a list in def statement"); 355 | }, 356 | ) 357 | } 358 | 359 | pub(crate) fn new() -> Box> { 360 | Box::new(LLVM::new()) 361 | } 362 | -------------------------------------------------------------------------------- /src/backend/llvm/scope/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests; 3 | 4 | use std::collections::HashMap; 5 | 6 | #[derive(Clone, Debug)] 7 | pub struct Scope { 8 | locals: HashMap, 9 | } 10 | 11 | impl Scope { 12 | pub fn new() -> Self { 13 | Scope { 14 | locals: HashMap::new(), 15 | } 16 | } 17 | 18 | pub fn register(&mut self, local: String) -> String { 19 | let mut copy = safe_name(&local); 20 | let mut n = 1; 21 | while self.locals.get(©).is_some() { 22 | copy = format!("{}{}", local, n); 23 | n += 1; 24 | } 25 | self.locals.insert(local, copy.to_owned()); 26 | copy 27 | } 28 | 29 | pub fn symbol(&mut self, prefix: Option<&str>) -> String { 30 | let nth = self.locals.len() + 1; 31 | let prefix = prefix.unwrap_or_else(|| "sym"); 32 | self.register(format!("{}{}", prefix, nth)) 33 | } 34 | 35 | pub fn get(&mut self, local: &str) -> Option { 36 | match self.locals.get(local) { 37 | Some(s) => Some(s.clone()), 38 | None => None, 39 | } 40 | } 41 | 42 | pub fn copy(&mut self) -> Scope { 43 | self.clone() 44 | } 45 | } 46 | 47 | pub(crate) fn safe_name(symbol_name: &str) -> String { 48 | if symbol_name == "-" { 49 | return symbol_name.to_owned(); 50 | } 51 | symbol_name.replace("-", "_") 52 | } 53 | -------------------------------------------------------------------------------- /src/backend/llvm/scope/tests.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/backend/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod llvm; 2 | pub mod x86; 3 | 4 | use crate::parser::Expression; 5 | use std::fmt; 6 | use std::str::FromStr; 7 | 8 | #[derive(Debug)] 9 | pub(crate) struct BackendOptError { 10 | details: String, 11 | } 12 | 13 | impl BackendOptError { 14 | fn new(details: String) -> Self { 15 | BackendOptError { details } 16 | } 17 | } 18 | 19 | impl fmt::Display for BackendOptError { 20 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 21 | write!(f, "{}", self.details) 22 | } 23 | } 24 | 25 | impl std::error::Error for BackendOptError { 26 | fn description(&self) -> &str { 27 | &self.details 28 | } 29 | } 30 | 31 | pub(crate) enum BackendOpt { 32 | LLVM, 33 | X86, 34 | } 35 | 36 | impl FromStr for BackendOpt { 37 | type Err = BackendOptError; 38 | fn from_str(backend: &str) -> Result { 39 | match backend { 40 | "x86" => Ok(BackendOpt::X86), 41 | "llvm" => Ok(BackendOpt::LLVM), 42 | _ => Err(BackendOptError::new(format!( 43 | "Unsupported backend: {}", 44 | backend 45 | ))), 46 | } 47 | } 48 | } 49 | 50 | pub(crate) trait Backend { 51 | type S; 52 | 53 | fn compile(&mut self, ast: &Expression) -> String; 54 | 55 | fn build(&mut self, asm: String, input: &str, output: &str); 56 | 57 | fn compile_expression( 58 | &mut self, 59 | arg: &Expression, 60 | destination: Option<&str>, 61 | scope: &mut Self::S, 62 | ); 63 | 64 | fn compile_call( 65 | &mut self, 66 | function: &str, 67 | args: &[Expression], 68 | destination: Option<&str>, 69 | scope: &mut Self::S, 70 | ); 71 | 72 | fn compile_define( 73 | &mut self, 74 | args: &[Expression], 75 | _destination: Option<&str>, 76 | scope: &mut Self::S, 77 | ); 78 | 79 | fn compile_module( 80 | &mut self, 81 | args: &[Expression], 82 | destination: Option<&str>, 83 | scope: &mut Self::S, 84 | ); 85 | 86 | fn emit(&mut self, depth: usize, code: T) 87 | where 88 | T: Into, 89 | Self: Sized; 90 | } 91 | -------------------------------------------------------------------------------- /src/backend/x86.rs: -------------------------------------------------------------------------------- 1 | use crate::backend::Backend; 2 | use crate::parser::Expression; 3 | use std::cell::RefCell; 4 | use std::collections::HashMap; 5 | use std::fs; 6 | use std::io::Write; 7 | use std::process::Command; 8 | 9 | pub type Scope = HashMap; 10 | 11 | type PrimitiveFunction = 12 | fn(&mut X86, args: &[Expression], destination: Option<&str>, scope: &mut Scope) -> (); 13 | 14 | const PARAM_REGISTERS: &[&str] = &["rdi", "rsi", "rdx"]; 15 | const LOCAL_REGISTERS: &[&str] = &["rbx", "rbp", "r12"]; 16 | 17 | struct X86 { 18 | primitive_functions: HashMap, 19 | builtin_functions: Scope, 20 | output: RefCell, 21 | } 22 | 23 | impl X86 { 24 | fn new() -> Self { 25 | let primitive_functions = { 26 | let mut m = HashMap::::new(); 27 | m.insert("def".to_string(), X86::compile_define); 28 | m.insert("module".to_string(), X86::compile_module); 29 | m 30 | }; 31 | let builtin_functions = { 32 | let mut m = HashMap::::new(); 33 | m.insert("+".to_string(), "plus".to_string()); 34 | m 35 | }; 36 | let output = RefCell::new(String::new()); 37 | 38 | X86 { 39 | primitive_functions, 40 | builtin_functions, 41 | output, 42 | } 43 | } 44 | 45 | fn emit_prefix(&mut self) { 46 | self.emit(0, "; Generated with ulisp"); 47 | self.emit(0, ";"); 48 | self.emit(0, "; To compile run the following:"); 49 | self.emit(0, "; $ nasm -f elf64 program.asm"); 50 | self.emit(0, "; $ gcc -o program program.o"); 51 | self.emit(0, ""); 52 | 53 | self.emit(1, "global main\n"); 54 | 55 | self.emit(1, "SECTION .text\n"); 56 | 57 | self.emit(0, "plus:"); 58 | self.emit(1, "add rdi, rsi"); 59 | self.emit(1, "mov rax, rdi"); 60 | self.emit(1, "ret\n"); 61 | } 62 | 63 | fn emit_postfix(&mut self) { 64 | let mut syscall_map = HashMap::new(); 65 | if cfg!(darwin) { 66 | syscall_map.insert("exit", "0x2000001"); 67 | } else { 68 | syscall_map.insert("exit", "60"); 69 | } 70 | 71 | self.emit(0, "main:"); 72 | self.emit(1, "call program_main"); 73 | self.emit(1, "mov rdi, rax"); 74 | self.emit(1, format!("mov rax, {}", syscall_map["exit"])); 75 | self.emit(1, "syscall"); 76 | } 77 | 78 | fn run_assembler(&mut self, asmfile: &str, codefile: &str) -> String { 79 | let objfile = format!("{}.o", codefile); 80 | Command::new("nasm") 81 | .arg("-f") 82 | .arg("elf64") 83 | .arg("-o") 84 | .arg(&objfile) 85 | .arg(asmfile) 86 | .output() 87 | .expect("failed to run nasm"); 88 | objfile 89 | } 90 | 91 | fn run_linker(&mut self, objfile: &str, binary: &str) { 92 | Command::new("gcc") 93 | .arg("-o") 94 | .arg(binary) 95 | .arg(objfile) 96 | .output() 97 | .expect("failed to run gcc"); 98 | } 99 | 100 | fn write_asm(&mut self, output: &str, asm: String) { 101 | let mut output = fs::File::create(output).expect("failed open output file"); 102 | output 103 | .write_all(asm.as_bytes()) 104 | .expect("failed write output file"); 105 | } 106 | } 107 | 108 | impl Backend for X86 { 109 | type S = Scope; 110 | 111 | fn compile(&mut self, ast: &Expression) -> String { 112 | self.emit_prefix(); 113 | let mut scope = HashMap::::new(); 114 | self.compile_expression(ast, None, &mut scope); 115 | self.emit_postfix(); 116 | 117 | self.output.borrow().to_string() 118 | } 119 | 120 | fn build(&mut self, asm: String, input: &str, output: &str) { 121 | let asmfile = &format!("{}.asm", input); 122 | self.write_asm(asmfile, asm); 123 | 124 | let objfile = self.run_assembler(asmfile, &input); 125 | self.run_linker(&objfile, &output); 126 | } 127 | 128 | fn compile_expression( 129 | &mut self, 130 | arg: &Expression, 131 | destination: Option<&str>, 132 | scope: &mut Scope, 133 | ) { 134 | #[allow(unused_assignments)] 135 | let mut origin: Option = None; 136 | match arg { 137 | Expression::List(_vec) => { 138 | let (function, args) = split_function(arg); 139 | self.compile_call(&function, args, destination, scope); 140 | return; 141 | } 142 | Expression::Symbol(symbol) => { 143 | origin = if let Some(name) = scope.get(symbol) { 144 | Some(name.to_string()) 145 | } else { 146 | panic!( 147 | "Attempt to reference undefined variable or unsupported literal: {} ", 148 | symbol 149 | ); 150 | }; 151 | } 152 | Expression::Integer(int) => { 153 | origin = Some(format!("{}", int)); 154 | } 155 | Expression::Float(_float) => { 156 | unimplemented!(); 157 | } 158 | Expression::Boolean(_boolean) => { 159 | unimplemented!(); 160 | } 161 | } 162 | self.emit( 163 | 1, 164 | format!("mov {}, {}", destination.unwrap(), origin.unwrap()), 165 | ); 166 | } 167 | 168 | fn compile_call( 169 | &mut self, 170 | function: &str, 171 | args: &[Expression], 172 | destination: Option<&str>, 173 | scope: &mut Scope, 174 | ) { 175 | if let Some(fun) = self.primitive_functions.get(function) { 176 | fun(self, args, destination, scope); 177 | return; 178 | } 179 | 180 | // Save param registers to the stack 181 | for (i, _) in args.iter().enumerate() { 182 | self.emit(1, format!("push {}", PARAM_REGISTERS[i])); 183 | } 184 | 185 | // Compile arguments and store in param registers 186 | for (i, arg) in args.iter().enumerate() { 187 | self.compile_expression(arg, Some(PARAM_REGISTERS[i]), scope); 188 | } 189 | 190 | // Call function 191 | self.emit( 192 | 1, 193 | format!( 194 | "call {}", 195 | self.builtin_functions 196 | .get(function) 197 | .unwrap_or(&function.to_string()) 198 | ), 199 | ); 200 | 201 | for (i, _) in args.iter().enumerate() { 202 | self.emit(1, format!("pop {}", PARAM_REGISTERS[args.len() - i - 1])); 203 | } 204 | 205 | if let Some(d) = destination { 206 | self.emit(1, format!("mov {}, rax", d)); 207 | } 208 | } 209 | 210 | fn compile_define( 211 | &mut self, 212 | args: &[Expression], 213 | _destination: Option<&str>, 214 | scope: &mut HashMap, 215 | ) { 216 | let (name, params, body) = split_def_expression(args); 217 | 218 | self.emit(0, format!("{}:", name)); 219 | 220 | let mut child_scope = scope.clone(); 221 | for (i, param) in params.iter().enumerate() { 222 | if let Expression::Symbol(name) = param { 223 | let register = PARAM_REGISTERS[i].to_string(); 224 | let local = LOCAL_REGISTERS[i].to_string(); 225 | self.emit(1, format!("push {}", local)); 226 | self.emit(1, format!("mov {}, {}", local, register)); 227 | 228 | // Store parameter mapped to associated local 229 | child_scope.insert(name.to_string(), register); 230 | } else { 231 | panic!("Function param must be a symbol"); 232 | }; 233 | } 234 | 235 | self.compile_expression(body, Some("rax"), &mut child_scope); 236 | 237 | for (i, _) in params.iter().enumerate() { 238 | let local = LOCAL_REGISTERS[params.len() - i - 1].to_string(); 239 | self.emit(1, format!("pop {}", local)); 240 | } 241 | 242 | self.emit(1, "ret\n"); 243 | } 244 | 245 | fn compile_module( 246 | &mut self, 247 | args: &[Expression], 248 | destination: Option<&str>, 249 | scope: &mut HashMap, 250 | ) { 251 | for expression in args { 252 | self.compile_expression(expression, Some("rax"), scope); 253 | } 254 | if let Some(dest) = destination { 255 | if dest == "rax" { 256 | } else { 257 | self.emit(1, format!("mov {}, rax", dest)); 258 | } 259 | } 260 | } 261 | 262 | fn emit(&mut self, depth: usize, code: T) 263 | where 264 | T: Into, 265 | { 266 | let mut indent = String::with_capacity(depth); 267 | for _ in 0..depth { 268 | indent.push_str("\t"); 269 | } 270 | 271 | self.output 272 | .borrow_mut() 273 | .push_str(&format!("{}{}\n", indent, code.into())); 274 | } 275 | } 276 | 277 | pub(crate) fn new() -> Box> { 278 | Box::new(X86::new()) 279 | } 280 | 281 | fn split_function(list: &Expression) -> (String, &[Expression]) { 282 | if let Expression::List(vec) = list { 283 | if let Expression::Symbol(name) = &vec[0] { 284 | return (name.to_owned(), vec.split_at(1).1); 285 | } else { 286 | panic!("First list item is not a symbol"); 287 | } 288 | } else { 289 | panic!("Expression is not a list item"); 290 | } 291 | } 292 | 293 | fn split_def_expression(args: &[Expression]) -> (String, &Vec, &Expression) { 294 | ( 295 | if let Expression::Symbol(name) = &args[0] { 296 | let mut name = name.replace("-", "_"); 297 | if name == "main" { 298 | name = "program_main".to_string(); 299 | } 300 | name 301 | } else { 302 | panic!("First item must be a symbol in def statement"); 303 | }, 304 | if let Expression::List(vec) = &args[1] { 305 | vec 306 | } else { 307 | panic!("Second item must be a list in def statement"); 308 | }, 309 | if let Expression::List(_) = &args[2] { 310 | &args[2] 311 | } else { 312 | panic!("Third item must be a list in def statement"); 313 | }, 314 | ) 315 | } 316 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate structopt; 2 | 3 | mod backend; 4 | mod parser; 5 | 6 | use backend::llvm::Scope as llvm_Scope; 7 | use backend::x86::Scope as x86_Scope; 8 | use backend::{llvm, x86, Backend, BackendOpt}; 9 | use parser::{parse, Expression}; 10 | use std::fs; 11 | use std::io::Read; 12 | use std::path; 13 | use structopt::StructOpt; 14 | 15 | #[derive(StructOpt)] 16 | struct Opt { 17 | #[structopt(parse(from_os_str))] 18 | input: path::PathBuf, 19 | #[structopt(short = "o", long = "output", default_value = "a.out")] 20 | output: path::PathBuf, 21 | #[structopt(short = "b", long = "backend", default_value = "llvm")] 22 | backend: BackendOpt, 23 | } 24 | 25 | type X86 = Box>; 26 | type LLVM = Box>; 27 | 28 | fn main() { 29 | let opt = Opt::from_args(); 30 | 31 | let input = opt.input.to_str().unwrap(); 32 | let output = opt.output.to_str().unwrap(); 33 | let backend = opt.backend; 34 | 35 | let code = read_input(input); 36 | let ast = parse(&code); 37 | 38 | match backend { 39 | BackendOpt::X86 => run_x86_backend(x86::new(), ast, input, output), 40 | BackendOpt::LLVM => run_llvm_backend(llvm::new(), ast, input, output), 41 | } 42 | } 43 | 44 | fn run_x86_backend(mut backend: X86, ast: Expression, input: &str, output: &str) { 45 | let asm = backend.compile(&ast); 46 | backend.build(asm, input, output); 47 | } 48 | 49 | fn run_llvm_backend(mut backend: LLVM, ast: Expression, input: &str, output: &str) { 50 | let asm = backend.compile(&ast); 51 | backend.build(asm, &input, &output); 52 | } 53 | 54 | fn read_input(input: &str) -> String { 55 | let mut input = fs::File::open(input).expect("failed open input file"); 56 | let mut code = String::new(); 57 | input 58 | .read_to_string(&mut code) 59 | .expect("failed read input file"); 60 | code 61 | } 62 | -------------------------------------------------------------------------------- /src/parser/mod.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Debug)] 2 | pub enum Expression { 3 | List(Vec), 4 | // Atoms: 5 | Symbol(String), 6 | Integer(i32), 7 | Float(f32), 8 | #[allow(dead_code)] 9 | Boolean(bool), 10 | } 11 | 12 | pub fn parse(program: &str) -> Expression { 13 | let mut tokens = tokenize(program); 14 | read_from_tokens(&mut tokens) 15 | } 16 | 17 | // Convert a string of characters into a list of tokens 18 | fn tokenize(string: &str) -> Vec { 19 | string 20 | .trim() 21 | .replace('(', "( ") 22 | .replace(')', " )") 23 | .replace("\n", "") 24 | .split(' ') 25 | .filter(|s| *s != "") // Empty list "()" generates List([Symbol("")]) 26 | .map(std::borrow::ToOwned::to_owned) 27 | .collect() 28 | } 29 | 30 | // Read an expression from a sequence of tokens 31 | fn read_from_tokens(mut tokens: &mut Vec) -> Expression { 32 | if tokens.is_empty() { 33 | panic!("Unexpected EOF"); 34 | } 35 | let token = tokens.remove(0); 36 | if token == "(" { 37 | let mut ts: Vec = Vec::new(); 38 | while tokens[0] != ")" { 39 | ts.push(read_from_tokens(&mut tokens)); 40 | } 41 | tokens.remove(0); 42 | Expression::List(ts) 43 | } else if token == ")" { 44 | panic!("Syntax error"); 45 | } else { 46 | atom(token.to_owned()) 47 | } 48 | } 49 | 50 | // Select the appropiated atom type for the expression 51 | fn atom(token: String) -> Expression { 52 | if let Ok(i) = str::parse::(&token) { 53 | return Expression::Integer(i); 54 | } 55 | if let Ok(f) = str::parse::(&token) { 56 | return Expression::Float(f); 57 | } 58 | Expression::Symbol(token) 59 | } 60 | --------------------------------------------------------------------------------