whenever_parser/
parser.rs

1use crate::lexer;
2use crate::ast;
3
4use crate::lexer::TokenVariant;
5
6// Nomenclature:
7// tokenX: the Xth token read, 0 based
8// cursorX: the place in the input where we will get tokenX and cursorX+1 when
9//          eating
10
11pub fn eat_absnumber<'a>(input: &'a str)
12    -> Result<(ast::AbsNumber<'a>, &'a str), (String, &'a str)>
13{
14    let cursor0 = input;
15    let (token0, cursor1) = lexer::eat(cursor0)?;
16    match token0.variant
17    {
18        TokenVariant::Number(val) =>
19        {
20            let numbertok = ast::NumberToken { tok: token0.tok, val };
21            Ok((ast::AbsNumber { alt: Box::new(numbertok) }, cursor1))
22        }
23        TokenVariant::N =>
24        {
25            let keywordtok = ast::NToken { tok: token0.tok };
26            let (token1, cursor2) = lexer::eat(cursor1)?;
27            if let TokenVariant::LeftParens = token1.variant {} else
28            {
29                return Err((String::from("Expected `(`"), token1.tok));
30            }
31            let lparenstok = ast::LeftParensToken { tok: token1.tok };
32            let (number, cursor3) = eat_number(cursor2)?;
33            let (token3, cursor4) = lexer::eat(cursor3)?;
34            if let TokenVariant::RightParens = token3.variant {} else
35            {
36                return Err((String::from("Expected `)`"), token3.tok));
37            }
38            let rparenstok = ast::RightParensToken { tok: token3.tok };
39            let n = ast::N::new(keywordtok, lparenstok, number, rparenstok);
40            Ok((ast::AbsNumber { alt: Box::new(n) }, cursor4))
41        }
42        TokenVariant::Read =>
43        {
44            let keywordtok = ast::ReadToken { tok: token0.tok };
45            let (token1, cursor2) = lexer::eat(cursor1)?;
46            if let TokenVariant::LeftParens = token1.variant {} else
47            {
48                return Err((String::from("Expected `(`"), token1.tok));
49            }
50            let lparenstok = ast::LeftParensToken { tok: token1.tok };
51            let (token2, cursor3) = lexer::eat(cursor2)?;
52            if let TokenVariant::RightParens = token2.variant {} else
53            {
54                return Err((String::from("Expected `)`"), token2.tok));
55            }
56            let rparenstok = ast::RightParensToken { tok: token2.tok };
57            let read = ast::Read::new(keywordtok, lparenstok, rparenstok);
58            Ok((ast::AbsNumber { alt: Box::new(read) }, cursor3))
59        }
60        _ => Err((String::from("Expected number"), token0.tok))
61    }
62}
63
64pub fn eat_number<'a>(input: &'a str)
65    -> Result<(ast::Number<'a>, &'a str), (String, &'a str)>
66{
67    let cursor0 = input;
68    let (token0, cursor1) = lexer::eat(cursor0)?;
69    let cursorlast; // cursor at the end of the current number
70    let number1 : ast::Number = match token0.variant
71    {
72        TokenVariant::Number(_) | TokenVariant::N | TokenVariant::Read =>
73        {
74            let (absnum, cursor1) = eat_absnumber(cursor0)?;
75            let absolutenumber = ast::AbsoluteNumber::new(absnum);
76            cursorlast = cursor1;
77            ast::Number { alt: Box::new(absolutenumber) }
78        }
79        TokenVariant::Plus | TokenVariant::Minus =>
80        {
81            let unmathop = ast::UnMathOp { alt: match token0.variant
82                {
83                    TokenVariant::Plus =>
84                        Box::new(ast::PlusToken { tok: token0.tok }),
85                    TokenVariant::Minus =>
86                        Box::new(ast::MinusToken { tok: token0.tok }),
87                    _ => unreachable!()
88                }
89            };
90            let (number, cursor2) = eat_number(cursor1)?;
91            let unopnum = ast::UnOpNumber::new(unmathop, number);
92            cursorlast = cursor2;
93            ast::Number { alt: Box::new(unopnum) }
94        }
95        TokenVariant::LeftParens =>
96        {
97            let lparenstok = ast::LeftParensToken { tok: token0.tok };
98            let (number, cursor2) = eat_number(cursor1)?;
99            let (token2, cursor3) = lexer::eat(cursor2)?;
100            if let TokenVariant::RightParens = token2.variant {} else
101            {
102                return Err((String::from("Expected `)`"), token2.tok));
103            }
104            let rparenstok = ast::RightParensToken { tok: token2.tok };
105            let parensnum = ast::ParensNumber::new(lparenstok,
106                                                   number,
107                                                   rparenstok);
108            cursorlast = cursor3;
109            ast::Number { alt: Box::new(parensnum) }
110        }
111        TokenVariant::String | TokenVariant::U =>
112        {
113            let (string, cursor1) = eat_string(cursor0)?;
114            let stringtonum = ast::StringToNum { string };
115            cursorlast = cursor1;
116            ast::Number { alt: Box::new(stringtonum) }
117        }
118        _ => { return Err((String::from("Expected number"), token0.tok)); }
119    };
120
121    let (tokenlast, cursorlastplus1) = lexer::eat(cursorlast)?;
122    match tokenlast.variant
123    {
124        TokenVariant::Plus | TokenVariant::Minus | TokenVariant::MathOp =>
125        {
126            let binmathop = ast::BinMathOp { alt : match tokenlast.variant
127                {
128                    TokenVariant::Plus =>
129                        Box::new(ast::PlusToken { tok: tokenlast.tok }),
130                    TokenVariant::Minus =>
131                        Box::new(ast::MinusToken { tok: tokenlast.tok }),
132                    TokenVariant::MathOp =>
133                        Box::new(ast::MathOpToken { tok: tokenlast.tok }),
134                    _ => unreachable!()
135                }
136            };
137            let (number2, cursorlastplus2) = eat_number(cursorlastplus1)?;
138            let binopnum = ast::BinOpNumber::new(number1, binmathop, number2);
139            Ok((ast::Number { alt: Box::new(binopnum) }, cursorlastplus2))
140        }
141        _ => Ok((number1, cursorlast))
142    }
143}
144
145pub fn eat_boolean<'a>(input: &'a str)
146    -> Result<(ast::Boolean<'a>, &'a str), (String, &'a str)>
147{
148    let cursor0 = input;
149    let (token0, cursor1) = lexer::eat(cursor0)?;
150    let cursorlast; // cursor at the end of the current boolean
151    let boolean1 : ast::Boolean = match token0.variant
152    {
153        TokenVariant::UnBoolOp =>
154        {
155            let unbooloptok = ast::UnBoolOpToken { tok: token0.tok };
156            let (boolean, cursor2) = eat_boolean(cursor1)?;
157            let unopboolean = ast::UnOpBoolean::new(unbooloptok, boolean);
158            cursorlast = cursor2;
159            ast::Boolean { alt: Box::new(unopboolean) }
160        }
161        TokenVariant::LeftParens =>
162        {
163            let lparenstok = ast::LeftParensToken { tok: token0.tok };
164            let (boolean, cursor2) = eat_boolean(cursor1)?;
165            let (token2, cursor3) = lexer::eat(cursor2)?;
166            if let TokenVariant::RightParens = token2.variant {} else
167            {
168                return Err((String::from("Expected `)`"), token2.tok));
169            }
170            let rparenstok = ast::RightParensToken { tok: token2.tok };
171            let parensbool = ast::ParensBoolean::new(lparenstok,
172                                                     boolean,
173                                                     rparenstok);
174            cursorlast = cursor3;
175            ast::Boolean { alt: Box::new(parensbool) }
176        }
177        TokenVariant::Number(_) | TokenVariant::N | TokenVariant::Read
178            | TokenVariant::Plus | TokenVariant::Minus
179            | TokenVariant::String | TokenVariant::U =>
180        {
181            let (number1, cursor1) = eat_number(cursor0)?;
182            let (token1, cursor2) = lexer::eat(cursor1)?;
183            match token1.variant
184            {
185                TokenVariant::BinNumBoolOp =>
186                {
187                    // number BINNUMBOOLOP number
188                    // reduce => binopnumbool
189                    let binnumbooloptok = ast::BinNumBoolOpToken {
190                        tok: token1.tok };
191                    let (number2, cursor3) = eat_number(cursor2)?;
192                    let binopnumboolean =
193                        ast::BinOpNumBoolean::new(number1,
194                                                  binnumbooloptok,
195                                                  number2);
196                    cursorlast = cursor3;
197                    ast::Boolean { alt: Box::new(binopnumboolean) }
198                }
199                _ =>
200                {
201                    // number
202                    // reduce => numtobool
203                    let numtobool = ast::NumToBool { num: number1 };
204                    cursorlast = cursor1;
205                    ast::Boolean { alt: Box::new(numtobool) }
206                }
207            }
208        }
209        _ => { return Err((String::from("Expected boolean"), token0.tok)); }
210    };
211
212    let (tokenlast, cursorlastplus1) = lexer::eat(cursorlast)?;
213    match tokenlast.variant
214    {
215        TokenVariant::BinBoolOp =>
216        {
217            let binbooloptok = ast::BinBoolOpToken { tok: tokenlast.tok };
218            let (boolean2, cursorlastplus2) = eat_boolean(cursorlastplus1)?;
219            let binopboolean = ast::BinOpBoolean::new(boolean1,
220                                                      binbooloptok,
221                                                      boolean2);
222            Ok((ast::Boolean { alt: Box::new(binopboolean) }, cursorlastplus2))
223        }
224        _ => Ok((boolean1, cursorlast))
225    }
226}
227
228pub fn eat_string<'a>(input: &'a str)
229    -> Result<(ast::String_<'a>, &'a str), (String, &'a str)>
230{
231    let cursor0 = input;
232    let (token0, cursor1) = lexer::eat(cursor0)?;
233    let cursorlast; // cursor at the end of the current string
234    let string1 : ast::String_<'a> = match token0.variant
235    {
236        TokenVariant::String =>
237        {
238            let stringtoken = ast::StringToken { tok: token0.tok };
239            cursorlast = cursor1;
240            ast::String_ { alt: Box::new(stringtoken) }
241        }
242        TokenVariant::U =>
243        {
244            let keywordtok = ast::UToken { tok: token0.tok };
245            let (token1, cursor2) = lexer::eat(cursor1)?;
246            if let TokenVariant::LeftParens = token1.variant {} else
247            {
248                return Err((String::from("Expected `(`"), token1.tok));
249            }
250            let lparenstok = ast::LeftParensToken { tok: token1.tok };
251            let (number, cursor3) = eat_absnumber(cursor2)?;
252            let (token3, cursor4) = lexer::eat(cursor3)?;
253            if let TokenVariant::RightParens = token3.variant {} else
254            {
255                return Err((String::from("Expected `)`"), token3.tok));
256            }
257            let rparenstok = ast::RightParensToken { tok: token3.tok };
258            let u = ast::U::new(keywordtok, lparenstok, number, rparenstok);
259            cursorlast = cursor4;
260            ast::String_ { alt: Box::new(u) }
261        }
262        TokenVariant::Number(_) | TokenVariant::N | TokenVariant::Read
263            | TokenVariant::Plus | TokenVariant::Minus
264            | TokenVariant::LeftParens =>
265        {
266            let (number, cursor1) = eat_number(cursor0)?;
267            let numtostring = ast::NumToString { num: number };
268            cursorlast = cursor1;
269            ast::String_ { alt: Box::new(numtostring) }
270        }
271        _ => { return Err((String::from("Expected string"), token0.tok)); }
272    };
273
274    let (tokenlast, cursorlastplus1) = lexer::eat(cursorlast)?;
275    match tokenlast.variant
276    {
277        TokenVariant::Plus =>
278        {
279            let plustoken = ast::PlusToken { tok: tokenlast.tok };
280            let (string2, cursorlastplus2) = eat_string(cursorlastplus1)?;
281            let concat = ast::Concat::new(string1, plustoken, string2);
282            Ok((ast::String_ { alt: Box::new(concat) }, cursorlastplus2))
283        }
284        _ => Ok((string1, cursorlast))
285    }
286}
287
288pub fn eat_lineops<'a>(input: &'a str)
289    -> Result<(ast::LineOps<'a>, &'a str), (String, &'a str)>
290{
291    let cursor0 = input;
292    let (number, cursor1) = eat_number(cursor0)?;
293    let (token1, cursor2) = lexer::eat(cursor1)?;
294    match token1.variant
295    {
296        TokenVariant::Comma =>
297        {
298            // number COMMA lineops
299            // reduce => numtolineop COMMA lineops
300            // reduce => lineop COMMA lineops
301            // reduce => lineoplist
302            let numtolineop = ast::NumToLineOp { num: number };
303            let lineop = ast::LineOp::new(ast::SingleLineOp {
304                alt: Box::new(numtolineop)
305            });
306            let commatok = ast::CommaToken { tok: token1.tok };
307            let (lineops, cursor3) = eat_lineops(cursor2)?;
308            let lineoplist = ast::LineOpList::new(lineop, commatok, lineops);
309            Ok((ast::LineOps { alt: Box::new(lineoplist) }, cursor3))
310        }
311        TokenVariant::Sharp =>
312        {
313            // number SHARP number
314            // reduce => countlineop
315            // reduce => lineop
316            let sharptok = ast::SharpToken { tok: token1.tok };
317            let (count, cursor3) = eat_number(cursor2)?;
318            let countlineop = ast::CountLineOp::new(number, sharptok, count);
319            let lineop = ast::LineOp::new(ast::SingleLineOp {
320                alt: Box::new(countlineop) });
321
322            let (token3, cursor4) = lexer::eat(cursor3)?;
323            match token3.variant
324            {
325                TokenVariant::Comma =>
326                {
327                    // lineop COMMA lineops
328                    // reduce => lineoplist
329                    let commatok = ast::CommaToken { tok: token3.tok };
330                    let (lineops, cursor5) = eat_lineops(cursor4)?;
331                    let lineoplist = ast::LineOpList::new(lineop,
332                                                          commatok,
333                                                          lineops);
334                    Ok((ast::LineOps { alt: Box::new(lineoplist) }, cursor5))
335                }
336                _ => Ok((ast::LineOps { alt: Box::new(lineop) }, cursor3))
337            }
338        }
339        _ =>
340        {
341            // number
342            // reduce => numtolineop
343            // reduce => lineop
344            let numtolineop = ast::NumToLineOp { num: number };
345            let lineop = ast::LineOp::new(ast::SingleLineOp {
346                alt: Box::new(numtolineop) });
347            Ok((ast::LineOps { alt: Box::new(lineop) }, cursor1))
348        }
349    }
350}
351
352pub fn eat_statement<'a>(input: &'a str)
353    -> Result<(ast::Statement<'a>, &'a str), (String, &'a str)>
354{
355    let cursor0 = input;
356    let (token0, cursor1) = lexer::eat(cursor0)?;
357    match token0.variant
358    {
359        TokenVariant::Again =>
360        {
361            let keywordtok = ast::AgainToken { tok: token0.tok };
362            let (token1, cursor2) = lexer::eat(cursor1)?;
363            if let TokenVariant::LeftParens = token1.variant {} else
364            {
365                return Err((String::from("Expected `(`"), token1.tok));
366            }
367            let lparenstok = ast::LeftParensToken { tok: token1.tok };
368            let (boolean, cursor3) = eat_boolean(cursor2)?;
369            let (token3, cursor4) = lexer::eat(cursor3)?;
370            if let TokenVariant::RightParens = token3.variant {} else
371            {
372                return Err((String::from("Expected `)`"), token3.tok));
373            }
374            let rparenstok = ast::RightParensToken { tok: token3.tok };
375            let (statement, cursor5) = eat_statement(cursor4)?;
376            let again = ast::Again::new(keywordtok,
377                                        lparenstok,
378                                        boolean,
379                                        rparenstok,
380                                        statement);
381            Ok((ast::Statement { alt: Box::new(again) }, cursor5))
382        }
383        TokenVariant::Defer =>
384        {
385            let keywordtok = ast::DeferToken { tok: token0.tok };
386            let (token1, cursor2) = lexer::eat(cursor1)?;
387            if let TokenVariant::LeftParens = token1.variant {} else
388            {
389                return Err((String::from("Expected `(`"), token1.tok));
390            }
391            let lparenstok = ast::LeftParensToken { tok: token1.tok };
392            let (boolean, cursor3) = eat_boolean(cursor2)?;
393            let (token3, cursor4) = lexer::eat(cursor3)?;
394            if let TokenVariant::RightParens = token3.variant {} else
395            {
396                return Err((String::from("Expected `)`"), token3.tok));
397            }
398            let rparenstok = ast::RightParensToken { tok: token3.tok };
399            let (statement, cursor5) = eat_statement(cursor4)?;
400            let defer = ast::Defer::new(keywordtok,
401                                        lparenstok,
402                                        boolean,
403                                        rparenstok,
404                                        statement);
405            Ok((ast::Statement { alt: Box::new(defer) }, cursor5))
406        }
407        TokenVariant::Forget =>
408        {
409            let keywordtok = ast::ForgetToken { tok: token0.tok };
410            let (token1, cursor2) = lexer::eat(cursor1)?;
411            if let TokenVariant::LeftParens = token1.variant {} else
412            {
413                return Err((String::from("Expected `(`"), token1.tok));
414            }
415            let lparenstok = ast::LeftParensToken { tok: token1.tok };
416            let (boolean, cursor3) = eat_boolean(cursor2)?;
417            let (token3, cursor4) = lexer::eat(cursor3)?;
418            if let TokenVariant::RightParens = token3.variant {} else
419            {
420                return Err((String::from("Expected `)`"), token3.tok));
421            }
422            let rparenstok = ast::RightParensToken { tok: token3.tok };
423            let (statement, cursor5) = eat_statement(cursor4)?;
424            let forget = ast::Forget::new(keywordtok,
425                                          lparenstok,
426                                          boolean,
427                                          rparenstok,
428                                          statement);
429            Ok((ast::Statement { alt: Box::new(forget) }, cursor5))
430        }
431        TokenVariant::Print =>
432        {
433            let keywordtok = ast::PrintToken { tok: token0.tok };
434            let (token1, cursor2) = lexer::eat(cursor1)?;
435            if let TokenVariant::LeftParens = token1.variant {} else
436            {
437                return Err((String::from("Expected `(`"), token1.tok));
438            }
439            let lparenstok = ast::LeftParensToken { tok: token1.tok };
440            let (string, cursor3) = eat_string(cursor2)?;
441            let (token3, cursor4) = lexer::eat(cursor3)?;
442            if let TokenVariant::RightParens = token3.variant {} else
443            {
444                return Err((String::from("Expected `)`"), token3.tok));
445            }
446            let rparenstok = ast::RightParensToken { tok: token3.tok };
447            let print = ast::Print::new(keywordtok,
448                                        lparenstok,
449                                        string,
450                                        rparenstok);
451            Ok((ast::Statement { alt: Box::new(print) }, cursor4))
452        }
453        TokenVariant::Number(_) | TokenVariant::N | TokenVariant::Read
454            | TokenVariant::Plus | TokenVariant::Minus
455            | TokenVariant::LeftParens | TokenVariant::String
456            | TokenVariant::U =>
457        {
458            let (lineops, cursor1) = eat_lineops(cursor0)?;
459            let lineoperations = ast::LineOperations::new(lineops);
460            Ok((ast::Statement { alt: Box::new(lineoperations) }, cursor1))
461        }
462        _ => Err((String::from("Expected statement"), token0.tok))
463    }
464}
465
466pub fn eat_line<'a>(input: &'a str)
467    -> Result<(ast::Line<'a>, &'a str), (String, &'a str)>
468{
469    let cursor0 = input;
470
471    let (token0, cursor1) = lexer::eat(cursor0)?;
472    let lineno;
473    if let TokenVariant::Number(val) = token0.variant
474    {
475        lineno = ast::NumberToken { tok: token0.tok, val };
476    }
477    else
478    {
479        return Err((String::from("Expected number"), token0.tok));
480    }
481
482    let (statement, cursor2) = eat_statement(cursor1)?;
483
484    let (token2, cursor3) = lexer::eat(cursor2)?;
485    if let TokenVariant::Semicolon = token2.variant {} else
486    {
487        return Err((String::from("Expected `;`"), token2.tok));
488    }
489    let semicolontok = ast::SemicolonToken { tok: token2.tok };
490
491    Ok((ast::Line::new(lineno, statement, semicolontok), cursor3))
492}
493
494#[cfg(test)]
495mod tests {
496    use super::*;
497
498    #[test]
499    fn absnumber_check()
500    {
501        let input = "123abc";
502
503        match eat_absnumber(input)
504        {
505            Ok((root, cursor)) =>
506            {
507                assert_eq!(root.alt.get_str(), &input[..3]);
508                assert_eq!(cursor, &input[3..]);
509            }
510            Err((error, at)) => panic!("{}: {}", error, at)
511        }
512    }
513}