├── .gitignore ├── Cargo.toml ├── LICENSE ├── README.md ├── sample_programs └── aoc_challenge.txt └── src ├── main.rs ├── parser.rs └── program.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | # Local configuration. 13 | /.cargo/ 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "monad_compiler" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | itertools = "0.10.1" 10 | nom = "7.1.0" 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Predrag Gruevski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # monad_compiler 2 | A compiler for the MONAD language from Advent of Code 2021 Day 24. 3 | 4 | Repo accompanying my Compiler Adventures blog post series: https://medium.com/@predrag.gruevski/compiler-adventures-part-1-no-op-instructions-c084358c7864 5 | 6 | ### Notable branches 7 | - `only_parser` ([link](https://github.com/obi1kenobi/monad_compiler/tree/only_parser)): `Instruction` data type and parser for programs 8 | - `part1` ([link](https://github.com/obi1kenobi/monad_compiler/tree/part1)): Code at the start of [Compiler Adventures, episode 1](https://medium.com/@predrag.gruevski/compiler-adventures-part-1-no-op-instructions-c084358c7864) 9 | - `part1_finished` ([link](https://github.com/obi1kenobi/monad_compiler/tree/part1_finished)): Code at the end of [Compiler Adventures, episode 1](https://medium.com/@predrag.gruevski/compiler-adventures-part-1-no-op-instructions-c084358c7864): implemented no-op instruction removal 10 | -------------------------------------------------------------------------------- /sample_programs/aoc_challenge.txt: -------------------------------------------------------------------------------- 1 | inp w 2 | mul x 0 3 | add x z 4 | mod x 26 5 | div z 1 6 | add x 11 7 | eql x w 8 | eql x 0 9 | mul y 0 10 | add y 25 11 | mul y x 12 | add y 1 13 | mul z y 14 | mul y 0 15 | add y w 16 | add y 3 17 | mul y x 18 | add z y 19 | inp w 20 | mul x 0 21 | add x z 22 | mod x 26 23 | div z 1 24 | add x 14 25 | eql x w 26 | eql x 0 27 | mul y 0 28 | add y 25 29 | mul y x 30 | add y 1 31 | mul z y 32 | mul y 0 33 | add y w 34 | add y 7 35 | mul y x 36 | add z y 37 | inp w 38 | mul x 0 39 | add x z 40 | mod x 26 41 | div z 1 42 | add x 13 43 | eql x w 44 | eql x 0 45 | mul y 0 46 | add y 25 47 | mul y x 48 | add y 1 49 | mul z y 50 | mul y 0 51 | add y w 52 | add y 1 53 | mul y x 54 | add z y 55 | inp w 56 | mul x 0 57 | add x z 58 | mod x 26 59 | div z 26 60 | add x -4 61 | eql x w 62 | eql x 0 63 | mul y 0 64 | add y 25 65 | mul y x 66 | add y 1 67 | mul z y 68 | mul y 0 69 | add y w 70 | add y 6 71 | mul y x 72 | add z y 73 | inp w 74 | mul x 0 75 | add x z 76 | mod x 26 77 | div z 1 78 | add x 11 79 | eql x w 80 | eql x 0 81 | mul y 0 82 | add y 25 83 | mul y x 84 | add y 1 85 | mul z y 86 | mul y 0 87 | add y w 88 | add y 14 89 | mul y x 90 | add z y 91 | inp w 92 | mul x 0 93 | add x z 94 | mod x 26 95 | div z 1 96 | add x 10 97 | eql x w 98 | eql x 0 99 | mul y 0 100 | add y 25 101 | mul y x 102 | add y 1 103 | mul z y 104 | mul y 0 105 | add y w 106 | add y 7 107 | mul y x 108 | add z y 109 | inp w 110 | mul x 0 111 | add x z 112 | mod x 26 113 | div z 26 114 | add x -4 115 | eql x w 116 | eql x 0 117 | mul y 0 118 | add y 25 119 | mul y x 120 | add y 1 121 | mul z y 122 | mul y 0 123 | add y w 124 | add y 9 125 | mul y x 126 | add z y 127 | inp w 128 | mul x 0 129 | add x z 130 | mod x 26 131 | div z 26 132 | add x -12 133 | eql x w 134 | eql x 0 135 | mul y 0 136 | add y 25 137 | mul y x 138 | add y 1 139 | mul z y 140 | mul y 0 141 | add y w 142 | add y 9 143 | mul y x 144 | add z y 145 | inp w 146 | mul x 0 147 | add x z 148 | mod x 26 149 | div z 1 150 | add x 10 151 | eql x w 152 | eql x 0 153 | mul y 0 154 | add y 25 155 | mul y x 156 | add y 1 157 | mul z y 158 | mul y 0 159 | add y w 160 | add y 6 161 | mul y x 162 | add z y 163 | inp w 164 | mul x 0 165 | add x z 166 | mod x 26 167 | div z 26 168 | add x -11 169 | eql x w 170 | eql x 0 171 | mul y 0 172 | add y 25 173 | mul y x 174 | add y 1 175 | mul z y 176 | mul y 0 177 | add y w 178 | add y 4 179 | mul y x 180 | add z y 181 | inp w 182 | mul x 0 183 | add x z 184 | mod x 26 185 | div z 1 186 | add x 12 187 | eql x w 188 | eql x 0 189 | mul y 0 190 | add y 25 191 | mul y x 192 | add y 1 193 | mul z y 194 | mul y 0 195 | add y w 196 | add y 0 197 | mul y x 198 | add z y 199 | inp w 200 | mul x 0 201 | add x z 202 | mod x 26 203 | div z 26 204 | add x -1 205 | eql x w 206 | eql x 0 207 | mul y 0 208 | add y 25 209 | mul y x 210 | add y 1 211 | mul z y 212 | mul y 0 213 | add y w 214 | add y 7 215 | mul y x 216 | add z y 217 | inp w 218 | mul x 0 219 | add x z 220 | mod x 26 221 | div z 26 222 | add x 0 223 | eql x w 224 | eql x 0 225 | mul y 0 226 | add y 25 227 | mul y x 228 | add y 1 229 | mul z y 230 | mul y 0 231 | add y w 232 | add y 12 233 | mul y x 234 | add z y 235 | inp w 236 | mul x 0 237 | add x z 238 | mod x 26 239 | div z 26 240 | add x -11 241 | eql x w 242 | eql x 0 243 | mul y 0 244 | add y 25 245 | mul y x 246 | add y 1 247 | mul z y 248 | mul y 0 249 | add y w 250 | add y 1 251 | mul y x 252 | add z y 253 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_imports)] 2 | 3 | use std::{env, fs}; 4 | 5 | use itertools::Itertools; 6 | 7 | use crate::{parser::parse_program, program::{Instruction, InstructionStream}}; 8 | 9 | mod parser; 10 | mod program; 11 | 12 | fn main() { 13 | let args: Vec = env::args().collect(); 14 | let mut reversed_args: Vec<_> = args.iter().map(|x| x.as_str()).rev().collect(); 15 | 16 | reversed_args 17 | .pop() 18 | .expect("Expected the executable name to be the first argument, but was missing"); 19 | 20 | let part = reversed_args.pop().expect("part number"); 21 | let input_file = reversed_args.pop().expect("input file"); 22 | let content = fs::read_to_string(input_file).unwrap(); 23 | 24 | let input_program: Vec = parse_program(content.as_str()); 25 | 26 | match part { 27 | "analyze" => { 28 | let analysis = analyze_program(input_program); 29 | println!("{:?}", analysis); 30 | } 31 | _ => unreachable!("{}", part), 32 | } 33 | } 34 | 35 | fn analyze_program(input_program: Vec) -> Vec { 36 | input_program 37 | } 38 | -------------------------------------------------------------------------------- /src/parser.rs: -------------------------------------------------------------------------------- 1 | use nom::{ 2 | branch::alt, 3 | bytes::complete::tag, 4 | character::complete::{char, digit1, line_ending, one_of, space1}, 5 | combinator::{map, map_res, opt, recognize}, 6 | multi::many1, 7 | sequence::tuple, 8 | IResult, 9 | }; 10 | 11 | use crate::program::{Register, Operand, Instruction}; 12 | 13 | fn register(input: &str) -> IResult<&str, Register> { 14 | let (remainder, matched_char) = one_of("wxyz")(input)?; 15 | let register_id = match matched_char { 16 | 'w' => 0, 17 | 'x' => 1, 18 | 'y' => 2, 19 | 'z' => 3, 20 | _ => unreachable!("{}", matched_char), 21 | }; 22 | 23 | Ok((remainder, Register(register_id))) 24 | } 25 | 26 | fn text_signed_int(input: &str) -> IResult<&str, i64> { 27 | map_res(recognize(tuple((opt(char('-')), digit1))), |value: &str| { 28 | value.parse() 29 | })(input) 30 | } 31 | 32 | fn operand(input: &str) -> IResult<&str, Operand> { 33 | if let Ok((remainder, register)) = register(input) { 34 | Ok((remainder, Operand::Register(register))) 35 | } else { 36 | map(text_signed_int, Operand::Literal)(input) 37 | } 38 | } 39 | 40 | fn input_instruction(input: &str) -> IResult<&str, Instruction> { 41 | map( 42 | tuple((tag("inp"), space1, register, opt(line_ending))), 43 | |(_, _, reg, _)| Instruction::Input(reg), 44 | )(input) 45 | } 46 | 47 | fn binary_instruction(input: &str) -> IResult<&str, Instruction> { 48 | map( 49 | tuple(( 50 | alt((tag("add"), tag("mul"), tag("div"), tag("mod"), tag("eql"))), 51 | space1, 52 | register, 53 | space1, 54 | operand, 55 | opt(line_ending), 56 | )), 57 | |(instr, _, reg, _, val, _)| match instr { 58 | "add" => Instruction::Add(reg, val), 59 | "mul" => Instruction::Mul(reg, val), 60 | "div" => Instruction::Div(reg, val), 61 | "mod" => Instruction::Mod(reg, val), 62 | "eql" => Instruction::Equal(reg, val), 63 | _ => unreachable!("{}", instr), 64 | }, 65 | )(input) 66 | } 67 | 68 | fn instruction(input: &str) -> IResult<&str, Instruction> { 69 | alt((input_instruction, binary_instruction))(input) 70 | } 71 | 72 | pub fn parse_program(input: &str) -> Vec { 73 | let (remainder, program) = many1(instruction)(input).unwrap(); 74 | assert!(remainder.is_empty()); 75 | program 76 | } 77 | -------------------------------------------------------------------------------- /src/program.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::fmt::Display; 4 | 5 | /// A register in a MONAD instruction. 6 | /// Registers w, x, y, z are Register(0) through Register(3), respectively. 7 | #[derive(Debug, Clone, Copy)] 8 | pub struct Register(pub usize); 9 | 10 | impl Display for Register { 11 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 12 | let letter = match self.0 { 13 | 0 => "w", 14 | 1 => "x", 15 | 2 => "y", 16 | 3 => "z", 17 | _ => unreachable!("{:?}", self), 18 | }; 19 | 20 | write!(f, "{}", letter) 21 | } 22 | } 23 | 24 | /// The second operand of a MONAD instruction. 25 | /// Can be a literal number like the `2` in `add x 2`, 26 | /// or a register like the `y` in `add x y`. 27 | #[derive(Debug, Clone, Copy)] 28 | pub enum Operand { 29 | Literal(i64), 30 | Register(Register), 31 | } 32 | 33 | impl Display for Operand { 34 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 35 | match self { 36 | Operand::Literal(l) => write!(f, "{}", l), 37 | Operand::Register(r) => write!(f, "{}", *r), 38 | } 39 | } 40 | } 41 | 42 | /// An instruction in the MONAD language. 43 | /// See Advent of Code 2021 Day 24 for the spec: https://adventofcode.com/2021/day/24 44 | #[derive(Debug, Clone, Copy)] 45 | pub enum Instruction { 46 | Input(Register), // e.g. inp x 47 | Add(Register, Operand), // e.g. add x 2 48 | Mul(Register, Operand), // e.g. mul x 0 49 | Div(Register, Operand), // e.g. div x 10 50 | Mod(Register, Operand), // e.g. mod x 31 51 | Equal(Register, Operand), // e.g. eql x y 52 | } 53 | 54 | impl Display for Instruction { 55 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 56 | match self { 57 | Instruction::Input(r) => write!(f, "inp {}", *r), 58 | Instruction::Add(r, o) => write!(f, "add {} {}", *r, *o), 59 | Instruction::Mul(r, o) => write!(f, "mul {} {}", *r, *o), 60 | Instruction::Div(r, o) => write!(f, "div {} {}", *r, *o), 61 | Instruction::Mod(r, o) => write!(f, "mod {} {}", *r, *o), 62 | Instruction::Equal(r, o) => write!(f, "eql {} {}", *r, *o), 63 | } 64 | } 65 | } 66 | 67 | impl Instruction { 68 | #[inline] 69 | pub fn destination(&self) -> Register { 70 | *match self { 71 | Instruction::Input(r) => r, 72 | Instruction::Add(r, _) => r, 73 | Instruction::Mul(r, _) => r, 74 | Instruction::Div(r, _) => r, 75 | Instruction::Mod(r, _) => r, 76 | Instruction::Equal(r, _) => r, 77 | } 78 | } 79 | 80 | #[inline] 81 | pub fn operand(&self) -> Option { 82 | match self { 83 | Instruction::Input(_) => None, 84 | Instruction::Add(_, o) => Some(*o), 85 | Instruction::Mul(_, o) => Some(*o), 86 | Instruction::Div(_, o) => Some(*o), 87 | Instruction::Mod(_, o) => Some(*o), 88 | Instruction::Equal(_, o) => Some(*o), 89 | } 90 | } 91 | } 92 | 93 | /// We can't impl `Display` for `&[Instruction]`, so we have to make a newtype for it. 94 | pub struct InstructionStream<'a>(pub &'a [Instruction]); 95 | 96 | impl<'a> Display for InstructionStream<'a> { 97 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 98 | for instr in self.0.iter() { 99 | writeln!(f, "{}", instr)?; 100 | } 101 | Ok(()) 102 | } 103 | } 104 | 105 | impl<'a> From<&'a [Instruction]> for InstructionStream<'a> { 106 | fn from(x: &'a [Instruction]) -> Self { 107 | Self(x) 108 | } 109 | } 110 | --------------------------------------------------------------------------------