Add support for doc comments
This commit is contained in:
parent
b9bc5438e5
commit
dc288588c8
|
@ -5,12 +5,16 @@ edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["shulkerbox"]
|
||||||
|
shulkerbox = ["dep:shulkerbox"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
colored = "2.1.0"
|
colored = "2.1.0"
|
||||||
derive_more = { version = "0.99.17", default-features = false, features = ["deref", "from", "deref_mut"] }
|
derive_more = { version = "0.99.17", default-features = false, features = ["deref", "from", "deref_mut"] }
|
||||||
enum-as-inner = "0.6.0"
|
enum-as-inner = "0.6.0"
|
||||||
getset = "0.1.2"
|
getset = "0.1.2"
|
||||||
shulkerbox = { path = "../shulkerbox" }
|
shulkerbox = { path = "../shulkerbox", optional = true}
|
||||||
strum = { version = "0.26.2", features = ["derive"] }
|
strum = { version = "0.26.2", features = ["derive"] }
|
||||||
strum_macros = "0.26.2"
|
strum_macros = "0.26.2"
|
||||||
thiserror = "1.0.58"
|
thiserror = "1.0.58"
|
||||||
|
|
|
@ -119,5 +119,9 @@ fn compile_statement(statement: &Statement) -> Option<Command> {
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Statement::DocComment(doccomment) => {
|
||||||
|
let content = doccomment.content();
|
||||||
|
Some(Command::Comment(content.to_string()))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,6 +75,7 @@ pub enum Token {
|
||||||
Punctuation(Punctuation),
|
Punctuation(Punctuation),
|
||||||
Numeric(Numeric),
|
Numeric(Numeric),
|
||||||
Comment(Comment),
|
Comment(Comment),
|
||||||
|
DocComment(DocComment),
|
||||||
CommandLiteral(CommandLiteral),
|
CommandLiteral(CommandLiteral),
|
||||||
StringLiteral(StringLiteral),
|
StringLiteral(StringLiteral),
|
||||||
}
|
}
|
||||||
|
@ -90,6 +91,7 @@ impl Token {
|
||||||
Self::Punctuation(token) => &token.span,
|
Self::Punctuation(token) => &token.span,
|
||||||
Self::Numeric(token) => &token.span,
|
Self::Numeric(token) => &token.span,
|
||||||
Self::Comment(token) => &token.span,
|
Self::Comment(token) => &token.span,
|
||||||
|
Self::DocComment(token) => &token.span,
|
||||||
Self::CommandLiteral(token) => &token.span,
|
Self::CommandLiteral(token) => &token.span,
|
||||||
Self::StringLiteral(token) => &token.span,
|
Self::StringLiteral(token) => &token.span,
|
||||||
}
|
}
|
||||||
|
@ -105,6 +107,7 @@ impl SourceElement for Token {
|
||||||
Self::Punctuation(token) => token.span(),
|
Self::Punctuation(token) => token.span(),
|
||||||
Self::Numeric(token) => token.span(),
|
Self::Numeric(token) => token.span(),
|
||||||
Self::Comment(token) => token.span(),
|
Self::Comment(token) => token.span(),
|
||||||
|
Self::DocComment(token) => token.span(),
|
||||||
Self::CommandLiteral(token) => token.span(),
|
Self::CommandLiteral(token) => token.span(),
|
||||||
Self::StringLiteral(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.
|
/// Represents a hardcoded literal command in the source code.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct CommandLiteral {
|
pub struct CommandLiteral {
|
||||||
|
@ -339,22 +363,37 @@ impl Token {
|
||||||
if let Some((_, '/')) = iter.peek() {
|
if let Some((_, '/')) = iter.peek() {
|
||||||
iter.next();
|
iter.next();
|
||||||
|
|
||||||
|
let is_doccomment = if let Some((_, '/')) = iter.peek() {
|
||||||
|
iter.next();
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
};
|
||||||
|
|
||||||
Self::walk_iter(iter, |character| !(character == '\n' || character == '\r'));
|
Self::walk_iter(iter, |character| !(character == '\n' || character == '\r'));
|
||||||
|
|
||||||
let is_cr = iter
|
let is_cr = iter
|
||||||
.peek()
|
.peek()
|
||||||
.map_or(false, |(_, character)| character == '\r');
|
.map_or(false, |(_, character)| character == '\r');
|
||||||
|
|
||||||
|
let span = Self::create_span(start, iter);
|
||||||
|
|
||||||
if let (true, Some((_, '\n'))) = (is_cr, iter.next()) {
|
if let (true, Some((_, '\n'))) = (is_cr, iter.next()) {
|
||||||
// skips the crlf
|
// skips the crlf
|
||||||
iter.next();
|
iter.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Comment {
|
let comment = if is_doccomment {
|
||||||
span: Self::create_span(start, iter),
|
DocComment { span }.into()
|
||||||
kind: CommentKind::Line,
|
} else {
|
||||||
}
|
Comment {
|
||||||
.into())
|
span,
|
||||||
|
kind: CommentKind::Line,
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(comment)
|
||||||
}
|
}
|
||||||
// Delimited comment
|
// Delimited comment
|
||||||
else if let Some((_, '*')) = iter.peek() {
|
else if let Some((_, '*')) = iter.peek() {
|
||||||
|
|
60
src/lib.rs
60
src/lib.rs
|
@ -21,9 +21,11 @@ use std::{cell::Cell, fmt::Display, path::Path};
|
||||||
|
|
||||||
use base::{source_file::SourceFile, Handler, Result};
|
use base::{source_file::SourceFile, Handler, Result};
|
||||||
use compile::compiler::Compiler;
|
use compile::compiler::Compiler;
|
||||||
use shulkerbox::{util::compile::CompileOptions, virtual_fs::VFolder};
|
|
||||||
use syntax::syntax_tree::program::Program;
|
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};
|
use crate::{base::Error, lexical::token_stream::TokenStream, syntax::parser::Parser};
|
||||||
|
|
||||||
/// Converts the given source code to tokens.
|
/// 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.",
|
"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)
|
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.
|
/// Compiles the given source code.
|
||||||
///
|
///
|
||||||
/// # Errors
|
/// # Errors
|
||||||
/// - If an error occurs while reading the file.
|
/// - 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> {
|
pub fn compile(path: &Path) -> Result<VFolder> {
|
||||||
let source_file = SourceFile::load(path)?;
|
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.",
|
"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:#?}");
|
// println!("program: {program:#?}");
|
||||||
|
|
||||||
let mut compiler = Compiler::new();
|
let mut compiler = Compiler::new();
|
||||||
let datapack = compiler.compile(&program, &printer)?;
|
let datapack = compiler.compile(&program, &printer)?;
|
||||||
|
|
||||||
|
// println!("datapack: {datapack:#?}");
|
||||||
|
|
||||||
if printer.has_printed() {
|
if printer.has_printed() {
|
||||||
return Err(Error::Other(
|
return Err(Error::Other(
|
||||||
"An error occurred while parsing the source code.",
|
"An error occurred while transpiling the source code.",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,6 +47,7 @@ impl Display for UnexpectedSyntax {
|
||||||
};
|
};
|
||||||
let found_binding = match self.found.clone() {
|
let found_binding = match self.found.clone() {
|
||||||
Some(Token::Comment(..)) => "a comment token".to_string(),
|
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::Identifier(..)) => "an identifier token".to_string(),
|
||||||
Some(Token::Keyword(keyword)) => {
|
Some(Token::Keyword(keyword)) => {
|
||||||
format!("a keyword token `{}`", keyword.keyword.as_str())
|
format!("a keyword token `{}`", keyword.keyword.as_str())
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
Handler,
|
Handler,
|
||||||
},
|
},
|
||||||
lexical::{
|
lexical::{
|
||||||
token::{CommandLiteral, Keyword, KeywordKind, Punctuation, Token},
|
token::{CommandLiteral, DocComment, Keyword, KeywordKind, Punctuation, Token},
|
||||||
token_stream::Delimiter,
|
token_stream::Delimiter,
|
||||||
},
|
},
|
||||||
syntax::{
|
syntax::{
|
||||||
|
@ -25,6 +25,8 @@ use super::expression::ParenthesizedCondition;
|
||||||
/// Statement:
|
/// Statement:
|
||||||
/// Block
|
/// Block
|
||||||
/// | LiteralCommand
|
/// | LiteralCommand
|
||||||
|
/// | Conditional
|
||||||
|
/// | DocComment
|
||||||
/// ;
|
/// ;
|
||||||
/// ```
|
/// ```
|
||||||
#[allow(missing_docs)]
|
#[allow(missing_docs)]
|
||||||
|
@ -33,6 +35,7 @@ pub enum Statement {
|
||||||
Block(Block),
|
Block(Block),
|
||||||
LiteralCommand(CommandLiteral),
|
LiteralCommand(CommandLiteral),
|
||||||
Conditional(Conditional),
|
Conditional(Conditional),
|
||||||
|
DocComment(DocComment),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SourceElement for Statement {
|
impl SourceElement for Statement {
|
||||||
|
@ -41,6 +44,7 @@ impl SourceElement for Statement {
|
||||||
Self::Block(block) => block.span(),
|
Self::Block(block) => block.span(),
|
||||||
Self::LiteralCommand(literal_command) => literal_command.span(),
|
Self::LiteralCommand(literal_command) => literal_command.span(),
|
||||||
Self::Conditional(conditional) => conditional.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
|
// other
|
||||||
unexpected => {
|
unexpected => {
|
||||||
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
handler.receive(Error::UnexpectedSyntax(UnexpectedSyntax {
|
||||||
|
|
Loading…
Reference in New Issue