feat: parse imports

This commit is contained in:
rvcas 2022-08-13 01:13:18 -04:00
parent 1d6809661c
commit 1d1a6fc404
No known key found for this signature in database
GPG Key ID: C09B64E263F7D68C
6 changed files with 186 additions and 13 deletions

View File

@ -10,23 +10,26 @@ use crate::{
pub type TypedModule = Module<tipo::Module, TypedDefinition>; pub type TypedModule = Module<tipo::Module, TypedDefinition>;
pub type UntypedModule = Module<(), UntypedDefinition>; pub type UntypedModule = Module<(), UntypedDefinition>;
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum ModuleKind { pub enum ModuleKind {
Contract, Contract,
Lib, Lib,
Script, Script,
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Module<Info, Definitions> { pub struct Module<Info, Definitions> {
pub name: Vec<String>, pub name: Vec<String>,
pub docs: Vec<String>, pub docs: Vec<String>,
pub type_info: Info, pub type_info: Info,
pub definitons: Vec<Definitions>, pub definitions: Vec<Definitions>,
pub kind: ModuleKind, pub kind: ModuleKind,
} }
pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, String, String>; pub type TypedDefinition = Definition<Arc<Type>, TypedExpr, String, String>;
pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>; pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>;
#[derive(Debug, Clone, PartialEq)]
pub enum Definition<T, Expr, ConstantRecordTag, PackageName> { pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
Fn { Fn {
location: Span, location: Span,
@ -61,6 +64,7 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
}, },
Use { Use {
location: Span,
module: Vec<String>, module: Vec<String>,
as_name: Option<String>, as_name: Option<String>,
unqualified: Vec<UnqualifiedImport>, unqualified: Vec<UnqualifiedImport>,
@ -81,6 +85,7 @@ pub enum Definition<T, Expr, ConstantRecordTag, PackageName> {
pub type TypedConstant = Constant<Arc<Type>, String>; pub type TypedConstant = Constant<Arc<Type>, String>;
pub type UntypedConstant = Constant<(), ()>; pub type UntypedConstant = Constant<(), ()>;
#[derive(Debug, Clone, PartialEq)]
pub enum Constant<T, RecordTag> { pub enum Constant<T, RecordTag> {
Int { Int {
location: Span, location: Span,
@ -127,17 +132,20 @@ pub enum Constant<T, RecordTag> {
}, },
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CallArg<A> { pub struct CallArg<A> {
pub label: Option<String>, pub label: Option<String>,
pub location: Span, pub location: Span,
pub value: A, pub value: A,
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FieldMap { pub struct FieldMap {
pub arity: usize, pub arity: usize,
pub fields: HashMap<String, usize>, pub fields: HashMap<String, usize>,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct RecordConstructor<T> { pub struct RecordConstructor<T> {
pub location: Span, pub location: Span,
pub name: String, pub name: String,
@ -145,6 +153,7 @@ pub struct RecordConstructor<T> {
pub documentation: Option<String>, pub documentation: Option<String>,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct RecordConstructorArg<T> { pub struct RecordConstructorArg<T> {
pub label: Option<String>, pub label: Option<String>,
// ast // ast
@ -154,6 +163,7 @@ pub struct RecordConstructorArg<T> {
pub doc: Option<String>, pub doc: Option<String>,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct Arg<T> { pub struct Arg<T> {
pub names: ArgName, pub names: ArgName,
pub location: Span, pub location: Span,
@ -161,6 +171,7 @@ pub struct Arg<T> {
pub tipo: T, pub tipo: T,
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ArgName { pub enum ArgName {
Discard { name: String }, Discard { name: String },
LabeledDiscard { label: String, name: String }, LabeledDiscard { label: String, name: String },
@ -168,6 +179,7 @@ pub enum ArgName {
NamedLabeled { name: String, label: String }, NamedLabeled { name: String, label: String },
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UnqualifiedImport { pub struct UnqualifiedImport {
pub location: Span, pub location: Span,
pub name: String, pub name: String,
@ -176,6 +188,7 @@ pub struct UnqualifiedImport {
} }
// TypeAst // TypeAst
#[derive(Debug, Clone, PartialEq)]
pub enum Annotation { pub enum Annotation {
Constructor { Constructor {
location: Span, location: Span,
@ -206,6 +219,7 @@ pub enum Annotation {
}, },
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Layer { pub enum Layer {
Value, Value,
Type, Type,
@ -217,6 +231,7 @@ impl Default for Layer {
} }
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BinOp { pub enum BinOp {
// Boolean logic // Boolean logic
And, And,
@ -240,6 +255,7 @@ pub enum BinOp {
ModInt, ModInt,
} }
#[derive(Debug, Clone, PartialEq)]
pub enum Pattern<Constructor, Type> { pub enum Pattern<Constructor, Type> {
Int { Int {
location: Span, location: Span,
@ -309,6 +325,7 @@ pub enum Pattern<Constructor, Type> {
}, },
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AssignmentKind { pub enum AssignmentKind {
Let, Let,
Assert, Assert,
@ -323,6 +340,7 @@ pub type TypedClause = Clause<TypedExpr, PatternConstructor, Arc<Type>, String>;
pub type UntypedClause = Clause<UntypedExpr, (), (), ()>; pub type UntypedClause = Clause<UntypedExpr, (), (), ()>;
#[derive(Debug, Clone, PartialEq)]
pub struct Clause<Expr, PatternConstructor, Type, RecordTag> { pub struct Clause<Expr, PatternConstructor, Type, RecordTag> {
pub location: Span, pub location: Span,
pub pattern: MultiPattern<PatternConstructor, Type>, pub pattern: MultiPattern<PatternConstructor, Type>,
@ -331,6 +349,7 @@ pub struct Clause<Expr, PatternConstructor, Type, RecordTag> {
pub then: Expr, pub then: Expr,
} }
#[derive(Debug, Clone, PartialEq)]
pub enum ClauseGuard<Type, RecordTag> { pub enum ClauseGuard<Type, RecordTag> {
Equals { Equals {
location: Span, location: Span,
@ -403,17 +422,20 @@ pub struct TypedRecordUpdateArg {
pub index: usize, pub index: usize,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct UntypedRecordUpdateArg { pub struct UntypedRecordUpdateArg {
pub label: String, pub label: String,
// pub location: SrcSpan, // pub location: SrcSpan,
pub value: UntypedExpr, pub value: UntypedExpr,
} }
#[derive(Debug, Clone, PartialEq)]
pub struct RecordUpdateSpread { pub struct RecordUpdateSpread {
pub base: Box<UntypedExpr>, pub base: Box<UntypedExpr>,
pub location: Span, pub location: Span,
} }
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TodoKind { pub enum TodoKind {
Keyword, Keyword,
EmptyFunction, EmptyFunction,

View File

@ -153,6 +153,7 @@ pub enum TypedExpr {
}, },
} }
#[derive(Debug, Clone, PartialEq)]
pub enum UntypedExpr { pub enum UntypedExpr {
Int { Int {
location: Span, location: Span,

View File

@ -26,6 +26,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
just('/').to(Token::Slash), just('/').to(Token::Slash),
just('%').to(Token::Percent), just('%').to(Token::Percent),
just("|>").to(Token::Pipe), just("|>").to(Token::Pipe),
just(',').to(Token::Comma),
)); ));
let grouping = choice(( let grouping = choice((
@ -75,7 +76,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
if s.chars().next().map_or(false, |c| c.is_uppercase()) { if s.chars().next().map_or(false, |c| c.is_uppercase()) {
Token::UpName { Token::UpName {
// TODO: do not allow _ in upname // TODO: do not allow _ in upname
name: Intern::new(s), name: s,
} }
} else if s.starts_with('_') { } else if s.starts_with('_') {
Token::DiscardName { Token::DiscardName {
@ -85,7 +86,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
} else { } else {
Token::Name { Token::Name {
// TODO: do not allow uppercase letters in name // TODO: do not allow uppercase letters in name
name: Intern::new(s), name: s,
} }
} }
} }
@ -93,7 +94,11 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = ParseError> {
let token = choice((keyword, int, op, grouping, string)) let token = choice((keyword, int, op, grouping, string))
.or(any().map(Token::Error).validate(|t, span, emit| { .or(any().map(Token::Error).validate(|t, span, emit| {
emit(ParseError::expected_input_found(span, None, Some(t))); emit(ParseError::expected_input_found(
span,
None,
Some(t.clone()),
));
t t
})) }))
.map_with_span(move |token, span| (token, span)) .map_with_span(move |token, span| (token, span))
@ -149,13 +154,13 @@ mod tests {
Token::GreaterEqual, Token::GreaterEqual,
Token::LeftBrace, Token::LeftBrace,
Token::UpName { Token::UpName {
name: Intern::new("Thing".to_string()) name: "Thing".to_string()
}, },
Token::DiscardName { Token::DiscardName {
name: Intern::new("_na_thing".to_string()) name: Intern::new("_na_thing".to_string())
}, },
Token::Name { Token::Name {
name: Intern::new("name".to_string()) name: "name".to_string()
} }
]), ]),
); );

View File

@ -2,6 +2,147 @@ use chumsky::prelude::*;
use crate::{ast, error::ParseError, token::Token}; use crate::{ast, error::ParseError, token::Token};
pub fn module_parser() -> impl Parser<Token, ast::UntypedModule, Error = ParseError> { pub fn module_parser(
let imports = just(Token::Use).ignore_then(); kind: ast::ModuleKind,
) -> impl Parser<Token, ast::UntypedModule, 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))
.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);
let import = just(Token::Use).ignore_then(module_path).map_with_span(
|((module, unqualified), as_name), span| ast::UntypedDefinition::Use {
module,
as_name,
unqualified: unqualified.unwrap_or_default(),
package: (),
location: span,
},
);
choice((import,))
.repeated()
.then_ignore(end())
.map(move |definitions| ast::UntypedModule {
kind,
definitions,
docs: vec![],
name: vec![],
type_info: (),
})
}
#[cfg(test)]
mod tests {
use chumsky::prelude::*;
use crate::{
ast::{self, Span, SrcId},
lexer,
parser::module_parser,
};
#[test]
fn simple() {
let code = r#"
use std/list
use std/address.{Address as A, thing as w}
use std/tx as t
"#;
let len = code.chars().count();
let span = |i| Span::new(SrcId::empty(), i..i + 1);
let tokens = lexer::lexer()
.parse(chumsky::Stream::from_iter(
span(len),
code.chars().enumerate().map(|(i, c)| (c, span(i))),
))
.unwrap();
dbg!(tokens.clone());
let res = module_parser(ast::ModuleKind::Script)
.parse(chumsky::Stream::from_iter(span(len), tokens.into_iter()))
.unwrap();
assert_eq!(
res,
ast::UntypedModule {
docs: vec![],
kind: ast::ModuleKind::Script,
name: vec![],
type_info: (),
definitions: vec![
ast::UntypedDefinition::Use {
location: Span::new(SrcId::empty(), 13..25),
module: vec!["std".to_string(), "list".to_string()],
as_name: None,
unqualified: vec![],
package: (),
},
ast::UntypedDefinition::Use {
location: Span::new(SrcId::empty(), 38..80),
module: vec!["std".to_string(), "address".to_string()],
as_name: None,
unqualified: vec![
ast::UnqualifiedImport {
as_name: Some("A".to_string()),
location: Span::new(SrcId::empty(), 55..67),
layer: Default::default(),
name: "Address".to_string()
},
ast::UnqualifiedImport {
as_name: Some("w".to_string()),
location: Span::new(SrcId::empty(), 69..79),
layer: Default::default(),
name: "thing".to_string()
}
],
package: (),
},
ast::UntypedDefinition::Use {
location: Span::new(SrcId::empty(), 93..108),
module: vec!["std".to_string(), "tx".to_string()],
as_name: Some("t".to_string()),
unqualified: vec![],
package: (),
}
]
},
"{:#?}",
res,
);
}
} }

View File

@ -5,6 +5,7 @@ use crate::{
build::Origin, build::Origin,
}; };
#[derive(Debug, Clone, PartialEq)]
pub enum Type { pub enum Type {
/// A nominal (named) type such as `Int`, `Float`, or a programmer defined /// A nominal (named) type such as `Int`, `Float`, or a programmer defined
/// custom type such as `Person`. The type can take other types as /// custom type such as `Person`. The type can take other types as
@ -39,6 +40,7 @@ pub enum Type {
Tuple { elems: Vec<Arc<Type>> }, Tuple { elems: Vec<Arc<Type>> },
} }
#[derive(Debug, Clone, PartialEq)]
pub enum TypeVar { pub enum TypeVar {
/// Unbound is an unbound variable. It is one specific type but we don't /// Unbound is an unbound variable. It is one specific type but we don't
/// know what yet in the inference process. It has a unique id which can be used to /// know what yet in the inference process. It has a unique id which can be used to
@ -65,12 +67,14 @@ pub enum TypeVar {
Generic { id: u64 }, Generic { id: u64 },
} }
#[derive(Debug, Clone, PartialEq)]
pub struct ValueConstructor { pub struct ValueConstructor {
pub public: bool, pub public: bool,
pub variant: ValueConstructorVariant, pub variant: ValueConstructorVariant,
pub tipo: Arc<Type>, pub tipo: Arc<Type>,
} }
#[derive(Debug, Clone, PartialEq)]
pub enum ValueConstructorVariant { pub enum ValueConstructorVariant {
/// A locally defined variable or function parameter /// A locally defined variable or function parameter
LocalVariable { location: Span }, LocalVariable { location: Span },

View File

@ -2,11 +2,11 @@ use std::fmt;
use internment::Intern; use internment::Intern;
#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)] #[derive(Clone, Debug, PartialEq, Hash, Eq)]
pub enum Token { pub enum Token {
Error(char), Error(char),
Name { name: Intern<String> }, Name { name: String },
UpName { name: Intern<String> }, UpName { name: String },
DiscardName { name: Intern<String> }, DiscardName { name: Intern<String> },
Int { value: Intern<String> }, Int { value: Intern<String> },
String { value: Intern<String> }, String { value: Intern<String> },
@ -82,8 +82,8 @@ impl fmt::Display for Token {
return Ok(()); return Ok(());
} }
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,