rsco/src/parser.rs

284 lines
7.3 KiB
Rust

use nom::{
branch::alt,
bytes::complete::{escaped, is_not},
bytes::complete::{tag, take_until},
character::complete::char,
character::complete::multispace0,
character::complete::{alpha1, alphanumeric1, digit1, one_of, space0},
combinator::value,
combinator::{cut, map, map_res, recognize},
error::{context, convert_error, ContextError, ErrorKind, ParseError, VerboseError},
multi::{fold_many0, many0, separated_list0},
number::complete::double,
sequence::pair,
sequence::{delimited, preceded, terminated, tuple},
IResult, Parser,
};
use crate::ast::*;
fn sc<'a, E>(i: &'a str) -> IResult<&'a str, (), E>
where
E: ParseError<&'a str>,
{
alt((value((), multispace0), pinline_comment, peol_comment))(i)
}
fn pinline_comment<'a, E>(i: &'a str) -> IResult<&'a str, (), E>
where
E: ParseError<&'a str>,
{
value(
(), // Output is thrown away.
tuple((tag("/*"), take_until("*/"), tag("*/"))),
)(i)
}
fn peol_comment<'a, E: ParseError<&'a str>>(i: &'a str) -> IResult<&'a str, (), E> {
value(
(), // Output is thrown away.
pair(char('/'), is_not("\n\r")),
)(i)
}
fn braces<'a, P, O, E>(a: P) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
where
P: Parser<&'a str, O, E>,
E: ParseError<&'a str>,
{
delimited(char('{'), a, char('}'))
}
fn parens<'a, P, O, E>(a: P) -> impl FnMut(&'a str) -> IResult<&'a str, O, E>
where
P: Parser<&'a str, O, E>,
E: ParseError<&'a str>,
{
delimited(
tuple((tag("("), recognize(sc))),
a,
tuple((recognize(sc), tag(")"))),
)
}
fn identifier(input: &str) -> IResult<&str, &str> {
recognize(pair(
alt((alpha1, tag("_"))),
many0(alt((alphanumeric1, tag("_")))),
))(input)
}
fn parse_str<'a>(i: &'a str) -> IResult<&'a str, &'a str> {
escaped(alphanumeric1, '\\', one_of("\"n\\"))(i)
}
fn string<'a>(i: &'a str) -> IResult<&'a str, &'a str> {
preceded(char('\"'), cut(terminated(parse_str, char('\"'))))(i)
}
fn boolean<'a>(i: &'a str) -> IResult<&'a str, bool> {
println!("boolean: {}", i);
alt((map(tag("true"), |_| true), map(tag("false"), |_| false)))(i)
}
fn call<'a>(i: &'a str) -> IResult<&'a str, (Identifier, Vec<Expr>)> {
println!("call: {}", i);
map(
tuple((
identifier,
delimited(
char('('),
separated_list0(tuple((sc, tag(","), sc)), expr),
char(')'),
),
)),
|(i, v)| (i.to_string(), v),
)(i)
}
fn num(i: &str) -> IResult<&str, Expr> {
println!("num: {}", i);
map(double, Expr::LNum)(i)
}
fn parens_bin(i: &str) -> IResult<&str, Expr> {
delimited(space0, delimited(tag("("), expr, tag(")")), space0)(i)
}
fn factor_bin(i: &str) -> IResult<&str, Expr> {
alt((delimited(space0, num, space0), parens_bin))(i)
}
fn bin_less_greater(i: &str) -> IResult<&str, Expr> {
let (i, init) = factor_bin(i)?;
fold_many0(
pair(alt((char('<'), char('>'))), factor_bin),
move || init.clone(),
|acc, (op, val): (char, Expr)| {
if op == '<' {
Expr::Binary(BinOp::LessThan, Box::new(acc), Box::new(val))
} else {
Expr::Binary(BinOp::GreaterThan, Box::new(acc), Box::new(val))
}
},
)(i)
}
fn bin_equal_not(i: &str) -> IResult<&str, Expr> {
let (i, init) = bin_less_greater(i)?;
fold_many0(
pair(alt((char('<'), char('>'))), bin_less_greater),
move || init.clone(),
|acc, (op, val): (char, Expr)| {
if op == '<' {
Expr::Binary(BinOp::LessThan, Box::new(acc), Box::new(val))
} else {
Expr::Binary(BinOp::GreaterThan, Box::new(acc), Box::new(val))
}
},
)(i)
}
fn bin_mult_div(i: &str) -> IResult<&str, Expr> {
let (i, init) = bin_equal_not(i)?;
fold_many0(
pair(alt((char('*'), char('/'))), bin_equal_not),
move || init.clone(),
|acc, (op, val): (char, Expr)| {
if op == '*' {
Expr::Binary(BinOp::Mult, Box::new(acc), Box::new(val))
} else {
Expr::Binary(BinOp::Div, Box::new(acc), Box::new(val))
}
},
)(i)
}
fn binary<'a>(i: &'a str) -> IResult<&'a str, Expr> {
println!("binary: {}", i);
let (i, init) = bin_mult_div(i)?;
fold_many0(
pair(alt((char('+'), char('-'))), bin_mult_div),
move || init.clone(),
|acc, (op, val): (char, Expr)| {
if op == '+' {
Expr::Binary(BinOp::Plus, Box::new(acc), Box::new(val))
} else {
Expr::Binary(BinOp::Minus, Box::new(acc), Box::new(val))
}
},
)(i)
}
fn receive<'a>(i: &'a str) -> IResult<&'a str, Expr> {
map(pair(tag("<-"), expr), |(_, expr)| {
Expr::Receive(Box::new(expr))
})(i)
}
pub fn expr<'a>(i: &'a str) -> IResult<&'a str, Expr> {
println!("expr: {}", i);
preceded(
sc,
alt((
parens(expr),
map(call, |(i, v)| Expr::Call(i, v)),
map(identifier, |i| Expr::Var(i.to_string())),
receive,
binary,
map(tag("null"), |_| Expr::LNull),
map(string, |s| Expr::LStr(String::from(s))),
num,
)),
)(i)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_parse_string_lit() {
let a = r#""hey" "#;
let (rest, res) = string(a).unwrap();
assert_eq!(res, "hey");
assert_eq!(rest, " ");
}
#[test]
fn can_parse_binary_op() {
let a = "3 +3 + 6";
let (_rest, res) = binary(a).unwrap();
assert_eq!(
res,
Expr::Binary(
BinOp::Plus,
Box::new(Expr::Binary(
BinOp::Plus,
Box::new(Expr::LNum(3.0)),
Box::new(Expr::LNum(3.0)),
)),
Box::new(Expr::LNum(6.0))
)
);
}
#[test]
fn can_parse_empty_call() {
let a = "hello()";
let (rest, res) = expr(a).unwrap();
assert_eq!(res, Expr::Call("hello".to_string(), vec![]));
assert_eq!(rest, "");
}
#[test]
fn can_parse_call() {
let a = "hello(1.0, 45 , yeah)";
let (rest, res) = expr(a).unwrap();
assert_eq!(
res,
Expr::Call(
"hello".to_string(),
vec![
Expr::LNum(1.0),
Expr::LNum(45.0),
Expr::Var("yeah".to_string())
]
)
);
assert_eq!(rest, "");
}
#[test]
fn can_parse_call_with_bin_op() {
let a = "hello(1.0, 45 + 33, yeah)";
let (rest, res) = expr(a).unwrap();
assert_eq!(
res,
Expr::Call(
"hello".to_string(),
vec![
Expr::LNum(1.0),
Expr::Binary(
BinOp::Plus,
Box::new(Expr::LNum(45.0)),
Box::new(Expr::LNum(33.0))
),
Expr::Var("yeah".to_string())
]
)
);
assert_eq!(rest, "");
}
#[test]
fn can_parse() {
let a = r#"let hello = ("hey" + 0.5)"#;
}
}