feat: finish moving definitions and start exprs

This commit is contained in:
rvcas 2023-06-21 16:54:13 -04:00
parent fc580d4fa0
commit 3339d41fdd
No known key found for this signature in database
GPG Key ID: C09B64E263F7D68C
12 changed files with 417 additions and 387 deletions

View File

@ -1,19 +1,23 @@
mod annotation;
pub mod definitions; pub mod definitions;
pub mod error; pub mod error;
mod expr;
pub mod extra; pub mod extra;
pub mod lexer; pub mod lexer;
mod module; mod module;
pub mod token; pub mod token;
mod utils; mod utils;
pub use annotation::parser as annotation;
use crate::{ use crate::{
ast::{self, BinOp, ByteArrayFormatPreference, Span, TraceKind, UnOp, CAPTURE_VARIABLE}, ast::{self, BinOp, ByteArrayFormatPreference, Span, UnOp, CAPTURE_VARIABLE},
expr, expr::{FnStyle, UntypedExpr},
}; };
use chumsky::{chain::Chain, prelude::*}; use chumsky::{chain::Chain, prelude::*};
use error::ParseError; use error::ParseError;
use extra::ModuleExtra; use extra::ModuleExtra;
use token::{Base, Token}; use token::Token;
use vec1::{vec1, Vec1}; use vec1::{vec1, Vec1};
pub fn module( pub fn module(
@ -74,7 +78,9 @@ pub fn module(
Token::NewLine => None, Token::NewLine => None,
_ => Some((token, *span)), _ => Some((token, *span)),
}; };
previous_is_newline = current_is_newline; previous_is_newline = current_is_newline;
result result
}); });
@ -92,152 +98,6 @@ pub fn module(
Ok((module, extra)) Ok((module, extra))
} }
fn constant_value_parser() -> impl Parser<Token, ast::Constant, Error = ParseError> {
let constant_string_parser =
select! {Token::String {value} => value}.map_with_span(|value, span| {
ast::Constant::String {
location: span,
value,
}
});
let constant_int_parser =
select! {Token::Int {value, base} => (value, base)}.map_with_span(|(value, base), span| {
ast::Constant::Int {
location: span,
value,
base,
}
});
let constant_bytearray_parser =
bytearray_parser().map_with_span(|(preferred_format, bytes), span| {
ast::Constant::ByteArray {
location: span,
bytes,
preferred_format,
}
});
choice((
constant_string_parser,
constant_int_parser,
constant_bytearray_parser,
))
}
pub fn bytearray_parser(
) -> impl Parser<Token, (ByteArrayFormatPreference, Vec<u8>), Error = ParseError> {
let bytearray_list_parser = just(Token::Hash)
.ignore_then(
select! {Token::Int {value, base, ..} => (value, base)}
.validate(|(value, base), span, emit| {
let byte: u8 = match value.parse() {
Ok(b) => b,
Err(_) => {
emit(ParseError::expected_input_found(
span,
None,
Some(error::Pattern::Byte),
));
0
}
};
(byte, base)
})
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftSquare), just(Token::RightSquare)),
)
.validate(|bytes, span, emit| {
let base = bytes.iter().fold(Ok(None), |acc, (_, base)| match acc {
Ok(None) => Ok(Some(base)),
Ok(Some(previous_base)) if previous_base == base => Ok(Some(base)),
_ => Err(()),
});
let base = match base {
Err(()) => {
emit(ParseError::hybrid_notation_in_bytearray(span));
Base::Decimal {
numeric_underscore: false,
}
}
Ok(None) => Base::Decimal {
numeric_underscore: false,
},
Ok(Some(base)) => *base,
};
(bytes.into_iter().map(|(b, _)| b).collect::<Vec<u8>>(), base)
})
.map(|(bytes, base)| (ByteArrayFormatPreference::ArrayOfBytes(base), bytes));
let bytearray_hexstring_parser =
just(Token::Hash)
.ignore_then(select! {Token::ByteString {value} => value}.validate(
|value, span, emit| match hex::decode(value) {
Ok(bytes) => bytes,
Err(_) => {
emit(ParseError::malformed_base16_string_literal(span));
vec![]
}
},
))
.map(|token| (ByteArrayFormatPreference::HexadecimalString, token));
let bytearray_utf8_parser = select! {Token::ByteString {value} => value.into_bytes() }
.map(|token| (ByteArrayFormatPreference::Utf8String, token));
choice((
bytearray_list_parser,
bytearray_hexstring_parser,
bytearray_utf8_parser,
))
}
pub fn fn_param_parser(
is_validator_param: bool,
) -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
choice((
select! {Token::Name {name} => name}
.then(select! {Token::DiscardName {name} => name})
.map_with_span(|(label, name), span| ast::ArgName::Discarded {
label,
name,
location: span,
}),
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::ArgName::Discarded {
label: name.clone(),
name,
location: span,
}
}),
select! {Token::Name {name} => name}
.then(select! {Token::Name {name} => name})
.map_with_span(move |(label, name), span| ast::ArgName::Named {
label,
name,
location: span,
is_validator_param,
}),
select! {Token::Name {name} => name}.map_with_span(move |name, span| ast::ArgName::Named {
label: name.clone(),
name,
location: span,
is_validator_param,
}),
))
.then(just(Token::Colon).ignore_then(type_parser()).or_not())
.map_with_span(|(arg_name, annotation), span| ast::Arg {
location: span,
annotation,
tipo: (),
arg_name,
})
}
pub fn anon_fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = ParseError> { pub fn anon_fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
// TODO: return a better error when a label is provided `UnexpectedLabel` // TODO: return a better error when a label is provided `UnexpectedLabel`
choice(( choice((
@ -255,7 +115,7 @@ pub fn anon_fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = Par
is_validator_param: false, is_validator_param: false,
}), }),
)) ))
.then(just(Token::Colon).ignore_then(type_parser()).or_not()) .then(just(Token::Colon).ignore_then(annotation()).or_not())
.map_with_span(|(arg_name, annotation), span| ast::Arg { .map_with_span(|(arg_name, annotation), span| ast::Arg {
location: span, location: span,
annotation, annotation,
@ -269,13 +129,13 @@ pub fn anon_fn_param_parser() -> impl Parser<Token, ast::UntypedArg, Error = Par
// This is mostly convenient so that todo & error works with either @"..." or plain "...". // This is mostly convenient so that todo & error works with either @"..." or plain "...".
// In this particular context, there's actually no ambiguity about the right-hand-side, so // In this particular context, there's actually no ambiguity about the right-hand-side, so
// we can provide this syntactic sugar. // we can provide this syntactic sugar.
fn flexible_string_literal(expr: expr::UntypedExpr) -> expr::UntypedExpr { fn flexible_string_literal(expr: UntypedExpr) -> UntypedExpr {
match expr { match expr {
expr::UntypedExpr::ByteArray { UntypedExpr::ByteArray {
preferred_format: ByteArrayFormatPreference::Utf8String, preferred_format: ByteArrayFormatPreference::Utf8String,
bytes, bytes,
location, location,
} => expr::UntypedExpr::String { } => UntypedExpr::String {
location, location,
value: String::from_utf8(bytes).unwrap(), value: String::from_utf8(bytes).unwrap(),
}, },
@ -283,49 +143,20 @@ fn flexible_string_literal(expr: expr::UntypedExpr) -> expr::UntypedExpr {
} }
} }
pub fn expr_seq_parser() -> impl Parser<Token, expr::UntypedExpr, Error = ParseError> {
recursive(|r| {
choice((
just(Token::Trace)
.ignore_then(expr_parser(r.clone()))
.then(r.clone())
.map_with_span(|(text, then_), span| expr::UntypedExpr::Trace {
kind: TraceKind::Trace,
location: span,
then: Box::new(then_),
text: Box::new(flexible_string_literal(text)),
}),
just(Token::ErrorTerm)
.ignore_then(expr_parser(r.clone()).or_not())
.map_with_span(|reason, span| {
expr::UntypedExpr::error(span, reason.map(flexible_string_literal))
}),
just(Token::Todo)
.ignore_then(expr_parser(r.clone()).or_not())
.map_with_span(|reason, span| {
expr::UntypedExpr::todo(span, reason.map(flexible_string_literal))
}),
expr_parser(r.clone())
.then(r.repeated())
.foldl(|current, next| current.append_in_sequence(next)),
))
})
}
pub fn expr_parser( pub fn expr_parser(
seq_r: Recursive<'_, Token, expr::UntypedExpr, ParseError>, seq_r: Recursive<'_, Token, UntypedExpr, ParseError>,
) -> impl Parser<Token, expr::UntypedExpr, Error = ParseError> + '_ { ) -> impl Parser<Token, UntypedExpr, Error = ParseError> + '_ {
recursive(|r| { recursive(|r| {
let string_parser = let string_parser =
select! {Token::String {value} => value}.map_with_span(|value, span| { select! {Token::String {value} => value}.map_with_span(|value, span| {
expr::UntypedExpr::String { UntypedExpr::String {
location: span, location: span,
value, value,
} }
}); });
let int_parser = select! { Token::Int {value, base} => (value, base)}.map_with_span( let int_parser = select! { Token::Int {value, base} => (value, base)}.map_with_span(
|(value, base), span| expr::UntypedExpr::Int { |(value, base), span| UntypedExpr::Int {
location: span, location: span,
value, value,
base, base,
@ -357,7 +188,7 @@ pub fn expr_parser(
select! {Token::Name {name} => name}.map_with_span( select! {Token::Name {name} => name}.map_with_span(
|name, span| ast::UntypedRecordUpdateArg { |name, span| ast::UntypedRecordUpdateArg {
location: span, location: span,
value: expr::UntypedExpr::Var { value: UntypedExpr::Var {
name: name.clone(), name: name.clone(),
location: span, location: span,
}, },
@ -375,16 +206,16 @@ pub fn expr_parser(
) )
.map(|((module, (name, n_span)), ((spread, opt_args), span))| { .map(|((module, (name, n_span)), ((spread, opt_args), span))| {
let constructor = if let Some((module, m_span)) = module { let constructor = if let Some((module, m_span)) = module {
expr::UntypedExpr::FieldAccess { UntypedExpr::FieldAccess {
location: m_span.union(n_span), location: m_span.union(n_span),
label: name, label: name,
container: Box::new(expr::UntypedExpr::Var { container: Box::new(UntypedExpr::Var {
location: m_span, location: m_span,
name: module, name: module,
}), }),
} }
} else { } else {
expr::UntypedExpr::Var { UntypedExpr::Var {
location: n_span, location: n_span,
name, name,
} }
@ -399,7 +230,7 @@ pub fn expr_parser(
location, location,
}; };
expr::UntypedExpr::RecordUpdate { UntypedExpr::RecordUpdate {
location: constructor.location().union(span), location: constructor.location().union(span),
constructor: Box::new(constructor), constructor: Box::new(constructor),
spread, spread,
@ -430,7 +261,7 @@ pub fn expr_parser(
Some(error::Pattern::Discard), Some(error::Pattern::Discard),
)); ));
expr::UntypedExpr::Var { UntypedExpr::Var {
location: span, location: span,
name: CAPTURE_VARIABLE.to_string(), name: CAPTURE_VARIABLE.to_string(),
} }
@ -445,7 +276,7 @@ pub fn expr_parser(
choice(( choice((
select! {Token::Name {name} => name}.map_with_span(|name, span| { select! {Token::Name {name} => name}.map_with_span(|name, span| {
( (
expr::UntypedExpr::Var { UntypedExpr::Var {
name: name.clone(), name: name.clone(),
location: span, location: span,
}, },
@ -461,7 +292,7 @@ pub fn expr_parser(
)); ));
( (
expr::UntypedExpr::Var { UntypedExpr::Var {
location: span, location: span,
name: CAPTURE_VARIABLE.to_string(), name: CAPTURE_VARIABLE.to_string(),
}, },
@ -510,7 +341,7 @@ pub fn expr_parser(
Some(error::Pattern::Discard), Some(error::Pattern::Discard),
)); ));
expr::UntypedExpr::Var { UntypedExpr::Var {
location: span, location: span,
name: CAPTURE_VARIABLE.to_string(), name: CAPTURE_VARIABLE.to_string(),
} }
@ -529,22 +360,22 @@ pub fn expr_parser(
)) ))
.map_with_span(|((module, (name, n_span)), arguments), span| { .map_with_span(|((module, (name, n_span)), arguments), span| {
let fun = if let Some((module, m_span)) = module { let fun = if let Some((module, m_span)) = module {
expr::UntypedExpr::FieldAccess { UntypedExpr::FieldAccess {
location: m_span.union(n_span), location: m_span.union(n_span),
label: name, label: name,
container: Box::new(expr::UntypedExpr::Var { container: Box::new(UntypedExpr::Var {
location: m_span, location: m_span,
name: module, name: module,
}), }),
} }
} else { } else {
expr::UntypedExpr::Var { UntypedExpr::Var {
location: n_span, location: n_span,
name, name,
} }
}; };
expr::UntypedExpr::Call { UntypedExpr::Call {
arguments, arguments,
fun: Box::new(fun), fun: Box::new(fun),
location: span, location: span,
@ -555,22 +386,20 @@ pub fn expr_parser(
.map_with_span(|module, span| (module, span)) .map_with_span(|module, span| (module, span))
.then_ignore(just(Token::Dot)) .then_ignore(just(Token::Dot))
.then(select! {Token::UpName { name } => name}) .then(select! {Token::UpName { name } => name})
.map_with_span( .map_with_span(|((module, m_span), name), span| UntypedExpr::FieldAccess {
|((module, m_span), name), span| expr::UntypedExpr::FieldAccess { location: span,
location: span, label: name,
label: name, container: Box::new(UntypedExpr::Var {
container: Box::new(expr::UntypedExpr::Var { location: m_span,
location: m_span, name: module,
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,
} }
.map_with_span(|name, span| expr::UntypedExpr::Var { .map_with_span(|name, span| UntypedExpr::Var {
location: span, location: span,
name, name,
}); });
@ -584,13 +413,13 @@ pub fn expr_parser(
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))), choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
just(Token::RightParen), just(Token::RightParen),
) )
.map_with_span(|elems, span| expr::UntypedExpr::Tuple { .map_with_span(|elems, span| UntypedExpr::Tuple {
location: span, location: span,
elems, elems,
}); });
let bytearray = bytearray_parser().map_with_span(|(preferred_format, bytes), span| { let bytearray = utils::bytearray().map_with_span(|(preferred_format, bytes), span| {
expr::UntypedExpr::ByteArray { UntypedExpr::ByteArray {
location: span, location: span,
bytes, bytes,
preferred_format, preferred_format,
@ -610,7 +439,7 @@ pub fn expr_parser(
))) )))
.then_ignore(just(Token::RightSquare)) .then_ignore(just(Token::RightSquare))
// TODO: check if tail.is_some and elements.is_empty then return ListSpreadWithoutElements error // TODO: check if tail.is_some and elements.is_empty then return ListSpreadWithoutElements error
.map_with_span(|(elements, tail), span| expr::UntypedExpr::List { .map_with_span(|(elements, tail), span| UntypedExpr::List {
location: span, location: span,
elements, elements,
tail, tail,
@ -633,14 +462,14 @@ pub fn expr_parser(
.allow_trailing() .allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)), .delimited_by(just(Token::LeftParen), just(Token::RightParen)),
) )
.then(just(Token::RArrow).ignore_then(type_parser()).or_not()) .then(just(Token::RArrow).ignore_then(annotation()).or_not())
.then(seq_r.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))) .then(seq_r.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)))
.map_with_span( .map_with_span(
|((arguments, return_annotation), body), span| expr::UntypedExpr::Fn { |((arguments, return_annotation), body), span| UntypedExpr::Fn {
arguments, arguments,
body: Box::new(body), body: Box::new(body),
location: span, location: span,
fn_style: expr::FnStyle::Plain, fn_style: FnStyle::Plain,
return_annotation, return_annotation,
}, },
); );
@ -703,24 +532,24 @@ pub fn expr_parser(
}, },
]; ];
let body = expr::UntypedExpr::BinOp { let body = UntypedExpr::BinOp {
location, location,
name, name,
left: Box::new(expr::UntypedExpr::Var { left: Box::new(UntypedExpr::Var {
location, location,
name: "left".to_string(), name: "left".to_string(),
}), }),
right: Box::new(expr::UntypedExpr::Var { right: Box::new(UntypedExpr::Var {
location, location,
name: "right".to_string(), name: "right".to_string(),
}), }),
}; };
expr::UntypedExpr::Fn { UntypedExpr::Fn {
arguments, arguments,
body: Box::new(body), body: Box::new(body),
return_annotation, return_annotation,
fn_style: expr::FnStyle::BinOp(name), fn_style: FnStyle::BinOp(name),
location, location,
} }
}); });
@ -754,7 +583,7 @@ pub fn expr_parser(
.or_not(), .or_not(),
) )
.map_with_span(|reason, span| { .map_with_span(|reason, span| {
expr::UntypedExpr::todo(span, reason.map(flexible_string_literal)) UntypedExpr::todo(span, reason.map(flexible_string_literal))
}), }),
just(Token::ErrorTerm) just(Token::ErrorTerm)
.ignore_then( .ignore_then(
@ -763,7 +592,7 @@ pub fn expr_parser(
.or_not(), .or_not(),
) )
.map_with_span(|reason, span| { .map_with_span(|reason, span| {
expr::UntypedExpr::error(span, reason.map(flexible_string_literal)) UntypedExpr::error(span, reason.map(flexible_string_literal))
}), }),
))) )))
.map_with_span( .map_with_span(
@ -787,7 +616,7 @@ pub fn expr_parser(
// TODO: If clauses are empty we should return ParseErrorType::NoCaseClause // TODO: If clauses are empty we should return ParseErrorType::NoCaseClause
.then(when_clause_parser.repeated()) .then(when_clause_parser.repeated())
.then_ignore(just(Token::RightBrace)) .then_ignore(just(Token::RightBrace))
.map_with_span(|(subject, clauses), span| expr::UntypedExpr::When { .map_with_span(|(subject, clauses), span| UntypedExpr::When {
location: span, location: span,
subject, subject,
clauses, clauses,
@ -795,11 +624,11 @@ pub fn expr_parser(
let let_parser = just(Token::Let) let let_parser = just(Token::Let)
.ignore_then(pattern_parser()) .ignore_then(pattern_parser())
.then(just(Token::Colon).ignore_then(type_parser()).or_not()) .then(just(Token::Colon).ignore_then(annotation()).or_not())
.then_ignore(just(Token::Equal)) .then_ignore(just(Token::Equal))
.then(r.clone()) .then(r.clone())
.map_with_span( .map_with_span(
|((pattern, annotation), value), span| expr::UntypedExpr::Assignment { |((pattern, annotation), value), span| UntypedExpr::Assignment {
location: span, location: span,
value: Box::new(value), value: Box::new(value),
pattern, pattern,
@ -810,11 +639,11 @@ pub fn expr_parser(
let expect_parser = just(Token::Expect) let expect_parser = just(Token::Expect)
.ignore_then(pattern_parser()) .ignore_then(pattern_parser())
.then(just(Token::Colon).ignore_then(type_parser()).or_not()) .then(just(Token::Colon).ignore_then(annotation()).or_not())
.then_ignore(just(Token::Equal)) .then_ignore(just(Token::Equal))
.then(r.clone()) .then(r.clone())
.map_with_span( .map_with_span(
|((pattern, annotation), value), span| expr::UntypedExpr::Assignment { |((pattern, annotation), value), span| UntypedExpr::Assignment {
location: span, location: span,
value: Box::new(value), value: Box::new(value),
pattern, pattern,
@ -850,7 +679,7 @@ pub fn expr_parser(
branches.extend(alternative_branches); branches.extend(alternative_branches);
expr::UntypedExpr::If { UntypedExpr::If {
location: span, location: span,
branches, branches,
final_else: Box::new(final_else), final_else: Box::new(final_else),
@ -879,7 +708,7 @@ pub fn expr_parser(
// Parsing a function call into the appropriate structure // Parsing a function call into the appropriate structure
#[derive(Debug)] #[derive(Debug)]
enum ParserArg { enum ParserArg {
Arg(Box<ast::CallArg<expr::UntypedExpr>>), Arg(Box<ast::CallArg<UntypedExpr>>),
Hole { Hole {
location: Span, location: Span,
label: Option<String>, label: Option<String>,
@ -971,7 +800,7 @@ pub fn expr_parser(
ast::CallArg { ast::CallArg {
label, label,
location, location,
value: expr::UntypedExpr::Var { value: UntypedExpr::Var {
location, location,
name: format!("{CAPTURE_VARIABLE}__{index}"), name: format!("{CAPTURE_VARIABLE}__{index}"),
}, },
@ -980,7 +809,7 @@ pub fn expr_parser(
}) })
.collect(); .collect();
let call = expr::UntypedExpr::Call { let call = UntypedExpr::Call {
location: expr.location().union(span), location: expr.location().union(span),
fun: Box::new(expr), fun: Box::new(expr),
arguments: args, arguments: args,
@ -989,9 +818,9 @@ pub fn expr_parser(
if holes.is_empty() { if holes.is_empty() {
call call
} else { } else {
expr::UntypedExpr::Fn { UntypedExpr::Fn {
location: call.location(), location: call.location(),
fn_style: expr::FnStyle::Capture, fn_style: FnStyle::Capture,
arguments: holes, arguments: holes,
body: Box::new(call), body: Box::new(call),
return_annotation: None, return_annotation: None,
@ -999,13 +828,13 @@ pub fn expr_parser(
} }
} }
Chain::FieldAccess(label, span) => expr::UntypedExpr::FieldAccess { Chain::FieldAccess(label, span) => UntypedExpr::FieldAccess {
location: expr.location().union(span), location: expr.location().union(span),
label, label,
container: Box::new(expr), container: Box::new(expr),
}, },
Chain::TupleIndex(index, span) => expr::UntypedExpr::TupleIndex { Chain::TupleIndex(index, span) => UntypedExpr::TupleIndex {
location: expr.location().union(span), location: expr.location().union(span),
index, index,
tuple: Box::new(expr), tuple: Box::new(expr),
@ -1014,7 +843,7 @@ pub fn expr_parser(
let debug = chained.then(just(Token::Question).or_not()).map_with_span( let debug = chained.then(just(Token::Question).or_not()).map_with_span(
|(value, token), location| match token { |(value, token), location| match token {
Some(_) => expr::UntypedExpr::TraceIfFalse { Some(_) => UntypedExpr::TraceIfFalse {
value: Box::new(value), value: Box::new(value),
location, location,
}, },
@ -1045,7 +874,7 @@ pub fn expr_parser(
.map_with_span(|op, span| (op, span)) .map_with_span(|op, span| (op, span))
.repeated() .repeated()
.then(debug) .then(debug)
.foldr(|(un_op, span), value| expr::UntypedExpr::UnOp { .foldr(|(un_op, span), value| UntypedExpr::UnOp {
op: un_op, op: un_op,
location: span.union(value.location()), location: span.union(value.location()),
value: Box::new(value), value: Box::new(value),
@ -1062,7 +891,7 @@ pub fn expr_parser(
let product = unary let product = unary
.clone() .clone()
.then(op.then(unary).repeated()) .then(op.then(unary).repeated())
.foldl(|a, (op, b)| expr::UntypedExpr::BinOp { .foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()), location: a.location().union(b.location()),
name: op, name: op,
left: Box::new(a), left: Box::new(a),
@ -1079,7 +908,7 @@ pub fn expr_parser(
let sum = product let sum = product
.clone() .clone()
.then(op.then(product).repeated()) .then(op.then(product).repeated())
.foldl(|a, (op, b)| expr::UntypedExpr::BinOp { .foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()), location: a.location().union(b.location()),
name: op, name: op,
left: Box::new(a), left: Box::new(a),
@ -1100,7 +929,7 @@ pub fn expr_parser(
let comparison = sum let comparison = sum
.clone() .clone()
.then(op.then(sum).repeated()) .then(op.then(sum).repeated())
.foldl(|a, (op, b)| expr::UntypedExpr::BinOp { .foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()), location: a.location().union(b.location()),
name: op, name: op,
left: Box::new(a), left: Box::new(a),
@ -1113,7 +942,7 @@ pub fn expr_parser(
let conjunction = comparison let conjunction = comparison
.clone() .clone()
.then(op.then(comparison).repeated()) .then(op.then(comparison).repeated())
.foldl(|a, (op, b)| expr::UntypedExpr::BinOp { .foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()), location: a.location().union(b.location()),
name: op, name: op,
left: Box::new(a), left: Box::new(a),
@ -1126,7 +955,7 @@ pub fn expr_parser(
let disjunction = conjunction let disjunction = conjunction
.clone() .clone()
.then(op.then(conjunction).repeated()) .then(op.then(conjunction).repeated())
.foldl(|a, (op, b)| expr::UntypedExpr::BinOp { .foldl(|a, (op, b)| UntypedExpr::BinOp {
location: a.location().union(b.location()), location: a.location().union(b.location()),
name: op, name: op,
left: Box::new(a), left: Box::new(a),
@ -1143,13 +972,13 @@ pub fn expr_parser(
.repeated(), .repeated(),
) )
.foldl(|l, (pipe, r)| { .foldl(|l, (pipe, r)| {
if let expr::UntypedExpr::PipeLine { if let UntypedExpr::PipeLine {
mut expressions, mut expressions,
one_liner, one_liner,
} = l } = l
{ {
expressions.push(r); expressions.push(r);
return expr::UntypedExpr::PipeLine { return UntypedExpr::PipeLine {
expressions, expressions,
one_liner, one_liner,
}; };
@ -1157,7 +986,7 @@ pub fn expr_parser(
let mut expressions = Vec1::new(l); let mut expressions = Vec1::new(l);
expressions.push(r); expressions.push(r);
expr::UntypedExpr::PipeLine { UntypedExpr::PipeLine {
expressions, expressions,
one_liner: pipe != Token::NewLinePipe, one_liner: pipe != Token::NewLinePipe,
} }
@ -1177,7 +1006,7 @@ pub fn when_clause_guard_parser() -> impl Parser<Token, ast::ClauseGuard<()>, Er
location: span, location: span,
}); });
let constant_parser = constant_value_parser().map(ast::ClauseGuard::Constant); let constant_parser = definitions::constant::value().map(ast::ClauseGuard::Constant);
let block_parser = r let block_parser = r
.clone() .clone()
@ -1281,122 +1110,6 @@ pub fn when_clause_guard_parser() -> impl Parser<Token, ast::ClauseGuard<()>, Er
}) })
} }
pub fn type_parser() -> impl Parser<Token, ast::Annotation, Error = ParseError> {
recursive(|r| {
choice((
// Type hole
select! {Token::DiscardName { name } => name}.map_with_span(|name, span| {
ast::Annotation::Hole {
location: span,
name,
}
}),
// Tuple
r.clone()
.separated_by(just(Token::Comma))
.at_least(2)
.allow_trailing()
.delimited_by(
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
just(Token::RightParen),
)
.map_with_span(|elems, span| ast::Annotation::Tuple {
location: span,
elems,
}),
// Function
just(Token::Fn)
.ignore_then(
r.clone()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
)
.then_ignore(just(Token::RArrow))
.then(r.clone())
.map_with_span(|(arguments, ret), span| ast::Annotation::Fn {
location: span,
arguments,
ret: Box::new(ret),
}),
// Constructor function
select! {Token::UpName { name } => name}
.then(
r.clone()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::Less), just(Token::Greater))
.or_not(),
)
.map_with_span(|(name, arguments), span| ast::Annotation::Constructor {
location: span,
module: None,
name,
arguments: arguments.unwrap_or_default(),
}),
// Constructor Module or type Variable
select! {Token::Name { name } => name}
.then(
just(Token::Dot)
.ignore_then(select! {Token::UpName {name} => name})
.then(
r.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::Less), just(Token::Greater))
.or_not(),
)
.or_not(),
)
.map_with_span(|(mod_name, opt_dot), span| {
if let Some((name, arguments)) = opt_dot {
ast::Annotation::Constructor {
location: span,
module: Some(mod_name),
name,
arguments: arguments.unwrap_or_default(),
}
} else {
// TODO: parse_error(ParseErrorType::NotConstType, SrcSpan { start, end })
ast::Annotation::Var {
location: span,
name: mod_name,
}
}
}),
))
})
}
pub fn labeled_constructor_type_args(
) -> impl Parser<Token, Vec<ast::RecordConstructorArg<()>>, Error = ParseError> {
select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon))
.then(type_parser())
.map_with_span(|(name, annotation), span| ast::RecordConstructorArg {
label: Some(name),
annotation,
tipo: (),
doc: None,
location: span,
})
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
}
pub fn type_name_with_args() -> impl Parser<Token, (String, Option<Vec<String>>), Error = ParseError>
{
just(Token::Type).ignore_then(
select! {Token::UpName { name } => name}.then(
select! {Token::Name { name } => name}
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::Less), just(Token::Greater))
.or_not(),
),
)
}
pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = ParseError> { pub fn pattern_parser() -> impl Parser<Token, ast::UntypedPattern, Error = ParseError> {
recursive(|r| { recursive(|r| {
let record_constructor_pattern_arg_parser = choice(( let record_constructor_pattern_arg_parser = choice((

View File

@ -0,0 +1,91 @@
use chumsky::prelude::*;
use crate::ast;
use super::{error::ParseError, token::Token};
pub fn parser() -> impl Parser<Token, ast::Annotation, Error = ParseError> {
recursive(|r| {
choice((
// Type hole
select! {Token::DiscardName { name } => name}.map_with_span(|name, span| {
ast::Annotation::Hole {
location: span,
name,
}
}),
// Tuple
r.clone()
.separated_by(just(Token::Comma))
.at_least(2)
.allow_trailing()
.delimited_by(
choice((just(Token::LeftParen), just(Token::NewLineLeftParen))),
just(Token::RightParen),
)
.map_with_span(|elems, span| ast::Annotation::Tuple {
location: span,
elems,
}),
// Function
just(Token::Fn)
.ignore_then(
r.clone()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)),
)
.then_ignore(just(Token::RArrow))
.then(r.clone())
.map_with_span(|(arguments, ret), span| ast::Annotation::Fn {
location: span,
arguments,
ret: Box::new(ret),
}),
// Constructor function
select! {Token::UpName { name } => name}
.then(
r.clone()
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::Less), just(Token::Greater))
.or_not(),
)
.map_with_span(|(name, arguments), span| ast::Annotation::Constructor {
location: span,
module: None,
name,
arguments: arguments.unwrap_or_default(),
}),
// Constructor Module or type Variable
select! {Token::Name { name } => name}
.then(
just(Token::Dot)
.ignore_then(select! {Token::UpName {name} => name})
.then(
r.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::Less), just(Token::Greater))
.or_not(),
)
.or_not(),
)
.map_with_span(|(mod_name, opt_dot), span| {
if let Some((name, arguments)) = opt_dot {
ast::Annotation::Constructor {
location: span,
module: Some(mod_name),
name,
arguments: arguments.unwrap_or_default(),
}
} else {
// TODO: parse_error(ParseErrorType::NotConstType, SrcSpan { start, end })
ast::Annotation::Var {
location: span,
name: mod_name,
}
}
}),
))
})
}

View File

@ -2,7 +2,7 @@ use chumsky::prelude::*;
use crate::{ use crate::{
ast, ast,
parser::{error::ParseError, token::Token, utils}, parser::{annotation, error::ParseError, token::Token, utils},
}; };
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> { pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
@ -10,9 +10,13 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
.or_not() .or_not()
.then_ignore(just(Token::Const)) .then_ignore(just(Token::Const))
.then(select! {Token::Name{name} => name}) .then(select! {Token::Name{name} => name})
.then(just(Token::Colon).ignore_then(type_parser()).or_not()) .then(
just(Token::Colon)
.ignore_then(annotation::parser())
.or_not(),
)
.then_ignore(just(Token::Equal)) .then_ignore(just(Token::Equal))
.then(constant_value_parser()) .then(value())
.map_with_span(|(((public, name), annotation), value), span| { .map_with_span(|(((public, name), annotation), value), span| {
ast::UntypedDefinition::ModuleConstant(ast::ModuleConstant { ast::UntypedDefinition::ModuleConstant(ast::ModuleConstant {
doc: None, doc: None,
@ -25,3 +29,37 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
}) })
}) })
} }
pub fn value() -> impl Parser<Token, ast::Constant, Error = ParseError> {
let constant_string_parser =
select! {Token::String {value} => value}.map_with_span(|value, span| {
ast::Constant::String {
location: span,
value,
}
});
let constant_int_parser =
select! {Token::Int {value, base} => (value, base)}.map_with_span(|(value, base), span| {
ast::Constant::Int {
location: span,
value,
base,
}
});
let constant_bytearray_parser =
utils::bytearray().map_with_span(|(preferred_format, bytes), span| {
ast::Constant::ByteArray {
location: span,
bytes,
preferred_format,
}
});
choice((
constant_string_parser,
constant_int_parser,
constant_bytearray_parser,
))
}

View File

@ -2,11 +2,11 @@ use chumsky::prelude::*;
use crate::{ use crate::{
ast, ast,
parser::{error::ParseError, token::Token, utils}, parser::{annotation, error::ParseError, token::Token, utils},
}; };
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> { pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
let unlabeled_constructor_type_args = type_parser() let unlabeled_constructor_type_args = annotation()
.map_with_span(|annotation, span| ast::RecordConstructorArg { .map_with_span(|annotation, span| ast::RecordConstructorArg {
label: None, label: None,
annotation, annotation,
@ -49,7 +49,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
utils::public() utils::public()
.then(just(Token::Opaque).ignored().or_not()) .then(just(Token::Opaque).ignored().or_not())
.or_not() .or_not()
.then(type_name_with_args()) .then(utils::type_name_with_args())
.then(choice((constructors, record_sugar))) .then(choice((constructors, record_sugar)))
.map_with_span(|((pub_opaque, (name, parameters)), constructors), span| { .map_with_span(|((pub_opaque, (name, parameters)), constructors), span| {
ast::UntypedDefinition::DataType(ast::DataType { ast::UntypedDefinition::DataType(ast::DataType {
@ -75,3 +75,20 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
}) })
}) })
} }
fn labeled_constructor_type_args(
) -> impl Parser<Token, Vec<ast::RecordConstructorArg<()>>, Error = ParseError> {
select! {Token::Name {name} => name}
.then_ignore(just(Token::Colon))
.then(annotation())
.map_with_span(|(name, annotation), span| ast::RecordConstructorArg {
label: Some(name),
annotation,
tipo: (),
doc: None,
location: span,
})
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
}

View File

@ -1,8 +1,9 @@
use chumsky::prelude::*; use chumsky::prelude::*;
use crate::{ use crate::{
ast, expr, ast,
parser::{error::ParseError, token::Token, utils}, expr::UntypedExpr,
parser::{annotation, error::ParseError, expr, token::Token, utils},
}; };
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> { pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
@ -11,15 +12,15 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
.then_ignore(just(Token::Fn)) .then_ignore(just(Token::Fn))
.then(select! {Token::Name {name} => name}) .then(select! {Token::Name {name} => name})
.then( .then(
fn_param_parser(false) param(false)
.separated_by(just(Token::Comma)) .separated_by(just(Token::Comma))
.allow_trailing() .allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)) .delimited_by(just(Token::LeftParen), just(Token::RightParen))
.map_with_span(|arguments, span| (arguments, span)), .map_with_span(|arguments, span| (arguments, span)),
) )
.then(just(Token::RArrow).ignore_then(type_parser()).or_not()) .then(just(Token::RArrow).ignore_then(annotation()).or_not())
.then( .then(
expr_seq_parser() expr::sequence()
.or_not() .or_not()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
) )
@ -27,7 +28,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
|((((opt_pub, name), (arguments, args_span)), return_annotation), body), span| { |((((opt_pub, 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(|| expr::UntypedExpr::todo(span, None)), body: body.unwrap_or_else(|| UntypedExpr::todo(span, None)),
doc: None, doc: None,
location: ast::Span { location: ast::Span {
start: span.start, start: span.start,
@ -46,3 +47,43 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
}, },
) )
} }
pub fn param(is_validator_param: bool) -> impl Parser<Token, ast::UntypedArg, Error = ParseError> {
choice((
select! {Token::Name {name} => name}
.then(select! {Token::DiscardName {name} => name})
.map_with_span(|(label, name), span| ast::ArgName::Discarded {
label,
name,
location: span,
}),
select! {Token::DiscardName {name} => name}.map_with_span(|name, span| {
ast::ArgName::Discarded {
label: name.clone(),
name,
location: span,
}
}),
select! {Token::Name {name} => name}
.then(select! {Token::Name {name} => name})
.map_with_span(move |(label, name), span| ast::ArgName::Named {
label,
name,
location: span,
is_validator_param,
}),
select! {Token::Name {name} => name}.map_with_span(move |name, span| ast::ArgName::Named {
label: name.clone(),
name,
location: span,
is_validator_param,
}),
))
.then(just(Token::Colon).ignore_then(annotation()).or_not())
.map_with_span(|(arg_name, annotation), span| ast::Arg {
location: span,
annotation,
tipo: (),
arg_name,
})
}

View File

@ -1,4 +1,4 @@
mod constant; pub mod constant;
mod data_type; mod data_type;
mod function; mod function;
mod import; mod import;

View File

@ -1,8 +1,9 @@
use chumsky::prelude::*; use chumsky::prelude::*;
use crate::{ use crate::{
ast, expr, ast,
parser::{error::ParseError, token::Token}, expr::UntypedExpr,
parser::{error::ParseError, expr, token::Token},
}; };
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> { pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
@ -15,14 +16,14 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
.then_ignore(just(Token::RightParen)) .then_ignore(just(Token::RightParen))
.map_with_span(|name, span| (name, span)) .map_with_span(|name, span| (name, span))
.then( .then(
expr_seq_parser() expr::sequence()
.or_not() .or_not()
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)), .delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
) )
.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(|| expr::UntypedExpr::todo(span, None)), body: body.unwrap_or_else(|| UntypedExpr::todo(span, None)),
doc: None, doc: None,
location: span_end, location: span_end,
end_position: span.end - 1, end_position: span.end - 1,

View File

@ -2,15 +2,15 @@ use chumsky::prelude::*;
use crate::{ use crate::{
ast, ast,
parser::{error::ParseError, token::Token, utils}, parser::{annotation, error::ParseError, token::Token, utils},
}; };
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> { pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
utils::public() utils::public()
.or_not() .or_not()
.then(type_name_with_args()) .then(utils::type_name_with_args())
.then_ignore(just(Token::Equal)) .then_ignore(just(Token::Equal))
.then(type_parser()) .then(annotation())
.map_with_span(|((opt_pub, (alias, parameters)), annotation), span| { .map_with_span(|((opt_pub, (alias, parameters)), annotation), span| {
ast::UntypedDefinition::TypeAlias(ast::TypeAlias { ast::UntypedDefinition::TypeAlias(ast::TypeAlias {
alias, alias,

View File

@ -5,10 +5,12 @@ use crate::{
parser::{error::ParseError, token::Token}, parser::{error::ParseError, token::Token},
}; };
use super::function;
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> { pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
just(Token::Validator) just(Token::Validator)
.ignore_then( .ignore_then(
fn_param_parser(true) function::param(true)
.separated_by(just(Token::Comma)) .separated_by(just(Token::Comma))
.allow_trailing() .allow_trailing()
.delimited_by(just(Token::LeftParen), just(Token::RightParen)) .delimited_by(just(Token::LeftParen), just(Token::RightParen))
@ -16,7 +18,7 @@ pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError
.or_not(), .or_not(),
) )
.then( .then(
super::function() function()
.repeated() .repeated()
.at_least(1) .at_least(1)
.at_most(2) .at_most(2)

View File

@ -0,0 +1,3 @@
mod sequence;
pub use sequence::parser as sequence;

View File

@ -0,0 +1,36 @@
use chumsky::prelude::*;
use crate::{
ast::TraceKind,
expr::UntypedExpr,
parser::{error::ParseError, token::Token},
};
pub fn parser() -> impl Parser<Token, UntypedExpr, Error = ParseError> {
recursive(|r| {
choice((
just(Token::Trace)
.ignore_then(expr_parser(r.clone()))
.then(r.clone())
.map_with_span(|(text, then_), span| UntypedExpr::Trace {
kind: TraceKind::Trace,
location: span,
then: Box::new(then_),
text: Box::new(flexible_string_literal(text)),
}),
just(Token::ErrorTerm)
.ignore_then(expr_parser(r.clone()).or_not())
.map_with_span(|reason, span| {
UntypedExpr::error(span, reason.map(flexible_string_literal))
}),
just(Token::Todo)
.ignore_then(expr_parser(r.clone()).or_not())
.map_with_span(|reason, span| {
UntypedExpr::todo(span, reason.map(flexible_string_literal))
}),
expr_parser(r.clone())
.then(r.repeated())
.foldl(|current, next| current.append_in_sequence(next)),
))
})
}

View File

@ -1,7 +1,95 @@
use chumsky::prelude::*; use chumsky::prelude::*;
use super::{error::ParseError, token::Token}; use crate::ast;
use super::{
error::{self, ParseError},
token::{Base, Token},
};
pub fn public() -> impl Parser<Token, (), Error = ParseError> { pub fn public() -> impl Parser<Token, (), Error = ParseError> {
just(Token::Pub).ignored() just(Token::Pub).ignored()
} }
pub fn bytearray(
) -> impl Parser<Token, (ast::ByteArrayFormatPreference, Vec<u8>), Error = ParseError> {
let bytearray_list_parser = just(Token::Hash)
.ignore_then(
select! {Token::Int {value, base, ..} => (value, base)}
.validate(|(value, base), span, emit| {
let byte: u8 = match value.parse() {
Ok(b) => b,
Err(_) => {
emit(ParseError::expected_input_found(
span,
None,
Some(error::Pattern::Byte),
));
0
}
};
(byte, base)
})
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::LeftSquare), just(Token::RightSquare)),
)
.validate(|bytes, span, emit| {
let base = bytes.iter().fold(Ok(None), |acc, (_, base)| match acc {
Ok(None) => Ok(Some(base)),
Ok(Some(previous_base)) if previous_base == base => Ok(Some(base)),
_ => Err(()),
});
let base = match base {
Err(()) => {
emit(ParseError::hybrid_notation_in_bytearray(span));
Base::Decimal {
numeric_underscore: false,
}
}
Ok(None) => Base::Decimal {
numeric_underscore: false,
},
Ok(Some(base)) => *base,
};
(bytes.into_iter().map(|(b, _)| b).collect::<Vec<u8>>(), base)
})
.map(|(bytes, base)| (ast::ByteArrayFormatPreference::ArrayOfBytes(base), bytes));
let bytearray_hexstring_parser =
just(Token::Hash)
.ignore_then(select! {Token::ByteString {value} => value}.validate(
|value, span, emit| match hex::decode(value) {
Ok(bytes) => bytes,
Err(_) => {
emit(ParseError::malformed_base16_string_literal(span));
vec![]
}
},
))
.map(|token| (ast::ByteArrayFormatPreference::HexadecimalString, token));
let bytearray_utf8_parser = select! {Token::ByteString {value} => value.into_bytes() }
.map(|token| (ast::ByteArrayFormatPreference::Utf8String, token));
choice((
bytearray_list_parser,
bytearray_hexstring_parser,
bytearray_utf8_parser,
))
}
pub fn type_name_with_args() -> impl Parser<Token, (String, Option<Vec<String>>), Error = ParseError>
{
just(Token::Type).ignore_then(
select! {Token::UpName { name } => name}.then(
select! {Token::Name { name } => name}
.separated_by(just(Token::Comma))
.allow_trailing()
.delimited_by(just(Token::Less), just(Token::Greater))
.or_not(),
),
)
}