initial commit

This commit is contained in:
Rowan 2025-01-04 15:00:18 -06:00
commit 250e878c0e
4 changed files with 338 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

7
Cargo.lock generated Normal file
View file

@ -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"

6
Cargo.toml Normal file
View file

@ -0,0 +1,6 @@
[package]
name = "dolllang"
version = "0.1.0"
edition = "2021"
[dependencies]

324
src/main.rs Normal file
View file

@ -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<dyn BufRead> {
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<Self, Self::Error> {
match value {
"-" => Ok(Self::Stdin(io::stdin())),
path => File::open(path).map(Self::File),
}
}
}
impl TryFrom<String> for Input {
type Error = io::Error;
fn try_from(value: String) -> StdResult<Self, Self::Error> {
value.as_str().try_into()
}
}
#[derive(Debug)]
struct Tape<T>(T);
impl<T: Read> Tape<T> {
pub fn new(value: T) -> Self {
Self(value)
}
}
impl<T: Read> Iterator for Tape<T> {
type Item = u8;
fn next(&mut self) -> Option<Self::Item> {
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<Box<dyn Error>>,
}
impl ProgramError {
pub fn new(kind: ProgramErrorKind, source: impl Into<Box<dyn Error>>) -> Self {
Self {
kind,
source: Some(source.into()),
}
}
}
impl From<ProgramErrorKind> for ProgramError {
fn from(kind: ProgramErrorKind) -> Self {
Self { kind, source: None }
}
}
impl From<TryFromIntError> 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<T> = std::result::Result<T, ProgramError>;
#[derive(PartialEq)]
enum State {
Continue(usize),
Halt,
}
#[derive(Debug)]
struct Program {
memory: Vec<isize>,
index: usize,
}
impl Program {
fn word_length(mut word: impl Iterator<Item = u8>) -> Result<isize> {
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<Item = u8>) -> Result<isize> {
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<Item = u8>) -> Result<Vec<isize>> {
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<Item = u8>) -> Result<Self> {
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<usize> {
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<usize> {
match usize::try_from(n) {
Ok(n) => self.get_as_usize(n),
Err(e) => Err(e.into()),
}
}
fn step(&mut self) -> Result<State> {
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<String> = env::args().collect();
let program: io::Result<Input> = 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("<halt>");
}
#[cfg(test)]
mod tests {}