test(parser): rename definitions to definition and more tests
This commit is contained in:
65
crates/aiken-lang/src/parser/definition/constant.rs
Normal file
65
crates/aiken-lang/src/parser/definition/constant.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
parser::{annotation, error::ParseError, token::Token, utils},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
utils::public()
|
||||
.or_not()
|
||||
.then_ignore(just(Token::Const))
|
||||
.then(select! {Token::Name{name} => name})
|
||||
.then(
|
||||
just(Token::Colon)
|
||||
.ignore_then(annotation::parser())
|
||||
.or_not(),
|
||||
)
|
||||
.then_ignore(just(Token::Equal))
|
||||
.then(value())
|
||||
.map_with_span(|(((public, name), annotation), value), span| {
|
||||
ast::UntypedDefinition::ModuleConstant(ast::ModuleConstant {
|
||||
doc: None,
|
||||
location: span,
|
||||
public: public.is_some(),
|
||||
name,
|
||||
annotation,
|
||||
value: Box::new(value),
|
||||
tipo: (),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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,
|
||||
))
|
||||
}
|
||||
94
crates/aiken-lang/src/parser/definition/data_type.rs
Normal file
94
crates/aiken-lang/src/parser/definition/data_type.rs
Normal file
@@ -0,0 +1,94 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
parser::{annotation, error::ParseError, token::Token, utils},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
let unlabeled_constructor_type_args = annotation()
|
||||
.map_with_span(|annotation, span| ast::RecordConstructorArg {
|
||||
label: None,
|
||||
annotation,
|
||||
tipo: (),
|
||||
doc: None,
|
||||
location: span,
|
||||
})
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen));
|
||||
|
||||
let constructors = select! {Token::UpName { name } => name}
|
||||
.then(
|
||||
choice((
|
||||
labeled_constructor_type_args(),
|
||||
unlabeled_constructor_type_args,
|
||||
))
|
||||
.or_not(),
|
||||
)
|
||||
.map_with_span(|(name, arguments), span| ast::RecordConstructor {
|
||||
location: span,
|
||||
arguments: arguments.unwrap_or_default(),
|
||||
name,
|
||||
doc: None,
|
||||
sugar: false,
|
||||
})
|
||||
.repeated()
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace));
|
||||
|
||||
let record_sugar = labeled_constructor_type_args().map_with_span(|arguments, span| {
|
||||
vec![ast::RecordConstructor {
|
||||
location: span,
|
||||
arguments,
|
||||
doc: None,
|
||||
name: String::from("_replace"),
|
||||
sugar: true,
|
||||
}]
|
||||
});
|
||||
|
||||
utils::public()
|
||||
.then(just(Token::Opaque).ignored().or_not())
|
||||
.or_not()
|
||||
.then(utils::type_name_with_args())
|
||||
.then(choice((constructors, record_sugar)))
|
||||
.map_with_span(|((pub_opaque, (name, parameters)), constructors), span| {
|
||||
ast::UntypedDefinition::DataType(ast::DataType {
|
||||
location: span,
|
||||
constructors: constructors
|
||||
.into_iter()
|
||||
.map(|mut constructor| {
|
||||
if constructor.sugar {
|
||||
constructor.name = name.clone();
|
||||
}
|
||||
|
||||
constructor
|
||||
})
|
||||
.collect(),
|
||||
doc: None,
|
||||
name,
|
||||
opaque: pub_opaque
|
||||
.map(|(_, opt_opaque)| opt_opaque.is_some())
|
||||
.unwrap_or(false),
|
||||
parameters: parameters.unwrap_or_default(),
|
||||
public: pub_opaque.is_some(),
|
||||
typed_parameters: vec![],
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
89
crates/aiken-lang/src/parser/definition/function.rs
Normal file
89
crates/aiken-lang/src/parser/definition/function.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
expr::UntypedExpr,
|
||||
parser::{annotation, error::ParseError, expr, token::Token, utils},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
utils::public()
|
||||
.or_not()
|
||||
.then_ignore(just(Token::Fn))
|
||||
.then(select! {Token::Name {name} => name})
|
||||
.then(
|
||||
param(false)
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.map_with_span(|arguments, span| (arguments, span)),
|
||||
)
|
||||
.then(just(Token::RArrow).ignore_then(annotation()).or_not())
|
||||
.then(
|
||||
expr::sequence()
|
||||
.or_not()
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||
)
|
||||
.map_with_span(
|
||||
|((((opt_pub, name), (arguments, args_span)), return_annotation), body), span| {
|
||||
ast::UntypedDefinition::Fn(ast::Function {
|
||||
arguments,
|
||||
body: body.unwrap_or_else(|| UntypedExpr::todo(span, None)),
|
||||
doc: None,
|
||||
location: ast::Span {
|
||||
start: span.start,
|
||||
end: return_annotation
|
||||
.as_ref()
|
||||
.map(|l| l.location().end)
|
||||
.unwrap_or_else(|| args_span.end),
|
||||
},
|
||||
end_position: span.end - 1,
|
||||
name,
|
||||
public: opt_pub.is_some(),
|
||||
return_annotation,
|
||||
return_type: (),
|
||||
can_error: true,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
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,
|
||||
})
|
||||
}
|
||||
83
crates/aiken-lang/src/parser/definition/import.rs
Normal file
83
crates/aiken-lang/src/parser/definition/import.rs
Normal file
@@ -0,0 +1,83 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
parser::{error::ParseError, token::Token},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
let unqualified_import = choice((
|
||||
select! {Token::Name { name } => name}.then(
|
||||
just(Token::As)
|
||||
.ignore_then(select! {Token::Name { name } => name})
|
||||
.or_not(),
|
||||
),
|
||||
select! {Token::UpName { name } => name}.then(
|
||||
just(Token::As)
|
||||
.ignore_then(select! {Token::UpName { name } => name})
|
||||
.or_not(),
|
||||
),
|
||||
))
|
||||
.map_with_span(|(name, as_name), span| ast::UnqualifiedImport {
|
||||
name,
|
||||
location: span,
|
||||
as_name,
|
||||
layer: Default::default(),
|
||||
});
|
||||
|
||||
let unqualified_imports = just(Token::Dot)
|
||||
.ignore_then(
|
||||
unqualified_import
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||
)
|
||||
.or_not();
|
||||
|
||||
let as_name = just(Token::As)
|
||||
.ignore_then(select! {Token::Name { name } => name})
|
||||
.or_not();
|
||||
|
||||
let module_path = select! {Token::Name { name } => name}
|
||||
.separated_by(just(Token::Slash))
|
||||
.then(unqualified_imports)
|
||||
.then(as_name);
|
||||
|
||||
just(Token::Use).ignore_then(module_path).map_with_span(
|
||||
|((module, unqualified), as_name), span| {
|
||||
ast::UntypedDefinition::Use(ast::Use {
|
||||
module,
|
||||
as_name,
|
||||
unqualified: unqualified.unwrap_or_default(),
|
||||
package: (),
|
||||
location: span,
|
||||
})
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chumsky::Parser;
|
||||
|
||||
use crate::assert_definition;
|
||||
|
||||
#[test]
|
||||
fn import_basic() {
|
||||
assert_definition!("use aiken/list");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_unqualified() {
|
||||
assert_definition!(
|
||||
r#"
|
||||
use std/address.{Address as A, thing as w}
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn import_alias() {
|
||||
assert_definition!("use aiken/list as foo");
|
||||
}
|
||||
}
|
||||
32
crates/aiken-lang/src/parser/definition/mod.rs
Normal file
32
crates/aiken-lang/src/parser/definition/mod.rs
Normal file
@@ -0,0 +1,32 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
pub mod constant;
|
||||
mod data_type;
|
||||
mod function;
|
||||
mod import;
|
||||
mod test;
|
||||
mod type_alias;
|
||||
mod validator;
|
||||
|
||||
pub use constant::parser as constant;
|
||||
pub use data_type::parser as data_type;
|
||||
pub use function::parser as function;
|
||||
pub use import::parser as import;
|
||||
pub use test::parser as test;
|
||||
pub use type_alias::parser as type_alias;
|
||||
pub use validator::parser as validator;
|
||||
|
||||
use super::{error::ParseError, token::Token};
|
||||
use crate::ast;
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
choice((
|
||||
import(),
|
||||
data_type(),
|
||||
type_alias(),
|
||||
validator(),
|
||||
function(),
|
||||
test(),
|
||||
constant(),
|
||||
))
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
---
|
||||
source: crates/aiken-lang/src/parser/definition/import.rs
|
||||
description: "Code:\n\nuse aiken/list as foo"
|
||||
---
|
||||
Use(
|
||||
Use {
|
||||
as_name: Some(
|
||||
"foo",
|
||||
),
|
||||
location: 0..21,
|
||||
module: [
|
||||
"aiken",
|
||||
"list",
|
||||
],
|
||||
package: (),
|
||||
unqualified: [],
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
---
|
||||
source: crates/aiken-lang/src/parser/definition/import.rs
|
||||
description: "Code:\n\nuse aiken/list"
|
||||
---
|
||||
Use(
|
||||
Use {
|
||||
as_name: None,
|
||||
location: 0..14,
|
||||
module: [
|
||||
"aiken",
|
||||
"list",
|
||||
],
|
||||
package: (),
|
||||
unqualified: [],
|
||||
},
|
||||
)
|
||||
@@ -0,0 +1,33 @@
|
||||
---
|
||||
source: crates/aiken-lang/src/parser/definition/import.rs
|
||||
description: "Code:\n\nuse std/address.{Address as A, thing as w}\n"
|
||||
---
|
||||
Use(
|
||||
Use {
|
||||
as_name: None,
|
||||
location: 0..42,
|
||||
module: [
|
||||
"std",
|
||||
"address",
|
||||
],
|
||||
package: (),
|
||||
unqualified: [
|
||||
UnqualifiedImport {
|
||||
location: 17..29,
|
||||
name: "Address",
|
||||
as_name: Some(
|
||||
"A",
|
||||
),
|
||||
layer: Value,
|
||||
},
|
||||
UnqualifiedImport {
|
||||
location: 31..41,
|
||||
name: "thing",
|
||||
as_name: Some(
|
||||
"w",
|
||||
),
|
||||
layer: Value,
|
||||
},
|
||||
],
|
||||
},
|
||||
)
|
||||
37
crates/aiken-lang/src/parser/definition/test.rs
Normal file
37
crates/aiken-lang/src/parser/definition/test.rs
Normal file
@@ -0,0 +1,37 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
expr::UntypedExpr,
|
||||
parser::{error::ParseError, expr, token::Token},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
just(Token::Bang)
|
||||
.ignored()
|
||||
.or_not()
|
||||
.then_ignore(just(Token::Test))
|
||||
.then(select! {Token::Name {name} => name})
|
||||
.then_ignore(just(Token::LeftParen))
|
||||
.then_ignore(just(Token::RightParen))
|
||||
.map_with_span(|name, span| (name, span))
|
||||
.then(
|
||||
expr::sequence()
|
||||
.or_not()
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace)),
|
||||
)
|
||||
.map_with_span(|(((fail, name), span_end), body), span| {
|
||||
ast::UntypedDefinition::Test(ast::Function {
|
||||
arguments: vec![],
|
||||
body: body.unwrap_or_else(|| UntypedExpr::todo(span, None)),
|
||||
doc: None,
|
||||
location: span_end,
|
||||
end_position: span.end - 1,
|
||||
name,
|
||||
public: false,
|
||||
return_annotation: None,
|
||||
return_type: (),
|
||||
can_error: fail.is_some(),
|
||||
})
|
||||
})
|
||||
}
|
||||
25
crates/aiken-lang/src/parser/definition/type_alias.rs
Normal file
25
crates/aiken-lang/src/parser/definition/type_alias.rs
Normal file
@@ -0,0 +1,25 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
parser::{annotation, error::ParseError, token::Token, utils},
|
||||
};
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
utils::public()
|
||||
.or_not()
|
||||
.then(utils::type_name_with_args())
|
||||
.then_ignore(just(Token::Equal))
|
||||
.then(annotation())
|
||||
.map_with_span(|((opt_pub, (alias, parameters)), annotation), span| {
|
||||
ast::UntypedDefinition::TypeAlias(ast::TypeAlias {
|
||||
alias,
|
||||
annotation,
|
||||
doc: None,
|
||||
location: span,
|
||||
parameters: parameters.unwrap_or_default(),
|
||||
public: opt_pub.is_some(),
|
||||
tipo: (),
|
||||
})
|
||||
})
|
||||
}
|
||||
60
crates/aiken-lang/src/parser/definition/validator.rs
Normal file
60
crates/aiken-lang/src/parser/definition/validator.rs
Normal file
@@ -0,0 +1,60 @@
|
||||
use chumsky::prelude::*;
|
||||
|
||||
use crate::{
|
||||
ast,
|
||||
parser::{error::ParseError, token::Token},
|
||||
};
|
||||
|
||||
use super::function;
|
||||
|
||||
pub fn parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
just(Token::Validator)
|
||||
.ignore_then(
|
||||
function::param(true)
|
||||
.separated_by(just(Token::Comma))
|
||||
.allow_trailing()
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.map_with_span(|arguments, span| (arguments, span))
|
||||
.or_not(),
|
||||
)
|
||||
.then(
|
||||
function()
|
||||
.repeated()
|
||||
.at_least(1)
|
||||
.at_most(2)
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
|
||||
.map(|defs| {
|
||||
defs.into_iter().map(|def| {
|
||||
let ast::UntypedDefinition::Fn(fun) = def else {
|
||||
unreachable!("It should be a fn definition");
|
||||
};
|
||||
|
||||
fun
|
||||
})
|
||||
}),
|
||||
)
|
||||
.map_with_span(|(opt_extra_params, mut functions), span| {
|
||||
let (params, params_span) = opt_extra_params.unwrap_or((
|
||||
vec![],
|
||||
ast::Span {
|
||||
start: 0,
|
||||
end: span.start + "validator".len(),
|
||||
},
|
||||
));
|
||||
|
||||
ast::UntypedDefinition::Validator(ast::Validator {
|
||||
doc: None,
|
||||
fun: functions
|
||||
.next()
|
||||
.expect("unwrapping safe because there's 'at_least(1)' function"),
|
||||
other_fun: functions.next(),
|
||||
location: ast::Span {
|
||||
start: span.start,
|
||||
// capture the span from the optional params
|
||||
end: params_span.end,
|
||||
},
|
||||
params,
|
||||
end_position: span.end - 1,
|
||||
})
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user