feat: parser improvements
- record creation with punning - disambiguate if condition from record creation with punning - split parser tests up into many smaller ones
This commit is contained in:
		
							parent
							
								
									375499930a
								
							
						
					
					
						commit
						391849bf37
					
				| 
						 | 
				
			
			@ -75,6 +75,7 @@ version = "0.0.26"
 | 
			
		|||
dependencies = [
 | 
			
		||||
 "chumsky",
 | 
			
		||||
 "indexmap",
 | 
			
		||||
 "indoc",
 | 
			
		||||
 "itertools",
 | 
			
		||||
 "miette",
 | 
			
		||||
 "pretty_assertions",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -21,4 +21,5 @@ uplc = { path = '../uplc', version = "0.0.25" }
 | 
			
		|||
vec1 = "1.8.0"
 | 
			
		||||
 | 
			
		||||
[dev-dependencies]
 | 
			
		||||
indoc = "1.0.7"
 | 
			
		||||
pretty_assertions = "1.3.0"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -562,6 +562,168 @@ pub fn expr_parser(
 | 
			
		|||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let record_update_parser = select! {Token::Name { name } => name}
 | 
			
		||||
            .map_with_span(|module, span: Span| (module, span))
 | 
			
		||||
            .then_ignore(just(Token::Dot))
 | 
			
		||||
            .or_not()
 | 
			
		||||
            .then(select! {Token::UpName { name } => name}.map_with_span(|name, span| (name, span)))
 | 
			
		||||
            .then(
 | 
			
		||||
                just(Token::DotDot)
 | 
			
		||||
                    .ignore_then(r.clone())
 | 
			
		||||
                    .then(
 | 
			
		||||
                        just(Token::Comma)
 | 
			
		||||
                            .ignore_then(
 | 
			
		||||
                                select! { Token::Name {name} => name }
 | 
			
		||||
                                    .then_ignore(just(Token::Colon))
 | 
			
		||||
                                    .then(r.clone())
 | 
			
		||||
                                    .map_with_span(|(label, value), span| {
 | 
			
		||||
                                        ast::UntypedRecordUpdateArg {
 | 
			
		||||
                                            label,
 | 
			
		||||
                                            value,
 | 
			
		||||
                                            location: span,
 | 
			
		||||
                                        }
 | 
			
		||||
                                    })
 | 
			
		||||
                                    .separated_by(just(Token::Comma))
 | 
			
		||||
                                    .allow_trailing(),
 | 
			
		||||
                            )
 | 
			
		||||
                            .or_not(),
 | 
			
		||||
                    )
 | 
			
		||||
                    .delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
 | 
			
		||||
                    .map_with_span(|a, span: Span| (a, span)),
 | 
			
		||||
            )
 | 
			
		||||
            .map(|((module, (name, n_span)), ((spread, opt_args), span))| {
 | 
			
		||||
                let constructor = if let Some((module, m_span)) = module {
 | 
			
		||||
                    expr::UntypedExpr::FieldAccess {
 | 
			
		||||
                        location: m_span.union(n_span),
 | 
			
		||||
                        label: name,
 | 
			
		||||
                        container: Box::new(expr::UntypedExpr::Var {
 | 
			
		||||
                            location: m_span,
 | 
			
		||||
                            name: module,
 | 
			
		||||
                        }),
 | 
			
		||||
                    }
 | 
			
		||||
                } else {
 | 
			
		||||
                    expr::UntypedExpr::Var {
 | 
			
		||||
                        location: n_span,
 | 
			
		||||
                        name,
 | 
			
		||||
                    }
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                let spread_span = spread.location();
 | 
			
		||||
 | 
			
		||||
                let location = Span::new((), spread_span.start - 2..spread_span.end);
 | 
			
		||||
 | 
			
		||||
                let spread = ast::RecordUpdateSpread {
 | 
			
		||||
                    base: Box::new(spread),
 | 
			
		||||
                    location,
 | 
			
		||||
                };
 | 
			
		||||
 | 
			
		||||
                expr::UntypedExpr::RecordUpdate {
 | 
			
		||||
                    location: constructor.location().union(span),
 | 
			
		||||
                    constructor: Box::new(constructor),
 | 
			
		||||
                    spread,
 | 
			
		||||
                    arguments: opt_args.unwrap_or_default(),
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        let record_parser = choice((
 | 
			
		||||
            select! {Token::Name { name } => name}
 | 
			
		||||
                .map_with_span(|module, span| (module, span))
 | 
			
		||||
                .then_ignore(just(Token::Dot))
 | 
			
		||||
                .or_not()
 | 
			
		||||
                .then(
 | 
			
		||||
                    select! {Token::UpName { name } => name}
 | 
			
		||||
                        .map_with_span(|name, span| (name, span)),
 | 
			
		||||
                )
 | 
			
		||||
                .then(
 | 
			
		||||
                    select! {Token::Name {name} => name}
 | 
			
		||||
                        .then_ignore(just(Token::Colon))
 | 
			
		||||
                        .or_not()
 | 
			
		||||
                        .then(r.clone())
 | 
			
		||||
                        .validate(|(label_opt, value), span, emit| {
 | 
			
		||||
                            dbg!(&label_opt);
 | 
			
		||||
                            let label = if label_opt.is_some() {
 | 
			
		||||
                                label_opt
 | 
			
		||||
                            } else if let expr::UntypedExpr::Var { name, .. } = &value {
 | 
			
		||||
                                Some(name.clone())
 | 
			
		||||
                            } else {
 | 
			
		||||
                                emit(ParseError::expected_input_found(
 | 
			
		||||
                                    value.location(),
 | 
			
		||||
                                    None,
 | 
			
		||||
                                    Some(error::Pattern::RecordPunning),
 | 
			
		||||
                                ));
 | 
			
		||||
 | 
			
		||||
                                None
 | 
			
		||||
                            };
 | 
			
		||||
 | 
			
		||||
                            ast::CallArg {
 | 
			
		||||
                                location: span,
 | 
			
		||||
                                value,
 | 
			
		||||
                                label,
 | 
			
		||||
                            }
 | 
			
		||||
                        })
 | 
			
		||||
                        .separated_by(just(Token::Comma))
 | 
			
		||||
                        .allow_trailing()
 | 
			
		||||
                        .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
 | 
			
		||||
                ),
 | 
			
		||||
            select! {Token::Name { name } => name}
 | 
			
		||||
                .map_with_span(|module, span| (module, span))
 | 
			
		||||
                .then_ignore(just(Token::Dot))
 | 
			
		||||
                .or_not()
 | 
			
		||||
                .then(
 | 
			
		||||
                    select! {Token::UpName { name } => name}
 | 
			
		||||
                        .map_with_span(|name, span| (name, span)),
 | 
			
		||||
                )
 | 
			
		||||
                .then(
 | 
			
		||||
                    r.clone()
 | 
			
		||||
                        .map_with_span(|value, span| ast::CallArg {
 | 
			
		||||
                            location: span,
 | 
			
		||||
                            value,
 | 
			
		||||
                            label: None,
 | 
			
		||||
                        })
 | 
			
		||||
                        .separated_by(just(Token::Comma))
 | 
			
		||||
                        .allow_trailing()
 | 
			
		||||
                        .delimited_by(just(Token::LeftParen), just(Token::RightParen)),
 | 
			
		||||
                ),
 | 
			
		||||
        ))
 | 
			
		||||
        .map_with_span(|((module, (name, n_span)), arguments), span| {
 | 
			
		||||
            let fun = if let Some((module, m_span)) = module {
 | 
			
		||||
                expr::UntypedExpr::FieldAccess {
 | 
			
		||||
                    location: m_span.union(n_span),
 | 
			
		||||
                    label: name,
 | 
			
		||||
                    container: Box::new(expr::UntypedExpr::Var {
 | 
			
		||||
                        location: m_span,
 | 
			
		||||
                        name: module,
 | 
			
		||||
                    }),
 | 
			
		||||
                }
 | 
			
		||||
            } else {
 | 
			
		||||
                expr::UntypedExpr::Var {
 | 
			
		||||
                    location: n_span,
 | 
			
		||||
                    name,
 | 
			
		||||
                }
 | 
			
		||||
            };
 | 
			
		||||
 | 
			
		||||
            expr::UntypedExpr::Call {
 | 
			
		||||
                arguments,
 | 
			
		||||
                fun: Box::new(fun),
 | 
			
		||||
                location: span,
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let field_access_constructor = select! {Token::Name { name } => name}
 | 
			
		||||
            .map_with_span(|module, span| (module, span))
 | 
			
		||||
            .then_ignore(just(Token::Dot))
 | 
			
		||||
            .then(select! {Token::UpName { name } => name})
 | 
			
		||||
            .map_with_span(
 | 
			
		||||
                |((module, m_span), name), span| expr::UntypedExpr::FieldAccess {
 | 
			
		||||
                    location: span,
 | 
			
		||||
                    label: name,
 | 
			
		||||
                    container: Box::new(expr::UntypedExpr::Var {
 | 
			
		||||
                        location: m_span,
 | 
			
		||||
                        name: module,
 | 
			
		||||
                    }),
 | 
			
		||||
                },
 | 
			
		||||
            );
 | 
			
		||||
 | 
			
		||||
        let var_parser = select! {
 | 
			
		||||
            Token::Name { name } => name,
 | 
			
		||||
            Token::UpName { name } => name,
 | 
			
		||||
| 
						 | 
				
			
			@ -756,23 +918,29 @@ pub fn expr_parser(
 | 
			
		|||
            );
 | 
			
		||||
 | 
			
		||||
        let if_parser = just(Token::If)
 | 
			
		||||
            .ignore_then(r.clone().then(block_parser.clone()).map_with_span(
 | 
			
		||||
                |(condition, body), span| ast::IfBranch {
 | 
			
		||||
                    condition,
 | 
			
		||||
                    body,
 | 
			
		||||
                    location: span,
 | 
			
		||||
                },
 | 
			
		||||
            ))
 | 
			
		||||
            .ignore_then(
 | 
			
		||||
                r.clone()
 | 
			
		||||
                    .then_ignore(just(Token::Then))
 | 
			
		||||
                    .then(block_parser.clone())
 | 
			
		||||
                    .map_with_span(|(condition, body), span| ast::IfBranch {
 | 
			
		||||
                        condition,
 | 
			
		||||
                        body,
 | 
			
		||||
                        location: span,
 | 
			
		||||
                    }),
 | 
			
		||||
            )
 | 
			
		||||
            .then(
 | 
			
		||||
                just(Token::Else)
 | 
			
		||||
                    .ignore_then(just(Token::If))
 | 
			
		||||
                    .ignore_then(r.clone().then(block_parser.clone()).map_with_span(
 | 
			
		||||
                        |(condition, body), span| ast::IfBranch {
 | 
			
		||||
                            condition,
 | 
			
		||||
                            body,
 | 
			
		||||
                            location: span,
 | 
			
		||||
                        },
 | 
			
		||||
                    ))
 | 
			
		||||
                    .ignore_then(
 | 
			
		||||
                        r.clone()
 | 
			
		||||
                            .then_ignore(just(Token::Then))
 | 
			
		||||
                            .then(block_parser.clone())
 | 
			
		||||
                            .map_with_span(|(condition, body), span| ast::IfBranch {
 | 
			
		||||
                                condition,
 | 
			
		||||
                                body,
 | 
			
		||||
                                location: span,
 | 
			
		||||
                            }),
 | 
			
		||||
                    )
 | 
			
		||||
                    .repeated(),
 | 
			
		||||
            )
 | 
			
		||||
            .then_ignore(just(Token::Else))
 | 
			
		||||
| 
						 | 
				
			
			@ -792,6 +960,9 @@ pub fn expr_parser(
 | 
			
		|||
        let expr_unit_parser = choice((
 | 
			
		||||
            string_parser,
 | 
			
		||||
            int_parser,
 | 
			
		||||
            record_update_parser,
 | 
			
		||||
            record_parser,
 | 
			
		||||
            field_access_constructor,
 | 
			
		||||
            var_parser,
 | 
			
		||||
            todo_parser,
 | 
			
		||||
            tuple,
 | 
			
		||||
| 
						 | 
				
			
			@ -819,42 +990,14 @@ pub fn expr_parser(
 | 
			
		|||
        enum Chain {
 | 
			
		||||
            Call(Vec<ParserArg>, Span),
 | 
			
		||||
            FieldAccess(String, Span),
 | 
			
		||||
            RecordUpdate(
 | 
			
		||||
                Box<(expr::UntypedExpr, Vec<ast::UntypedRecordUpdateArg>)>,
 | 
			
		||||
                Span,
 | 
			
		||||
            ),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let field_access_parser = just(Token::Dot)
 | 
			
		||||
            .ignore_then(select! {
 | 
			
		||||
                Token::Name { name } => name,
 | 
			
		||||
                Token::UpName { name } => name
 | 
			
		||||
            })
 | 
			
		||||
            .map_with_span(Chain::FieldAccess);
 | 
			
		||||
 | 
			
		||||
        let record_update_parser = just(Token::DotDot)
 | 
			
		||||
            .ignore_then(r.clone())
 | 
			
		||||
            .then(
 | 
			
		||||
                just(Token::Comma)
 | 
			
		||||
                    .ignore_then(
 | 
			
		||||
                        select! { Token::Name {name} => name }
 | 
			
		||||
                            .then_ignore(just(Token::Colon))
 | 
			
		||||
                            .then(r.clone())
 | 
			
		||||
                            .map_with_span(|(label, value), span| ast::UntypedRecordUpdateArg {
 | 
			
		||||
                                label,
 | 
			
		||||
                                value,
 | 
			
		||||
                                location: span,
 | 
			
		||||
                            })
 | 
			
		||||
                            .separated_by(just(Token::Comma))
 | 
			
		||||
                            .allow_trailing(),
 | 
			
		||||
                    )
 | 
			
		||||
                    .or_not(),
 | 
			
		||||
            )
 | 
			
		||||
            .delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
 | 
			
		||||
            .map_with_span(|(spread, args_opt), span| {
 | 
			
		||||
                Chain::RecordUpdate(Box::new((spread, args_opt.unwrap_or_default())), span)
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        let call_parser = choice((
 | 
			
		||||
            select! { Token::Name { name } => name }
 | 
			
		||||
                .then_ignore(just(Token::Colon))
 | 
			
		||||
| 
						 | 
				
			
			@ -881,7 +1024,7 @@ pub fn expr_parser(
 | 
			
		|||
        .delimited_by(just(Token::LeftParen), just(Token::RightParen))
 | 
			
		||||
        .map_with_span(Chain::Call);
 | 
			
		||||
 | 
			
		||||
        let chain = choice((field_access_parser, record_update_parser, call_parser));
 | 
			
		||||
        let chain = choice((field_access_parser, call_parser));
 | 
			
		||||
 | 
			
		||||
        let chained = expr_unit_parser
 | 
			
		||||
            .then(chain.repeated())
 | 
			
		||||
| 
						 | 
				
			
			@ -941,24 +1084,6 @@ pub fn expr_parser(
 | 
			
		|||
                    label,
 | 
			
		||||
                    container: Box::new(e),
 | 
			
		||||
                },
 | 
			
		||||
 | 
			
		||||
                Chain::RecordUpdate(data, span) => {
 | 
			
		||||
                    let (spread, arguments) = *data;
 | 
			
		||||
 | 
			
		||||
                    let location = span.union(spread.location());
 | 
			
		||||
 | 
			
		||||
                    let spread = ast::RecordUpdateSpread {
 | 
			
		||||
                        base: Box::new(spread),
 | 
			
		||||
                        location,
 | 
			
		||||
                    };
 | 
			
		||||
 | 
			
		||||
                    expr::UntypedExpr::RecordUpdate {
 | 
			
		||||
                        location: e.location().union(span),
 | 
			
		||||
                        constructor: Box::new(e),
 | 
			
		||||
                        spread,
 | 
			
		||||
                        arguments,
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        // Negate
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -69,6 +69,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
 | 
			
		|||
        "fn" => Token::Fn,
 | 
			
		||||
        "if" => Token::If,
 | 
			
		||||
        "else" => Token::Else,
 | 
			
		||||
        "then" => Token::Then,
 | 
			
		||||
        "is" => Token::Is,
 | 
			
		||||
        "let" => Token::Let,
 | 
			
		||||
        "opaque" => Token::Opaque,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -72,6 +72,7 @@ pub enum Token {
 | 
			
		|||
    Trace,
 | 
			
		||||
    Type,
 | 
			
		||||
    When,
 | 
			
		||||
    Then,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl fmt::Display for Token {
 | 
			
		||||
| 
						 | 
				
			
			@ -145,6 +146,7 @@ impl fmt::Display for Token {
 | 
			
		|||
            Token::Todo => "todo",
 | 
			
		||||
            Token::Trace => "try",
 | 
			
		||||
            Token::Type => "type",
 | 
			
		||||
            Token::Then => "then",
 | 
			
		||||
        };
 | 
			
		||||
        write!(f, "\"{}\"", s)
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
				
			
			@ -32,7 +32,7 @@ pub fn final_check(z: Int) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
pub fn incrementor(counter: Int, target: Int) -> Int {
 | 
			
		||||
  if counter == target {
 | 
			
		||||
  if counter == target then {
 | 
			
		||||
    target
 | 
			
		||||
  } else {
 | 
			
		||||
    incrementor(counter + 1, target)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue