diff --git a/schala-lang/language/src/ast.rs b/schala-lang/language/src/ast.rs index 9baf563..c6d11aa 100644 --- a/schala-lang/language/src/ast.rs +++ b/schala-lang/language/src/ast.rs @@ -58,7 +58,8 @@ pub struct Statement { #[derive(Debug, PartialEq, Clone)] pub enum StatementKind { Expression(Expression), - Declaration(Declaration), //TODO Declaration should also be Meta-wrapped; only Expression and Declaration are Meta-wrapped maybe? + Declaration(Declaration), + Import(ImportSpecifier), } pub type Block = Vec; @@ -272,3 +273,20 @@ pub enum ForBody { MonadicReturn(Expression), StatementBlock(Block), } + +#[derive(Debug, Derivative, Clone)] +#[derivative(PartialEq)] +pub struct ImportSpecifier { + #[derivative(PartialEq="ignore")] + pub id: ItemId, + pub path_components: Vec>, + pub imported_names: ImportedNames +} + +#[derive(Debug, PartialEq, Clone)] +pub enum ImportedNames { + All, + LastOfPath, + List(Vec>) +} + diff --git a/schala-lang/language/src/parsing.rs b/schala-lang/language/src/parsing.rs index a7d164c..4a4f5d7 100644 --- a/schala-lang/language/src/parsing.rs +++ b/schala-lang/language/src/parsing.rs @@ -143,7 +143,8 @@ //! ``` //! ## Imports //! ```text -//! import := 'import' +//! import := 'import' IDENTIFIER (:: IDENTIFIER)* import_suffix +//! import_suffix := ε | '::{' IDENTIFIER (, IDENTIFIER)* '}' | '*' //TODO add qualified, exclusions, etc. //! ``` mod test; @@ -351,6 +352,7 @@ impl Parser { Keyword(Let) => self.binding_declaration().map(|decl| StatementKind::Declaration(decl)), Keyword(Interface) => self.interface_declaration().map(|decl| StatementKind::Declaration(decl)), Keyword(Impl) => self.impl_declaration().map(|decl| StatementKind::Declaration(decl)), + Keyword(Import) => self.import_declaration().map(|spec| StatementKind::Import(spec)), _ => self.expression().map(|expr| { StatementKind::Expression(expr.into()) } ), }?; Ok(Statement { kind, id: self.id_store.fresh() }) @@ -765,7 +767,8 @@ impl Parser { #[recursive_descent_method] fn identifier_expr(&mut self) -> ParseResult { use self::ExpressionKind::*; - let qualified_identifier = self.qualified_identifier()?; + let components = self.qualified_identifier()?; + let qualified_identifier = QualifiedName { id: self.id_store.fresh(), components }; Ok(match self.token_handler.peek_kind() { LCurlyBrace if !self.restrictions.no_struct_literal => { let fields = self.record_block()?; @@ -776,7 +779,7 @@ impl Parser { } #[recursive_descent_method] - fn qualified_identifier(&mut self) -> ParseResult { + fn qualified_identifier(&mut self) -> ParseResult>> { let mut components = vec![self.identifier()?]; loop { match (self.token_handler.peek_kind(), self.token_handler.peek_kind_n(1)) { @@ -787,7 +790,7 @@ impl Parser { _ => break, } } - Ok(QualifiedName { id: self.id_store.fresh(), components }) + Ok(components) } #[recursive_descent_method] @@ -939,18 +942,19 @@ impl Parser { fn simple_pattern(&mut self) -> ParseResult { Ok(match self.token_handler.peek_kind() { Identifier(_) => { - let qualified_name = self.qualified_identifier()?; + let components = self.qualified_identifier()?; + let qualified_identifier = QualifiedName { id: self.id_store.fresh(), components }; match self.token_handler.peek_kind() { LCurlyBrace => { let members = delimited!(self, LCurlyBrace, record_pattern_entry, Comma, RCurlyBrace); - Pattern::Record(qualified_name, members) + Pattern::Record(qualified_identifier, members) }, LParen => { let members = delimited!(self, LParen, pattern, Comma, RParen); - Pattern::TupleStruct(qualified_name, members) + Pattern::TupleStruct(qualified_identifier, members) }, _ => { - Pattern::VarOrName(qualified_name) + Pattern::VarOrName(qualified_identifier) }, } }, @@ -1233,6 +1237,59 @@ impl Parser { } Ok(ds) } + + #[recursive_descent_method] + fn import_declaration(&mut self) -> ParseResult { + expect!(self, Keyword(Import)); + let mut path_components = vec![]; + path_components.push(self.identifier()?); + loop { + match (self.token_handler.peek_kind(), self.token_handler.peek_kind_n(1)) { + (Colon, Colon) => { + self.token_handler.next(); self.token_handler.next(); + if let Identifier(_) = self.token_handler.peek_kind() { + path_components.push(self.identifier()?); + } else { + break; + } + }, + _ => break, + } + } + + let imported_names = match self.token_handler.peek_kind() { + LCurlyBrace => { + let names = delimited!(self, LCurlyBrace, identifier, Comma, RCurlyBrace); + ImportedNames::List(names) + }, + Operator(ref s) if **s == "*" => { + self.token_handler.next(); + ImportedNames::All + }, + _ => ImportedNames::LastOfPath + }; + + Ok(ImportSpecifier { + id: self.id_store.fresh(), + path_components, + imported_names + }) + } + + #[recursive_descent_method] + fn import_suffix(&mut self) -> ParseResult { + Ok(match self.token_handler.peek_kind() { + Operator(ref s) if **s == "*" => { + self.token_handler.next(); + ImportedNames::All + }, + LCurlyBrace => { + let names = delimited!(self, LCurlyBrace, identifier, Comma, RCurlyBrace); + ImportedNames::List(names) + }, + _ => return ParseError::new_with_token("Expected '{{' or '*'", self.token_handler.peek()), + }) + } } fn parse_binary(digits: String, tok: Token) -> ParseResult { @@ -1267,3 +1324,4 @@ fn parse_hex(digits: String, tok: Token) -> ParseResult { } Ok(result) } + diff --git a/schala-lang/language/src/parsing/test.rs b/schala-lang/language/src/parsing/test.rs index 83042ec..8e9312c 100644 --- a/schala-lang/language/src/parsing/test.rs +++ b/schala-lang/language/src/parsing/test.rs @@ -4,7 +4,7 @@ use std::str::FromStr; use super::tokenize; use super::ParseResult; -use crate::ast::{ItemIdStore, AST, Expression, Statement, StatementKind, IfExpressionBody, Discriminator, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody, InvocationArgument, FormalParam, PrefixOp, BinOp, QualifiedName}; +use crate::ast::{ItemIdStore, AST, Expression, Statement, StatementKind, IfExpressionBody, Discriminator, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody, InvocationArgument, FormalParam, PrefixOp, BinOp, QualifiedName, ImportSpecifier, ImportedNames}; use super::Declaration::*; use super::Signature; use super::TypeIdentifier::*; @@ -57,6 +57,12 @@ macro_rules! decl { }; } +macro_rules! import { + ($import_spec:expr) => { + Statement { id: ItemIdStore::new_id(), kind: StatementKind::Import($import_spec) } + } +} + macro_rules! ex { ($expr_type:expr) => { Expression::new(ItemIdStore::new_id(), $expr_type) }; ($expr_type:expr, $type_anno:expr) => { Expression::with_anno(ItemIdStore::new_id(), $expr_type, $type_anno) }; @@ -702,3 +708,55 @@ fn pattern_literals() { ) } } + +#[test] +fn imports() { + parse_test_wrap_ast! { + "import harbinger::draughts::Norgleheim", + import!(ImportSpecifier { + id: ItemIdStore::new_id(), + path_components: vec![rc!(harbinger), rc!(draughts), rc!(Norgleheim)], + imported_names: ImportedNames::LastOfPath + }) + } +} + +#[test] +fn imports_2() { + parse_test_wrap_ast! { + "import harbinger::draughts::{Norgleheim, Xraksenlaigar}", + import!(ImportSpecifier { + id: ItemIdStore::new_id(), + path_components: vec![rc!(harbinger), rc!(draughts)], + imported_names: ImportedNames::List(vec![ + rc!(Norgleheim), + rc!(Xraksenlaigar) + ]) + }) + } +} + +#[test] +fn imports_3() { + parse_test_wrap_ast! { + "import bespouri::{}", + import!(ImportSpecifier { + id: ItemIdStore::new_id(), + path_components: vec![rc!(bespouri)], + imported_names: ImportedNames::List(vec![]) + }) + } +} + + +#[test] +fn imports_4() { + parse_test_wrap_ast! { + "import bespouri::*", + import!(ImportSpecifier { + id: ItemIdStore::new_id(), + path_components: vec![rc!(bespouri)], + imported_names: ImportedNames::All + }) + } +} diff --git a/schala-lang/language/src/reduced_ast.rs b/schala-lang/language/src/reduced_ast.rs index 49272d1..08ee499 100644 --- a/schala-lang/language/src/reduced_ast.rs +++ b/schala-lang/language/src/reduced_ast.rs @@ -129,6 +129,7 @@ impl<'a> Reducer<'a> { match &stmt.kind { StatementKind::Expression(expr) => Stmt::Expr(self.expression(&expr)), StatementKind::Declaration(decl) => self.declaration(&decl), + StatementKind::Import(_) => Stmt::Noop, } } diff --git a/schala-lang/language/src/scope_resolution.rs b/schala-lang/language/src/scope_resolution.rs index 2f155b3..bb78424 100644 --- a/schala-lang/language/src/scope_resolution.rs +++ b/schala-lang/language/src/scope_resolution.rs @@ -14,6 +14,7 @@ impl<'a> ScopeResolver<'a> { match statement.kind { StatementKind::Declaration(ref decl) => self.decl(decl), StatementKind::Expression(ref expr) => self.expr(expr), + StatementKind::Import(_) => Ok(()) }?; } Ok(()) @@ -32,6 +33,7 @@ impl<'a> ScopeResolver<'a> { match statement.kind { StatementKind::Declaration(ref decl) => self.decl(decl), StatementKind::Expression(ref expr) => self.expr(expr), + StatementKind::Import(_) => Ok(()) }?; } Ok(()) diff --git a/schala-lang/language/src/typechecking.rs b/schala-lang/language/src/typechecking.rs index e4fd486..b25d036 100644 --- a/schala-lang/language/src/typechecking.rs +++ b/schala-lang/language/src/typechecking.rs @@ -274,6 +274,7 @@ impl<'a> TypeContext<'a> { match &statement.kind { StatementKind::Expression(e) => self.expr(e), StatementKind::Declaration(decl) => self.decl(&decl), + StatementKind::Import(_) => Ok(ty!(Unit)), } }