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