litto/lang/minilang/
parser.rs1use super::types;
10use crate::sexp;
11use crate::Interpreter;
12use pest::Parser;
13use pest_derive::Parser;
14use std::borrow::Cow;
15
16type Expr = <super::MiniLang as Interpreter>::Expr;
17type Value = <super::MiniLang as Interpreter>::Value;
18type Pair<'a> = pest::iterators::Pair<'a, Rule>;
19
20#[derive(Parser)]
21#[grammar_inline = r##"
22WHITESPACE = _{" " | "\t" | NEWLINE}
23COMMENT = _{ ";" ~ (!NEWLINE ~ ANY)* }
24
25list = { ("(" ~ listbody ~ ")") | ("[" ~ listbody ~ "]") }
26listbody = _{ (quote | symbol | list | string | int | bool)* }
27
28quote = {"'" ~ (list | symbol)}
29
30int = @{ "-"? ~ ('0'..'9')+ }
31string = @{ "\"" ~ (!("\\" | "\"") ~ ANY | "\\" ~ ANY)* ~ "\"" }
32symbol = @{('a'..'z' | 'A'..'Z' | "λ" | "<" | ">" | "!" | "+" | "-" | "*" | "/" | "=" | "?")+}
33bool = @{"#t" | "#f"}
34
35file = {SOI ~ listbody ~ EOI}
36"##]
37struct MiniLangParser;
38
39pub fn parse(code: &str) -> Result<Expr, String> {
41 let pairs = MiniLangParser::parse(Rule::file, code).map_err(|e| e.to_string())?;
42
43 fn handle_pair(pair: Pair) -> Result<Expr, String> {
44 Ok(match pair.as_rule() {
45 Rule::file => {
46 let mut vec = vec![sexp!(begin)];
48 for pair in pair.into_inner() {
49 let rule = pair.as_rule();
50 if rule != Rule::EOI {
51 vec.push(handle_pair(pair)?);
52 }
53 }
54 Expr::Compound(vec)
55 }
56 Rule::list => {
57 let mut vec = vec![];
58 for pair in pair.into_inner() {
59 vec.push(handle_pair(pair)?);
60 }
61 Expr::Compound(vec)
62 }
63 Rule::quote => {
64 let inner = pair.into_inner().nth(0).unwrap();
66 Expr::Inlined(Value::new(types::Quote(handle_pair(inner)?)))
67 }
68 Rule::string => sexp!(types::Str::from_escaped(pair.as_str())),
69 Rule::symbol => Expr::Symbol(Cow::Owned(pair.as_str().to_string())),
70 Rule::bool => Expr::Inlined(Value::new(types::Bool(pair.as_str() == "#t"))),
71 Rule::int => {
72 let value: i64 = pair.as_str().parse::<i64>().map_err(|e| e.to_string())?;
73 Expr::Inlined(Value::new(types::Int(value)))
74 }
75 Rule::EOI | Rule::listbody | Rule::WHITESPACE | Rule::COMMENT => unreachable!(),
76 })
77 };
78
79 for pair in pairs {
80 return handle_pair(pair);
81 }
82
83 Err("empty input".into())
84}