a4_core/
compiler.rs

1use std::collections::BTreeMap;
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 data_map: Vec<String> = Vec::new();
31        let mut ctxt = SerContext::new();
32
33        for (word, val) in self.data.iter() {
34            out.insert(word.to_string(), ser_srw(&mut ctxt, &word, val));
35        }
36
37        let mut data = Vec::new();
38        for word in ctxt.seqs {
39            data.push(out.get(&word).unwrap().clone());
40            data_map.push(word.clone());
41        }
42
43        SerDict {
44            data,
45            data_map: Some(data_map),
46            bis: ctxt.bis,
47        }
48    }
49}
50
51pub struct Context {
52    pub rt: StdRuntime,
53    pub dict: Dict,
54}
55
56impl Context {
57    pub fn load_ser_dict(&mut self, data: &SerDict) {
58        let data_map = if let Some(dm) = data.data_map.as_ref() {
59            dm.clone()
60        } else {
61            eprintln!("Error: dict has no name map! Refusing to load.");
62            return;
63        };
64
65        if !data.bis.iter().all(|bi| self.dict.bis.contains_key(bi)) {
66            eprintln!("Missing builtins! Refusing to load.");
67            return;
68        }
69
70        if data_map.len() != data.data.len() {
71            eprintln!("Data map size mismatch! Refusing to load.");
72            return;
73        }
74
75        for (name, word) in data_map.iter().zip(data.data.iter()) {
76            let cword = word
77                .iter()
78                .map(|x| match x {
79                    SerWord::LiteralVal(v) => NamedStdRuntimeWord {
80                        name: format!("LIT({})", v),
81                        word: RuntimeWord::LiteralVal(*v),
82                    },
83                    SerWord::Verb(i) => {
84                        let txt = data.bis.get(*i as usize).unwrap();
85                        NamedStdRuntimeWord {
86                            name: txt.clone(),
87                            word: RuntimeWord::Verb(self.dict.bis.get(txt).unwrap().clone()),
88                        }
89                    }
90                    SerWord::VerbSeq(i) => {
91                        let txt = data_map.get(*i as usize).unwrap();
92                        NamedStdRuntimeWord {
93                            name: txt.clone(),
94                            word: RuntimeWord::VerbSeq(VerbSeqInner::from_word(txt.to_string())),
95                        }
96                    }
97                    SerWord::UncondRelativeJump { offset } => NamedStdRuntimeWord {
98                        name: format!("UCRJ({})", offset),
99                        word: RuntimeWord::UncondRelativeJump { offset: *offset },
100                    },
101                    SerWord::CondRelativeJump { offset, jump_on } => NamedStdRuntimeWord {
102                        name: format!("CRJ({})", offset),
103                        word: RuntimeWord::CondRelativeJump {
104                            offset: *offset,
105                            jump_on: *jump_on,
106                        },
107                    },
108                })
109                .collect::<Vec<_>>();
110
111            self.dict.data.insert(
112                name.clone(),
113                StdFuncSeq {
114                    inner: Arc::new(cword),
115                },
116            );
117        }
118    }
119
120    fn compile(&mut self, data: &[String]) -> Result<Vec<NamedStdRuntimeWord>, Error> {
121        let mut vd_data: VecDeque<String> = data
122            .iter()
123            .map(String::as_str)
124            .map(str::to_lowercase)
125            .collect();
126
127        let munched = muncher(&mut vd_data);
128        assert!(vd_data.is_empty());
129
130        let conv: Vec<NamedStdRuntimeWord> = munched
131            .into_iter()
132            .map(|m| m.to_named_rt_words(&mut self.dict))
133            .flatten()
134            .collect();
135
136        Ok(conv)
137    }
138
139    pub fn evaluate(&mut self, data: Vec<String>) -> Result<(), Error> {
140        match (data.first(), data.last()) {
141            (Some(f), Some(l)) if f == ":" && l == ";" => {
142                // Must have ":", "$NAME", "$SOMETHING+", ";"
143                assert!(data.len() >= 3);
144
145                let name = data[1].to_lowercase();
146
147                // TODO: Doesn't handle "empty" definitions
148                let relevant = &data[2..][..data.len() - 3];
149
150                // let compiled = Arc::new(self.compile(relevant)?);
151                let compiled = Arc::new(self.compile(relevant).unwrap());
152
153                self.dict.data.insert(name, StdFuncSeq { inner: compiled });
154            }
155            _ => {
156                // We should interpret this as a line to compile and run
157                // (but then discard, because it isn't bound in the dict)
158                // let temp_compiled = RuntimeWord::VerbSeq(StdFuncSeq { inner:  });
159                if !data.is_empty() {
160                    let name = format!("__{}", self.dict.shame_idx);
161                    // let comp = self.compile(&data)?;
162                    let comp = self.compile(&data).unwrap();
163                    self.dict.data.insert(
164                        name.clone(),
165                        StdFuncSeq {
166                            inner: Arc::new(comp),
167                        },
168                    );
169                    self.dict.shame_idx += 1;
170                    let temp_compiled = RuntimeWord::VerbSeq(VerbSeqInner::from_word(name));
171                    self.push_exec(temp_compiled);
172                }
173            }
174        }
175
176        Ok(())
177    }
178
179    pub fn serialize(&self) -> SerDict {
180        self.dict.serialize()
181    }
182
183    pub fn step(&mut self) -> Result<StepResult<BuiltinToken, String>, Error> {
184        self.rt.step()
185    }
186
187    pub fn data_stack(&self) -> &StdVecStack<i32> {
188        &self.rt.data_stk
189    }
190
191    pub fn return_stack(&self) -> &StdVecStack<i32> {
192        &self.rt.ret_stk
193    }
194
195    pub fn flow_stack(&self) -> &StdVecStack<RuntimeWord<BuiltinToken, String>> {
196        &self.rt.flow_stk
197    }
198
199    pub fn with_builtins(bi: &[(&'static str, fn(&mut StdRuntime) -> Result<(), Error>)]) -> Self {
200        let mut new = Context {
201            rt: new_runtime(),
202            dict: Dict::new(),
203        };
204
205        for (word, func) in bi {
206            new.dict
207                .bis
208                .insert(word.to_string(), BuiltinToken::new(*func));
209        }
210
211        new
212    }
213
214    pub fn output(&mut self) -> String {
215        self.rt.exchange_output()
216    }
217
218    pub fn push_exec(&mut self, word: StdRuntimeWord) {
219        self.rt.push_exec(word)
220    }
221}
222
223// TODO: Expand number parser
224// Make this a function to later allow for more custom parsing
225// of literals like '0b1111_0000_1111_0000'
226//
227// See https://github.com/rust-analyzer/rust-analyzer/blob/c96481e25f08d1565cb9b3cac89323216e6f8d7f/crates/syntax/src/ast/token_ext.rs#L616-L662
228// for one way of doing this!
229fn parse_num(input: &str) -> Option<i32> {
230    input.parse::<i32>().ok()
231}
232
233/// This struct represents a "chunk" of the AST
234#[derive(Debug)]
235enum Chunk {
236    IfThen {
237        if_body: Vec<Chunk>,
238    },
239    IfElseThen {
240        if_body: Vec<Chunk>,
241        else_body: Vec<Chunk>,
242    },
243    DoLoop {
244        do_body: Vec<Chunk>,
245    },
246    Token(String),
247    Comment {
248        contents: Vec<String>,
249    }
250}
251
252impl Chunk {
253    /// Convert a chunk of AST words into a vec of `NamedStdRuntimeWord`s
254    fn to_named_rt_words(self, dict: &mut Dict) -> Vec<NamedStdRuntimeWord> {
255        let mut ret = vec![];
256
257        match self {
258            Chunk::IfThen { if_body } => {
259                // First, convert the body into a sequence
260                let mut conv: VecDeque<NamedStdRuntimeWord> = if_body
261                    .into_iter()
262                    .map(|m| m.to_named_rt_words(dict))
263                    .flatten()
264                    .collect();
265
266                conv.push_front(NamedStdRuntimeWord {
267                    name: "CRJ".into(),
268                    word: RuntimeWord::CondRelativeJump {
269                        offset: conv.len() as i32,
270                        jump_on: false,
271                    },
272                });
273
274                let conv: Vec<NamedStdRuntimeWord> = conv.into_iter().collect();
275                ret.extend(conv);
276            }
277            Chunk::IfElseThen { if_body, else_body } => {
278                let mut if_conv: VecDeque<NamedStdRuntimeWord> = if_body
279                    .into_iter()
280                    .map(|m| m.to_named_rt_words(dict))
281                    .flatten()
282                    .collect();
283
284                let else_conv: Vec<NamedStdRuntimeWord> = else_body
285                    .into_iter()
286                    .map(|m| m.to_named_rt_words(dict))
287                    .flatten()
288                    .collect();
289
290                if_conv.push_back(NamedStdRuntimeWord {
291                    name: "UCRJ".into(),
292                    word: RuntimeWord::UncondRelativeJump {
293                        offset: else_conv.len() as i32,
294                    },
295                });
296
297                if_conv.push_front(NamedStdRuntimeWord {
298                    name: "CRJ".into(),
299                    word: RuntimeWord::CondRelativeJump {
300                        offset: if_conv.len() as i32,
301                        jump_on: false,
302                    },
303                });
304
305                let conv: Vec<NamedStdRuntimeWord> =
306                    if_conv.into_iter().chain(else_conv.into_iter()).collect();
307                ret.extend(conv);
308            }
309            Chunk::DoLoop { do_body } => {
310                // First, convert the body into a sequence
311                let mut conv: VecDeque<NamedStdRuntimeWord> = do_body
312                    .into_iter()
313                    .map(|m| m.to_named_rt_words(dict))
314                    .flatten()
315                    .collect();
316
317                conv.push_back(NamedStdRuntimeWord {
318                    word: RuntimeWord::Verb(BuiltinToken::new(crate::builtins::bi_priv_loop)),
319                    name: "PRIV_LOOP".into(),
320                });
321
322                let len = conv.len();
323
324                conv.push_front(NamedStdRuntimeWord {
325                    word: RuntimeWord::Verb(BuiltinToken::new(crate::builtins::bi_retstk_push)),
326                    name: ">r".into(),
327                });
328                conv.push_front(NamedStdRuntimeWord {
329                    word: RuntimeWord::Verb(BuiltinToken::new(crate::builtins::bi_retstk_push)),
330                    name: ">r".into(),
331                });
332
333                // The Minus One here accounts for the addition of the CRJ. We should not loop back to
334                // the double `>r`s, as those only happen once at the top of the loop.
335                conv.push_back(NamedStdRuntimeWord {
336                    word: RuntimeWord::CondRelativeJump {
337                        offset: -1 * len as i32 - 1,
338                        jump_on: false,
339                    },
340                    name: "CRJ".into(),
341                });
342
343                let conv: Vec<NamedStdRuntimeWord> = conv.into_iter().collect();
344                ret.extend(conv);
345            }
346            Chunk::Token(tok) => {
347                ret.push(if let Some(bi) = dict.bis.get(&tok).cloned() {
348                    NamedStdRuntimeWord {
349                        name: tok,
350                        word: RuntimeWord::Verb(bi.clone()),
351                    }
352                } else if dict.data.contains_key(&tok) {
353                    NamedStdRuntimeWord {
354                        word: RuntimeWord::VerbSeq(VerbSeqInner::from_word(tok.clone())),
355                        name: tok,
356                    }
357                } else if let Some(num) = parse_num(&tok) {
358                    NamedStdRuntimeWord {
359                        word: RuntimeWord::LiteralVal(num),
360                        name: format!("LIT({})", num),
361                    }
362                } else {
363                    panic!("{:?}", tok);
364                    // return Err(Error::InternalError);
365                });
366            }
367            Chunk::Comment { .. } => {
368                // Nothing to do for comments
369            }
370        }
371
372        ret
373    }
374}
375
376use std::collections::VecDeque;
377
378fn muncher(data: &mut VecDeque<String>) -> Vec<Chunk> {
379    let mut chunks = vec![];
380    loop {
381        let next = if let Some(t) = data.pop_front() {
382            t
383        } else {
384            break;
385        };
386
387        match next.as_str() {
388            "do" => {
389                chunks.push(munch_do(data));
390            }
391            "if" => {
392                chunks.push(munch_if(data));
393            }
394            "(" => {
395                chunks.push(Chunk::Comment { contents: munch_comment(data) });
396            }
397            _ => chunks.push(Chunk::Token(next)),
398        }
399    }
400
401    chunks
402}
403
404fn munch_comment(data: &mut VecDeque<String>) -> Vec<String> {
405    let mut contents = vec![];
406    loop {
407        let next = if let Some(t) = data.pop_front() {
408            t
409        } else {
410            break;
411        };
412
413        match next.as_str() {
414            "(" => {
415                contents.extend(munch_comment(data));
416            }
417            ")" => {
418                return contents;
419            }
420            _ => {
421                contents.push(next);
422            }
423        }
424    }
425
426    // We... shouldn't get here. This means we never found our ")" after the "("
427    todo!()
428}
429
430fn munch_do(data: &mut VecDeque<String>) -> Chunk {
431    let mut chunks = vec![];
432    loop {
433        let next = if let Some(t) = data.pop_front() {
434            t
435        } else {
436            break;
437        };
438
439        match next.as_str() {
440            "do" => {
441                chunks.push(munch_do(data));
442            }
443            "if" => {
444                chunks.push(munch_if(data));
445            }
446            "loop" => return Chunk::DoLoop { do_body: chunks },
447            _ => chunks.push(Chunk::Token(next)),
448        }
449    }
450
451    // We... shouldn't get here. This means we never found our "loop" after the "do"
452    todo!()
453}
454
455fn munch_if(data: &mut VecDeque<String>) -> Chunk {
456    let mut chunks = vec![];
457    loop {
458        let next = if let Some(t) = data.pop_front() {
459            t
460        } else {
461            break;
462        };
463
464        match next.as_str() {
465            "do" => {
466                chunks.push(munch_do(data));
467            }
468            "if" => {
469                chunks.push(munch_if(data));
470            }
471            "then" => return Chunk::IfThen { if_body: chunks },
472            "else" => {
473                return munch_else(data, chunks);
474            }
475            _ => chunks.push(Chunk::Token(next)),
476        }
477    }
478
479    // We... shouldn't get here. This means we never found our "then"/"else" after the "if"
480    todo!()
481}
482
483fn munch_else(data: &mut VecDeque<String>, if_body: Vec<Chunk>) -> Chunk {
484    let mut chunks = vec![];
485    loop {
486        let next = if let Some(t) = data.pop_front() {
487            t
488        } else {
489            break;
490        };
491
492        match next.as_str() {
493            "do" => {
494                chunks.push(munch_do(data));
495            }
496            "if" => {
497                chunks.push(munch_if(data));
498            }
499            "then" => {
500                return Chunk::IfElseThen {
501                    if_body,
502                    else_body: chunks,
503                }
504            }
505            _ => chunks.push(Chunk::Token(next)),
506        }
507    }
508
509    // We... shouldn't get here. This means we never found our "then" after the "else"
510    todo!()
511}