Skip to content

Commit

Permalink
feat: interpret escape characters in print, write and save
Browse files Browse the repository at this point in the history
  • Loading branch information
arthurfiorette committed Mar 6, 2022
1 parent c795ae6 commit c10a792
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 47 deletions.
6 changes: 5 additions & 1 deletion packages/brainease/tests/resources/write-char.brain
@@ -1,5 +1,5 @@
# Second line is expected output and third line is provided input.
# A B C 65 66 67
# A B C 65 66 67 1 2
#

print 'A'
Expand All @@ -13,3 +13,7 @@ print ' '
write 'B'
print ' '
write 'C'
print ' '
print '1'
print ' '
print '2'
39 changes: 1 addition & 38 deletions packages/lexer/src/syntax/cell_char.rs
@@ -1,7 +1,6 @@
use std::str::FromStr;

use super::{CellOrPointer, CellPosition};

/// A simple enum that may contains a Char value or a Cell indicator.
#[derive(Debug, Clone, PartialEq)]
pub enum CellOrChar {
Cell(CellOrPointer),
Expand All @@ -18,25 +17,6 @@ impl CellOrChar {
}
}

impl FromStr for CellOrChar {
type Err = ();

/// Parses the given string into an cell or char enum.
fn from_str(s: &str) -> Result<Self, Self::Err> {
if let Some(char) = s.chars().next() {
if !char.is_numeric() && char != '@' {
return Ok(CellOrChar::Char(char));
}

if let Ok(cell_or_pointer) = CellOrPointer::from_str(s) {
return Ok(CellOrChar::Cell(cell_or_pointer));
}
}

Err(())
}
}

impl ToString for CellOrChar {
fn to_string(&self) -> String {
match self {
Expand All @@ -45,20 +25,3 @@ impl ToString for CellOrChar {
}
}
}

#[cfg(test)]
pub mod tests {
use super::*;
#[test]

pub fn test() {
assert_eq!(
CellOrChar::from_str("2"),
Ok(CellOrChar::Cell(CellOrPointer::Cell(2)))
);

assert_eq!(CellOrChar::from_str("@"), Ok(CellOrChar::pointer()));
assert_eq!(CellOrChar::from_str("A"), Ok(CellOrChar::Char('A')));
assert_eq!(CellOrChar::from_str(" "), Ok(CellOrChar::Char(' ')));
}
}
26 changes: 23 additions & 3 deletions packages/lexer/src/token/print.rs
@@ -1,6 +1,9 @@
use lazy_regex::{regex, Captures, Lazy, Regex};

use crate::syntax::{CellOrChar, Instruction};
use crate::{
syntax::{CellOrChar, Instruction},
util::interpret_escape_chars,
};

use super::Token;

Expand All @@ -11,8 +14,9 @@ impl Token for PrintToken {
fn name(&self) -> &'static str {
"print"
}

fn regex(&self) -> &'static Lazy<Regex> {
static REGEX: &Lazy<Regex> = regex!(r"^print\s(?:(?:\*(\d+|@))|'(.)')\s*$");
static REGEX: &Lazy<Regex> = regex!(r"^print\s(?:(?:\*(\d+|@))|'(\\?.)')\s*$");
REGEX
}

