diff --git a/schala-lang/language/src/ast.rs b/schala-lang/language/src/ast.rs index 55da055..b1cc40f 100644 --- a/schala-lang/language/src/ast.rs +++ b/schala-lang/language/src/ast.rs @@ -65,6 +65,7 @@ pub enum StatementKind { Expression(Expression), Declaration(Declaration), Import(ImportSpecifier), + Module(ModuleSpecifier), } pub type Block = Vec; @@ -297,3 +298,10 @@ pub enum ImportedNames { List(Vec>) } + +#[derive(Debug, PartialEq, Clone)] +pub struct ModuleSpecifier { + pub name: Rc, + pub contents: Vec, +} + diff --git a/schala-lang/language/src/ast/visitor.rs b/schala-lang/language/src/ast/visitor.rs index 9f0b783..05fc157 100644 --- a/schala-lang/language/src/ast/visitor.rs +++ b/schala-lang/language/src/ast/visitor.rs @@ -29,6 +29,7 @@ pub trait ASTVisitor: Sized { fn invocation_argument(&mut self, _arg: &InvocationArgument) {} fn formal_param(&mut self, _param: &FormalParam) {} fn import(&mut self, _import: &ImportSpecifier) {} + fn module(&mut self, _module: &ModuleSpecifier) {} fn qualified_name(&mut self, _name: &QualifiedName) {} fn nat_literal(&mut self, _n: u64) {} fn float_literal(&mut self, _f: f64) {} diff --git a/schala-lang/language/src/ast/walker.rs b/schala-lang/language/src/ast/walker.rs index a17406d..1125b38 100644 --- a/schala-lang/language/src/ast/walker.rs +++ b/schala-lang/language/src/ast/walker.rs @@ -28,6 +28,10 @@ fn statement(v: &mut V, statement: &Statement) { declaration(v, decl); }, Import(ref import_spec) => v.import(import_spec), + Module(ref module_spec) => { + v.module(module_spec); + walk_block(v, &module_spec.contents); + } } } diff --git a/schala-lang/language/src/parsing.rs b/schala-lang/language/src/parsing.rs index f78911c..e7d1630 100644 --- a/schala-lang/language/src/parsing.rs +++ b/schala-lang/language/src/parsing.rs @@ -13,7 +13,7 @@ //! ```text //! program := (statement delimiter)* EOF //! delimiter := NEWLINE | ";" -//! statement := expression | declaration | import +//! statement := expression | declaration | import | module //! block := "{" (statement delimiter)* "}" //! declaration := type_declaration | func_declaration | binding_declaration | impl_declaration //! ``` @@ -153,6 +153,10 @@ //! ```text //! import := 'import' IDENTIFIER (:: IDENTIFIER)* import_suffix //! import_suffix := ε | '::{' IDENTIFIER (, IDENTIFIER)* '}' | '*' //TODO add qualified, exclusions, etc. +//! +//! ## Modules +//! +//! module := 'module' IDENTIFIER '{' statement* '}' //! ``` mod test; @@ -370,6 +374,7 @@ impl Parser { 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)), + Keyword(Module) => self.module_declaration().map(|spec| StatementKind::Module(spec)), _ => self.expression().map(|expr| { StatementKind::Expression(expr) } ), }?; Ok(Statement { kind, id: self.id_store.fresh() }) @@ -1252,6 +1257,14 @@ impl Parser { _ => return ParseError::new_with_token("Expected '{{' or '*'", self.token_handler.peek()), }) } + + #[recursive_descent_method] + fn module_declaration(&mut self) -> ParseResult { + expect!(self, Keyword(Kw::Module)); + let name = self.identifier()?; + let contents = delimited!(self, LCurlyBrace, statement, Newline | Semicolon, RCurlyBrace, nonstrict); + Ok(ModuleSpecifier { name, contents }) + } } fn parse_binary(digits: String, tok: Token) -> ParseResult { diff --git a/schala-lang/language/src/parsing/test.rs b/schala-lang/language/src/parsing/test.rs index 6059842..d86d478 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, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody, InvocationArgument, FormalParam, PrefixOp, BinOp, QualifiedName, ImportSpecifier, ImportedNames, Condition, ConditionArm}; +use crate::ast::{ItemIdStore, AST, Expression, Statement, StatementKind, IfExpressionBody, Pattern, PatternLiteral, TypeBody, Enumerator, ForBody, InvocationArgument, FormalParam, PrefixOp, BinOp, QualifiedName, ImportSpecifier, ImportedNames, Condition, ConditionArm, ModuleSpecifier}; use super::Declaration::*; use super::Signature; use super::TypeIdentifier::*; @@ -63,6 +63,12 @@ macro_rules! import { } } +macro_rules! module { + ($module_spec:expr) => { + Statement { id: ItemIdStore::new_id(), kind: StatementKind::Module($module_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) }; @@ -796,3 +802,21 @@ fn if_expr() { } } } + +#[test] +fn modules() { + parse_test_wrap_ast! { +r#" + module ephraim { + let a = 10 + fn nah() { 33 } + } +"#, +module!( + ModuleSpecifier { name: rc!(ephraim), contents: vec![ + decl!(Binding { name: rc!(a), constant: true, type_anno: None, expr: ex!(s "10") }), + decl!(FuncDecl(Signature { name: rc!(nah), operator: false, params: vec![], type_anno: None }, vec![exst!(NatLiteral(33))])), + ] } +) + } +} diff --git a/schala-lang/language/src/reduced_ast.rs b/schala-lang/language/src/reduced_ast.rs index 5dc5551..3e0cf65 100644 --- a/schala-lang/language/src/reduced_ast.rs +++ b/schala-lang/language/src/reduced_ast.rs @@ -131,6 +131,12 @@ impl<'a> Reducer<'a> { StatementKind::Expression(expr) => Stmt::Expr(self.expression(&expr)), StatementKind::Declaration(decl) => self.declaration(&decl), StatementKind::Import(_) => Stmt::Noop, + StatementKind::Module(modspec) => { + for statement in modspec.contents.iter() { + self.statement(&statement); + } + Stmt::Noop + } } } diff --git a/schala-lang/language/src/typechecking.rs b/schala-lang/language/src/typechecking.rs index a2ccdbd..ad274f8 100644 --- a/schala-lang/language/src/typechecking.rs +++ b/schala-lang/language/src/typechecking.rs @@ -276,6 +276,7 @@ impl<'a> TypeContext<'a> { StatementKind::Expression(e) => self.expr(e), StatementKind::Declaration(decl) => self.decl(&decl), StatementKind::Import(_) => Ok(ty!(Unit)), + StatementKind::Module(_) => Ok(ty!(Unit)), } }