bitsy_script/
interpreter.rs

1use crate::*;
2use alloc::format;
3use alloc::string::String;
4use alloc::string::ToString;
5
6#[derive(Debug, PartialEq)]
7pub enum Word {
8    Text(String, TextEffect),
9    Sprite(ID),
10    Tile(ID),
11    Item(ID),
12    LineBreak,
13    PageBreak,
14}
15
16pub struct Interpreter<'a, T: Iterator<Item = Token>> {
17    pub tokens: T,
18    pub state: &'a mut State,
19}
20
21impl<'a, T: Iterator<Item = Token>> Iterator for Interpreter<'a, T> {
22    type Item = Word;
23
24    fn next(&mut self) -> Option<Self::Item> {
25        interpret(&mut self.tokens, self.state)
26    }
27}
28
29pub fn interpret<T>(tokens: &mut T, state: &mut State) -> Option<Word>
30where
31    T: Iterator<Item = Token>,
32{
33    for token in tokens.by_ref() {
34        let maybe_word = match token {
35            Token::OpenTag(tag) => handle_open_tag(tag, state),
36            Token::CloseTag(tag) => handle_close_tag(tag, state),
37            Token::Word(t) => Some(Word::Text(t, state.effect)),
38        };
39        if let Some(word) = maybe_word {
40            return Some(word);
41        }
42    }
43    None
44}
45
46fn handle_open_tag(tag: Tag, state: &mut State) -> Option<Word> {
47    match tag {
48        Tag::Br => return Some(Word::LineBreak),
49        Tag::Pg => return Some(Word::PageBreak),
50        Tag::Eff(eff) => {
51            if state.effect != TextEffect::None {
52                state.effect = TextEffect::None;
53            } else {
54                state.effect = eff
55            }
56        }
57        Tag::End => state.end = true,
58        Tag::Say(expr) => {
59            let val = eval_expr(expr, state);
60            // TODO(@orsinium): String values can contain multiple words
61            // and even subnodes. We need to parse them with Tokenizer.
62            let s = val_to_string(&val);
63            return Some(Word::Text(s, state.effect));
64        }
65        Tag::DrwT(id) => return Some(Word::Tile(id)),
66        Tag::DrwS(id) => return Some(Word::Sprite(id)),
67        Tag::DrwI(id) => return Some(Word::Item(id)),
68        Tag::Pal(pal) => state.palette = pal,
69        Tag::Ava(id) => state.avatar = id,
70        Tag::Exit(room, x, y) => {
71            state.room = room;
72            state.pos_x = x;
73            state.pos_y = y;
74        }
75        Tag::Set(name, expr) => {
76            let val = eval_expr(expr, state);
77            state.vars.set(name, val);
78        }
79        Tag::Unknown(_, _) => {}
80    };
81    None
82}
83
84fn handle_close_tag(tag: Tag, state: &mut State) -> Option<Word> {
85    if let Tag::Eff(_) = tag {
86        state.effect = TextEffect::None
87    };
88    None
89}
90
91fn eval_expr(expr: Expr, state: &mut State) -> Val {
92    match expr {
93        Expr::SimpleExpr(expr) => eval_simple_expr(expr, state),
94        Expr::BinOp(op, lhs, rhs) => {
95            let lhs = eval_simple_expr(lhs, state);
96            let rhs = eval_simple_expr(rhs, state);
97            eval_bin_op(op, lhs, rhs)
98        }
99    }
100}
101
102fn eval_simple_expr(expr: SimpleExpr, state: &mut State) -> Val {
103    match expr {
104        SimpleExpr::Var(name) => state.vars.get(&name).clone(),
105        SimpleExpr::Item(name) => Val::I(state.inventory.get(&name) as i16),
106        SimpleExpr::Val(val) => val,
107    }
108}
109
110fn eval_bin_op(op: BinOp, lhs: Val, rhs: Val) -> Val {
111    match op {
112        BinOp::Mul => match (lhs, rhs) {
113            (Val::I(a), Val::I(b)) => Val::I(a * b),
114            (Val::I(a), Val::F(b)) => Val::F(a as f32 * b),
115            (Val::F(a), Val::I(b)) => Val::F(a * b as f32),
116            (Val::F(a), Val::F(b)) => Val::F(a * b),
117            (Val::Undef, b) => b,
118            (a, _) => a,
119        },
120        BinOp::Div => match (lhs, rhs) {
121            (Val::I(a), Val::I(b)) => Val::I(a / b),
122            (Val::I(a), Val::F(b)) => Val::F(a as f32 / b),
123            (Val::F(a), Val::I(b)) => Val::F(a / b as f32),
124            (Val::F(a), Val::F(b)) => Val::F(a / b),
125            (Val::Undef, b) => b,
126            (a, _) => a,
127        },
128        BinOp::Add => match (lhs, rhs) {
129            (Val::I(a), Val::I(b)) => Val::I(a + b),
130            (Val::I(a), Val::F(b)) => Val::F(a as f32 + b),
131            (Val::F(a), Val::I(b)) => Val::F(a + b as f32),
132            (Val::S(a), Val::S(b)) => Val::S(alloc::format!("{a}{b}")),
133            (Val::F(a), Val::F(b)) => Val::F(a + b),
134            (a, Val::Undef) => a,
135            (_, b) => b,
136        },
137        BinOp::Sub => match (lhs, rhs) {
138            (Val::I(a), Val::I(b)) => Val::I(a - b),
139            (Val::I(a), Val::F(b)) => Val::F(a as f32 - b),
140            (Val::F(a), Val::I(b)) => Val::F(a - b as f32),
141            (Val::F(a), Val::F(b)) => Val::F(a - b),
142            (Val::Undef, b) => b,
143            (a, _) => a,
144        },
145        BinOp::Lt => {
146            let res = match (lhs, rhs) {
147                (Val::I(a), Val::I(b)) => a < b,
148                (Val::I(a), Val::F(b)) => (a as f32) < b,
149                (Val::F(a), Val::I(b)) => a < b as f32,
150                (Val::F(a), Val::F(b)) => a < b,
151                _ => false,
152            };
153            Val::I(if res { 1 } else { 0 })
154        }
155        BinOp::Gt => {
156            let res = match (lhs, rhs) {
157                (Val::I(a), Val::I(b)) => a > b,
158                (Val::I(a), Val::F(b)) => a as f32 > b,
159                (Val::F(a), Val::I(b)) => a > b as f32,
160                (Val::F(a), Val::F(b)) => a > b,
161                _ => false,
162            };
163            Val::I(if res { 1 } else { 0 })
164        }
165        BinOp::Lte => {
166            let res = match (lhs, rhs) {
167                (Val::I(a), Val::I(b)) => a <= b,
168                (Val::I(a), Val::F(b)) => a as f32 <= b,
169                (Val::F(a), Val::I(b)) => a <= b as f32,
170                (Val::F(a), Val::F(b)) => a <= b,
171                _ => false,
172            };
173            Val::I(if res { 1 } else { 0 })
174        }
175        BinOp::Gte => {
176            let res = match (lhs, rhs) {
177                (Val::I(a), Val::I(b)) => a >= b,
178                (Val::I(a), Val::F(b)) => a as f32 >= b,
179                (Val::F(a), Val::I(b)) => a >= b as f32,
180                (Val::F(a), Val::F(b)) => a >= b,
181                _ => false,
182            };
183            Val::I(if res { 1 } else { 0 })
184        }
185        BinOp::Eq => {
186            let res = match (lhs, rhs) {
187                (Val::I(a), Val::I(b)) => a == b,
188                (Val::I(a), Val::F(b)) => a as f32 == b,
189                (Val::F(a), Val::I(b)) => a == b as f32,
190                (Val::F(a), Val::F(b)) => a == b,
191                _ => false,
192            };
193            Val::I(if res { 1 } else { 0 })
194        }
195    }
196}
197
198fn val_to_string(val: &Val) -> String {
199    match val {
200        Val::Undef => "0".to_string(),
201        Val::I(i) => format!("{i}"),
202        Val::S(s) => s.to_string(),
203        Val::F(f) => format!("{f}"),
204    }
205}