#include "Bytecode.hpp"
#include "executable/bytecode/BytecodeProgram.hpp"
#include "executable/bytecode/codegen/BytecodeGenerator.hpp"
#include "lexer/Lexer.hpp"
#include "parser/Parser.hpp"
#include "gtest/gtest.h"
namespace {
Bytecode make_bytecode_with_locations(std::vector locations)
{
return Bytecode{ /*register_count=*/0,
/*locals_count=*/0,
/*stack_size=*/0,
/*function_name=*/"",
InstructionVector{},
std::move(locations),
/*program=*/nullptr };
}
}// namespace
TEST(BytecodeLocationFor, ReturnsNulloptWhenTableIsEmpty)
{
auto bc = make_bytecode_with_locations({});
EXPECT_FALSE(bc.location_for(0).has_value());
}
TEST(BytecodeLocationFor, ReturnsNulloptWhenQueryPrecedesFirstEntry)
{
auto bc = make_bytecode_with_locations({
InstructionSourceLocation{ /*instruction_index=*/5, /*line=*/10, /*column=*/2 },
});
EXPECT_FALSE(bc.location_for(0).has_value());
}
TEST(BytecodeLocationFor, ReturnsExactMatchEntry)
{
auto bc = make_bytecode_with_locations({
InstructionSourceLocation{ 0, 1, 0 },
InstructionSourceLocation{ 3, 7, 4 },
InstructionSourceLocation{ 10, 12, 0 },
});
const auto loc = bc.location_for(3);
ASSERT_TRUE(loc.has_value());
EXPECT_EQ(loc->line, 7u);
EXPECT_EQ(loc->column, 4u);
}
TEST(BytecodeLocationFor, ExtendsEntryUntilNextOne)
{
auto bc = make_bytecode_with_locations({
InstructionSourceLocation{ 0, 1, 0 },
InstructionSourceLocation{ 3, 7, 4 },
InstructionSourceLocation{ 10, 12, 0 },
});
// Query between entries should return the most recent preceding entry.
for (uint32_t idx : { 0u, 1u, 2u }) {
const auto loc = bc.location_for(idx);
ASSERT_TRUE(loc.has_value()) << "idx=" << idx;
EXPECT_EQ(loc->line, 1u) << "idx=" << idx;
EXPECT_EQ(loc->column, 0u) << "idx=" << idx;
}
for (uint32_t idx : { 3u, 4u, 9u }) {
const auto loc = bc.location_for(idx);
ASSERT_TRUE(loc.has_value()) << "idx=" << idx;
EXPECT_EQ(loc->line, 7u) << "idx=" << idx;
EXPECT_EQ(loc->column, 4u) << "idx=" << idx;
}
for (uint32_t idx : { 10u, 100u, 9999u }) {
const auto loc = bc.location_for(idx);
ASSERT_TRUE(loc.has_value()) << "idx=" << idx;
EXPECT_EQ(loc->line, 12u) << "idx=" << idx;
EXPECT_EQ(loc->column, 0u) << "idx=" << idx;
}
}
// FIXME: think about what should be tested here
// namespace {
// std::shared_ptr generate_bytecode_executable(std::string_view program)
// {
// auto lexer = Lexer::create(std::string(program), "_bytecode_tests_.py");
// parser::Parser p{ lexer };
// p.parse();
// auto module = as<:module>(p.module());
// ASSERT(module);
// auto bytecode =
// codegen::BytecodeGenerator::compile(module, {}, compiler::OptimizationLevel::None);
// return std::static_pointer_cast(bytecode);
// }
// }// namespace
// TEST(Bytecode, CreatesExecutableWithOnlyMain)
// {
// static constexpr std::string_view program = "print(\"Hello, world!\")\n";
// auto bytecode = generate_bytecode_executable(program);
// const auto entry_instruction = bytecode->begin();
// const auto return_instruction = bytecode->end();
// ASSERT_EQ(std::distance(entry_instruction, return_instruction), 4);
// }
// TEST(Bytecode, CreatesExecutableWithMultipleFunctionDefinitions)
// {
// constexpr std::string_view program =
// "def foo(arg):\n"
// " return arg + 42\n"
// "def bar(arg):\n"
// " return arg\n";
// auto bytecode = generate_bytecode_executable(program);
// {
// const auto entry_instruction = bytecode->begin();
// const auto return_instruction = bytecode->end();
// ASSERT_EQ(std::distance(entry_instruction, return_instruction), 3);
// }
// // {
// // const auto &foo = bytecode->as_pyfunction("foo");
// // ASSERT_EQ(foo->backend(), FunctionExecutionBackend::BYTECODE);
// // const auto entry_instruction = std::static_pointer_cast(foo)->begin();
// // const auto return_instruction = std::static_pointer_cast(foo)->end();
// // ASSERT_EQ(std::distance(entry_instruction, return_instruction), 6);
// // }
// // {
// // const auto &bar = bytecode->as_pyfunction("bar");
// // ASSERT_EQ(bar->backend(), FunctionExecutionBackend::BYTECODE);
// // const auto entry_instruction = std::static_pointer_cast(bar)->begin();
// // const auto return_instruction = std::static_pointer_cast(bar)->end();
// // ASSERT_EQ(std::distance(entry_instruction, return_instruction), 4);
// // }
// }