lazyk_rust/
runner.rs

1use crate::{
2    expression::{Expr, ExprId},
3    io::{Input, Output},
4    util::{num_repr, NumRepr},
5};
6use anyhow::{bail, Result};
7use std::{
8    collections::VecDeque,
9    mem::{size_of, swap},
10};
11
12pub struct LazyKRunner {
13    e: Vec<Expr>,
14    church_chars: Vec<ExprId>,
15    pub s: ExprId,
16    pub k: ExprId,
17    pub i: ExprId,
18    pub ki: ExprId,
19    pub inc: ExprId,
20    pub zero: ExprId,
21    pub iota: ExprId,
22    input: Input,
23
24    // If zero, there are no Free slots.
25    // If not zero, id of next potential slot.
26    gc_free_ptr: usize,
27    gc_queue: VecDeque<ExprId>,
28}
29
30// Garbade collector will try to keep memory usage below this number.
31static GC_LIMIT_BYTES: usize = 256 * 1024 * 1024;
32static GC_LIMIT_EXPR: usize = GC_LIMIT_BYTES / size_of::<Expr>();
33// Number of expressions at the beginning that are never garbage-collected.
34static PREAMBLE_LENGTH: usize = 448;
35// This Church number is used to mark end of input/output.
36static EOF_MARKER: u16 = 256;
37
38impl LazyKRunner {
39    pub fn new() -> Self {
40        let mut pool: Vec<Expr> = Vec::with_capacity(1000000);
41        pool.push(Expr::Free);
42        let mut n = |expr: Expr| {
43            pool.push(expr);
44            (pool.len() - 1) as ExprId
45        };
46
47        let k = n(Expr::K);
48        let s = n(Expr::S);
49        let i = n(Expr::I);
50        let ki = n(Expr::K1(i));
51        let ks = n(Expr::K1(s));
52        let kk = n(Expr::K1(k));
53        let sksk = n(Expr::S2(ks, k));
54        let siks = n(Expr::S2(i, ks));
55        let iota = n(Expr::S2(siks, kk));
56        let inc = n(Expr::Inc);
57        let zero = n(Expr::Num(0));
58
59        let mut church_chars = vec![ki, i];
60        for i in 2..=EOF_MARKER {
61            let church_expr = match num_repr(i) {
62                NumRepr::Pow(a, b) => Expr::A(church_chars[b], church_chars[a]),
63                NumRepr::Mul(a, b) => Expr::S2(n(Expr::K1(church_chars[a])), church_chars[b]),
64                NumRepr::Inc(a) => Expr::S2(sksk, church_chars[a]),
65            };
66            church_chars.push(n(church_expr));
67        }
68        assert!(pool.len() <= PREAMBLE_LENGTH);
69        Self {
70            e: pool,
71            church_chars,
72            s,
73            k,
74            i,
75            ki,
76            inc,
77            zero,
78            iota,
79            input: Input::Null,
80            gc_free_ptr: 0,
81            gc_queue: VecDeque::new(),
82        }
83    }
84
85    #[inline(always)]
86    fn new_expr_push(&mut self, expr: Expr) -> ExprId {
87        let ans = self.e.len() as ExprId;
88        self.e.push(expr);
89        ans
90    }
91
92    pub(crate) fn new_expr(&mut self, expr: Expr) -> ExprId {
93        if self.gc_free_ptr == 0 {
94            return self.new_expr_push(expr);
95        }
96        // Try use next free slot.
97        for i in self.gc_free_ptr..self.e.len() {
98            if let Expr::Free = self.e[i] {
99                self.e[i] = expr;
100                self.gc_free_ptr = i + 1;
101                return i as ExprId;
102            }
103        }
104        // Reached end of allocated pool, push.
105        self.gc_free_ptr = 0;
106        self.new_expr_push(expr)
107    }
108
109    // Frees all expressions not reachable from expr_id.
110    fn garbage_collect(&mut self, expr_id: ExprId) {
111        let fp = self.gc_free_ptr;
112        let n = self.e.len();
113        let need_gc = (fp == 0 && n > GC_LIMIT_EXPR) || (fp > GC_LIMIT_EXPR);
114        if !need_gc {
115            return;
116        }
117
118        // BFS.
119        let mut needed: Vec<bool> = vec![false; n];
120        self.gc_queue.push_back(expr_id);
121        while let Some(next_id) = self.gc_queue.pop_front() {
122            if needed[next_id as usize] {
123                continue;
124            }
125            needed[next_id as usize] = true;
126            match &self.e[next_id as usize] {
127                Expr::A(arg1, arg2) | Expr::S2(arg1, arg2) => {
128                    self.gc_queue.push_back(*arg1);
129                    self.gc_queue.push_back(*arg2);
130                }
131                Expr::K1(arg1) | Expr::S1(arg1) | Expr::I1(arg1) => self.gc_queue.push_back(*arg1),
132                _ => {}
133            }
134        }
135        #[allow(clippy::needless_range_loop)]
136        for i in PREAMBLE_LENGTH..n {
137            if !needed[i] {
138                self.e[i] = Expr::Free;
139            }
140        }
141        self.gc_free_ptr = PREAMBLE_LENGTH;
142    }
143
144    fn partial_eval_primitive_application(&mut self, expr_id: ExprId) {
145        match self.e[expr_id as usize] {
146            Expr::A(lhs, rhs) => {
147                self.e[expr_id as usize] = self.partial_eval_primitive_application_2(lhs, rhs);
148            }
149            _ => panic!("Not an application!"),
150        }
151    }
152
153    fn partial_eval_primitive_application_2(&mut self, lhs: ExprId, rhs: ExprId) -> Expr {
154        let rhs = self.drop_i1(rhs);
155        match &self.e[lhs as usize] {
156            Expr::K => Expr::K1(rhs),
157            Expr::K1(arg1) => Expr::I1(*arg1),
158            Expr::S => Expr::S1(rhs),
159            Expr::I => Expr::I1(rhs),
160            Expr::S1(arg1) => Expr::S2(*arg1, rhs),
161            Expr::LazyRead => self.apply_lazy_read(lhs, rhs),
162            Expr::S2(arg1, arg2) => self.apply_s2(*arg1, *arg2, rhs),
163            Expr::Inc => {
164                let rhs2 = self.partial_eval(rhs);
165                match self.e[rhs2 as usize] {
166                    Expr::Num(num) => Expr::Num(num + 1),
167                    _ => panic!("Attempted to apply inc to a non-number"),
168                }
169            }
170            _ => panic!("Unreachable code."),
171        }
172    }
173
174    // lhs points to LazyRead.
175    fn apply_lazy_read(&mut self, lhs: ExprId, rhs: ExprId) -> Expr {
176        let next_char = match self.input.read_byte() {
177            Some(ch) => ch as u16,
178            None => EOF_MARKER,
179        };
180        let ch = self.church_char(next_char);
181        let x_rhs = self.new_expr(Expr::K1(ch));
182        let x = self.new_expr(Expr::S2(self.i, x_rhs));
183        let new_lazy_read = self.new_expr(Expr::LazyRead);
184        let y = self.new_expr(Expr::K1(new_lazy_read));
185        self.e[lhs as usize] = Expr::S2(x, y);
186        self.partial_eval_primitive_application_2(lhs, rhs)
187    }
188
189    fn apply_s2(&mut self, arg1: ExprId, arg2: ExprId, rhs: ExprId) -> Expr {
190        let new_lhs = self.partial_apply(arg1, rhs);
191        Expr::A(new_lhs, self.partial_apply(arg2, rhs))
192    }
193
194    pub fn partial_apply(&mut self, lhs: ExprId, rhs: ExprId) -> ExprId {
195        self.new_expr(Expr::A(lhs, rhs))
196    }
197
198    fn drop_i1(&self, mut expr: ExprId) -> ExprId {
199        while let Expr::I1(arg1) = self.e[expr as usize] {
200            expr = arg1;
201        }
202        expr
203    }
204
205    fn partial_eval(&mut self, mut cur: ExprId) -> ExprId {
206        let mut prev: ExprId = 0;
207        loop {
208            cur = self.drop_i1(cur);
209            while let Expr::A(arg1, _) = &mut self.e[cur as usize] {
210                swap(arg1, &mut prev);
211                prev = self.drop_i1(prev);
212                swap(&mut cur, &mut prev);
213            }
214            if prev == 0 {
215                return cur;
216            }
217
218            if let Expr::A(arg1, _) = &mut self.e[prev as usize] {
219                swap(arg1, &mut cur);
220            } else {
221                panic!("Unreachable code")
222            }
223            swap(&mut cur, &mut prev);
224
225            self.partial_eval_primitive_application(cur);
226        }
227    }
228
229    pub fn church2int(&mut self, church: ExprId) -> Result<u16> {
230        let inc = self.partial_apply(church, self.inc);
231        let e = self.partial_apply(inc, self.zero);
232        let result_id = self.partial_eval(e);
233        match self.e[result_id as usize] {
234            Expr::Num(num) => Ok(num),
235            _ => bail!("Program's output is not a church numeral."),
236        }
237    }
238
239    fn car(&mut self, list: ExprId) -> ExprId {
240        self.partial_apply(list, self.k)
241    }
242
243    fn cdr(&mut self, list: ExprId) -> ExprId {
244        self.partial_apply(list, self.ki)
245    }
246
247    // pair(X,Y)F := (FX)Y
248    // pair(X,Y) = S(SI(KX))(KY)
249    pub(crate) fn pair(&mut self, x: ExprId, y: ExprId) -> ExprId {
250        let d = self.new_expr(Expr::K1(x));
251        let a = self.new_expr(Expr::S2(self.i, d));
252        let b = self.new_expr(Expr::K1(y));
253        self.new_expr(Expr::S2(a, b))
254    }
255
256    pub fn church_char(&self, mut idx: u16) -> ExprId {
257        if idx > EOF_MARKER {
258            idx = EOF_MARKER;
259        }
260        self.church_chars[idx as usize]
261    }
262
263    pub fn run(
264        &mut self,
265        expr_id: ExprId,
266        input: Input,
267        output: &mut Output,
268        output_limit: Option<usize>,
269    ) -> Result<u16> {
270        self.input = input;
271        let lr = self.new_expr(Expr::LazyRead);
272        let mut e = self.partial_apply(expr_id, lr);
273        let mut output_size = 0;
274        loop {
275            let head = self.car(e);
276            let ch = self.church2int(head)?;
277            if ch >= EOF_MARKER {
278                return Ok(ch - EOF_MARKER);
279            }
280            output.write_char(ch as u8)?;
281            e = self.cdr(e);
282            output_size += 1;
283            self.garbage_collect(e);
284            if output_limit.is_some() && output_limit.unwrap() == output_size {
285                return Ok(1);
286            }
287        }
288    }
289
290    pub(crate) fn get_expr(&'_ self, expr_id: ExprId) -> &'_ Expr {
291        &self.e[expr_id as usize]
292    }
293}
294
295impl Default for LazyKRunner {
296    fn default() -> Self {
297        Self::new()
298    }
299}