tvl-depot/src/parser/mod.rs

124 lines
2.9 KiB
Rust
Raw Normal View History

use nom::character::complete::{multispace0, multispace1};
2021-03-07 21:29:59 +01:00
use nom::error::{ErrorKind, ParseError};
2021-03-14 17:27:28 +01:00
use nom::{alt, char, complete, do_parse, many0, named, separated_list0, tag, terminated};
2021-03-07 21:29:59 +01:00
#[macro_use]
mod macros;
mod expr;
mod type_;
2021-03-07 21:29:59 +01:00
use crate::ast::{Decl, Fun, Ident};
pub use expr::expr;
pub use type_::type_;
2021-03-07 21:29:59 +01:00
pub type Error = nom::Err<nom::error::Error<String>>;
2021-03-07 21:29:59 +01:00
pub(crate) fn is_reserved(s: &str) -> bool {
matches!(
s,
2021-03-14 16:53:13 +01:00
"if" | "then"
| "else"
| "let"
| "in"
| "fn"
| "int"
| "float"
| "bool"
| "true"
| "false"
| "cstring"
)
}
pub(crate) fn ident<'a, E>(i: &'a str) -> nom::IResult<&'a str, Ident, E>
2021-03-07 21:29:59 +01:00
where
E: ParseError<&'a str>,
{
let mut chars = i.chars();
if let Some(f) = chars.next() {
if f.is_alphabetic() || f == '_' {
let mut idx = 1;
for c in chars {
if !(c.is_alphanumeric() || c == '_') {
break;
}
idx += 1;
}
let id = &i[..idx];
if is_reserved(id) {
Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Satisfy)))
} else {
Ok((&i[idx..], Ident::from_str_unchecked(id)))
}
2021-03-07 21:29:59 +01:00
} else {
Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Satisfy)))
}
} else {
Err(nom::Err::Error(E::from_error_kind(i, ErrorKind::Eof)))
}
}
named!(fun_decl(&str) -> Decl, do_parse!(
complete!(tag!("fn"))
2021-03-07 21:29:59 +01:00
>> multispace0
>> name: ident
>> multispace1
>> args: separated_list0!(multispace1, ident)
>> multispace0
>> char!('=')
>> multispace0
>> body: expr
>> (Decl::Fun {
2021-03-07 21:29:59 +01:00
name,
body: Fun {
args,
body
}
2021-03-07 21:29:59 +01:00
})
));
named!(pub decl(&str) -> Decl, alt!(
fun_decl
2021-03-07 21:29:59 +01:00
));
2021-03-14 17:27:28 +01:00
named!(pub toplevel(&str) -> Vec<Decl>, terminated!(many0!(decl), multispace0));
2021-03-07 21:29:59 +01:00
#[cfg(test)]
mod tests {
use std::convert::TryInto;
2021-03-07 21:29:59 +01:00
use super::*;
use expr::tests::ident_expr;
2021-03-07 21:29:59 +01:00
#[test]
fn fn_decl() {
let res = test_parse!(decl, "fn id x = x");
assert_eq!(
res,
Decl::Fun {
2021-03-07 21:29:59 +01:00
name: "id".try_into().unwrap(),
body: Fun {
args: vec!["x".try_into().unwrap()],
body: *ident_expr("x"),
}
}
2021-03-07 21:29:59 +01:00
)
}
#[test]
fn multiple_decls() {
let res = test_parse!(
toplevel,
"fn id x = x
fn plus x y = x + y
fn main = plus (id 2) 7"
);
assert_eq!(res.len(), 3);
2021-03-14 17:27:28 +01:00
let res = test_parse!(
toplevel,
"fn id x = x\nfn plus x y = x + y\nfn main = plus (id 2) 7\n"
);
assert_eq!(res.len(), 3);
}
2021-03-07 21:29:59 +01:00
}