Expand All @@ -26,7 +30,11 @@ impl Token for PrintToken {
let cell: CellOrChar = if let Some(c) = captures.get(1) {
CellOrChar::Cell(c.as_str().parse().unwrap())
} else {
captures[2].parse::<CellOrChar>().unwrap()
CellOrChar::Char(
interpret_escape_chars(&captures[2])
.parse::<char>()
.unwrap(),
)
};

(line_index + 1, Some(Instruction::Print(cell)))
Expand Down Expand Up @@ -71,6 +79,18 @@ pub mod tests {
assert!(captures.get(1).is_none());
assert_eq!(&captures[2], "H");

let (token, captures) = find_match("print ' '").unwrap();

assert_eq!(token, &PrintToken);
assert!(captures.get(1).is_none());
assert_eq!(&captures[2], " ");

let (token, captures) = find_match("print '1'").unwrap();

assert_eq!(token, &PrintToken);
assert!(captures.get(1).is_none());
assert_eq!(&captures[2], "1");

let (token, captures) = find_match("print *@").unwrap();

assert_eq!(token, &PrintToken);
Expand Down
7 changes: 5 additions & 2 deletions packages/lexer/src/token/save.rs
@@ -1,6 +1,6 @@
use lazy_regex::{regex, Captures, Lazy, Regex};

use crate::{logger, syntax::Instruction};
use crate::{logger, syntax::Instruction, util::interpret_escape_chars};

use super::Token;

Expand All @@ -23,7 +23,10 @@ impl Token for SaveToken {
line_index: usize,
_: usize,
) -> (usize, Option<Instruction>) {
let char = captures[1].chars().next().unwrap();
let char = interpret_escape_chars(&captures[1])
.parse::<char>()
.unwrap();

let cell = captures[2].parse().unwrap();

if char as usize > u8::MAX as usize {
Expand Down
22 changes: 19 additions & 3 deletions packages/lexer/src/token/write.rs
@@ -1,6 +1,6 @@
use lazy_regex::{regex, Captures, Lazy, Regex};

use crate::syntax::{CellOrChar, Instruction};
use crate::{syntax::{CellOrChar, Instruction}, util::interpret_escape_chars};

use super::Token;

Expand All @@ -13,7 +13,7 @@ impl Token for WriteToken {
}

fn regex(&self) -> &'static Lazy<Regex> {
static REGEX: &Lazy<Regex> = regex!(r"^write\s(?:(?:\*(\d+|@))|'(.)')\s*$");
static REGEX: &Lazy<Regex> = regex!(r"^write\s(?:(?:\*(\d+|@))|'(\\?.)')\s*$");
REGEX
}

Expand All @@ -27,7 +27,11 @@ impl Token for WriteToken {
let cell: CellOrChar = if let Some(c) = captures.get(1) {
CellOrChar::Cell(c.as_str().parse().unwrap())
} else {
captures[2].parse().unwrap()
CellOrChar::Char(
interpret_escape_chars(&captures[2])
.parse::<char>()
.unwrap(),
)
};

(line_index + 1, Some(Instruction::Write(cell)))
Expand Down Expand Up @@ -70,6 +74,18 @@ pub mod tests {
assert!(captures.get(1).is_none());
assert_eq!(&captures[2], "H");

let (token, captures) = find_match("write ' '").unwrap();

assert_eq!(token, &WriteToken);
assert!(captures.get(1).is_none());
assert_eq!(&captures[2], " ");

let (token, captures) = find_match("write '1'").unwrap();

assert_eq!(token, &WriteToken);
assert!(captures.get(1).is_none());
assert_eq!(&captures[2], "1");

let (token, captures) = find_match("write *@").unwrap();

assert_eq!(token, &WriteToken);
Expand Down
38 changes: 38 additions & 0 deletions packages/lexer/src/util.rs
Expand Up @@ -19,6 +19,18 @@ pub fn match_indentation(spaces: usize, line: &str) -> bool {
chars.next().map_or(true, |c| !c.is_whitespace())
}

/// Replaces all characters that should be escaped with their escaped version.
///
/// https://doc.rust-lang.org/reference/tokens.html#ascii-escapes
pub fn interpret_escape_chars(text: &str) -> String {
text
.replace("\\n", "\n")
.replace("\\t", "\t")
.replace("\\r", "\r")
.replace("\\\\", "\\")
.replace("\\0", "\0")
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down Expand Up @@ -105,4 +117,30 @@ mod tests {
}
}
}

#[test]
fn test_escape_characters() {
let characters = [r#"\n"#, r#"\r"#, r#"\t"#, r#"\\"#, r#"\0"#];
let escaped = ["\n", "\r", "\t", "\\", "\0"];

for (c1, e1) in characters.iter().zip(escaped.iter()) {
for (c2, e2) in characters.iter().zip(escaped.iter()) {
for (c3, e3) in characters.iter().zip(escaped.iter()) {
for (c4, e4) in characters.iter().zip(escaped.iter()) {
let original = format!(
"random text {} between {} all {} escape {} characters",
c1, c2, c3, c4
);

let expected = format!(
"random text {} between {} all {} escape {} characters",
e1, e2, e3, e4
);

assert_eq!(interpret_escape_chars(&original), expected);
}
}
}
}
}
}

0 comments on commit c10a792

Please sign in to comment.