From dc288588c811879bbfe7eb7f3e576e4095d58435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20H=C3=B6lting?= <87192362+moritz-hoelting@users.noreply.github.com> Date: Wed, 3 Apr 2024 00:45:34 +0200 Subject: [PATCH] Add support for doc comments --- Cargo.toml | 6 ++- src/compile/compiler.rs | 4 ++ src/lexical/token.rs | 49 ++++++++++++++++++++--- src/lib.rs | 60 ++++++++++++++++++++++++++++- src/syntax/error.rs | 1 + src/syntax/syntax_tree/statement.rs | 12 +++++- 6 files changed, 123 insertions(+), 9 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8934748..6098f6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,12 +5,16 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +default = ["shulkerbox"] +shulkerbox = ["dep:shulkerbox"] + [dependencies] colored = "2.1.0" derive_more = { version = "0.99.17", default-features = false, features = ["deref", "from", "deref_mut"] } enum-as-inner = "0.6.0" getset = "0.1.2" -shulkerbox = { path = "../shulkerbox" } +shulkerbox = { path = "../shulkerbox", optional = true} strum = { version = "0.26.2", features = ["derive"] } strum_macros = "0.26.2" thiserror = "1.0.58" diff --git a/src/compile/compiler.rs b/src/compile/compiler.rs index 384e805..cb2d2c2 100644 --- a/src/compile/compiler.rs +++ b/src/compile/compiler.rs @@ -119,5 +119,9 @@ fn compile_statement(statement: &Statement) -> Option { ))) } } + Statement::DocComment(doccomment) => { + let content = doccomment.content(); + Some(Command::Comment(content.to_string())) + } } } diff --git a/src/lexical/token.rs b/src/lexical/token.rs index bdf24d9..4dcf95d 100644 --- a/src/lexical/token.rs +++ b/src/lexical/token.rs @@ -75,6 +75,7 @@ pub enum Token { Punctuation(Punctuation), Numeric(Numeric), Comment(Comment), + DocComment(DocComment), CommandLiteral(CommandLiteral), StringLiteral(StringLiteral), } @@ -90,6 +91,7 @@ impl Token { Self::Punctuation(token) => &token.span, Self::Numeric(token) => &token.span, Self::Comment(token) => &token.span, + Self::DocComment(token) => &token.span, Self::CommandLiteral(token) => &token.span, Self::StringLiteral(token) => &token.span, } @@ -105,6 +107,7 @@ impl SourceElement for Token { Self::Punctuation(token) => token.span(), Self::Numeric(token) => token.span(), Self::Comment(token) => token.span(), + Self::DocComment(token) => token.span(), Self::CommandLiteral(token) => token.span(), Self::StringLiteral(token) => token.span(), } @@ -229,6 +232,27 @@ impl SourceElement for Comment { } } +/// Represents a documentation comment in the source code. +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DocComment { + /// Is the span that makes up the token. + pub span: Span, +} + +impl SourceElement for DocComment { + fn span(&self) -> Span { + self.span.clone() + } +} + +impl DocComment { + /// Returns the content of the doc comment without the leading `///`. + #[must_use] + pub fn content(&self) -> &str { + &self.span.str().trim()[3..] + } +} + /// Represents a hardcoded literal command in the source code. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct CommandLiteral { @@ -339,22 +363,37 @@ impl Token { if let Some((_, '/')) = iter.peek() { iter.next(); + let is_doccomment = if let Some((_, '/')) = iter.peek() { + iter.next(); + true + } else { + false + }; + Self::walk_iter(iter, |character| !(character == '\n' || character == '\r')); let is_cr = iter .peek() .map_or(false, |(_, character)| character == '\r'); + let span = Self::create_span(start, iter); + if let (true, Some((_, '\n'))) = (is_cr, iter.next()) { // skips the crlf iter.next(); } - Ok(Comment { - span: Self::create_span(start, iter), - kind: CommentKind::Line, - } - .into()) + let comment = if is_doccomment { + DocComment { span }.into() + } else { + Comment { + span, + kind: CommentKind::Line, + } + .into() + }; + + Ok(comment) } // Delimited comment else if let Some((_, '*')) = iter.peek() { diff --git a/src/lib.rs b/src/lib.rs index 1344b13..1b9cd6d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -21,9 +21,11 @@ use std::{cell::Cell, fmt::Display, path::Path}; use base::{source_file::SourceFile, Handler, Result}; use compile::compiler::Compiler; -use shulkerbox::{util::compile::CompileOptions, virtual_fs::VFolder}; use syntax::syntax_tree::program::Program; +#[cfg(feature = "shulkerbox")] +use shulkerbox::{datapack::Datapack, util::compile::CompileOptions, virtual_fs::VFolder}; + use crate::{base::Error, lexical::token_stream::TokenStream, syntax::parser::Parser}; /// Converts the given source code to tokens. @@ -61,13 +63,59 @@ pub fn parse(path: &Path) -> Result { "An error occured while parsing the source code.", ))?; + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while parsing the source code.", + )); + } + Ok(program) } +/// Transpiles the given source code into a shulkerbox [`Datapack`]. +/// +/// # Errors +/// - If an error occurs while reading the file. +/// - If an error occurs while parsing the source code. +/// - If an error occurs while transpiling the source code. +#[cfg(feature = "shulkerbox")] +pub fn transpile(path: &Path) -> Result { + let source_file = SourceFile::load(path)?; + + let printer = Printer::new(); + + let tokens = TokenStream::tokenize(&source_file, &printer); + + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while tokenizing the source code.", + )); + } + + let mut parser = Parser::new(&tokens); + let program = parser.parse_program(&printer).ok_or(Error::Other( + "An error occured while parsing the source code.", + ))?; + + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while parsing the source code.", + )); + } + + let mut compiler = Compiler::new(); + let datapack = compiler.compile(&program, &printer)?; + + Ok(datapack) +} + /// Compiles the given source code. /// /// # Errors /// - If an error occurs while reading the file. +/// - If an error occurs while parsing the source code. +/// - If an error occurs while transpiling the source code. +#[cfg(feature = "shulkerbox")] pub fn compile(path: &Path) -> Result { let source_file = SourceFile::load(path)?; @@ -88,14 +136,22 @@ pub fn compile(path: &Path) -> Result { "An error occured while parsing the source code.", ))?; + if printer.has_printed() { + return Err(Error::Other( + "An error occurred while parsing the source code.", + )); + } + // println!("program: {program:#?}"); let mut compiler = Compiler::new(); let datapack = compiler.compile(&program, &printer)?; + // println!("datapack: {datapack:#?}"); + if printer.has_printed() { return Err(Error::Other( - "An error occurred while parsing the source code.", + "An error occurred while transpiling the source code.", )); } diff --git a/src/syntax/error.rs b/src/syntax/error.rs index 489821c..c5fd3e3 100644 --- a/src/syntax/error.rs +++ b/src/syntax/error.rs @@ -47,6 +47,7 @@ impl Display for UnexpectedSyntax { }; let found_binding = match self.found.clone() { Some(Token::Comment(..)) => "a comment token".to_string(), + Some(Token::DocComment(..)) => "a doc comment token".to_string(), Some(Token::Identifier(..)) => "an identifier token".to_string(), Some(Token::Keyword(keyword)) => { format!("a keyword token `{}`", keyword.keyword.as_str()) diff --git a/src/syntax/syntax_tree/statement.rs b/src/syntax/syntax_tree/statement.rs index 08d987d..f6a54d9 100644 --- a/src/syntax/syntax_tree/statement.rs +++ b/src/syntax/syntax_tree/statement.rs @@ -8,7 +8,7 @@ use crate::{ Handler, }, lexical::{ - token::{CommandLiteral, Keyword, KeywordKind, Punctuation, Token}, + token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token}, token_stream::Delimiter, }, syntax::{ @@ -25,6 +25,8 @@ use super::expression::ParenthesizedCondition; /// Statement: /// Block /// | LiteralCommand +/// | Conditional +/// | DocComment /// ; /// ``` #[allow(missing_docs)] @@ -33,6 +35,7 @@ pub enum Statement { Block(Block), LiteralCommand(CommandLiteral), Conditional(Conditional), + DocComment(DocComment), } impl SourceElement for Statement { @@ -41,6 +44,7 @@ impl SourceElement for Statement { Self::Block(block) => block.span(), Self::LiteralCommand(literal_command) => literal_command.span(), Self::Conditional(conditional) => conditional.span(), + Self::DocComment(doc_comment) => doc_comment.span(), } } } @@ -257,6 +261,12 @@ impl<'a> Parser<'a> { })), } } + + Reading::Atomic(Token::DocComment(doc_comment)) => { + self.forward(); + Some(Statement::DocComment(doc_comment)) + } + // other unexpected => { handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {