-
-
Save zserge/c26f541af4f0ce8db7b0ac9d02e9ac93 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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