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)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
||||||
Fn {
|
Fn {
|
||||||
location: Span,
|
|
||||||
arguments: Vec<Vec<Arg<T>>>,
|
arguments: Vec<Vec<Arg<T>>>,
|
||||||
body: Expr,
|
body: Expr,
|
||||||
doc: Option<String>,
|
doc: Option<String>,
|
||||||
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
public: bool,
|
public: bool,
|
||||||
return_annotation: Option<Annotation>,
|
return_annotation: Option<Annotation>,
|
||||||
|
@ -43,19 +43,19 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
||||||
},
|
},
|
||||||
|
|
||||||
TypeAlias {
|
TypeAlias {
|
||||||
location: Span,
|
|
||||||
alias: String,
|
alias: String,
|
||||||
annotation: Annotation,
|
annotation: Annotation,
|
||||||
doc: Option<String>,
|
doc: Option<String>,
|
||||||
|
location: Span,
|
||||||
parameters: Vec<String>,
|
parameters: Vec<String>,
|
||||||
public: bool,
|
public: bool,
|
||||||
tipo: T,
|
tipo: T,
|
||||||
},
|
},
|
||||||
|
|
||||||
DataType {
|
DataType {
|
||||||
location: Span,
|
|
||||||
constructors: Vec<RecordConstructor<T>>,
|
constructors: Vec<RecordConstructor<T>>,
|
||||||
doc: Option<String>,
|
doc: Option<String>,
|
||||||
|
location: Span,
|
||||||
name: String,
|
name: String,
|
||||||
opaque: bool,
|
opaque: bool,
|
||||||
parameters: Vec<String>,
|
parameters: Vec<String>,
|
||||||
|
@ -64,11 +64,11 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
|
||||||
},
|
},
|
||||||
|
|
||||||
Use {
|
Use {
|
||||||
|
as_name: Option<String>,
|
||||||
location: Span,
|
location: Span,
|
||||||
module: Vec<String>,
|
module: Vec<String>,
|
||||||
as_name: Option<String>,
|
|
||||||
unqualified: Vec<UnqualifiedImport>,
|
|
||||||
package: PackageName,
|
package: PackageName,
|
||||||
|
unqualified: Vec<UnqualifiedImport>,
|
||||||
},
|
},
|
||||||
|
|
||||||
ModuleConstant {
|
ModuleConstant {
|
||||||
|
@ -151,6 +151,7 @@ pub struct RecordConstructor<T> {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub arguments: Vec<RecordConstructorArg<T>>,
|
pub arguments: Vec<RecordConstructorArg<T>>,
|
||||||
pub documentation: Option<String>,
|
pub documentation: Option<String>,
|
||||||
|
pub sugar: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[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::Percent),
|
||||||
just("|>").to(Token::Pipe),
|
just("|>").to(Token::Pipe),
|
||||||
just(',').to(Token::Comma),
|
just(',').to(Token::Comma),
|
||||||
|
just(':').to(Token::Colon),
|
||||||
));
|
));
|
||||||
|
|
||||||
let grouping = choice((
|
let grouping = choice((
|
||||||
|
@ -81,7 +82,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
} else if s.starts_with('_') {
|
} else if s.starts_with('_') {
|
||||||
Token::DiscardName {
|
Token::DiscardName {
|
||||||
// TODO: do not allow uppercase letters in discard name
|
// TODO: do not allow uppercase letters in discard name
|
||||||
name: Intern::new(s),
|
name: s,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Token::Name {
|
Token::Name {
|
||||||
|
@ -125,7 +126,6 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use chumsky::prelude::*;
|
use chumsky::prelude::*;
|
||||||
use internment::Intern;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::{Span, SrcId},
|
ast::{Span, SrcId},
|
||||||
|
@ -157,7 +157,7 @@ mod tests {
|
||||||
name: "Thing".to_string()
|
name: "Thing".to_string()
|
||||||
},
|
},
|
||||||
Token::DiscardName {
|
Token::DiscardName {
|
||||||
name: Intern::new("_na_thing".to_string())
|
name: "_na_thing".to_string()
|
||||||
},
|
},
|
||||||
Token::Name {
|
Token::Name {
|
||||||
name: "name".to_string()
|
name: "name".to_string()
|
||||||
|
|
|
@ -5,6 +5,19 @@ use crate::{ast, error::ParseError, token::Token};
|
||||||
pub fn module_parser(
|
pub fn module_parser(
|
||||||
kind: ast::ModuleKind,
|
kind: ast::ModuleKind,
|
||||||
) -> impl Parser<Token, ast::UntypedModule, Error = ParseError> {
|
) -> 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((
|
let unqualified_import = choice((
|
||||||
select! {Token::Name { name } => name}.then(
|
select! {Token::Name { name } => name}.then(
|
||||||
just(Token::As)
|
just(Token::As)
|
||||||
|
@ -41,7 +54,7 @@ pub fn module_parser(
|
||||||
.then(unqualified_imports)
|
.then(unqualified_imports)
|
||||||
.then(as_name);
|
.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, unqualified), as_name), span| ast::UntypedDefinition::Use {
|
||||||
module,
|
module,
|
||||||
as_name,
|
as_name,
|
||||||
|
@ -49,18 +62,168 @@ pub fn module_parser(
|
||||||
package: (),
|
package: (),
|
||||||
location: span,
|
location: span,
|
||||||
},
|
},
|
||||||
);
|
)
|
||||||
|
}
|
||||||
|
|
||||||
choice((import,))
|
pub fn data_parser() -> impl Parser<Token, ast::UntypedDefinition, Error = ParseError> {
|
||||||
.repeated()
|
let unlabeled_constructor_type_args = type_parser()
|
||||||
.then_ignore(end())
|
.map_with_span(|annotation, span| ast::RecordConstructorArg {
|
||||||
.map(move |definitions| ast::UntypedModule {
|
label: None,
|
||||||
kind,
|
annotation,
|
||||||
definitions,
|
tipo: (),
|
||||||
docs: vec![],
|
doc: None,
|
||||||
name: vec![],
|
location: span,
|
||||||
type_info: (),
|
|
||||||
})
|
})
|
||||||
|
.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)]
|
#[cfg(test)]
|
||||||
|
@ -79,6 +242,16 @@ mod tests {
|
||||||
use std/list
|
use std/list
|
||||||
use std/address.{Address as A, thing as w}
|
use std/address.{Address as A, thing as w}
|
||||||
use std/tx as t
|
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();
|
let len = code.chars().count();
|
||||||
|
|
||||||
|
@ -138,6 +311,111 @@ mod tests {
|
||||||
as_name: Some("t".to_string()),
|
as_name: Some("t".to_string()),
|
||||||
unqualified: vec![],
|
unqualified: vec![],
|
||||||
package: (),
|
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),
|
Error(char),
|
||||||
Name { name: String },
|
Name { name: String },
|
||||||
UpName { name: String },
|
UpName { name: String },
|
||||||
DiscardName { name: Intern<String> },
|
DiscardName { name: String },
|
||||||
Int { value: Intern<String> },
|
Int { value: Intern<String> },
|
||||||
String { value: Intern<String> },
|
String { value: Intern<String> },
|
||||||
// Groupings
|
// Groupings
|
||||||
|
@ -84,7 +84,7 @@ impl fmt::Display for Token {
|
||||||
}
|
}
|
||||||
Token::Name { name } => name,
|
Token::Name { name } => name,
|
||||||
Token::UpName { name } => name,
|
Token::UpName { name } => name,
|
||||||
Token::DiscardName { name } => &**name,
|
Token::DiscardName { name } => name,
|
||||||
Token::Int { value } => &**value,
|
Token::Int { value } => &**value,
|
||||||
Token::String { value } => &**value,
|
Token::String { value } => &**value,
|
||||||
Token::LeftParen => "(",
|
Token::LeftParen => "(",
|
||||||
|
|
Loading…
Reference in New Issue