foo/
foo.rs

1use std::{collections::HashMap, fmt::Display};
2
3use cypress::prelude::*;
4
5/// foo is a play language with assignment and printing.
6/// There are two types of expressions `print <expr>` and `<ident> = <expr>`.
7/// Each expression returns a value, `print` returns the value that is printed
8/// and assignment returns the value that was assigned. Thus a valid sentence may
9/// be `print x = y = 4` how this would expected is: first y gets 4, the x gets 4
10/// then the program finishes with printing 4. Lines are delimitted by a newline
11/// and executed linearly in sequence.
12///
13/// This language cannot do much but it can help give a hint of how to write a
14/// parser using cypress.
15
16#[derive(PartialEq, Clone, Debug, Eq, Hash)]
17enum Expr {
18    Str(String),
19    Num(i32),
20    Var(String),
21    Assignment(String, Box<Expr>),
22    Print(Box<Expr>),
23    Seq(Vec<Expr>),
24}
25
26impl Display for Expr {
27    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
28        match self {
29            Expr::Str(str) => write!(f, "\"{}\"", str),
30            Expr::Num(num) => write!(f, "{}", num),
31            Expr::Var(var) => write!(f, "{}", var),
32            Expr::Assignment(name, expr) => write!(f, "{} = {}", name, expr),
33            Expr::Print(expr) => write!(f, "{}", expr),
34            Expr::Seq(exprs) => write!(
35                f,
36                "{}\n",
37                exprs
38                    .iter()
39                    .map(|expr| format!("{}", expr))
40                    .collect::<String>()
41            ),
42        }
43    }
44}
45
46fn num<'a>() -> impl Parser<'a, u8, i32> {
47    pnum()
48        .many()
49        .map(|xs| String::from_utf8(xs).unwrap().parse::<i32>().unwrap())
50}
51
52fn str_<'a>() -> impl Parser<'a, u8, String> {
53    pbetween(just('\"'), pletter().many(), just('\"')).map(|val| String::from_utf8(val).unwrap())
54}
55
56fn ident<'a>() -> impl Parser<'a, u8, String> {
57    let start = choice!(pletter(), just('_'));
58    let valid_idents = choice!(pnum(), just('_'), pletter());
59
60    let ident = start.then(valid_idents.many()).map(|(s, mut rest)| {
61        let mut all = vec![s];
62        all.append(&mut rest);
63        all
64    });
65
66    ident.map(|val| String::from_utf8(val).unwrap())
67}
68
69/// Parsing a variable assignment, note the use of `sequence!`
70/// a macro to support applying multiple parsers in sequence. See `print`
71/// below for an example of doing a similar parse without this macro.
72fn var_assignment<'a>(expr: impl Parser<'a, u8, Expr>) -> impl Parser<'a, u8, Expr> {
73    sequence!(
74        (ident()) > (just('=').padded_by(pws())) > expr
75            => |(name, (_, val))| Expr::Assignment(name, Box::new(val))
76    )
77}
78
79/// An example of applying two parsers in sequence without `sequence!` macro.
80fn print<'a>(expr: impl Parser<'a, u8, Expr>) -> impl Parser<'a, u8, Expr> {
81    let print_ident = pident("print");
82
83    print_ident
84        .padded_by(pws())
85        .then(expr)
86        .map(|(_, val)| Expr::Print(Box::new(val)))
87}
88
89fn expr<'a>() -> impl Parser<'a, u8, Expr> {
90    recursive(|expr| {
91        Box::new(
92            choice!(
93                var_assignment(expr.clone()),
94                print(expr),
95                ident().map(Expr::Var),
96                str_().map(Expr::Str),
97                num().map(Expr::Num)
98            )
99            .map(|expr| expr),
100        )
101    })
102}
103
104fn parser<'a>() -> impl Parser<'a, u8, Expr> {
105    (expr().delimited_by(just('\n'))).until_end().map(Expr::Seq)
106}
107
108fn eval<'a>(expr: Expr, var_map: &mut HashMap<String, Expr>) -> Result<Expr, String> {
109    match expr {
110        Expr::Str(_) | Expr::Num(_) => Ok(expr),
111        Expr::Var(name) => match var_map.get(&name) {
112            Some(val) => eval(val.clone(), var_map),
113            None => Err(format!("No value associated with key: \"{}\"", name)),
114        },
115        Expr::Assignment(name, expr) => {
116            let eval_expr = eval(*expr, var_map)?;
117            var_map.insert(name, eval_expr.clone());
118            Ok(eval_expr)
119        }
120        Expr::Print(expr) => {
121            let eval_expr = eval(*expr, var_map)?;
122            println!("{}", eval_expr);
123            Ok(eval_expr)
124        }
125        Expr::Seq(exprs) => {
126            let mut res = vec![];
127            for expr in exprs {
128                let expr_res = eval(expr.clone(), var_map);
129                match expr_res {
130                    Ok(eval_expr) => res.push(eval_expr),
131                    Err(err) => return Err(err),
132                }
133            }
134
135            Ok(Expr::Seq(res))
136        }
137    }
138}
139
140fn main() {
141    let input = b"y = print z = 4
142x = print y
143y = 10
144print y
145print x
146print z";
147
148    match parser().parse(input.into_input()) {
149        Ok(PSuccess { val, rest: _ }) => {
150            print!("{:?}", val);
151            let res = eval(val, &mut HashMap::new());
152            match res {
153                Ok(ret) => println!("{:?}", ret),
154                Err(err) => println!("Evaluation Error: {}", err),
155            }
156        }
157        Err(e) => {
158            println!("{}", e)
159        }
160    }
161}