Add initial functions for the CharTapeMachine

master
trivernis 4 years ago
commit d1cd958e86

3
.gitignore vendored

@ -0,0 +1,3 @@
/target
Cargo.lock
.idea

@ -0,0 +1,9 @@
[package]
name = "charred"
version = "0.1.0"
authors = ["trivernis <trivernis@protonmail.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

@ -0,0 +1,5 @@
mod tapemachine;
#[cfg(test)]
mod tests {
}

@ -0,0 +1,276 @@
use std::error::Error;
use std::fmt::{self, Display, Formatter};
#[derive(Debug)]
pub struct TapeError {
index: usize
}
impl TapeError {
pub fn new(index: usize) -> Self {
Self {
index
}
}
}
impl Display for TapeError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Tape Error at: {}", self.index)
}
}
impl Error for TapeError {}
pub type TapeResult<T> = Result<T, TapeError>;
const ESCAPE: char = '\\';
pub struct CharTapeMachine {
index: usize,
text: Vec<char>,
current_char: char,
previous_char: char,
}
impl CharTapeMachine {
pub fn new(text: Vec<char>) -> Self {
let current_char = if text.len() > 0 {
*text.first().unwrap()
} else {
' '
};
Self {
text,
index: 0,
previous_char: current_char,
current_char,
}
}
/// Returns the next char
/// if there is any
pub fn next_char(&mut self) -> Option<char> {
if self.index < self.text.len() {
self.index += 1;
self.previous_char = self.current_char;
self.current_char = *self.text.get(self.index)?;
Some(self.current_char)
} else {
None
}
}
/// Peeks the next available char
#[inline]
pub fn peek_char(&mut self) -> Option<char> {
Some(*self.text.get(self.index + 1)?)
}
/// Rewinds to a given index
#[inline]
pub fn rewind(&mut self, index: usize) {
self.index = index;
self.current_char = *self.text.get(index).unwrap();
}
/// Rewinds to a given index and returns an error
#[inline]
pub fn rewind_with_error(&mut self, index: usize) -> TapeError {
self.rewind(index);
TapeError::new(index)
}
/// Seeks one character or returns an error
/// if there is no next character
#[inline]
pub fn seek_one(&mut self) -> TapeResult<()> {
if let Some(_) = self.next_char() {
Ok(())
} else {
Err(TapeError::new(self.index))
}
}
/// Seeks until it encounters a non whitespace character
pub fn seek_whitespace(&mut self) {
if self.current_char.is_whitespace() {
while let Some(next) = self.next_char() {
if !next.is_whitespace() || self.check_escaped() {
break
}
}
}
}
/// checks if the current char is escaped
#[inline]
pub fn check_escaped(&mut self) -> bool {
self.previous_char == ESCAPE
}
/// Returns true if the given character is equal to the current one
/// and the current character is not escaped
#[inline]
pub fn check_char(&mut self, value: &char) -> bool {
self.current_char == *value && !self.check_escaped()
}
/// Checks if one of the given chars matches the current one
#[inline]
pub fn check_any(&mut self, chars: &[char]) -> bool {
!self.check_escaped() && chars.contains(&self.current_char)
}
/// checks if the next characters match a given sequence of characters
pub fn check_sequence(&mut self, sequence: &[char]) -> bool {
let start_index = self.index;
if self.check_escaped() {
self.rewind(start_index);
false
} else {
for sq_character in sequence {
if self.current_char != *sq_character {
self.rewind(start_index);
return false;
}
if self.next_char() == None {
self.rewind(start_index);
return false;
}
}
if self.index > 0 {
self.rewind(self.index - 1);
}
true
}
}
/// checks if the next characters match any given sequence
#[inline]
pub fn check_any_sequence(&mut self, sequences: &[&[char]]) -> bool {
for seq in sequences {
if self.check_sequence(*seq) {
return true;
}
}
false
}
/// returns an error on the current position and optionally rewinds
/// if a rewind index is given
#[inline]
fn assert_error(&mut self, rewind_index: Option<usize>) -> TapeError {
if let Some(index) = rewind_index {
self.rewind_with_error(index)
} else {
TapeError::new(self.index)
}
}
/// returns an error if the given char doesn't match the current one and rewinds
/// if a rewind index is given
#[inline]
pub fn assert_char(&mut self, value: &char, rewind_index: Option<usize>) -> TapeResult<()> {
if self.check_char(value) {
Ok(())
} else {
Err(self.assert_error(rewind_index))
}
}
/// returns an error if the current char doesn't match any of the given group
#[inline]
pub fn assert_any(&mut self, chars: &[char], rewind_index: Option<usize>) -> TapeResult<()> {
if self.check_any(chars) {
Ok(())
} else {
Err(self.assert_error(rewind_index))
}
}
/// returns an error if the next chars don't match a special sequence
#[inline]
pub fn assert_sequence(&mut self, sequence: &[char], rewind_index: Option<usize>) -> TapeResult<()> {
if self.check_sequence(sequence) {
Ok(())
} else {
Err(self.assert_error(rewind_index))
}
}
/// returns an error if the next chars don't match any given sequence
pub fn assert_any_sequence(&mut self, sequences: &[&[char]], rewind_index: Option<usize>) -> TapeResult<()> {
if self.check_any_sequence(sequences) {
Ok(())
} else {
Err(self.assert_error(rewind_index))
}
}
/// returns the string until any given character is matched is matched.
/// rewinds with error if it encounters a character form the error group
pub fn get_string_until_any(
&mut self,
until: &[char],
err_at: &[char],
) -> TapeResult<String> {
let start_index = self.index;
let mut result = String::new();
if self.check_any(until) {
return Ok(result);
} else if self.check_any(err_at) {
return Err(TapeError::new(self.index));
}
result.push(self.current_char);
while let Some(ch) = self.next_char() {
if self.check_any(until) || self.check_any(err_at) {
break;
}
result.push(ch);
}
if self.check_any(err_at) {
Err(self.rewind_with_error(start_index))
} else {
Ok(result)
}
}
/// Returns the string until it encounters a given sequence or rewinds with error
/// if it encounters an err sequence
fn get_string_until_sequence(
&mut self,
until: &[&[char]],
err_at: &[&[char]],
) -> Result<String, TapeError> {
let start_index = self.index;
let mut result = String::new();
if self.check_any_sequence(until) {
return Ok(result);
} else if self.check_any_sequence(err_at) {
return Err(TapeError::new(self.index));
}
result.push(self.current_char);
while let Some(ch) = self.next_char() {
if self.check_any_sequence(until) || self.check_any_sequence(err_at) {
break;
}
result.push(ch);
}
if self.check_any_sequence(err_at) {
Err(self.rewind_with_error(start_index))
} else {
Ok(result)
}
}
}
Loading…
Cancel
Save