mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-15 13:55:02 +01:00
broken AF but profiling could perhaps work like mayb eidk
This commit is contained in:
parent
cec7204c6d
commit
799b1591e0
9 changed files with 168 additions and 703 deletions
|
|
@ -70,3 +70,92 @@ fn execute<W, R>(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Profile = Vec<u64>;
|
||||
|
||||
pub fn run_profile<W, R>(instrs: &Ir<'_>, mut stdout: W, mut stdin: R) -> Profile
|
||||
where
|
||||
W: Write,
|
||||
R: Read,
|
||||
{
|
||||
let mut mem = [Wrapping(0u8); MEM_SIZE];
|
||||
let mut ptr = 0;
|
||||
|
||||
let size = instrs
|
||||
.spans
|
||||
.last()
|
||||
.map(|sp| sp.start() + sp.len())
|
||||
.unwrap_or(0);
|
||||
|
||||
let mut profile = vec![0; size];
|
||||
|
||||
execute_profile(
|
||||
&mut mem,
|
||||
&mut ptr,
|
||||
instrs,
|
||||
&mut stdout,
|
||||
&mut stdin,
|
||||
&mut profile,
|
||||
);
|
||||
|
||||
profile
|
||||
}
|
||||
|
||||
fn execute_profile<W, R>(
|
||||
mem: &mut Memory,
|
||||
ptr: &mut usize,
|
||||
ir: &Ir<'_>,
|
||||
stdout: &mut W,
|
||||
stdin: &mut R,
|
||||
profile: &mut [u64],
|
||||
) where
|
||||
W: Write,
|
||||
R: Read,
|
||||
{
|
||||
for (i, stmt) in ir.stmts.iter().enumerate() {
|
||||
match stmt {
|
||||
Stmt::Add(n) => {
|
||||
mem[*ptr] += n;
|
||||
}
|
||||
Stmt::Sub(n) => {
|
||||
mem[*ptr] -= n;
|
||||
}
|
||||
Stmt::Right(n) => {
|
||||
*ptr += n;
|
||||
if *ptr >= MEM_SIZE {
|
||||
*ptr = 0;
|
||||
}
|
||||
}
|
||||
Stmt::Left(n) => {
|
||||
if *ptr < *n {
|
||||
let diff = *n - *ptr;
|
||||
*ptr = MEM_SIZE - 1 - diff;
|
||||
} else {
|
||||
*ptr -= n;
|
||||
}
|
||||
}
|
||||
Stmt::Out => {
|
||||
let char = mem[*ptr].0 as char;
|
||||
write!(stdout, "{char}").unwrap();
|
||||
stdout.flush().unwrap();
|
||||
}
|
||||
Stmt::In => {
|
||||
let mut buf = [0; 1];
|
||||
stdin.read_exact(&mut buf).unwrap();
|
||||
mem[*ptr] = Wrapping(buf[0]);
|
||||
}
|
||||
Stmt::Loop(body) => {
|
||||
while mem[*ptr] != Wrapping(0) {
|
||||
execute_profile(mem, ptr, body, stdout, stdin, profile);
|
||||
}
|
||||
}
|
||||
Stmt::SetNull => {
|
||||
mem[*ptr] = Wrapping(0);
|
||||
}
|
||||
}
|
||||
let span = ir.spans[i];
|
||||
profile[span.start()..][..span.len()]
|
||||
.iter_mut()
|
||||
.for_each(|p| *p += 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,20 +3,26 @@
|
|||
|
||||
use crate::parse::ParseError;
|
||||
use bumpalo::Bump;
|
||||
use std::fmt::Display;
|
||||
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>
|
||||
pub enum UseProfile {
|
||||
Yes,
|
||||
No,
|
||||
}
|
||||
|
||||
pub fn run<R, W>(str: &str, stdout: W, stdin: R, use_profile: UseProfile) -> Result<(), ParseError>
|
||||
where
|
||||
W: Write,
|
||||
R: Read,
|
||||
{
|
||||
let ast_alloc = Bump::new();
|
||||
|
||||
let parsed = parse::parse(&ast_alloc, bytes.enumerate())?;
|
||||
let parsed = parse::parse(&ast_alloc, str.bytes().enumerate())?;
|
||||
|
||||
let ir_alloc = Bump::new();
|
||||
|
||||
|
|
@ -25,20 +31,49 @@ where
|
|||
drop(parsed);
|
||||
drop(ast_alloc);
|
||||
|
||||
ir_interpreter::run(&optimized_ir, stdout, stdin);
|
||||
match use_profile {
|
||||
UseProfile::Yes => {
|
||||
let profile = ir_interpreter::run_profile(&optimized_ir, stdout, stdin);
|
||||
let max = profile.iter().max().copied().unwrap_or(0);
|
||||
println!("---------------- Profile ----------------");
|
||||
for (i, char) in str.bytes().enumerate() {
|
||||
print!("{}", color_by_profile(char as char, profile[i], max));
|
||||
}
|
||||
}
|
||||
UseProfile::No => {
|
||||
ir_interpreter::run(&optimized_ir, stdout, stdin);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn color_by_profile(char: char, value: u64, max: u64) -> impl Display {
|
||||
use owo_colors::OwoColorize;
|
||||
|
||||
let percentage = ((max as f64) / (value as f64) * 100.0) as u64;
|
||||
|
||||
match percentage {
|
||||
0..=1 => char.default_color().to_string(),
|
||||
2..=10 => char.green().to_string(),
|
||||
11..=30 => char.yellow().to_string(),
|
||||
31..=90 => char.red().to_string(),
|
||||
91..=100 => char.bright_red().to_string(),
|
||||
_ => char.default_color().to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::UseProfile;
|
||||
|
||||
#[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();
|
||||
super::run(str, &mut stdout, stdin.as_slice(), UseProfile::No).unwrap();
|
||||
|
||||
insta::assert_debug_snapshot!(String::from_utf8(stdout));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#![feature(allocator_api, let_else)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
|
||||
use brainfuck::UseProfile;
|
||||
use std::{env, fs, io, process};
|
||||
|
||||
fn main() {
|
||||
|
|
@ -19,7 +20,7 @@ fn main() {
|
|||
let stdin = io::stdin();
|
||||
let stdin = stdin.lock();
|
||||
|
||||
brainfuck::run(file.bytes(), stdout, stdin).unwrap_or_else(|_| {
|
||||
brainfuck::run(&file, stdout, stdin, UseProfile::Yes).unwrap_or_else(|_| {
|
||||
eprintln!("error: Failed to parse brainfuck code");
|
||||
process::exit(1);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
use crate::parse::{Instr, Span};
|
||||
use bumpalo::Bump;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Ir<'ir> {
|
||||
pub stmts: Vec<Stmt<'ir>, &'ir Bump>,
|
||||
pub spans: Vec<Span, &'ir Bump>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Stmt<'ir> {
|
||||
Add(u8),
|
||||
Sub(u8),
|
||||
|
|
@ -36,16 +36,19 @@ fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
|||
};
|
||||
|
||||
let mut last = &first.0;
|
||||
let mut last_span = first.1;
|
||||
let mut start_span = first.1;
|
||||
let mut count = 1;
|
||||
let mut end_span = start_span;
|
||||
|
||||
for (next, next_span) in instr_iter {
|
||||
match last {
|
||||
Instr::Add | Instr::Sub | Instr::Right | Instr::Left if last == next => {
|
||||
count += 1;
|
||||
end_span = *next_span;
|
||||
continue;
|
||||
}
|
||||
_ => {
|
||||
end_span = *next_span;
|
||||
let new_last = match last {
|
||||
Instr::Add => Stmt::Add(count),
|
||||
Instr::Sub => Stmt::Sub(count),
|
||||
|
|
@ -56,9 +59,9 @@ fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
|||
Instr::Loop(body) => Stmt::Loop(ast_to_ir(alloc, body)),
|
||||
};
|
||||
stmts.push(new_last);
|
||||
spans.push(last_span.until(*next_span));
|
||||
spans.push(start_span.until(end_span));
|
||||
last = next;
|
||||
last_span = *next_span;
|
||||
start_span = *next_span;
|
||||
count = 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -74,7 +77,7 @@ fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
|||
Instr::Loop(body) => Stmt::Loop(ast_to_ir(alloc, &body)),
|
||||
};
|
||||
stmts.push(new_last);
|
||||
spans.push(last_span);
|
||||
spans.push(start_span.until(end_span));
|
||||
|
||||
Ir { stmts, spans }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ use bumpalo::Bump;
|
|||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Span {
|
||||
pub start: u32,
|
||||
pub len: u32,
|
||||
start: u32,
|
||||
len: u32,
|
||||
}
|
||||
|
||||
impl Span {
|
||||
|
|
@ -27,6 +27,14 @@ impl Span {
|
|||
len: (other.start + other.len) - self.len,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn start(&self) -> usize {
|
||||
self.start.try_into().unwrap()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.len.try_into().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub type Instrs<'ast> = Vec<(Instr<'ast>, Span), &'ast Bump>;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue