lazyk_rust/
program.rs

1use anyhow::Result;
2use std::io::{stdin, stdout, Cursor};
3
4use crate::{
5    expression::{Expr, ExprId},
6    io::{Input, Output},
7    parser::Parser,
8    printer::{CcPrinter, GenericPrinter},
9    runner::LazyKRunner,
10};
11
12pub enum Style {
13    CombCalculus,
14    Unlambda,
15    Jot,
16    Iota,
17}
18
19pub struct LazyKProgram {
20    runner: LazyKRunner,
21    root_id: ExprId,
22    output_limit: Option<usize>,
23}
24
25/// Compiled LazyK program, ready to be executed.
26impl LazyKProgram {
27    /// Compiles LazyK source to a runnable program.
28    ///
29    /// ```
30    /// use lazyk_rust::LazyKProgram;
31    /// let source = "I";
32    /// let mut program = LazyKProgram::compile(source).unwrap();
33    /// assert_eq!(program.run_string("abcd").unwrap(), "abcd");
34    /// ```
35    pub fn compile(source: &str) -> Result<Self> {
36        let mut runner = LazyKRunner::new();
37        let root_id = Parser::parse(source, &mut runner)?;
38        Ok(Self {
39            runner,
40            root_id,
41            output_limit: None,
42        })
43    }
44
45    /// Sets maximal number of cbytes in output, after which program halts.
46    /// Useful for running programs that produce infinite ouput.
47    pub fn set_output_limit(&mut self, value: Option<usize>) {
48        self.output_limit = value;
49    }
50
51    /// Runs program as Vec<u8> -> Vec<u8> function.
52    pub fn run_vec(&mut self, input: Vec<u8>) -> Result<Vec<u8>> {
53        let input = Input::Reader(Box::new(Cursor::new(input)));
54        let mut output = Output::Buffer(Vec::new());
55        self.runner
56            .run(self.root_id, input, &mut output, self.output_limit)?;
57        match output {
58            Output::Buffer(result) => Ok(result),
59            _ => panic!("Unreachable code."),
60        }
61    }
62
63    /// Runs program as String -> String function.
64    pub fn run_string(&mut self, input: &str) -> Result<String> {
65        let result = self.run_vec(input.as_bytes().to_owned())?;
66        String::from_utf8(result).map_err(anyhow::Error::from)
67    }
68
69    /// Runs program, reading from standard input and writing to standard output.
70    pub fn run_console(&mut self) -> Result<()> {
71        let input = Input::Reader(Box::new(stdin().lock()));
72        let mut output = Output::Writer(Box::new(stdout().lock()));
73        self.runner
74            .run(self.root_id, input, &mut output, self.output_limit)?;
75        Ok(())
76    }
77
78    /// Produces source code for this program.
79    ///
80    /// There are four supported styles: combinator-calculus, Unlambda, Jot and
81    /// Iota.
82    ///
83    /// ```
84    /// use lazyk_rust::{LazyKProgram, Style};
85    /// let prog = LazyKProgram::compile("S(SI(K(KI)))(K(KI))").unwrap();
86    /// assert_eq!(prog.to_source(Style::CombCalculus), "S(SI(K(KI)))(K(KI))");
87    /// assert_eq!(prog.to_source(Style::Unlambda), "``s``si`k`ki`k`ki");                                         
88    /// assert_eq!(prog.to_source(Style::Jot), "11111110001111111000111111111000001111001111001111111110000011110011110011111111100000");
89    /// assert_eq!(prog.to_source(Style::Iota), "***i*i*i*ii***i*i*i*ii*ii**i*i*ii**i*i*ii*ii**i*i*ii**i*i*ii*ii");
90    /// ```
91    pub fn to_source(&self, style: Style) -> String {
92        match style {
93            Style::CombCalculus => CcPrinter::new(&self.runner).print(self.root_id),
94            _ => GenericPrinter::new(&self.runner, style).print(self.root_id),
95        }
96    }
97
98    /// Produces LazyK program that prints given byte sequence to output.
99    ///
100    /// ```
101    /// use lazyk_rust::LazyKProgram;
102    /// let mut prog = LazyKProgram::make_printer("Hi!".as_bytes());
103    /// assert_eq!(prog.run_string("").unwrap(), "Hi!");
104    /// ```
105    pub fn make_printer(bytes: &[u8]) -> LazyKProgram {
106        let mut runner = LazyKRunner::new();
107        let eof = runner.church_char(256);
108        let mut list = runner.pair(eof, runner.k);
109        for i in (0..bytes.len()).rev() {
110            list = runner.pair(runner.church_char(bytes[i] as u16), list);
111        }
112        let root_id = runner.new_expr(Expr::K1(list));
113        Self {
114            runner,
115            root_id,
116            output_limit: None,
117        }
118    }
119}