diff --git a/sketching/may_2025/use.dm b/sketching/may_2025/use.dm new file mode 100644 index 0000000..bcaefc3 --- /dev/null +++ b/sketching/may_2025/use.dm @@ -0,0 +1,3 @@ +use std::core::println; +use std::core::*; +use std::core::{print, println}; diff --git a/src/ast/build.rs b/src/ast/build.rs index 69f1a66..5bbe695 100644 --- a/src/ast/build.rs +++ b/src/ast/build.rs @@ -15,7 +15,11 @@ fn expect_and_use( f(file_id, pair) } -pub fn build_ast(file_name: &str, file_id: usize, compilation_unit_pair: Pair) -> CompilationUnit { +pub fn build_ast( + file_name: &str, + file_id: usize, + compilation_unit_pair: Pair, +) -> CompilationUnit { build_compilation_unit(file_name, file_id, compilation_unit_pair) } @@ -293,8 +297,13 @@ fn build_references(file_id: usize, ref_list_pair: Pair) -> References { ) } -fn build_compilation_unit(file_name: &str, file_id: usize, compilation_unit_pair: Pair) -> CompilationUnit { +fn build_compilation_unit( + file_name: &str, + file_id: usize, + compilation_unit_pair: Pair, +) -> CompilationUnit { let mut namespace = None; + let mut use_statements = vec![]; let mut declarations = vec![]; for inner_pair in compilation_unit_pair.into_inner() { @@ -302,10 +311,13 @@ fn build_compilation_unit(file_name: &str, file_id: usize, compilation_unit_pair Rule::Namespace => { namespace = Some(build_namespace(file_id, inner_pair)); } + Rule::UseStatement => { + use_statements.push(build_use_statement(file_id, inner_pair)); + } Rule::ModuleLevelDeclaration => { declarations.push(build_module_level_declaration(file_id, inner_pair)); } - Rule::EOI => {} + Rule::Semicolon | Rule::EOI => {} _ => unreachable!(), } } @@ -314,6 +326,7 @@ fn build_compilation_unit(file_name: &str, file_id: usize, compilation_unit_pair file_name: file_name.to_string(), file_id, namespace, + use_statements, declarations, } } @@ -741,6 +754,53 @@ fn build_field_declaration(file_id: usize, field_pair: Pair) -> FieldDecla } } +fn build_use_statement(file_id: usize, use_statement_pair: Pair) -> UseStatement { + let mut inner = use_statement_pair.into_inner(); + let inner_length = inner.len(); + + inner.next().unwrap(); // use + + let mut identifiers = vec![]; + let mut last = None; + + for (i, inner_pair) in inner.into_iter().enumerate() { + if i != inner_length - 2 { + identifiers.push(expect_and_use( + file_id, + inner_pair, + Rule::Identifier, + build_identifier, + )) + } else { + last = Some(match inner_pair.as_rule() { + Rule::Identifier => { + UseStatementLast::Identifier(build_identifier(file_id, inner_pair)) + } + Rule::Star => UseStatementLast::Star, + Rule::UseList => UseStatementLast::Identifiers( + inner_pair + .into_inner() + .map(|identifier_pair| { + expect_and_use( + file_id, + identifier_pair, + Rule::Identifier, + build_identifier, + ) + }) + .collect(), + ), + _ => unreachable!(), + }) + } + } + + UseStatement { + identifiers, + last: last.unwrap(), + } +} + fn build_block_statement(file_id: usize, block_statement_pair: Pair) -> BlockStatement { let mut statements = vec![]; let mut expression = None; @@ -1690,7 +1750,7 @@ mod tests { } "}) } - + #[test] fn if_with_call_condition() { assert_builds(indoc! {" @@ -1701,7 +1761,7 @@ mod tests { } "}) } - + #[test] fn while_with_call_condition() { assert_builds(indoc! {" @@ -1712,7 +1772,7 @@ mod tests { } "}) } - + #[test] fn for_with_call_iterator() { assert_builds(indoc! {" @@ -1723,4 +1783,19 @@ mod tests { } "}) } + + #[test] + fn use_statement() { + assert_builds("use std::core::println;"); + } + + #[test] + fn use_star() { + assert_builds("use std::core::*;"); + } + + #[test] + fn use_list() { + assert_builds("use std::core::{print, println};"); + } } diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 42313e5..1fa3046 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -290,6 +290,7 @@ pub struct CompilationUnit { pub file_name: String, pub file_id: usize, pub namespace: Option, + pub use_statements: Vec, pub declarations: Vec, } @@ -450,6 +451,19 @@ pub struct FieldDeclaration { // Statements +#[derive(Debug)] +pub struct UseStatement { + pub identifiers: Vec, + pub last: UseStatementLast +} + +#[derive(Debug)] +pub enum UseStatementLast { + Identifier(Identifier), + Identifiers(Vec), + Star, +} + #[derive(Debug)] pub struct BlockStatement { pub statements: Vec, diff --git a/src/ast/pretty_print.rs b/src/ast/pretty_print.rs index c8caf06..e852f2a 100644 --- a/src/ast/pretty_print.rs +++ b/src/ast/pretty_print.rs @@ -282,6 +282,9 @@ impl PrettyPrint for CompilationUnit { namespace.pretty_print(writer)?; writer.decrease_indent(); } + for use_statement in &self.use_statements { + use_statement.pretty_print(writer)?; + } for declaration in &self.declarations { declaration.pretty_print(writer)?; } @@ -585,6 +588,39 @@ impl PrettyPrint for FieldDeclaration { } } +impl PrettyPrint for UseStatement { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented("UseStatement")?; + writer.increase_indent(); + for identifier in &self.identifiers { + identifier.pretty_print(writer)?; + } + self.last.pretty_print(writer)?; + writer.decrease_indent(); + Ok(()) + } +} + +impl PrettyPrint for UseStatementLast { + fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.writeln_indented("UseStatementLast")?; + writer.increase_indent(); + match self { + UseStatementLast::Identifier(i) => i.pretty_print(writer)?, + UseStatementLast::Identifiers(is) => { + for i in is { + i.pretty_print(writer)?; + } + }, + UseStatementLast::Star => { + writer.writeln_indented("Star")?; + } + } + writer.decrease_indent(); + Ok(()) + } +} + impl PrettyPrint for BlockStatement { fn pretty_print(&self, writer: &mut IndentWriter) -> std::io::Result<()> { writer.writeln_indented("BlockStatement")?; diff --git a/src/ast/unparse.rs b/src/ast/unparse.rs index a2239cb..e15bcab 100644 --- a/src/ast/unparse.rs +++ b/src/ast/unparse.rs @@ -373,7 +373,11 @@ impl Unparse for CompilationUnit { if let Some(namespace) = &self.namespace { writer.write("ns ")?; namespace.unparse(writer)?; - writer.write("\n\n")?; + writer.write(";\n\n")?; + } + for use_statement in &self.use_statements { + use_statement.unparse(writer)?; + writer.write(";\n")?; } unparse_list(writer, "\n\n", &to_unparse_vec!(self.declarations))?; Ok(()) @@ -722,6 +726,41 @@ impl Unparse for FieldDeclaration { /* Statements */ +impl Unparse for UseStatement { + fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + writer.write_indented("use ")?; + for (i, identifier) in self.identifiers.iter().enumerate() { + identifier.unparse(writer)?; + if i != self.identifiers.len() - 2 { + // 2 because of use + writer.write("::")?; + } + } + self.last.unparse(writer)?; + Ok(()) + } +} + +impl Unparse for UseStatementLast { + fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { + match self { + UseStatementLast::Star => writer.write("*"), + UseStatementLast::Identifiers(identifiers) => { + writer.write("{")?; + for (i, identifier) in identifiers.iter().enumerate() { + identifier.unparse(writer)?; + if i != identifiers.len() - 1 { + writer.write(", ")?; + } + } + writer.write("}")?; + Ok(()) + } + UseStatementLast::Identifier(i) => i.unparse(writer), + } + } +} + impl Unparse for BlockStatement { fn unparse(&self, writer: &mut IndentWriter) -> std::io::Result<()> { writer.writeln_indented("{")?; diff --git a/src/parser/deimos.pest b/src/parser/deimos.pest index 7a09a0b..3c284a8 100644 --- a/src/parser/deimos.pest +++ b/src/parser/deimos.pest @@ -30,6 +30,7 @@ Move = { "move" } Alias = { "alias" } True = { "true" } False = { "false" } +Use = { "use" } // Keywords as a rule (for preventing identifiers with keywords, etc.) Keyword = { @@ -64,6 +65,7 @@ Keyword = { | Alias | True | False + | Use } // Symbols @@ -280,7 +282,8 @@ RefList = { CompilationUnit = { SOI - ~ Namespace? + ~ ( Namespace ~ Semicolon )? + ~ ( UseStatement ~ Semicolon )* ~ ModuleLevelDeclaration* ~ EOI } @@ -479,6 +482,20 @@ Field = { // Statements +UseStatement = { + Use + ~ Identifier + ~ ( "::" ~ Identifier )* + ~ ( "::" ~ ( Star | UseList ) )? +} + +UseList = { + "{" + ~ Identifier + ~ ( "," ~ Identifier )* + ~ "}" +} + BlockStatement = { "{" ~ Statement* @@ -492,6 +509,7 @@ Statement = { | AssignmentStatement | CallStatement | ReturnStatement + | UseStatement ) ~ Semicolon | ( diff --git a/src/parser/mod.rs b/src/parser/mod.rs index be090cd..78b7239 100644 --- a/src/parser/mod.rs +++ b/src/parser/mod.rs @@ -8,7 +8,7 @@ pub struct DeimosParser; mod deimos_parser_tests { use crate::parser::{DeimosParser, Rule}; use indoc::indoc; - use pest::{parses_to, Parser}; + use pest::Parser; macro_rules! fail_rule { ($pair: expr; $rule:path) => {{ @@ -147,37 +147,58 @@ mod deimos_parser_tests { }"}, ) } - + #[test] fn while_statement() { parses_to(Rule::WhileStatement, "while (foo) { bar() }"); } - + #[test] fn for_statement() { parses_to(Rule::ForStatement, "for (foo in bar) { baz(foo); }"); } - + #[test] fn if_statement_with_call_condition() { - parses_to(Rule::IfStatement, indoc! {" + parses_to( + Rule::IfStatement, + indoc! {" if (foo()) { bar() } - "}) + "}, + ) } - + #[test] fn while_statement_with_call_condition() { parses_to(Rule::WhileStatement, "while (foo()) { bar(); }") } - + #[test] fn for_statement_with_call_iterator() { - parses_to(Rule::ForStatement, indoc! {" + parses_to( + Rule::ForStatement, + indoc! {" for (foo in bar()) { baz(foo); } - "}) + "}, + ) + } + + #[test] + fn use_statement() { + parses_to(Rule::UseStatement, "use std::core::println"); + } + + #[test] + fn use_star() { + parses_to(Rule::UseStatement, "use std::core::*") + } + + #[test] + fn use_list() { + parses_to(Rule::UseStatement, "use std::core::{print, println}"); } }