1use std::fmt;
2
3use nom::{
4 branch::alt,
5 bytes::complete::{tag, take_till, take_until},
6 character::complete::{alpha0, alpha1, char, i64, multispace0},
7 combinator::{map, opt},
8 multi::many1,
9 number::complete::double,
10 sequence::{delimited, preceded, terminated, tuple},
11 IResult,
12};
13use serde::{Deserialize, Serialize};
14
15#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
18pub enum Atom {
19 String(String),
20 Variable(String),
21 Boolean(bool),
22 Integer(i64),
23 Double(f64),
24 HogwartsHouse(HogwartsHouse),
25}
26
27#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
28pub enum HogwartsHouse {
29 Gryffindor,
30 Hufflepuff,
31 Ravenclaw,
32 Slytherin,
33}
34
35impl fmt::Display for HogwartsHouse {
36 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37 match self {
38 HogwartsHouse::Gryffindor => write!(f, "Gryffindor"),
39 HogwartsHouse::Hufflepuff => write!(f, "Hufflepuff"),
40 HogwartsHouse::Ravenclaw => write!(f, "Ravenclaw"),
41 HogwartsHouse::Slytherin => write!(f, "Slytherin"),
42 }
43 }
44}
45
46impl Atom {
47 pub fn to_string(&self) -> String {
48 match self {
49 Atom::Boolean(boolean) => boolean.to_string(),
50 Atom::Integer(integer) => integer.to_string(),
51 Atom::Double(float) => float.to_string(),
52 Atom::String(string) => string.to_string(),
53 Atom::Variable(var) => var.to_string(),
54 Atom::HogwartsHouse(house) => house.to_string(),
55 }
56 }
57}
58
59impl From<Atom> for Expression {
60 fn from(atom: Atom) -> Self {
61 Expression::Atom(atom)
62 }
63}
64
65fn parse_atom(input: &str) -> IResult<&str, Expression> {
66 let parser = alt((
67 parse_boolean,
68 parse_hogwarts_house,
69 parse_double,
70 parse_integer,
71 parse_string,
72 parse_variable,
73 ));
74 map(parser, |atom| atom.into())(input)
75}
76
77fn parse_boolean(input: &str) -> IResult<&str, Atom> {
78 let parser = alt((tag("true"), tag("false")));
79 map(parser, |boolean: &str| Atom::Boolean(boolean == "true"))(input)
80}
81
82fn parse_double(input: &str) -> IResult<&str, Atom> {
83 if !input.contains(".") {
84 return Err(nom::Err::Error(nom::error::Error::new(
85 input,
86 nom::error::ErrorKind::Digit,
87 )));
88 }
89
90 let parser = double;
91 map(parser, |float| Atom::Double(float))(input)
92}
93
94fn parse_integer(input: &str) -> IResult<&str, Atom> {
95 let parser = i64;
96 map(parser, |integer| Atom::Integer(integer))(input)
97}
98
99fn parse_string(input: &str) -> IResult<&str, Atom> {
100 let parser = delimited(tag("\""), take_until("\""), tag("\""));
101 map(parser, |string: &str| Atom::String(string.to_string()))(input)
102}
103
104fn parse_variable(input: &str) -> IResult<&str, Atom> {
105 map(alpha1, |var: &str| Atom::Variable(var.to_string()))(input)
106}
107
108fn parse_hogwarts_house(input: &str) -> IResult<&str, Atom> {
109 let parser = alt((
110 tag("Gryffindor"),
111 tag("Hufflepuff"),
112 tag("Ravenclaw"),
113 tag("Slytherin"),
114 ));
115 map(parser, |house: &str| match house {
116 "Gryffindor" => Atom::HogwartsHouse(HogwartsHouse::Gryffindor),
117 "Hufflepuff" => Atom::HogwartsHouse(HogwartsHouse::Hufflepuff),
118 "Ravenclaw" => Atom::HogwartsHouse(HogwartsHouse::Ravenclaw),
119 "Slytherin" => Atom::HogwartsHouse(HogwartsHouse::Slytherin),
120 _ => panic!("Unknown Hogwarts house: {}", house),
121 })(input)
122}
123
124#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
127pub enum Expression {
128 SpellCast(Spell, Box<Option<Expression>>),
129 BinaryOperation(BinaryOperation, Box<Expression>, Box<Expression>),
130 Atom(Atom),
131 Comment(String),
132 SortingHat,
133}
134
135#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
136pub enum Spell {
137 Aguamenti,
138 AvadaKedabra,
139 Engorgio,
140 Incendio,
141 Inmobolus,
142 Lumos,
143 Nox,
144 Obliviate,
145 OculusReparo,
146 Periculum,
147 Reducio,
148 PetrificusTotalus,
149 Revelio,
150 Serpensortia,
151 WingardiumLeviosa,
152}
153
154#[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)]
155pub enum BinaryOperation {
156 Plus,
157 Minus,
158 Times,
159 Divide,
160 Equal,
161 NotEqual,
162}
163
164pub fn parse_expression(input: &str) -> IResult<&str, Expression> {
165 alt((
168 parse_sorting_hat,
169 parse_comment,
170 parse_spell_cast,
171 parse_binary_operation,
172 parse_atom,
173 ))(input)
174}
175
176pub fn parse_sorting_hat(input: &str) -> IResult<&str, Expression> {
177 map(alt((tag("SortingHat"), tag("🎩✨"))), |_| {
178 Expression::SortingHat
179 })(input)
180}
181
182pub fn parse_spell_cast(input: &str) -> IResult<&str, Expression> {
183 let spell_parser = delimited(tag("~"), alpha0, opt(tag(" ")));
185 let target_parser = parse_expression;
186 let parser = tuple((spell_parser, opt(target_parser)));
187
188 map(parser, |(spell, target)| match spell {
189 "AvadaKedabra" => Expression::SpellCast(Spell::AvadaKedabra, Box::new(target)),
190 "Aguamenti" => Expression::SpellCast(Spell::Aguamenti, Box::new(target)),
191 "Engorgio" => Expression::SpellCast(Spell::Engorgio, Box::new(target)),
192 "Incendio" => Expression::SpellCast(Spell::Incendio, Box::new(target)),
193 "Inmobolus" => Expression::SpellCast(Spell::Inmobolus, Box::new(target)),
194 "Lumos" => Expression::SpellCast(Spell::Lumos, Box::new(target)),
195 "Nox" => Expression::SpellCast(Spell::Nox, Box::new(target)),
196 "Obliviate" => Expression::SpellCast(Spell::Obliviate, Box::new(target)),
197 "OculusReparo" => Expression::SpellCast(Spell::OculusReparo, Box::new(target)),
198 "Periculum" => Expression::SpellCast(Spell::Periculum, Box::new(target)),
199 "Reducio" => Expression::SpellCast(Spell::Reducio, Box::new(target)),
200 "PetrificusTotalus" => Expression::SpellCast(Spell::PetrificusTotalus, Box::new(target)),
201 "Revelio" => Expression::SpellCast(Spell::Revelio, Box::new(target)),
202 "Serpensortia" => Expression::SpellCast(Spell::Serpensortia, Box::new(target)),
203 "WingardiumLeviosa" => Expression::SpellCast(Spell::WingardiumLeviosa, Box::new(target)),
204 _ => panic!("Wand broken: Unknown spell: {}", spell),
205 })(input)
206}
207
208pub fn parse_binary_operation(input: &str) -> IResult<&str, Expression> {
209 let (rest, (left, _, op, _, right)) = tuple((
210 parse_atom,
211 multispace0,
212 parse_binary_operator,
213 multispace0,
214 parse_atom,
215 ))(input)?;
216
217 let expression = Expression::BinaryOperation(op, Box::new(left), Box::new(right));
218 Ok((rest, expression))
219}
220
221pub fn parse_comment(input: &str) -> IResult<&str, Expression> {
222 map(
223 preceded(char('#'), take_till(|c| c == '\n')),
224 |comment: &str| Expression::Comment(comment.to_string()),
225 )(input)
226}
227
228pub fn parse_binary_operator(input: &str) -> IResult<&str, BinaryOperation> {
229 alt((
230 map(char('+'), |_| BinaryOperation::Plus),
231 map(char('-'), |_| BinaryOperation::Minus),
232 map(char('*'), |_| BinaryOperation::Times),
233 map(char('/'), |_| BinaryOperation::Divide),
234 map(tag("=="), |_| BinaryOperation::Equal),
235 map(tag("!="), |_| BinaryOperation::NotEqual),
236 ))(input)
237}
238
239#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
242pub enum Statement {
243 VariableAssignment(String, Expression),
244 ExpressionStatement(Expression),
245 If(Expression, Vec<Statement>, Vec<Statement>),
246 Quidditch(Vec<Statement>),
247 Snitch,
248}
249
250fn parse_statement(input: &str) -> IResult<&str, Statement> {
251 let parser_content = alt((
252 parse_if_statement,
253 parse_snitch_statement,
254 parse_quidditch_statement,
255 parse_variable_assignment,
256 parse_expression_statement,
257 ));
258
259 preceded(multispace0, terminated(parser_content, multispace0))(input)
260}
261
262fn parse_variable_assignment(input: &str) -> IResult<&str, Statement> {
263 let (rest, (var, _, _, _, atom)) = tuple((
264 parse_variable,
265 multispace0,
266 char('='),
267 multispace0,
268 parse_expression,
269 ))(input)?;
270
271 let statement = Statement::VariableAssignment(var.to_string(), atom);
272 Ok((rest, statement))
273}
274
275fn parse_expression_statement(input: &str) -> IResult<&str, Statement> {
276 let (rest, expression) = terminated(parse_expression, multispace0)(input)?;
277 let statement = Statement::ExpressionStatement(expression);
278 Ok((rest, statement))
279}
280
281fn parse_if_statement(input: &str) -> IResult<&str, Statement> {
282 let parse_if = preceded(multispace0, terminated(tag("if"), multispace0));
283 let parse_condition = preceded(multispace0, terminated(parse_expression, multispace0));
284 let parse_true_block = delimited(char('{'), many1(parse_statement), char('}'));
285 let parse_else = preceded(multispace0, terminated(tag("else"), multispace0));
286 let parse_false_block = delimited(char('{'), many1(parse_statement), char('}'));
287
288 map(
289 tuple((
290 preceded(parse_if, parse_condition),
291 parse_true_block,
292 opt(delimited(parse_else, parse_false_block, multispace0)),
293 )),
294 |(cond, true_block, else_block)| {
295 Statement::If(cond, true_block, else_block.unwrap_or(vec![]))
296 },
297 )(input)
298}
299
300fn parse_quidditch_statement(input: &str) -> IResult<&str, Statement> {
301 let parse_quidditch = preceded(multispace0, terminated(tag("quidditch"), multispace0));
302 let parse_block = delimited(char('{'), many1(parse_statement), char('}'));
303
304 map(preceded(parse_quidditch, parse_block), |block| {
305 Statement::Quidditch(block)
306 })(input)
307}
308
309fn parse_snitch_statement(input: &str) -> IResult<&str, Statement> {
310 let parse_snitch = preceded(multispace0, terminated(tag("snitch"), multispace0));
311
312 map(parse_snitch, |_| Statement::Snitch)(input)
313}
314
315#[derive(Debug, PartialEq, Serialize, Deserialize)]
318pub struct Program(pub Vec<Statement>);
319
320pub fn parse_program(input: &str) -> IResult<&str, Program> {
321 map(many1(terminated(parse_statement, multispace0)), Program)(input)
322}
323
324#[cfg(test)]
325mod tests {
326 use super::*;
327
328 #[test]
331 fn test_parse_string() {
332 let input = "\"Hello, world!\"";
333 let expected = Atom::String("Hello, world!".to_string());
334 let (_, actual) = parse_string(input).unwrap();
335 assert_eq!(expected, actual);
336 }
337
338 #[test]
339 fn test_parse_variable() {
340 let input = "foo";
341 let expected = Atom::Variable("foo".to_string());
342 let (_, actual) = parse_variable(input).unwrap();
343 assert_eq!(expected, actual);
344 }
345
346 #[test]
347 fn test_parse_boolean_true() {
348 let input = "true";
349 let expected = Atom::Boolean(true);
350 let (_, actual) = parse_boolean(input).unwrap();
351 assert_eq!(expected, actual);
352 }
353
354 #[test]
355 fn test_parse_boolean_false() {
356 let input = "false";
357 let expected = Atom::Boolean(false);
358 let (_, actual) = parse_boolean(input).unwrap();
359 assert_eq!(expected, actual);
360 }
361
362 #[test]
363 fn test_parse_double() {
364 let input = "123.456";
365 let expected = Atom::Double(123.456);
366 let (_, actual) = parse_double(input).unwrap();
367 assert_eq!(expected, actual);
368 }
369
370 #[test]
371 fn test_parse_integer() {
372 let input = "123";
373 let expected = Atom::Integer(123);
374 let (_, actual) = parse_integer(input).unwrap();
375 assert_eq!(expected, actual);
376 }
377
378 #[test]
379 fn test_parse_hogwarts_house() {
380 let input = "Gryffindor";
381 let expected = Atom::HogwartsHouse(HogwartsHouse::Gryffindor);
382 let (_, actual) = parse_hogwarts_house(input).unwrap();
383 assert_eq!(expected, actual);
384 }
385
386 #[test]
389 fn test_parse_statement_with_whitespaces() {
390 let input = " ~AvadaKedabra ";
391 let expected = Statement::ExpressionStatement(Expression::SpellCast(
392 Spell::AvadaKedabra,
393 Box::new(None),
394 ));
395 let (_, actual) = parse_statement(input).unwrap();
396 assert_eq!(expected, actual);
397 }
398
399 #[test]
400 fn test_parse_spell_cast() {
401 let input = "~AvadaKedabra";
402 let expected = Expression::SpellCast(Spell::AvadaKedabra, Box::new(None));
403 let (_, actual) = parse_spell_cast(input).unwrap();
404 assert_eq!(expected, actual);
405 }
406
407 #[test]
408 fn test_parse_spell_cast_with_string() {
409 let input = "~Revelio \"Hello, world!\"";
410 let expected = Expression::SpellCast(
411 Spell::Revelio,
412 Box::new(Some(Atom::String("Hello, world!".to_string()).into())),
413 );
414 let (_, actual) = parse_spell_cast(input).unwrap();
415 assert_eq!(expected, actual);
416 }
417
418 #[test]
419 fn test_parse_spell_cast_with_string_and_space() {
420 let input = "~Revelio \"Hello, world!\" ";
421 let expected = Expression::SpellCast(
422 Spell::Revelio,
423 Box::new(Some(Atom::String("Hello, world!".to_string()).into())),
424 );
425 let (_, actual) = parse_spell_cast(input).unwrap();
426 assert_eq!(expected, actual);
427 }
428
429 #[test]
430 fn test_parse_binary_operation() {
431 let input = "\"Hello, \" + \"world!\"";
432 let expected = Expression::BinaryOperation(
433 BinaryOperation::Plus,
434 Box::new(Atom::String("Hello, ".to_string()).into()),
435 Box::new(Atom::String("world!".to_string()).into()),
436 );
437 let (_, actual) = parse_binary_operation(input).unwrap();
438 assert_eq!(expected, actual);
439 }
440
441 #[test]
442 fn test_parse_binary_operation_with_variable() {
443 let input = "foo + \"bar\"";
444 let expected = Expression::BinaryOperation(
445 BinaryOperation::Plus,
446 Box::new(Atom::Variable("foo".to_string()).into()),
447 Box::new(Atom::String("bar".to_string()).into()),
448 );
449 let (_, actual) = parse_binary_operation(input).unwrap();
450 assert_eq!(expected, actual);
451 }
452
453 #[test]
454 fn test_parse_binary_operation_with_integer() {
455 let input = "123 + 456";
456 let expected = Expression::BinaryOperation(
457 BinaryOperation::Plus,
458 Box::new(Atom::Integer(123).into()),
459 Box::new(Atom::Integer(456).into()),
460 );
461 let (_, actual) = parse_binary_operation(input).unwrap();
462 assert_eq!(expected, actual);
463 }
464
465 #[test]
466 fn test_parse_binary_operation_with_double() {
467 let input = "123.456 + 456.789";
468 let expected = Expression::BinaryOperation(
469 BinaryOperation::Plus,
470 Box::new(Atom::Double(123.456).into()),
471 Box::new(Atom::Double(456.789).into()),
472 );
473 let (_, actual) = parse_binary_operation(input).unwrap();
474 assert_eq!(expected, actual);
475 }
476
477 #[test]
478 fn test_parse_binary_operation_with_boolean() {
479 let input = "true == false";
480 let expected = Expression::BinaryOperation(
481 BinaryOperation::Equal,
482 Box::new(Atom::Boolean(true).into()),
483 Box::new(Atom::Boolean(false).into()),
484 );
485 let (_, actual) = parse_binary_operation(input).unwrap();
486 assert_eq!(expected, actual);
487 }
488
489 #[test]
490 fn test_parse_binary_operation_with_variable_and_integer() {
491 let input = "foo + 4";
492 let expected = Expression::BinaryOperation(
493 BinaryOperation::Plus,
494 Box::new(Atom::Variable("foo".to_string()).into()),
495 Box::new(Atom::Integer(4).into()),
496 );
497 let (_, actual) = parse_binary_operation(input).unwrap();
498 assert_eq!(expected, actual);
499 }
500
501 #[test]
502 fn test_parse_comment() {
503 let input = "# Hello, world!";
504 let expected = Expression::Comment(" Hello, world!".to_string());
505 let (_, actual) = parse_comment(input).unwrap();
506 assert_eq!(expected, actual);
507 }
508
509 #[test]
510 fn test_sorting_hat() {
511 let input = "🎩✨";
512 let expected = Expression::SortingHat;
513 let (_, actual) = parse_sorting_hat(input).unwrap();
514 assert_eq!(expected, actual);
515 }
516
517 #[test]
520 fn test_parse_variable_assignment() {
521 let input = "foo = \"Hello, world!\"";
522 let expected = Statement::VariableAssignment(
523 "foo".to_string(),
524 Atom::String("Hello, world!".to_string()).into(),
525 );
526 let (_, actual) = parse_variable_assignment(input).unwrap();
527 assert_eq!(expected, actual);
528 }
529
530 #[test]
531 fn test_parse_expression_statement() {
532 let input = "~AvadaKedabra";
533 let expected = Statement::ExpressionStatement(Expression::SpellCast(
534 Spell::AvadaKedabra,
535 Box::new(None),
536 ));
537 let (_, actual) = parse_expression_statement(input).unwrap();
538 assert_eq!(expected, actual);
539 }
540
541 #[test]
542 fn test_parse_expression_statement_with_string() {
543 let input = "~Revelio \"Hello, world!\"";
544 let expected = Statement::ExpressionStatement(Expression::SpellCast(
545 Spell::Revelio,
546 Box::new(Some(Atom::String("Hello, world!".to_string()).into())),
547 ));
548 let (_, actual) = parse_expression_statement(input).unwrap();
549 assert_eq!(expected, actual);
550 }
551
552 #[test]
553 fn test_parse_if() {
554 let input = "if true { ~Revelio 4 }";
555 let expected = Statement::If(
556 Atom::Boolean(true).into(),
557 vec![Statement::ExpressionStatement(Expression::SpellCast(
558 Spell::Revelio,
559 Box::new(Some(Atom::Integer(4).into())),
560 ))],
561 vec![],
562 );
563 let (_, actual) = parse_if_statement(input).unwrap();
564 assert_eq!(expected, actual);
565 }
566
567 #[test]
568 fn test_parse_if_multiple_statements() {
569 let input = "if 4 == 4 {
570 ~Revelio 4
571 ~AvadaKedabra
572 }";
573 let expected = Statement::If(
574 Expression::BinaryOperation(
575 BinaryOperation::Equal,
576 Box::new(Atom::Integer(4).into()),
577 Box::new(Atom::Integer(4).into()),
578 ),
579 vec![
580 Statement::ExpressionStatement(Expression::SpellCast(
581 Spell::Revelio,
582 Box::new(Some(Atom::Integer(4).into())),
583 )),
584 Statement::ExpressionStatement(Expression::SpellCast(
585 Spell::AvadaKedabra,
586 Box::new(None),
587 )),
588 ],
589 vec![],
590 );
591 let (_, actual) = parse_if_statement(input).unwrap();
592 assert_eq!(expected, actual);
593 }
594
595 #[test]
596 fn test_parse_if_with_else() {
597 let input = "if y != 11 {
598 ~Revelio \"y is not 11\"
599} else {
600 ~Lumos
601 ~Revelio \"y is 11\"
602}";
603 let expected = Statement::If(
604 Expression::BinaryOperation(
605 BinaryOperation::NotEqual,
606 Box::new(Atom::Variable("y".to_string()).into()),
607 Box::new(Atom::Integer(11).into()),
608 ),
609 vec![Statement::ExpressionStatement(Expression::SpellCast(
610 Spell::Revelio,
611 Box::new(Some(Atom::String("y is not 11".to_string()).into())),
612 ))],
613 vec![
614 Statement::ExpressionStatement(Expression::SpellCast(Spell::Lumos, Box::new(None))),
615 Statement::ExpressionStatement(Expression::SpellCast(
616 Spell::Revelio,
617 Box::new(Some(Atom::String("y is 11".to_string()).into())),
618 )),
619 ],
620 );
621 let (_, actual) = parse_if_statement(input).unwrap();
622 assert_eq!(expected, actual);
623 }
624
625 #[test]
626 fn test_parse_quidditch() {
627 let input = "quidditch {
628 ~Engorgio x
629 snitch
630 ~Revelio x
631}";
632 let expected = Statement::Quidditch(vec![
633 Statement::ExpressionStatement(Expression::SpellCast(
634 Spell::Engorgio,
635 Box::new(Some(Atom::Variable("x".to_string()).into())),
636 )),
637 Statement::Snitch,
638 Statement::ExpressionStatement(Expression::SpellCast(
639 Spell::Revelio,
640 Box::new(Some(Atom::Variable("x".to_string()).into())),
641 )),
642 ]);
643 let (_, actual) = parse_quidditch_statement(input).unwrap();
644 assert_eq!(expected, actual);
645 }
646
647 #[test]
650 fn test_parse_program() {
651 let input = "~AvadaKedabra\n~Revelio \"Hello, world!\"";
652 let expected = Program(vec![
653 Statement::ExpressionStatement(Expression::SpellCast(
654 Spell::AvadaKedabra,
655 Box::new(None),
656 )),
657 Statement::ExpressionStatement(Expression::SpellCast(
658 Spell::Revelio,
659 Box::new(Some(Atom::String("Hello, world!".to_string()).into())),
660 )),
661 ]);
662 let (_, actual) = parse_program(input).unwrap();
663 assert_eq!(expected, actual);
664 }
665
666 #[test]
667 fn test_parse_program_serialized() {
668 let code = r#"index = 0
669
670 quidditch {
671 snake = ~Serpensortia
672 ~WingardiumLeviosa snake
673 ~WingardiumLeviosa snake
674 snake = snake + " some string"
675 ~Revelio snake
676 ~Incendio snake
677 ~Revelio snake
678 ~Engorgio index
679
680 if index == 4 {
681 snitch # Break loop
682 }
683 }
684 "#;
685 let result = parse_program(code).unwrap();
686 let json = serde_json::to_string(&result).unwrap();
687
688 println!("{}", json);
689
690 assert_eq!(
691 json,
692 r#"["",[{"VariableAssignment":["index",{"Atom":{"Integer":0}}]},{"Quidditch":[{"VariableAssignment":["snake",{"SpellCast":["Serpensortia",null]}]},{"ExpressionStatement":{"SpellCast":["WingardiumLeviosa",{"Atom":{"Variable":"snake"}}]}},{"ExpressionStatement":{"SpellCast":["WingardiumLeviosa",{"Atom":{"Variable":"snake"}}]}},{"VariableAssignment":["snake",{"BinaryOperation":["Plus",{"Atom":{"Variable":"snake"}},{"Atom":{"String":" some string"}}]}]},{"ExpressionStatement":{"SpellCast":["Revelio",{"Atom":{"Variable":"snake"}}]}},{"ExpressionStatement":{"SpellCast":["Incendio",{"Atom":{"Variable":"snake"}}]}},{"ExpressionStatement":{"SpellCast":["Revelio",{"Atom":{"Variable":"snake"}}]}},{"ExpressionStatement":{"SpellCast":["Engorgio",{"Atom":{"Variable":"index"}}]}},{"If":[{"BinaryOperation":["Equal",{"Atom":{"Variable":"index"}},{"Atom":{"Integer":4}}]},["Snitch",{"ExpressionStatement":{"Comment":" Break loop"}}],[]]}]}]]"#
693 );
694 }
695}