A Quadrilateral Cowboy clone intended to help me learn Game Dev
1use bevy::prelude::*;
2
3#[derive(Component)]
4pub struct TerminalSystem;
5
6#[derive(Clone, Debug)]
7enum Token {
8 Identifier(String),
9 Integer(i32),
10 Period,
11 Semicolon,
12 LParen,
13 RParen
14}
15
16impl std::fmt::Display for Token {
17 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18 match self {
19 Token::Identifier(id) => write!(f, "Identifer({})", id),
20 Token::Integer(int) => write!(f, "Integer({})", int),
21 Token::LParen => write!(f, "("),
22 Token::RParen => write!(f, ")"),
23 Token::Period => write!(f, "."),
24 Token::Semicolon => write!(f, ";")
25 }
26 }
27}
28
29#[derive(Debug)]
30pub enum CallArg {
31 Integer(i32)
32}
33
34#[derive(Debug)]
35pub struct CallNode {
36 target: Option<String>,
37 method: String,
38 args: Vec<CallArg>
39}
40
41impl std::fmt::Display for CallNode {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 write!(f, "{}{}({})", match &self.target {
44 Some(val) => {
45 val.to_owned() + "."
46 },
47 None => "".into()
48 }, self.method, {
49 let mut str_args: Vec<String> = Vec::new();
50
51 for arg in &self.args {
52 match arg {
53 CallArg::Integer(int) => {
54 str_args.push(int.to_string());
55 }
56 }
57 }
58
59 str_args.join(", ")
60 })
61 }
62}
63
64#[derive(Event)]
65pub struct TerminalEvent(Vec<CallNode>);
66
67impl TerminalEvent {
68 pub fn new(nodes: Vec<CallNode>) -> Self {
69 Self(nodes)
70 }
71}
72
73#[derive(Component)]
74pub struct TerminalObject(String, TObjectType);
75
76impl TerminalObject {
77 pub fn new(val: String, object_type: TObjectType) -> Self {
78 Self(val.to_owned(), object_type)
79 }
80}
81
82pub enum TObjectType {
83 Door,
84 Laser
85}
86
87pub fn plugin(app: &mut App) {
88 app.add_observer(|event: On<TerminalEvent>, terminal_objects: Query<&TerminalObject>| {
89 for node in &event.0 {
90 if node.method == "wait" && matches!(node.target, None) {
91 // call wait
92 } else {
93 if matches!(node.target, None) {
94 panic!("invalid target None");
95 }
96
97 if let Some(obj) = terminal_objects.iter().find(|o| o.0 == *node.target.as_ref().unwrap()) {
98 match obj.1 {
99 TObjectType::Door => {
100 if node.method == "open" {
101 // call open
102 println!("open door {}", obj.0);
103 } else if node.method == "close" {
104 // call close
105 println!("close door {}", obj.0);
106 } else {
107 panic!("invalid method door.{}", node.method);
108 }
109 },
110 TObjectType::Laser => {
111 if node.method == "on" {
112 // call on
113 println!("turn on laser {}", obj.0);
114 } else if node.method == "off" {
115 // call off
116 println!("turn off laser {}", obj.0);
117 } else {
118 panic!("invalid method laser.{}", node.method);
119 }
120 }
121 }
122 } else {
123 panic!("invalid target {}", node.target.as_ref().unwrap());
124 }
125 }
126 }
127 });
128}
129
130impl TerminalSystem {
131 pub fn parse(&mut self, input: String) -> Vec<CallNode> {
132 let tokens = self.lex(input);
133 self.parse_tokens(tokens)
134 }
135
136 fn lex(&mut self, input: String) -> Vec<Token> {
137 let mut tokens = Vec::new();
138 let mut buffer = String::new();
139 let mut in_id = false;
140 let mut in_int = false;
141
142 let mut iter = 0;
143
144 while iter < input.len() {
145 let chr = input.chars().skip(iter).take(1).collect::<String>();
146
147 if in_id {
148 if chr.chars().any(|c| matches!(c, 'a'..='z')) || "0123456789".contains(&chr) {
149 buffer += &chr;
150 iter += 1;
151 continue;
152 } else {
153 tokens.push(Token::Identifier(buffer.clone()));
154 in_id = false;
155 continue;
156 }
157 } else if in_int {
158 if "0123456789".contains(&chr) {
159 buffer += &chr;
160 iter += 1;
161 continue;
162 } else {
163 tokens.push(Token::Integer(buffer.parse::<i32>().unwrap()));
164 in_int = false;
165 continue;
166 }
167 } else if chr == ";" {
168 tokens.push(Token::Semicolon);
169 iter += 1;
170 continue;
171 } else if chr == "." {
172 tokens.push(Token::Period);
173 iter += 1;
174 continue;
175 } else if chr == "(" {
176 tokens.push(Token::LParen);
177 iter += 1;
178 continue;
179 } else if chr == ")" {
180 tokens.push(Token::RParen);
181 iter += 1;
182 continue;
183 } else if chr == " " {
184 iter += 1;
185 continue;
186 } else if chr.chars().any(|c| matches!(c, 'a'..='z') || matches!(c, 'A'..='Z')) {
187 in_id = true;
188 buffer = chr.clone();
189 iter += 1;
190 continue;
191 } else if "0123456789".contains(&chr) {
192 if !in_int {
193 in_int = true;
194 buffer = chr.clone();
195 iter += 1;
196 continue;
197 }
198 } else {
199 panic!("Need to figure out how to throw errors");
200 }
201 }
202
203 tokens
204 }
205
206 fn parse_tokens(&mut self, tokens: Vec<Token>) -> Vec<CallNode> {
207 let mut nodes = Vec::new();
208 let mut iter = 0;
209
210 let consume = |tokens: &Vec<Token>, mut iter: &mut usize, expected_type: Option<Token>| -> Token {
211 let token = {
212 if *iter < tokens.len() {
213 Some(&tokens[*iter])
214 } else {
215 None
216 }
217 };
218
219 if let Some(exp_type) = expected_type {
220 if let Some(Token::Identifier(_)) = token {
221 if let Token::Identifier(_) = exp_type {
222 (*iter) += 1;
223 (*token.unwrap()).clone()
224 } else {
225 panic!("Expected type not found");
226 }
227 } else if let Some(Token::Integer(_)) = token {
228 if let Token::Integer(_) = exp_type {
229 (*iter) += 1;
230 (*token.unwrap()).clone()
231 } else {
232 panic!("Expected type not found");
233 }
234 } else if let Some(Token::LParen) = token {
235 if let Token::LParen = exp_type {
236 (*iter) += 1;
237 (*token.unwrap()).clone()
238 } else {
239 panic!("Expected type not found");
240 }
241 } else if let Some(Token::Period) = token {
242 if let Token::Period = exp_type {
243 (*iter) += 1;
244 (*token.unwrap()).clone()
245 } else {
246 panic!("Expected type not found");
247 }
248 } else if let Some(Token::RParen) = token {
249 if let Token::RParen = exp_type {
250 (*iter) += 1;
251 (*token.unwrap()).clone()
252 } else {
253 panic!("Expected type not found");
254 }
255 } else if let Some(Token::Semicolon) = token {
256 if let Token::Semicolon = exp_type {
257 (*iter) += 1;
258 (*token.unwrap()).clone()
259 } else {
260 panic!("Expected type not found");
261 }
262 } else {
263 panic!("Not sure why this would happen");
264 }
265 } else {
266 (*iter) += 1;
267 (*token.unwrap()).clone()
268 }
269 };
270
271 let parse_call = |nodes: &mut Vec<CallNode>, tokens: &Vec<Token>, mut iter: &mut usize| {
272 let first_id = consume(tokens, iter, Some(Token::Identifier(String::new())));
273 let mut target: Option<Token> = None;
274 let mut method = first_id.clone();
275
276 if matches!(tokens[*iter], Token::Period) {
277 consume(tokens, iter, Some(Token::Period));
278 target = Some(first_id.clone());
279 method = consume(tokens, iter, Some(Token::Identifier(String::new())))
280 }
281
282 consume(tokens, iter, Some(Token::LParen));
283 let mut args: Vec<CallArg> = Vec::new();
284 let mut current_token = {
285 if *iter < tokens.len() {
286 Some(&tokens[*iter])
287 } else {
288 None
289 }
290 };
291
292 while matches!(current_token, Some(_)) && !matches!(current_token, Some(Token::RParen)) {
293 let arg_token = consume(tokens, iter, Some(Token::Integer(0)));
294
295 match arg_token {
296 Token::Integer(val) => {
297 args.push(CallArg::Integer(val));
298 },
299 _ => panic!("invalid")
300 }
301
302 current_token = {
303 if *iter < tokens.len() {
304 Some(&tokens[*iter])
305 } else {
306 None
307 }
308 };
309 }
310
311 consume(tokens, iter, Some(Token::RParen));
312
313 nodes.push(CallNode{
314 target: {
315 match target {
316 Some(Token::Identifier(id)) => {
317 Some(id.to_owned())
318 },
319 Some(_) => panic!("invalid"),
320 None => None
321 }
322 },
323 method: match method {
324 Token::Identifier(id) => {
325 id.to_owned()
326 },
327 _ => panic!("invalid")
328 },
329 args
330 });
331 };
332
333 while iter < tokens.len() {
334 parse_call(&mut nodes, &tokens, &mut iter);
335 let current_token = {
336 if iter < tokens.len() {
337 Some(&tokens[iter])
338 } else {
339 None
340 }
341 };
342
343 if matches!(current_token, Some(Token::Semicolon)) {
344 consume(&tokens, &mut iter, Some(Token::Semicolon));
345 }
346 }
347
348 nodes
349 }
350}