mini_lang/
parser.rs

1use crate::{MiniError, MiniResult};
2
3pub type Ast = Vec<Stmt>;
4
5#[derive(Clone, Debug, PartialEq, Eq)]
6pub enum Stmt {
7    Binding(String, Expr),
8    Print(Expr),
9    Define(String, Vec<String>, Expr),
10}
11
12#[derive(Clone, Debug, PartialEq, Eq)]
13pub enum Expr {
14    Value(i32),
15    Variable(String),
16    Operation(Operator, Box<Expr>, Box<Expr>),
17    FuncCall(String, Vec<Expr>),
18    If(Box<Expr>, Box<Expr>, Box<Expr>),
19}
20
21/// The operator enum for expressions.
22#[derive(Copy, Clone, Debug, PartialEq, Eq)]
23pub enum Operator {
24    /// `+`
25    Add,
26    /// `-`
27    Sub,
28    /// `*`
29    Mul,
30    /// `/`
31    Div,
32    /// `%`
33    Rem,
34    /// `>`
35    Gt,
36    /// `>=`, or `=>`
37    Ge,
38    /// `<`
39    Lt,
40    /// `<=`, or `=<`
41    Le,
42    /// `==`
43    Eq,
44    /// `!=`
45    Neq,
46}
47
48pub fn parse<S: AsRef<str>>(input: S) -> MiniResult<Ast> {
49    parser::program(input.as_ref()).map_err(MiniError::Parse)
50}
51
52peg::parser! { grammar parser() for str {
53    rule _ = ("\t"/" "/"\\\n"/"\\\r")*
54    rule __ = (_ ("\n"/"\r"))*
55    rule space() = ("\t"/" "/"\\\n"/"\\\r")+
56
57    pub rule program() -> Ast
58        = stmt()*
59
60    rule stmt() -> Stmt
61        = print()
62        / binding()
63        / define()
64
65    rule print() -> Stmt
66        = _ "print" space() e:expr() __ { Stmt::Print(e) }
67
68    rule binding() -> Stmt
69        = _ "let" space() v:ident() _ "=" _ e:expr() __ { Stmt::Binding(v, e) }
70
71    rule define() -> Stmt
72        = _ "def" space() n:ident() _
73          "(" a:((_ a:ident() _ { a }) ** (",")) ","? _ ")" _
74          "=" _ e:expr() __ { Stmt::Define(n, a, e) }
75
76    rule expr() -> Expr = eq()
77
78    rule eq() -> Expr
79        = l:comp() rs:( _ op:$(("=="/"!=")) _ r:comp() { (op, r) })*
80        {
81            rs.into_iter().fold(l, |l, (op, r)| Expr::Operation(
82                match op {
83                    "==" => Operator::Eq,
84                    "!=" => Operator::Neq,
85                    _ => unreachable!(),
86                },
87                Box::new(l),
88                Box::new(r),
89            ))
90        }
91
92    rule comp() -> Expr
93        = l:add() rs:( _ op:$(("=>"/"=<"/">="/"<="/">"/"<")) _ r:add() { (op, r) })*
94        {
95            rs.into_iter().fold(l, |l, (op, r)| Expr::Operation(
96                match op {
97                    ">" => Operator::Gt,
98                    "=>" | ">=" => Operator::Ge,
99                    "<" => Operator::Lt,
100                    "=<" | "<=" => Operator::Le,
101                    _ => unreachable!(),
102                },
103                Box::new(l),
104                Box::new(r),
105            ))
106        }
107
108    rule add() -> Expr
109        = l:mul() rs:( _ op:$(("+"/"-")) _ r:mul() { (op, r) })*
110        {
111            rs.into_iter().fold(l, |l, (op, r)| Expr::Operation(
112                match op {
113                    "+" => Operator::Add,
114                    "-" => Operator::Sub,
115                    _ => unreachable!(),
116                },
117                Box::new(l),
118                Box::new(r),
119            ))
120        }
121
122    rule mul() -> Expr
123        = l:atom() rs:( _ op:$(("*"/"/"/"%")) _ r:atom() { (op, r) })*
124        {
125            rs.into_iter().fold(l, |l, (op, r)| Expr::Operation(
126                match op {
127                    "*" => Operator::Mul,
128                    "/" => Operator::Div,
129                    "%" => Operator::Rem,
130                    _ => unreachable!(),
131                },
132                Box::new(l),
133                Box::new(r),
134            ))
135        }
136
137    rule atom() -> Expr
138        = n:number() { Expr::Value(n) }
139        / "(" _ e:expr() _ ")" { e }
140        / funccall()
141        / if_expr()
142        / v:ident() { Expr::Variable(v) }
143
144    rule funccall() -> Expr
145        = n:ident() _ "(" e:((_ e:expr() _ { e }) ** (",")) ","? _ ")" {
146            Expr::FuncCall(n, e)
147        }
148
149    rule if_expr() -> Expr
150        = "if" space() c:expr() space()
151          "then" space() t:expr() space()
152          "else" space() f:expr() { Expr::If(Box::new(c), Box::new(t), Box::new(f)) }
153
154    rule ident() -> String
155        = s:$(['a'..='z' | '_']*) { String::from(s) }
156
157    rule number() -> i32
158        = "-" _ n:unsigned() { -n }
159        / ("+"/"") _ n:unsigned() { n }
160
161    rule unsigned() -> i32
162        = n:$(['0'..='9']+) {? n.parse().or(Err("Integer Parsing Error"))}
163
164}}