Add support for doc comments

This commit is contained in:
Moritz Hölting 2024-04-03 00:45:34 +02:00
parent b9bc5438e5
commit dc288588c8
6 changed files with 123 additions and 9 deletions

View File

@ -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"

View File

@ -119,5 +119,9 @@ fn compile_statement(statement: &Statement) -> Option<Command> {
)))
}
}
Statement::DocComment(doccomment) => {
let content = doccomment.content();
Some(Command::Comment(content.to_string()))
}
}
}

View File

@ -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() {

View File

@ -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<Program> {
"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<Datapack> {
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<VFolder> {
let source_file = SourceFile::load(path)?;
@ -88,14 +136,22 @@ pub fn compile(path: &Path) -> Result<VFolder> {
"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.",
));
}

View File

@ -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())

View File

@ -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 {