feat: parse custom types

This commit is contained in:
rvcas 2022-08-13 18:36:43 -04:00
parent 1d1a6fc404
commit fbc9b27efe
No known key found for this signature in database
GPG Key ID: C09B64E263F7D68C
4 changed files with 300 additions and 21 deletions

View File

@ -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)]

View File

@ -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()

View File

@ -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![],
} }
] ]
}, },

View File

@ -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 => "(",