mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 21:35:02 +01:00
spans and tests
This commit is contained in:
parent
e82b14b09a
commit
5634330287
10 changed files with 300 additions and 112 deletions
7
rust2/src/fizzbuzz.bf
Normal file
7
rust2/src/fizzbuzz.bf
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
++++++++++[>++++++++++<-]>>++++++++++>->>>>>>>>>>>>>>>>-->+++++++[->++\n++++++++<]>[->+>+>+>+<<<<]+++>>+++>>>++++++++[-<
|
||||
++++<++++<++++>>>]++++\n+[-<++++<++++>>]>>-->++++++[->+++++++++++<]>[->+>+>+>+<<<<]+++++>>+>++\n++++>++++++>++++++++[-<+
|
||||
+++<++++<++++>>>]++++++[-<+++<+++<+++>>>]>>-->\n---+[-<+]-<[+[->+]-<<->>>+>[-]++[-->++]-->+++[---++[--<++]---->>-<+>[+\n
|
||||
+++[----<++++]--[>]++[-->++]--<]>++[--+[-<+]->>[-]+++++[---->++++]-->[\n->+<]>>[.>]++[-->++]]-->+++]---+[-<+]->>-[+>>>+[
|
||||
-<+]->>>++++++++++<<[-\n>+>-[>+>>]>[+[-<+>]>+>>]<<<<<<]>>[-]>>>++++++++++<[->-[>+>>]>[+[-<+>]>\n+>>]<<<<<]>[-]>>[>++++++
|
||||
[-<++++++++>]<.<<+>+>[-]]<[<[->-<]++++++[->+++\n+++++<]>.[-]]<<++++++[-<++++++++>]<.[-]<<[-<+>]+[-<+]->>]+[-]<<<.>>>+[\n
|
||||
-<+]-<<]
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
use crate::opts::Stmt;
|
||||
use crate::opts::{Ir, Stmt};
|
||||
use std::io::{Read, Write};
|
||||
use std::num::Wrapping;
|
||||
|
||||
|
|
@ -6,7 +6,7 @@ const MEM_SIZE: usize = 32_000;
|
|||
|
||||
type Memory = [Wrapping<u8>; MEM_SIZE];
|
||||
|
||||
pub fn run<W, R>(instrs: &[Stmt<'_>], mut stdout: W, mut stdin: R)
|
||||
pub fn run<W, R>(instrs: &Ir<'_>, mut stdout: W, mut stdin: R)
|
||||
where
|
||||
W: Write,
|
||||
R: Read,
|
||||
|
|
@ -14,7 +14,7 @@ where
|
|||
let mut mem = [Wrapping(0u8); MEM_SIZE];
|
||||
let mut ptr = 0;
|
||||
|
||||
execute(&mut mem, &mut ptr, instrs, &mut stdout, &mut stdin);
|
||||
execute(&mut mem, &mut ptr, &instrs.stmts, &mut stdout, &mut stdin);
|
||||
}
|
||||
|
||||
fn execute<W, R>(
|
||||
|
|
@ -61,7 +61,7 @@ fn execute<W, R>(
|
|||
}
|
||||
Stmt::Loop(body) => {
|
||||
while mem[*ptr] != Wrapping(0) {
|
||||
execute(mem, ptr, body, stdout, stdin);
|
||||
execute(mem, ptr, &body.stmts, stdout, stdin);
|
||||
}
|
||||
}
|
||||
Stmt::SetNull => {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,45 @@
|
|||
#![feature(allocator_api, let_else)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use crate::parse::ParseError;
|
||||
use bumpalo::Bump;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
pub mod ir_interpreter;
|
||||
pub mod opts;
|
||||
pub mod parse;
|
||||
|
||||
pub fn run<R, W>(bytes: impl Iterator<Item = u8>, stdout: W, stdin: R) -> Result<(), ParseError>
|
||||
where
|
||||
W: Write,
|
||||
R: Read,
|
||||
{
|
||||
let ast_alloc = Bump::new();
|
||||
|
||||
let parsed = parse::parse(&ast_alloc, bytes.enumerate())?;
|
||||
|
||||
let ir_alloc = Bump::new();
|
||||
|
||||
let optimized_ir = opts::optimize(&ir_alloc, &parsed);
|
||||
|
||||
drop(parsed);
|
||||
drop(ast_alloc);
|
||||
|
||||
ir_interpreter::run(&optimized_ir, stdout, stdin);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[test]
|
||||
fn fizzbuzz() {
|
||||
let str = include_str!("fizzbuzz.bf");
|
||||
let mut stdout = Vec::new();
|
||||
let stdin = [];
|
||||
|
||||
super::run(str.bytes(), &mut stdout, stdin.as_slice()).unwrap();
|
||||
|
||||
insta::assert_debug_snapshot!(String::from_utf8(stdout));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,11 +1,8 @@
|
|||
#![feature(allocator_api, let_else)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use bumpalo::Bump;
|
||||
use std::{env, fs, io, process};
|
||||
|
||||
use brainfuck::{ir_interpreter, opts, parse};
|
||||
|
||||
fn main() {
|
||||
let Some(path) = env::args().nth(1) else {
|
||||
eprintln!("error: Provide a path as input.");
|
||||
|
|
@ -17,24 +14,13 @@ fn main() {
|
|||
process::exit(1);
|
||||
});
|
||||
|
||||
let ast_alloc = Bump::new();
|
||||
|
||||
let parsed = parse::parse(&ast_alloc, file.bytes()).unwrap_or_else(|_| {
|
||||
eprintln!("Failed to parse brainfuck code.");
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
let ir_alloc = Bump::new();
|
||||
|
||||
let optimized_ir = opts::optimize(&ir_alloc, &parsed);
|
||||
|
||||
drop(parsed);
|
||||
drop(ast_alloc);
|
||||
|
||||
let stdout = io::stdout();
|
||||
let stdout = stdout.lock();
|
||||
let stdin = io::stdin();
|
||||
let stdin = stdin.lock();
|
||||
|
||||
ir_interpreter::run(&optimized_ir, stdout, stdin);
|
||||
brainfuck::run(file.bytes(), stdout, stdin).unwrap_or_else(|_| {
|
||||
eprintln!("error: Failed to parse brainfuck code");
|
||||
process::exit(1);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
use crate::parse::Instr;
|
||||
use crate::parse::{Instr, Span};
|
||||
use bumpalo::Bump;
|
||||
|
||||
pub type Ir<'ir> = Vec<Stmt<'ir>, &'ir Bump>;
|
||||
#[derive(Debug)]
|
||||
pub struct Ir<'ir> {
|
||||
pub stmts: Vec<Stmt<'ir>, &'ir Bump>,
|
||||
pub spans: Vec<Span, &'ir Bump>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Stmt<'ir> {
|
||||
|
|
@ -15,23 +19,27 @@ pub enum Stmt<'ir> {
|
|||
SetNull,
|
||||
}
|
||||
|
||||
pub fn optimize<'ir>(alloc: &'ir Bump, instrs: &[Instr<'_>]) -> Ir<'ir> {
|
||||
pub fn optimize<'ir>(alloc: &'ir Bump, instrs: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
||||
let mut ir = ast_to_ir(alloc, instrs);
|
||||
pass_find_set_null(&mut ir);
|
||||
ir
|
||||
}
|
||||
|
||||
fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[Instr<'_>]) -> Ir<'ir> {
|
||||
let mut ir = Vec::new_in(alloc);
|
||||
fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
||||
let mut stmts = Vec::new_in(alloc);
|
||||
let mut spans = Vec::new_in(alloc);
|
||||
|
||||
let mut instr_iter = ast.iter();
|
||||
|
||||
let Some(first) = instr_iter.next() else { return ir; };
|
||||
let Some(first) = instr_iter.next() else {
|
||||
return Ir { stmts, spans: Vec::new_in(alloc) };
|
||||
};
|
||||
|
||||
let mut last = first;
|
||||
let mut last = &first.0;
|
||||
let mut last_span = first.1;
|
||||
let mut count = 1;
|
||||
|
||||
for next in instr_iter {
|
||||
for (next, next_span) in instr_iter {
|
||||
match last {
|
||||
Instr::Add | Instr::Sub | Instr::Right | Instr::Left if last == next => {
|
||||
count += 1;
|
||||
|
|
@ -47,8 +55,10 @@ fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[Instr<'_>]) -> Ir<'ir> {
|
|||
Instr::In => Stmt::In,
|
||||
Instr::Loop(body) => Stmt::Loop(ast_to_ir(alloc, body)),
|
||||
};
|
||||
ir.push(new_last);
|
||||
stmts.push(new_last);
|
||||
spans.push(last_span.until(*next_span));
|
||||
last = next;
|
||||
last_span = *next_span;
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -61,17 +71,18 @@ fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[Instr<'_>]) -> Ir<'ir> {
|
|||
Instr::Left => Stmt::Left(count.into()),
|
||||
Instr::Out => Stmt::Out,
|
||||
Instr::In => Stmt::In,
|
||||
Instr::Loop(body) => Stmt::Loop(ast_to_ir(alloc, body)),
|
||||
Instr::Loop(body) => Stmt::Loop(ast_to_ir(alloc, &body)),
|
||||
};
|
||||
ir.push(new_last);
|
||||
stmts.push(new_last);
|
||||
spans.push(last_span);
|
||||
|
||||
ir
|
||||
Ir { stmts, spans }
|
||||
}
|
||||
|
||||
fn pass_find_set_null(ir: &mut Ir<'_>) {
|
||||
for stmt in ir {
|
||||
for stmt in &mut ir.stmts {
|
||||
if let Stmt::Loop(body) = stmt {
|
||||
if let [Stmt::Sub(_)] = body.as_slice() {
|
||||
if let [Stmt::Sub(_)] = body.stmts.as_slice() {
|
||||
*stmt = Stmt::SetNull;
|
||||
} else {
|
||||
pass_find_set_null(body);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,35 @@
|
|||
use bumpalo::Bump;
|
||||
|
||||
pub type Instrs<'ast> = Vec<Instr<'ast>, &'ast Bump>;
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Span {
|
||||
pub start: u32,
|
||||
pub len: u32,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
fn single(idx: usize) -> Self {
|
||||
Self {
|
||||
start: idx.try_into().unwrap(),
|
||||
len: 1,
|
||||
}
|
||||
}
|
||||
|
||||
fn start_end(start: usize, end: usize) -> Span {
|
||||
Self {
|
||||
start: start.try_into().unwrap(),
|
||||
len: (end - start).try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn until(&self, other: Self) -> Self {
|
||||
Self {
|
||||
start: self.start,
|
||||
len: (other.start + other.len) - self.len,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub type Instrs<'ast> = Vec<(Instr<'ast>, Span), &'ast Bump>;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum Instr<'ast> {
|
||||
|
|
@ -18,23 +47,23 @@ pub struct ParseError;
|
|||
|
||||
pub fn parse<I>(alloc: &Bump, mut src: I) -> Result<Instrs<'_>, ParseError>
|
||||
where
|
||||
I: Iterator<Item = u8>,
|
||||
I: Iterator<Item = (usize, u8)>,
|
||||
{
|
||||
let mut instrs = Vec::new_in(alloc);
|
||||
|
||||
loop {
|
||||
match src.next() {
|
||||
Some(b'+') => instrs.push(Instr::Add),
|
||||
Some(b'-') => instrs.push(Instr::Sub),
|
||||
Some(b'>') => instrs.push(Instr::Right),
|
||||
Some(b'<') => instrs.push(Instr::Left),
|
||||
Some(b'.') => instrs.push(Instr::Out),
|
||||
Some(b',') => instrs.push(Instr::In),
|
||||
Some(b'[') => {
|
||||
let loop_instrs = parse_loop(alloc, &mut src, 0)?;
|
||||
instrs.push(Instr::Loop(loop_instrs));
|
||||
Some((idx, b'+')) => instrs.push((Instr::Add, Span::single(idx))),
|
||||
Some((idx, b'-')) => instrs.push((Instr::Sub, Span::single(idx))),
|
||||
Some((idx, b'>')) => instrs.push((Instr::Right, Span::single(idx))),
|
||||
Some((idx, b'<')) => instrs.push((Instr::Left, Span::single(idx))),
|
||||
Some((idx, b'.')) => instrs.push((Instr::Out, Span::single(idx))),
|
||||
Some((idx, b',')) => instrs.push((Instr::In, Span::single(idx))),
|
||||
Some((idx, b'[')) => {
|
||||
let (loop_instrs, span) = parse_loop(alloc, &mut src, 0, idx)?;
|
||||
instrs.push((Instr::Loop(loop_instrs), span));
|
||||
}
|
||||
Some(b']') => return Err(ParseError),
|
||||
Some((_, b']')) => return Err(ParseError),
|
||||
Some(_) => {} // comment
|
||||
None => break,
|
||||
}
|
||||
|
|
@ -47,9 +76,10 @@ fn parse_loop<'ast, I>(
|
|||
alloc: &'ast Bump,
|
||||
src: &mut I,
|
||||
depth: u16,
|
||||
) -> Result<Instrs<'ast>, ParseError>
|
||||
start_idx: usize,
|
||||
) -> Result<(Instrs<'ast>, Span), ParseError>
|
||||
where
|
||||
I: Iterator<Item = u8>,
|
||||
I: Iterator<Item = (usize, u8)>,
|
||||
{
|
||||
const MAX_DEPTH: u16 = 1000;
|
||||
|
||||
|
|
@ -59,25 +89,25 @@ where
|
|||
|
||||
let mut instrs = Vec::new_in(alloc);
|
||||
|
||||
loop {
|
||||
let end_idx = loop {
|
||||
match src.next() {
|
||||
Some(b'+') => instrs.push(Instr::Add),
|
||||
Some(b'-') => instrs.push(Instr::Sub),
|
||||
Some(b'>') => instrs.push(Instr::Right),
|
||||
Some(b'<') => instrs.push(Instr::Left),
|
||||
Some(b'.') => instrs.push(Instr::Out),
|
||||
Some(b',') => instrs.push(Instr::In),
|
||||
Some(b'[') => {
|
||||
let loop_instrs = parse_loop(alloc, src, depth + 1)?;
|
||||
instrs.push(Instr::Loop(loop_instrs));
|
||||
Some((idx, b'+')) => instrs.push((Instr::Add, Span::single(idx))),
|
||||
Some((idx, b'-')) => instrs.push((Instr::Sub, Span::single(idx))),
|
||||
Some((idx, b'>')) => instrs.push((Instr::Right, Span::single(idx))),
|
||||
Some((idx, b'<')) => instrs.push((Instr::Left, Span::single(idx))),
|
||||
Some((idx, b'.')) => instrs.push((Instr::Out, Span::single(idx))),
|
||||
Some((idx, b',')) => instrs.push((Instr::In, Span::single(idx))),
|
||||
Some((idx, b'[')) => {
|
||||
let (loop_instrs, span) = parse_loop(alloc, src, depth + 1, idx)?;
|
||||
instrs.push((Instr::Loop(loop_instrs), span));
|
||||
}
|
||||
Some(b']') => break,
|
||||
Some((idx, b']')) => break idx,
|
||||
Some(_) => {} // comment
|
||||
None => return Err(ParseError),
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Ok(instrs)
|
||||
Ok((instrs, Span::start_end(start_idx, end_idx)))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
|
@ -89,7 +119,7 @@ mod tests {
|
|||
let alloc = Bump::new();
|
||||
|
||||
let bf = ">+<++[-].";
|
||||
let instrs = super::parse(&alloc, bf.bytes());
|
||||
let instrs = super::parse(&alloc, bf.bytes().enumerate());
|
||||
insta::assert_debug_snapshot!(instrs);
|
||||
}
|
||||
|
||||
|
|
@ -98,7 +128,7 @@ mod tests {
|
|||
let alloc = Bump::new();
|
||||
|
||||
let bf = "+[-[-[-]]+>>>]";
|
||||
let instrs = super::parse(&alloc, bf.bytes());
|
||||
let instrs = super::parse(&alloc, bf.bytes().enumerate());
|
||||
insta::assert_debug_snapshot!(instrs);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,95 @@
|
|||
---
|
||||
source: src/parse.rs
|
||||
assertion_line: 102
|
||||
assertion_line: 132
|
||||
expression: instrs
|
||||
---
|
||||
Ok(
|
||||
[
|
||||
Add,
|
||||
Loop(
|
||||
[
|
||||
Sub,
|
||||
Loop(
|
||||
[
|
||||
(
|
||||
Add,
|
||||
Span {
|
||||
start: 0,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Loop(
|
||||
[
|
||||
(
|
||||
Sub,
|
||||
Span {
|
||||
start: 2,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Loop(
|
||||
[
|
||||
Sub,
|
||||
(
|
||||
Sub,
|
||||
Span {
|
||||
start: 4,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Loop(
|
||||
[
|
||||
(
|
||||
Sub,
|
||||
Span {
|
||||
start: 6,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Span {
|
||||
start: 5,
|
||||
len: 2,
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
Add,
|
||||
Right,
|
||||
Right,
|
||||
Right,
|
||||
],
|
||||
Span {
|
||||
start: 3,
|
||||
len: 5,
|
||||
},
|
||||
),
|
||||
(
|
||||
Add,
|
||||
Span {
|
||||
start: 9,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Right,
|
||||
Span {
|
||||
start: 10,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Right,
|
||||
Span {
|
||||
start: 11,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Right,
|
||||
Span {
|
||||
start: 12,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Span {
|
||||
start: 1,
|
||||
len: 12,
|
||||
},
|
||||
),
|
||||
],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,20 +1,68 @@
|
|||
---
|
||||
source: src/parse.rs
|
||||
assertion_line: 93
|
||||
assertion_line: 123
|
||||
expression: instrs
|
||||
---
|
||||
Ok(
|
||||
[
|
||||
Right,
|
||||
Add,
|
||||
Left,
|
||||
Add,
|
||||
Add,
|
||||
Loop(
|
||||
[
|
||||
Sub,
|
||||
],
|
||||
(
|
||||
Right,
|
||||
Span {
|
||||
start: 0,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Add,
|
||||
Span {
|
||||
start: 1,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Left,
|
||||
Span {
|
||||
start: 2,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Add,
|
||||
Span {
|
||||
start: 3,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Add,
|
||||
Span {
|
||||
start: 4,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
(
|
||||
Loop(
|
||||
[
|
||||
(
|
||||
Sub,
|
||||
Span {
|
||||
start: 6,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
Span {
|
||||
start: 5,
|
||||
len: 2,
|
||||
},
|
||||
),
|
||||
(
|
||||
Out,
|
||||
Span {
|
||||
start: 8,
|
||||
len: 1,
|
||||
},
|
||||
),
|
||||
Out,
|
||||
],
|
||||
)
|
||||
|
|
|
|||
8
rust2/src/snapshots/brainfuck__tests__fizzbuzz.snap
Normal file
8
rust2/src/snapshots/brainfuck__tests__fizzbuzz.snap
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
---
|
||||
source: src/lib.rs
|
||||
assertion_line: 43
|
||||
expression: "String::from_utf8(stdout)"
|
||||
---
|
||||
Ok(
|
||||
"1\n2\nFizz\n4\nBuzz\nFizz\n7\n8\nFizz\nBuzz\n11\nFizz\n13\n14\nFizzBuzz\n16\n17\nFizz\n19\nBuzz\nFizz\n22\n23\nFizz\nBuzz\n26\nFizz\n28\n29\nFizzBuzz\n31\n32\nFizz\n34\nBuzz\nFizz\n37\n38\nFizz\nBuzz\n41\nFizz\n43\n44\nFizzBuzz\n46\n47\nFizz\n49\nBuzz\nFizz\n52\n53\nFizz\nBuzz\n56\nFizz\n58\n59\nFizzBuzz\n61\n62\nFizz\n64\nBuzz\nFizz\n67\n68\nFizz\nBuzz\n71\nFizz\n73\n74\nFizzBuzz\n76\n77\nFizz\n79\nBuzz\nFizz\n82\n83\nFizz\nBuzz\n86\nFizz\n88\n89\nFizzBuzz\n91\n92\nFizz\n94\nBuzz\nFizz\n97\n98\nFizz\nBuzz\n",
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue