feat: parse custom types
This commit is contained in:
parent
1d1a6fc404
commit
fbc9b27efe
|
@ -32,10 +32,10 @@ pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
|
|||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
||||
Fn {
|
||||
location: Span,
|
||||
arguments: Vec<Vec<Arg<T>>>,
|
||||
body: Expr,
|
||||
doc: Option<String>,
|
||||
location: Span,
|
||||
name: String,
|
||||
public: bool,
|
||||
return_annotation: Option<Annotation>,
|
||||
|
@ -43,19 +43,19 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
|||
},
|
||||
|
||||
TypeAlias {
|
||||
location: Span,
|
||||
alias: String,
|
||||
annotation: Annotation,
|
||||
doc: Option<String>,
|
||||
location: Span,
|
||||
parameters: Vec<String>,
|
||||
public: bool,
|
||||
tipo: T,
|
||||
},
|
||||
|
||||
DataType {
|
||||
location: Span,
|
||||
constructors: Vec<RecordConstructor<T>>,
|
||||
doc: Option<String>,
|
||||
location: Span,
|
||||
name: String,
|
||||
opaque: bool,
|
||||
parameters: Vec<String>,
|
||||
|
@ -64,11 +64,11 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
|||
},
|
||||
|
||||
Use {
|
||||
as_name: Option<String>,
|
||||
location: Span,
|
||||
module: Vec<String>,
|
||||
as_name: Option<String>,
|
||||
unqualified: Vec<UnqualifiedImport>,
|
||||
package: PackageName,
|
||||
unqualified: Vec<UnqualifiedImport>,
|
||||
},
|
||||
|
||||
ModuleConstant {
|
||||
|
@ -151,6 +151,7 @@ pub struct RecordConstructor<T> {
|
|||
pub name: String,
|
||||
pub arguments: Vec<RecordConstructorArg<T>>,
|
||||
pub documentation: Option<String>,
|
||||
pub sugar: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
|
|
|
@ -27,6 +27,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
|||
just('%').to(Token::Percent),
|
||||
just("|>").to(Token::Pipe),
|
||||
just(',').to(Token::Comma),
|
||||
just(':').to(Token::Colon),
|
||||
));
|
||||
|
||||
let grouping = choice((
|
||||
|
@ -81,7 +82,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
|||
} else if s.starts_with('_') {
|
||||
Token::DiscardName {
|
||||
// TODO: do not allow uppercase letters in discard name
|
||||
name: Intern::new(s),
|
||||
name: s,
|
||||
}
|
||||
} else {
|
||||
Token::Name {
|
||||
|
@ -125,7 +126,6 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use chumsky::prelude::*;
|
||||
use internment::Intern;
|
||||
|
||||
use crate::{
|
||||
ast::{Span, SrcId},
|
||||
|
@ -157,7 +157,7 @@ mod tests {
|
|||
name: "Thing".to_string()
|
||||
},
|
||||
Token::DiscardName {
|
||||
name: Intern::new("_na_thing".to_string())
|
||||
name: "_na_thing".to_string()
|
||||
},
|
||||
Token::Name {
|
||||
name: "name".to_string()
|
||||
|
|
|
@ -5,6 +5,19 @@ use crate::{ast, error::ParseError, token::Token};
|
|||
pub fn module_parser(
|
||||
kind: ast::ModuleKind,
|
||||
) -> impl Parser<Token, ast::UntypedModule, Error = ParseError> {
|
||||
choice((import_parser(), data_parser()))
|
||||
.repeated()
|
||||
.then_ignore(end())
|
||||
.map(move |definitions| ast::UntypedModule {
|
||||
kind,
|
||||
definitions,
|
||||
docs: vec![],
|
||||
name: vec![],
|
||||
type_info: (),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn import_parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
let unqualified_import = choice((
|
||||
select! {Token::Name { name } => name}.then(
|
||||
just(Token::As)
|
||||
|
@ -41,7 +54,7 @@ pub fn module_parser(
|
|||
.then(unqualified_imports)
|
||||
.then(as_name);
|
||||
|
||||
let import = just(Token::Use).ignore_then(module_path).map_with_span(
|
||||
just(Token::Use).ignore_then(module_path).map_with_span(
|
||||
|((module, unqualified), as_name), span| ast::UntypedDefinition::Use {
|
||||
module,
|
||||
as_name,
|
||||
|
@ -49,18 +62,168 @@ pub fn module_parser(
|
|||
package: (),
|
||||
location: span,
|
||||
},
|
||||
);
|
||||
)
|
||||
}
|
||||
|
||||
choice((import,))
|
||||
.repeated()
|
||||
.then_ignore(end())
|
||||
.map(move |definitions| ast::UntypedModule {
|
||||
kind,
|
||||
definitions,
|
||||
docs: vec![],
|
||||
name: vec![],
|
||||
type_info: (),
|
||||
pub fn data_parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||
let unlabeled_constructor_type_args = type_parser()
|
||||
.map_with_span(|annotation, span| ast::RecordConstructorArg {
|
||||
label: None,
|
||||
annotation,
|
||||
tipo: (),
|
||||
doc: None,
|
||||
location: span,
|
||||
})
|
||||
.separated_by(just(Token::Comma))
|
||||
.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,
|
||||
documentation: 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,
|
||||
documentation: None,
|
||||
name: String::from("_replace"),
|
||||
sugar: true,
|
||||
}]
|
||||
});
|
||||
|
||||
just(Token::Pub)
|
||||
.ignored()
|
||||
.then(just(Token::Opaque).ignored().or_not())
|
||||
.or_not()
|
||||
.then(
|
||||
just(Token::Type).ignore_then(
|
||||
select! {Token::UpName { name } => name}.then(
|
||||
select! {Token::Name { name } => name}
|
||||
.separated_by(just(Token::Comma))
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.or_not(),
|
||||
),
|
||||
),
|
||||
)
|
||||
.then(choice((constructors, record_sugar)))
|
||||
.map_with_span(|((pub_opaque, (name, parameters)), constructors), span| {
|
||||
ast::UntypedDefinition::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![],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn type_parser() -> impl Parser<Token, ast::Annotation, Error = ParseError> {
|
||||
recursive(|r| {
|
||||
choice((
|
||||
select! {Token::DiscardName { name } => name}.map_with_span(|name, span| {
|
||||
ast::Annotation::Hole {
|
||||
location: span,
|
||||
name,
|
||||
}
|
||||
}),
|
||||
just(Token::Fn)
|
||||
.ignore_then(
|
||||
r.clone()
|
||||
.separated_by(just(Token::Comma))
|
||||
.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),
|
||||
}),
|
||||
select! {Token::UpName { name } => name}
|
||||
.then(
|
||||
r.clone()
|
||||
.separated_by(just(Token::Comma))
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.or_not(),
|
||||
)
|
||||
.map_with_span(|(name, arguments), span| ast::Annotation::Constructor {
|
||||
location: span,
|
||||
module: None,
|
||||
name,
|
||||
arguments: arguments.unwrap_or_default(),
|
||||
}),
|
||||
select! {Token::Name { name } => name}
|
||||
.then(
|
||||
just(Token::Dot)
|
||||
.ignore_then(select! {Token::UpName {name} => name})
|
||||
.then(
|
||||
r.separated_by(just(Token::Comma))
|
||||
.delimited_by(just(Token::LeftParen), just(Token::RightParen))
|
||||
.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 {
|
||||
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))
|
||||
.delimited_by(just(Token::LeftBrace), just(Token::RightBrace))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -79,6 +242,16 @@ mod tests {
|
|||
use std/list
|
||||
use std/address.{Address as A, thing as w}
|
||||
use std/tx as t
|
||||
|
||||
type Option(a) {
|
||||
Some(a, Int)
|
||||
None
|
||||
Wow { name: Int, age: Int }
|
||||
}
|
||||
|
||||
pub opaque type User {
|
||||
name: _w
|
||||
}
|
||||
"#;
|
||||
let len = code.chars().count();
|
||||
|
||||
|
@ -138,6 +311,111 @@ mod tests {
|
|||
as_name: Some("t".to_string()),
|
||||
unqualified: vec![],
|
||||
package: (),
|
||||
},
|
||||
ast::UntypedDefinition::DataType {
|
||||
location: Span::new(SrcId::empty(), 122..246),
|
||||
constructors: vec![
|
||||
ast::RecordConstructor {
|
||||
location: Span::new(SrcId::empty(), 155..167),
|
||||
name: "Some".to_string(),
|
||||
arguments: vec![
|
||||
ast::RecordConstructorArg {
|
||||
label: None,
|
||||
annotation: ast::Annotation::Var {
|
||||
location: Span::new(SrcId::empty(), 160..161),
|
||||
name: "a".to_string(),
|
||||
},
|
||||
location: Span::new(SrcId::empty(), 160..161),
|
||||
tipo: (),
|
||||
doc: None,
|
||||
},
|
||||
ast::RecordConstructorArg {
|
||||
label: None,
|
||||
annotation: ast::Annotation::Constructor {
|
||||
location: Span::new(SrcId::empty(), 163..166),
|
||||
module: None,
|
||||
name: "Int".to_string(),
|
||||
arguments: vec![],
|
||||
},
|
||||
location: Span::new(SrcId::empty(), 163..166),
|
||||
tipo: (),
|
||||
doc: None,
|
||||
},
|
||||
],
|
||||
documentation: None,
|
||||
sugar: false,
|
||||
},
|
||||
ast::RecordConstructor {
|
||||
location: Span::new(SrcId::empty(), 184..188),
|
||||
name: "None".to_string(),
|
||||
arguments: vec![],
|
||||
documentation: None,
|
||||
sugar: false,
|
||||
},
|
||||
ast::RecordConstructor {
|
||||
location: Span::new(SrcId::empty(), 205..232),
|
||||
name: "Wow".to_string(),
|
||||
arguments: vec![
|
||||
ast::RecordConstructorArg {
|
||||
label: Some("name".to_string(),),
|
||||
annotation: ast::Annotation::Constructor {
|
||||
location: Span::new(SrcId::empty(), 217..220),
|
||||
module: None,
|
||||
name: "Int".to_string(),
|
||||
arguments: vec![],
|
||||
},
|
||||
location: Span::new(SrcId::empty(), 211..220),
|
||||
tipo: (),
|
||||
doc: None,
|
||||
},
|
||||
ast::RecordConstructorArg {
|
||||
label: Some("age".to_string(),),
|
||||
annotation: ast::Annotation::Constructor {
|
||||
location: Span::new(SrcId::empty(), 227..230),
|
||||
module: None,
|
||||
name: "Int".to_string(),
|
||||
arguments: vec![],
|
||||
},
|
||||
location: Span::new(SrcId::empty(), 222..230),
|
||||
tipo: (),
|
||||
doc: None,
|
||||
},
|
||||
],
|
||||
documentation: None,
|
||||
sugar: false,
|
||||
},
|
||||
],
|
||||
doc: None,
|
||||
name: "Option".to_string(),
|
||||
opaque: false,
|
||||
parameters: vec!["a".to_string(),],
|
||||
public: false,
|
||||
typed_parameters: vec![],
|
||||
},
|
||||
ast::UntypedDefinition::DataType {
|
||||
location: Span::new(SrcId::empty(), 260..321),
|
||||
constructors: vec![ast::RecordConstructor {
|
||||
location: Span::new(SrcId::empty(), 281..321),
|
||||
name: "User".to_string(),
|
||||
arguments: vec![ast::RecordConstructorArg {
|
||||
label: Some("name".to_string(),),
|
||||
annotation: ast::Annotation::Hole {
|
||||
location: Span::new(SrcId::empty(), 305..307),
|
||||
name: "_w".to_string(),
|
||||
},
|
||||
location: Span::new(SrcId::empty(), 299..307),
|
||||
tipo: (),
|
||||
doc: None,
|
||||
},],
|
||||
documentation: None,
|
||||
sugar: true,
|
||||
},],
|
||||
doc: None,
|
||||
name: "User".to_string(),
|
||||
opaque: true,
|
||||
parameters: vec![],
|
||||
public: true,
|
||||
typed_parameters: vec![],
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -7,7 +7,7 @@ pub enum Token {
|
|||
Error(char),
|
||||
Name { name: String },
|
||||
UpName { name: String },
|
||||
DiscardName { name: Intern<String> },
|
||||
DiscardName { name: String },
|
||||
Int { value: Intern<String> },
|
||||
String { value: Intern<String> },
|
||||
// Groupings
|
||||
|
@ -84,7 +84,7 @@ impl fmt::Display for Token {
|
|||
}
|
||||
Token::Name { name } => name,
|
||||
Token::UpName { name } => name,
|
||||
Token::DiscardName { name } => &**name,
|
||||
Token::DiscardName { name } => name,
|
||||
Token::Int { value } => &**value,
|
||||
Token::String { value } => &**value,
|
||||
Token::LeftParen => "(",
|
||||
|
|
Loading…
Reference in New Issue