ArkScript
A small, lisp-inspired, functional scripting language
Formatter.hpp
Go to the documentation of this file.
1#ifndef ARK_FORMATTER_HPP
2#define ARK_FORMATTER_HPP
3
4#include <string>
5#include <ranges>
6
8
9constexpr struct FormatterConfig
10{
11 static constexpr std::size_t SpacePerIndent = 2; ///< Indentation level of each node
12 static constexpr std::size_t LongLineLength = 120; ///< Max number of characters per line segment to consider splitting
14
15enum class CallKind
16{
17 List,
18 Dict,
19 Switch,
21};
22
23class Formatter final
24{
25public:
26 explicit Formatter(bool dry_run);
27 Formatter(std::string filename, bool dry_run);
28
29 /**
30 * @brief Read the file and process it. The file isn't modified
31 */
32 void run();
33
34 /**
35 *
36 * @param code code to process (bypass reading the file if initialized with a filename)
37 */
38 void runWithString(const std::string& code);
39
40 [[nodiscard]] const std::string& output() const;
41
42 /**
43 *
44 * @return true if code has been modified by the formatter
45 */
46 [[nodiscard]] bool codeModified() const;
47
48private:
49 const std::string m_filename;
50 bool m_dry_run; ///< If true, only prints the formatted file instead of saving it to disk
52 std::string m_output;
53 bool m_updated; ///< True if the original code now difer from the formatted one
55
56 void processAst(const Ark::internal::Node& ast);
57
58 /**
59 * @brief Given the original code, produce a warning if comments from it were removed during formatting
60 * @param original_code
61 * @param filename
62 */
63 void warnIfCommentsWereRemoved(const std::string& original_code, const std::string& filename);
64
65 /**
66 * @brief Check if a given node starts with a given keyword
67 * @param node
68 * @param keyword
69 * @return bool
70 */
71 [[nodiscard]] static bool isListStartingWithKeyword(const Ark::internal::Node& node, Ark::internal::Keyword keyword);
72
73 /**
74 * @brief Check if a node is a begin block
75 * @param node
76 * @return bool
77 */
78 [[nodiscard]] static bool isBeginBlock(const Ark::internal::Node& node);
79
80 /**
81 * @brief Check if a node is a function definition (fun (args) body)
82 * @param node
83 * @return bool
84 */
85 [[nodiscard]] static bool isFuncDef(const Ark::internal::Node& node);
86
87 /**
88 * @brief Check if a node is a function call (foo bar egg)
89 * @param node
90 * @return bool
91 */
92 [[nodiscard]] static bool isFuncCall(const Ark::internal::Node& node);
93
94 /**
95 * @brief Compute the line on which the deepest right most node of node is at
96 * @param node
97 * @return
98 */
99 static std::size_t lineOfLastNodeIn(const Ark::internal::Node& node);
100
101 [[nodiscard]] bool isLongLine(const Ark::internal::Node& node);
102
103 /**
104 * @brief Decide if a node should be split on a newline or not
105 * @param node
106 * @param on_multiple_lines
107 * @return bool
108 */
109 [[nodiscard]] bool shouldSplitOnNewline(const Ark::internal::Node& node, bool on_multiple_lines);
110
111 /**
112 * @brief Decide if we should add a newline after a node in a block
113 * @param node a List node
114 * @param at the node we are currently formatting
115 * @return
116 */
117 [[nodiscard]] bool shouldAddNewLineBetweenNodes(const Ark::internal::Node& node, std::size_t at);
118
119 /**
120 * @brief Compute indentation level
121 * @param indent indentation level
122 * @return std::string
123 */
124 [[nodiscard]] static std::string prefix(const std::size_t indent)
125 {
126 return std::string(indent * FormatterConfig::SpacePerIndent, ' ');
127 }
128
129 [[nodiscard]] static bool isOnMultipleLines(const std::string& formatted)
130 {
131 return std::ranges::count(formatted, '\n') >= 1;
132 }
133
134 static CallKind callKind(const Ark::internal::Node& node);
135
136 /**
137 * @brief Handles all node formatting
138 * @param node
139 * @param indent indentation level, starting at 0, increment by 1
140 * @param after_newline when false, do not add prefix
141 * @return
142 */
143 [[nodiscard]] std::string format(const Ark::internal::Node& node, std::size_t indent, bool after_newline);
144
145 [[nodiscard]] std::string formatComment(const std::string& comment, std::size_t indent) const;
146
147 [[nodiscard]] std::string formatBlock(const Ark::internal::Node& node, std::size_t indent, bool after_newline);
148
149 [[nodiscard]] std::string formatFunction(const Ark::internal::Node& node, std::size_t indent);
150 [[nodiscard]] std::string formatVariable(const Ark::internal::Node& node, std::size_t indent);
151 [[nodiscard]] std::string formatCondition(const Ark::internal::Node& node, std::size_t indent, bool is_macro = false);
152 [[nodiscard]] std::string formatLoop(const Ark::internal::Node& node, std::size_t indent);
153 [[nodiscard]] std::string formatBegin(const Ark::internal::Node& node, std::size_t indent, bool after_newline);
154 [[nodiscard]] std::string formatImport(const Ark::internal::Node& node, std::size_t indent);
155 [[nodiscard]] std::string formatDel(const Ark::internal::Node& node, std::size_t indent);
156 [[nodiscard]] std::string formatCall(const Ark::internal::Node& node, std::size_t indent);
157 [[nodiscard]] std::string formatMacro(const Ark::internal::Node& node, std::size_t indent);
158};
159
160#endif // ARK_FORMATTER_HPP
constexpr struct FormatterConfig FormatterConfig
CallKind
Definition Formatter.hpp:16
Parse ArkScript code, but do not handle any import declarations.
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:32
std::string formatMacro(const Ark::internal::Node &node, std::size_t indent)
Ark::internal::Logger m_logger
Definition Formatter.hpp:54
void run()
Read the file and process it. The file isn't modified.
Definition Formatter.cpp:24
std::string formatVariable(const Ark::internal::Node &node, std::size_t indent)
bool codeModified() const
Definition Formatter.cpp:62
std::string formatBlock(const Ark::internal::Node &node, std::size_t indent, bool after_newline)
std::string formatDel(const Ark::internal::Node &node, std::size_t indent)
void processAst(const Ark::internal::Node &ast)
Definition Formatter.cpp:67
static std::string prefix(const std::size_t indent)
Compute indentation level.
std::string formatCall(const Ark::internal::Node &node, std::size_t indent)
static bool isBeginBlock(const Ark::internal::Node &node)
Check if a node is a begin block.
bool m_dry_run
If true, only prints the formatted file instead of saving it to disk.
Definition Formatter.hpp:50
static bool isFuncCall(const Ark::internal::Node &node)
Check if a node is a function call (foo bar egg)
static bool isFuncDef(const Ark::internal::Node &node)
Check if a node is a function definition (fun (args) body)
Ark::internal::Parser m_parser
Definition Formatter.hpp:51
bool shouldAddNewLineBetweenNodes(const Ark::internal::Node &node, std::size_t at)
Decide if we should add a newline after a node in a block.
std::string formatBegin(const Ark::internal::Node &node, std::size_t indent, bool after_newline)
bool m_updated
True if the original code now difer from the formatted one.
Definition Formatter.hpp:53
static CallKind callKind(const Ark::internal::Node &node)
static bool isOnMultipleLines(const std::string &formatted)
Formatter(bool dry_run)
Definition Formatter.cpp:16
std::string formatFunction(const Ark::internal::Node &node, std::size_t indent)
std::string formatLoop(const Ark::internal::Node &node, std::size_t indent)
bool shouldSplitOnNewline(const Ark::internal::Node &node, bool on_multiple_lines)
Decide if a node should be split on a newline or not.
std::string m_output
Definition Formatter.hpp:52
const std::string & output() const
Definition Formatter.cpp:57
std::string formatImport(const Ark::internal::Node &node, std::size_t indent)
std::string formatComment(const std::string &comment, std::size_t indent) const
const std::string m_filename
Definition Formatter.hpp:49
void runWithString(const std::string &code)
Definition Formatter.cpp:41
void warnIfCommentsWereRemoved(const std::string &original_code, const std::string &filename)
Given the original code, produce a warning if comments from it were removed during formatting.
Definition Formatter.cpp:90
static std::size_t lineOfLastNodeIn(const Ark::internal::Node &node)
Compute the line on which the deepest right most node of node is at.
std::string format(const Ark::internal::Node &node, std::size_t indent, bool after_newline)
Handles all node formatting.
bool isLongLine(const Ark::internal::Node &node)
static bool isListStartingWithKeyword(const Ark::internal::Node &node, Ark::internal::Keyword keyword)
Check if a given node starts with a given keyword.
std::string formatCondition(const Ark::internal::Node &node, std::size_t indent, bool is_macro=false)
Keyword
The different keywords available.
Definition Common.hpp:79
static constexpr std::size_t SpacePerIndent
Indentation level of each node.
Definition Formatter.hpp:11
static constexpr std::size_t LongLineLength
Max number of characters per line segment to consider splitting.
Definition Formatter.hpp:12