Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion include/ast.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,19 @@ namespace broma {
std::string inner; ///< The inline body of the function as a raw string.
};

/// @brief A comment (`// ...` or `/* ... */`) found inside a class body.
struct CommentField {
Comment thread
prevter marked this conversation as resolved.
std::string inner; ///< The raw comment string.
bool multiline = false; ///< Whether this was a `/* ... */` style comment, as opposed to a `//` one.
bool trailing = false; ///< Whether the comment follows other code on the same line (e.g. `int x; // note`),
/// as opposed to being on its own line.
};

/// @brief A class field.
struct Field {
size_t field_id; ///< The index of the field. This starts from 0 and counts up across all classes.
std::string parent; ///< The name of the parent class.
std::variant<InlineField, FunctionBindField, PadField, MemberField> inner;
std::variant<InlineField, FunctionBindField, PadField, MemberField, CommentField> inner;
size_t line = 0; ///< The line number where this class was defined.

/// @brief Cast the field into a variant type. This is useful to extract data from the field.
Expand Down Expand Up @@ -231,13 +239,24 @@ namespace broma {
Platform platform = Platform::All;
};

/// @brief A comment (`// ...` or `/* ... */`) found outside of any class or function.
struct Comment {
std::string inner; ///< The raw comment string.
bool multiline = false; ///< Whether this was a `/* ... */` style comment, as opposed to a `//` one.
bool trailing = false; ///< Whether the comment follows other code on the same line (e.g. `int x; // note`),
/// as opposed to being on its own line.
std::string source; ///< The source file where this comment was defined.
size_t line = 0; ///< The line number where this comment was defined.
};

/// @brief Broma's root grammar (the root AST).
///
/// See the user's guide for an example on how to traverse this AST.
struct Root {
std::vector<Class> classes;
std::vector<Function> functions;
std::vector<Header> headers;
std::vector<Comment> comments;

inline Class* operator[](std::string const& name) {
auto it = std::find_if(classes.begin(), classes.end(), [name](Class& cls) {
Expand Down
9 changes: 1 addition & 8 deletions src/basic_components.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,8 @@
using namespace tao::pegtl;

namespace broma {
/// @brief C and C++-style comments.
struct comment :
disable<sor<
seq<at<ascii::string<'/', '/'>, not_at<one<'/'>>>, until<eolf>>,
seq<ascii::string<'/', '*'>, until<seq<ascii::string<'*', '/'>>>>
>> {};

/// @brief Noisy filler grammar elements we want to ignore when parsing.
struct ignore : sor<comment, one<'\n', '\t', '\r', ' '>> {};
struct ignore : sor<one<'\n', '\t', '\r', ' '>> {};

/// @brief Separator (zero or more ignoreable elements).
struct sep : star<ignore> {};
Expand Down
21 changes: 20 additions & 1 deletion src/broma.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,27 @@
using namespace tao::pegtl;

namespace broma {
struct top_comment_expr : comment_expr_base {};

template <>
struct run_action<top_comment_expr> {
template <typename T>
static void apply(T& input, Root* root, ScratchData* scratch) {
auto parsed = extract_comment(input);

Comment c;
c.inner = std::move(parsed.text);
c.multiline = parsed.multiline;
c.trailing = parsed.trailing;
c.source = input.input().source();
c.line = input.position().line;

root->comments.push_back(std::move(c));
}
};

/// @brief Broma's top-level grammar.
struct root_grammar : until<eof, sep, must<sor<import_expr, include_expr, seq<opt<attribute>, sor<class_statement, function>>>>, sep> {};
struct root_grammar : until<eof, sep, must<sor<import_expr, include_expr, top_comment_expr, seq<opt<attribute>, sor<class_statement, function>>>>, sep> {};

geode::Result<Root, ParseError> parse_file(std::filesystem::path const& fname) {
Root root;
Expand Down
21 changes: 20 additions & 1 deletion src/class.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <tao/pegtl.hpp>
using namespace tao::pegtl;

#include "comments.hpp"
#include "basic_components.hpp"
#include "function.hpp"
#include "member.hpp"
Expand Down Expand Up @@ -31,7 +32,25 @@ namespace broma {
}
};

struct field : sor<inline_expr, pad_expr, member_expr, bind_expr> {};
struct comment_expr : comment_expr_base {};

template <>
struct run_action<comment_expr> {
template <typename T>
static void apply(T& input, Root* root, ScratchData* scratch) {
auto parsed = extract_comment(input);

CommentField cf;
cf.inner = std::move(parsed.text);
cf.multiline = parsed.multiline;
cf.trailing = parsed.trailing;

scratch->wip_field.inner = std::move(cf);
scratch->wip_field.line = input.position().line;
}
};

struct field : sor<inline_expr, pad_expr, member_expr, bind_expr, comment_expr> {};

template <>
struct run_action<field> {
Expand Down
63 changes: 63 additions & 0 deletions src/comments.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#pragma once

#include <tao/pegtl.hpp>
using namespace tao::pegtl;

namespace broma {
/// @brief C and C++-style comments.
struct comment_expr_base : sor<
seq<at<ascii::string<'/', '/'>, not_at<one<'/'>>>, until<eolf>>,
seq<ascii::string<'/', '*'>, until<ascii::string<'*', '/'>>>
> {};

/// @brief A parsed comment, with metadata about its type and position.
struct ParsedComment {
std::string text; ///< The raw comment string.
bool multiline = false; ///< Whether this was a `/* ... */` style comment, as opposed to a `//` one.
bool trailing = false; ///< Whether the comment follows other code on the same line.
};

template <typename T>
inline ParsedComment extract_comment(T& input) {
ParsedComment out;
std::string raw = input.string();

out.multiline = raw.size() >= 2 && raw[0] == '/' && raw[1] == '*';

// check if there's any non-whitespace characters before the comment on the same line
auto pos = input.begin();
auto file_begin = input.input().begin();
while (pos != file_begin) {
--pos;
if (*pos == '\n') break;
if (*pos != ' ' && *pos != '\t' && *pos != '\r') {
out.trailing = true;
break;
}
}

// strip the comment delimiters
if (out.multiline) {
raw.erase(0, 2);
if (raw.size() >= 2) {
raw.erase(raw.size() - 2);
}
} else {
raw.erase(0, 2);
}

// strip whitespace for single-line comments
if (!out.multiline) {
auto first = raw.find_first_not_of(" \t\r");
if (first == std::string::npos) {
raw.clear();
} else {
auto last = raw.find_last_not_of(" \t\r\n");
raw = raw.substr(first, last - first + 1);
}
}

out.text = std::move(raw);
return out;
}
}