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 = [
 | 
					dependencies = [
 | 
				
			||||||
 "chumsky",
 | 
					 "chumsky",
 | 
				
			||||||
 "indexmap",
 | 
					 "indexmap",
 | 
				
			||||||
 | 
					 "indoc",
 | 
				
			||||||
 "itertools",
 | 
					 "itertools",
 | 
				
			||||||
 "miette",
 | 
					 "miette",
 | 
				
			||||||
 "pretty_assertions",
 | 
					 "pretty_assertions",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -21,4 +21,5 @@ uplc = { path = '../uplc', version = "0.0.25" }
 | 
				
			||||||
vec1 = "1.8.0"
 | 
					vec1 = "1.8.0"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[dev-dependencies]
 | 
					[dev-dependencies]
 | 
				
			||||||
 | 
					indoc = "1.0.7"
 | 
				
			||||||
pretty_assertions = "1.3.0"
 | 
					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! {
 | 
					        let var_parser = select! {
 | 
				
			||||||
            Token::Name { name } => name,
 | 
					            Token::Name { name } => name,
 | 
				
			||||||
            Token::UpName { name } => name,
 | 
					            Token::UpName { name } => name,
 | 
				
			||||||
| 
						 | 
					@ -756,23 +918,29 @@ pub fn expr_parser(
 | 
				
			||||||
            );
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let if_parser = just(Token::If)
 | 
					        let if_parser = just(Token::If)
 | 
				
			||||||
            .ignore_then(r.clone().then(block_parser.clone()).map_with_span(
 | 
					            .ignore_then(
 | 
				
			||||||
                |(condition, body), span| ast::IfBranch {
 | 
					                r.clone()
 | 
				
			||||||
                    condition,
 | 
					                    .then_ignore(just(Token::Then))
 | 
				
			||||||
                    body,
 | 
					                    .then(block_parser.clone())
 | 
				
			||||||
                    location: span,
 | 
					                    .map_with_span(|(condition, body), span| ast::IfBranch {
 | 
				
			||||||
                },
 | 
					                        condition,
 | 
				
			||||||
            ))
 | 
					                        body,
 | 
				
			||||||
 | 
					                        location: span,
 | 
				
			||||||
 | 
					                    }),
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
            .then(
 | 
					            .then(
 | 
				
			||||||
                just(Token::Else)
 | 
					                just(Token::Else)
 | 
				
			||||||
                    .ignore_then(just(Token::If))
 | 
					                    .ignore_then(just(Token::If))
 | 
				
			||||||
                    .ignore_then(r.clone().then(block_parser.clone()).map_with_span(
 | 
					                    .ignore_then(
 | 
				
			||||||
                        |(condition, body), span| ast::IfBranch {
 | 
					                        r.clone()
 | 
				
			||||||
                            condition,
 | 
					                            .then_ignore(just(Token::Then))
 | 
				
			||||||
                            body,
 | 
					                            .then(block_parser.clone())
 | 
				
			||||||
                            location: span,
 | 
					                            .map_with_span(|(condition, body), span| ast::IfBranch {
 | 
				
			||||||
                        },
 | 
					                                condition,
 | 
				
			||||||
                    ))
 | 
					                                body,
 | 
				
			||||||
 | 
					                                location: span,
 | 
				
			||||||
 | 
					                            }),
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
                    .repeated(),
 | 
					                    .repeated(),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            .then_ignore(just(Token::Else))
 | 
					            .then_ignore(just(Token::Else))
 | 
				
			||||||
| 
						 | 
					@ -792,6 +960,9 @@ pub fn expr_parser(
 | 
				
			||||||
        let expr_unit_parser = choice((
 | 
					        let expr_unit_parser = choice((
 | 
				
			||||||
            string_parser,
 | 
					            string_parser,
 | 
				
			||||||
            int_parser,
 | 
					            int_parser,
 | 
				
			||||||
 | 
					            record_update_parser,
 | 
				
			||||||
 | 
					            record_parser,
 | 
				
			||||||
 | 
					            field_access_constructor,
 | 
				
			||||||
            var_parser,
 | 
					            var_parser,
 | 
				
			||||||
            todo_parser,
 | 
					            todo_parser,
 | 
				
			||||||
            tuple,
 | 
					            tuple,
 | 
				
			||||||
| 
						 | 
					@ -819,42 +990,14 @@ pub fn expr_parser(
 | 
				
			||||||
        enum Chain {
 | 
					        enum Chain {
 | 
				
			||||||
            Call(Vec<ParserArg>, Span),
 | 
					            Call(Vec<ParserArg>, Span),
 | 
				
			||||||
            FieldAccess(String, Span),
 | 
					            FieldAccess(String, Span),
 | 
				
			||||||
            RecordUpdate(
 | 
					 | 
				
			||||||
                Box<(expr::UntypedExpr, Vec<ast::UntypedRecordUpdateArg>)>,
 | 
					 | 
				
			||||||
                Span,
 | 
					 | 
				
			||||||
            ),
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let field_access_parser = just(Token::Dot)
 | 
					        let field_access_parser = just(Token::Dot)
 | 
				
			||||||
            .ignore_then(select! {
 | 
					            .ignore_then(select! {
 | 
				
			||||||
                Token::Name { name } => name,
 | 
					                Token::Name { name } => name,
 | 
				
			||||||
                Token::UpName { name } => name
 | 
					 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
            .map_with_span(Chain::FieldAccess);
 | 
					            .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((
 | 
					        let call_parser = choice((
 | 
				
			||||||
            select! { Token::Name { name } => name }
 | 
					            select! { Token::Name { name } => name }
 | 
				
			||||||
                .then_ignore(just(Token::Colon))
 | 
					                .then_ignore(just(Token::Colon))
 | 
				
			||||||
| 
						 | 
					@ -881,7 +1024,7 @@ pub fn expr_parser(
 | 
				
			||||||
        .delimited_by(just(Token::LeftParen), just(Token::RightParen))
 | 
					        .delimited_by(just(Token::LeftParen), just(Token::RightParen))
 | 
				
			||||||
        .map_with_span(Chain::Call);
 | 
					        .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
 | 
					        let chained = expr_unit_parser
 | 
				
			||||||
            .then(chain.repeated())
 | 
					            .then(chain.repeated())
 | 
				
			||||||
| 
						 | 
					@ -941,24 +1084,6 @@ pub fn expr_parser(
 | 
				
			||||||
                    label,
 | 
					                    label,
 | 
				
			||||||
                    container: Box::new(e),
 | 
					                    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
 | 
					        // Negate
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -69,6 +69,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
 | 
				
			||||||
        "fn" => Token::Fn,
 | 
					        "fn" => Token::Fn,
 | 
				
			||||||
        "if" => Token::If,
 | 
					        "if" => Token::If,
 | 
				
			||||||
        "else" => Token::Else,
 | 
					        "else" => Token::Else,
 | 
				
			||||||
 | 
					        "then" => Token::Then,
 | 
				
			||||||
        "is" => Token::Is,
 | 
					        "is" => Token::Is,
 | 
				
			||||||
        "let" => Token::Let,
 | 
					        "let" => Token::Let,
 | 
				
			||||||
        "opaque" => Token::Opaque,
 | 
					        "opaque" => Token::Opaque,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,6 +72,7 @@ pub enum Token {
 | 
				
			||||||
    Trace,
 | 
					    Trace,
 | 
				
			||||||
    Type,
 | 
					    Type,
 | 
				
			||||||
    When,
 | 
					    When,
 | 
				
			||||||
 | 
					    Then,
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl fmt::Display for Token {
 | 
					impl fmt::Display for Token {
 | 
				
			||||||
| 
						 | 
					@ -145,6 +146,7 @@ impl fmt::Display for Token {
 | 
				
			||||||
            Token::Todo => "todo",
 | 
					            Token::Todo => "todo",
 | 
				
			||||||
            Token::Trace => "try",
 | 
					            Token::Trace => "try",
 | 
				
			||||||
            Token::Type => "type",
 | 
					            Token::Type => "type",
 | 
				
			||||||
 | 
					            Token::Then => "then",
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        write!(f, "\"{}\"", s)
 | 
					        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 {
 | 
					pub fn incrementor(counter: Int, target: Int) -> Int {
 | 
				
			||||||
  if counter == target {
 | 
					  if counter == target then {
 | 
				
			||||||
    target
 | 
					    target
 | 
				
			||||||
  } else {
 | 
					  } else {
 | 
				
			||||||
    incrementor(counter + 1, target)
 | 
					    incrementor(counter + 1, target)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue