diff --git a/crates/lang/src/ast.rs b/crates/lang/src/ast.rs index d4eae330..f33d21ec 100644 --- a/crates/lang/src/ast.rs +++ b/crates/lang/src/ast.rs @@ -10,23 +10,26 @@ use crate::{ pub type TypedModule = Module; pub type UntypedModule = Module<(), UntypedDefinition>; +#[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum ModuleKind { Contract, Lib, Script, } +#[derive(Debug, Clone, PartialEq, Eq)] pub struct Module { pub name: Vec, pub docs: Vec, pub type_info: Info, - pub definitons: Vec, + pub definitions: Vec, pub kind: ModuleKind, } pub type TypedDefinition = Definition, TypedExpr, String, String>; pub type UntypedDefinition = Definition<(), UntypedExpr, (), ()>; +#[derive(Debug, Clone, PartialEq)] pub enum Definition { Fn { location: Span, @@ -61,6 +64,7 @@ pub enum Definition { }, Use { + location: Span, module: Vec, as_name: Option, unqualified: Vec, @@ -81,6 +85,7 @@ pub enum Definition { pub type TypedConstant = Constant, String>; pub type UntypedConstant = Constant<(), ()>; +#[derive(Debug, Clone, PartialEq)] pub enum Constant { Int { location: Span, @@ -127,17 +132,20 @@ pub enum Constant { }, } +#[derive(Debug, Clone, PartialEq, Eq)] pub struct CallArg { pub label: Option, pub location: Span, pub value: A, } +#[derive(Debug, Clone, PartialEq, Eq)] pub struct FieldMap { pub arity: usize, pub fields: HashMap, } +#[derive(Debug, Clone, PartialEq)] pub struct RecordConstructor { pub location: Span, pub name: String, @@ -145,6 +153,7 @@ pub struct RecordConstructor { pub documentation: Option, } +#[derive(Debug, Clone, PartialEq)] pub struct RecordConstructorArg { pub label: Option, // ast @@ -154,6 +163,7 @@ pub struct RecordConstructorArg { pub doc: Option, } +#[derive(Debug, Clone, PartialEq)] pub struct Arg { pub names: ArgName, pub location: Span, @@ -161,6 +171,7 @@ pub struct Arg { pub tipo: T, } +#[derive(Debug, Clone, PartialEq, Eq)] pub enum ArgName { Discard { name: String }, LabeledDiscard { label: String, name: String }, @@ -168,6 +179,7 @@ pub enum ArgName { NamedLabeled { name: String, label: String }, } +#[derive(Debug, Clone, PartialEq, Eq)] pub struct UnqualifiedImport { pub location: Span, pub name: String, @@ -176,6 +188,7 @@ pub struct UnqualifiedImport { } // TypeAst +#[derive(Debug, Clone, PartialEq)] pub enum Annotation { Constructor { location: Span, @@ -206,6 +219,7 @@ pub enum Annotation { }, } +#[derive(Debug, Clone, PartialEq, Eq)] pub enum Layer { Value, Type, @@ -217,6 +231,7 @@ impl Default for Layer { } } +#[derive(Debug, Clone, PartialEq, Eq)] pub enum BinOp { // Boolean logic And, @@ -240,6 +255,7 @@ pub enum BinOp { ModInt, } +#[derive(Debug, Clone, PartialEq)] pub enum Pattern { Int { location: Span, @@ -309,6 +325,7 @@ pub enum Pattern { }, } +#[derive(Debug, Clone, PartialEq, Eq)] pub enum AssignmentKind { Let, Assert, @@ -323,6 +340,7 @@ pub type TypedClause = Clause, String>; pub type UntypedClause = Clause; +#[derive(Debug, Clone, PartialEq)] pub struct Clause { pub location: Span, pub pattern: MultiPattern, @@ -331,6 +349,7 @@ pub struct Clause { pub then: Expr, } +#[derive(Debug, Clone, PartialEq)] pub enum ClauseGuard { Equals { location: Span, @@ -403,17 +422,20 @@ pub struct TypedRecordUpdateArg { pub index: usize, } +#[derive(Debug, Clone, PartialEq)] pub struct UntypedRecordUpdateArg { pub label: String, // pub location: SrcSpan, pub value: UntypedExpr, } +#[derive(Debug, Clone, PartialEq)] pub struct RecordUpdateSpread { pub base: Box, pub location: Span, } +#[derive(Debug, Clone, PartialEq, Eq)] pub enum TodoKind { Keyword, EmptyFunction, diff --git a/crates/lang/src/expr.rs b/crates/lang/src/expr.rs index e3fd064e..a6a067f6 100644 --- a/crates/lang/src/expr.rs +++ b/crates/lang/src/expr.rs @@ -153,6 +153,7 @@ pub enum TypedExpr { }, } +#[derive(Debug, Clone, PartialEq)] pub enum UntypedExpr { Int { location: Span, diff --git a/crates/lang/src/lexer.rs b/crates/lang/src/lexer.rs index 958538ba..58d9ab0d 100644 --- a/crates/lang/src/lexer.rs +++ b/crates/lang/src/lexer.rs @@ -26,6 +26,7 @@ pub fn lexer() -> impl Parser, Error = ParseError> { just('/').to(Token::Slash), just('%').to(Token::Percent), just("|>").to(Token::Pipe), + just(',').to(Token::Comma), )); let grouping = choice(( @@ -75,7 +76,7 @@ pub fn lexer() -> impl Parser, Error = ParseError> { if s.chars().next().map_or(false, |c| c.is_uppercase()) { Token::UpName { // TODO: do not allow _ in upname - name: Intern::new(s), + name: s, } } else if s.starts_with('_') { Token::DiscardName { @@ -85,7 +86,7 @@ pub fn lexer() -> impl Parser, Error = ParseError> { } else { Token::Name { // TODO: do not allow uppercase letters in name - name: Intern::new(s), + name: s, } } } @@ -93,7 +94,11 @@ pub fn lexer() -> impl Parser, Error = ParseError> { let token = choice((keyword, int, op, grouping, string)) .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 })) .map_with_span(move |token, span| (token, span)) @@ -149,13 +154,13 @@ mod tests { Token::GreaterEqual, Token::LeftBrace, Token::UpName { - name: Intern::new("Thing".to_string()) + name: "Thing".to_string() }, Token::DiscardName { name: Intern::new("_na_thing".to_string()) }, Token::Name { - name: Intern::new("name".to_string()) + name: "name".to_string() } ]), ); diff --git a/crates/lang/src/parser.rs b/crates/lang/src/parser.rs index e2dcf2f6..c5473892 100644 --- a/crates/lang/src/parser.rs +++ b/crates/lang/src/parser.rs @@ -2,6 +2,147 @@ use chumsky::prelude::*; use crate::{ast, error::ParseError, token::Token}; -pub fn module_parser() -> impl Parser { - let imports = just(Token::Use).ignore_then(); +pub fn module_parser( + kind: ast::ModuleKind, +) -> impl Parser { + 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, + ); + } } diff --git a/crates/lang/src/tipo.rs b/crates/lang/src/tipo.rs index 85299bae..265c415c 100644 --- a/crates/lang/src/tipo.rs +++ b/crates/lang/src/tipo.rs @@ -5,6 +5,7 @@ use crate::{ build::Origin, }; +#[derive(Debug, Clone, PartialEq)] pub enum Type { /// A nominal (named) type such as `Int`, `Float`, or a programmer defined /// custom type such as `Person`. The type can take other types as @@ -39,6 +40,7 @@ pub enum Type { Tuple { elems: Vec> }, } +#[derive(Debug, Clone, PartialEq)] pub enum TypeVar { /// 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 @@ -65,12 +67,14 @@ pub enum TypeVar { Generic { id: u64 }, } +#[derive(Debug, Clone, PartialEq)] pub struct ValueConstructor { pub public: bool, pub variant: ValueConstructorVariant, pub tipo: Arc, } +#[derive(Debug, Clone, PartialEq)] pub enum ValueConstructorVariant { /// A locally defined variable or function parameter LocalVariable { location: Span }, diff --git a/crates/lang/src/token.rs b/crates/lang/src/token.rs index 3b2d947b..90b1f4ea 100644 --- a/crates/lang/src/token.rs +++ b/crates/lang/src/token.rs @@ -2,11 +2,11 @@ use std::fmt; use internment::Intern; -#[derive(Copy, Clone, Debug, PartialEq, Hash, Eq)] +#[derive(Clone, Debug, PartialEq, Hash, Eq)] pub enum Token { Error(char), - Name { name: Intern }, - UpName { name: Intern }, + Name { name: String }, + UpName { name: String }, DiscardName { name: Intern }, Int { value: Intern }, String { value: Intern }, @@ -82,8 +82,8 @@ impl fmt::Display for Token { return Ok(()); } - Token::Name { name } => &**name, - Token::UpName { name } => &**name, + Token::Name { name } => name, + Token::UpName { name } => name, Token::DiscardName { name } => &**name, Token::Int { value } => &**value, Token::String { value } => &**value,