Skip to content

Instantly share code, notes, and snippets.

@zserge

zserge/false.rs Secret

Created July 5, 2022 17:29
Show Gist options
  • Save zserge/c26f541af4f0ce8db7b0ac9d02e9ac93 to your computer and use it in GitHub Desktop.
Save zserge/c26f541af4f0ce8db7b0ac9d02e9ac93 to your computer and use it in GitHub Desktop.
extern crate sdl2;
use sdl2::pixels::Color;
use sdl2::rect::Rect;
use sdl2::render::Canvas;
use sdl2::video::Window;
pub struct Stack<'a, T> {
mem: &'a mut [T],
ptr: usize,
}
impl<'a, T: 'a + Copy> Stack<'a, T> {
pub fn new(mem: &'a mut [T]) -> Self {
Stack { mem: mem, ptr: 0 }
}
pub fn push(&mut self, item: T) -> Option<T> {
if self.mem.len() > self.ptr {
self.mem[self.ptr] = item;
self.ptr += 1;
Some(item)
} else {
None
}
}
pub fn pop(&mut self) -> Option<T> {
if self.ptr > 0 {
self.ptr -= 1;
Some(self.mem[self.ptr])
} else {
None
}
}
pub fn pick(&mut self, index: usize) -> Option<T> {
if index < self.ptr {
Some(self.mem[self.ptr - index - 1])
} else {
None
}
}
}
pub struct False<'a> {
vars: [i32; 128],
stack: Stack<'a, i32>,
ret: Stack<'a, i32>,
}
impl<'a> False<'a> {
pub fn new(smem: &'a mut [i32], rmem: &'a mut [i32]) -> Self {
False {
vars: [0; 128],
stack: Stack::new(smem),
ret: Stack::new(rmem),
}
}
pub fn eval(&mut self, code: &[u8]) -> Option<i32> {
let end = code.len();
let mut ip: usize = 0;
while ip < code.len() {
macro_rules! unary {
($f:expr) => {{
let z = self.stack.pop()?;
self.stack.push($f(z))?
}};
}
macro_rules! binary {
($f:expr) => {{
let b = self.stack.pop()?;
let a = self.stack.pop()?;
self.stack.push($f(a, b))?
}};
}
match code[ip] as char {
// Whitespace and comments
' ' | '\t' | '\n' | '\r' => 0,
'{' => {
while ip < end && code[ip] != b'}' {
ip += 1
}
0
}
// Literals
'0'..='9' => {
let mut n: i32 = 0;
while ip < end && code[ip] >= b'0' && code[ip] <= b'9' {
n = n * 10 + (code[ip] - b'0') as i32;
ip += 1;
}
ip -= 1;
self.stack.push(n)?
}
'\'' => {
ip += 1;
self.stack.push(code[ip].into())?
}
// unary
'_' => unary!(|a: i32| (-a)),
'~' => unary!(|a: i32| !a),
// binary
'&' => binary!(|a, b| a & b),
'|' => binary!(|a, b: i32| a | b),
'=' => binary!(|a, b: i32| (a == b) as i32 - 1),
'<' => binary!(|a, b: i32| (a < b) as i32 - 1),
'>' => binary!(|a, b: i32| (a > b) as i32 - 1),
'+' => binary!(|a, b: i32| a + b),
'-' => binary!(|a, b: i32| a - b),
'*' => binary!(|a, b: i32| a * b),
'/' => binary!(|a, b: i32| if b == 0 { 0 } else { a / b }),
// vars
'a'..='z' => self.stack.push((code[ip] - b'a') as i32)?,
'A'..='Z' => self.stack.push(self.vars[(code[ip] - b'A') as usize])?,
';' => {
let i = self.stack.pop()?;
if i >= 0 && i < 128 {
self.stack.push(self.vars[i as usize])?;
0
} else {
return None;
}
}
':' => {
let i = self.stack.pop()?;
if i >= 0 && i < 128 {
self.vars[i as usize] = self.stack.pop()?;
0
} else {
return None;
}
}
// stack: DROP, DUP, OVER, ROT
'%' => self.stack.pop()?,
'$' => {
let n = self.stack.pick(0)?;
self.stack.push(n)?
}
'\\' => {
let a = self.stack.pop()?;
let b = self.stack.pop()?;
self.stack.push(a)?;
self.stack.push(b)?
}
'@' => {
let a = self.stack.pop()?;
let b = self.stack.pop()?;
let c = self.stack.pop()?;
self.stack.push(b)?;
self.stack.push(a)?;
self.stack.push(c)?
}
// Control flow
'[' => {
self.stack.push(ip as i32)?;
let mut depth: i32 = 0;
while ip < code.len() {
if code[ip] == b'[' {
depth += 1;
} else if code[ip] == b']' {
depth -= 1;
}
if depth == 0 {
break;
}
ip += 1;
}
0
}
']' => {
if let Some(a) = self.ret.pick(2) {
if code[a as usize] == b'#' {
if self.stack.pop()? != 0 {
let a = self.ret.pick(1)?;
self.ret.push(a)?;
let b = self.ret.pick(1)?;
self.ret.push(b)?;
} else {
self.ret.pop()?;
self.ret.pop()?;
}
}
}
ip = self.ret.pop()? as usize;
0
}
'?' => {
let body = self.stack.pop()?;
if self.stack.pop()? != 0 {
self.ret.push(ip as i32)?;
ip = body as usize;
}
0
}
'!' => {
self.ret.push(ip as i32);
ip = self.stack.pop()? as usize;
// TODO: validate ip range?
0
}
'#' => {
self.ret.push(ip as i32)?; // put '#' address
self.ret.push(self.stack.pick(1)?)?; // put condition
self.ret.push(self.stack.pop()?)?; // put body
ip = self.stack.pop()? as usize; // ip = loop condition
0
}
_ => return None,
};
ip += 1;
}
self.stack.pick(0)
}
}
fn main() {
let sdl = sdl2::init().unwrap();
let video = sdl.video().unwrap();
let window = video.window("FALSE", 240, 240).build().unwrap();
let mut canvas: Canvas<Window> = window.into_canvas().present_vsync().build().unwrap();
let mut pump = sdl.event_pump().unwrap();
let mut stack = [0 as i32; 16];
let mut ret = [0 as i32; 16];
let mut ph: False = False::new(&mut stack, &mut ret);
let mut t: i32 = 0;
'main: loop {
for event in pump.poll_iter() {
match event {
sdl2::event::Event::Quit { .. } => break 'main,
_ => {}
}
}
canvas.set_draw_color(Color::RGB(0, 0, 0));
canvas.clear();
for x in 0..240 {
for y in 0..240 {
ph.vars[19] = t;
ph.vars[23] = x;
ph.vars[24] = y;
ph.stack.ptr = 0;
ph.ret.ptr = 0;
ph.eval("TXY*+".as_bytes());
let r = ph.stack.pick(0).unwrap_or(0);
let g = ph.stack.pick(1).unwrap_or(r);
let b = ph.stack.pick(2).unwrap_or(g);
canvas.set_draw_color(Color::RGB(r as u8, g as u8, b as u8));
canvas.fill_rect(Rect::new(x, y, 1, 1));
}
}
t += 1;
canvas.present();
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_stack() {
let mut mem = [0 as u8; 3];
let mut s = Stack::new(&mut mem);
// Push
assert_eq!(s.push(1).unwrap(), 1);
assert_eq!(s.push(3).unwrap(), 3);
assert_eq!(s.push(7).unwrap(), 7);
assert!(s.push(11).is_none());
assert!(s.push(13).is_none());
// Pick
assert_eq!(s.pick(0).unwrap(), 7);
assert_eq!(s.pick(1).unwrap(), 3);
assert_eq!(s.pick(2).unwrap(), 1);
assert!(s.pick(3).is_none());
// Pop
assert_eq!(s.pop().unwrap(), 7);
assert_eq!(s.pop().unwrap(), 3);
assert_eq!(s.push(2).unwrap(), 2);
assert_eq!(s.push(3).unwrap(), 3);
assert!(s.push(4).is_none());
assert_eq!(s.pick(1).unwrap(), 2);
}
macro_rules! false_eval_eq {
($code:tt, $result:expr) => {{
let mut stack = [0 as i32; 16];
let mut ret = [0 as i32; 16];
let mut ph: False = False::new(&mut stack, &mut ret);
assert_eq!(ph.eval($code.as_bytes()), $result);
}};
}
#[test]
fn test_false_lit() {
false_eval_eq!("", None);
false_eval_eq!(" \n", None);
false_eval_eq!(" { hello 123 + } \n", None);
false_eval_eq!("123", Some(123));
false_eval_eq!(" 123 ", Some(123));
false_eval_eq!("'A", Some(65));
}
#[test]
fn test_false_unary() {
false_eval_eq!("12_", Some(-12));
false_eval_eq!("12~", Some(-13));
}
#[test]
fn test_false_binary() {
false_eval_eq!("1 2+", Some(3));
false_eval_eq!("1 2 +", Some(3));
false_eval_eq!("1 2 -", Some(-1));
false_eval_eq!("2 1 -", Some(1));
false_eval_eq!("3 4 *", Some(12));
false_eval_eq!("15 3 /", Some(5));
false_eval_eq!("15 0 /", Some(0)); // XXX: expected?
false_eval_eq!("1 2 3 4*++", Some(15));
false_eval_eq!("5 3 >", Some(0));
false_eval_eq!("1 3 >", Some(-1));
false_eval_eq!("5 3 <", Some(-1));
false_eval_eq!("4 6 <", Some(0));
false_eval_eq!("5 3 =", Some(-1));
false_eval_eq!("3 3 =", Some(0));
false_eval_eq!("4 3 |", Some(7));
false_eval_eq!("5 3 &", Some(1));
}
#[test]
fn test_false_variables() {
false_eval_eq!("5a:a;", Some(5));
false_eval_eq!("5a: 3a;+", Some(8));
false_eval_eq!("5a: 7b: a;b;+", Some(12));
false_eval_eq!("5a: 7b: AB+", Some(12));
}
#[test]
fn test_false_stack() {
false_eval_eq!("3$+", Some(6));
false_eval_eq!("1 2 3 % +", Some(3));
false_eval_eq!("3 1 5 \\ - +", Some(7));
// TODO: pick?
}
#[test]
fn test_false_blocks() {
false_eval_eq!("[2 3+]!", Some(5));
false_eval_eq!("[1+]a: 5a;!a;!", Some(7));
false_eval_eq!("5 0[7]?", Some(5));
false_eval_eq!("5 1[7]?", Some(7));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment