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) => state.effect = eff,
51        Tag::End => state.end = true,
52        Tag::Say(expr) => {
53            let val = eval_expr(expr, state);
54            // TODO(@orsinium): String values can contain multiple words
55            // and even subnodes. We need to parse them with Tokenizer.
56            let s = val_to_string(&val);
57            return Some(Word::Text(s, state.effect));
58        }
59        Tag::DrwT(id) => return Some(Word::Tile(id)),
60        Tag::DrwS(id) => return Some(Word::Sprite(id)),
61        Tag::DrwI(id) => return Some(Word::Item(id)),
62        Tag::Pal(pal) => state.palette = pal,
63        Tag::Ava(id) => state.avatar = id,
64        Tag::Exit(room, x, y) => {
65            state.room = room;
66            state.pos_x = x;
67            state.pos_y = y;
68        }
69        Tag::Set(name, expr) => {
70            let val = eval_expr(expr, state);
71            state.vars.set(name, val);
72        }
73        Tag::Unknown(_, _) => {}
74    };
75    None
76}
77
78fn handle_close_tag(tag: Tag, state: &mut State) -> Option<Word> {
79    if let Tag::Eff(_) = tag {
80        state.effect = TextEffect::None
81    };
82    None
83}
84
85fn eval_expr(expr: Expr, state: &mut State) -> Val {
86    match expr {
87        Expr::SimpleExpr(expr) => eval_simple_expr(expr, state),
88        Expr::BinOp(op, lhs, rhs) => {
89            let lhs = eval_simple_expr(lhs, state);
90            let rhs = eval_simple_expr(rhs, state);
91            eval_bin_op(op, lhs, rhs)
92        }
93    }
94}
95
96fn eval_simple_expr(expr: SimpleExpr, state: &mut State) -> Val {
97    match expr {
98        SimpleExpr::Var(name) => state.vars.get(&name).clone(),
99        SimpleExpr::Item(name) => Val::I(state.inventory.get(&name) as i16),
100        SimpleExpr::Val(val) => val,
101    }
102}
103
104fn eval_bin_op(op: BinOp, lhs: Val, rhs: Val) -> Val {
105    match op {
106        BinOp::Mul => match (lhs, rhs) {
107            (Val::I(a), Val::I(b)) => Val::I(a * b),
108            (Val::I(a), Val::F(b)) => Val::F(a as f32 * b),
109            (Val::F(a), Val::I(b)) => Val::F(a * b as f32),
110            (Val::F(a), Val::F(b)) => Val::F(a * b),
111            (Val::Undef, b) => b,
112            (a, _) => a,
113        },
114        BinOp::Div => match (lhs, rhs) {
115            (Val::I(a), Val::I(b)) => Val::I(a / b),
116            (Val::I(a), Val::F(b)) => Val::F(a as f32 / b),
117            (Val::F(a), Val::I(b)) => Val::F(a / b as f32),
118            (Val::F(a), Val::F(b)) => Val::F(a / b),
119            (Val::Undef, b) => b,
120            (a, _) => a,
121        },
122        BinOp::Add => match (lhs, rhs) {
123            (Val::I(a), Val::I(b)) => Val::I(a + b),
124            (Val::I(a), Val::F(b)) => Val::F(a as f32 + b),
125            (Val::F(a), Val::I(b)) => Val::F(a + b as f32),
126            (Val::S(a), Val::S(b)) => Val::S(alloc::format!("{a}{b}")),
127            (Val::F(a), Val::F(b)) => Val::F(a + b),
128            (a, Val::Undef) => a,
129            (_, b) => b,
130        },
131        BinOp::Sub => match (lhs, rhs) {
132            (Val::I(a), Val::I(b)) => Val::I(a - b),
133            (Val::I(a), Val::F(b)) => Val::F(a as f32 - b),
134            (Val::F(a), Val::I(b)) => Val::F(a - b as f32),
135            (Val::F(a), Val::F(b)) => Val::F(a - b),
136            (Val::Undef, b) => b,
137            (a, _) => a,
138        },
139        BinOp::Lt => {
140            let res = match (lhs, rhs) {
141                (Val::I(a), Val::I(b)) => a < b,
142                (Val::I(a), Val::F(b)) => (a as f32) < b,
143                (Val::F(a), Val::I(b)) => a < b as f32,
144                (Val::F(a), Val::F(b)) => a < b,
145                _ => false,
146            };
147            Val::I(if res { 1 } else { 0 })
148        }
149        BinOp::Gt => {
150            let res = match (lhs, rhs) {
151                (Val::I(a), Val::I(b)) => a > b,
152                (Val::I(a), Val::F(b)) => a as f32 > b,
153                (Val::F(a), Val::I(b)) => a > b as f32,
154                (Val::F(a), Val::F(b)) => a > b,
155                _ => false,
156            };
157            Val::I(if res { 1 } else { 0 })
158        }
159        BinOp::Lte => {
160            let res = match (lhs, rhs) {
161                (Val::I(a), Val::I(b)) => a <= b,
162                (Val::I(a), Val::F(b)) => a as f32 <= b,
163                (Val::F(a), Val::I(b)) => a <= b as f32,
164                (Val::F(a), Val::F(b)) => a <= b,
165                _ => false,
166            };
167            Val::I(if res { 1 } else { 0 })
168        }
169        BinOp::Gte => {
170            let res = match (lhs, rhs) {
171                (Val::I(a), Val::I(b)) => a >= b,
172                (Val::I(a), Val::F(b)) => a as f32 >= b,
173                (Val::F(a), Val::I(b)) => a >= b as f32,
174                (Val::F(a), Val::F(b)) => a >= b,
175                _ => false,
176            };
177            Val::I(if res { 1 } else { 0 })
178        }
179        BinOp::Eq => {
180            let res = match (lhs, rhs) {
181                (Val::I(a), Val::I(b)) => a == b,
182                (Val::I(a), Val::F(b)) => a as f32 == b,
183                (Val::F(a), Val::I(b)) => a == b as f32,
184                (Val::F(a), Val::F(b)) => a == b,
185                _ => false,
186            };
187            Val::I(if res { 1 } else { 0 })
188        }
189    }
190}
191
192fn val_to_string(val: &Val) -> String {
193    match val {
194        Val::Undef => "0".to_string(),
195        Val::I(i) => format!("{i}"),
196        Val::S(s) => s.to_string(),
197        Val::F(f) => format!("{f}"),
198    }
199}