Merge pull request #664 from aiken-lang/KtorZ/error-todo-parser
This commit is contained in:
commit
bb01ddd7b5
|
@ -540,7 +540,7 @@ pub const DEFAULT_TODO_STR: &str = "aiken::todo";
|
||||||
pub const DEFAULT_ERROR_STR: &str = "aiken::error";
|
pub const DEFAULT_ERROR_STR: &str = "aiken::error";
|
||||||
|
|
||||||
impl UntypedExpr {
|
impl UntypedExpr {
|
||||||
pub fn todo(location: Span, reason: Option<Self>) -> Self {
|
pub fn todo(reason: Option<Self>, location: Span) -> Self {
|
||||||
UntypedExpr::Trace {
|
UntypedExpr::Trace {
|
||||||
location,
|
location,
|
||||||
kind: TraceKind::Todo,
|
kind: TraceKind::Todo,
|
||||||
|
@ -552,7 +552,7 @@ impl UntypedExpr {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn error(location: Span, reason: Option<Self>) -> Self {
|
pub fn error(reason: Option<Self>, location: Span) -> Self {
|
||||||
UntypedExpr::Trace {
|
UntypedExpr::Trace {
|
||||||
location,
|
location,
|
||||||
kind: TraceKind::Error,
|
kind: TraceKind::Error,
|
||||||
|
|
|
@ -27,7 +27,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
|
||||||
|((((public, name), (arguments, args_span)), return_annotation), body), span| {
|
|((((public, name), (arguments, args_span)), return_annotation), body), span| {
|
||||||
ast::UntypedDefinition::Fn(ast::Function {
|
ast::UntypedDefinition::Fn(ast::Function {
|
||||||
arguments,
|
arguments,
|
||||||
body: body.unwrap_or_else(|| UntypedExpr::todo(span, None)),
|
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
|
||||||
doc: None,
|
doc: None,
|
||||||
location: ast::Span {
|
location: ast::Span {
|
||||||
start: span.start,
|
start: span.start,
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
|
||||||
.map_with_span(|(((fail, name), span_end), body), span| {
|
.map_with_span(|(((fail, name), span_end), body), span| {
|
||||||
ast::UntypedDefinition::Test(ast::Function {
|
ast::UntypedDefinition::Test(ast::Function {
|
||||||
arguments: vec![],
|
arguments: vec![],
|
||||||
body: body.unwrap_or_else(|| UntypedExpr::todo(span, None)),
|
body: body.unwrap_or_else(|| UntypedExpr::todo(None, span)),
|
||||||
doc: None,
|
doc: None,
|
||||||
location: span_end,
|
location: span_end,
|
||||||
end_position: span.end - 1,
|
end_position: span.end - 1,
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
expr::UntypedExpr,
|
||||||
|
parser::{
|
||||||
|
error::ParseError,
|
||||||
|
expr::{block::parser as block, string},
|
||||||
|
token::Token,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parser(
|
||||||
|
sequence: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||||
|
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||||
|
let message = || choice((string::hybrid(), block(sequence.clone())));
|
||||||
|
choice((
|
||||||
|
just(Token::Todo)
|
||||||
|
.ignore_then(message().or_not())
|
||||||
|
.map_with_span(UntypedExpr::todo),
|
||||||
|
just(Token::ErrorTerm)
|
||||||
|
.ignore_then(message().or_not())
|
||||||
|
.map_with_span(UntypedExpr::error),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::assert_expr;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_basic() {
|
||||||
|
assert_expr!(
|
||||||
|
r#"
|
||||||
|
error @"foo"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_sugar() {
|
||||||
|
assert_expr!(
|
||||||
|
r#"
|
||||||
|
error "foo"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn todo_basic() {
|
||||||
|
assert_expr!(
|
||||||
|
r#"
|
||||||
|
todo @"foo"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn todo_sugar() {
|
||||||
|
assert_expr!(
|
||||||
|
r#"
|
||||||
|
todo "foo"
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,6 +7,7 @@ pub mod assignment;
|
||||||
mod block;
|
mod block;
|
||||||
pub(crate) mod bytearray;
|
pub(crate) mod bytearray;
|
||||||
mod chained;
|
mod chained;
|
||||||
|
mod error_todo;
|
||||||
mod if_else;
|
mod if_else;
|
||||||
mod int;
|
mod int;
|
||||||
mod list;
|
mod list;
|
||||||
|
@ -22,6 +23,7 @@ pub use anonymous_function::parser as anonymous_function;
|
||||||
pub use block::parser as block;
|
pub use block::parser as block;
|
||||||
pub use bytearray::parser as bytearray;
|
pub use bytearray::parser as bytearray;
|
||||||
pub use chained::parser as chained;
|
pub use chained::parser as chained;
|
||||||
|
pub use error_todo::parser as error_todo;
|
||||||
pub use if_else::parser as if_else;
|
pub use if_else::parser as if_else;
|
||||||
pub use int::parser as int;
|
pub use int::parser as int;
|
||||||
pub use list::parser as list;
|
pub use list::parser as list;
|
||||||
|
@ -40,159 +42,169 @@ pub fn parser(
|
||||||
sequence: Recursive<'_, Token, UntypedExpr, ParseError>,
|
sequence: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||||
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
|
||||||
recursive(|expression| {
|
recursive(|expression| {
|
||||||
let chained_debugged = chained(sequence, expression)
|
choice((
|
||||||
.then(just(Token::Question).or_not())
|
error_todo(sequence.clone()),
|
||||||
.map_with_span(|(value, token), location| match token {
|
pure_expression(sequence, expression),
|
||||||
Some(_) => UntypedExpr::TraceIfFalse {
|
))
|
||||||
value: Box::new(value),
|
|
||||||
location,
|
|
||||||
},
|
|
||||||
None => value,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Negate
|
|
||||||
let op = choice((
|
|
||||||
just(Token::Bang).to(ast::UnOp::Not),
|
|
||||||
just(Token::Minus)
|
|
||||||
// NOTE: Prevent conflict with usage for '-' as a standalone binary op.
|
|
||||||
// This will make '-' parse when used as standalone binop in a function call.
|
|
||||||
// For example:
|
|
||||||
//
|
|
||||||
// foo(a, -, b)
|
|
||||||
//
|
|
||||||
// but it'll fail in a let-binding:
|
|
||||||
//
|
|
||||||
// let foo = -
|
|
||||||
//
|
|
||||||
// which seems acceptable.
|
|
||||||
.then_ignore(just(Token::Comma).not().rewind())
|
|
||||||
.to(ast::UnOp::Negate),
|
|
||||||
));
|
|
||||||
|
|
||||||
let unary = op
|
|
||||||
.map_with_span(|op, span| (op, span))
|
|
||||||
.repeated()
|
|
||||||
.then(chained_debugged)
|
|
||||||
.foldr(|(un_op, span), value| UntypedExpr::UnOp {
|
|
||||||
op: un_op,
|
|
||||||
location: span.union(value.location()),
|
|
||||||
value: Box::new(value),
|
|
||||||
})
|
|
||||||
.boxed();
|
|
||||||
|
|
||||||
// Product
|
|
||||||
let op = choice((
|
|
||||||
just(Token::Star).to(ast::BinOp::MultInt),
|
|
||||||
just(Token::Slash).to(ast::BinOp::DivInt),
|
|
||||||
just(Token::Percent).to(ast::BinOp::ModInt),
|
|
||||||
));
|
|
||||||
|
|
||||||
let product = unary
|
|
||||||
.clone()
|
|
||||||
.then(op.then(unary).repeated())
|
|
||||||
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
|
||||||
location: a.location().union(b.location()),
|
|
||||||
name: op,
|
|
||||||
left: Box::new(a),
|
|
||||||
right: Box::new(b),
|
|
||||||
})
|
|
||||||
.boxed();
|
|
||||||
|
|
||||||
// Sum
|
|
||||||
let op = choice((
|
|
||||||
just(Token::Plus).to(ast::BinOp::AddInt),
|
|
||||||
just(Token::Minus).to(ast::BinOp::SubInt),
|
|
||||||
));
|
|
||||||
|
|
||||||
let sum = product
|
|
||||||
.clone()
|
|
||||||
.then(op.then(product).repeated())
|
|
||||||
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
|
||||||
location: a.location().union(b.location()),
|
|
||||||
name: op,
|
|
||||||
left: Box::new(a),
|
|
||||||
right: Box::new(b),
|
|
||||||
})
|
|
||||||
.boxed();
|
|
||||||
|
|
||||||
// Comparison
|
|
||||||
let op = choice((
|
|
||||||
just(Token::EqualEqual).to(ast::BinOp::Eq),
|
|
||||||
just(Token::NotEqual).to(ast::BinOp::NotEq),
|
|
||||||
just(Token::Less).to(ast::BinOp::LtInt),
|
|
||||||
just(Token::Greater).to(ast::BinOp::GtInt),
|
|
||||||
just(Token::LessEqual).to(ast::BinOp::LtEqInt),
|
|
||||||
just(Token::GreaterEqual).to(ast::BinOp::GtEqInt),
|
|
||||||
));
|
|
||||||
|
|
||||||
let comparison = sum
|
|
||||||
.clone()
|
|
||||||
.then(op.then(sum).repeated())
|
|
||||||
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
|
||||||
location: a.location().union(b.location()),
|
|
||||||
name: op,
|
|
||||||
left: Box::new(a),
|
|
||||||
right: Box::new(b),
|
|
||||||
})
|
|
||||||
.boxed();
|
|
||||||
|
|
||||||
// Conjunction
|
|
||||||
let op = just(Token::AmperAmper).to(ast::BinOp::And);
|
|
||||||
let conjunction = comparison
|
|
||||||
.clone()
|
|
||||||
.then(op.then(comparison).repeated())
|
|
||||||
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
|
||||||
location: a.location().union(b.location()),
|
|
||||||
name: op,
|
|
||||||
left: Box::new(a),
|
|
||||||
right: Box::new(b),
|
|
||||||
})
|
|
||||||
.boxed();
|
|
||||||
|
|
||||||
// Disjunction
|
|
||||||
let op = just(Token::VbarVbar).to(ast::BinOp::Or);
|
|
||||||
let disjunction = conjunction
|
|
||||||
.clone()
|
|
||||||
.then(op.then(conjunction).repeated())
|
|
||||||
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
|
||||||
location: a.location().union(b.location()),
|
|
||||||
name: op,
|
|
||||||
left: Box::new(a),
|
|
||||||
right: Box::new(b),
|
|
||||||
})
|
|
||||||
.boxed();
|
|
||||||
|
|
||||||
// Pipeline
|
|
||||||
disjunction
|
|
||||||
.clone()
|
|
||||||
.then(
|
|
||||||
choice((just(Token::Pipe), just(Token::NewLinePipe)))
|
|
||||||
.then(disjunction)
|
|
||||||
.repeated(),
|
|
||||||
)
|
|
||||||
.foldl(|l, (pipe, r)| {
|
|
||||||
if let UntypedExpr::PipeLine {
|
|
||||||
mut expressions,
|
|
||||||
one_liner,
|
|
||||||
} = l
|
|
||||||
{
|
|
||||||
expressions.push(r);
|
|
||||||
return UntypedExpr::PipeLine {
|
|
||||||
expressions,
|
|
||||||
one_liner,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut expressions = Vec1::new(l);
|
|
||||||
expressions.push(r);
|
|
||||||
UntypedExpr::PipeLine {
|
|
||||||
expressions,
|
|
||||||
one_liner: pipe != Token::NewLinePipe,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pure_expression<'a>(
|
||||||
|
sequence: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
|
expression: Recursive<'a, Token, UntypedExpr, ParseError>,
|
||||||
|
) -> impl Parser<Token, UntypedExpr, Error = ParseError> + 'a {
|
||||||
|
let chained_debugged = chained(sequence, expression)
|
||||||
|
.then(just(Token::Question).or_not())
|
||||||
|
.map_with_span(|(value, token), location| match token {
|
||||||
|
Some(_) => UntypedExpr::TraceIfFalse {
|
||||||
|
value: Box::new(value),
|
||||||
|
location,
|
||||||
|
},
|
||||||
|
None => value,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Negate
|
||||||
|
let op = choice((
|
||||||
|
just(Token::Bang).to(ast::UnOp::Not),
|
||||||
|
just(Token::Minus)
|
||||||
|
// NOTE: Prevent conflict with usage for '-' as a standalone binary op.
|
||||||
|
// This will make '-' parse when used as standalone binop in a function call.
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// foo(a, -, b)
|
||||||
|
//
|
||||||
|
// but it'll fail in a let-binding:
|
||||||
|
//
|
||||||
|
// let foo = -
|
||||||
|
//
|
||||||
|
// which seems acceptable.
|
||||||
|
.then_ignore(just(Token::Comma).not().rewind())
|
||||||
|
.to(ast::UnOp::Negate),
|
||||||
|
));
|
||||||
|
|
||||||
|
let unary = op
|
||||||
|
.map_with_span(|op, span| (op, span))
|
||||||
|
.repeated()
|
||||||
|
.then(chained_debugged)
|
||||||
|
.foldr(|(un_op, span), value| UntypedExpr::UnOp {
|
||||||
|
op: un_op,
|
||||||
|
location: span.union(value.location()),
|
||||||
|
value: Box::new(value),
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
// Product
|
||||||
|
let op = choice((
|
||||||
|
just(Token::Star).to(ast::BinOp::MultInt),
|
||||||
|
just(Token::Slash).to(ast::BinOp::DivInt),
|
||||||
|
just(Token::Percent).to(ast::BinOp::ModInt),
|
||||||
|
));
|
||||||
|
|
||||||
|
let product = unary
|
||||||
|
.clone()
|
||||||
|
.then(op.then(unary).repeated())
|
||||||
|
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
||||||
|
location: a.location().union(b.location()),
|
||||||
|
name: op,
|
||||||
|
left: Box::new(a),
|
||||||
|
right: Box::new(b),
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
// Sum
|
||||||
|
let op = choice((
|
||||||
|
just(Token::Plus).to(ast::BinOp::AddInt),
|
||||||
|
just(Token::Minus).to(ast::BinOp::SubInt),
|
||||||
|
));
|
||||||
|
|
||||||
|
let sum = product
|
||||||
|
.clone()
|
||||||
|
.then(op.then(product).repeated())
|
||||||
|
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
||||||
|
location: a.location().union(b.location()),
|
||||||
|
name: op,
|
||||||
|
left: Box::new(a),
|
||||||
|
right: Box::new(b),
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
// Comparison
|
||||||
|
let op = choice((
|
||||||
|
just(Token::EqualEqual).to(ast::BinOp::Eq),
|
||||||
|
just(Token::NotEqual).to(ast::BinOp::NotEq),
|
||||||
|
just(Token::Less).to(ast::BinOp::LtInt),
|
||||||
|
just(Token::Greater).to(ast::BinOp::GtInt),
|
||||||
|
just(Token::LessEqual).to(ast::BinOp::LtEqInt),
|
||||||
|
just(Token::GreaterEqual).to(ast::BinOp::GtEqInt),
|
||||||
|
));
|
||||||
|
|
||||||
|
let comparison = sum
|
||||||
|
.clone()
|
||||||
|
.then(op.then(sum).repeated())
|
||||||
|
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
||||||
|
location: a.location().union(b.location()),
|
||||||
|
name: op,
|
||||||
|
left: Box::new(a),
|
||||||
|
right: Box::new(b),
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
// Conjunction
|
||||||
|
let op = just(Token::AmperAmper).to(ast::BinOp::And);
|
||||||
|
let conjunction = comparison
|
||||||
|
.clone()
|
||||||
|
.then(op.then(comparison).repeated())
|
||||||
|
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
||||||
|
location: a.location().union(b.location()),
|
||||||
|
name: op,
|
||||||
|
left: Box::new(a),
|
||||||
|
right: Box::new(b),
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
// Disjunction
|
||||||
|
let op = just(Token::VbarVbar).to(ast::BinOp::Or);
|
||||||
|
let disjunction = conjunction
|
||||||
|
.clone()
|
||||||
|
.then(op.then(conjunction).repeated())
|
||||||
|
.foldl(|a, (op, b)| UntypedExpr::BinOp {
|
||||||
|
location: a.location().union(b.location()),
|
||||||
|
name: op,
|
||||||
|
left: Box::new(a),
|
||||||
|
right: Box::new(b),
|
||||||
|
})
|
||||||
|
.boxed();
|
||||||
|
|
||||||
|
// Pipeline
|
||||||
|
disjunction
|
||||||
|
.clone()
|
||||||
|
.then(
|
||||||
|
choice((just(Token::Pipe), just(Token::NewLinePipe)))
|
||||||
|
.then(disjunction)
|
||||||
|
.repeated(),
|
||||||
|
)
|
||||||
|
.foldl(|l, (pipe, r)| {
|
||||||
|
if let UntypedExpr::PipeLine {
|
||||||
|
mut expressions,
|
||||||
|
one_liner,
|
||||||
|
} = l
|
||||||
|
{
|
||||||
|
expressions.push(r);
|
||||||
|
return UntypedExpr::PipeLine {
|
||||||
|
expressions,
|
||||||
|
one_liner,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut expressions = Vec1::new(l);
|
||||||
|
expressions.push(r);
|
||||||
|
UntypedExpr::PipeLine {
|
||||||
|
expressions,
|
||||||
|
one_liner: pipe != Token::NewLinePipe,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::assert_expr;
|
use crate::assert_expr;
|
||||||
|
|
|
@ -3,33 +3,27 @@ use chumsky::prelude::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::TraceKind,
|
ast::TraceKind,
|
||||||
expr::UntypedExpr,
|
expr::UntypedExpr,
|
||||||
parser::{error::ParseError, token::Token},
|
parser::{
|
||||||
|
error::ParseError,
|
||||||
|
expr::{block::parser as block, string},
|
||||||
|
token::Token,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
|
pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
|
||||||
recursive(|expression| {
|
recursive(|sequence| {
|
||||||
choice((
|
choice((
|
||||||
just(Token::Trace)
|
just(Token::Trace)
|
||||||
.ignore_then(super::parser(expression.clone()))
|
.ignore_then(choice((string::hybrid(), block(sequence.clone()))))
|
||||||
.then(expression.clone())
|
.then(sequence.clone())
|
||||||
.map_with_span(|(text, then_), span| UntypedExpr::Trace {
|
.map_with_span(|(text, then_), span| UntypedExpr::Trace {
|
||||||
kind: TraceKind::Trace,
|
kind: TraceKind::Trace,
|
||||||
location: span,
|
location: span,
|
||||||
then: Box::new(then_),
|
then: Box::new(then_),
|
||||||
text: Box::new(super::string::flexible(text)),
|
text: Box::new(text),
|
||||||
}),
|
}),
|
||||||
just(Token::ErrorTerm)
|
super::parser(sequence.clone())
|
||||||
.ignore_then(super::parser(expression.clone()).or_not())
|
.then(sequence.repeated())
|
||||||
.map_with_span(|reason, span| {
|
|
||||||
UntypedExpr::error(span, reason.map(super::string::flexible))
|
|
||||||
}),
|
|
||||||
just(Token::Todo)
|
|
||||||
.ignore_then(super::parser(expression.clone()).or_not())
|
|
||||||
.map_with_span(|reason, span| {
|
|
||||||
UntypedExpr::todo(span, reason.map(super::string::flexible))
|
|
||||||
}),
|
|
||||||
super::parser(expression.clone())
|
|
||||||
.then(expression.repeated())
|
|
||||||
.foldl(|current, next| current.append_in_sequence(next)),
|
.foldl(|current, next| current.append_in_sequence(next)),
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/expr/error_todo.rs
|
||||||
|
description: "Code:\n\nerror @\"foo\"\n"
|
||||||
|
---
|
||||||
|
Trace {
|
||||||
|
kind: Error,
|
||||||
|
location: 0..12,
|
||||||
|
then: ErrorTerm {
|
||||||
|
location: 0..12,
|
||||||
|
},
|
||||||
|
text: String {
|
||||||
|
location: 6..12,
|
||||||
|
value: "foo",
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/expr/error_todo.rs
|
||||||
|
description: "Code:\n\nerror \"foo\"\n"
|
||||||
|
---
|
||||||
|
Trace {
|
||||||
|
kind: Error,
|
||||||
|
location: 0..11,
|
||||||
|
then: ErrorTerm {
|
||||||
|
location: 0..11,
|
||||||
|
},
|
||||||
|
text: String {
|
||||||
|
location: 6..11,
|
||||||
|
value: "foo",
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/expr/error_todo.rs
|
||||||
|
description: "Code:\n\ntodo @\"foo\"\n"
|
||||||
|
---
|
||||||
|
Trace {
|
||||||
|
kind: Todo,
|
||||||
|
location: 0..11,
|
||||||
|
then: ErrorTerm {
|
||||||
|
location: 0..11,
|
||||||
|
},
|
||||||
|
text: String {
|
||||||
|
location: 5..11,
|
||||||
|
value: "foo",
|
||||||
|
},
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/expr/error_todo.rs
|
||||||
|
description: "Code:\n\ntodo \"foo\"\n"
|
||||||
|
---
|
||||||
|
Trace {
|
||||||
|
kind: Todo,
|
||||||
|
location: 0..10,
|
||||||
|
then: ErrorTerm {
|
||||||
|
location: 0..10,
|
||||||
|
},
|
||||||
|
text: String {
|
||||||
|
location: 5..10,
|
||||||
|
value: "foo",
|
||||||
|
},
|
||||||
|
}
|
|
@ -1,9 +1,11 @@
|
||||||
use chumsky::prelude::*;
|
use chumsky::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast,
|
|
||||||
expr::UntypedExpr,
|
expr::UntypedExpr,
|
||||||
parser::{error::ParseError, literal::string::parser as string, token::Token},
|
parser::{
|
||||||
|
error::ParseError, literal::bytearray::utf8_string, literal::string::parser as string,
|
||||||
|
token::Token,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
|
pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
|
||||||
|
@ -13,23 +15,15 @@ pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Interpret bytearray string literals written as utf-8 strings, as strings.
|
pub fn hybrid() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
|
||||||
///
|
choice((
|
||||||
/// This is mostly convenient so that todo & error works with either @"..." or plain "...".
|
string(),
|
||||||
/// In this particular context, there's actually no ambiguity about the right-hand-side, so
|
utf8_string().map(|(_, bytes)| String::from_utf8(bytes).unwrap()),
|
||||||
/// we can provide this syntactic sugar.
|
))
|
||||||
pub fn flexible(expr: UntypedExpr) -> UntypedExpr {
|
.map_with_span(|value, span| UntypedExpr::String {
|
||||||
match expr {
|
location: span,
|
||||||
UntypedExpr::ByteArray {
|
value,
|
||||||
preferred_format: ast::ByteArrayFormatPreference::Utf8String,
|
})
|
||||||
bytes,
|
|
||||||
location,
|
|
||||||
} => UntypedExpr::String {
|
|
||||||
location,
|
|
||||||
value: String::from_utf8(bytes).unwrap(),
|
|
||||||
},
|
|
||||||
_ => expr,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -4,13 +4,13 @@ use vec1::vec1;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast,
|
ast,
|
||||||
expr::UntypedExpr,
|
expr::UntypedExpr,
|
||||||
parser::{error::ParseError, expr::string::flexible, pattern, token::Token},
|
parser::{error::ParseError, pattern, token::Token},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::guard;
|
use super::guard;
|
||||||
|
|
||||||
pub fn parser(
|
pub fn parser(
|
||||||
r: Recursive<'_, Token, UntypedExpr, ParseError>,
|
expression: Recursive<'_, Token, UntypedExpr, ParseError>,
|
||||||
) -> impl Parser<Token, ast::UntypedClause, Error = ParseError> + '_ {
|
) -> impl Parser<Token, ast::UntypedClause, Error = ParseError> + '_ {
|
||||||
pattern()
|
pattern()
|
||||||
.then(just(Token::Vbar).ignore_then(pattern()).repeated().or_not())
|
.then(just(Token::Vbar).ignore_then(pattern()).repeated().or_not())
|
||||||
|
@ -27,23 +27,7 @@ pub fn parser(
|
||||||
}),
|
}),
|
||||||
)))
|
)))
|
||||||
// TODO: add hint "Did you mean to wrap a multi line clause in curly braces?"
|
// TODO: add hint "Did you mean to wrap a multi line clause in curly braces?"
|
||||||
.then(choice((
|
.then(expression)
|
||||||
r.clone(),
|
|
||||||
just(Token::Todo)
|
|
||||||
.ignore_then(
|
|
||||||
r.clone()
|
|
||||||
.then_ignore(one_of(Token::RArrow).not().rewind())
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.map_with_span(|reason, span| UntypedExpr::todo(span, reason.map(flexible))),
|
|
||||||
just(Token::ErrorTerm)
|
|
||||||
.ignore_then(
|
|
||||||
r.clone()
|
|
||||||
.then_ignore(just(Token::RArrow).not().rewind())
|
|
||||||
.or_not(),
|
|
||||||
)
|
|
||||||
.map_with_span(|reason, span| UntypedExpr::error(span, reason.map(flexible))),
|
|
||||||
)))
|
|
||||||
.map_with_span(
|
.map_with_span(
|
||||||
|(((pattern, alternative_patterns_opt), guard), then), span| {
|
|(((pattern, alternative_patterns_opt), guard), then), span| {
|
||||||
let mut patterns = vec1![pattern];
|
let mut patterns = vec1![pattern];
|
||||||
|
@ -57,3 +41,43 @@ pub fn parser(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use crate::assert_expr;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn todo_clause() {
|
||||||
|
assert_expr!(
|
||||||
|
r#"
|
||||||
|
when val is {
|
||||||
|
Bar1{..} -> True
|
||||||
|
Bar2{..} -> todo @"unimplemented"
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn error_single_clause_no_message() {
|
||||||
|
assert_expr!(
|
||||||
|
r#"
|
||||||
|
when val is {
|
||||||
|
Bar1{..} -> error
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn todo_double_clause_no_message() {
|
||||||
|
assert_expr!(
|
||||||
|
r#"
|
||||||
|
when val is {
|
||||||
|
Bar1{..} -> todo
|
||||||
|
Bar2{..} -> todo
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/expr/when/clause.rs
|
||||||
|
description: "Code:\n\nwhen val is {\n Bar1{..} -> error\n}\n"
|
||||||
|
---
|
||||||
|
When {
|
||||||
|
location: 0..35,
|
||||||
|
subject: Var {
|
||||||
|
location: 5..8,
|
||||||
|
name: "val",
|
||||||
|
},
|
||||||
|
clauses: [
|
||||||
|
UntypedClause {
|
||||||
|
location: 16..33,
|
||||||
|
patterns: [
|
||||||
|
Constructor {
|
||||||
|
is_record: true,
|
||||||
|
location: 16..24,
|
||||||
|
name: "Bar1",
|
||||||
|
arguments: [],
|
||||||
|
module: None,
|
||||||
|
constructor: (),
|
||||||
|
with_spread: true,
|
||||||
|
tipo: (),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
guard: None,
|
||||||
|
then: Trace {
|
||||||
|
kind: Error,
|
||||||
|
location: 28..33,
|
||||||
|
then: ErrorTerm {
|
||||||
|
location: 28..33,
|
||||||
|
},
|
||||||
|
text: String {
|
||||||
|
location: 28..33,
|
||||||
|
value: "aiken::error",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/expr/when/clause.rs
|
||||||
|
description: "Code:\n\nwhen val is {\n Bar1{..} -> True\n Bar2{..} -> todo @\"unimplemented\"\n}\n"
|
||||||
|
---
|
||||||
|
When {
|
||||||
|
location: 0..70,
|
||||||
|
subject: Var {
|
||||||
|
location: 5..8,
|
||||||
|
name: "val",
|
||||||
|
},
|
||||||
|
clauses: [
|
||||||
|
UntypedClause {
|
||||||
|
location: 16..32,
|
||||||
|
patterns: [
|
||||||
|
Constructor {
|
||||||
|
is_record: true,
|
||||||
|
location: 16..24,
|
||||||
|
name: "Bar1",
|
||||||
|
arguments: [],
|
||||||
|
module: None,
|
||||||
|
constructor: (),
|
||||||
|
with_spread: true,
|
||||||
|
tipo: (),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
guard: None,
|
||||||
|
then: Var {
|
||||||
|
location: 28..32,
|
||||||
|
name: "True",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UntypedClause {
|
||||||
|
location: 35..68,
|
||||||
|
patterns: [
|
||||||
|
Constructor {
|
||||||
|
is_record: true,
|
||||||
|
location: 35..43,
|
||||||
|
name: "Bar2",
|
||||||
|
arguments: [],
|
||||||
|
module: None,
|
||||||
|
constructor: (),
|
||||||
|
with_spread: true,
|
||||||
|
tipo: (),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
guard: None,
|
||||||
|
then: Trace {
|
||||||
|
kind: Todo,
|
||||||
|
location: 47..68,
|
||||||
|
then: ErrorTerm {
|
||||||
|
location: 47..68,
|
||||||
|
},
|
||||||
|
text: String {
|
||||||
|
location: 52..68,
|
||||||
|
value: "unimplemented",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
---
|
||||||
|
source: crates/aiken-lang/src/parser/expr/when/clause.rs
|
||||||
|
description: "Code:\n\nwhen val is {\n Bar1{..} -> todo\n Bar2{..} -> todo\n}\n"
|
||||||
|
---
|
||||||
|
When {
|
||||||
|
location: 0..53,
|
||||||
|
subject: Var {
|
||||||
|
location: 5..8,
|
||||||
|
name: "val",
|
||||||
|
},
|
||||||
|
clauses: [
|
||||||
|
UntypedClause {
|
||||||
|
location: 16..32,
|
||||||
|
patterns: [
|
||||||
|
Constructor {
|
||||||
|
is_record: true,
|
||||||
|
location: 16..24,
|
||||||
|
name: "Bar1",
|
||||||
|
arguments: [],
|
||||||
|
module: None,
|
||||||
|
constructor: (),
|
||||||
|
with_spread: true,
|
||||||
|
tipo: (),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
guard: None,
|
||||||
|
then: Trace {
|
||||||
|
kind: Todo,
|
||||||
|
location: 28..32,
|
||||||
|
then: ErrorTerm {
|
||||||
|
location: 28..32,
|
||||||
|
},
|
||||||
|
text: String {
|
||||||
|
location: 28..32,
|
||||||
|
value: "aiken::todo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UntypedClause {
|
||||||
|
location: 35..51,
|
||||||
|
patterns: [
|
||||||
|
Constructor {
|
||||||
|
is_record: true,
|
||||||
|
location: 35..43,
|
||||||
|
name: "Bar2",
|
||||||
|
arguments: [],
|
||||||
|
module: None,
|
||||||
|
constructor: (),
|
||||||
|
with_spread: true,
|
||||||
|
tipo: (),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
guard: None,
|
||||||
|
then: Trace {
|
||||||
|
kind: Todo,
|
||||||
|
location: 47..51,
|
||||||
|
then: ErrorTerm {
|
||||||
|
location: 47..51,
|
||||||
|
},
|
||||||
|
text: String {
|
||||||
|
location: 47..51,
|
||||||
|
value: "aiken::todo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
Loading…
Reference in New Issue