Skip to content

Commit

Permalink
Add support for docstrings.
Browse files Browse the repository at this point in the history
  • Loading branch information
azteca1998 committed Jan 14, 2025
1 parent 6fe75de commit abe73e5
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 8 deletions.
3 changes: 2 additions & 1 deletion src/ast/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ impl From<Span> for Range<usize> {

#[derive(Clone, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub struct DocString {
contents: String,
pub contents: Vec<String>,
pub span: Span,
}

/// Identifiers, without a path or generics.
Expand Down
20 changes: 14 additions & 6 deletions src/grammar.lalrpop
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ extern {
"boolean" => Token::Boolean(<bool>),

// Other
"docstring" => Token::DocString(<String>),

"(" => Token::LeftParen,
")" => Token::RightParen,
Expand Down Expand Up @@ -142,6 +143,13 @@ SemiColonSeparated<T>: Vec<T> = {

// -- Common

DocString: ast::common::DocString = {
<lo:@L> <contents:"docstring"+> <hi:@R> => ast::common::DocString {
contents,
span: ast::common::Span::new(lo, hi),
},
};

Ident: ast::common::Ident = {
<lo:@L> <name:"identifier"> <hi:@R> => ast::common::Ident {
name,
Expand Down Expand Up @@ -223,9 +231,9 @@ ExternalModule: ast::common::Ident = {
}

Module: ast::modules::Module = {
<lo:@L> "mod" <name:Ident> "{" <external_modules:List<ExternalModule>?> <imports:ImportList?> <contents:ModuleItems?> "}" <hi:@R> => {
<lo:@L> <doc_string:DocString?> "mod" <name:Ident> "{" <external_modules:List<ExternalModule>?> <imports:ImportList?> <contents:ModuleItems?> "}" <hi:@R> => {
ast::modules::Module {
doc_string: None,
doc_string,
imports: imports.unwrap_or_else(Vec::new),
external_modules: external_modules.unwrap_or(vec![]),
name,
Expand Down Expand Up @@ -293,10 +301,10 @@ ModuleDefItem: ast::modules::ModuleDefItem = {
// Constants

ConstantDef: ast::constants::ConstantDef = {
<is_pub:"pub"?> "const" <name:Ident> ":" <type_spec:TypeDescriptor> "=" <exp:Expression> => {
<doc_string:DocString?> <is_pub:"pub"?> "const" <name:Ident> ":" <type_spec:TypeDescriptor> "=" <exp:Expression> => {
ast::constants::ConstantDef {
decl: ast::constants::ConstantDecl {
doc_string: None,
doc_string,
name,
is_pub: is_pub.is_some(),
r#type: type_spec
Expand Down Expand Up @@ -338,11 +346,11 @@ Attribute: ast::common::Attribute = {
}

FunctionDecl: ast::functions::FunctionDecl = {
<lo:@L> <attributes:List<Attribute>?> <is_pub:"pub"?> <is_extern:"extern"?>
<lo:@L> <doc_string:DocString?> <attributes:List<Attribute>?> <is_pub:"pub"?> <is_extern:"extern"?>
"fn" <name:Ident> <generic_params:GenericParams?> "(" <params:Comma<Param>> ")"
<ret_type:FunctionRetType?> <hi:@R> =>
ast::functions::FunctionDecl {
doc_string: None,
doc_string,
generic_params: generic_params.unwrap_or(vec![]),
attributes: attributes.unwrap_or(vec![]),
name,
Expand Down
57 changes: 57 additions & 0 deletions src/parser/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ pub fn parse_ast<'db>(db: &'db dyn salsa::Database, source: ProgramSource<'db>)
#[cfg(test)]
mod tests {
use super::{grammar, lexer::Lexer};
use crate::ast;

#[test]
fn parse_simple_program() {
Expand Down Expand Up @@ -358,4 +359,60 @@ mod ModuleName {
let parser = grammar::ProgramParser::new();
parser.parse(lexer).unwrap();
}

#[test]
fn parse_doc_strings() {
let source = r##"
/// Documentation for `MyMod`.
mod MyMod {
/// The PI number.
///
/// Note: Rounded to an integer.
const ROUNDED_PI: u8 = 3;
/// Increment a number.
pub fn my_func(x: u64) -> u64 {
return x + 1;
}
}
"##;
let lexer = Lexer::new(source);
let parser = grammar::ProgramParser::new();
let module = parser.parse(lexer).unwrap();

let const_item = match &module.modules[0].contents[0] {
ast::modules::ModuleDefItem::Constant(x) => x,
_ => unreachable!(),
};
let fn_item = match &module.modules[0].contents[1] {
ast::modules::ModuleDefItem::Function(x) => x,
_ => unreachable!(),
};

assert_eq!(
module.modules[0].doc_string,
Some(ast::common::DocString {
contents: vec![" Documentation for `MyMod`.".to_string()],
span: ast::common::Span::new(1, 31),
}),
);
assert_eq!(
const_item.decl.doc_string,
Some(ast::common::DocString {
contents: vec![
" The PI number.".to_string(),
"".to_string(),
" Note: Rounded to an integer.".to_string(),
],
span: ast::common::Span::new(48, 111),
}),
);
assert_eq!(
fn_item.decl.doc_string,
Some(ast::common::DocString {
contents: vec![" Increment a number.".to_string()],
span: ast::common::Span::new(147, 170),
}),
);
}
}
5 changes: 4 additions & 1 deletion src/parser/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ impl From<Infallible> for LexingError {
}

#[derive(Logos, logos_display::Debug, logos_display::Display, PartialEq, Clone)]
#[logos(error = LexingError, skip r"[ \t\n\f]+", skip r"//[^\n]*", skip r"/\*(?:[^*]|\*[^/])*\*/")]
#[logos(error = LexingError, skip r"[ \t\n\f]+", skip r"//[^/][^\n]*", skip r"/\*(?:[^*]|\*[^/])*\*/")]
pub enum Token {
#[token("let")]
KeywordLet,
Expand Down Expand Up @@ -154,4 +154,7 @@ pub enum Token {
Ampersand,
#[token("|")]
OperatorBitwiseOr,

#[regex("///[^\n]*", |lex| lex.slice().strip_prefix("///").unwrap().to_string())]
DocString(String),
}

0 comments on commit abe73e5

Please sign in to comment.