use std::{cell::RefCell, rc::Rc}; use nom::{ branch::alt, bytes::complete::{tag, take_till}, character::complete::{ alpha1, alphanumeric0, char, line_ending, multispace0, none_of, not_line_ending, one_of, space0, space1, }, combinator::{map, not, opt, peek, recognize, value}, error::{context, ParseError, VerboseError}, multi::{fold_many1, many0, many1, separated_list0, separated_list1}, sequence::{pair, preceded, tuple}, Err, IResult, Parser, }; use nom_locate::{position, LocatedSpan}; use crate::identifier::{Id, IdStore}; type StoreRef = Rc>>; pub type Span<'a> = LocatedSpan<&'a str, StoreRef>; type ParseResult<'a, O> = IResult, O, VerboseError>>; use crate::ast::*; fn rc_string(s: &str) -> Rc { Rc::new(s.to_string()) } fn fresh_id(span: &Span) -> Id { let mut table_handle = span.extra.borrow_mut(); table_handle.fresh() } fn fresh_id_rc(store_ref: &StoreRef) -> Id { let mut table_handle = store_ref.borrow_mut(); table_handle.fresh() } fn tok<'a, O>( input_parser: impl Parser, O, VerboseError>>, ) -> impl FnMut(Span<'a>) -> IResult, O, VerboseError>> { context("tok", map(tuple((ws0, input_parser)), |(_, output)| output)) } fn kw<'a>(keyword_str: &'static str) -> impl FnMut(Span<'a>) -> ParseResult<()> { context("keyword", tok(value((), tag(keyword_str)))) } // whitespace does consume at least one piece of whitespace - use ws0 for maybe none fn whitespace(input: Span) -> ParseResult<()> { context("whitespace", alt((block_comment, line_comment, value((), space1))))(input) } fn ws0(input: Span) -> ParseResult<()> { context("WS0", value((), many0(whitespace)))(input) } fn line_comment(input: Span) -> ParseResult<()> { value((), tuple((tag("//"), not_line_ending)))(input) } fn block_comment(input: Span) -> ParseResult<()> { context( "Block-comment", value( (), tuple(( tag("/*"), many0(alt((value((), none_of("*/")), value((), none_of("/*")), block_comment))), tag("*/"), )), ), )(input) } fn statement_delimiter(input: Span) -> ParseResult<()> { tok(alt((value((), line_ending), value((), char(';')))))(input) } fn block(input: Span) -> ParseResult { context( "block", map( tuple(( tok(char('{')), many0(statement_delimiter), separated_list0(statement_delimiter, statement), many0(statement_delimiter), tok(char('}')), )), |(_, _, items, _, _)| items.into(), ), )(input) } fn statement(input: Span) -> ParseResult { let (input, pos) = position(input)?; let pos: usize = pos.location_offset(); let id = fresh_id(&input); context( "Parsing-statement", map(expression, move |expr| Statement { id, location: pos.into(), kind: StatementKind::Expression(expr), }), )(input) } pub fn expression(input: Span) -> ParseResult { let id = fresh_id(&input); map(pair(expression_kind, opt(type_anno)), move |(kind, maybe_anno)| Expression::new(id, kind))(input) } fn type_anno(input: Span) -> ParseResult { preceded(kw(":"), type_identifier)(input) } fn type_identifier(input: Span) -> ParseResult { /* alt(( tuple((kw("("), separated_list0(kw(","), type_identifier), kw(")"))), type_singleton_name ))(input) */ unimplemented!() } fn type_singleton_name(input: Span) -> ParseResult { unimplemented!() } pub fn expression_kind(input: Span) -> ParseResult { context("expression-kind", prefix_expr)(input) } fn precedence_expr(input: Span) -> ParseResult { map( pair(prefix_expr, many0(precedence_continuation)), |(first, rest): (ExpressionKind, Vec<(BinOp, ExpressionKind)>)| unimplemented!(), )(input) } fn precedence_continuation(input: Span) -> ParseResult<(BinOp, ExpressionKind)> { pair(operator, prefix_expr)(input) } fn operator(input: Span) -> ParseResult { tok(map( tuple((not(tag("*/")), recognize(many1(one_of("+-*/%<>=!$&|?^`"))))), |(_, sigil_span): ((), Span)| BinOp::from_sigil(sigil_span.fragment()), ))(input) } fn prefix_op(input: Span) -> ParseResult { tok(map(recognize(one_of("+-!")), |sigil: Span| PrefixOp::from_sigil(sigil.fragment())))(input) } fn prefix_expr(input: Span) -> ParseResult { let handle = input.extra.clone(); context( "prefix-expr", map(pair(opt(prefix_op), extended_expr), move |(prefix, expr)| { if let Some(prefix) = prefix { let expr = Expression::new(fresh_id_rc(&handle), expr); ExpressionKind::PrefixExp(prefix, Box::new(expr)) } else { expr } }), )(input) } fn extended_expr(input: Span) -> ParseResult { context("extended-expr", primary_expr)(input) } fn primary_expr(input: Span) -> ParseResult { context("primary-expr", alt((float_literal, number_literal, bool_literal, identifier_expr)))(input) } fn identifier_expr(input: Span) -> ParseResult { context("identifier-expr", map(qualified_identifier, ExpressionKind::Value))(input) } fn qualified_identifier(input: Span) -> ParseResult { let id = fresh_id(&input); tok(map(separated_list1(tag("::"), map(identifier, |x| rc_string(x.fragment()))), move |items| { QualifiedName { id, components: items } }))(input) } fn identifier(input: Span) -> ParseResult { recognize(tuple((alt((tag("_"), alpha1)), alphanumeric0)))(input) } fn bool_literal(input: Span) -> ParseResult { context( "bool-literal", alt(( map(kw("true"), |_| ExpressionKind::BoolLiteral(true)), map(kw("false"), |_| ExpressionKind::BoolLiteral(false)), )), )(input) } fn float_literal(input: Span) -> ParseResult { tok(map( alt(( recognize(tuple((digits(digit_group_dec), char('.'), opt(digits(digit_group_dec))))), recognize(tuple((char('.'), digits(digit_group_dec)))), )), |ds| ExpressionKind::FloatLiteral(ds.fragment().parse().unwrap()), ))(input) } fn number_literal(input: Span) -> ParseResult { map(alt((tok(hex_literal), tok(bin_literal), tok(dec_literal))), ExpressionKind::NatLiteral)(input) } fn dec_literal(input: Span) -> ParseResult { map(digits(digit_group_dec), |chars: Vec| { let s: String = chars.into_iter().collect(); s.parse().unwrap() })(input) } fn hex_literal(input: Span) -> ParseResult { map(preceded(alt((tag("0x"), tag("0X"))), digits(digit_group_hex)), |chars: Vec| { let s: String = chars.into_iter().collect(); parse_hex(&s).unwrap() })(input) } fn bin_literal(input: Span) -> ParseResult { map(preceded(alt((tag("0b"), tag("0B"))), digits(digit_group_bin)), |chars: Vec| { let s: String = chars.into_iter().collect(); parse_binary(&s).unwrap() })(input) } fn digits<'a, E: ParseError>>( digit_type: impl Parser, Vec, E>, ) -> impl FnMut(Span<'a>) -> IResult, Vec, E> { map(separated_list1(many1(char('_')), digit_type), |items: Vec>| { items.into_iter().flatten().collect() }) } fn digit_group_dec(input: Span) -> ParseResult> { many1(one_of("0123456789"))(input) } fn digit_group_hex(input: Span) -> ParseResult> { many1(one_of("0123456789abcdefABCDEF"))(input) } fn digit_group_bin(input: Span) -> ParseResult> { many1(one_of("01"))(input) } fn parse_binary(digits: &str) -> Result { let mut result: u64 = 0; let mut multiplier = 1; for d in digits.chars().rev() { match d { '1' => result += multiplier, '0' => (), '_' => continue, _ => unreachable!(), } multiplier = match multiplier.checked_mul(2) { Some(m) => m, None => return Err("Binary expression will overflow"), } } Ok(result) } fn parse_hex(digits: &str) -> Result { let mut result: u64 = 0; let mut multiplier: u64 = 1; for d in digits.chars().rev() { if d == '_' { continue; } match d.to_digit(16) { Some(n) => result += n as u64 * multiplier, None => return Err("Internal parser error: invalid hex digit"), } multiplier = match multiplier.checked_mul(16) { Some(m) => m, None => return Err("Hexadecimal expression will overflow"), } } Ok(result) } #[cfg(test)] mod test { use pretty_assertions::assert_eq; use super::*; fn rc(s: &str) -> Rc { Rc::new(s.to_owned()) } macro_rules! qn { ( $( $component:ident),* ) => { { let mut components = vec![]; $( components.push(rc(stringify!($component))); )* QualifiedName { components, id: Default::default() } } }; } macro_rules! span { ($func:expr, $input:expr) => {{ let id_store: IdStore = IdStore::new(); let span = Span::new_extra($input, Rc::new(RefCell::new(id_store))); $func(span).map(|(span, x)| (*span.fragment(), x)) }}; } #[test] fn combinator_test1() { assert_eq!(span!(digits(digit_group_dec), "342"), Ok(("", vec!['3', '4', '2']))); assert_eq!(span!(bin_literal, "0b1111qsdf"), Ok(("qsdf", 15))); } #[test] fn combinator_test_ws0() { assert_eq!(span!(block_comment, "/*yolo*/"), Ok(("", ()))); assert_eq!(span!(block_comment, "/*yolo*/ jumpy /*nah*/"), Ok((" jumpy /*nah*/", ()))); assert_eq!(span!(ws0, "/* yolo */ "), Ok(("", ()))); assert_eq!(span!(ws0, "/* /* no */ yolo */ "), Ok(("", ()))); } #[test] fn combinator_test2() { for s in [" 15", " 0b1111", " 1_5_", "0XF__", "0Xf"].iter() { assert_eq!(span!(expression_kind, s).unwrap().1, ExpressionKind::NatLiteral(15)); } assert_eq!(span!(expression_kind, " /*gay*/ true").unwrap().1, ExpressionKind::BoolLiteral(true)); assert_eq!( span!(expression_kind, " /*yolo*/ barnaby").unwrap().1, ExpressionKind::Value(qn!(barnaby)) ); let source = "!4"; let parsed = span!(expression_kind, source).map_err(|err| match err { Err::Error(err) | Err::Failure(err) => { let err = VerboseError { errors: err.errors.into_iter().map(|(sp, kind)| (*sp.fragment(), kind)).collect(), }; nom::error::convert_error(source, err) } _ => panic!(), }); if let Err(err) = parsed { println!("{}", err); panic!("parse error desu!"); } assert_eq!( parsed.unwrap().1, ExpressionKind::PrefixExp( PrefixOp::from_sigil("!"), Box::new(Expression::new(Default::default(), ExpressionKind::NatLiteral(4))) ) ); } #[test] fn combinator_test3() { let source = "{}"; assert_eq!(span!(block, source).unwrap().1, vec![].into()); let source = r#"{ //hella 4_5 //bog 11; /*chutney*/0xf }"#; let parsed = span!(block, source).map_err(|err| match err { Err::Error(err) | Err::Failure(err) => { let err = VerboseError { errors: err.errors.into_iter().map(|(sp, kind)| (*sp.fragment(), kind)).collect(), }; nom::error::convert_error(source, err) } _ => panic!(), }); if let Err(err) = parsed { println!("{}", err); panic!("parse error desu!"); } assert_eq!( parsed.unwrap().1, vec![ Statement { id: Default::default(), location: Default::default(), kind: StatementKind::Expression(Expression::new( Default::default(), ExpressionKind::NatLiteral(45) )) }, Statement { id: Default::default(), location: Default::default(), kind: StatementKind::Expression(Expression::new( Default::default(), ExpressionKind::NatLiteral(11) )) }, Statement { id: Default::default(), location: Default::default(), kind: StatementKind::Expression(Expression::new( Default::default(), ExpressionKind::NatLiteral(15) )) }, ] .into() ); } }