diff --git a/core_lang/src/hll.pest b/core_lang/src/hll.pest index 6914252f81d..92cf3c33363 100644 --- a/core_lang/src/hll.pest +++ b/core_lang/src/hll.pest @@ -12,6 +12,7 @@ while_keyword = {"while"} match_keyword = {"match"} mut_keyword = {"mut"} assign = _{"="} +docstring_open = {"///"} line_comment_open = {"//"} block_comment_open = {"/*"} block_comment_close = {"*/"} @@ -99,10 +100,10 @@ struct_expr_fields = {struct_field_name ~ ":" ~ expr ~ ("," ~ struct_field_name array_exp = {"[" ~ (expr ~ ("," ~ expr)*) ~ "]"} // declarations -declaration = {(enum_decl|var_decl|fn_decl|trait_decl|abi_decl|struct_decl|reassignment|impl_trait|impl_self)} +declaration = {docstring|enum_decl|var_decl|fn_decl|trait_decl|abi_decl|struct_decl|reassignment|impl_trait|impl_self} var_decl = {var_decl_keyword ~ mut_keyword? ~ var_name ~ type_ascription? ~ assign ~ expr ~ ";"} type_ascription = {":" ~ type_name} -fn_decl = {visibility ~ fn_signature ~ code_block} +fn_decl = {docstring? ~ visibility ~ fn_signature ~ code_block} fn_signature = {fn_decl_keyword ~ fn_decl_name ~ type_params? ~ fn_decl_params ~ (fn_returns ~ return_type)? ~ trait_bounds?} var_name = {ident} reassignment = {variable_reassignment | struct_field_reassignment} @@ -113,11 +114,11 @@ visibility = {"pub"?} struct_decl = {visibility ~ struct_keyword ~ struct_name ~ type_params? ~ trait_bounds? ~ "{" ~ struct_fields ~ "}"} struct_name = {ident} -struct_fields = {struct_field_name ~ ":" ~ type_name ~ ("," ~ struct_field_name ~ ":" ~ type_name)* ~ ","?} +struct_fields = {docstring? ~ struct_field_name ~ ":" ~ type_name ~ ("," ~ docstring? ~ struct_field_name ~ ":" ~ type_name)* ~ ","?} struct_field_name = {ident} // // enum declaration enum_decl = {enum_keyword ~ enum_name ~ type_params? ~ trait_bounds? ~ "{" ~ enum_fields ~ "}"} -enum_fields = {enum_field_name ~ ":" ~ type_name ~ ("," ~ enum_field_name ~ ":" ~ type_name)* ~ ","?} +enum_fields = {docstring? ~ enum_field_name ~ ":" ~ type_name ~ ("," ~ docstring? ~ enum_field_name ~ ":" ~ type_name)* ~ ","?} enum_name = {ident} enum_field_name = {ident} @@ -170,16 +171,17 @@ opcode = {ident} control_flow = _{while_loop|return_statement} // boilerplate -WHITESPACE = _{(" "|"\t"|"\r"|"\n")+} -COMMENT = _{block_comment|line_comment} -block_comment = @{block_comment_open ~ (!block_comment_close ~ ANY)* ~ block_comment_close} -line_comment = @{line_comment_open ~ (!("\r"|"\n") ~ ANY)*} -char = @{ +WHITESPACE = _{(" "|"\t"|"\r"|"\n")+} +COMMENT = _{block_comment|line_comment} +docstring = @{docstring_open ~ (!("\r"|"\n") ~ ANY)*} +block_comment = @{block_comment_open ~ (!block_comment_close ~ ANY)* ~ block_comment_close} +line_comment = @{line_comment_open ~ (!"/") ~ (!("\r"|"\n") ~ ANY)*} +char = @{ !("\""|"\\") ~ ANY | "\\" ~ ("\""|"\\"|"/"|"b"|"f"|"n"|"r"|"t") | "\\" ~ ("u" ~ ASCII_HEX_DIGIT{4}) } -unit = {"(" ~ ")"} -ident = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC|"_")* } -reserved_words = @{(true_keyword|false_keyword|asm_keyword|ref_keyword|deref_keyword|abi_keyword|while_keyword|struct_keyword|enum_keyword|match_keyword|use_keyword|var_decl_keyword|fn_decl_keyword|trait_decl_keyword|return_keyword|include_keyword) ~ !(ASCII_ALPHANUMERIC|"_")} +unit = {"(" ~ ")"} +ident = @{ ASCII_ALPHA ~ (ASCII_ALPHANUMERIC|"_")* } +reserved_words = @{(true_keyword|false_keyword|asm_keyword|ref_keyword|deref_keyword|abi_keyword|while_keyword|struct_keyword|enum_keyword|match_keyword|use_keyword|var_decl_keyword|fn_decl_keyword|trait_decl_keyword|return_keyword|include_keyword) ~ !(ASCII_ALPHANUMERIC|"_")} diff --git a/core_lang/src/lib.rs b/core_lang/src/lib.rs index f8e36b54825..1e85747a8f2 100644 --- a/core_lang/src/lib.rs +++ b/core_lang/src/lib.rs @@ -26,6 +26,7 @@ use semantic_analysis::{TreeType, TypedParseTree}; pub mod types; pub(crate) mod utils; pub use crate::parse_tree::{Declaration, Expression, UseStatement, WhileLoop}; +use std::collections::HashMap; pub use crate::span::Span; pub use error::{CompileError, CompileResult, CompileWarning}; @@ -118,8 +119,9 @@ pub fn parse<'sc>( ) } }; + let mut docstrings = HashMap::new(); let res = check!( - parse_root_from_pairs(parsed.next().unwrap().into_inner(), config), + parse_root_from_pairs(parsed.next().unwrap().into_inner(), config, &mut docstrings), return err(warnings, errors), warnings, errors @@ -525,6 +527,7 @@ fn perform_control_flow_analysis_on_library_exports<'sc>( fn parse_root_from_pairs<'sc>( input: impl Iterator>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, HllParseTree<'sc>> { let path = config.map(|config| config.dir_of_code.clone()); let mut warnings = Vec::new(); @@ -535,6 +538,7 @@ fn parse_root_from_pairs<'sc>( predicate_ast: None, library_exports: vec![], }; + let mut unassigned_docstring = "".to_string(); for block in input { let mut parse_tree = ParseTree::new(span::Span { span: block.as_span(), @@ -546,19 +550,37 @@ fn parse_root_from_pairs<'sc>( for pair in input { match pair.as_rule() { Rule::declaration => { - let decl = check!( - Declaration::parse_from_pair(pair.clone(), config), - continue, - warnings, - errors - ); - parse_tree.push(AstNode { - content: AstNodeContent::Declaration(decl), - span: span::Span { - span: pair.as_span(), - path: path.clone(), - }, - }); + let mut decl = pair.clone().into_inner(); + let decl_inner = decl.next().unwrap(); + match decl_inner.as_rule() { + Rule::docstring => { + let docstring = decl_inner.as_str().to_string().split_off(3); + let docstring = docstring.as_str().trim(); + unassigned_docstring.push_str("\n"); + unassigned_docstring.push_str(docstring); + } + _ => { + let decl = check!( + Declaration::parse_from_pair( + pair.clone(), + config, + unassigned_docstring.clone(), + docstrings + ), + continue, + warnings, + errors + ); + parse_tree.push(AstNode { + content: AstNodeContent::Declaration(decl), + span: span::Span { + span: pair.as_span(), + path: path.clone(), + }, + }); + unassigned_docstring = "".to_string(); + } + } } Rule::use_statement => { let stmt = check!( @@ -574,6 +596,7 @@ fn parse_root_from_pairs<'sc>( path: path.clone(), }, }); + unassigned_docstring = "".to_string(); } Rule::library_name => { let lib_pair = pair.into_inner().next().unwrap(); @@ -583,6 +606,7 @@ fn parse_root_from_pairs<'sc>( warnings, errors )); + unassigned_docstring = "".to_string(); } Rule::include_statement => { // parse the include statement into a reference to a specific file @@ -599,6 +623,7 @@ fn parse_root_from_pairs<'sc>( path: path.clone(), }, }); + unassigned_docstring = "".to_string(); } _ => unreachable!("{:?}", pair.as_str()), } diff --git a/core_lang/src/parse_tree/code_block.rs b/core_lang/src/parse_tree/code_block.rs index 39b1b77a623..6d5d36e6c32 100644 --- a/core_lang/src/parse_tree/code_block.rs +++ b/core_lang/src/parse_tree/code_block.rs @@ -21,6 +21,7 @@ impl<'sc> CodeBlock<'sc> { pub(crate) fn parse_from_pair( block: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut warnings = Vec::new(); @@ -30,80 +31,113 @@ impl<'sc> CodeBlock<'sc> { path: path.clone(), }; let block_inner = block.into_inner(); + let mut unassigned_docstring: String = "".to_string(); let mut contents = Vec::new(); for pair in block_inner { - contents.push(match pair.as_rule() { - Rule::declaration => AstNode { - content: AstNodeContent::Declaration(check!( - Declaration::parse_from_pair(pair.clone(), config), - continue, - warnings, - errors - )), - span: span::Span { - span: pair.as_span(), - path: path.clone(), - }, - }, + let content = match pair.as_rule() { + Rule::declaration => { + let mut decl = pair.clone().into_inner(); + let decl_inner = decl.next().unwrap(); + match decl_inner.as_rule() { + Rule::docstring => { + let parts = decl_inner.clone().into_inner(); + let docstring = parts.as_str().to_string().split_off(3); + let docstring = docstring.as_str().trim(); + unassigned_docstring.push_str("\n"); + unassigned_docstring.push_str(docstring); + None + } + _ => { + let decl_stmt = AstNode { + content: AstNodeContent::Declaration(check!( + Declaration::parse_from_pair( + pair.clone(), + config, + unassigned_docstring.clone(), + docstrings + ), + continue, + warnings, + errors + )), + span: span::Span { + span: pair.as_span(), + path: path.clone(), + }, + }; + unassigned_docstring = "".to_string(); + Some(decl_stmt) + } + } + } Rule::expr_statement => { let evaluated_node = check!( Expression::parse_from_pair( pair.clone().into_inner().next().unwrap().clone(), - config + config, + docstrings ), continue, warnings, errors ); - AstNode { + let expr_stmt = AstNode { content: AstNodeContent::Expression(evaluated_node), span: span::Span { span: pair.as_span(), path: path.clone(), }, - } + }; + unassigned_docstring = "".to_string(); + Some(expr_stmt) } Rule::return_statement => { let evaluated_node = check!( - ReturnStatement::parse_from_pair(pair.clone(), config), + ReturnStatement::parse_from_pair(pair.clone(), config, docstrings), continue, warnings, errors ); - AstNode { + let return_stmt = AstNode { content: AstNodeContent::ReturnStatement(evaluated_node), span: span::Span { span: pair.as_span(), path: path.clone(), }, - } + }; + unassigned_docstring = "".to_string(); + Some(return_stmt) } Rule::expr => { let res = check!( - Expression::parse_from_pair(pair.clone(), config), + Expression::parse_from_pair(pair.clone(), config, docstrings), continue, warnings, errors ); - AstNode { + let expr = AstNode { content: AstNodeContent::ImplicitReturnExpression(res.clone()), span: res.span(), - } + }; + unassigned_docstring = "".to_string(); + Some(expr) } Rule::while_loop => { let res = check!( - WhileLoop::parse_from_pair(pair.clone(), config), + WhileLoop::parse_from_pair(pair.clone(), config, docstrings), continue, warnings, errors ); - AstNode { + let while_stmt = AstNode { content: AstNodeContent::WhileLoop(res), span: span::Span { span: pair.as_span(), path: path.clone(), }, - } + }; + unassigned_docstring = "".to_string(); + Some(while_stmt) } a => { println!("In code block parsing: {:?} {:?}", a, pair.as_str()); @@ -116,7 +150,10 @@ impl<'sc> CodeBlock<'sc> { )); continue; } - }) + }; + if let Some(content) = content { + contents.push(content); + } } ok( diff --git a/core_lang/src/parse_tree/declaration/abi_declaration.rs b/core_lang/src/parse_tree/declaration/abi_declaration.rs index c8ac74bb5b4..eca24aea92c 100644 --- a/core_lang/src/parse_tree/declaration/abi_declaration.rs +++ b/core_lang/src/parse_tree/declaration/abi_declaration.rs @@ -5,6 +5,7 @@ use crate::parser::Rule; use crate::span::Span; use crate::{error::*, Ident}; use pest::iterators::Pair; +use std::collections::HashMap; /// An `abi` declaration, which declares an interface for a contract /// to implement or for a caller to use to call a contract. @@ -23,6 +24,7 @@ impl<'sc> AbiDeclaration<'sc> { pub(crate) fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let span = Span { span: pair.as_span(), @@ -50,7 +52,7 @@ impl<'sc> AbiDeclaration<'sc> { errors )), Rule::fn_decl => methods.push(check!( - FunctionDeclaration::parse_from_pair(func, config), + FunctionDeclaration::parse_from_pair(func, config, docstrings), continue, warnings, errors diff --git a/core_lang/src/parse_tree/declaration/enum_declaration.rs b/core_lang/src/parse_tree/declaration/enum_declaration.rs index db9edab0823..6c696ecd4b9 100644 --- a/core_lang/src/parse_tree/declaration/enum_declaration.rs +++ b/core_lang/src/parse_tree/declaration/enum_declaration.rs @@ -10,6 +10,7 @@ use crate::{ use crate::{parser::Rule, types::IntegerBits}; use inflector::cases::classcase::is_class_case; use pest::iterators::Pair; +use std::collections::HashMap; #[derive(Debug, Clone)] pub struct EnumDeclaration<'sc> { @@ -58,6 +59,7 @@ impl<'sc> EnumDeclaration<'sc> { pub(crate) fn parse_from_pair( decl_inner: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let whole_enum_span = Span { @@ -118,7 +120,12 @@ impl<'sc> EnumDeclaration<'sc> { ); let variants = check!( - EnumVariant::parse_from_pairs(variants, config), + EnumVariant::parse_from_pairs( + variants, + config, + name.primary_name.to_string(), + docstrings + ), Vec::new(), warnings, errors @@ -172,6 +179,8 @@ impl<'sc> EnumVariant<'sc> { pub(crate) fn parse_from_pairs( decl_inner: Option>, config: Option<&BuildConfig>, + enum_name: String, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Vec> { let mut warnings = Vec::new(); let mut errors = Vec::new(); @@ -179,38 +188,60 @@ impl<'sc> EnumVariant<'sc> { let mut tag = 0; if let Some(decl_inner) = decl_inner { let fields = decl_inner.into_inner().collect::>(); - for i in (0..fields.len()).step_by(2) { - let variant_span = Span { - span: fields[i].as_span(), - path: config.map(|c| c.path()), - }; - let name = check!( - Ident::parse_from_pair(fields[i].clone(), config), - return err(warnings, errors), - warnings, - errors - ); - assert_or_warn!( - is_class_case(name.primary_name), - warnings, - name.span.clone(), - Warning::NonClassCaseEnumVariantName { - variant_name: name.primary_name + let mut unassigned_docstring = "".to_string(); + let mut i = 0; + while i < fields.len() { + let field = &fields[i]; + match field.as_rule() { + Rule::docstring => { + let docstring = field.as_str().to_string().split_off(3); + let docstring = docstring.as_str().trim(); + unassigned_docstring.push_str("\n"); + unassigned_docstring.push_str(docstring); + i = i + 1; + } + _ => { + let variant_span = Span { + span: fields[i].as_span(), + path: config.map(|c| c.path()), + }; + let name = check!( + Ident::parse_from_pair(fields[i].clone(), config), + return err(warnings, errors), + warnings, + errors + ); + if !unassigned_docstring.is_empty() { + docstrings.insert( + format!("enum.{}.{}", enum_name, name.primary_name), + unassigned_docstring.clone(), + ); + unassigned_docstring.clear(); + } + assert_or_warn!( + is_class_case(name.primary_name), + warnings, + name.span.clone(), + Warning::NonClassCaseEnumVariantName { + variant_name: name.primary_name + } + ); + let r#type = check!( + TypeInfo::parse_from_pair_inner(fields[i + 1].clone(), config), + TypeInfo::Unit, + warnings, + errors + ); + fields_buf.push(EnumVariant { + name, + r#type, + tag, + span: variant_span, + }); + tag = tag + 1; + i = i + 2; } - ); - let r#type = check!( - TypeInfo::parse_from_pair_inner(fields[i + 1].clone(), config), - TypeInfo::Unit, - warnings, - errors - ); - fields_buf.push(EnumVariant { - name, - r#type, - tag, - span: variant_span, - }); - tag = tag + 1; + } } } ok(fields_buf, warnings, errors) diff --git a/core_lang/src/parse_tree/declaration/function_declaration.rs b/core_lang/src/parse_tree/declaration/function_declaration.rs index b9c40614992..29b70f4f163 100644 --- a/core_lang/src/parse_tree/declaration/function_declaration.rs +++ b/core_lang/src/parse_tree/declaration/function_declaration.rs @@ -6,6 +6,7 @@ use crate::types::TypeInfo; use crate::{CodeBlock, Ident, Rule}; use inflector::cases::snakecase::is_snake_case; use pest::iterators::Pair; +use std::collections::HashMap; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub(crate) enum Visibility { @@ -38,6 +39,7 @@ impl<'sc> FunctionDeclaration<'sc> { pub fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut parts = pair.clone().into_inner(); @@ -176,7 +178,7 @@ impl<'sc> FunctionDeclaration<'sc> { path: path.clone(), }; let body = check!( - CodeBlock::parse_from_pair(body, config), + CodeBlock::parse_from_pair(body, config, docstrings), crate::CodeBlock { contents: Vec::new(), whole_block_span, diff --git a/core_lang/src/parse_tree/declaration/impl_trait.rs b/core_lang/src/parse_tree/declaration/impl_trait.rs index e0e4f9241ff..99b8225fd04 100644 --- a/core_lang/src/parse_tree/declaration/impl_trait.rs +++ b/core_lang/src/parse_tree/declaration/impl_trait.rs @@ -4,6 +4,7 @@ use crate::parse_tree::CallPath; use crate::span::Span; use crate::{error::*, parser::Rule, types::TypeInfo}; use pest::iterators::Pair; +use std::collections::HashMap; #[derive(Debug, Clone)] pub struct ImplTrait<'sc> { @@ -34,6 +35,7 @@ impl<'sc> ImplTrait<'sc> { pub(crate) fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut warnings = Vec::new(); @@ -95,7 +97,7 @@ impl<'sc> ImplTrait<'sc> { for pair in iter { fn_decls_buf.push(check!( - FunctionDeclaration::parse_from_pair(pair, config), + FunctionDeclaration::parse_from_pair(pair, config, docstrings), continue, warnings, errors @@ -122,6 +124,7 @@ impl<'sc> ImplSelf<'sc> { pub(crate) fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut warnings = Vec::new(); @@ -174,7 +177,7 @@ impl<'sc> ImplSelf<'sc> { for pair in iter { fn_decls_buf.push(check!( - FunctionDeclaration::parse_from_pair(pair, config), + FunctionDeclaration::parse_from_pair(pair, config, docstrings), continue, warnings, errors diff --git a/core_lang/src/parse_tree/declaration/mod.rs b/core_lang/src/parse_tree/declaration/mod.rs index 9a443855c74..6c504e4aee4 100644 --- a/core_lang/src/parse_tree/declaration/mod.rs +++ b/core_lang/src/parse_tree/declaration/mod.rs @@ -25,6 +25,7 @@ use crate::parser::Rule; use crate::types::TypeInfo; use crate::Ident; use pest::iterators::Pair; +use std::collections::HashMap; #[derive(Debug, Clone)] pub enum Declaration<'sc> { @@ -42,18 +43,29 @@ impl<'sc> Declaration<'sc> { pub(crate) fn parse_from_pair( decl: Pair<'sc, Rule>, config: Option<&BuildConfig>, + unassigned_docstring: String, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let mut warnings = Vec::new(); let mut errors = Vec::new(); let mut pair = decl.clone().into_inner(); let decl_inner = pair.next().unwrap(); let parsed_declaration = match decl_inner.as_rule() { - Rule::fn_decl => Declaration::FunctionDeclaration(check!( - FunctionDeclaration::parse_from_pair(decl_inner, config), - return err(warnings, errors), - warnings, - errors - )), + Rule::fn_decl => { + let fn_decl = check!( + FunctionDeclaration::parse_from_pair(decl_inner, config, docstrings), + return err(warnings, errors), + warnings, + errors + ); + if !unassigned_docstring.is_empty() { + docstrings.insert( + format!("fn.{}", fn_decl.name.primary_name), + unassigned_docstring, + ); + } + Declaration::FunctionDeclaration(fn_decl) + } Rule::var_decl => { let mut var_decl_parts = decl_inner.into_inner(); let _let_keyword = var_decl_parts.next(); @@ -84,7 +96,7 @@ impl<'sc> Declaration<'sc> { None }; let body = check!( - Expression::parse_from_pair(maybe_body, config.clone()), + Expression::parse_from_pair(maybe_body, config.clone(), docstrings), return err(warnings, errors), warnings, errors @@ -102,43 +114,61 @@ impl<'sc> Declaration<'sc> { }) } Rule::trait_decl => Declaration::TraitDeclaration(check!( - TraitDeclaration::parse_from_pair(decl_inner, config), - return err(warnings, errors), - warnings, - errors - )), - Rule::struct_decl => Declaration::StructDeclaration(check!( - StructDeclaration::parse_from_pair(decl_inner, config), - return err(warnings, errors), - warnings, - errors - )), - Rule::enum_decl => Declaration::EnumDeclaration(check!( - EnumDeclaration::parse_from_pair(decl_inner, config), + TraitDeclaration::parse_from_pair(decl_inner, config, docstrings), return err(warnings, errors), warnings, errors )), + Rule::struct_decl => { + let struct_decl = check!( + StructDeclaration::parse_from_pair(decl_inner, config, docstrings), + return err(warnings, errors), + warnings, + errors + ); + if !unassigned_docstring.is_empty() { + docstrings.insert( + format!("struct.{}", struct_decl.name.primary_name), + unassigned_docstring, + ); + } + Declaration::StructDeclaration(struct_decl) + } + Rule::enum_decl => { + let enum_decl = check!( + EnumDeclaration::parse_from_pair(decl_inner, config, docstrings), + return err(warnings, errors), + warnings, + errors + ); + if !unassigned_docstring.is_empty() { + docstrings.insert( + format!("enum.{}", enum_decl.name.primary_name), + unassigned_docstring, + ); + } + Declaration::EnumDeclaration(enum_decl) + } Rule::reassignment => Declaration::Reassignment(check!( - Reassignment::parse_from_pair(decl_inner, config), + Reassignment::parse_from_pair(decl_inner, config, docstrings), return err(warnings, errors), warnings, errors )), Rule::impl_trait => Declaration::ImplTrait(check!( - ImplTrait::parse_from_pair(decl_inner, config), + ImplTrait::parse_from_pair(decl_inner, config, docstrings), return err(warnings, errors), warnings, errors )), Rule::impl_self => Declaration::ImplSelf(check!( - ImplSelf::parse_from_pair(decl_inner, config), + ImplSelf::parse_from_pair(decl_inner, config, docstrings), return err(warnings, errors), warnings, errors )), Rule::abi_decl => Declaration::AbiDeclaration(check!( - AbiDeclaration::parse_from_pair(decl_inner, config), + AbiDeclaration::parse_from_pair(decl_inner, config, docstrings), return err(warnings, errors), warnings, errors diff --git a/core_lang/src/parse_tree/declaration/reassignment.rs b/core_lang/src/parse_tree/declaration/reassignment.rs index 7da866262bb..517dd2ac8ad 100644 --- a/core_lang/src/parse_tree/declaration/reassignment.rs +++ b/core_lang/src/parse_tree/declaration/reassignment.rs @@ -5,6 +5,7 @@ use crate::parser::Rule; use crate::span::Span; use crate::Ident; use pest::iterators::Pair; +use std::collections::HashMap; #[derive(Debug, Clone)] pub struct Reassignment<'sc> { @@ -19,6 +20,7 @@ impl<'sc> Reassignment<'sc> { pub(crate) fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Reassignment<'sc>> { let path = config.map(|c| c.path()); let span = Span { @@ -33,14 +35,14 @@ impl<'sc> Reassignment<'sc> { Rule::variable_reassignment => { let mut iter = variable_or_struct_reassignment.into_inner(); let name = check!( - Expression::parse_from_pair_inner(iter.next().unwrap(), config), + Expression::parse_from_pair_inner(iter.next().unwrap(), config, docstrings), return err(warnings, errors), warnings, errors ); let body = iter.next().unwrap(); let body = check!( - Expression::parse_from_pair(body.clone(), config), + Expression::parse_from_pair(body.clone(), config, docstrings), Expression::Unit { span: Span { span: body.as_span(), @@ -70,7 +72,7 @@ impl<'sc> Reassignment<'sc> { path: path.clone(), }; let body = check!( - Expression::parse_from_pair(rhs, config), + Expression::parse_from_pair(rhs, config, docstrings), Expression::Unit { span: rhs_span }, warnings, errors diff --git a/core_lang/src/parse_tree/declaration/struct_declaration.rs b/core_lang/src/parse_tree/declaration/struct_declaration.rs index b61b89d6568..b78bbd111e5 100644 --- a/core_lang/src/parse_tree/declaration/struct_declaration.rs +++ b/core_lang/src/parse_tree/declaration/struct_declaration.rs @@ -7,6 +7,7 @@ use crate::{error::*, Ident}; use inflector::cases::classcase::is_class_case; use inflector::cases::snakecase::is_snake_case; use pest::iterators::Pair; +use std::collections::HashMap; use super::Visibility; @@ -29,6 +30,7 @@ impl<'sc> StructDeclaration<'sc> { pub(crate) fn parse_from_pair( decl: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut warnings = Vec::new(); @@ -69,17 +71,6 @@ impl<'sc> StructDeclaration<'sc> { ) .unwrap_or_else(&mut warnings, &mut errors, || Vec::new()); - let fields = if let Some(fields) = fields_pair { - check!( - StructField::parse_from_pairs(fields, config), - Vec::new(), - warnings, - errors - ) - } else { - Vec::new() - }; - let span = Span { span: name.as_span(), path: path.clone(), @@ -98,6 +89,21 @@ impl<'sc> StructDeclaration<'sc> { struct_name: name.primary_name } ); + let fields = if let Some(fields) = fields_pair { + check!( + StructField::parse_from_pairs( + fields, + config, + name.primary_name.to_string(), + docstrings + ), + Vec::new(), + warnings, + errors + ) + } else { + Vec::new() + }; ok( StructDeclaration { name, @@ -115,38 +121,62 @@ impl<'sc> StructField<'sc> { pub(crate) fn parse_from_pairs( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + struct_name: String, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Vec> { let path = config.map(|c| c.path()); let mut warnings = Vec::new(); let mut errors = Vec::new(); let fields = pair.into_inner().collect::>(); let mut fields_buf = Vec::new(); - for i in (0..fields.len()).step_by(2) { - let span = Span { - span: fields[i].as_span(), - path: path.clone(), - }; - let name = check!( - Ident::parse_from_pair(fields[i].clone(), config), - return err(warnings, errors), - warnings, - errors - ); - assert_or_warn!( - is_snake_case(name.primary_name), - warnings, - span.clone(), - Warning::NonSnakeCaseStructFieldName { - field_name: name.primary_name.clone() + let mut unassigned_docstring = "".to_string(); + let mut i = 0; + while i < fields.len() { + let field = &fields[i]; + match field.as_rule() { + Rule::docstring => { + let docstring = field.as_str().to_string().split_off(3); + let docstring = docstring.as_str().trim(); + unassigned_docstring.push_str("\n"); + unassigned_docstring.push_str(docstring); + i = i + 1; } - ); - let r#type = check!( - TypeInfo::parse_from_pair_inner(fields[i + 1].clone(), config), - TypeInfo::Unit, - warnings, - errors - ); - fields_buf.push(StructField { name, r#type, span }); + _ => { + let span = Span { + span: fields[i].as_span(), + path: path.clone(), + }; + let name = check!( + Ident::parse_from_pair(fields[i].clone(), config), + return err(warnings, errors), + warnings, + errors + ); + if !unassigned_docstring.is_empty() { + docstrings.insert( + format!("struct.{}.{}", struct_name, name.primary_name), + unassigned_docstring.clone(), + ); + unassigned_docstring.clear(); + } + assert_or_warn!( + is_snake_case(name.primary_name), + warnings, + span.clone(), + Warning::NonSnakeCaseStructFieldName { + field_name: name.primary_name.clone() + } + ); + let r#type = check!( + TypeInfo::parse_from_pair_inner(fields[i + 1].clone(), config), + TypeInfo::Unit, + warnings, + errors + ); + fields_buf.push(StructField { name, r#type, span }); + i = i + 2; + } + } } ok(fields_buf, warnings, errors) } diff --git a/core_lang/src/parse_tree/declaration/trait_declaration.rs b/core_lang/src/parse_tree/declaration/trait_declaration.rs index 585532bab94..901f6f0eac1 100644 --- a/core_lang/src/parse_tree/declaration/trait_declaration.rs +++ b/core_lang/src/parse_tree/declaration/trait_declaration.rs @@ -8,6 +8,7 @@ use crate::{error::*, Ident}; use inflector::cases::classcase::is_class_case; use inflector::cases::snakecase::is_snake_case; use pest::iterators::Pair; +use std::collections::HashMap; #[derive(Debug, Clone)] pub struct TraitDeclaration<'sc> { @@ -22,6 +23,7 @@ impl<'sc> TraitDeclaration<'sc> { pub(crate) fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let mut warnings = Vec::new(); let mut errors = Vec::new(); @@ -82,7 +84,11 @@ impl<'sc> TraitDeclaration<'sc> { } Rule::fn_decl => { methods.push(check!( - FunctionDeclaration::parse_from_pair(fn_sig_or_decl, config), + FunctionDeclaration::parse_from_pair( + fn_sig_or_decl, + config, + docstrings + ), continue, warnings, errors diff --git a/core_lang/src/parse_tree/expression/asm.rs b/core_lang/src/parse_tree/expression/asm.rs index 627d24067f1..fdb0b3b930e 100644 --- a/core_lang/src/parse_tree/expression/asm.rs +++ b/core_lang/src/parse_tree/expression/asm.rs @@ -4,6 +4,7 @@ use crate::parser::Rule; use crate::span::Span; use crate::{Ident, TypeInfo}; use pest::iterators::Pair; +use std::collections::HashMap; use super::Expression; use crate::types::IntegerBits; @@ -21,6 +22,7 @@ impl<'sc> AsmExpression<'sc> { pub(crate) fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let whole_block_span = Span { @@ -33,7 +35,7 @@ impl<'sc> AsmExpression<'sc> { let _asm_keyword = iter.next(); let asm_registers = iter.next().unwrap(); let asm_registers = check!( - AsmRegisterDeclaration::parse_from_pair(asm_registers, config), + AsmRegisterDeclaration::parse_from_pair(asm_registers, config, docstrings), return err(warnings, errors), warnings, errors @@ -196,6 +198,7 @@ impl<'sc> AsmRegisterDeclaration<'sc> { fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Vec> { let mut iter = pair.into_inner(); let mut warnings = Vec::new(); @@ -209,7 +212,7 @@ impl<'sc> AsmRegisterDeclaration<'sc> { // assigned to that register let initializer = if let Some(pair) = iter.next() { Some(check!( - Expression::parse_from_pair(pair, config), + Expression::parse_from_pair(pair, config, docstrings), return err(warnings, errors), warnings, errors diff --git a/core_lang/src/parse_tree/expression/match_branch.rs b/core_lang/src/parse_tree/expression/match_branch.rs index 9e95d3b1310..8f18ee90388 100644 --- a/core_lang/src/parse_tree/expression/match_branch.rs +++ b/core_lang/src/parse_tree/expression/match_branch.rs @@ -19,6 +19,7 @@ impl<'sc> MatchBranch<'sc> { pub fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut warnings = Vec::new(); @@ -44,7 +45,7 @@ impl<'sc> MatchBranch<'sc> { let condition = match condition.into_inner().next() { Some(e) => { let expr = check!( - Expression::parse_from_pair(e.clone(), config), + Expression::parse_from_pair(e.clone(), config, docstrings), Expression::Unit { span: span::Span { span: e.as_span(), @@ -74,7 +75,7 @@ impl<'sc> MatchBranch<'sc> { }; let result = match result.as_rule() { Rule::expr => check!( - Expression::parse_from_pair(result.clone(), config), + Expression::parse_from_pair(result.clone(), config, docstrings), Expression::Unit { span: span::Span { span: result.as_span(), @@ -91,7 +92,7 @@ impl<'sc> MatchBranch<'sc> { }; Expression::CodeBlock { contents: check!( - CodeBlock::parse_from_pair(result, config), + CodeBlock::parse_from_pair(result, config, docstrings), CodeBlock { contents: Vec::new(), whole_block_span: span.clone(), diff --git a/core_lang/src/parse_tree/expression/mod.rs b/core_lang/src/parse_tree/expression/mod.rs index 265c4f29068..8a6524b74fb 100644 --- a/core_lang/src/parse_tree/expression/mod.rs +++ b/core_lang/src/parse_tree/expression/mod.rs @@ -7,6 +7,7 @@ use crate::{CodeBlock, Ident}; use either::Either; use pest; use pest::iterators::Pair; +use std::collections::HashMap; use std::collections::VecDeque; mod asm; @@ -172,6 +173,7 @@ impl<'sc> Expression<'sc> { pub(crate) fn parse_from_pair( expr: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut warnings = Vec::new(); @@ -181,7 +183,7 @@ impl<'sc> Expression<'sc> { // first expr is always here let first_expr = expr_iter.next().unwrap(); let first_expr = check!( - Expression::parse_from_pair_inner(first_expr.clone(), config), + Expression::parse_from_pair_inner(first_expr.clone(), config, docstrings), Expression::Unit { span: Span { span: first_expr.as_span(), @@ -211,7 +213,7 @@ impl<'sc> Expression<'sc> { // an op is necessarily followed by an expression let next_expr = match expr_iter.next() { Some(o) => check!( - Expression::parse_from_pair_inner(o.clone(), config), + Expression::parse_from_pair_inner(o.clone(), config, docstrings), Expression::Unit { span: Span { span: o.as_span(), @@ -265,6 +267,7 @@ impl<'sc> Expression<'sc> { pub(crate) fn parse_from_pair_inner( expr: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut errors = Vec::new(); @@ -293,7 +296,7 @@ impl<'sc> Expression<'sc> { let mut arguments_buf = Vec::new(); for argument in arguments.into_inner() { let arg = check!( - Expression::parse_from_pair(argument.clone(), config), + Expression::parse_from_pair(argument.clone(), config, docstrings), Expression::Unit { span: Span { span: argument.as_span(), @@ -342,7 +345,7 @@ impl<'sc> Expression<'sc> { let mut contents = Vec::new(); for expr in array_exps { contents.push(check!( - Expression::parse_from_pair(expr, config), + Expression::parse_from_pair(expr, config, docstrings), Expression::Unit { span: span.clone() }, warnings, errors @@ -353,7 +356,7 @@ impl<'sc> Expression<'sc> { Rule::match_expression => { let mut expr_iter = expr.into_inner(); let primary_expression = check!( - Expression::parse_from_pair(expr_iter.next().unwrap(), config), + Expression::parse_from_pair(expr_iter.next().unwrap(), config, docstrings), Expression::Unit { span: span.clone() }, warnings, errors @@ -362,7 +365,7 @@ impl<'sc> Expression<'sc> { let mut branches = Vec::new(); for exp in expr_iter { let res = check!( - MatchBranch::parse_from_pair(exp, config), + MatchBranch::parse_from_pair(exp, config, docstrings), MatchBranch { condition: MatchCondition::CatchAll, result: Expression::Unit { span: span.clone() }, @@ -402,7 +405,7 @@ impl<'sc> Expression<'sc> { path: path.clone(), }; let value = check!( - Expression::parse_from_pair(fields[i + 1].clone(), config), + Expression::parse_from_pair(fields[i + 1].clone(), config, docstrings), Expression::Unit { span: span.clone() }, warnings, errors @@ -418,7 +421,11 @@ impl<'sc> Expression<'sc> { } Rule::parenthesized_expression => { let expr = check!( - Expression::parse_from_pair(expr.clone().into_inner().next().unwrap(), config), + Expression::parse_from_pair( + expr.clone().into_inner().next().unwrap(), + config, + docstrings + ), Expression::Unit { span: Span { span: expr.as_span(), @@ -436,7 +443,7 @@ impl<'sc> Expression<'sc> { path, }; let expr = check!( - crate::CodeBlock::parse_from_pair(expr, config), + crate::CodeBlock::parse_from_pair(expr, config, docstrings), crate::CodeBlock { contents: Vec::new(), whole_block_span, @@ -460,20 +467,20 @@ impl<'sc> Expression<'sc> { let then_pair = if_exp_pairs.next().unwrap(); let else_pair = if_exp_pairs.next(); let condition = Box::new(check!( - Expression::parse_from_pair(condition_pair, config), + Expression::parse_from_pair(condition_pair, config, docstrings), Expression::Unit { span: span.clone() }, warnings, errors )); let then = Box::new(check!( - Expression::parse_from_pair_inner(then_pair, config), + Expression::parse_from_pair_inner(then_pair, config, docstrings), Expression::Unit { span: span.clone() }, warnings, errors )); let r#else = match else_pair { Some(else_pair) => Some(Box::new(check!( - Expression::parse_from_pair_inner(else_pair, config), + Expression::parse_from_pair_inner(else_pair, config, docstrings), Expression::Unit { span: span.clone() }, warnings, errors @@ -493,7 +500,7 @@ impl<'sc> Expression<'sc> { path: path.clone(), }; let asm = check!( - AsmExpression::parse_from_pair(expr, config), + AsmExpression::parse_from_pair(expr, config, docstrings), return err(warnings, errors), warnings, errors @@ -536,7 +543,7 @@ impl<'sc> Expression<'sc> { let mut arguments_buf = VecDeque::new(); for argument in function_arguments { let arg = check!( - Expression::parse_from_pair(argument.clone(), config), + Expression::parse_from_pair(argument.clone(), config, docstrings), Expression::Unit { span: Span { span: argument.as_span(), @@ -554,7 +561,8 @@ impl<'sc> Expression<'sc> { let mut expr = check!( parse_call_item( name_parts.next().expect("guaranteed by grammar"), - config + config, + docstrings ), return err(warnings, errors), warnings, @@ -645,7 +653,11 @@ impl<'sc> Expression<'sc> { if let Some(arguments) = arguments { for argument in arguments.into_inner() { let arg = check!( - Expression::parse_from_pair(argument.clone(), config), + Expression::parse_from_pair( + argument.clone(), + config, + docstrings + ), Expression::Unit { span: Span { span: argument.as_span(), @@ -689,7 +701,7 @@ impl<'sc> Expression<'sc> { let mut buf = vec![]; for exp in inst.into_inner() { let exp = check!( - Expression::parse_from_pair(exp, config), + Expression::parse_from_pair(exp, config, docstrings), return err(warnings, errors), warnings, errors @@ -731,7 +743,11 @@ impl<'sc> Expression<'sc> { // a field let mut name_parts = name_parts.into_iter(); let mut expr = check!( - parse_call_item(name_parts.next().expect("guaranteed by grammar"), config), + parse_call_item( + name_parts.next().expect("guaranteed by grammar"), + config, + docstrings + ), return err(warnings, errors), warnings, errors @@ -771,7 +787,7 @@ impl<'sc> Expression<'sc> { ); let address = iter.next().expect("guaranteed by grammar"); let address = check!( - Expression::parse_from_pair(address, config), + Expression::parse_from_pair(address, config, docstrings), return err(warnings, errors), warnings, errors @@ -784,7 +800,7 @@ impl<'sc> Expression<'sc> { } Rule::unary_op_expr => { check!( - convert_unary_to_fn_calls(expr, config), + convert_unary_to_fn_calls(expr, config, docstrings), return err(warnings, errors), warnings, errors @@ -820,6 +836,7 @@ impl<'sc> Expression<'sc> { fn convert_unary_to_fn_calls<'sc>( item: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Expression<'sc>> { let iter = item.into_inner(); let mut unary_stack = vec![]; @@ -842,7 +859,7 @@ fn convert_unary_to_fn_calls<'sc>( )), _ => { expr = Some(check!( - Expression::parse_from_pair_inner(item, config), + Expression::parse_from_pair_inner(item, config, docstrings), return err(warnings, errors), warnings, errors @@ -869,6 +886,7 @@ fn convert_unary_to_fn_calls<'sc>( fn parse_call_item<'sc>( item: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Expression<'sc>> { let mut warnings = vec![]; let mut errors = vec![]; @@ -888,7 +906,7 @@ fn parse_call_item<'sc>( }, }, Rule::expr => check!( - Expression::parse_from_pair(item, config), + Expression::parse_from_pair(item, config, docstrings), return err(warnings, errors), warnings, errors diff --git a/core_lang/src/parse_tree/return_statement.rs b/core_lang/src/parse_tree/return_statement.rs index 18b840f8dff..455574ff539 100644 --- a/core_lang/src/parse_tree/return_statement.rs +++ b/core_lang/src/parse_tree/return_statement.rs @@ -4,6 +4,7 @@ use crate::parser::Rule; use crate::span; use crate::{CompileResult, Expression}; use pest::iterators::Pair; +use std::collections::HashMap; #[derive(Debug, Clone)] pub struct ReturnStatement<'sc> { @@ -14,6 +15,7 @@ impl<'sc> ReturnStatement<'sc> { pub(crate) fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let span = span::Span { span: pair.as_span(), @@ -30,7 +32,7 @@ impl<'sc> ReturnStatement<'sc> { }, Some(expr_pair) => { let expr = check!( - Expression::parse_from_pair(expr_pair, config), + Expression::parse_from_pair(expr_pair, config, docstrings), Expression::Unit { span }, warnings, errors diff --git a/core_lang/src/parse_tree/while_loop.rs b/core_lang/src/parse_tree/while_loop.rs index 6ae363415fc..97218615ec6 100644 --- a/core_lang/src/parse_tree/while_loop.rs +++ b/core_lang/src/parse_tree/while_loop.rs @@ -6,6 +6,7 @@ use crate::{ CodeBlock, Expression, }; use pest::iterators::Pair; +use std::collections::HashMap; #[derive(Debug, Clone)] pub struct WhileLoop<'sc> { @@ -17,6 +18,7 @@ impl<'sc> WhileLoop<'sc> { pub(crate) fn parse_from_pair( pair: Pair<'sc, Rule>, config: Option<&BuildConfig>, + docstrings: &mut HashMap, ) -> CompileResult<'sc, Self> { let path = config.map(|c| c.path()); let mut warnings = Vec::new(); @@ -31,7 +33,7 @@ impl<'sc> WhileLoop<'sc> { }; let condition = check!( - Expression::parse_from_pair(condition.clone(), config), + Expression::parse_from_pair(condition.clone(), config, docstrings), Expression::Unit { span: Span { span: condition.as_span(), @@ -43,7 +45,7 @@ impl<'sc> WhileLoop<'sc> { ); let body = check!( - CodeBlock::parse_from_pair(body, config), + CodeBlock::parse_from_pair(body, config, docstrings), CodeBlock { contents: Default::default(), whole_block_span, diff --git a/test/src/e2e_vm_tests/mod.rs b/test/src/e2e_vm_tests/mod.rs index 99fbd5fced6..c7269045914 100644 --- a/test/src/e2e_vm_tests/mod.rs +++ b/test/src/e2e_vm_tests/mod.rs @@ -46,6 +46,7 @@ pub fn run(filter_regex: Option) { ("b256_bad_jumps", ProgramState::Return(1)), ("b256_ops", ProgramState::Return(100)), ("bool_and_or", ProgramState::Return(42)), + ("doc_strings", ProgramState::Return(20)), ("neq_4_test", ProgramState::Return(0)), ("eq_4_test", ProgramState::Return(1)), ("local_impl_for_ord", ProgramState::Return(1)), // true diff --git a/test/src/e2e_vm_tests/test_programs/doc_strings/Forc.toml b/test/src/e2e_vm_tests/test_programs/doc_strings/Forc.toml new file mode 100644 index 00000000000..56a7fcf984c --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/doc_strings/Forc.toml @@ -0,0 +1,11 @@ +[project] +author = "Emily Herbert " +license = "MIT" +name = "doc_strings" +entry = "main.sw" + + +[dependencies] +std = { path = "../../../../../stdlib" } + + diff --git a/test/src/e2e_vm_tests/test_programs/doc_strings/src/main.sw b/test/src/e2e_vm_tests/test_programs/doc_strings/src/main.sw new file mode 100644 index 00000000000..41ebb02ba6f --- /dev/null +++ b/test/src/e2e_vm_tests/test_programs/doc_strings/src/main.sw @@ -0,0 +1,35 @@ +script; + +/// Enum representing either a number or a string +/// +/// # Examples +/// +/// `NumberOrString::Number(42)` +/// `NumberOrString::String("foo")` +enum NumberOrString { + /// The `Number` variant in `NumberOrString` + Number: u64, + /// The `String` variant in `NumberOrString` + String: str[4], +} + +/// Struct holding: +/// +/// 1. A `value` of type `NumberOrString` +/// 2. An `address` of type `byte` +struct Data { + /// The `value` field in `Data` + value: NumberOrString, + /// The `address` field in `Data` + address: byte, +} + +/// The main function that does all the things! +fn main() -> u64 { + let mut data = Data { + value: NumberOrString::Number(20), + address: 0b00001111, + }; + + return 20; +} \ No newline at end of file