broken AF but profiling could perhaps work like mayb eidk

This commit is contained in:
nora 2022-04-12 22:28:41 +02:00
parent cec7204c6d
commit 799b1591e0
9 changed files with 168 additions and 703 deletions

View file

@ -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);
}
}

View file

@ -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));
}

View file

@ -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);
});

View file

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

View file

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