anachro_forth_core/
compiler.rs

1use std::{collections::BTreeMap, ops::Deref};
2use std::sync::Arc;
3
4use crate::{
5    ser_de::{SerDict, SerWord},
6    std_rt::{
7        new_runtime, ser_srw, BuiltinToken, NamedStdRuntimeWord, SerContext, StdFuncSeq,
8        StdRuntime, StdRuntimeWord, StdVecStack,
9    },
10    Error, RuntimeWord, StepResult, VerbSeqInner,
11};
12
13pub struct Dict {
14    pub bis: BTreeMap<String, BuiltinToken>,
15    pub data: BTreeMap<String, StdFuncSeq>,
16    pub(crate) shame_idx: usize,
17}
18
19impl Dict {
20    pub fn new() -> Self {
21        Self {
22            bis: BTreeMap::new(),
23            data: BTreeMap::new(),
24            shame_idx: 0,
25        }
26    }
27
28    pub fn serialize(&self) -> SerDict {
29        let mut out: BTreeMap<String, Vec<SerWord>> = BTreeMap::new();
30        let mut ctxt = SerContext::new();
31
32        for (word, val) in self.data.iter() {
33            out.insert(word.to_string(), ser_srw(&mut ctxt, &word, val));
34        }
35
36        let mut data = Vec::new();
37        for word in ctxt.seqs {
38            data.push(out.get(&word).unwrap().clone());
39        }
40
41        SerDict {
42            data,
43            bis: ctxt.bis,
44        }
45    }
46}
47
48pub struct Context {
49    pub rt: StdRuntime,
50    pub dict: Dict,
51}
52
53impl Context {
54
55
56    #[allow(dead_code)]
57    fn compile2(&mut self, data: &[String]) -> Result<Vec<NamedStdRuntimeWord>, Error> {
58        let mut vd_data: VecDeque<String> = data.iter().map(String::as_str).map(str::to_lowercase).collect();
59
60        let munched = muncher(&mut vd_data);
61        assert!(vd_data.is_empty());
62
63        let conv: Vec<NamedStdRuntimeWord> = munched.into_iter().map(|m| m.to_named_rt_words(&mut self.dict)).flatten().collect();
64
65        Ok(conv)
66    }
67
68    pub fn evaluate(&mut self, data: Vec<String>) -> Result<(), Error> {
69        match (data.first(), data.last()) {
70            (Some(f), Some(l)) if f == ":" && l == ";" => {
71                // Must have ":", "$NAME", "$SOMETHING+", ";"
72                assert!(data.len() >= 3);
73
74                let name = data[1].to_lowercase();
75
76                // TODO: Doesn't handle "empty" definitions
77                let relevant = &data[2..][..data.len() - 3];
78
79                // let compiled = Arc::new(self.compile(relevant)?);
80                let compiled = Arc::new(self.compile2(relevant).unwrap());
81
82                self.dict.data.insert(name, StdFuncSeq { inner: compiled });
83            }
84            _ => {
85                // We should interpret this as a line to compile and run
86                // (but then discard, because it isn't bound in the dict)
87                // let temp_compiled = RuntimeWord::VerbSeq(StdFuncSeq { inner:  });
88                if !data.is_empty() {
89                    let name = format!("__{}", self.dict.shame_idx);
90                    // let comp = self.compile(&data)?;
91                    let comp = self.compile2(&data).unwrap();
92                    self.dict.data.insert(
93                        name.clone(),
94                        StdFuncSeq {
95                            inner: Arc::new(comp),
96                        },
97                    );
98                    self.dict.shame_idx += 1;
99                    let temp_compiled = RuntimeWord::VerbSeq(VerbSeqInner::from_word(name));
100                    self.push_exec(temp_compiled);
101                }
102            }
103        }
104
105        Ok(())
106    }
107
108
109
110    pub fn serialize(&self) -> SerDict {
111        self.dict.serialize()
112    }
113
114    pub fn step(&mut self) -> Result<StepResult<BuiltinToken, String>, Error> {
115        self.rt.step()
116    }
117
118    pub fn data_stack(&self) -> &StdVecStack<i32> {
119        &self.rt.data_stk
120    }
121
122    pub fn return_stack(&self) -> &StdVecStack<i32> {
123        &self.rt.ret_stk
124    }
125
126    pub fn flow_stack(&self) -> &StdVecStack<RuntimeWord<BuiltinToken, String>> {
127        &self.rt.flow_stk
128    }
129
130    pub fn with_builtins(bi: &[(&'static str, fn(&mut StdRuntime) -> Result<(), Error>)]) -> Self {
131        let mut new = Context {
132            rt: new_runtime(),
133            dict: Dict::new(),
134        };
135
136        for (word, func) in bi {
137            new.dict
138                .bis
139                .insert(word.to_string(), BuiltinToken::new(*func));
140        }
141
142        new
143    }
144
145    pub fn output(&mut self) -> String {
146        self.rt.exchange_output()
147    }
148
149    pub fn push_exec(&mut self, word: StdRuntimeWord) {
150        self.rt.push_exec(word)
151    }
152}
153
154// TODO: Expand number parser
155// Make this a function to later allow for more custom parsing
156// of literals like '0b1111_0000_1111_0000'
157//
158// See https://github.com/rust-analyzer/rust-analyzer/blob/c96481e25f08d1565cb9b3cac89323216e6f8d7f/crates/syntax/src/ast/token_ext.rs#L616-L662
159// for one way of doing this!
160fn parse_num(input: &str) -> Option<i32> {
161    input.parse::<i32>().ok()
162}
163
164
165/// This struct represents a "chunk" of the AST
166#[derive(Debug)]
167enum Chunk {
168    IfThen {
169        if_body: Vec<Chunk>,
170    },
171    IfElseThen {
172        if_body: Vec<Chunk>,
173        else_body: Vec<Chunk>,
174    },
175    DoLoop {
176        do_body: Vec<Chunk>,
177    },
178    Token(String),
179}
180
181impl Chunk {
182    /// Convert a chunk of AST words into a vec of `NamedStdRuntimeWord`s
183    fn to_named_rt_words(self, dict: &mut Dict) -> Vec<NamedStdRuntimeWord> {
184        let mut ret = vec![];
185
186        match self {
187            Chunk::IfThen { if_body } => {
188                // First, convert the body into a sequence
189                let mut conv: VecDeque<NamedStdRuntimeWord> = if_body
190                    .into_iter()
191                    .map(|m| m.to_named_rt_words(dict))
192                    .flatten()
193                    .collect();
194
195                conv.push_front(NamedStdRuntimeWord {
196                    name: "CRJ".into(),
197                    word: RuntimeWord::CondRelativeJump { offset: conv.len() as i32, jump_on: false },
198                });
199
200                let conv: Vec<NamedStdRuntimeWord> = conv.into_iter().collect();
201                ret.extend(conv);
202            },
203            Chunk::IfElseThen { if_body, else_body } => {
204                let mut if_conv: VecDeque<NamedStdRuntimeWord> = if_body
205                    .into_iter()
206                    .map(|m| m.to_named_rt_words(dict))
207                    .flatten()
208                    .collect();
209
210                let else_conv: Vec<NamedStdRuntimeWord> = else_body
211                    .into_iter()
212                    .map(|m| m.to_named_rt_words(dict))
213                    .flatten()
214                    .collect();
215
216                if_conv.push_back(NamedStdRuntimeWord {
217                    name: "UCRJ".into(),
218                    word: RuntimeWord::UncondRelativeJump { offset: else_conv.len() as i32 },
219                });
220
221                if_conv.push_front(NamedStdRuntimeWord {
222                    name: "CRJ".into(),
223                    word: RuntimeWord::CondRelativeJump { offset: if_conv.len() as i32, jump_on: false },
224                });
225
226                let conv: Vec<NamedStdRuntimeWord> = if_conv.into_iter().chain(else_conv.into_iter()).collect();
227                ret.extend(conv);
228            },
229            Chunk::DoLoop { do_body } => {
230                // First, convert the body into a sequence
231                let mut conv: VecDeque<NamedStdRuntimeWord> = do_body
232                    .into_iter()
233                    .map(|m| m.to_named_rt_words(dict))
234                    .flatten()
235                    .collect();
236
237                conv.push_back(NamedStdRuntimeWord {
238                    word: RuntimeWord::Verb(BuiltinToken::new(crate::builtins::bi_priv_loop)),
239                    name: "PRIV_LOOP".into(),
240                });
241
242                let len = conv.len();
243
244                conv.push_front(NamedStdRuntimeWord {
245                    word: RuntimeWord::Verb(BuiltinToken::new(crate::builtins::bi_retstk_push)),
246                    name: ">r".into(),
247                });
248                conv.push_front(NamedStdRuntimeWord {
249                    word: RuntimeWord::Verb(BuiltinToken::new(crate::builtins::bi_retstk_push)),
250                    name: ">r".into(),
251                });
252
253                // The Minus One here accounts for the addition of the CRJ. We should not loop back to
254                // the double `>r`s, as those only happen once at the top of the loop.
255                conv.push_back(NamedStdRuntimeWord {
256                    word: RuntimeWord::CondRelativeJump { offset: -1 * len as i32 - 1, jump_on: false },
257                    name: "CRJ".into(),
258                });
259
260                let conv: Vec<NamedStdRuntimeWord> = conv.into_iter().collect();
261                ret.extend(conv);
262            },
263            Chunk::Token(tok) => {
264                ret.push(if let Some(bi) = dict.bis.get(&tok).cloned() {
265                    NamedStdRuntimeWord {
266                        name: tok,
267                        word: RuntimeWord::Verb(bi.clone()),
268                    }
269                } else if dict.data.contains_key(&tok) {
270                    NamedStdRuntimeWord {
271                        word: RuntimeWord::VerbSeq(VerbSeqInner::from_word(tok.clone())),
272                        name: tok,
273                    }
274                } else if let Some(num) = parse_num(&tok) {
275                    NamedStdRuntimeWord {
276                        word: RuntimeWord::LiteralVal(num),
277                        name: format!("LIT({})", num),
278                    }
279                } else {
280                    panic!()
281                    // return Err(Error::InternalError);
282                });
283            },
284        }
285
286        ret
287    }
288}
289
290use std::collections::VecDeque;
291
292fn muncher(data: &mut VecDeque<String>) -> Vec<Chunk> {
293    let mut chunks = vec![];
294    loop {
295        let next = if let Some(t) = data.pop_front() {
296            t
297        } else {
298            break;
299        };
300
301        match next.as_str() {
302            "do" => {
303                chunks.push(munch_do(data));
304            }
305            "if" => {
306                chunks.push(munch_if(data));
307            }
308            _ => chunks.push(Chunk::Token(next)),
309        }
310    }
311
312    chunks
313}
314
315fn munch_do(data: &mut VecDeque<String>) -> Chunk {
316    let mut chunks = vec![];
317    loop {
318        let next = if let Some(t) = data.pop_front() {
319            t
320        } else {
321            break;
322        };
323
324        match next.as_str() {
325            "do" => {
326                chunks.push(munch_do(data));
327            }
328            "if" => {
329                chunks.push(munch_if(data));
330            }
331            "loop" => {
332                return Chunk::DoLoop {
333                    do_body: chunks,
334                }
335            }
336            _ => chunks.push(Chunk::Token(next)),
337        }
338    }
339
340    // We... shouldn't get here. This means we never found our "loop" after the "do"
341    todo!()
342}
343
344fn munch_if(data: &mut VecDeque<String>) -> Chunk {
345    let mut chunks = vec![];
346    loop {
347        let next = if let Some(t) = data.pop_front() {
348            t
349        } else {
350            break;
351        };
352
353        match next.as_str() {
354            "do" => {
355                chunks.push(munch_do(data));
356            }
357            "if" => {
358                chunks.push(munch_if(data));
359            }
360            "then" => {
361                return Chunk::IfThen {
362                    if_body: chunks,
363                }
364            }
365            "else" => {
366                return munch_else(data, chunks);
367            }
368            _ => chunks.push(Chunk::Token(next)),
369        }
370    }
371
372    // We... shouldn't get here. This means we never found our "then"/"else" after the "if"
373    todo!()
374}
375
376fn munch_else(data: &mut VecDeque<String>, if_body: Vec<Chunk>) -> Chunk {
377    let mut chunks = vec![];
378    loop {
379        let next = if let Some(t) = data.pop_front() {
380            t
381        } else {
382            break;
383        };
384
385        match next.as_str() {
386            "do" => {
387                chunks.push(munch_do(data));
388            }
389            "if" => {
390                chunks.push(munch_if(data));
391            }
392            "then" => {
393                return Chunk::IfElseThen {
394                    if_body,
395                    else_body: chunks,
396                }
397            }
398            _ => chunks.push(Chunk::Token(next)),
399        }
400    }
401
402    // We... shouldn't get here. This means we never found our "then" after the "else"
403    todo!()
404}