1use crate::error::Error;
19use crate::lexer::{Token, TokenKind};
20use crate::syntax::{Expr, VarName};
21
22enum Peek<'a> {
24 Eof,
25 Tok(&'a Token),
26}
27
28fn peek(tokens: &[Token], pos: usize) -> Peek<'_> {
29 tokens.get(pos).map_or(Peek::Eof, Peek::Tok)
30}
31
32pub fn parse(tokens: &[Token]) -> Result<Expr, Error> {
57 let (expr, end_pos) = parse_expr(tokens, 0)?;
58 tokens.get(end_pos).map_or(Ok(expr), |tok| {
59 Err(Error::UnexpectedToken {
60 at: tok.at(),
61 expected: "end of input",
62 found: format!("{}", tok.kind()),
63 })
64 })
65}
66
67fn parse_expr(tokens: &[Token], pos: usize) -> Result<(Expr, usize), Error> {
68 match peek(tokens, pos) {
69 Peek::Eof => Err(Error::UnexpectedEnd {
70 expected: "expression",
71 }),
72 Peek::Tok(tok) => dispatch_expr(tokens, pos, tok),
73 }
74}
75
76fn dispatch_expr(tokens: &[Token], pos: usize, tok: &Token) -> Result<(Expr, usize), Error> {
77 match tok.kind() {
78 TokenKind::Lambda => parse_lambda(tokens, pos),
79 TokenKind::KwLet => parse_let(tokens, pos),
80 TokenKind::KwFix => parse_fix(tokens, pos),
81 TokenKind::Ident(_) | TokenKind::LParen => parse_app(tokens, pos),
82 TokenKind::KwIn | TokenKind::Dot | TokenKind::Equals | TokenKind::RParen => {
83 Err(Error::UnexpectedToken {
84 at: tok.at(),
85 expected: "expression",
86 found: format!("{}", tok.kind()),
87 })
88 }
89 }
90}
91
92fn parse_lambda(tokens: &[Token], pos: usize) -> Result<(Expr, usize), Error> {
93 let after_lambda = expect_kind(tokens, pos, &TokenKind::Lambda, "`\\`")?;
94 let (param, after_ident) = expect_ident(tokens, after_lambda)?;
95 let after_dot = expect_kind(tokens, after_ident, &TokenKind::Dot, "`.`")?;
96 let (body, after_body) = parse_expr(tokens, after_dot)?;
97 Ok((Expr::lam(param, body), after_body))
98}
99
100fn parse_let(tokens: &[Token], pos: usize) -> Result<(Expr, usize), Error> {
101 let after_let = expect_kind(tokens, pos, &TokenKind::KwLet, "keyword `let`")?;
102 let (name, after_name) = expect_ident(tokens, after_let)?;
103 let after_eq = expect_kind(tokens, after_name, &TokenKind::Equals, "`=`")?;
104 let (value, after_value) = parse_expr(tokens, after_eq)?;
105 let after_in = expect_kind(tokens, after_value, &TokenKind::KwIn, "keyword `in`")?;
106 let (body, after_body) = parse_expr(tokens, after_in)?;
107 Ok((Expr::bind(name, value, body), after_body))
108}
109
110fn parse_fix(tokens: &[Token], pos: usize) -> Result<(Expr, usize), Error> {
111 let after_fix = expect_kind(tokens, pos, &TokenKind::KwFix, "keyword `fix`")?;
112 let (name, after_name) = expect_ident(tokens, after_fix)?;
113 let after_dot = expect_kind(tokens, after_name, &TokenKind::Dot, "`.`")?;
114 let (body, after_body) = parse_expr(tokens, after_dot)?;
115 Ok((Expr::fix(name, body), after_body))
116}
117
118fn parse_app(tokens: &[Token], pos: usize) -> Result<(Expr, usize), Error> {
119 let (head, after_head) = parse_atom(tokens, pos)?;
120 parse_app_tail(tokens, after_head, head)
121}
122
123fn parse_app_tail(tokens: &[Token], pos: usize, lhs: Expr) -> Result<(Expr, usize), Error> {
124 let can_apply = starts_atom(tokens, pos);
125 if can_apply {
126 let (arg, after_arg) = parse_atom(tokens, pos)?;
127 parse_app_tail(tokens, after_arg, Expr::app(lhs, arg))
128 } else {
129 Ok((lhs, pos))
130 }
131}
132
133fn starts_atom(tokens: &[Token], pos: usize) -> bool {
134 match peek(tokens, pos) {
135 Peek::Eof => false,
136 Peek::Tok(tok) => matches!(tok.kind(), TokenKind::Ident(_) | TokenKind::LParen),
137 }
138}
139
140fn parse_atom(tokens: &[Token], pos: usize) -> Result<(Expr, usize), Error> {
141 match peek(tokens, pos) {
142 Peek::Eof => Err(Error::UnexpectedEnd {
143 expected: "atom (identifier or `(`)",
144 }),
145 Peek::Tok(tok) => dispatch_atom(tokens, pos, tok),
146 }
147}
148
149fn dispatch_atom(tokens: &[Token], pos: usize, tok: &Token) -> Result<(Expr, usize), Error> {
150 match tok.kind() {
151 TokenKind::Ident(name) => Ok((Expr::Var(name.clone()), pos + 1)),
152 TokenKind::LParen => parse_paren_inner(tokens, pos + 1),
153 TokenKind::Lambda
154 | TokenKind::KwLet
155 | TokenKind::KwFix
156 | TokenKind::KwIn
157 | TokenKind::Dot
158 | TokenKind::Equals
159 | TokenKind::RParen => Err(Error::UnexpectedToken {
160 at: tok.at(),
161 expected: "atom (identifier or `(`)",
162 found: format!("{}", tok.kind()),
163 }),
164 }
165}
166
167fn parse_paren_inner(tokens: &[Token], pos: usize) -> Result<(Expr, usize), Error> {
168 let (inner, after_inner) = parse_expr(tokens, pos)?;
169 let after_close = expect_kind(tokens, after_inner, &TokenKind::RParen, "`)`")?;
170 Ok((inner, after_close))
171}
172
173fn expect_ident(tokens: &[Token], pos: usize) -> Result<(VarName, usize), Error> {
174 match peek(tokens, pos) {
175 Peek::Eof => Err(Error::UnexpectedEnd {
176 expected: "identifier",
177 }),
178 Peek::Tok(tok) => dispatch_expect_ident(tok, pos),
179 }
180}
181
182fn dispatch_expect_ident(tok: &Token, pos: usize) -> Result<(VarName, usize), Error> {
183 match tok.kind() {
184 TokenKind::Ident(name) => Ok((name.clone(), pos + 1)),
185 TokenKind::Lambda
186 | TokenKind::KwLet
187 | TokenKind::KwIn
188 | TokenKind::KwFix
189 | TokenKind::Dot
190 | TokenKind::Equals
191 | TokenKind::LParen
192 | TokenKind::RParen => Err(Error::UnexpectedToken {
193 at: tok.at(),
194 expected: "identifier",
195 found: format!("{}", tok.kind()),
196 }),
197 }
198}
199
200fn expect_kind(
201 tokens: &[Token],
202 pos: usize,
203 expected: &TokenKind,
204 name: &'static str,
205) -> Result<usize, Error> {
206 match peek(tokens, pos) {
207 Peek::Eof => Err(Error::UnexpectedEnd { expected: name }),
208 Peek::Tok(tok) => {
209 let matches = tok.kind() == expected;
210 if matches {
211 Ok(pos + 1)
212 } else {
213 Err(Error::UnexpectedToken {
214 at: tok.at(),
215 expected: name,
216 found: format!("{}", tok.kind()),
217 })
218 }
219 }
220 }
221}
222
223#[cfg(test)]
224mod tests {
225 use super::*;
226 use crate::lexer::lex;
227
228 fn parse_str(src: &str) -> Result<Expr, Error> {
229 let tokens = lex(src)?;
230 parse(&tokens)
231 }
232
233 #[test]
234 fn identity_function() -> Result<(), Error> {
235 let e = parse_str("\\x. x")?;
236 let expected = Expr::lam("x", Expr::var("x"));
237 (e == expected).then_some(()).ok_or(Error::UnexpectedEnd {
238 expected: "identity AST",
239 })
240 }
241
242 #[test]
243 fn left_associative_application() -> Result<(), Error> {
244 let e = parse_str("f x y")?;
245 let expected = Expr::app(Expr::app(Expr::var("f"), Expr::var("x")), Expr::var("y"));
246 (e == expected).then_some(()).ok_or(Error::UnexpectedEnd {
247 expected: "left-assoc AST",
248 })
249 }
250
251 #[test]
252 fn let_with_body() -> Result<(), Error> {
253 let e = parse_str("let id = \\x. x in id id")?;
254 let expected = Expr::bind(
255 "id",
256 Expr::lam("x", Expr::var("x")),
257 Expr::app(Expr::var("id"), Expr::var("id")),
258 );
259 (e == expected).then_some(()).ok_or(Error::UnexpectedEnd {
260 expected: "let AST",
261 })
262 }
263}