commit 250e878c0e8327873bccd727797cfeddf7527daa Author: rowan Date: Sat Jan 4 15:00:18 2025 -0600 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2f62f9d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "dolllang" +version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7165007 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "dolllang" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..e55614b --- /dev/null +++ b/src/main.rs @@ -0,0 +1,324 @@ +use core::{fmt, slice}; +use std::error::Error; +use std::fmt::Debug; +use std::fs::File; +use std::io::{BufRead, BufReader, Read}; +use std::num::TryFromIntError; +use std::result::Result as StdResult; +use std::{env, fs, io}; + +enum Input { + File(fs::File), + Stdin(io::Stdin), +} + +impl Input { + fn into_reader(self) -> Box { + match self { + Input::File(file) => Box::new(BufReader::new(file)), + Input::Stdin(stdin) => Box::new(stdin.lock()), + } + } +} + +impl TryFrom<&str> for Input { + type Error = io::Error; + + fn try_from(value: &str) -> StdResult { + match value { + "-" => Ok(Self::Stdin(io::stdin())), + path => File::open(path).map(Self::File), + } + } +} + +impl TryFrom for Input { + type Error = io::Error; + + fn try_from(value: String) -> StdResult { + value.as_str().try_into() + } +} + +#[derive(Debug)] +struct Tape(T); + +impl Tape { + pub fn new(value: T) -> Self { + Self(value) + } +} + +impl Iterator for Tape { + type Item = u8; + + fn next(&mut self) -> Option { + let mut byte = 0u8; + match self.0.read(slice::from_mut(&mut byte)) { + Ok(0usize) => None, + Ok(_) => Some(byte), + Err(_) => None, + } + } +} + +#[derive(Debug)] +enum ProgramErrorKind { + InvalidSyntax, + IllegalMemoryAccess(isize), + ConversionError, + EndOfFile, +} + +impl fmt::Display for ProgramErrorKind { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::InvalidSyntax => write!(f, "invalid syntax"), + Self::EndOfFile => write!(f, "end of file"), + Self::IllegalMemoryAccess(loc) => write!(f, "illegal memory access @ {loc}"), + Self::ConversionError => todo!(), + } + } +} + +#[derive(Debug)] +struct ProgramError { + kind: ProgramErrorKind, + source: Option>, +} + +impl ProgramError { + pub fn new(kind: ProgramErrorKind, source: impl Into>) -> Self { + Self { + kind, + source: Some(source.into()), + } + } +} + +impl From for ProgramError { + fn from(kind: ProgramErrorKind) -> Self { + Self { kind, source: None } + } +} + +impl From for ProgramError { + fn from(value: TryFromIntError) -> Self { + Self::new(ProgramErrorKind::ConversionError, value) + } +} + +impl fmt::Display for ProgramError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ({:?})", self.kind, self.source) + } +} + +impl Error for ProgramError {} + +type Result = std::result::Result; + +#[derive(PartialEq)] +enum State { + Continue(usize), + Halt, +} + +#[derive(Debug)] +struct Program { + memory: Vec, + index: usize, +} + +impl Program { + fn word_length(mut word: impl Iterator) -> Result { + let result = word.try_fold((0isize, true, true), |(count, was_w, pos), byte| { + match (byte, was_w, pos) { + (b'a', true, true) => Some((count, false, true)), + (b'w', false, true) => Some((count + 1, true, true)), + (b'!', false, true) => Some((count, false, false)), + _ => None, + } + }); + + let sign = match result { + Some((_, _, false)) => -1, + _ => 1, + }; + + match result { + Some((count, false, _)) => Ok(count * sign), + _ => Err(ProgramError::from(ProgramErrorKind::InvalidSyntax)), + } + } + + fn next_word(program: impl Iterator) -> Result { + let mut word = program + .skip_while(|ch| *ch != b'a' && *ch != b'w' && *ch != b'!') + .take_while(|ch| *ch == b'a' || *ch == b'w' || *ch == b'!') + .peekable(); + + if word.peek().is_none() { + Err(ProgramError::from(ProgramErrorKind::EndOfFile)) + } else { + Self::word_length(word) + } + } + + fn parse(mut program: impl Iterator) -> Result> { + let mut memory = Vec::new(); + + loop { + match Self::next_word(&mut program) { + Ok(word) => memory.push(word), + Err(ProgramError { + kind: ProgramErrorKind::EndOfFile, + .. + }) => break, + Err(e) => return Err(e), + } + } + + Ok(memory) + } + + pub fn from_program(program: impl Iterator) -> Result { + Ok(Self { + memory: Self::parse(program)?, + index: 0, + }) + } + + fn next_instruction(&self) -> Option<(isize, isize, isize)> { + let [a, b, c] = self.memory.get(self.index..=self.index + 2)? else { + return None; + }; + + Some((*a, *b, *c)) + } + + fn set(&mut self, addr: usize, value: isize) -> Result<()> { + if addr > self.memory.len() { + Err(ProgramErrorKind::IllegalMemoryAccess(addr as isize).into()) + } else { + self.memory[addr] = value; + Ok(()) + } + } + + fn set_isize(&mut self, addr: isize, value: isize) -> Result<()> { + if addr < 0 { + Err(ProgramErrorKind::IllegalMemoryAccess(addr).into()) + } else { + match usize::try_from(addr) { + Ok(addr) => self.set(addr, value), + Err(e) => Err(ProgramError::new(ProgramErrorKind::ConversionError, e)), + } + } + } + + fn set_index(&mut self, value: isize) -> Result<()> { + if value < 0 { + Err(ProgramErrorKind::IllegalMemoryAccess(value).into()) + } else { + match usize::try_from(value) { + Ok(value) => { + self.index = value; + Ok(()) + } + Err(e) => Err(ProgramError::new(ProgramErrorKind::ConversionError, e)), + } + } + } + + fn get(&self, n: usize) -> Result<&isize> { + match self.memory.get(n) { + Some(n) => Ok(n), + None => Err(ProgramError::from(ProgramErrorKind::IllegalMemoryAccess( + n as isize, + ))), + } + } + + fn get_isize(&self, n: isize) -> Result<&isize> { + match usize::try_from(n) { + Ok(m) => self.get(m), + Err(_) => Err(ProgramError::from(ProgramErrorKind::IllegalMemoryAccess(n))), + } + } + + fn get_as_usize(&self, n: usize) -> Result { + match self.get(n) { + Ok(n) => match usize::try_from(*n) { + Ok(m) => Ok(m), + Err(e) => Err(e.into()), + }, + Err(e) => Err(e), + } + } + + fn get_isize_as_usize(&self, n: isize) -> Result { + match usize::try_from(n) { + Ok(n) => self.get_as_usize(n), + Err(e) => Err(e.into()), + } + } + + fn step(&mut self) -> Result { + if let Some((a, b, c)) = self.next_instruction() { + if b == -1 && a > -1 { + let ax = self.get_isize_as_usize(a)?.try_into()?; + char::from_u32(ax).inspect(|c| print!("{c}")); + + let counter = c.try_into()?; + Ok(State::Continue(counter)) + } else if a > -1 && b > -1 { + let ax = self.get_isize(a)?; + let bx = self.get_isize(b)?; + let next = bx - ax; + self.set_isize(b, next)?; + + if next > 0 { + Ok(State::Continue(self.index + 3)) + } else { + let counter = c.try_into()?; + Ok(State::Continue(counter)) + } + } else { + Ok(State::Halt) + } + } else { + Ok(State::Halt) + } + } + + pub fn execute(&mut self) -> Result<()> { + loop { + match self.step() { + Ok(State::Continue(next)) => { + self.index = next; + } + Ok(State::Halt) => break, + Err(e) => return Err(e), + } + } + + Ok(()) + } +} + +fn main() { + let args: Vec = env::args().collect(); + let program: io::Result = args[1].as_str().try_into(); + + let program: &mut dyn Read = match program { + Ok(input) => &mut input.into_reader(), + Err(_) => &mut args[1].as_bytes(), + }; + + let mut machine = Program::from_program(Tape::new(program)).expect("failed to load program"); + machine.execute().expect(""); +} + +#[cfg(test)] +mod tests {}