ArkScript
A small, lisp-inspired, functional scripting language
Optimizer.cpp
Go to the documentation of this file.
2
3#include <Ark/Utils/Utils.hpp>
4
5namespace Ark::internal
6{
7 Optimizer::Optimizer(const unsigned debug) noexcept :
8 Pass("Optimizer", debug), m_ast()
9 {}
10
11 void Optimizer::process(const Node& ast)
12 {
13 // do not handle non-list nodes
15 return;
16 m_ast = ast;
17
18 m_logger.traceStart("process");
20
21 // logic: remove piece of code with only 1 reference, if they aren't function calls
24
25 m_logger.trace("AST after name pruning nodes");
27 m_ast.debugPrint(std::cout) << '\n';
28 }
29
30 const Node& Optimizer::ast() const noexcept
31 {
32 return m_ast;
33 }
34
36 {
37 if (node.nodeType() == NodeType::Symbol || node.nodeType() == NodeType::Capture)
38 {
39 auto [element, inserted] = m_sym_appearances.try_emplace(node.string(), 0);
40 if (!inserted)
41 element->second++;
42 }
43 else if (node.nodeType() == NodeType::Field)
44 {
45 for (auto& child : node.list())
47 }
48 else if (node.nodeType() == NodeType::List)
49 {
50 // FIXME: very primitive removal of (if true/false ...) and (while false ...)
51 if (node.constList().size() >= 3 && node.constList().front().nodeType() == NodeType::Keyword &&
52 (node.constList().front().keyword() == Keyword::If || node.constList().front().keyword() == Keyword::While))
53 {
54 const auto keyword = node.constList().front().keyword();
55 const auto condition = node.constList()[1];
56 const auto body = node.constList()[2];
57
58 if (condition.nodeType() == NodeType::Symbol && condition.string() == "false")
59 {
60 // replace the node by an Unused, it is either a (while cond block) or (if cond then)
61 if (node.constList().size() == 3)
62 node = Node(NodeType::Unused);
63 else // it is a (if cond then else)
64 {
65 const auto back = node.constList().back();
66 node = back;
67 }
68 }
69 // only update '(if true then [else])' to 'then'
70 else if (keyword == Keyword::If && condition.nodeType() == NodeType::Symbol && condition.string() == "true")
71 node = body;
72
73 // do not try to iterate on the child nodes as they do not exist anymore,
74 // we performed some optimization that squashed them.
75 if (!node.isListLike())
76 return;
77 }
78
79 // iterate over children
80 for (auto& child : node.list())
82 }
83 else if (node.nodeType() == NodeType::Namespace)
85 }
86
88 {
89 for (auto& child : node.list())
90 {
91 if (child.nodeType() == NodeType::List && !child.constList().empty() &&
92 child.constList()[0].nodeType() == NodeType::Keyword)
93 {
94 const Keyword kw = child.constList()[0].keyword();
95
96 // eliminate nested begin blocks
97 if (kw == Keyword::Begin)
98 {
100 // skip let/ mut detection
101 continue;
102 }
103
104 // check if it's a let/mut declaration and perform removal
105 if (kw == Keyword::Let || kw == Keyword::Mut)
106 {
107 const std::string name = child.constList()[1].string();
108 // a variable was only declared and never used
109 if (m_sym_appearances.contains(name) && m_sym_appearances[name] < 1)
110 {
111 m_logger.debug("Removing unused variable '{}'", name);
112 // erase the node by turning it to an Unused node
113 child = Node(NodeType::Unused);
114 }
115 else if (child.comment().find("@deprecated") != std::string::npos)
116 {
117 std::string advice;
118 const std::vector<std::string> vec = Utils::splitString(child.comment(), '\n');
119 if (auto result = std::ranges::find_if(vec, [](const std::string& line) {
120 return line.find("@deprecated") != std::string::npos;
121 });
122 result != vec.end())
123 {
124 advice = result->substr(result->find("@deprecated") + 11);
125 Utils::trimWhitespace(advice);
126 }
127
129 "Using a deprecated {} `{}'.{}",
130 child.constList()[2].nodeType() == NodeType::List &&
131 !child.constList()[2].constList().empty() &&
132 child.constList()[2].constList()[0].nodeType() == NodeType::Keyword &&
133 child.constList()[2].constList()[0].keyword() == Keyword::Fun
134 ? "function"
135 : "value",
136 name,
137 advice.empty() ? "" : " " + advice);
138 }
139 }
140 }
141 else if (child.nodeType() == NodeType::Namespace)
142 pruneUnusedGlobalVariables(*child.arkNamespace().ast);
143 }
144 }
145}
Lots of utilities about string, filesystem and more.
Optimizes a given ArkScript AST.
void debug(const Logger::MessageAndLocation &data, Args &&... args)
Write a debug level log using fmtlib.
Definition Logger.hpp:96
bool shouldTrace() const
Definition Logger.hpp:54
void trace(const char *fmt, Args &&... args)
Write a trace level log using fmtlib.
Definition Logger.hpp:132
void warn(const char *fmt, Args &&... args)
Write a warn level log using fmtlib.
Definition Logger.hpp:80
void traceStart(std::string &&trace_name)
Definition Logger.hpp:109
A node of an Abstract Syntax Tree for ArkScript.
Definition Node.hpp:32
NodeType nodeType() const noexcept
Return the node type.
Definition Node.cpp:78
bool isListLike() const noexcept
Check if the node is a list like node.
Definition Node.cpp:83
const std::string & string() const noexcept
Return the string held by the value (if the node type allows it)
Definition Node.cpp:38
const std::vector< Node > & constList() const noexcept
Return the list of sub-nodes held by the node.
Definition Node.cpp:73
Namespace & arkNamespace() noexcept
Return the namespace held by the value (if the node type allows it)
Definition Node.cpp:53
std::ostream & debugPrint(std::ostream &os) const noexcept
Print a node to an output stream with added type annotations.
Definition Node.cpp:287
std::vector< Node > & list() noexcept
Return the list of sub-nodes held by the node.
Definition Node.cpp:68
Optimizer(unsigned debug) noexcept
Construct a new Optimizer.
Definition Optimizer.cpp:7
void pruneUnusedGlobalVariables(Node &node)
Remove unused global variables from the AST.
Definition Optimizer.cpp:87
const Node & ast() const noexcept
Returns the modified AST.
Definition Optimizer.cpp:30
void countAndPruneDeadCode(Node &node)
Count the occurrences of each symbol in the AST, recursively, and prune if false/true,...
Definition Optimizer.cpp:35
std::unordered_map< std::string, unsigned > m_sym_appearances
Definition Optimizer.hpp:54
void process(const Node &ast)
Send the AST to the optimizer, then run the different optimization strategies on it.
Definition Optimizer.cpp:11
An interface to describe compiler passes.
Definition Pass.hpp:24
std::vector< std::string > splitString(const std::string &source, const char sep)
Cut a string into pieces, given a character separator.
Definition Utils.hpp:31
ARK_API void trimWhitespace(std::string &line)
Remove whitespaces at the start and end of a string.
Definition Utils.cpp:40
Keyword
The different keywords available.
Definition Common.hpp:79
std::shared_ptr< Node > ast
Definition Namespace.hpp:18