• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ArkScript-lang / Ark / 13762570662

10 Mar 2025 10:35AM UTC coverage: 78.931% (-0.1%) from 79.05%
13762570662

push

github

SuperFola
refactor(builtins): use std::numbers instead of <cmath> for M_PI and HUGE_VAL (float inf)

5848 of 7409 relevant lines covered (78.93%)

79635.36 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

84.21
/src/arkreactor/Compiler/Package/ImportSolver.cpp
1
#include <Ark/Compiler/Package/ImportSolver.hpp>
2

3
#include <ranges>
4
#include <algorithm>
5
#include <fmt/core.h>
6

7
#include <Ark/Files.hpp>
8
#include <Ark/Exceptions.hpp>
9
#include <Ark/Compiler/AST/Parser.hpp>
10

11
namespace Ark::internal
12
{
13
    ImportSolver::ImportSolver(const unsigned debug, const std::vector<std::filesystem::path>& libenv) :
588✔
14
        Pass("ImportSolver", debug), m_debug_level(debug), m_libenv(libenv), m_ast()
196✔
15
    {}
588✔
16

17
    ImportSolver& ImportSolver::setup(const std::filesystem::path& root, const std::vector<Import>& origin_imports)
166✔
18
    {
166✔
19
        if (is_directory(root))
166✔
20
            m_root = root;
4✔
21
        else
22
            m_root = root.parent_path();
162✔
23

24
        for (const auto& origin_import : std::ranges::reverse_view(origin_imports))
231✔
25
            m_imports.push(origin_import);
65✔
26

27
        return *this;
166✔
28
    }
29

30
    void ImportSolver::process(const Node& origin_ast)
166✔
31
    {
166✔
32
        m_logger.traceStart("process");
166✔
33

34
        while (!m_imports.empty())
282✔
35
        {
36
            Import import = m_imports.top();
116✔
37
            m_logger.debug("Importing {}", import.toPackageString());
116✔
38

39
            // Remove the top element to process the other imports
40
            // It needs to be removed first because we might be adding
41
            // other imports later and don't want to pop THEM
42
            m_imports.pop();
116✔
43
            const auto package = import.toPackageString();
116✔
44

45
            if (m_packages.contains(package))
116✔
46
            {
47
                // merge the definition, so that we can generate valid Full Qualified Names in the name & scope resolver
48
                m_packages[package].import.with_prefix |= import.with_prefix;
25✔
49
                m_packages[package].import.is_glob |= import.is_glob;
25✔
50
                for (auto&& symbol : import.symbols)
30✔
51
                    m_packages[package].import.symbols.push_back(symbol);
5✔
52
            }
25✔
53
            else
54
            {
55
                // NOTE: since the "file" (=root) argument doesn't change between all calls, we could get rid of it
56
                std::vector<Import> temp = parseImport(m_root, import);
91✔
57
                for (auto& additional_import : std::ranges::reverse_view(temp))
142✔
58
                    m_imports.push(additional_import);
51✔
59
            }
91✔
60
        }
116✔
61

62
        m_logger.traceStart("findAndReplaceImports");
166✔
63
        m_ast = findAndReplaceImports(origin_ast).first;
166✔
64
        m_logger.traceEnd();
166✔
65

66
        m_logger.traceEnd();
166✔
67
    }
166✔
68

69
    std::pair<Node, bool> ImportSolver::findAndReplaceImports(const Node& ast)
112,311✔
70
    {
112,311✔
71
        Node x = ast;
112,311✔
72
        if (x.nodeType() == NodeType::List)
112,311✔
73
        {
74
            if (x.constList().size() >= 2 && x.constList()[0].nodeType() == NodeType::Keyword &&
22,663✔
75
                x.constList()[0].keyword() == Keyword::Import)
6,978✔
76
            {
77
                // compute the package string: foo.bar.egg
78
                const auto import_node = x.constList()[1].constList();
116✔
79
                const std::string package = std::accumulate(
232✔
80
                    std::next(import_node.begin()),
116✔
81
                    import_node.end(),
116✔
82
                    import_node[0].string(),
116✔
83
                    [](const std::string& acc, const Node& elem) -> std::string {
84✔
84
                        return acc + "." + elem.string();
84✔
85
                    });
×
86

87
                // if it wasn't imported already, register it
88
                if (std::ranges::find(m_imported, package) == m_imported.end())
116✔
89
                {
90
                    m_imported.push_back(package);
91✔
91
                    // modules are already handled, we can safely replace the node
92
                    x = m_packages[package].ast;
91✔
93
                    if (!m_packages[package].has_been_processed)
91✔
94
                    {
95
                        const auto import = m_packages[package].import;
91✔
96

97
                        // prefix to lowercase ; usually considered unsafe (https://devblogs.microsoft.com/oldnewthing/20241007-00/?p=110345)
98
                        // but we are dealing with prefix from filenames, thus we can somewhat assume we are in safe zone
99
                        std::string prefix = import.prefix;
91✔
100
                        std::ranges::transform(
91✔
101
                            prefix, prefix.begin(),
91✔
102
                            [](auto c) {
486✔
103
                                return std::tolower(c);
486✔
104
                            });
105

106
                        x = Node(Namespace {
364✔
107
                            .name = prefix,
91✔
108
                            .is_glob = import.is_glob,
91✔
109
                            .with_prefix = import.with_prefix,
91✔
110
                            .symbols = import.symbols,
91✔
111
                            .ast = std::make_shared<Node>(findAndReplaceImports(x).first) });
91✔
112
                        x.arkNamespace().ast->setPos(ast.line(), ast.col());
91✔
113
                        x.arkNamespace().ast->setFilename(ast.filename());
91✔
114
                    }
91✔
115
                    // we parsed an import node, return true in the pair to notify the caller
116
                    return std::make_pair(x, /* is_import= */ true);
91✔
117
                }
118

119
                // Replace by empty node to avoid breaking the code gen
120
                x = Node(NodeType::List);
25✔
121
                x.push_back(Node(Keyword::Begin));
25✔
122
            }
116✔
123
            else
124
            {
125
                for (std::size_t i = 0; i < x.constList().size(); ++i)
127,623✔
126
                {
127
                    auto [node, is_import] = findAndReplaceImports(x.constList()[i]);
112,054✔
128
                    x.list()[i] = node;
112,054✔
129
                }
112,054✔
130
            }
131
        }
15,594✔
132

133
        return std::make_pair(x, /* is_import= */ false);
112,220✔
134
    }
112,311✔
135

136
    const Node& ImportSolver::ast() const noexcept
166✔
137
    {
166✔
138
        return m_ast;
166✔
139
    }
140

141
    std::vector<Import> ImportSolver::parseImport(const std::filesystem::path& base_path, const Import& import)
91✔
142
    {
91✔
143
        m_logger.traceStart(fmt::format("parseImport {}", base_path.string()));
91✔
144

145
        const auto path = findFile(base_path, import);
91✔
146
        if (path.extension() == ".arkm")  // Nothing to import in case of modules
91✔
147
        {
148
            // Creating an import node that will stay there when visiting the AST and
149
            // replacing the imports with their parsed module
150
            auto module_node = Node(NodeType::List);
×
151
            module_node.push_back(Node(Keyword::Import));
×
152

153
            auto package_node = Node(NodeType::List);
×
154
            std::ranges::transform(import.package, std::back_inserter(package_node.list()), [](const std::string& stem) {
×
155
                return Node(NodeType::String, stem);
×
156
            });
157
            module_node.push_back(package_node);
×
158
            // empty symbols list
159
            module_node.push_back(Node(NodeType::List));
×
160

161
            m_packages[import.toPackageString()] = Package {
×
162
                module_node,
×
163
                import,
×
164
                true
165
            };
166

167
            return {};
×
168
        }
×
169

170
        Parser parser(m_debug_level);
91✔
171
        const std::string code = Utils::readFile(path.generic_string());
91✔
172
        parser.process(path.string(), code);
91✔
173
        m_packages[import.toPackageString()] = Package {
273✔
174
            parser.ast(),
91✔
175
            import,
91✔
176
            false
177
        };
178

179
        m_logger.traceEnd();
91✔
180
        return parser.imports();
91✔
181
    }
91✔
182

183
    std::optional<std::filesystem::path> testExtensions(const std::filesystem::path& folder, const std::string& package_path)
146✔
184
    {
146✔
185
        if (auto code_path = folder / (package_path + ".ark"); std::filesystem::exists(code_path))
237✔
186
            return code_path;
91✔
187
        if (auto module_path = folder / (package_path + ".arkm"); std::filesystem::exists(module_path))
55✔
188
            return module_path;
×
189
        return {};
55✔
190
    }
146✔
191

192
    std::filesystem::path ImportSolver::findFile(const std::filesystem::path& file, const Import& import) const
91✔
193
    {
91✔
194
        const std::string package_path = import.packageToPath();
91✔
195
        if (auto maybe_path = testExtensions(m_root, package_path); maybe_path.has_value())
127✔
196
            return maybe_path.value();
36✔
197

198
        // search in all folders in environment path
199
        for (const auto& path : m_libenv)
55✔
200
        {
201
            if (auto maybe_path = testExtensions(path, package_path); maybe_path.has_value())
110✔
202
                return maybe_path.value();
55✔
203
        }
55✔
204

205
        // fallback, we couldn't find the file
206
        throw CodeError(
×
207
            fmt::format("While processing file {}, couldn't import {}: file not found",
×
208
                        file.generic_string(), import.toPackageString()),
×
209
            file.generic_string(),
×
210
            import.line,
×
211
            import.col,
×
212
            fmt::format("(import {})", import.toPackageString()));
×
213
    }
91✔
214
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc