• 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

92.86
/src/arkreactor/Compiler/IntermediateRepresentation/IRCompiler.cpp
1
#include <Ark/Compiler/IntermediateRepresentation/IRCompiler.hpp>
2

3
#include <chrono>
4
#include <utility>
5
#include <unordered_map>
6
#include <picosha2.h>
7
#include <fmt/ostream.h>
8

9
#include <Ark/Constants.hpp>
10
#include <Ark/Literals.hpp>
11
#include <Ark/Compiler/Serialization/IntegerSerializer.hpp>
12
#include <Ark/Compiler/Serialization/IEEE754Serializer.hpp>
13

14
namespace Ark::internal
15
{
16
    using namespace literals;
17

18
    IRCompiler::IRCompiler(const unsigned debug) :
392✔
19
        m_logger("IRCompiler", debug)
196✔
20
    {}
392✔
21

22
    void IRCompiler::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
92✔
23
    {
92✔
24
        m_logger.traceStart("process");
92✔
25
        pushFileHeader();
92✔
26
        pushSymAndValTables(symbols, values);
92✔
27

28
        m_ir = pages;
92✔
29
        compile();
92✔
30

31
        if (m_ir.empty())
92✔
32
        {
33
            // code segment with a single instruction
34
            m_bytecode.push_back(CODE_SEGMENT_START);
×
35
            m_bytecode.push_back(0_u8);
×
36
            m_bytecode.push_back(1_u8);
×
37

38
            m_bytecode.push_back(0_u8);
×
39
            m_bytecode.push_back(HALT);
×
40
            m_bytecode.push_back(0_u8);
×
41
            m_bytecode.push_back(0_u8);
×
42
        }
×
43

44
        // generate a hash of the tables + bytecode
45
        std::vector<unsigned char> hash_out(picosha2::k_digest_size);
92✔
46
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize, m_bytecode.end(), hash_out);
92✔
47
        m_bytecode.insert(m_bytecode.begin() + bytecode::HeaderSize, hash_out.begin(), hash_out.end());
92✔
48

49
        m_logger.traceEnd();
92✔
50
    }
92✔
51

52
    void IRCompiler::dumpToStream(std::ostream& stream) const
9✔
53
    {
9✔
54
        std::size_t index = 0;
9✔
55
        for (const auto& block : m_ir)
32✔
56
        {
57
            fmt::println(stream, "page_{}", index);
23✔
58
            for (const auto entity : block)
461✔
59
            {
60
                switch (entity.kind())
438✔
61
                {
24✔
62
                    case IR::Kind::Label:
63
                        fmt::println(stream, ".L{}:", entity.label());
24✔
64
                        break;
36✔
65

66
                    case IR::Kind::Goto:
67
                        fmt::println(stream, "\tGOTO L{}", entity.label());
12✔
68
                        break;
20✔
69

70
                    case IR::Kind::GotoIfTrue:
71
                        fmt::println(stream, "\tGOTO_IF_TRUE L{}", entity.label());
8✔
72
                        break;
12✔
73

74
                    case IR::Kind::GotoIfFalse:
75
                        fmt::println(stream, "\tGOTO_IF_FALSE L{}", entity.label());
4✔
76
                        break;
357✔
77

78
                    case IR::Kind::Opcode:
79
                        fmt::println(stream, "\t{} {}", InstructionNames[entity.inst()], entity.primaryArg());
353✔
80
                        break;
390✔
81

82
                    case IR::Kind::Opcode2Args:
83
                        fmt::println(stream, "\t{} {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg());
37✔
84
                        break;
37✔
85
                }
438✔
86
            }
438✔
87

88
            fmt::println(stream, "");
23✔
89
            ++index;
23✔
90
        }
23✔
91
    }
9✔
92

93
    const bytecode_t& IRCompiler::bytecode() const noexcept
92✔
94
    {
92✔
95
        return m_bytecode;
92✔
96
    }
97

98
    void IRCompiler::compile()
92✔
99
    {
92✔
100
        // push the different code segments
101
        for (std::size_t i = 0, end = m_ir.size(); i < end; ++i)
1,409✔
102
        {
103
            IR::Block& page = m_ir[i];
1,317✔
104
            // just in case we got too far, always add a HALT to be sure the
105
            // VM won't do anything crazy
106
            page.emplace_back(HALT);
1,317✔
107

108
            // push number of elements
109
            const auto page_size = std::ranges::count_if(page, [](const auto& a) {
41,189✔
110
                return a.kind() != IR::Kind::Label;
39,872✔
111
            });
112
            if (std::cmp_greater(page_size, std::numeric_limits<uint16_t>::max()))
1,317✔
113
                throw std::overflow_error(fmt::format("Size of page {} exceeds the maximum size of 2^16 - 1", i));
×
114

115
            m_bytecode.push_back(CODE_SEGMENT_START);
1,317✔
116
            m_bytecode.push_back(static_cast<uint8_t>((page_size & 0xff00) >> 8));
1,317✔
117
            m_bytecode.push_back(static_cast<uint8_t>(page_size & 0x00ff));
1,317✔
118

119
            // register labels position
120
            uint16_t pos = 0;
1,317✔
121
            std::unordered_map<IR::label_t, uint16_t> label_to_position;
1,317✔
122
            for (auto inst : page)
41,189✔
123
            {
124
                switch (inst.kind())
39,872✔
125
                {
2,983✔
126
                    case IR::Kind::Label:
127
                        label_to_position[inst.label()] = pos;
2,983✔
128
                        break;
39,872✔
129

130
                    default:
131
                        ++pos;
36,889✔
132
                }
39,872✔
133
            }
39,872✔
134

135
            for (auto inst : page)
41,189✔
136
            {
137
                switch (inst.kind())
39,872✔
138
                {
1,431✔
139
                    case IR::Kind::Goto:
140
                        pushWord(Word(JUMP, label_to_position[inst.label()]));
1,431✔
141
                        break;
2,408✔
142

143
                    case IR::Kind::GotoIfTrue:
144
                        pushWord(Word(POP_JUMP_IF_TRUE, label_to_position[inst.label()]));
977✔
145
                        break;
1,561✔
146

147
                    case IR::Kind::GotoIfFalse:
148
                        pushWord(Word(POP_JUMP_IF_FALSE, label_to_position[inst.label()]));
584✔
149
                        break;
34,481✔
150

151
                    case IR::Kind::Opcode:
152
                        [[fallthrough]];
153
                    case IR::Kind::Opcode2Args:
154
                        pushWord(inst.bytecode());
33,897✔
155
                        break;
36,880✔
156

157
                    default:
158
                        break;
2,983✔
159
                }
39,872✔
160
            }
39,872✔
161
        }
1,317✔
162
    }
92✔
163

164
    void IRCompiler::pushWord(const Word& word)
36,889✔
165
    {
36,889✔
166
        m_bytecode.push_back(word.opcode);
36,889✔
167
        m_bytecode.push_back(word.byte_1);
36,889✔
168
        m_bytecode.push_back(word.byte_2);
36,889✔
169
        m_bytecode.push_back(word.byte_3);
36,889✔
170
    }
36,889✔
171

172
    void IRCompiler::pushFileHeader() noexcept
92✔
173
    {
92✔
174
        /*
175
            Generating headers:
176
                - lang name (to be sure we are executing an ArkScript file)
177
                    on 4 bytes (ark + padding)
178
                - version (major: 2 bytes, minor: 2 bytes, patch: 2 bytes)
179
                - timestamp (8 bytes, unix format)
180
        */
181

182
        m_bytecode.push_back('a');
92✔
183
        m_bytecode.push_back('r');
92✔
184
        m_bytecode.push_back('k');
92✔
185
        m_bytecode.push_back(0_u8);
92✔
186

187
        // push version
188
        for (const int n : std::array { ARK_VERSION_MAJOR, ARK_VERSION_MINOR, ARK_VERSION_PATCH })
368✔
189
            serializeOn2BytesToVecBE(n, m_bytecode);
276✔
190

191
        // push timestamp
192
        const long long timestamp = std::chrono::duration_cast<std::chrono::seconds>(
92✔
193
                                        std::chrono::system_clock::now().time_since_epoch())
92✔
194
                                        .count();
92✔
195
        for (long i = 0; i < 8; ++i)
828✔
196
        {
197
            const long shift = 8 * (7 - i);
736✔
198
            const auto ts_byte = static_cast<uint8_t>((timestamp & (0xffLL << shift)) >> shift);
736✔
199
            m_bytecode.push_back(ts_byte);
736✔
200
        }
736✔
201
    }
92✔
202

203
    void IRCompiler::pushSymAndValTables(const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
92✔
204
    {
92✔
205
        const std::size_t symbol_size = symbols.size();
92✔
206
        if (symbol_size > std::numeric_limits<uint16_t>::max())
92✔
207
            throw std::overflow_error(fmt::format("Too many symbols: {}, exceeds the maximum size of 2^16 - 1", symbol_size));
×
208

209
        m_bytecode.push_back(SYM_TABLE_START);
92✔
210
        serializeOn2BytesToVecBE(symbol_size, m_bytecode);
92✔
211

212
        for (const auto& sym : symbols)
2,517✔
213
        {
214
            // push the string, null terminated
215
            std::ranges::transform(sym, std::back_inserter(m_bytecode), [](const char i) {
24,401✔
216
                return static_cast<uint8_t>(i);
21,976✔
217
            });
218
            m_bytecode.push_back(0_u8);
2,425✔
219
        }
2,425✔
220

221
        const std::size_t value_size = values.size();
92✔
222
        if (value_size > std::numeric_limits<uint16_t>::max())
92✔
223
            throw std::overflow_error(fmt::format("Too many values: {}, exceeds the maximum size of 2^16 - 1", value_size));
×
224

225
        m_bytecode.push_back(VAL_TABLE_START);
92✔
226
        serializeOn2BytesToVecBE(value_size, m_bytecode);
92✔
227

228
        for (const ValTableElem& val : values)
3,049✔
229
        {
230
            switch (val.type)
2,957✔
231
            {
560✔
232
                case ValTableElemType::Number:
233
                {
234
                    m_bytecode.push_back(NUMBER_TYPE);
560✔
235
                    const auto n = std::get<double>(val.value);
560✔
236
                    const auto [exponent, mantissa] = ieee754::serialize(n);
560✔
237
                    serializeToVecLE(exponent, m_bytecode);
560✔
238
                    serializeToVecLE(mantissa, m_bytecode);
560✔
239
                    break;
240
                }
1,732✔
241

242
                case ValTableElemType::String:
243
                {
244
                    m_bytecode.push_back(STRING_TYPE);
1,172✔
245
                    auto t = std::get<std::string>(val.value);
1,172✔
246
                    std::ranges::transform(t, std::back_inserter(m_bytecode), [](const char i) {
21,495✔
247
                        return static_cast<uint8_t>(i);
20,323✔
248
                    });
249
                    break;
250
                }
2,397✔
251

252
                case ValTableElemType::PageAddr:
253
                {
254
                    m_bytecode.push_back(FUNC_TYPE);
1,225✔
255
                    const std::size_t addr = std::get<std::size_t>(val.value);
1,225✔
256
                    serializeOn2BytesToVecBE(addr, m_bytecode);
1,225✔
257
                    break;
258
                }
1,225✔
259
            }
2,957✔
260

261
            m_bytecode.push_back(0_u8);
2,957✔
262
        }
2,957✔
263
    }
92✔
264
}
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