mirror of
https://github.com/Noratrieb/brainfuck.git
synced 2026-01-14 21:35:02 +01:00
things that don't work but sound fun
This commit is contained in:
parent
799b1591e0
commit
eda2476021
6 changed files with 256 additions and 191 deletions
94
rust2/src/codegen.rs
Normal file
94
rust2/src/codegen.rs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
//! codegen to flat code
|
||||
//!
|
||||
//! ```bf
|
||||
//! ++[-].
|
||||
//! ```
|
||||
//! compiles down to
|
||||
//! ```text
|
||||
//! Add | Add | JmpIfZero | Out | End | Sub | JmpIfNonZero | Jmp
|
||||
//! | | ^ | |
|
||||
//! +-------------------+---------|----------+
|
||||
//! +---------------------+
|
||||
//! ```
|
||||
|
||||
use crate::opts::{Ir, Stmt as IrStmt, StmtKind};
|
||||
use crate::parse::Span;
|
||||
use bumpalo::Bump;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum Stmt {
|
||||
Add(u8),
|
||||
Sub(u8),
|
||||
Right(usize),
|
||||
Left(usize),
|
||||
Out,
|
||||
In,
|
||||
SetNull,
|
||||
JmpIfZero(usize),
|
||||
JmpIfNonZero(usize),
|
||||
Jmp(usize),
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Code<'c> {
|
||||
pub stmts: Vec<Stmt, &'c Bump>,
|
||||
pub debug: Vec<Span, &'c Bump>,
|
||||
}
|
||||
|
||||
struct UnlinkedCode<'u> {
|
||||
pub stmts: Vec<Vec<Stmt, &'u Bump>, &'u Bump>,
|
||||
pub debug: Vec<Vec<Span, &'u Bump>, &'u Bump>,
|
||||
}
|
||||
|
||||
pub fn generate<'c>(alloc: &'c Bump, ir: &Ir<'_>) -> Code<'c> {
|
||||
let unlinked_alloc = Bump::new();
|
||||
|
||||
let stmts = Vec::new_in(&unlinked_alloc);
|
||||
let debug = Vec::new_in(&unlinked_alloc);
|
||||
let mut unlinked = UnlinkedCode { stmts, debug };
|
||||
|
||||
generate_stmts(&unlinked_alloc, &mut unlinked, &ir.stmts);
|
||||
|
||||
link(alloc, &unlinked)
|
||||
}
|
||||
|
||||
fn generate_stmts<'u>(alloc: &'u Bump, code: &mut UnlinkedCode<'u>, ir: &[IrStmt<'_>]) {
|
||||
for ir_stmt in ir {
|
||||
ir_to_stmt(alloc, code, ir_stmt, 0);
|
||||
}
|
||||
assert_eq!(code.stmts.len(), code.debug.len());
|
||||
}
|
||||
|
||||
fn ir_to_stmt<'u>(
|
||||
alloc: &'u Bump,
|
||||
code: &mut UnlinkedCode<'u>,
|
||||
ir_stmt: &IrStmt<'_>,
|
||||
current_block: usize,
|
||||
) {
|
||||
let stmt = match &ir_stmt.kind {
|
||||
StmtKind::Add(n) => Stmt::Add(*n),
|
||||
StmtKind::Sub(n) => Stmt::Sub(*n),
|
||||
StmtKind::Right(n) => Stmt::Right(*n),
|
||||
StmtKind::Left(n) => Stmt::Left(*n),
|
||||
StmtKind::Out => Stmt::Out,
|
||||
StmtKind::In => Stmt::In,
|
||||
StmtKind::SetNull => Stmt::SetNull,
|
||||
StmtKind::Loop(instr) => {
|
||||
let new_block = Vec::new_in(alloc);
|
||||
let new_block_debug = Vec::new_in(alloc);
|
||||
code.stmts.push(new_block);
|
||||
code.stmts.push(new_block_debug);
|
||||
|
||||
let current_block = code.stmts.len() - 1;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
code.stmts[current_block].push(stmt);
|
||||
code.debug[current_block].push(ir_stmt.span);
|
||||
}
|
||||
|
||||
fn link<'c>(alloc: &'c Bump, code: &UnlinkedCode<'_>) -> Code<'c> {
|
||||
todo!()
|
||||
}
|
||||
100
rust2/src/codegen_interpreter.rs
Normal file
100
rust2/src/codegen_interpreter.rs
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
use crate::codegen::{Code, Stmt};
|
||||
use std::io::{Read, Write};
|
||||
use std::num::Wrapping;
|
||||
|
||||
const MEM_SIZE: usize = 32_000;
|
||||
|
||||
type Memory = [Wrapping<u8>; MEM_SIZE];
|
||||
|
||||
// TODO maybe repr(C) to prevent field reordering?
|
||||
struct Interpreter<'c, W, R> {
|
||||
code: &'c Code<'c>,
|
||||
ip: usize,
|
||||
ptr: usize,
|
||||
stdout: W,
|
||||
stdin: R,
|
||||
mem: Memory,
|
||||
}
|
||||
|
||||
pub fn run<W, R>(code: &Code<'_>, stdout: W, stdin: R)
|
||||
where
|
||||
W: Write,
|
||||
R: Read,
|
||||
{
|
||||
let mut interpreter = Interpreter {
|
||||
code,
|
||||
ip: 0,
|
||||
ptr: 0,
|
||||
stdout,
|
||||
stdin,
|
||||
mem: [Wrapping(0u8); MEM_SIZE],
|
||||
};
|
||||
|
||||
interpreter.execute();
|
||||
}
|
||||
|
||||
impl<'c, W: Write, R: Read> Interpreter<'c, W, R> {
|
||||
fn execute(&mut self) {
|
||||
loop {
|
||||
let instr = self.code.stmts[self.ip];
|
||||
self.ip += 1;
|
||||
match instr {
|
||||
Stmt::Add(n) => {
|
||||
*self.elem_mut() += n;
|
||||
}
|
||||
Stmt::Sub(n) => {
|
||||
*self.elem_mut() -= n;
|
||||
}
|
||||
Stmt::Right(n) => {
|
||||
self.ptr += n;
|
||||
if self.ptr >= MEM_SIZE {
|
||||
self.ptr = 0;
|
||||
}
|
||||
}
|
||||
Stmt::Left(n) => {
|
||||
if self.ptr < n {
|
||||
let diff = n - self.ptr;
|
||||
self.ptr = MEM_SIZE - 1 - diff;
|
||||
} else {
|
||||
self.ptr -= n;
|
||||
}
|
||||
}
|
||||
Stmt::Out => {
|
||||
let char = self.elem() as char;
|
||||
write!(self.stdout, "{char}").unwrap();
|
||||
self.stdout.flush().unwrap();
|
||||
}
|
||||
Stmt::In => {
|
||||
let mut buf = [0; 1];
|
||||
self.stdin.read_exact(&mut buf).unwrap();
|
||||
*self.elem_mut() = Wrapping(buf[0]);
|
||||
}
|
||||
Stmt::SetNull => {
|
||||
*self.elem_mut() = Wrapping(0);
|
||||
}
|
||||
Stmt::JmpIfZero(pos) => {
|
||||
if self.elem() == 0 {
|
||||
self.ip = pos;
|
||||
}
|
||||
}
|
||||
Stmt::JmpIfNonZero(pos) => {
|
||||
if self.elem() != 0 {
|
||||
self.ip = pos;
|
||||
}
|
||||
}
|
||||
Stmt::Jmp(pos) => {
|
||||
self.ip = pos;
|
||||
}
|
||||
Stmt::End => break,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn elem_mut(&mut self) -> &mut Wrapping<u8> {
|
||||
&mut self.mem[self.ptr]
|
||||
}
|
||||
|
||||
fn elem(&self) -> u8 {
|
||||
self.mem[self.ptr].0
|
||||
}
|
||||
}
|
||||
|
|
@ -1,161 +0,0 @@
|
|||
use crate::opts::{Ir, Stmt};
|
||||
use std::io::{Read, Write};
|
||||
use std::num::Wrapping;
|
||||
|
||||
const MEM_SIZE: usize = 32_000;
|
||||
|
||||
type Memory = [Wrapping<u8>; MEM_SIZE];
|
||||
|
||||
pub fn run<W, R>(instrs: &Ir<'_>, mut stdout: W, mut stdin: R)
|
||||
where
|
||||
W: Write,
|
||||
R: Read,
|
||||
{
|
||||
let mut mem = [Wrapping(0u8); MEM_SIZE];
|
||||
let mut ptr = 0;
|
||||
|
||||
execute(&mut mem, &mut ptr, &instrs.stmts, &mut stdout, &mut stdin);
|
||||
}
|
||||
|
||||
fn execute<W, R>(
|
||||
mem: &mut Memory,
|
||||
ptr: &mut usize,
|
||||
instrs: &[Stmt<'_>],
|
||||
stdout: &mut W,
|
||||
stdin: &mut R,
|
||||
) where
|
||||
W: Write,
|
||||
R: Read,
|
||||
{
|
||||
for instr in instrs {
|
||||
match instr {
|
||||
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(mem, ptr, &body.stmts, stdout, stdin);
|
||||
}
|
||||
}
|
||||
Stmt::SetNull => {
|
||||
mem[*ptr] = Wrapping(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
#![feature(allocator_api, let_else)]
|
||||
#![warn(rust_2018_idioms)]
|
||||
#![allow(dead_code)]
|
||||
|
||||
use crate::parse::ParseError;
|
||||
use bumpalo::Bump;
|
||||
use std::fmt::Display;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
pub mod ir_interpreter;
|
||||
mod codegen;
|
||||
mod codegen_interpreter;
|
||||
pub mod opts;
|
||||
pub mod parse;
|
||||
|
||||
|
|
@ -31,17 +33,25 @@ where
|
|||
drop(parsed);
|
||||
drop(ast_alloc);
|
||||
|
||||
let cg_alloc = Bump::new();
|
||||
|
||||
let code = codegen::generate(&cg_alloc, &optimized_ir);
|
||||
|
||||
drop(optimized_ir);
|
||||
drop(ir_alloc);
|
||||
|
||||
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));
|
||||
}
|
||||
// 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));
|
||||
// }
|
||||
println!("no supported lol");
|
||||
}
|
||||
UseProfile::No => {
|
||||
ir_interpreter::run(&optimized_ir, stdout, stdin);
|
||||
codegen_interpreter::run(&code, stdout, stdin);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,11 +4,25 @@ use bumpalo::Bump;
|
|||
#[derive(Debug, Clone)]
|
||||
pub struct Ir<'ir> {
|
||||
pub stmts: Vec<Stmt<'ir>, &'ir Bump>,
|
||||
pub spans: Vec<Span, &'ir Bump>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Stmt<'ir> {
|
||||
pub struct Stmt<'ir> {
|
||||
pub kind: StmtKind<'ir>,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
impl<'ir> Stmt<'ir> {
|
||||
fn lol(kind: StmtKind<'ir>) -> Stmt<'ir> {
|
||||
Self {
|
||||
kind,
|
||||
span: Span::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum StmtKind<'ir> {
|
||||
Add(u8),
|
||||
Sub(u8),
|
||||
Right(usize),
|
||||
|
|
@ -32,7 +46,7 @@ fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
|||
let mut instr_iter = ast.iter();
|
||||
|
||||
let Some(first) = instr_iter.next() else {
|
||||
return Ir { stmts, spans: Vec::new_in(alloc) };
|
||||
return Ir { stmts };
|
||||
};
|
||||
|
||||
let mut last = &first.0;
|
||||
|
|
@ -50,13 +64,13 @@ fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
|||
_ => {
|
||||
end_span = *next_span;
|
||||
let new_last = match last {
|
||||
Instr::Add => Stmt::Add(count),
|
||||
Instr::Sub => Stmt::Sub(count),
|
||||
Instr::Right => Stmt::Right(count.into()),
|
||||
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::Add => Stmt::lol(StmtKind::Add(count)),
|
||||
Instr::Sub => Stmt::lol(StmtKind::Sub(count)),
|
||||
Instr::Right => Stmt::lol(StmtKind::Right(count.into())),
|
||||
Instr::Left => Stmt::lol(StmtKind::Left(count.into())),
|
||||
Instr::Out => Stmt::lol(StmtKind::Out),
|
||||
Instr::In => Stmt::lol(StmtKind::In),
|
||||
Instr::Loop(body) => Stmt::lol(StmtKind::Loop(ast_to_ir(alloc, body))),
|
||||
};
|
||||
stmts.push(new_last);
|
||||
spans.push(start_span.until(end_span));
|
||||
|
|
@ -68,25 +82,33 @@ fn ast_to_ir<'ir>(alloc: &'ir Bump, ast: &[(Instr<'_>, Span)]) -> Ir<'ir> {
|
|||
}
|
||||
|
||||
let new_last = match last {
|
||||
Instr::Add => Stmt::Add(count),
|
||||
Instr::Sub => Stmt::Sub(count),
|
||||
Instr::Right => Stmt::Right(count.into()),
|
||||
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::Add => Stmt::lol(StmtKind::Add(count)),
|
||||
Instr::Sub => Stmt::lol(StmtKind::Sub(count)),
|
||||
Instr::Right => Stmt::lol(StmtKind::Right(count.into())),
|
||||
Instr::Left => Stmt::lol(StmtKind::Left(count.into())),
|
||||
Instr::Out => Stmt::lol(StmtKind::Out),
|
||||
Instr::In => Stmt::lol(StmtKind::In),
|
||||
Instr::Loop(body) => Stmt::lol(StmtKind::Loop(ast_to_ir(alloc, &body))),
|
||||
};
|
||||
stmts.push(new_last);
|
||||
spans.push(start_span.until(end_span));
|
||||
|
||||
Ir { stmts, spans }
|
||||
Ir { stmts }
|
||||
}
|
||||
|
||||
fn pass_find_set_null(ir: &mut Ir<'_>) {
|
||||
for stmt in &mut ir.stmts {
|
||||
if let Stmt::Loop(body) = stmt {
|
||||
if let [Stmt::Sub(_)] = body.stmts.as_slice() {
|
||||
*stmt = Stmt::SetNull;
|
||||
if let Stmt {
|
||||
kind: StmtKind::Loop(body),
|
||||
..
|
||||
} = stmt
|
||||
{
|
||||
if let [Stmt {
|
||||
kind: StmtKind::Sub(_),
|
||||
..
|
||||
}] = body.stmts.as_slice()
|
||||
{
|
||||
*stmt = Stmt::lol(StmtKind::SetNull);
|
||||
} else {
|
||||
pass_find_set_null(body);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use bumpalo::Bump;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
|
||||
pub struct Span {
|
||||
start: u32,
|
||||
len: u32,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue