Compare commits
No commits in common. "cpp" and "haskell" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,5 +3,4 @@
|
|||||||
CMakeFiles/
|
CMakeFiles/
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
node_modules/
|
node_modules/
|
||||||
/dist/
|
|
||||||
/dist-newstyle/
|
/dist-newstyle/
|
||||||
|
1
.ruby-version
Normal file
1
.ruby-version
Normal file
@ -0,0 +1 @@
|
|||||||
|
3.3.5
|
@ -1,5 +0,0 @@
|
|||||||
# Revision history for elna
|
|
||||||
|
|
||||||
## 1.0 -- YYYY-mm-dd
|
|
||||||
|
|
||||||
* First version. Released on an unsuspecting world.
|
|
@ -1,28 +0,0 @@
|
|||||||
cmake_minimum_required(VERSION 3.21)
|
|
||||||
project(Elna)
|
|
||||||
|
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS 1)
|
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
|
|
||||||
set(CMAKE_CXX_STANDARD 17)
|
|
||||||
|
|
||||||
find_package(Boost COMPONENTS process program_options REQUIRED)
|
|
||||||
find_package(FLEX)
|
|
||||||
include_directories(${Boost_INCLUDE_DIR})
|
|
||||||
|
|
||||||
FLEX_TARGET(scanner source/scanner.l ${CMAKE_CURRENT_BINARY_DIR}/scanner.cpp)
|
|
||||||
|
|
||||||
add_executable(elna cli/main.cpp
|
|
||||||
source/lexer.cpp include/elna/source/lexer.hpp
|
|
||||||
source/parser.cpp include/elna/source/parser.hpp
|
|
||||||
source/types.cpp include/elna/source/types.hpp
|
|
||||||
source/symbol_table.cpp include/elna/source/symbol_table.hpp
|
|
||||||
source/result.cpp include/elna/source/result.hpp
|
|
||||||
source/semantic.cpp include/elna/source/semantic.hpp
|
|
||||||
source/optimizer.cpp include/elna/source/optimizer.hpp
|
|
||||||
backend/riscv.cpp include/elna/backend/riscv.hpp
|
|
||||||
backend/target.cpp include/elna/backend/target.hpp
|
|
||||||
cli/cl.cpp include/elna/cli/cl.hpp
|
|
||||||
${FLEX_scanner_OUTPUTS}
|
|
||||||
)
|
|
||||||
target_include_directories(elna PRIVATE include)
|
|
||||||
target_link_libraries(elna LINK_PUBLIC ${Boost_LIBRARIES})
|
|
34
TODO
34
TODO
@ -1,14 +1,22 @@
|
|||||||
# Compiler
|
# Intermediate code generation
|
||||||
|
|
||||||
- Catch exceptions thrown by the argument parser and print them normally.
|
- To access named parameters inside a procedure, IR should be able to reference
|
||||||
- Structs or records.
|
them. During the generation the needed information (e.g. offsets or registers)
|
||||||
- Unions.
|
can be extracted from the symbol table and saved in the operands.
|
||||||
- Support immediates greater than 12 bits.
|
|
||||||
- It seems instructions are correctly encoded only if the compiler is running
|
# ELF generation
|
||||||
on a little endian architecture.
|
|
||||||
- Assignment to a pointer.
|
- Don't ignore relocations where the symbol is not defined in the symbol table.
|
||||||
- Static array.
|
Report an error about an undefined symbol.
|
||||||
- Syscalls.
|
|
||||||
- Error message with an empty file wrongly says that a ")" is expected.
|
# Register allocation
|
||||||
- Support any expressions for constants.
|
|
||||||
- Name analysis should fail if there are undefined symbols.
|
- Each temporary variable gets a tn register where n is the variable index. If
|
||||||
|
there more variables the allocation will fail with out of bounds runtime
|
||||||
|
error. Implement spill over.
|
||||||
|
- The allocator puts temporary and local variables into the same registers,
|
||||||
|
causing conflicts.
|
||||||
|
|
||||||
|
# Language
|
||||||
|
|
||||||
|
- Array support.
|
||||||
|
@ -1,479 +0,0 @@
|
|||||||
#include "elna/backend/riscv.hpp"
|
|
||||||
#include <cassert>
|
|
||||||
#include <functional>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace elna::riscv
|
|
||||||
{
|
|
||||||
instruction::instruction(base_opcode opcode)
|
|
||||||
{
|
|
||||||
this->representation = static_cast<std::underlying_type<base_opcode>::type>(opcode);
|
|
||||||
}
|
|
||||||
|
|
||||||
instruction& instruction::i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate)
|
|
||||||
{
|
|
||||||
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
|
|
||||||
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
|
|
||||||
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
|
|
||||||
| (immediate << 20);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
instruction& instruction::s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2)
|
|
||||||
{
|
|
||||||
this->representation |= ((imm & 0x1f) << 7)
|
|
||||||
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
|
|
||||||
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
|
|
||||||
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
|
|
||||||
| ((imm & 0xfe0) << 20);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
instruction& instruction::b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2)
|
|
||||||
{
|
|
||||||
this->representation |= ((imm & 0x800) >> 4) | ((imm & 0x1e) << 7)
|
|
||||||
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
|
|
||||||
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
|
|
||||||
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
|
|
||||||
| ((imm & 0x7e0) << 20) | ((imm & 0x1000) << 19);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
instruction& instruction::r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2, funct7_t funct7)
|
|
||||||
{
|
|
||||||
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
|
|
||||||
| (static_cast<std::underlying_type<funct3_t>::type>(funct3) << 12)
|
|
||||||
| (static_cast<std::underlying_type<x_register>::type>(rs1) << 15)
|
|
||||||
| (static_cast<std::underlying_type<x_register>::type>(rs2) << 20)
|
|
||||||
| (static_cast<std::underlying_type<funct7_t>::type>(funct7) << 25);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
instruction& instruction::u(x_register rd, std::uint32_t imm)
|
|
||||||
{
|
|
||||||
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7) | (imm << 12);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
instruction& instruction::j(x_register rd, std::uint32_t imm)
|
|
||||||
{
|
|
||||||
this->representation |= (static_cast<std::underlying_type<x_register>::type>(rd) << 7)
|
|
||||||
| (imm & 0xff000) | ((imm & 0x800) << 9) | ((imm & 0x7fe) << 20) | ((imm & 0x100000) << 11);
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::byte *instruction::cbegin() const
|
|
||||||
{
|
|
||||||
return reinterpret_cast<const std::byte *>(&this->representation);
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::byte *instruction::cend() const
|
|
||||||
{
|
|
||||||
return reinterpret_cast<const std::byte *>(&this->representation) + sizeof(this->representation);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void relocate(std::string_view name, address_t target, std::vector<reference>& references,
|
|
||||||
std::vector<instruction>& instructions, std::shared_ptr<source::writer<std::byte>> writer)
|
|
||||||
{
|
|
||||||
references.push_back(reference());
|
|
||||||
references.back().name = name;
|
|
||||||
references.back().offset = writer->size() + instructions.size() * 4;
|
|
||||||
references.back().target = target;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void prologue(std::vector<instruction>& instructions)
|
|
||||||
{
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm));
|
|
||||||
instructions.push_back(instruction(base_opcode::store));
|
|
||||||
instructions.push_back(instruction(base_opcode::store));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void epilogue(const std::size_t stack_size, std::vector<instruction>& instructions)
|
|
||||||
{
|
|
||||||
instructions[0].i(x_register::sp, funct3_t::addi, x_register::sp, -stack_size);
|
|
||||||
instructions[1].s(0, funct3_t::sw, x_register::sp, x_register::s0);
|
|
||||||
instructions[2].s(4, funct3_t::sw, x_register::sp, x_register::ra);
|
|
||||||
instructions[3].i(x_register::s0, funct3_t::addi, x_register::sp, stack_size);
|
|
||||||
|
|
||||||
// Epilogue.
|
|
||||||
instructions.push_back(instruction(base_opcode::load)
|
|
||||||
.i(x_register::s0, funct3_t::lw, x_register::sp, 0));
|
|
||||||
instructions.push_back(instruction(base_opcode::load)
|
|
||||||
.i(x_register::ra, funct3_t::lw, x_register::sp, 4));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::sp, funct3_t::addi, x_register::sp, stack_size));
|
|
||||||
instructions.push_back(instruction(base_opcode::jalr)
|
|
||||||
.i(x_register::zero, funct3_t::jalr, x_register::ra, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void generate_intrinsics(std::shared_ptr<source::writer<std::byte>> writer,
|
|
||||||
std::vector<reference>& references)
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::vector<instruction> instructions;
|
|
||||||
prologue(instructions);
|
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a1, funct3_t::addi, x_register::zero, 't'));
|
|
||||||
instructions.push_back(instruction(base_opcode::branch)
|
|
||||||
.b(8, funct3_t::bne, x_register::zero, x_register::a0));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a1, funct3_t::addi, x_register::zero, 'f'));
|
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::t0, funct3_t::addi, x_register::a0, 0));
|
|
||||||
instructions.push_back(instruction(base_opcode::store)
|
|
||||||
.s(0, funct3_t::sw, x_register::s0, x_register::a1));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a0, funct3_t::addi, x_register::zero, 1));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a1, funct3_t::addi, x_register::s0, 0));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a2, funct3_t::addi, x_register::zero, 1));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a7, funct3_t::addi, x_register::zero, 64));
|
|
||||||
instructions.push_back(instruction(base_opcode::system)
|
|
||||||
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::t1, funct3_t::addi, x_register::zero, '\n'));
|
|
||||||
instructions.push_back(instruction(base_opcode::store)
|
|
||||||
.s(0, funct3_t::sw, x_register::s0, x_register::t1));
|
|
||||||
instructions.push_back(instruction(base_opcode::system)
|
|
||||||
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a0, funct3_t::addi, x_register::t0, 0));
|
|
||||||
|
|
||||||
epilogue(12, instructions);
|
|
||||||
|
|
||||||
writer->sink("writeb", reinterpret_cast<const std::byte *>(instructions.data()),
|
|
||||||
instructions.size() * sizeof(instruction));
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::vector<instruction> instructions;
|
|
||||||
prologue(instructions);
|
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::t0, funct3_t::addi, x_register::a0, 0));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a0, funct3_t::addi, x_register::a0, '0'));
|
|
||||||
instructions.push_back(instruction(base_opcode::store)
|
|
||||||
.s(0, funct3_t::sw, x_register::s0, x_register::a0));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a0, funct3_t::addi, x_register::zero, 1));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a1, funct3_t::addi, x_register::s0, 0));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a2, funct3_t::addi, x_register::zero, 1));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a7, funct3_t::addi, x_register::zero, 64));
|
|
||||||
instructions.push_back(instruction(base_opcode::system)
|
|
||||||
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::t1, funct3_t::addi, x_register::zero, '\n'));
|
|
||||||
instructions.push_back(instruction(base_opcode::store)
|
|
||||||
.s(0, funct3_t::sw, x_register::s0, x_register::t1));
|
|
||||||
instructions.push_back(instruction(base_opcode::system)
|
|
||||||
.i(x_register::zero, funct3_t::priv, x_register::zero, 0));
|
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a0, funct3_t::addi, x_register::t0, 0));
|
|
||||||
|
|
||||||
epilogue(12, instructions);
|
|
||||||
|
|
||||||
writer->sink("writei", reinterpret_cast<const std::byte *>(instructions.data()),
|
|
||||||
instructions.size() * sizeof(instruction));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void load_in_register(std::shared_ptr<source::operand> operand, const x_register target,
|
|
||||||
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
|
|
||||||
{
|
|
||||||
std::shared_ptr<source::integer_operand> integer_operand{ nullptr };
|
|
||||||
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
|
|
||||||
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
|
|
||||||
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
|
|
||||||
std::shared_ptr<source::parameter_info> parameter_symbol{ nullptr };
|
|
||||||
|
|
||||||
if ((integer_operand = std::dynamic_pointer_cast<source::integer_operand>(operand)) != nullptr)
|
|
||||||
{
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(target, funct3_t::addi, x_register::zero, integer_operand->value()));
|
|
||||||
}
|
|
||||||
else if ((variable_operand = std::dynamic_pointer_cast<source::variable_operand>(operand)) != nullptr)
|
|
||||||
{
|
|
||||||
const auto& name = procedure_info->scope()->lookup(variable_operand->name());
|
|
||||||
if ((variable_symbol = std::dynamic_pointer_cast<source::variable_info>(name)) != nullptr)
|
|
||||||
{
|
|
||||||
instructions.push_back(instruction(base_opcode::load)
|
|
||||||
.i(target, funct3_t::lw, x_register::s0, variable_symbol->offset));
|
|
||||||
}
|
|
||||||
else if ((parameter_symbol = std::dynamic_pointer_cast<source::parameter_info>(name)) != nullptr)
|
|
||||||
{
|
|
||||||
instructions.push_back(instruction(base_opcode::load)
|
|
||||||
.i(target, funct3_t::lw, x_register::s0, parameter_symbol->offset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((temporary_variable = std::dynamic_pointer_cast<source::temporary_variable>(operand)) != nullptr)
|
|
||||||
{
|
|
||||||
instructions.push_back(instruction(base_opcode::load));
|
|
||||||
instructions.back().i(target, funct3_t::lw, x_register::s0,
|
|
||||||
procedure_info->local_stack_size + 4 * temporary_variable->counter());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void store_from_register(std::shared_ptr<source::operand> destination, const x_register target,
|
|
||||||
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
|
|
||||||
{
|
|
||||||
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
|
|
||||||
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
|
|
||||||
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
|
|
||||||
|
|
||||||
if ((variable_operand = std::dynamic_pointer_cast<source::variable_operand>(destination)) != nullptr)
|
|
||||||
{
|
|
||||||
variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
|
|
||||||
procedure_info->scope()->lookup(variable_operand->name()));
|
|
||||||
instructions.push_back(instruction(base_opcode::store)
|
|
||||||
.s(variable_symbol->offset, funct3_t::sw, x_register::s0, target));
|
|
||||||
}
|
|
||||||
else if ((temporary_variable = std::dynamic_pointer_cast<source::temporary_variable>(destination)) != nullptr)
|
|
||||||
{
|
|
||||||
instructions.push_back(instruction(base_opcode::store));
|
|
||||||
instructions.back().s(procedure_info->local_stack_size + 4 * temporary_variable->counter(),
|
|
||||||
funct3_t::sw, x_register::s0, target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void perform_binary_operation(const source::binary_operator operation, std::shared_ptr<source::operand> lhs,
|
|
||||||
std::shared_ptr<source::operand> rhs, std::shared_ptr<source::operand> destination,
|
|
||||||
std::shared_ptr<source::procedure_info> procedure_info, std::vector<instruction>& instructions)
|
|
||||||
{
|
|
||||||
constexpr auto lhs_register = x_register::a0;
|
|
||||||
std::shared_ptr<source::variable_operand> variable_operand{ nullptr };
|
|
||||||
std::shared_ptr<source::temporary_variable> temporary_variable{ nullptr };
|
|
||||||
std::shared_ptr<source::variable_info> variable_symbol{ nullptr };
|
|
||||||
|
|
||||||
load_in_register(lhs, x_register::a0, procedure_info, instructions);
|
|
||||||
load_in_register(rhs, x_register::t0, procedure_info, instructions);
|
|
||||||
|
|
||||||
// Calculate the result and assign it to a variable on the stack.
|
|
||||||
switch (operation)
|
|
||||||
{
|
|
||||||
case source::binary_operator::sum:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::add, x_register::a0, x_register::t0));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::subtraction:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::multiplication:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::mul, x_register::a0, x_register::t0, funct7_t::muldiv));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::division:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::div, x_register::a0, x_register::t0, funct7_t::muldiv));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::equals:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(lhs_register, funct3_t::sltiu, lhs_register, 1));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::not_equals:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::sub, x_register::a0, x_register::t0, funct7_t::sub));
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::sltu, x_register::zero, lhs_register));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::less:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::sltu, x_register::a0, x_register::t0));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::greater_equal:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::sltu, x_register::t0, x_register::a0));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::greater:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::slt, x_register::a0, x_register::t0));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(lhs_register, funct3_t::xori, lhs_register, 1));
|
|
||||||
break;
|
|
||||||
case source::binary_operator::less_equal:
|
|
||||||
instructions.push_back(instruction(base_opcode::op)
|
|
||||||
.r(lhs_register, funct3_t::slt, x_register::t0, x_register::a0));
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(lhs_register, funct3_t::xori, lhs_register, 1));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
store_from_register(destination, lhs_register, procedure_info, instructions);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<reference> generate(source::intermediate_code_generator generator,
|
|
||||||
std::shared_ptr<source::symbol_table> table, std::shared_ptr<source::writer<std::byte>> writer)
|
|
||||||
{
|
|
||||||
std::vector<reference> references;
|
|
||||||
|
|
||||||
generate_intrinsics(writer, references);
|
|
||||||
|
|
||||||
for (auto& [identifier, intermediate_code] : generator)
|
|
||||||
{
|
|
||||||
std::vector<instruction> instructions;
|
|
||||||
auto main_symbol = std::dynamic_pointer_cast<source::procedure_info>(table->lookup(identifier));
|
|
||||||
std::size_t argument_offset{ 0 };
|
|
||||||
const auto stack_size = static_cast<std::uint32_t>(
|
|
||||||
intermediate_code.variable_counter() * 4 + 8 + main_symbol->stack_size());
|
|
||||||
std::unordered_map<std::size_t, std::function<void(std::size_t)>> missing_labels;
|
|
||||||
std::unordered_map<std::size_t, std::function<void(std::size_t)>>::iterator missing_label =
|
|
||||||
missing_labels.end();
|
|
||||||
std::unordered_map<std::size_t, std::size_t> local_labels;
|
|
||||||
|
|
||||||
for (auto& quadruple : intermediate_code)
|
|
||||||
{
|
|
||||||
switch (quadruple.operation())
|
|
||||||
{
|
|
||||||
case source::quadruple_operator::start:
|
|
||||||
prologue(instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::stop:
|
|
||||||
epilogue(stack_size, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::add:
|
|
||||||
perform_binary_operation(source::binary_operator::sum, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::sub:
|
|
||||||
perform_binary_operation(source::binary_operator::subtraction, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::mul:
|
|
||||||
perform_binary_operation(source::binary_operator::multiplication, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::div:
|
|
||||||
perform_binary_operation(source::binary_operator::division, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::eq:
|
|
||||||
perform_binary_operation(source::binary_operator::equals, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::neq:
|
|
||||||
perform_binary_operation(source::binary_operator::not_equals, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::lt:
|
|
||||||
perform_binary_operation(source::binary_operator::less, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::ge:
|
|
||||||
perform_binary_operation(source::binary_operator::greater_equal, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::gt:
|
|
||||||
perform_binary_operation(source::binary_operator::greater, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::le:
|
|
||||||
perform_binary_operation(source::binary_operator::less_equal, quadruple.operand1(),
|
|
||||||
quadruple.operand2(), quadruple.operand3(), main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::load:
|
|
||||||
{
|
|
||||||
auto operand_identifier =
|
|
||||||
std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name();
|
|
||||||
auto variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
|
|
||||||
main_symbol->scope()->lookup(operand_identifier));
|
|
||||||
|
|
||||||
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
|
|
||||||
instructions.push_back(instruction(base_opcode::load)
|
|
||||||
.i(x_register::a0, funct3_t::lw, x_register::a0, 0));
|
|
||||||
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::ref:
|
|
||||||
{
|
|
||||||
auto operand_identifier =
|
|
||||||
std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name();
|
|
||||||
auto variable_symbol = std::dynamic_pointer_cast<source::variable_info>(
|
|
||||||
main_symbol->scope()->lookup(operand_identifier));
|
|
||||||
|
|
||||||
instructions.push_back(instruction(base_opcode::opImm)
|
|
||||||
.i(x_register::a0, funct3_t::addi, x_register::s0, variable_symbol->offset));
|
|
||||||
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::beqz:
|
|
||||||
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
|
|
||||||
instructions.push_back(instruction(base_opcode::branch));
|
|
||||||
missing_labels.emplace(
|
|
||||||
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter(),
|
|
||||||
[before_branch = instructions.size() - 1, &instructions](std::size_t target) {
|
|
||||||
instructions[before_branch].b((target - before_branch) * 4,
|
|
||||||
funct3_t::beq, x_register::zero, x_register::a0);
|
|
||||||
});
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::j:
|
|
||||||
{
|
|
||||||
auto local_label = local_labels.find(
|
|
||||||
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter());
|
|
||||||
if (local_label != local_labels.end())
|
|
||||||
{
|
|
||||||
auto offset = -(instructions.size() - local_label->second) * 4;
|
|
||||||
instructions.push_back(instruction(base_opcode::auipc).u(x_register::a0, 0));
|
|
||||||
instructions.push_back(instruction(base_opcode::jalr)
|
|
||||||
.i(x_register::zero, funct3_t::jalr, x_register::a0, offset));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::label:
|
|
||||||
{
|
|
||||||
auto label_counter =
|
|
||||||
std::dynamic_pointer_cast<source::label_operand>(quadruple.operand3())->counter();
|
|
||||||
missing_label = missing_labels.find(label_counter);
|
|
||||||
|
|
||||||
if (missing_label != missing_labels.end())
|
|
||||||
{
|
|
||||||
missing_label->second(instructions.size());
|
|
||||||
}
|
|
||||||
local_labels[label_counter] = instructions.size();
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::assign:
|
|
||||||
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
|
|
||||||
store_from_register(quadruple.operand3(), x_register::a0, main_symbol, instructions);
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::param:
|
|
||||||
load_in_register(quadruple.operand1(), x_register::a0, main_symbol, instructions);
|
|
||||||
instructions.push_back(instruction(base_opcode::store)
|
|
||||||
.s(argument_offset, funct3_t::sw, x_register::sp, x_register::a0));
|
|
||||||
argument_offset += 4;
|
|
||||||
break;
|
|
||||||
case source::quadruple_operator::call:
|
|
||||||
relocate(std::dynamic_pointer_cast<source::variable_operand>(quadruple.operand1())->name(),
|
|
||||||
address_t::text, references, instructions, writer);
|
|
||||||
instructions.push_back(instruction(base_opcode::auipc).u(x_register::ra, 0));
|
|
||||||
instructions.push_back(instruction(base_opcode::jalr)
|
|
||||||
.i(x_register::ra, funct3_t::jalr, x_register::ra, 0));
|
|
||||||
argument_offset = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer->sink(identifier,
|
|
||||||
reinterpret_cast<const std::byte *>(instructions.data()),
|
|
||||||
instructions.size() * sizeof(instruction));
|
|
||||||
}
|
|
||||||
return references;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,132 +0,0 @@
|
|||||||
#include "elna/backend/target.hpp"
|
|
||||||
#include "elna/backend/riscv.hpp"
|
|
||||||
#include <cstring>
|
|
||||||
|
|
||||||
namespace elna::riscv
|
|
||||||
{
|
|
||||||
elfio_writer::elfio_writer(ELFIO::section *text,
|
|
||||||
ELFIO::symbol_section_accessor symbol_accessor, ELFIO::string_section_accessor string_accessor)
|
|
||||||
: text(text),
|
|
||||||
symbol_accessor(symbol_accessor), string_accessor(string_accessor)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t elfio_writer::sink(const std::string& label, const std::byte *data, std::size_t size)
|
|
||||||
{
|
|
||||||
auto offset = text->get_size();
|
|
||||||
text->append_data(reinterpret_cast<const char *>(data), size);
|
|
||||||
|
|
||||||
this->symbol_accessor.add_symbol(this->string_accessor, label.data(), offset, label.size(),
|
|
||||||
ELFIO::STB_GLOBAL, ELFIO::STT_FUNC, 0, text->get_index());
|
|
||||||
|
|
||||||
return text->get_size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void elfio_writer::sink(const std::string& label)
|
|
||||||
{
|
|
||||||
this->symbol_accessor.add_symbol(this->string_accessor, "printf", 0x00000000, 0,
|
|
||||||
ELFIO::STB_GLOBAL, ELFIO::STT_NOTYPE, 0, ELFIO::SHN_UNDEF);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t elfio_writer::size() const
|
|
||||||
{
|
|
||||||
return this->text->get_size();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label)
|
|
||||||
{
|
|
||||||
for (ptrdiff_t j = 0; j < symbol_accessor.get_symbols_num(); ++j)
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
ELFIO::Elf64_Addr value;
|
|
||||||
ELFIO::Elf_Xword size;
|
|
||||||
unsigned char bind;
|
|
||||||
unsigned char type;
|
|
||||||
ELFIO::Elf_Half section_index;
|
|
||||||
unsigned char other;
|
|
||||||
|
|
||||||
symbol_accessor.get_symbol(j, name, value, size, bind, type, section_index, other);
|
|
||||||
if (name == label)
|
|
||||||
{
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator,
|
|
||||||
std::shared_ptr<source::symbol_table> table, const std::filesystem::path& out_file)
|
|
||||||
{
|
|
||||||
ELFIO::elfio writer;
|
|
||||||
|
|
||||||
writer.create(ELFIO::ELFCLASS32, ELFIO::ELFDATA2LSB);
|
|
||||||
|
|
||||||
writer.set_os_abi(ELFIO::ELFOSABI_NONE);
|
|
||||||
writer.set_type(ELFIO::ET_REL);
|
|
||||||
writer.set_flags(0x4); // EF_RISCV_FLOAT_ABI_DOUBLE
|
|
||||||
writer.set_machine(ELFIO::EM_RISCV);
|
|
||||||
|
|
||||||
// Create code section
|
|
||||||
ELFIO::section* text_sec = writer.sections.add(".text");
|
|
||||||
text_sec->set_type(ELFIO::SHT_PROGBITS);
|
|
||||||
text_sec->set_flags(ELFIO::SHF_ALLOC | ELFIO::SHF_EXECINSTR);
|
|
||||||
text_sec->set_addr_align(0x1);
|
|
||||||
|
|
||||||
// Create string table section
|
|
||||||
ELFIO::section* str_sec = writer.sections.add(".strtab");
|
|
||||||
str_sec->set_type(ELFIO::SHT_STRTAB);
|
|
||||||
|
|
||||||
// Create string table writer
|
|
||||||
ELFIO::string_section_accessor stra(str_sec);
|
|
||||||
|
|
||||||
// Create symbol table section
|
|
||||||
ELFIO::section* sym_sec = writer.sections.add(".symtab");
|
|
||||||
sym_sec->set_type(ELFIO::SHT_SYMTAB);
|
|
||||||
sym_sec->set_info(2);
|
|
||||||
sym_sec->set_addr_align(0x4);
|
|
||||||
sym_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_SYMTAB));
|
|
||||||
sym_sec->set_link(str_sec->get_index());
|
|
||||||
|
|
||||||
// Create relocation table section
|
|
||||||
ELFIO::section* rel_sec = writer.sections.add(".rel.text");
|
|
||||||
rel_sec->set_type(ELFIO::SHT_REL);
|
|
||||||
rel_sec->set_info(text_sec->get_index());
|
|
||||||
rel_sec->set_addr_align(0x4);
|
|
||||||
rel_sec->set_entry_size(writer.get_default_entry_size(ELFIO::SHT_REL));
|
|
||||||
rel_sec->set_link(sym_sec->get_index());
|
|
||||||
rel_sec->set_flags(ELFIO::SHF_ALLOC);
|
|
||||||
|
|
||||||
// Create symbol relocation table writers
|
|
||||||
ELFIO::symbol_section_accessor syma(writer, sym_sec);
|
|
||||||
ELFIO::relocation_section_accessor rela(writer, rel_sec);
|
|
||||||
|
|
||||||
auto _writer = std::make_shared<elfio_writer>(text_sec, syma, stra);
|
|
||||||
auto references = generate(intermediate_code_generator, table, _writer);
|
|
||||||
|
|
||||||
syma.arrange_local_symbols();
|
|
||||||
|
|
||||||
for (auto& reference : references)
|
|
||||||
{
|
|
||||||
ELFIO::Elf_Word relocated_symbol = lookup(syma, reference.name);
|
|
||||||
|
|
||||||
switch (reference.target)
|
|
||||||
{
|
|
||||||
case address_t::high20:
|
|
||||||
rela.add_entry(reference.offset, relocated_symbol, 26 /* ELFIO::R_RISCV_HI20 */);
|
|
||||||
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
|
|
||||||
break;
|
|
||||||
case address_t::lower12i:
|
|
||||||
rela.add_entry(reference.offset, relocated_symbol, 27 /* ELFIO::R_RISCV_LO12_I */);
|
|
||||||
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
|
|
||||||
break;
|
|
||||||
case address_t::text:
|
|
||||||
rela.add_entry(reference.offset, relocated_symbol, 18 /* ELFIO::R_RISCV_CALL */);
|
|
||||||
// rela.add_entry(reference.offset, relocated_symbol, 51 /* ELFIO::R_RISCV_RELAX */);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create ELF object file
|
|
||||||
writer.save(out_file);
|
|
||||||
}
|
|
||||||
}
|
|
78
cli/cl.cpp
78
cli/cl.cpp
@ -1,78 +0,0 @@
|
|||||||
#include "elna/cli/cl.hpp"
|
|
||||||
#include "elna/backend/target.hpp"
|
|
||||||
#include "elna/source/semantic.hpp"
|
|
||||||
#include "elna/source/optimizer.hpp"
|
|
||||||
#include "elna/source/types.hpp"
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace elna::cli
|
|
||||||
{
|
|
||||||
void print_error(const std::unique_ptr<source::error>& compile_error)
|
|
||||||
{
|
|
||||||
std::cerr << compile_error->path().string() << ":"
|
|
||||||
<< compile_error->line() << ':' << compile_error->column()
|
|
||||||
<< ": " << compile_error->what() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr std::size_t pointer_size = 4;
|
|
||||||
|
|
||||||
static std::shared_ptr<source::symbol_table> add_builtin_symbols()
|
|
||||||
{
|
|
||||||
source::symbol_table result;
|
|
||||||
std::vector<std::shared_ptr<const source::type>> intrinsic_arguments;
|
|
||||||
|
|
||||||
auto boolean_info = std::make_shared<source::type_info>(source::boolean_type);
|
|
||||||
auto int_info = std::make_shared<source::type_info>(source::int_type);
|
|
||||||
result.enter("Boolean", boolean_info);
|
|
||||||
result.enter("Int", int_info);
|
|
||||||
|
|
||||||
intrinsic_arguments.push_back(int_info->type());
|
|
||||||
auto writei = std::make_shared<source::intrinsic_info>(
|
|
||||||
source::procedure_type{ intrinsic_arguments, pointer_size });
|
|
||||||
result.enter("writei", writei);
|
|
||||||
intrinsic_arguments.clear();
|
|
||||||
|
|
||||||
intrinsic_arguments.push_back(boolean_info->type());
|
|
||||||
auto writeb = std::make_shared<source::intrinsic_info>(
|
|
||||||
source::procedure_type{ intrinsic_arguments, pointer_size });
|
|
||||||
result.enter("writeb", writeb);
|
|
||||||
intrinsic_arguments.clear();
|
|
||||||
|
|
||||||
return std::make_shared<source::symbol_table>(std::move(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
source::result<source::lexer> lex_result = source::tokenize(in_file);
|
|
||||||
|
|
||||||
if (lex_result.has_errors())
|
|
||||||
{
|
|
||||||
print_errors(lex_result.errors().cbegin(), lex_result.errors().cend());
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
source::parser parser{ std::move(lex_result.success()) };
|
|
||||||
auto ast = parser.parse();
|
|
||||||
if (ast == nullptr)
|
|
||||||
{
|
|
||||||
print_errors(parser.errors().cbegin(), parser.errors().cend());
|
|
||||||
return 2;
|
|
||||||
}
|
|
||||||
auto global_scope = add_builtin_symbols();
|
|
||||||
source::name_analysis_visitor(global_scope, in_file, pointer_size).visit(ast.get());
|
|
||||||
source::type_analysis_visitor(global_scope, in_file, pointer_size).visit(ast.get());
|
|
||||||
source::allocator_visitor(global_scope).visit(ast.get());
|
|
||||||
|
|
||||||
source::intermediate_code_generator intermediate_code_generator{ global_scope };
|
|
||||||
intermediate_code_generator.visit(ast.get());
|
|
||||||
|
|
||||||
riscv::riscv32_elf(ast.get(), intermediate_code_generator, global_scope, out_file);
|
|
||||||
}
|
|
||||||
catch (std::ios_base::failure&)
|
|
||||||
{
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
47
cli/main.cpp
47
cli/main.cpp
@ -1,47 +0,0 @@
|
|||||||
#include "elna/cli/cl.hpp"
|
|
||||||
|
|
||||||
#include <filesystem>
|
|
||||||
#include <iostream>
|
|
||||||
#include <boost/program_options.hpp>
|
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
|
||||||
{
|
|
||||||
boost::program_options::options_description visible{ "Allowed options" };
|
|
||||||
boost::program_options::options_description description;
|
|
||||||
boost::program_options::positional_options_description positional;
|
|
||||||
boost::program_options::variables_map variables_map;
|
|
||||||
|
|
||||||
visible.add_options()
|
|
||||||
("help", "Print this help message")
|
|
||||||
("output,o", boost::program_options::value<std::filesystem::path>(), "Output object file")
|
|
||||||
;
|
|
||||||
description.add_options()
|
|
||||||
("input", boost::program_options::value<std::filesystem::path>()->required())
|
|
||||||
;
|
|
||||||
description.add(visible);
|
|
||||||
positional.add("input", 1);
|
|
||||||
|
|
||||||
auto parsed = boost::program_options::command_line_parser(argc, argv)
|
|
||||||
.options(description).positional(positional)
|
|
||||||
.run();
|
|
||||||
boost::program_options::store(parsed, variables_map);
|
|
||||||
boost::program_options::notify(variables_map);
|
|
||||||
|
|
||||||
if (variables_map.count("help"))
|
|
||||||
{
|
|
||||||
std::cout << description << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
const auto in_file = variables_map["input"].as<std::filesystem::path>();
|
|
||||||
std::filesystem::path out_file;
|
|
||||||
|
|
||||||
if (variables_map.count("output"))
|
|
||||||
{
|
|
||||||
out_file = variables_map["output"].as<std::filesystem::path>();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
out_file = in_file.filename().replace_extension(".o");
|
|
||||||
}
|
|
||||||
return elna::cli::compile(in_file, out_file);
|
|
||||||
}
|
|
87
elna.cabal
Normal file
87
elna.cabal
Normal file
@ -0,0 +1,87 @@
|
|||||||
|
cabal-version: 3.4
|
||||||
|
name: elna
|
||||||
|
version: 0.1.0.0
|
||||||
|
synopsis:
|
||||||
|
Elna programming language compiles simple mathematical operations to RISC-V code
|
||||||
|
|
||||||
|
license: MPL-2.0
|
||||||
|
license-file: LICENSE
|
||||||
|
author: Eugen Wissner
|
||||||
|
maintainer: belka@caraus.de
|
||||||
|
category: Language
|
||||||
|
build-type: Simple
|
||||||
|
extra-doc-files: TODO README
|
||||||
|
|
||||||
|
common warnings
|
||||||
|
build-depends:
|
||||||
|
base >=4.7 && <5,
|
||||||
|
bytestring ^>= 0.12.1,
|
||||||
|
megaparsec ^>= 9.6,
|
||||||
|
optparse-applicative ^>= 0.18.1,
|
||||||
|
vector ^>= 0.13.1,
|
||||||
|
text ^>= 2.0
|
||||||
|
ghc-options: -Wall
|
||||||
|
default-extensions:
|
||||||
|
DataKinds,
|
||||||
|
ExplicitForAll,
|
||||||
|
LambdaCase,
|
||||||
|
OverloadedStrings,
|
||||||
|
DuplicateRecordFields,
|
||||||
|
RecordWildCards
|
||||||
|
default-language: GHC2021
|
||||||
|
|
||||||
|
library elna-internal
|
||||||
|
import: warnings
|
||||||
|
exposed-modules:
|
||||||
|
Language.Elna.Architecture.RiscV
|
||||||
|
Language.Elna.Backend.Allocator
|
||||||
|
Language.Elna.Backend.Intermediate
|
||||||
|
Language.Elna.CommandLine
|
||||||
|
Language.Elna.Frontend.AST
|
||||||
|
Language.Elna.Frontend.NameAnalysis
|
||||||
|
Language.Elna.Frontend.Parser
|
||||||
|
Language.Elna.Frontend.SymbolTable
|
||||||
|
Language.Elna.Frontend.TypeAnalysis
|
||||||
|
Language.Elna.Frontend.Types
|
||||||
|
Language.Elna.Glue
|
||||||
|
Language.Elna.Location
|
||||||
|
Language.Elna.Object.Elf
|
||||||
|
Language.Elna.Object.ElfCoder
|
||||||
|
Language.Elna.Object.StringTable
|
||||||
|
Language.Elna.RiscV.CodeGenerator
|
||||||
|
Language.Elna.RiscV.ElfWriter
|
||||||
|
build-depends:
|
||||||
|
exceptions ^>= 0.10,
|
||||||
|
hashable ^>= 1.4.3,
|
||||||
|
parser-combinators ^>= 1.3,
|
||||||
|
transformers ^>= 0.6.1,
|
||||||
|
unordered-containers ^>= 0.2.20
|
||||||
|
hs-source-dirs: lib
|
||||||
|
|
||||||
|
executable elna
|
||||||
|
import: warnings
|
||||||
|
main-is: Main.hs
|
||||||
|
build-depends:
|
||||||
|
elna:elna-internal,
|
||||||
|
filepath ^>= 1.5.3
|
||||||
|
hs-source-dirs: src
|
||||||
|
|
||||||
|
test-suite elna-test
|
||||||
|
import: warnings
|
||||||
|
type: exitcode-stdio-1.0
|
||||||
|
main-is: Spec.hs
|
||||||
|
other-modules:
|
||||||
|
Language.Elna.NameAnalysisSpec
|
||||||
|
Language.Elna.ParserSpec
|
||||||
|
hs-source-dirs:
|
||||||
|
tests
|
||||||
|
ghc-options: -threaded -rtsopts -with-rtsopts=-N -Wall
|
||||||
|
|
||||||
|
build-depends:
|
||||||
|
elna:elna-internal,
|
||||||
|
hspec >= 2.10.9 && < 2.12,
|
||||||
|
hspec-expectations ^>= 0.8.2,
|
||||||
|
hspec-megaparsec ^>= 2.2.0,
|
||||||
|
text
|
||||||
|
build-tool-depends:
|
||||||
|
hspec-discover:hspec-discover
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_ARRAY_HPP
|
|
||||||
#define ELFIO_ARRAY_HPP
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S, typename T> class array_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
explicit array_section_accessor_template( const elfio& elf_file,
|
|
||||||
S* section )
|
|
||||||
: elf_file( elf_file ), array_section( section )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Xword get_entries_num() const
|
|
||||||
{
|
|
||||||
Elf_Xword entry_size = sizeof( T );
|
|
||||||
return array_section->get_size() / entry_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_entry( Elf_Xword index, Elf64_Addr& address ) const
|
|
||||||
{
|
|
||||||
if ( index >= get_entries_num() ) { // Is index valid
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
const T temp = *reinterpret_cast<const T*>( array_section->get_data() +
|
|
||||||
index * sizeof( T ) );
|
|
||||||
address = convertor( temp );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_entry( Elf64_Addr address )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
T temp = convertor( (T)address );
|
|
||||||
array_section->append_data( reinterpret_cast<char*>( &temp ),
|
|
||||||
sizeof( temp ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
const elfio& elf_file;
|
|
||||||
S* array_section;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T = Elf32_Word>
|
|
||||||
using array_section_accessor = array_section_accessor_template<section, T>;
|
|
||||||
template <typename T = Elf32_Word>
|
|
||||||
using const_array_section_accessor =
|
|
||||||
array_section_accessor_template<const section, T>;
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_ARRAY_HPP
|
|
File diff suppressed because it is too large
Load Diff
@ -1,274 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_DYNAMIC_HPP
|
|
||||||
#define ELFIO_DYNAMIC_HPP
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S> class dynamic_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
explicit dynamic_section_accessor_template( const elfio& elf_file,
|
|
||||||
S* section )
|
|
||||||
: elf_file( elf_file ), dynamic_section( section ), entries_num( 0 )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Xword get_entries_num() const
|
|
||||||
{
|
|
||||||
size_t needed_entry_size = -1;
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
needed_entry_size = sizeof( Elf32_Dyn );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
needed_entry_size = sizeof( Elf64_Dyn );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( ( 0 == entries_num ) &&
|
|
||||||
( 0 != dynamic_section->get_entry_size() &&
|
|
||||||
dynamic_section->get_entry_size() >= needed_entry_size ) ) {
|
|
||||||
entries_num =
|
|
||||||
dynamic_section->get_size() / dynamic_section->get_entry_size();
|
|
||||||
Elf_Xword i;
|
|
||||||
Elf_Xword tag = DT_NULL;
|
|
||||||
Elf_Xword value = 0;
|
|
||||||
std::string str;
|
|
||||||
for ( i = 0; i < entries_num; i++ ) {
|
|
||||||
get_entry( i, tag, value, str );
|
|
||||||
if ( tag == DT_NULL )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
entries_num = std::min<Elf_Xword>( entries_num, i + 1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
return entries_num;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_entry( Elf_Xword index,
|
|
||||||
Elf_Xword& tag,
|
|
||||||
Elf_Xword& value,
|
|
||||||
std::string& str ) const
|
|
||||||
{
|
|
||||||
if ( index >= get_entries_num() ) { // Is index valid
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
generic_get_entry_dyn<Elf32_Dyn>( index, tag, value );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
generic_get_entry_dyn<Elf64_Dyn>( index, tag, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the tag has a string table reference - prepare the string
|
|
||||||
if ( tag == DT_NEEDED || tag == DT_SONAME || tag == DT_RPATH ||
|
|
||||||
tag == DT_RUNPATH ) {
|
|
||||||
string_section_accessor strsec(
|
|
||||||
elf_file.sections[get_string_table_index()] );
|
|
||||||
const char* result = strsec.get_string( (Elf_Word)value );
|
|
||||||
if ( nullptr == result ) {
|
|
||||||
str.clear();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
str = result;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
str.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_entry( Elf_Xword tag, Elf_Xword value )
|
|
||||||
{
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
generic_add_entry_dyn<Elf32_Dyn>( tag, value );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
generic_add_entry_dyn<Elf64_Dyn>( tag, value );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_entry( Elf_Xword tag, const std::string& str )
|
|
||||||
{
|
|
||||||
string_section_accessor strsec(
|
|
||||||
elf_file.sections[get_string_table_index()] );
|
|
||||||
Elf_Xword value = strsec.add_string( str );
|
|
||||||
add_entry( tag, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half get_string_table_index() const
|
|
||||||
{
|
|
||||||
return (Elf_Half)dynamic_section->get_link();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
void generic_get_entry_dyn( Elf_Xword index,
|
|
||||||
Elf_Xword& tag,
|
|
||||||
Elf_Xword& value ) const
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
// Check unusual case when dynamic section has no data
|
|
||||||
if ( dynamic_section->get_data() == nullptr ||
|
|
||||||
( index + 1 ) * dynamic_section->get_entry_size() >
|
|
||||||
dynamic_section->get_size() ||
|
|
||||||
dynamic_section->get_entry_size() < sizeof( T ) ) {
|
|
||||||
tag = DT_NULL;
|
|
||||||
value = 0;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const T* pEntry = reinterpret_cast<const T*>(
|
|
||||||
dynamic_section->get_data() +
|
|
||||||
index * dynamic_section->get_entry_size() );
|
|
||||||
tag = convertor( pEntry->d_tag );
|
|
||||||
switch ( tag ) {
|
|
||||||
case DT_NULL:
|
|
||||||
case DT_SYMBOLIC:
|
|
||||||
case DT_TEXTREL:
|
|
||||||
case DT_BIND_NOW:
|
|
||||||
value = 0;
|
|
||||||
break;
|
|
||||||
case DT_NEEDED:
|
|
||||||
case DT_PLTRELSZ:
|
|
||||||
case DT_RELASZ:
|
|
||||||
case DT_RELAENT:
|
|
||||||
case DT_STRSZ:
|
|
||||||
case DT_SYMENT:
|
|
||||||
case DT_SONAME:
|
|
||||||
case DT_RPATH:
|
|
||||||
case DT_RELSZ:
|
|
||||||
case DT_RELENT:
|
|
||||||
case DT_PLTREL:
|
|
||||||
case DT_INIT_ARRAYSZ:
|
|
||||||
case DT_FINI_ARRAYSZ:
|
|
||||||
case DT_RUNPATH:
|
|
||||||
case DT_FLAGS:
|
|
||||||
case DT_PREINIT_ARRAYSZ:
|
|
||||||
value = convertor( pEntry->d_un.d_val );
|
|
||||||
break;
|
|
||||||
case DT_PLTGOT:
|
|
||||||
case DT_HASH:
|
|
||||||
case DT_STRTAB:
|
|
||||||
case DT_SYMTAB:
|
|
||||||
case DT_RELA:
|
|
||||||
case DT_INIT:
|
|
||||||
case DT_FINI:
|
|
||||||
case DT_REL:
|
|
||||||
case DT_DEBUG:
|
|
||||||
case DT_JMPREL:
|
|
||||||
case DT_INIT_ARRAY:
|
|
||||||
case DT_FINI_ARRAY:
|
|
||||||
case DT_PREINIT_ARRAY:
|
|
||||||
default:
|
|
||||||
value = convertor( pEntry->d_un.d_ptr );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
void generic_add_entry_dyn( Elf_Xword tag, Elf_Xword value )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
T entry;
|
|
||||||
|
|
||||||
switch ( tag ) {
|
|
||||||
case DT_NULL:
|
|
||||||
case DT_SYMBOLIC:
|
|
||||||
case DT_TEXTREL:
|
|
||||||
case DT_BIND_NOW:
|
|
||||||
entry.d_un.d_val = convertor( decltype( entry.d_un.d_val )( 0 ) );
|
|
||||||
break;
|
|
||||||
case DT_NEEDED:
|
|
||||||
case DT_PLTRELSZ:
|
|
||||||
case DT_RELASZ:
|
|
||||||
case DT_RELAENT:
|
|
||||||
case DT_STRSZ:
|
|
||||||
case DT_SYMENT:
|
|
||||||
case DT_SONAME:
|
|
||||||
case DT_RPATH:
|
|
||||||
case DT_RELSZ:
|
|
||||||
case DT_RELENT:
|
|
||||||
case DT_PLTREL:
|
|
||||||
case DT_INIT_ARRAYSZ:
|
|
||||||
case DT_FINI_ARRAYSZ:
|
|
||||||
case DT_RUNPATH:
|
|
||||||
case DT_FLAGS:
|
|
||||||
case DT_PREINIT_ARRAYSZ:
|
|
||||||
entry.d_un.d_val =
|
|
||||||
convertor( decltype( entry.d_un.d_val )( value ) );
|
|
||||||
break;
|
|
||||||
case DT_PLTGOT:
|
|
||||||
case DT_HASH:
|
|
||||||
case DT_STRTAB:
|
|
||||||
case DT_SYMTAB:
|
|
||||||
case DT_RELA:
|
|
||||||
case DT_INIT:
|
|
||||||
case DT_FINI:
|
|
||||||
case DT_REL:
|
|
||||||
case DT_DEBUG:
|
|
||||||
case DT_JMPREL:
|
|
||||||
case DT_INIT_ARRAY:
|
|
||||||
case DT_FINI_ARRAY:
|
|
||||||
case DT_PREINIT_ARRAY:
|
|
||||||
default:
|
|
||||||
entry.d_un.d_ptr =
|
|
||||||
convertor( decltype( entry.d_un.d_val )( value ) );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
entry.d_tag = convertor( decltype( entry.d_tag )( tag ) );
|
|
||||||
|
|
||||||
dynamic_section->append_data( reinterpret_cast<char*>( &entry ),
|
|
||||||
sizeof( entry ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
const elfio& elf_file;
|
|
||||||
S* dynamic_section;
|
|
||||||
mutable Elf_Xword entries_num;
|
|
||||||
};
|
|
||||||
|
|
||||||
using dynamic_section_accessor = dynamic_section_accessor_template<section>;
|
|
||||||
using const_dynamic_section_accessor =
|
|
||||||
dynamic_section_accessor_template<const section>;
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_DYNAMIC_HPP
|
|
@ -1,153 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELF_HEADER_HPP
|
|
||||||
#define ELF_HEADER_HPP
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
class elf_header
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~elf_header() = default;
|
|
||||||
|
|
||||||
virtual bool load( std::istream& stream ) = 0;
|
|
||||||
virtual bool save( std::ostream& stream ) const = 0;
|
|
||||||
|
|
||||||
// ELF header functions
|
|
||||||
ELFIO_GET_ACCESS_DECL( unsigned char, class );
|
|
||||||
ELFIO_GET_ACCESS_DECL( unsigned char, elf_version );
|
|
||||||
ELFIO_GET_ACCESS_DECL( unsigned char, encoding );
|
|
||||||
ELFIO_GET_ACCESS_DECL( Elf_Half, header_size );
|
|
||||||
ELFIO_GET_ACCESS_DECL( Elf_Half, section_entry_size );
|
|
||||||
ELFIO_GET_ACCESS_DECL( Elf_Half, segment_entry_size );
|
|
||||||
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, version );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( unsigned char, os_abi );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( unsigned char, abi_version );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, type );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, machine );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, entry );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, sections_num );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, sections_offset );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, segments_num );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Off, segments_offset );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Half, section_name_str_index );
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T> struct elf_header_impl_types;
|
|
||||||
template <> struct elf_header_impl_types<Elf32_Ehdr>
|
|
||||||
{
|
|
||||||
using Phdr_type = Elf32_Phdr;
|
|
||||||
using Shdr_type = Elf32_Shdr;
|
|
||||||
static const unsigned char file_class = ELFCLASS32;
|
|
||||||
};
|
|
||||||
template <> struct elf_header_impl_types<Elf64_Ehdr>
|
|
||||||
{
|
|
||||||
using Phdr_type = Elf64_Phdr;
|
|
||||||
using Shdr_type = Elf64_Shdr;
|
|
||||||
static const unsigned char file_class = ELFCLASS64;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T> class elf_header_impl : public elf_header
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
elf_header_impl( endianess_convertor* convertor,
|
|
||||||
unsigned char encoding,
|
|
||||||
const address_translator* translator )
|
|
||||||
: convertor( convertor ), translator( translator )
|
|
||||||
{
|
|
||||||
header.e_ident[EI_MAG0] = ELFMAG0;
|
|
||||||
header.e_ident[EI_MAG1] = ELFMAG1;
|
|
||||||
header.e_ident[EI_MAG2] = ELFMAG2;
|
|
||||||
header.e_ident[EI_MAG3] = ELFMAG3;
|
|
||||||
header.e_ident[EI_CLASS] = elf_header_impl_types<T>::file_class;
|
|
||||||
header.e_ident[EI_DATA] = encoding;
|
|
||||||
header.e_ident[EI_VERSION] = EV_CURRENT;
|
|
||||||
header.e_version = ( *convertor )( (Elf_Word)EV_CURRENT );
|
|
||||||
header.e_ehsize = ( sizeof( header ) );
|
|
||||||
header.e_ehsize = ( *convertor )( header.e_ehsize );
|
|
||||||
header.e_shstrndx = ( *convertor )( (Elf_Half)1 );
|
|
||||||
header.e_phentsize =
|
|
||||||
sizeof( typename elf_header_impl_types<T>::Phdr_type );
|
|
||||||
header.e_shentsize =
|
|
||||||
sizeof( typename elf_header_impl_types<T>::Shdr_type );
|
|
||||||
header.e_phentsize = ( *convertor )( header.e_phentsize );
|
|
||||||
header.e_shentsize = ( *convertor )( header.e_shentsize );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool load( std::istream& stream ) override
|
|
||||||
{
|
|
||||||
stream.seekg( ( *translator )[0] );
|
|
||||||
stream.read( reinterpret_cast<char*>( &header ), sizeof( header ) );
|
|
||||||
|
|
||||||
return ( stream.gcount() == sizeof( header ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool save( std::ostream& stream ) const override
|
|
||||||
{
|
|
||||||
stream.seekp( ( *translator )[0] );
|
|
||||||
stream.write( reinterpret_cast<const char*>( &header ),
|
|
||||||
sizeof( header ) );
|
|
||||||
|
|
||||||
return stream.good();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// ELF header functions
|
|
||||||
ELFIO_GET_ACCESS( unsigned char, class, header.e_ident[EI_CLASS] );
|
|
||||||
ELFIO_GET_ACCESS( unsigned char, elf_version, header.e_ident[EI_VERSION] );
|
|
||||||
ELFIO_GET_ACCESS( unsigned char, encoding, header.e_ident[EI_DATA] );
|
|
||||||
ELFIO_GET_ACCESS( Elf_Half, header_size, header.e_ehsize );
|
|
||||||
ELFIO_GET_ACCESS( Elf_Half, section_entry_size, header.e_shentsize );
|
|
||||||
ELFIO_GET_ACCESS( Elf_Half, segment_entry_size, header.e_phentsize );
|
|
||||||
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Word, version, header.e_version );
|
|
||||||
ELFIO_GET_SET_ACCESS( unsigned char, os_abi, header.e_ident[EI_OSABI] );
|
|
||||||
ELFIO_GET_SET_ACCESS( unsigned char,
|
|
||||||
abi_version,
|
|
||||||
header.e_ident[EI_ABIVERSION] );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Half, type, header.e_type );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Half, machine, header.e_machine );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Word, flags, header.e_flags );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Half, section_name_str_index, header.e_shstrndx );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf64_Addr, entry, header.e_entry );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Half, sections_num, header.e_shnum );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf64_Off, sections_offset, header.e_shoff );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Half, segments_num, header.e_phnum );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf64_Off, segments_offset, header.e_phoff );
|
|
||||||
|
|
||||||
private:
|
|
||||||
T header = {};
|
|
||||||
endianess_convertor* convertor = nullptr;
|
|
||||||
const address_translator* translator = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELF_HEADER_HPP
|
|
@ -1,124 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_MODINFO_HPP
|
|
||||||
#define ELFIO_MODINFO_HPP
|
|
||||||
|
|
||||||
#include <string_view>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S> class modinfo_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
explicit modinfo_section_accessor_template( S* section )
|
|
||||||
: modinfo_section( section )
|
|
||||||
{
|
|
||||||
process_section();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word get_attribute_num() const { return (Elf_Word)content.size(); }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool
|
|
||||||
get_attribute( Elf_Word no, std::string& field, std::string& value ) const
|
|
||||||
{
|
|
||||||
if ( no < content.size() ) {
|
|
||||||
field = content[no].first;
|
|
||||||
value = content[no].second;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_attribute( const std::string_view& field_name,
|
|
||||||
std::string& value ) const
|
|
||||||
{
|
|
||||||
for ( const auto [first, second] : content ) {
|
|
||||||
if ( field_name == first ) {
|
|
||||||
value = second;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word add_attribute( const std::string& field, const std::string& value )
|
|
||||||
{
|
|
||||||
Elf_Word current_position = 0;
|
|
||||||
|
|
||||||
if ( modinfo_section ) {
|
|
||||||
// Strings are addeded to the end of the current section data
|
|
||||||
current_position = (Elf_Word)modinfo_section->get_size();
|
|
||||||
|
|
||||||
std::string attribute = field + "=" + value;
|
|
||||||
|
|
||||||
modinfo_section->append_data( attribute + '\0' );
|
|
||||||
content.emplace_back( field, value );
|
|
||||||
}
|
|
||||||
|
|
||||||
return current_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
void process_section()
|
|
||||||
{
|
|
||||||
const char* pdata = modinfo_section->get_data();
|
|
||||||
if ( pdata ) {
|
|
||||||
ELFIO::Elf_Xword i = 0;
|
|
||||||
while ( i < modinfo_section->get_size() ) {
|
|
||||||
while ( i < modinfo_section->get_size() && !pdata[i] )
|
|
||||||
i++;
|
|
||||||
if ( i < modinfo_section->get_size() ) {
|
|
||||||
std::string info = pdata + i;
|
|
||||||
size_t loc = info.find( '=' );
|
|
||||||
content.emplace_back( info.substr( 0, loc ),
|
|
||||||
info.substr( loc + 1 ) );
|
|
||||||
|
|
||||||
i += info.length();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
S* modinfo_section;
|
|
||||||
std::vector<std::pair<std::string, std::string>> content;
|
|
||||||
};
|
|
||||||
|
|
||||||
using modinfo_section_accessor = modinfo_section_accessor_template<section>;
|
|
||||||
using const_modinfo_section_accessor =
|
|
||||||
modinfo_section_accessor_template<const section>;
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_MODINFO_HPP
|
|
@ -1,184 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_NOTE_HPP
|
|
||||||
#define ELFIO_NOTE_HPP
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// There are discrepancies in documentations. SCO documentation
|
|
||||||
// (http://www.sco.com/developers/gabi/latest/ch5.pheader.html#note_section)
|
|
||||||
// requires 8 byte entries alignment for 64-bit ELF file,
|
|
||||||
// but Oracle's definition uses the same structure
|
|
||||||
// for 32-bit and 64-bit formats.
|
|
||||||
// (https://docs.oracle.com/cd/E23824_01/html/819-0690/chapter6-18048.html)
|
|
||||||
//
|
|
||||||
// It looks like EM_X86_64 Linux implementation is similar to Oracle's
|
|
||||||
// definition. Therefore, the same alignment works for both formats
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S, Elf_Xword ( S::*F_get_size )() const>
|
|
||||||
class note_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
explicit note_section_accessor_template( const elfio& elf_file, S* section )
|
|
||||||
: elf_file( elf_file ), notes( section )
|
|
||||||
{
|
|
||||||
process_section();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word get_notes_num() const
|
|
||||||
{
|
|
||||||
return (Elf_Word)note_start_positions.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_note( Elf_Word index,
|
|
||||||
Elf_Word& type,
|
|
||||||
std::string& name,
|
|
||||||
char*& desc,
|
|
||||||
Elf_Word& descSize ) const
|
|
||||||
{
|
|
||||||
if ( index >= ( notes->*F_get_size )() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* pData = notes->get_data() + note_start_positions[index];
|
|
||||||
int align = sizeof( Elf_Word );
|
|
||||||
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
type = convertor( *(const Elf_Word*)( pData + 2 * (size_t)align ) );
|
|
||||||
Elf_Word namesz = convertor( *(const Elf_Word*)( pData ) );
|
|
||||||
descSize = convertor( *(const Elf_Word*)( pData + sizeof( namesz ) ) );
|
|
||||||
|
|
||||||
Elf_Xword max_name_size =
|
|
||||||
( notes->*F_get_size )() - note_start_positions[index];
|
|
||||||
if ( namesz < 1 || namesz > max_name_size ||
|
|
||||||
(Elf_Xword)namesz + descSize > max_name_size ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
name.assign( pData + 3 * (size_t)align, namesz - 1 );
|
|
||||||
if ( 0 == descSize ) {
|
|
||||||
desc = nullptr;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
desc = const_cast<char*>( pData + 3 * (size_t)align +
|
|
||||||
( ( namesz + align - 1 ) / align ) *
|
|
||||||
(size_t)align );
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_note( Elf_Word type,
|
|
||||||
const std::string& name,
|
|
||||||
const char* desc,
|
|
||||||
Elf_Word descSize )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
int align = sizeof( Elf_Word );
|
|
||||||
Elf_Word nameLen = (Elf_Word)name.size() + 1;
|
|
||||||
Elf_Word nameLenConv = convertor( nameLen );
|
|
||||||
std::string buffer( reinterpret_cast<char*>( &nameLenConv ), align );
|
|
||||||
Elf_Word descSizeConv = convertor( descSize );
|
|
||||||
|
|
||||||
buffer.append( reinterpret_cast<char*>( &descSizeConv ), align );
|
|
||||||
type = convertor( type );
|
|
||||||
buffer.append( reinterpret_cast<char*>( &type ), align );
|
|
||||||
buffer.append( name );
|
|
||||||
buffer.append( 1, '\x00' );
|
|
||||||
const char pad[] = { '\0', '\0', '\0', '\0' };
|
|
||||||
if ( nameLen % align != 0 ) {
|
|
||||||
buffer.append( pad, (size_t)align - nameLen % align );
|
|
||||||
}
|
|
||||||
if ( desc != nullptr && descSize != 0 ) {
|
|
||||||
buffer.append( desc, descSize );
|
|
||||||
if ( descSize % align != 0 ) {
|
|
||||||
buffer.append( pad, (size_t)align - descSize % align );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
note_start_positions.emplace_back( ( notes->*F_get_size )() );
|
|
||||||
notes->append_data( buffer );
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void process_section()
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
const char* data = notes->get_data();
|
|
||||||
Elf_Xword size = ( notes->*F_get_size )();
|
|
||||||
Elf_Xword current = 0;
|
|
||||||
|
|
||||||
note_start_positions.clear();
|
|
||||||
|
|
||||||
// Is it empty?
|
|
||||||
if ( nullptr == data || 0 == size ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Elf_Word align = sizeof( Elf_Word );
|
|
||||||
while ( current + (Elf_Xword)3 * align <= size ) {
|
|
||||||
Elf_Word namesz = convertor( *(const Elf_Word*)( data + current ) );
|
|
||||||
Elf_Word descsz = convertor(
|
|
||||||
*(const Elf_Word*)( data + current + sizeof( namesz ) ) );
|
|
||||||
Elf_Word advance =
|
|
||||||
(Elf_Xword)3 * sizeof( Elf_Word ) +
|
|
||||||
( ( namesz + align - 1 ) / align ) * (Elf_Xword)align +
|
|
||||||
( ( descsz + align - 1 ) / align ) * (Elf_Xword)align;
|
|
||||||
if ( namesz < size && descsz < size && current + advance <= size ) {
|
|
||||||
note_start_positions.emplace_back( current );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
current += advance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
const elfio& elf_file;
|
|
||||||
S* notes;
|
|
||||||
std::vector<Elf_Xword> note_start_positions;
|
|
||||||
};
|
|
||||||
|
|
||||||
using note_section_accessor =
|
|
||||||
note_section_accessor_template<section, §ion::get_size>;
|
|
||||||
using const_note_section_accessor =
|
|
||||||
note_section_accessor_template<const section, §ion::get_size>;
|
|
||||||
using note_segment_accessor =
|
|
||||||
note_section_accessor_template<segment, &segment::get_file_size>;
|
|
||||||
using const_note_segment_accessor =
|
|
||||||
note_section_accessor_template<const segment, &segment::get_file_size>;
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_NOTE_HPP
|
|
@ -1,460 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_RELOCATION_HPP
|
|
||||||
#define ELFIO_RELOCATION_HPP
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
template <typename T> struct get_sym_and_type;
|
|
||||||
template <> struct get_sym_and_type<Elf32_Rel>
|
|
||||||
{
|
|
||||||
static int get_r_sym( Elf_Xword info )
|
|
||||||
{
|
|
||||||
return ELF32_R_SYM( (Elf_Word)info );
|
|
||||||
}
|
|
||||||
static int get_r_type( Elf_Xword info )
|
|
||||||
{
|
|
||||||
return ELF32_R_TYPE( (Elf_Word)info );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <> struct get_sym_and_type<Elf32_Rela>
|
|
||||||
{
|
|
||||||
static int get_r_sym( Elf_Xword info )
|
|
||||||
{
|
|
||||||
return ELF32_R_SYM( (Elf_Word)info );
|
|
||||||
}
|
|
||||||
static int get_r_type( Elf_Xword info )
|
|
||||||
{
|
|
||||||
return ELF32_R_TYPE( (Elf_Word)info );
|
|
||||||
}
|
|
||||||
};
|
|
||||||
template <> struct get_sym_and_type<Elf64_Rel>
|
|
||||||
{
|
|
||||||
static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); }
|
|
||||||
static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); }
|
|
||||||
};
|
|
||||||
template <> struct get_sym_and_type<Elf64_Rela>
|
|
||||||
{
|
|
||||||
static int get_r_sym( Elf_Xword info ) { return ELF64_R_SYM( info ); }
|
|
||||||
static int get_r_type( Elf_Xword info ) { return ELF64_R_TYPE( info ); }
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S> class relocation_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
explicit relocation_section_accessor_template( const elfio& elf_file,
|
|
||||||
S* section )
|
|
||||||
: elf_file( elf_file ), relocation_section( section )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Xword get_entries_num() const
|
|
||||||
{
|
|
||||||
Elf_Xword nRet = 0;
|
|
||||||
|
|
||||||
if ( 0 != relocation_section->get_entry_size() ) {
|
|
||||||
nRet = relocation_section->get_size() /
|
|
||||||
relocation_section->get_entry_size();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_entry( Elf_Xword index,
|
|
||||||
Elf64_Addr& offset,
|
|
||||||
Elf_Word& symbol,
|
|
||||||
unsigned& type,
|
|
||||||
Elf_Sxword& addend ) const
|
|
||||||
{
|
|
||||||
if ( index >= get_entries_num() ) { // Is index valid
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
if ( SHT_REL == relocation_section->get_type() ) {
|
|
||||||
generic_get_entry_rel<Elf32_Rel>( index, offset, symbol, type,
|
|
||||||
addend );
|
|
||||||
}
|
|
||||||
else if ( SHT_RELA == relocation_section->get_type() ) {
|
|
||||||
generic_get_entry_rela<Elf32_Rela>( index, offset, symbol, type,
|
|
||||||
addend );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( SHT_REL == relocation_section->get_type() ) {
|
|
||||||
generic_get_entry_rel<Elf64_Rel>( index, offset, symbol, type,
|
|
||||||
addend );
|
|
||||||
}
|
|
||||||
else if ( SHT_RELA == relocation_section->get_type() ) {
|
|
||||||
generic_get_entry_rela<Elf64_Rela>( index, offset, symbol, type,
|
|
||||||
addend );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_entry( Elf_Xword index,
|
|
||||||
Elf64_Addr& offset,
|
|
||||||
Elf64_Addr& symbolValue,
|
|
||||||
std::string& symbolName,
|
|
||||||
unsigned& type,
|
|
||||||
Elf_Sxword& addend,
|
|
||||||
Elf_Sxword& calcValue ) const
|
|
||||||
{
|
|
||||||
// Do regular job
|
|
||||||
Elf_Word symbol = 0;
|
|
||||||
bool ret = get_entry( index, offset, symbol, type, addend );
|
|
||||||
|
|
||||||
// Find the symbol
|
|
||||||
Elf_Xword size;
|
|
||||||
unsigned char bind;
|
|
||||||
unsigned char symbolType;
|
|
||||||
Elf_Half section;
|
|
||||||
unsigned char other;
|
|
||||||
|
|
||||||
symbol_section_accessor symbols(
|
|
||||||
elf_file, elf_file.sections[get_symbol_table_index()] );
|
|
||||||
ret = ret && symbols.get_symbol( symbol, symbolName, symbolValue, size,
|
|
||||||
bind, symbolType, section, other );
|
|
||||||
|
|
||||||
if ( ret ) { // Was it successful?
|
|
||||||
switch ( type ) {
|
|
||||||
case R_386_NONE: // none
|
|
||||||
calcValue = 0;
|
|
||||||
break;
|
|
||||||
case R_386_32: // S + A
|
|
||||||
calcValue = symbolValue + addend;
|
|
||||||
break;
|
|
||||||
case R_386_PC32: // S + A - P
|
|
||||||
calcValue = symbolValue + addend - offset;
|
|
||||||
break;
|
|
||||||
case R_386_GOT32: // G + A - P
|
|
||||||
calcValue = 0;
|
|
||||||
break;
|
|
||||||
case R_386_PLT32: // L + A - P
|
|
||||||
calcValue = 0;
|
|
||||||
break;
|
|
||||||
case R_386_COPY: // none
|
|
||||||
calcValue = 0;
|
|
||||||
break;
|
|
||||||
case R_386_GLOB_DAT: // S
|
|
||||||
case R_386_JMP_SLOT: // S
|
|
||||||
calcValue = symbolValue;
|
|
||||||
break;
|
|
||||||
case R_386_RELATIVE: // B + A
|
|
||||||
calcValue = addend;
|
|
||||||
break;
|
|
||||||
case R_386_GOTOFF: // S + A - GOT
|
|
||||||
calcValue = 0;
|
|
||||||
break;
|
|
||||||
case R_386_GOTPC: // GOT + A - P
|
|
||||||
calcValue = 0;
|
|
||||||
break;
|
|
||||||
default: // Not recognized symbol!
|
|
||||||
calcValue = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool set_entry( Elf_Xword index,
|
|
||||||
Elf64_Addr offset,
|
|
||||||
Elf_Word symbol,
|
|
||||||
unsigned type,
|
|
||||||
Elf_Sxword addend )
|
|
||||||
{
|
|
||||||
if ( index >= get_entries_num() ) { // Is index valid
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
if ( SHT_REL == relocation_section->get_type() ) {
|
|
||||||
generic_set_entry_rel<Elf32_Rel>( index, offset, symbol, type,
|
|
||||||
addend );
|
|
||||||
}
|
|
||||||
else if ( SHT_RELA == relocation_section->get_type() ) {
|
|
||||||
generic_set_entry_rela<Elf32_Rela>( index, offset, symbol, type,
|
|
||||||
addend );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ( SHT_REL == relocation_section->get_type() ) {
|
|
||||||
generic_set_entry_rel<Elf64_Rel>( index, offset, symbol, type,
|
|
||||||
addend );
|
|
||||||
}
|
|
||||||
else if ( SHT_RELA == relocation_section->get_type() ) {
|
|
||||||
generic_set_entry_rela<Elf64_Rela>( index, offset, symbol, type,
|
|
||||||
addend );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_entry( Elf64_Addr offset, Elf_Xword info )
|
|
||||||
{
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
generic_add_entry<Elf32_Rel>( offset, info );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
generic_add_entry<Elf64_Rel>( offset, info );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_entry( Elf64_Addr offset, Elf_Word symbol, unsigned type )
|
|
||||||
{
|
|
||||||
Elf_Xword info;
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
info = ELF32_R_INFO( (Elf_Xword)symbol, type );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
info = ELF64_R_INFO( (Elf_Xword)symbol, type );
|
|
||||||
}
|
|
||||||
|
|
||||||
add_entry( offset, info );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend )
|
|
||||||
{
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
generic_add_entry<Elf32_Rela>( offset, info, addend );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
generic_add_entry<Elf64_Rela>( offset, info, addend );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_entry( Elf64_Addr offset,
|
|
||||||
Elf_Word symbol,
|
|
||||||
unsigned type,
|
|
||||||
Elf_Sxword addend )
|
|
||||||
{
|
|
||||||
Elf_Xword info;
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
info = ELF32_R_INFO( (Elf_Xword)symbol, type );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
info = ELF64_R_INFO( (Elf_Xword)symbol, type );
|
|
||||||
}
|
|
||||||
|
|
||||||
add_entry( offset, info, addend );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void add_entry( string_section_accessor str_writer,
|
|
||||||
const char* str,
|
|
||||||
symbol_section_accessor sym_writer,
|
|
||||||
Elf64_Addr value,
|
|
||||||
Elf_Word size,
|
|
||||||
unsigned char sym_info,
|
|
||||||
unsigned char other,
|
|
||||||
Elf_Half shndx,
|
|
||||||
Elf64_Addr offset,
|
|
||||||
unsigned type )
|
|
||||||
{
|
|
||||||
Elf_Word str_index = str_writer.add_string( str );
|
|
||||||
Elf_Word sym_index = sym_writer.add_symbol( str_index, value, size,
|
|
||||||
sym_info, other, shndx );
|
|
||||||
add_entry( offset, sym_index, type );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void swap_symbols( Elf_Xword first, Elf_Xword second )
|
|
||||||
{
|
|
||||||
Elf64_Addr offset = 0;
|
|
||||||
Elf_Word symbol = 0;
|
|
||||||
unsigned rtype = 0;
|
|
||||||
Elf_Sxword addend = 0;
|
|
||||||
for ( Elf_Word i = 0; i < get_entries_num(); i++ ) {
|
|
||||||
get_entry( i, offset, symbol, rtype, addend );
|
|
||||||
if ( symbol == first ) {
|
|
||||||
set_entry( i, offset, (Elf_Word)second, rtype, addend );
|
|
||||||
}
|
|
||||||
if ( symbol == second ) {
|
|
||||||
set_entry( i, offset, (Elf_Word)first, rtype, addend );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half get_symbol_table_index() const
|
|
||||||
{
|
|
||||||
return (Elf_Half)relocation_section->get_link();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
void generic_get_entry_rel( Elf_Xword index,
|
|
||||||
Elf64_Addr& offset,
|
|
||||||
Elf_Word& symbol,
|
|
||||||
unsigned& type,
|
|
||||||
Elf_Sxword& addend ) const
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
const T* pEntry = reinterpret_cast<const T*>(
|
|
||||||
relocation_section->get_data() +
|
|
||||||
index * relocation_section->get_entry_size() );
|
|
||||||
offset = convertor( pEntry->r_offset );
|
|
||||||
Elf_Xword tmp = convertor( pEntry->r_info );
|
|
||||||
symbol = get_sym_and_type<T>::get_r_sym( tmp );
|
|
||||||
type = get_sym_and_type<T>::get_r_type( tmp );
|
|
||||||
addend = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
void generic_get_entry_rela( Elf_Xword index,
|
|
||||||
Elf64_Addr& offset,
|
|
||||||
Elf_Word& symbol,
|
|
||||||
unsigned& type,
|
|
||||||
Elf_Sxword& addend ) const
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
const T* pEntry = reinterpret_cast<const T*>(
|
|
||||||
relocation_section->get_data() +
|
|
||||||
index * relocation_section->get_entry_size() );
|
|
||||||
offset = convertor( pEntry->r_offset );
|
|
||||||
Elf_Xword tmp = convertor( pEntry->r_info );
|
|
||||||
symbol = get_sym_and_type<T>::get_r_sym( tmp );
|
|
||||||
type = get_sym_and_type<T>::get_r_type( tmp );
|
|
||||||
addend = convertor( pEntry->r_addend );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
void generic_set_entry_rel( Elf_Xword index,
|
|
||||||
Elf64_Addr offset,
|
|
||||||
Elf_Word symbol,
|
|
||||||
unsigned type,
|
|
||||||
Elf_Sxword )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
T* pEntry = const_cast<T*>( reinterpret_cast<const T*>(
|
|
||||||
relocation_section->get_data() +
|
|
||||||
index * relocation_section->get_entry_size() ) );
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type );
|
|
||||||
}
|
|
||||||
pEntry->r_offset = decltype( pEntry->r_offset )( offset );
|
|
||||||
pEntry->r_offset = convertor( pEntry->r_offset );
|
|
||||||
pEntry->r_info = convertor( pEntry->r_info );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
void generic_set_entry_rela( Elf_Xword index,
|
|
||||||
Elf64_Addr offset,
|
|
||||||
Elf_Word symbol,
|
|
||||||
unsigned type,
|
|
||||||
Elf_Sxword addend )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
T* pEntry = const_cast<T*>( reinterpret_cast<const T*>(
|
|
||||||
relocation_section->get_data() +
|
|
||||||
index * relocation_section->get_entry_size() ) );
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
pEntry->r_info = ELF32_R_INFO( (Elf_Xword)symbol, type );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
pEntry->r_info = ELF64_R_INFO( (Elf_Xword)symbol, type );
|
|
||||||
}
|
|
||||||
pEntry->r_offset = decltype( pEntry->r_offset )( offset );
|
|
||||||
pEntry->r_addend = decltype( pEntry->r_addend )( addend );
|
|
||||||
pEntry->r_offset = convertor( pEntry->r_offset );
|
|
||||||
pEntry->r_info = convertor( pEntry->r_info );
|
|
||||||
pEntry->r_addend = convertor( pEntry->r_addend );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
void generic_add_entry( Elf64_Addr offset, Elf_Xword info )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
T entry;
|
|
||||||
entry.r_offset = decltype( entry.r_offset )( offset );
|
|
||||||
entry.r_info = decltype( entry.r_info )( info );
|
|
||||||
entry.r_offset = convertor( entry.r_offset );
|
|
||||||
entry.r_info = convertor( entry.r_info );
|
|
||||||
|
|
||||||
relocation_section->append_data( reinterpret_cast<char*>( &entry ),
|
|
||||||
sizeof( entry ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
void
|
|
||||||
generic_add_entry( Elf64_Addr offset, Elf_Xword info, Elf_Sxword addend )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
T entry;
|
|
||||||
entry.r_offset = offset;
|
|
||||||
entry.r_info = info;
|
|
||||||
entry.r_addend = addend;
|
|
||||||
entry.r_offset = convertor( entry.r_offset );
|
|
||||||
entry.r_info = convertor( entry.r_info );
|
|
||||||
entry.r_addend = convertor( entry.r_addend );
|
|
||||||
|
|
||||||
relocation_section->append_data( reinterpret_cast<char*>( &entry ),
|
|
||||||
sizeof( entry ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
const elfio& elf_file;
|
|
||||||
S* relocation_section = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
using relocation_section_accessor =
|
|
||||||
relocation_section_accessor_template<section>;
|
|
||||||
using const_relocation_section_accessor =
|
|
||||||
relocation_section_accessor_template<const section>;
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_RELOCATION_HPP
|
|
@ -1,367 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_SECTION_HPP
|
|
||||||
#define ELFIO_SECTION_HPP
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <iostream>
|
|
||||||
#include <new>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
class section
|
|
||||||
{
|
|
||||||
friend class elfio;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~section() = default;
|
|
||||||
|
|
||||||
ELFIO_GET_ACCESS_DECL( Elf_Half, index );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( std::string, name );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, flags );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, info );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, link );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, addr_align );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, entry_size );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, address );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, size );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, name_string_offset );
|
|
||||||
ELFIO_GET_ACCESS_DECL( Elf64_Off, offset );
|
|
||||||
|
|
||||||
virtual const char* get_data() const = 0;
|
|
||||||
virtual void set_data( const char* raw_data, Elf_Word size ) = 0;
|
|
||||||
virtual void set_data( const std::string& data ) = 0;
|
|
||||||
virtual void append_data( const char* raw_data, Elf_Word size ) = 0;
|
|
||||||
virtual void append_data( const std::string& data ) = 0;
|
|
||||||
virtual void
|
|
||||||
insert_data( Elf_Xword pos, const char* raw_data, Elf_Word size ) = 0;
|
|
||||||
virtual void insert_data( Elf_Xword pos, const std::string& data ) = 0;
|
|
||||||
virtual size_t get_stream_size() const = 0;
|
|
||||||
virtual void set_stream_size( size_t value ) = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ELFIO_SET_ACCESS_DECL( Elf64_Off, offset );
|
|
||||||
ELFIO_SET_ACCESS_DECL( Elf_Half, index );
|
|
||||||
|
|
||||||
virtual bool load( std::istream& stream,
|
|
||||||
std::streampos header_offset,
|
|
||||||
bool is_lazy ) = 0;
|
|
||||||
virtual void save( std::ostream& stream,
|
|
||||||
std::streampos header_offset,
|
|
||||||
std::streampos data_offset ) = 0;
|
|
||||||
virtual bool is_address_initialized() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <class T> class section_impl : public section
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
section_impl( const endianess_convertor* convertor,
|
|
||||||
const address_translator* translator,
|
|
||||||
const std::shared_ptr<compression_interface>& compression )
|
|
||||||
: convertor( convertor ), translator( translator ),
|
|
||||||
compression( compression )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Section info functions
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Word, type, header.sh_type );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Xword, flags, header.sh_flags );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Xword, size, header.sh_size );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Word, link, header.sh_link );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Word, info, header.sh_info );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Xword, addr_align, header.sh_addralign );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Xword, entry_size, header.sh_entsize );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Word, name_string_offset, header.sh_name );
|
|
||||||
ELFIO_GET_ACCESS( Elf64_Addr, address, header.sh_addr );
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half get_index() const override { return index; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
std::string get_name() const override { return name; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_name( const std::string& name_prm ) override
|
|
||||||
{
|
|
||||||
this->name = name_prm;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_address( const Elf64_Addr& value ) override
|
|
||||||
{
|
|
||||||
header.sh_addr = decltype( header.sh_addr )( value );
|
|
||||||
header.sh_addr = ( *convertor )( header.sh_addr );
|
|
||||||
is_address_set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool is_address_initialized() const override { return is_address_set; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
const char* get_data() const override
|
|
||||||
{
|
|
||||||
if ( is_lazy ) {
|
|
||||||
load_data();
|
|
||||||
}
|
|
||||||
return data.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_data( const char* raw_data, Elf_Word size ) override
|
|
||||||
{
|
|
||||||
if ( get_type() != SHT_NOBITS ) {
|
|
||||||
data = std::unique_ptr<char[]>( new ( std::nothrow ) char[size] );
|
|
||||||
if ( nullptr != data.get() && nullptr != raw_data ) {
|
|
||||||
data_size = size;
|
|
||||||
std::copy( raw_data, raw_data + size, data.get() );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data_size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_size( data_size );
|
|
||||||
if ( translator->empty() ) {
|
|
||||||
set_stream_size( data_size );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_data( const std::string& str_data ) override
|
|
||||||
{
|
|
||||||
return set_data( str_data.c_str(), (Elf_Word)str_data.size() );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void append_data( const char* raw_data, Elf_Word size ) override
|
|
||||||
{
|
|
||||||
insert_data( get_size(), raw_data, size );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void append_data( const std::string& str_data ) override
|
|
||||||
{
|
|
||||||
return append_data( str_data.c_str(), (Elf_Word)str_data.size() );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void
|
|
||||||
insert_data( Elf_Xword pos, const char* raw_data, Elf_Word size ) override
|
|
||||||
{
|
|
||||||
if ( get_type() != SHT_NOBITS ) {
|
|
||||||
if ( get_size() + size < data_size ) {
|
|
||||||
char* d = data.get();
|
|
||||||
std::copy_backward( d + pos, d + get_size(),
|
|
||||||
d + get_size() + size );
|
|
||||||
std::copy( raw_data, raw_data + size, d + pos );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data_size = 2 * ( data_size + size );
|
|
||||||
std::unique_ptr<char[]> new_data(
|
|
||||||
new ( std::nothrow ) char[data_size] );
|
|
||||||
|
|
||||||
if ( nullptr != new_data ) {
|
|
||||||
char* d = data.get();
|
|
||||||
std::copy( d, d + pos, new_data.get() );
|
|
||||||
std::copy( raw_data, raw_data + size,
|
|
||||||
new_data.get() + pos );
|
|
||||||
std::copy( d + pos, d + get_size(),
|
|
||||||
new_data.get() + pos + size );
|
|
||||||
data = std::move( new_data );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set_size( get_size() + size );
|
|
||||||
if ( translator->empty() ) {
|
|
||||||
set_stream_size( get_stream_size() + size );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void insert_data( Elf_Xword pos, const std::string& str_data ) override
|
|
||||||
{
|
|
||||||
return insert_data( pos, str_data.c_str(), (Elf_Word)str_data.size() );
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t get_stream_size() const override { return stream_size; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_stream_size( size_t value ) override { stream_size = value; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
protected:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf64_Off, offset, header.sh_offset );
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_index( const Elf_Half& value ) override { index = value; }
|
|
||||||
|
|
||||||
bool is_compressed() const
|
|
||||||
{
|
|
||||||
return ( ( get_flags() & SHF_RPX_DEFLATE ) ||
|
|
||||||
( get_flags() & SHF_COMPRESSED ) ) &&
|
|
||||||
compression != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool load( std::istream& stream,
|
|
||||||
std::streampos header_offset,
|
|
||||||
bool is_lazy_ ) override
|
|
||||||
{
|
|
||||||
pstream = &stream;
|
|
||||||
is_lazy = is_lazy_;
|
|
||||||
|
|
||||||
if ( translator->empty() ) {
|
|
||||||
stream.seekg( 0, std::istream::end );
|
|
||||||
set_stream_size( size_t( stream.tellg() ) );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
set_stream_size( std::numeric_limits<size_t>::max() );
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.seekg( ( *translator )[header_offset] );
|
|
||||||
stream.read( reinterpret_cast<char*>( &header ), sizeof( header ) );
|
|
||||||
|
|
||||||
if ( !is_lazy || is_compressed() ) {
|
|
||||||
|
|
||||||
bool ret = load_data();
|
|
||||||
|
|
||||||
if ( is_compressed() ) {
|
|
||||||
Elf_Xword size = get_size();
|
|
||||||
Elf_Xword uncompressed_size = 0;
|
|
||||||
auto decompressed_data = compression->inflate(
|
|
||||||
data.get(), convertor, size, uncompressed_size );
|
|
||||||
if ( decompressed_data != nullptr ) {
|
|
||||||
set_size( uncompressed_size );
|
|
||||||
data = std::move( decompressed_data );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool load_data() const
|
|
||||||
{
|
|
||||||
is_lazy = false;
|
|
||||||
Elf_Xword size = get_size();
|
|
||||||
if ( nullptr == data && SHT_NULL != get_type() &&
|
|
||||||
SHT_NOBITS != get_type() && size < get_stream_size() ) {
|
|
||||||
data.reset( new ( std::nothrow ) char[size_t( size ) + 1] );
|
|
||||||
|
|
||||||
if ( ( 0 != size ) && ( nullptr != data ) ) {
|
|
||||||
pstream->seekg(
|
|
||||||
( *translator )[( *convertor )( header.sh_offset )] );
|
|
||||||
pstream->read( data.get(), size );
|
|
||||||
if ( static_cast<Elf_Xword>( pstream->gcount() ) != size ) {
|
|
||||||
data = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// refresh size because it may have changed if we had to decompress data
|
|
||||||
size = get_size();
|
|
||||||
data.get()[size] =
|
|
||||||
0; // Ensure data is ended with 0 to avoid oob read
|
|
||||||
data_size = decltype( data_size )( size );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data_size = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void save( std::ostream& stream,
|
|
||||||
std::streampos header_offset,
|
|
||||||
std::streampos data_offset ) override
|
|
||||||
{
|
|
||||||
if ( 0 != get_index() ) {
|
|
||||||
header.sh_offset = decltype( header.sh_offset )( data_offset );
|
|
||||||
header.sh_offset = ( *convertor )( header.sh_offset );
|
|
||||||
}
|
|
||||||
|
|
||||||
save_header( stream, header_offset );
|
|
||||||
if ( get_type() != SHT_NOBITS && get_type() != SHT_NULL &&
|
|
||||||
get_size() != 0 && data != nullptr ) {
|
|
||||||
save_data( stream, data_offset );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void save_header( std::ostream& stream, std::streampos header_offset ) const
|
|
||||||
{
|
|
||||||
adjust_stream_size( stream, header_offset );
|
|
||||||
stream.write( reinterpret_cast<const char*>( &header ),
|
|
||||||
sizeof( header ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void save_data( std::ostream& stream, std::streampos data_offset )
|
|
||||||
{
|
|
||||||
adjust_stream_size( stream, data_offset );
|
|
||||||
|
|
||||||
if ( ( ( get_flags() & SHF_COMPRESSED ) ||
|
|
||||||
( get_flags() & SHF_RPX_DEFLATE ) ) &&
|
|
||||||
compression != nullptr ) {
|
|
||||||
Elf_Xword decompressed_size = get_size();
|
|
||||||
Elf_Xword compressed_size = 0;
|
|
||||||
auto compressed_ptr = compression->deflate(
|
|
||||||
data.get(), convertor, decompressed_size, compressed_size );
|
|
||||||
stream.write( compressed_ptr.get(), compressed_size );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
stream.write( get_data(), get_size() );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
mutable std::istream* pstream = nullptr;
|
|
||||||
T header = {};
|
|
||||||
Elf_Half index = 0;
|
|
||||||
std::string name;
|
|
||||||
mutable std::unique_ptr<char[]> data;
|
|
||||||
mutable Elf_Word data_size = 0;
|
|
||||||
const endianess_convertor* convertor = nullptr;
|
|
||||||
const address_translator* translator = nullptr;
|
|
||||||
const std::shared_ptr<compression_interface> compression = nullptr;
|
|
||||||
bool is_address_set = false;
|
|
||||||
size_t stream_size = 0;
|
|
||||||
mutable bool is_lazy = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_SECTION_HPP
|
|
@ -1,254 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_SEGMENT_HPP
|
|
||||||
#define ELFIO_SEGMENT_HPP
|
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <vector>
|
|
||||||
#include <new>
|
|
||||||
#include <limits>
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
class segment
|
|
||||||
{
|
|
||||||
friend class elfio;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~segment() = default;
|
|
||||||
|
|
||||||
ELFIO_GET_ACCESS_DECL( Elf_Half, index );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, type );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Word, flags );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, align );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, virtual_address );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf64_Addr, physical_address );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, file_size );
|
|
||||||
ELFIO_GET_SET_ACCESS_DECL( Elf_Xword, memory_size );
|
|
||||||
ELFIO_GET_ACCESS_DECL( Elf64_Off, offset );
|
|
||||||
|
|
||||||
virtual const char* get_data() const = 0;
|
|
||||||
|
|
||||||
virtual Elf_Half add_section( section* psec, Elf_Xword addr_align ) = 0;
|
|
||||||
virtual Elf_Half add_section_index( Elf_Half index,
|
|
||||||
Elf_Xword addr_align ) = 0;
|
|
||||||
virtual Elf_Half get_sections_num() const = 0;
|
|
||||||
virtual Elf_Half get_section_index_at( Elf_Half num ) const = 0;
|
|
||||||
virtual bool is_offset_initialized() const = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
ELFIO_SET_ACCESS_DECL( Elf64_Off, offset );
|
|
||||||
ELFIO_SET_ACCESS_DECL( Elf_Half, index );
|
|
||||||
|
|
||||||
virtual const std::vector<Elf_Half>& get_sections() const = 0;
|
|
||||||
|
|
||||||
virtual bool load( std::istream& stream,
|
|
||||||
std::streampos header_offset,
|
|
||||||
bool is_lazy ) = 0;
|
|
||||||
virtual void save( std::ostream& stream,
|
|
||||||
std::streampos header_offset,
|
|
||||||
std::streampos data_offset ) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T> class segment_impl : public segment
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
segment_impl( const endianess_convertor* convertor,
|
|
||||||
const address_translator* translator )
|
|
||||||
: convertor( convertor ), translator( translator )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
// Section info functions
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Word, type, ph.p_type );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Word, flags, ph.p_flags );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Xword, align, ph.p_align );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf64_Addr, virtual_address, ph.p_vaddr );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf64_Addr, physical_address, ph.p_paddr );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Xword, file_size, ph.p_filesz );
|
|
||||||
ELFIO_GET_SET_ACCESS( Elf_Xword, memory_size, ph.p_memsz );
|
|
||||||
ELFIO_GET_ACCESS( Elf64_Off, offset, ph.p_offset );
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half get_index() const override { return index; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
const char* get_data() const override
|
|
||||||
{
|
|
||||||
if ( is_lazy ) {
|
|
||||||
load_data();
|
|
||||||
}
|
|
||||||
return data.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half add_section_index( Elf_Half sec_index,
|
|
||||||
Elf_Xword addr_align ) override
|
|
||||||
{
|
|
||||||
sections.emplace_back( sec_index );
|
|
||||||
if ( addr_align > get_align() ) {
|
|
||||||
set_align( addr_align );
|
|
||||||
}
|
|
||||||
|
|
||||||
return (Elf_Half)sections.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half add_section( section* psec, Elf_Xword addr_align ) override
|
|
||||||
{
|
|
||||||
return add_section_index( psec->get_index(), addr_align );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half get_sections_num() const override
|
|
||||||
{
|
|
||||||
return (Elf_Half)sections.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half get_section_index_at( Elf_Half num ) const override
|
|
||||||
{
|
|
||||||
if ( num < sections.size() ) {
|
|
||||||
return sections[num];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Elf_Half( -1 );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
protected:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_offset( const Elf64_Off& value ) override
|
|
||||||
{
|
|
||||||
ph.p_offset = decltype( ph.p_offset )( value );
|
|
||||||
ph.p_offset = ( *convertor )( ph.p_offset );
|
|
||||||
is_offset_set = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool is_offset_initialized() const override { return is_offset_set; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
const std::vector<Elf_Half>& get_sections() const override
|
|
||||||
{
|
|
||||||
return sections;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_index( const Elf_Half& value ) override { index = value; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool load( std::istream& stream,
|
|
||||||
std::streampos header_offset,
|
|
||||||
bool is_lazy_ ) override
|
|
||||||
{
|
|
||||||
pstream = &stream;
|
|
||||||
is_lazy = is_lazy_;
|
|
||||||
|
|
||||||
if ( translator->empty() ) {
|
|
||||||
stream.seekg( 0, std::istream::end );
|
|
||||||
set_stream_size( size_t( stream.tellg() ) );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
set_stream_size( std::numeric_limits<size_t>::max() );
|
|
||||||
}
|
|
||||||
|
|
||||||
stream.seekg( ( *translator )[header_offset] );
|
|
||||||
stream.read( reinterpret_cast<char*>( &ph ), sizeof( ph ) );
|
|
||||||
is_offset_set = true;
|
|
||||||
|
|
||||||
if ( !is_lazy ) {
|
|
||||||
return load_data();
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool load_data() const
|
|
||||||
{
|
|
||||||
is_lazy = false;
|
|
||||||
if ( PT_NULL == get_type() || 0 == get_file_size() ) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
pstream->seekg( ( *translator )[( *convertor )( ph.p_offset )] );
|
|
||||||
Elf_Xword size = get_file_size();
|
|
||||||
|
|
||||||
if ( size > get_stream_size() ) {
|
|
||||||
data = nullptr;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data.reset( new ( std::nothrow ) char[(size_t)size + 1] );
|
|
||||||
|
|
||||||
if ( nullptr != data.get() && pstream->read( data.get(), size ) ) {
|
|
||||||
data.get()[size] = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data = nullptr;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void save( std::ostream& stream,
|
|
||||||
std::streampos header_offset,
|
|
||||||
std::streampos data_offset ) override
|
|
||||||
{
|
|
||||||
ph.p_offset = decltype( ph.p_offset )( data_offset );
|
|
||||||
ph.p_offset = ( *convertor )( ph.p_offset );
|
|
||||||
adjust_stream_size( stream, header_offset );
|
|
||||||
stream.write( reinterpret_cast<const char*>( &ph ), sizeof( ph ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
size_t get_stream_size() const { return stream_size; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_stream_size( size_t value ) { stream_size = value; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
mutable std::istream* pstream = nullptr;
|
|
||||||
T ph = {};
|
|
||||||
Elf_Half index = 0;
|
|
||||||
mutable std::unique_ptr<char[]> data;
|
|
||||||
std::vector<Elf_Half> sections;
|
|
||||||
const endianess_convertor* convertor = nullptr;
|
|
||||||
const address_translator* translator = nullptr;
|
|
||||||
size_t stream_size = 0;
|
|
||||||
bool is_offset_set = false;
|
|
||||||
mutable bool is_lazy = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_SEGMENT_HPP
|
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_STRINGS_HPP
|
|
||||||
#define ELFIO_STRINGS_HPP
|
|
||||||
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S> class string_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
explicit string_section_accessor_template( S* section )
|
|
||||||
: string_section( section )
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
const char* get_string( Elf_Word index ) const
|
|
||||||
{
|
|
||||||
if ( string_section ) {
|
|
||||||
const char* data = string_section->get_data();
|
|
||||||
if ( index < string_section->get_size() && nullptr != data ) {
|
|
||||||
size_t string_length =
|
|
||||||
strnlen( data + index, string_section->get_size() - index );
|
|
||||||
if ( string_length < ( string_section->get_size() - index ) )
|
|
||||||
return data + index;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word add_string( const char* str )
|
|
||||||
{
|
|
||||||
Elf_Word current_position = 0;
|
|
||||||
|
|
||||||
if ( string_section ) {
|
|
||||||
// Strings are addeded to the end of the current section data
|
|
||||||
current_position =
|
|
||||||
static_cast<Elf_Word>( string_section->get_size() );
|
|
||||||
|
|
||||||
if ( current_position == 0 ) {
|
|
||||||
char empty_string = '\0';
|
|
||||||
string_section->append_data( &empty_string, 1 );
|
|
||||||
current_position++;
|
|
||||||
}
|
|
||||||
string_section->append_data(
|
|
||||||
str, static_cast<Elf_Word>( std::strlen( str ) + 1 ) );
|
|
||||||
}
|
|
||||||
|
|
||||||
return current_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word add_string( const std::string& str )
|
|
||||||
{
|
|
||||||
return add_string( str.c_str() );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
S* string_section;
|
|
||||||
};
|
|
||||||
|
|
||||||
using string_section_accessor = string_section_accessor_template<section>;
|
|
||||||
using const_string_section_accessor =
|
|
||||||
string_section_accessor_template<const section>;
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_STRINGS_HPP
|
|
@ -1,562 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_SYMBOLS_HPP
|
|
||||||
#define ELFIO_SYMBOLS_HPP
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S> class symbol_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
explicit symbol_section_accessor_template( const elfio& elf_file,
|
|
||||||
S* symbol_section )
|
|
||||||
: elf_file( elf_file ), symbol_section( symbol_section )
|
|
||||||
{
|
|
||||||
find_hash_section();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Xword get_symbols_num() const
|
|
||||||
{
|
|
||||||
Elf_Xword nRet = 0;
|
|
||||||
|
|
||||||
size_t minimum_symbol_size;
|
|
||||||
switch ( elf_file.get_class() ) {
|
|
||||||
case ELFCLASS32:
|
|
||||||
minimum_symbol_size = sizeof( Elf32_Sym );
|
|
||||||
break;
|
|
||||||
case ELFCLASS64:
|
|
||||||
minimum_symbol_size = sizeof( Elf64_Sym );
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
return nRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( symbol_section->get_entry_size() >= minimum_symbol_size &&
|
|
||||||
symbol_section->get_size() <= symbol_section->get_stream_size() ) {
|
|
||||||
nRet =
|
|
||||||
symbol_section->get_size() / symbol_section->get_entry_size();
|
|
||||||
}
|
|
||||||
|
|
||||||
return nRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_symbol( Elf_Xword index,
|
|
||||||
std::string& name,
|
|
||||||
Elf64_Addr& value,
|
|
||||||
Elf_Xword& size,
|
|
||||||
unsigned char& bind,
|
|
||||||
unsigned char& type,
|
|
||||||
Elf_Half& section_index,
|
|
||||||
unsigned char& other ) const
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
ret = generic_get_symbol<Elf32_Sym>( index, name, value, size, bind,
|
|
||||||
type, section_index, other );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ret = generic_get_symbol<Elf64_Sym>( index, name, value, size, bind,
|
|
||||||
type, section_index, other );
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_symbol( const std::string& name,
|
|
||||||
Elf64_Addr& value,
|
|
||||||
Elf_Xword& size,
|
|
||||||
unsigned char& bind,
|
|
||||||
unsigned char& type,
|
|
||||||
Elf_Half& section_index,
|
|
||||||
unsigned char& other ) const
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
if ( 0 != get_hash_table_index() ) {
|
|
||||||
if ( hash_section->get_type() == SHT_HASH ) {
|
|
||||||
ret = hash_lookup( name, value, size, bind, type, section_index,
|
|
||||||
other );
|
|
||||||
}
|
|
||||||
if ( hash_section->get_type() == SHT_GNU_HASH ||
|
|
||||||
hash_section->get_type() == DT_GNU_HASH ) {
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
ret = gnu_hash_lookup<uint32_t>(
|
|
||||||
name, value, size, bind, type, section_index, other );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
ret = gnu_hash_lookup<uint64_t>(
|
|
||||||
name, value, size, bind, type, section_index, other );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( !ret ) {
|
|
||||||
for ( Elf_Xword i = 0; !ret && i < get_symbols_num(); i++ ) {
|
|
||||||
std::string symbol_name;
|
|
||||||
if ( get_symbol( i, symbol_name, value, size, bind, type,
|
|
||||||
section_index, other ) ) {
|
|
||||||
if ( symbol_name == name ) {
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_symbol( const Elf64_Addr& value,
|
|
||||||
std::string& name,
|
|
||||||
Elf_Xword& size,
|
|
||||||
unsigned char& bind,
|
|
||||||
unsigned char& type,
|
|
||||||
Elf_Half& section_index,
|
|
||||||
unsigned char& other ) const
|
|
||||||
{
|
|
||||||
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
Elf_Xword idx = 0;
|
|
||||||
bool match = false;
|
|
||||||
Elf64_Addr v = 0;
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
match = generic_search_symbols<Elf32_Sym>(
|
|
||||||
[&]( const Elf32_Sym* sym ) {
|
|
||||||
return convertor( sym->st_value ) == value;
|
|
||||||
},
|
|
||||||
idx );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
match = generic_search_symbols<Elf64_Sym>(
|
|
||||||
[&]( const Elf64_Sym* sym ) {
|
|
||||||
return convertor( sym->st_value ) == value;
|
|
||||||
},
|
|
||||||
idx );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( match ) {
|
|
||||||
return get_symbol( idx, name, v, size, bind, type, section_index,
|
|
||||||
other );
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word add_symbol( Elf_Word name,
|
|
||||||
Elf64_Addr value,
|
|
||||||
Elf_Xword size,
|
|
||||||
unsigned char info,
|
|
||||||
unsigned char other,
|
|
||||||
Elf_Half shndx )
|
|
||||||
{
|
|
||||||
Elf_Word nRet;
|
|
||||||
|
|
||||||
if ( symbol_section->get_size() == 0 ) {
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
nRet = generic_add_symbol<Elf32_Sym>( 0, 0, 0, 0, 0, 0 );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nRet = generic_add_symbol<Elf64_Sym>( 0, 0, 0, 0, 0, 0 );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
nRet = generic_add_symbol<Elf32_Sym>( name, value, size, info,
|
|
||||||
other, shndx );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nRet = generic_add_symbol<Elf64_Sym>( name, value, size, info,
|
|
||||||
other, shndx );
|
|
||||||
}
|
|
||||||
|
|
||||||
return nRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word add_symbol( Elf_Word name,
|
|
||||||
Elf64_Addr value,
|
|
||||||
Elf_Xword size,
|
|
||||||
unsigned char bind,
|
|
||||||
unsigned char type,
|
|
||||||
unsigned char other,
|
|
||||||
Elf_Half shndx )
|
|
||||||
{
|
|
||||||
return add_symbol( name, value, size, ELF_ST_INFO( bind, type ), other,
|
|
||||||
shndx );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word add_symbol( string_section_accessor& pStrWriter,
|
|
||||||
const char* str,
|
|
||||||
Elf64_Addr value,
|
|
||||||
Elf_Xword size,
|
|
||||||
unsigned char info,
|
|
||||||
unsigned char other,
|
|
||||||
Elf_Half shndx )
|
|
||||||
{
|
|
||||||
Elf_Word index = pStrWriter.add_string( str );
|
|
||||||
return add_symbol( index, value, size, info, other, shndx );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word add_symbol( string_section_accessor& pStrWriter,
|
|
||||||
const char* str,
|
|
||||||
Elf64_Addr value,
|
|
||||||
Elf_Xword size,
|
|
||||||
unsigned char bind,
|
|
||||||
unsigned char type,
|
|
||||||
unsigned char other,
|
|
||||||
Elf_Half shndx )
|
|
||||||
{
|
|
||||||
return add_symbol( pStrWriter, str, value, size,
|
|
||||||
ELF_ST_INFO( bind, type ), other, shndx );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Xword arrange_local_symbols(
|
|
||||||
std::function<void( Elf_Xword first, Elf_Xword second )> func =
|
|
||||||
nullptr )
|
|
||||||
{
|
|
||||||
Elf_Xword nRet = 0;
|
|
||||||
|
|
||||||
if ( elf_file.get_class() == ELFCLASS32 ) {
|
|
||||||
nRet = generic_arrange_local_symbols<Elf32_Sym>( func );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
nRet = generic_arrange_local_symbols<Elf64_Sym>( func );
|
|
||||||
}
|
|
||||||
|
|
||||||
return nRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void find_hash_section()
|
|
||||||
{
|
|
||||||
Elf_Half nSecNo = elf_file.sections.size();
|
|
||||||
for ( Elf_Half i = 0; i < nSecNo; ++i ) {
|
|
||||||
const section* sec = elf_file.sections[i];
|
|
||||||
if ( sec->get_link() == symbol_section->get_index() &&
|
|
||||||
( sec->get_type() == SHT_HASH ||
|
|
||||||
sec->get_type() == SHT_GNU_HASH ||
|
|
||||||
sec->get_type() == DT_GNU_HASH ) ) {
|
|
||||||
hash_section = sec;
|
|
||||||
hash_section_index = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half get_string_table_index() const
|
|
||||||
{
|
|
||||||
return (Elf_Half)symbol_section->get_link();
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Half get_hash_table_index() const { return hash_section_index; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool hash_lookup( const std::string& name,
|
|
||||||
Elf64_Addr& value,
|
|
||||||
Elf_Xword& size,
|
|
||||||
unsigned char& bind,
|
|
||||||
unsigned char& type,
|
|
||||||
Elf_Half& section_index,
|
|
||||||
unsigned char& other ) const
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
Elf_Word nbucket = *(const Elf_Word*)hash_section->get_data();
|
|
||||||
nbucket = convertor( nbucket );
|
|
||||||
Elf_Word nchain =
|
|
||||||
*(const Elf_Word*)( hash_section->get_data() + sizeof( Elf_Word ) );
|
|
||||||
nchain = convertor( nchain );
|
|
||||||
Elf_Word val = elf_hash( (const unsigned char*)name.c_str() );
|
|
||||||
Elf_Word y =
|
|
||||||
*(const Elf_Word*)( hash_section->get_data() +
|
|
||||||
( 2 + val % nbucket ) * sizeof( Elf_Word ) );
|
|
||||||
y = convertor( y );
|
|
||||||
std::string str;
|
|
||||||
get_symbol( y, str, value, size, bind, type, section_index, other );
|
|
||||||
while ( str != name && STN_UNDEF != y && y < nchain ) {
|
|
||||||
y = *(const Elf_Word*)( hash_section->get_data() +
|
|
||||||
( 2 + nbucket + y ) * sizeof( Elf_Word ) );
|
|
||||||
y = convertor( y );
|
|
||||||
get_symbol( y, str, value, size, bind, type, section_index, other );
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( str == name ) {
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
bool gnu_hash_lookup( const std::string& name,
|
|
||||||
Elf64_Addr& value,
|
|
||||||
Elf_Xword& size,
|
|
||||||
unsigned char& bind,
|
|
||||||
unsigned char& type,
|
|
||||||
Elf_Half& section_index,
|
|
||||||
unsigned char& other ) const
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
uint32_t nbuckets = *( (uint32_t*)hash_section->get_data() + 0 );
|
|
||||||
uint32_t symoffset = *( (uint32_t*)hash_section->get_data() + 1 );
|
|
||||||
uint32_t bloom_size = *( (uint32_t*)hash_section->get_data() + 2 );
|
|
||||||
uint32_t bloom_shift = *( (uint32_t*)hash_section->get_data() + 3 );
|
|
||||||
nbuckets = convertor( nbuckets );
|
|
||||||
symoffset = convertor( symoffset );
|
|
||||||
bloom_size = convertor( bloom_size );
|
|
||||||
bloom_shift = convertor( bloom_shift );
|
|
||||||
|
|
||||||
T* bloom_filter =
|
|
||||||
(T*)( hash_section->get_data() + 4 * sizeof( uint32_t ) );
|
|
||||||
|
|
||||||
uint32_t hash = elf_gnu_hash( (const unsigned char*)name.c_str() );
|
|
||||||
uint32_t bloom_index = ( hash / ( 8 * sizeof( T ) ) ) % bloom_size;
|
|
||||||
T bloom_bits =
|
|
||||||
( (T)1 << ( hash % ( 8 * sizeof( T ) ) ) ) |
|
|
||||||
( (T)1 << ( ( hash >> bloom_shift ) % ( 8 * sizeof( T ) ) ) );
|
|
||||||
|
|
||||||
if ( ( convertor( bloom_filter[bloom_index] ) & bloom_bits ) !=
|
|
||||||
bloom_bits )
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
uint32_t bucket = hash % nbuckets;
|
|
||||||
auto* buckets =
|
|
||||||
(uint32_t*)( hash_section->get_data() + 4 * sizeof( uint32_t ) +
|
|
||||||
bloom_size * sizeof( T ) );
|
|
||||||
auto* chains =
|
|
||||||
(uint32_t*)( hash_section->get_data() + 4 * sizeof( uint32_t ) +
|
|
||||||
bloom_size * sizeof( T ) +
|
|
||||||
nbuckets * sizeof( uint32_t ) );
|
|
||||||
|
|
||||||
if ( convertor( buckets[bucket] ) >= symoffset ) {
|
|
||||||
uint32_t chain_index = convertor( buckets[bucket] ) - symoffset;
|
|
||||||
uint32_t chain_hash = convertor( chains[chain_index] );
|
|
||||||
std::string symname;
|
|
||||||
|
|
||||||
while ( true ) {
|
|
||||||
if ( ( chain_hash >> 1 ) == ( hash >> 1 ) &&
|
|
||||||
get_symbol( chain_index + symoffset, symname, value, size,
|
|
||||||
bind, type, section_index, other ) &&
|
|
||||||
name == symname ) {
|
|
||||||
ret = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( chain_hash & 1 )
|
|
||||||
break;
|
|
||||||
chain_hash = convertor( chains[++chain_index] );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T> const T* generic_get_symbol_ptr( Elf_Xword index ) const
|
|
||||||
{
|
|
||||||
if ( 0 != symbol_section->get_data() && index < get_symbols_num() ) {
|
|
||||||
const T* pSym = reinterpret_cast<const T*>(
|
|
||||||
symbol_section->get_data() +
|
|
||||||
index * symbol_section->get_entry_size() );
|
|
||||||
|
|
||||||
return pSym;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
bool generic_search_symbols( std::function<bool( const T* )> match,
|
|
||||||
Elf_Xword& idx ) const
|
|
||||||
{
|
|
||||||
for ( Elf_Xword i = 0; i < get_symbols_num(); i++ ) {
|
|
||||||
const T* symPtr = generic_get_symbol_ptr<T>( i );
|
|
||||||
|
|
||||||
if ( symPtr == nullptr )
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if ( match( symPtr ) ) {
|
|
||||||
idx = i;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
bool generic_get_symbol( Elf_Xword index,
|
|
||||||
std::string& name,
|
|
||||||
Elf64_Addr& value,
|
|
||||||
Elf_Xword& size,
|
|
||||||
unsigned char& bind,
|
|
||||||
unsigned char& type,
|
|
||||||
Elf_Half& section_index,
|
|
||||||
unsigned char& other ) const
|
|
||||||
{
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
if ( nullptr != symbol_section->get_data() &&
|
|
||||||
index < get_symbols_num() ) {
|
|
||||||
const T* pSym = reinterpret_cast<const T*>(
|
|
||||||
symbol_section->get_data() +
|
|
||||||
index * symbol_section->get_entry_size() );
|
|
||||||
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
section* string_section =
|
|
||||||
elf_file.sections[get_string_table_index()];
|
|
||||||
string_section_accessor str_reader( string_section );
|
|
||||||
const char* pStr =
|
|
||||||
str_reader.get_string( convertor( pSym->st_name ) );
|
|
||||||
if ( nullptr != pStr ) {
|
|
||||||
name = pStr;
|
|
||||||
}
|
|
||||||
value = convertor( pSym->st_value );
|
|
||||||
size = convertor( pSym->st_size );
|
|
||||||
bind = ELF_ST_BIND( pSym->st_info );
|
|
||||||
type = ELF_ST_TYPE( pSym->st_info );
|
|
||||||
section_index = convertor( pSym->st_shndx );
|
|
||||||
other = pSym->st_other;
|
|
||||||
|
|
||||||
ret = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
Elf_Word generic_add_symbol( Elf_Word name,
|
|
||||||
Elf64_Addr value,
|
|
||||||
Elf_Xword size,
|
|
||||||
unsigned char info,
|
|
||||||
unsigned char other,
|
|
||||||
Elf_Half shndx )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
T entry;
|
|
||||||
entry.st_name = convertor( name );
|
|
||||||
entry.st_value = decltype( entry.st_value )( value );
|
|
||||||
entry.st_value = convertor( entry.st_value );
|
|
||||||
entry.st_size = decltype( entry.st_size )( size );
|
|
||||||
entry.st_size = convertor( entry.st_size );
|
|
||||||
entry.st_info = convertor( info );
|
|
||||||
entry.st_other = convertor( other );
|
|
||||||
entry.st_shndx = convertor( shndx );
|
|
||||||
|
|
||||||
symbol_section->append_data( reinterpret_cast<char*>( &entry ),
|
|
||||||
sizeof( entry ) );
|
|
||||||
|
|
||||||
Elf_Word nRet =
|
|
||||||
Elf_Word( symbol_section->get_size() / sizeof( entry ) - 1 );
|
|
||||||
|
|
||||||
return nRet;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class T>
|
|
||||||
Elf_Xword generic_arrange_local_symbols(
|
|
||||||
std::function<void( Elf_Xword first, Elf_Xword second )> func )
|
|
||||||
{
|
|
||||||
const endianess_convertor& convertor = elf_file.get_convertor();
|
|
||||||
|
|
||||||
Elf_Word first_not_local =
|
|
||||||
1; // Skip the first entry. It is always NOTYPE
|
|
||||||
Elf_Xword current = 0;
|
|
||||||
Elf_Xword count = get_symbols_num();
|
|
||||||
|
|
||||||
while ( true ) {
|
|
||||||
T* p1 = nullptr;
|
|
||||||
T* p2 = nullptr;
|
|
||||||
|
|
||||||
while ( first_not_local < count ) {
|
|
||||||
p1 = const_cast<T*>(
|
|
||||||
generic_get_symbol_ptr<T>( first_not_local ) );
|
|
||||||
if ( ELF_ST_BIND( convertor( p1->st_info ) ) != STB_LOCAL )
|
|
||||||
break;
|
|
||||||
++first_not_local;
|
|
||||||
}
|
|
||||||
|
|
||||||
current = first_not_local + 1;
|
|
||||||
while ( current < count ) {
|
|
||||||
p2 = const_cast<T*>( generic_get_symbol_ptr<T>( current ) );
|
|
||||||
if ( ELF_ST_BIND( convertor( p2->st_info ) ) == STB_LOCAL )
|
|
||||||
break;
|
|
||||||
++current;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( first_not_local < count && current < count ) {
|
|
||||||
if ( func )
|
|
||||||
func( first_not_local, current );
|
|
||||||
|
|
||||||
std::swap( *p1, *p2 );
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Update 'info' field of the section
|
|
||||||
symbol_section->set_info( first_not_local );
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return first_not_local;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
const elfio& elf_file;
|
|
||||||
S* symbol_section;
|
|
||||||
Elf_Half hash_section_index{ 0 };
|
|
||||||
const section* hash_section{ nullptr };
|
|
||||||
};
|
|
||||||
|
|
||||||
using symbol_section_accessor = symbol_section_accessor_template<section>;
|
|
||||||
using const_symbol_section_accessor =
|
|
||||||
symbol_section_accessor_template<const section>;
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_SYMBOLS_HPP
|
|
@ -1,302 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_UTILS_HPP
|
|
||||||
#define ELFIO_UTILS_HPP
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <ostream>
|
|
||||||
|
|
||||||
#define ELFIO_GET_ACCESS_DECL( TYPE, NAME ) virtual TYPE get_##NAME() const = 0
|
|
||||||
|
|
||||||
#define ELFIO_SET_ACCESS_DECL( TYPE, NAME ) \
|
|
||||||
virtual void set_##NAME( const TYPE& value ) = 0
|
|
||||||
|
|
||||||
#define ELFIO_GET_SET_ACCESS_DECL( TYPE, NAME ) \
|
|
||||||
virtual TYPE get_##NAME() const = 0; \
|
|
||||||
virtual void set_##NAME( const TYPE& value ) = 0
|
|
||||||
|
|
||||||
#define ELFIO_GET_ACCESS( TYPE, NAME, FIELD ) \
|
|
||||||
TYPE get_##NAME() const override { return ( *convertor )( FIELD ); }
|
|
||||||
|
|
||||||
#define ELFIO_SET_ACCESS( TYPE, NAME, FIELD ) \
|
|
||||||
void set_##NAME( const TYPE& value ) override \
|
|
||||||
{ \
|
|
||||||
FIELD = decltype( FIELD )( value ); \
|
|
||||||
FIELD = ( *convertor )( FIELD ); \
|
|
||||||
}
|
|
||||||
#define ELFIO_GET_SET_ACCESS( TYPE, NAME, FIELD ) \
|
|
||||||
TYPE get_##NAME() const override { return ( *convertor )( FIELD ); } \
|
|
||||||
void set_##NAME( const TYPE& value ) override \
|
|
||||||
{ \
|
|
||||||
FIELD = decltype( FIELD )( value ); \
|
|
||||||
FIELD = ( *convertor )( FIELD ); \
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class endianess_convertor
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void setup( unsigned char elf_file_encoding )
|
|
||||||
{
|
|
||||||
need_conversion = ( elf_file_encoding != get_host_encoding() );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
uint64_t operator()( uint64_t value ) const
|
|
||||||
{
|
|
||||||
if ( !need_conversion ) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
value = ( ( value & 0x00000000000000FFuLL ) << 56 ) |
|
|
||||||
( ( value & 0x000000000000FF00uLL ) << 40 ) |
|
|
||||||
( ( value & 0x0000000000FF0000uLL ) << 24 ) |
|
|
||||||
( ( value & 0x00000000FF000000uLL ) << 8 ) |
|
|
||||||
( ( value & 0x000000FF00000000uLL ) >> 8 ) |
|
|
||||||
( ( value & 0x0000FF0000000000uLL ) >> 24 ) |
|
|
||||||
( ( value & 0x00FF000000000000uLL ) >> 40 ) |
|
|
||||||
( ( value & 0xFF00000000000000uLL ) >> 56 );
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
int64_t operator()( int64_t value ) const
|
|
||||||
{
|
|
||||||
if ( !need_conversion ) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return (int64_t)( *this )( (uint64_t)value );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
uint32_t operator()( uint32_t value ) const
|
|
||||||
{
|
|
||||||
if ( !need_conversion ) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
value =
|
|
||||||
( ( value & 0x000000FF ) << 24 ) | ( ( value & 0x0000FF00 ) << 8 ) |
|
|
||||||
( ( value & 0x00FF0000 ) >> 8 ) | ( ( value & 0xFF000000 ) >> 24 );
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
int32_t operator()( int32_t value ) const
|
|
||||||
{
|
|
||||||
if ( !need_conversion ) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return (int32_t)( *this )( (uint32_t)value );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
uint16_t operator()( uint16_t value ) const
|
|
||||||
{
|
|
||||||
if ( !need_conversion ) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
value =
|
|
||||||
(uint16_t)( ( value & 0x00FF ) << 8 ) | ( ( value & 0xFF00 ) >> 8 );
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
int16_t operator()( int16_t value ) const
|
|
||||||
{
|
|
||||||
if ( !need_conversion ) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return (int16_t)( *this )( (uint16_t)value );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
int8_t operator()( int8_t value ) const { return value; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
uint8_t operator()( uint8_t value ) const { return value; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
unsigned char get_host_encoding() const
|
|
||||||
{
|
|
||||||
static const int tmp = 1;
|
|
||||||
if ( 1 == *reinterpret_cast<const char*>( &tmp ) ) {
|
|
||||||
return ELFDATA2LSB;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return ELFDATA2MSB;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool need_conversion = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
struct address_translation
|
|
||||||
{
|
|
||||||
address_translation( uint64_t start, uint64_t size, uint64_t mapped_to )
|
|
||||||
: start( start ), size( size ), mapped_to( mapped_to ){};
|
|
||||||
std::streampos start;
|
|
||||||
std::streampos size;
|
|
||||||
std::streampos mapped_to;
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
class address_translator
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
void set_address_translation( std::vector<address_translation>& addr_trans )
|
|
||||||
{
|
|
||||||
addr_translations = addr_trans;
|
|
||||||
|
|
||||||
std::sort( addr_translations.begin(), addr_translations.end(),
|
|
||||||
[]( const address_translation& a,
|
|
||||||
const address_translation& b ) -> bool {
|
|
||||||
return a.start < b.start;
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
std::streampos operator[]( std::streampos value ) const
|
|
||||||
{
|
|
||||||
if ( addr_translations.empty() ) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
for ( auto& t : addr_translations ) {
|
|
||||||
if ( ( t.start <= value ) && ( ( value - t.start ) < t.size ) ) {
|
|
||||||
return value - t.start + t.mapped_to;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool empty() const { return addr_translations.empty(); }
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<address_translation> addr_translations;
|
|
||||||
};
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
inline uint32_t elf_hash( const unsigned char* name )
|
|
||||||
{
|
|
||||||
uint32_t h = 0;
|
|
||||||
uint32_t g = 0;
|
|
||||||
while ( *name != '\0' ) {
|
|
||||||
h = ( h << 4 ) + *name++;
|
|
||||||
g = h & 0xf0000000;
|
|
||||||
if ( g != 0 )
|
|
||||||
h ^= g >> 24;
|
|
||||||
h &= ~g;
|
|
||||||
}
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
inline uint32_t elf_gnu_hash( const unsigned char* s )
|
|
||||||
{
|
|
||||||
uint32_t h = 0x1505;
|
|
||||||
for ( unsigned char c = *s; c != '\0'; c = *++s )
|
|
||||||
h = ( h << 5 ) + h + c;
|
|
||||||
return h;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
inline std::string to_hex_string( uint64_t value )
|
|
||||||
{
|
|
||||||
std::string str;
|
|
||||||
|
|
||||||
while ( value ) {
|
|
||||||
if ( auto digit = value & 0xF; digit < 0xA ) {
|
|
||||||
str = char( '0' + digit ) + str;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
str = char( 'A' + digit - 0xA ) + str;
|
|
||||||
}
|
|
||||||
value >>= 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
return "0x" + str;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
inline void adjust_stream_size( std::ostream& stream, std::streamsize offset )
|
|
||||||
{
|
|
||||||
stream.seekp( 0, std::ios_base::end );
|
|
||||||
if ( stream.tellp() < offset ) {
|
|
||||||
std::streamsize size = offset - stream.tellp();
|
|
||||||
stream.write( std::string( size_t( size ), '\0' ).c_str(), size );
|
|
||||||
}
|
|
||||||
stream.seekp( offset );
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consumers should write an implementation of this class and pass an instance of it to the ELFIO::elfio constructor.
|
|
||||||
*/
|
|
||||||
class compression_interface
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~compression_interface() = default;
|
|
||||||
/**
|
|
||||||
* decompresses a compressed section
|
|
||||||
*
|
|
||||||
* @param data the buffer of compressed data
|
|
||||||
* @param endianness_convertor pointer to an endianness_convertor instance, used to convert numbers to/from the target endianness.
|
|
||||||
* @param compressed_size the size of the data buffer, in bytes
|
|
||||||
* @param decompressed_size a reference to a variable where the decompressed buffer size will be stored.
|
|
||||||
* @returns a smart pointer to the decompressed data.
|
|
||||||
*/
|
|
||||||
virtual std::unique_ptr<char[]>
|
|
||||||
inflate( const char* data,
|
|
||||||
const endianess_convertor* convertor,
|
|
||||||
Elf_Xword compressed_size,
|
|
||||||
Elf_Xword& uncompressed_size ) const = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* compresses a section
|
|
||||||
*
|
|
||||||
* @param data the buffer of uncompressed data
|
|
||||||
* @param endianness_convertor pointer to an endianness_convertor instance, used to convert numbers to/from the target endianness.
|
|
||||||
* @param decompressed_size the size of the data buffer, in bytes
|
|
||||||
* @param compressed_size a reference to a variable where the compressed buffer size will be stored.
|
|
||||||
* @returns a smart pointer to the compressed data.
|
|
||||||
*/
|
|
||||||
virtual std::unique_ptr<char[]>
|
|
||||||
deflate( const char* data,
|
|
||||||
const endianess_convertor* convertor,
|
|
||||||
Elf_Xword decompressed_size,
|
|
||||||
Elf_Xword& compressed_size ) const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_UTILS_HPP
|
|
@ -1 +0,0 @@
|
|||||||
#define ELFIO_VERSION "3.12"
|
|
@ -1,179 +0,0 @@
|
|||||||
/*
|
|
||||||
Copyright (C) 2001-present by Serge Lamikhov-Center
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in
|
|
||||||
all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
||||||
THE SOFTWARE.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ELFIO_VERSYM_HPP
|
|
||||||
#define ELFIO_VERSYM_HPP
|
|
||||||
|
|
||||||
namespace ELFIO {
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S> class versym_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
explicit versym_section_accessor_template( S* section )
|
|
||||||
: versym_section( section )
|
|
||||||
{
|
|
||||||
if ( section != nullptr ) {
|
|
||||||
entries_num = decltype( entries_num )( section->get_size() /
|
|
||||||
sizeof( Elf_Half ) );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word get_entries_num() const
|
|
||||||
{
|
|
||||||
if ( versym_section ) {
|
|
||||||
return entries_num;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_entry( Elf_Word no, Elf_Half& value ) const
|
|
||||||
{
|
|
||||||
if ( versym_section && ( no < get_entries_num() ) ) {
|
|
||||||
value = ( (Elf_Half*)versym_section->get_data() )[no];
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool modify_entry( Elf_Word no, Elf_Half value )
|
|
||||||
{
|
|
||||||
if ( versym_section && ( no < get_entries_num() ) ) {
|
|
||||||
( (Elf_Half*)versym_section->get_data() )[no] = value;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool add_entry( Elf_Half value )
|
|
||||||
{
|
|
||||||
if ( !versym_section ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
versym_section->append_data( (const char*)&value, sizeof( Elf_Half ) );
|
|
||||||
++entries_num;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
S* versym_section = nullptr;
|
|
||||||
Elf_Word entries_num = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
using versym_section_accessor = versym_section_accessor_template<section>;
|
|
||||||
using const_versym_section_accessor =
|
|
||||||
versym_section_accessor_template<const section>;
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
template <class S> class versym_r_section_accessor_template
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
versym_r_section_accessor_template( const elfio& elf_file,
|
|
||||||
S* versym_r_section )
|
|
||||||
: elf_file( elf_file ), versym_r_section( versym_r_section ),
|
|
||||||
entries_num( 0 )
|
|
||||||
{
|
|
||||||
// Find .dynamic section
|
|
||||||
const section* dynamic_section = elf_file.sections[".dynamic"];
|
|
||||||
|
|
||||||
if ( dynamic_section == nullptr ) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const_dynamic_section_accessor dynamic_section_acc( elf_file,
|
|
||||||
dynamic_section );
|
|
||||||
Elf_Xword dyn_sec_num = dynamic_section_acc.get_entries_num();
|
|
||||||
for ( Elf_Xword i = 0; i < dyn_sec_num; ++i ) {
|
|
||||||
Elf_Xword tag;
|
|
||||||
Elf_Xword value;
|
|
||||||
std::string str;
|
|
||||||
|
|
||||||
if ( dynamic_section_acc.get_entry( i, tag, value, str ) &&
|
|
||||||
tag == DT_VERNEEDNUM ) {
|
|
||||||
entries_num = (Elf_Word)value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
Elf_Word get_entries_num() const { return entries_num; }
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
bool get_entry( Elf_Word no,
|
|
||||||
Elf_Half& version,
|
|
||||||
std::string& file_name,
|
|
||||||
Elf_Word& hash,
|
|
||||||
Elf_Half& flags,
|
|
||||||
Elf_Half& other,
|
|
||||||
std::string& dep_name ) const
|
|
||||||
{
|
|
||||||
if ( versym_r_section == nullptr || ( no >= get_entries_num() ) ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const_string_section_accessor string_section_acc(
|
|
||||||
elf_file.sections[versym_r_section->get_link()] );
|
|
||||||
|
|
||||||
Elfxx_Verneed* verneed = (Elfxx_Verneed*)versym_r_section->get_data();
|
|
||||||
Elfxx_Vernaux* veraux =
|
|
||||||
(Elfxx_Vernaux*)( (char*)verneed + verneed->vn_aux );
|
|
||||||
for ( Elf_Word i = 0; i < no; ++i ) {
|
|
||||||
verneed = (Elfxx_Verneed*)( (char*)verneed + verneed->vn_next );
|
|
||||||
veraux = (Elfxx_Vernaux*)( (char*)verneed + verneed->vn_aux );
|
|
||||||
}
|
|
||||||
|
|
||||||
version = verneed->vn_version;
|
|
||||||
file_name = string_section_acc.get_string( verneed->vn_file );
|
|
||||||
hash = veraux->vna_hash;
|
|
||||||
flags = veraux->vna_flags;
|
|
||||||
other = veraux->vna_other;
|
|
||||||
dep_name = string_section_acc.get_string( veraux->vna_name );
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
|
||||||
private:
|
|
||||||
const elfio& elf_file;
|
|
||||||
S* versym_r_section = nullptr;
|
|
||||||
Elf_Word entries_num = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
using versym_r_section_accessor = versym_r_section_accessor_template<section>;
|
|
||||||
using const_versym_r_section_accessor =
|
|
||||||
versym_r_section_accessor_template<const section>;
|
|
||||||
|
|
||||||
} // namespace ELFIO
|
|
||||||
|
|
||||||
#endif // ELFIO_VERSYM_HPP
|
|
@ -1,163 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include "elna/source/optimizer.hpp"
|
|
||||||
|
|
||||||
namespace elna::riscv
|
|
||||||
{
|
|
||||||
enum class address_t
|
|
||||||
{
|
|
||||||
text,
|
|
||||||
high20,
|
|
||||||
lower12i
|
|
||||||
};
|
|
||||||
|
|
||||||
struct reference
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
std::size_t offset;
|
|
||||||
address_t target;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class x_register : std::uint8_t
|
|
||||||
{
|
|
||||||
zero = 0,
|
|
||||||
ra = 1,
|
|
||||||
sp = 2,
|
|
||||||
gp = 3,
|
|
||||||
tp = 4,
|
|
||||||
t0 = 5,
|
|
||||||
t1 = 6,
|
|
||||||
t2 = 7,
|
|
||||||
s0 = 8,
|
|
||||||
s1 = 9,
|
|
||||||
a0 = 10,
|
|
||||||
a1 = 11,
|
|
||||||
a2 = 12,
|
|
||||||
a3 = 13,
|
|
||||||
a4 = 14,
|
|
||||||
a5 = 15,
|
|
||||||
a6 = 16,
|
|
||||||
a7 = 17,
|
|
||||||
s2 = 18,
|
|
||||||
s3 = 19,
|
|
||||||
s4 = 20,
|
|
||||||
s5 = 21,
|
|
||||||
s6 = 22,
|
|
||||||
s7 = 23,
|
|
||||||
s8 = 24,
|
|
||||||
s9 = 25,
|
|
||||||
s10 = 26,
|
|
||||||
s11 = 27,
|
|
||||||
t3 = 28,
|
|
||||||
t4 = 29,
|
|
||||||
t5 = 30,
|
|
||||||
t6 = 31,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class funct3_t : std::uint8_t
|
|
||||||
{
|
|
||||||
addi = 0b000,
|
|
||||||
slti = 0b001,
|
|
||||||
sltiu = 0b011,
|
|
||||||
andi = 0b111,
|
|
||||||
ori = 0b110,
|
|
||||||
xori = 0b100,
|
|
||||||
slli = 0b000,
|
|
||||||
srli = 0b101,
|
|
||||||
srai = 0b101,
|
|
||||||
add = 0b000,
|
|
||||||
slt = 0b010,
|
|
||||||
sltu = 0b011,
|
|
||||||
_and = 0b111,
|
|
||||||
_or = 0b110,
|
|
||||||
_xor = 0b100,
|
|
||||||
sll = 0b001,
|
|
||||||
srl = 0b101,
|
|
||||||
sub = 0b000,
|
|
||||||
sra = 0b101,
|
|
||||||
beq = 0b000,
|
|
||||||
bne = 0b001,
|
|
||||||
blt = 0b100,
|
|
||||||
bltu = 0b110,
|
|
||||||
bge = 0b101,
|
|
||||||
bgeu = 0b111,
|
|
||||||
fence = 0b000,
|
|
||||||
fenceI = 0b001,
|
|
||||||
csrrw = 0b001,
|
|
||||||
csrrs = 0b010,
|
|
||||||
csrrc = 0b011,
|
|
||||||
csrrwi = 0b101,
|
|
||||||
csrrsi = 0b110,
|
|
||||||
csrrci = 0b111,
|
|
||||||
priv = 0b000,
|
|
||||||
sb = 0b000,
|
|
||||||
sh = 0b001,
|
|
||||||
sw = 0b010,
|
|
||||||
lb = 0b000,
|
|
||||||
lh = 0b001,
|
|
||||||
lw = 0b010,
|
|
||||||
lbu = 0b100,
|
|
||||||
lhu = 0b101,
|
|
||||||
jalr = 0b000,
|
|
||||||
mul = 0b000,
|
|
||||||
mulh = 0b001,
|
|
||||||
mulhsu = 0b010,
|
|
||||||
mulhu = 0b011,
|
|
||||||
div = 0b100,
|
|
||||||
divu = 0b101,
|
|
||||||
rem = 0b110,
|
|
||||||
remu = 0b111
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class funct12_t : std::uint8_t
|
|
||||||
{
|
|
||||||
ecall = 0b000000000000,
|
|
||||||
ebreak = 0b000000000001,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class funct7_t : std::uint8_t
|
|
||||||
{
|
|
||||||
none = 0,
|
|
||||||
sub = 0b0100000,
|
|
||||||
muldiv = 0b0000001
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class base_opcode : std::uint8_t
|
|
||||||
{
|
|
||||||
opImm = 0b0010011,
|
|
||||||
lui = 0b0110111,
|
|
||||||
auipc = 0b0010111,
|
|
||||||
op = 0b0110011,
|
|
||||||
jal = 0b1101111,
|
|
||||||
jalr = 0b1100111,
|
|
||||||
branch = 0b1100011,
|
|
||||||
load = 0b0000011,
|
|
||||||
store = 0b0100011,
|
|
||||||
miscMem = 0b0001111,
|
|
||||||
system = 0b1110011,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct instruction
|
|
||||||
{
|
|
||||||
instruction() = default; // NOP = addi x0, x0, 0.
|
|
||||||
instruction(base_opcode opcode);
|
|
||||||
|
|
||||||
instruction& i(x_register rd, funct3_t funct3, x_register rs1, std::uint32_t immediate);
|
|
||||||
instruction& s(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2);
|
|
||||||
instruction& b(std::uint32_t imm, funct3_t funct3, x_register rs1, x_register rs2);
|
|
||||||
instruction& r(x_register rd, funct3_t funct3, x_register rs1, x_register rs2,
|
|
||||||
funct7_t funct7 = funct7_t::none);
|
|
||||||
instruction& u(x_register rd, std::uint32_t imm);
|
|
||||||
instruction& j(x_register rd, std::uint32_t imm);
|
|
||||||
|
|
||||||
const std::byte *cbegin() const;
|
|
||||||
const std::byte *cend() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::uint32_t representation{ 0 };
|
|
||||||
};
|
|
||||||
|
|
||||||
std::vector<reference> generate(source::intermediate_code_generator generator,
|
|
||||||
std::shared_ptr<source::symbol_table> table, std::shared_ptr<source::writer<std::byte>> writer);
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "elna/source/parser.hpp"
|
|
||||||
#include "elna/source/optimizer.hpp"
|
|
||||||
#include <filesystem>
|
|
||||||
#include <elfio/elfio.hpp>
|
|
||||||
|
|
||||||
namespace elna::riscv
|
|
||||||
{
|
|
||||||
class elfio_writer final : public source::writer<std::byte>
|
|
||||||
{
|
|
||||||
ELFIO::section *text;
|
|
||||||
ELFIO::symbol_section_accessor symbol_accessor;
|
|
||||||
ELFIO::string_section_accessor string_accessor;
|
|
||||||
|
|
||||||
public:
|
|
||||||
elfio_writer(ELFIO::section *text, ELFIO::symbol_section_accessor symbol_accessor,
|
|
||||||
ELFIO::string_section_accessor string_accessor);
|
|
||||||
|
|
||||||
std::size_t sink(const std::string& label, const std::byte *data, std::size_t size) override;
|
|
||||||
void sink(const std::string& label) override;
|
|
||||||
std::size_t size() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Searches the content by label and returns its index or -1 when the
|
|
||||||
* label does not exist.
|
|
||||||
*
|
|
||||||
* \param symbol_accessor Object accessor.
|
|
||||||
* \param needle Label name.
|
|
||||||
* \return Data index.
|
|
||||||
*/
|
|
||||||
std::ptrdiff_t lookup(ELFIO::symbol_section_accessor symbol_accessor, const std::string& label);
|
|
||||||
|
|
||||||
void riscv32_elf(source::program *ast, source::intermediate_code_generator intermediate_code_generator,
|
|
||||||
std::shared_ptr<source::symbol_table> table, const std::filesystem::path& out_file);
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <filesystem>
|
|
||||||
#include "elna/source/result.hpp"
|
|
||||||
|
|
||||||
namespace elna::cli
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Formats and prints the given error.
|
|
||||||
*
|
|
||||||
* \param compile_error The error to print.
|
|
||||||
*/
|
|
||||||
void print_error(const std::unique_ptr<source::error>& compile_error);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints the given errors to the standard output.
|
|
||||||
*
|
|
||||||
* \param begin Pointer to the first error.
|
|
||||||
* \param end Pointer pass the last error.
|
|
||||||
*/
|
|
||||||
template<typename I>
|
|
||||||
void print_errors(I begin, I end)
|
|
||||||
{
|
|
||||||
std::for_each(begin, end, &print_error);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compiles \a in_file and writes the generated code into \a out_file.
|
|
||||||
*
|
|
||||||
* \param in_file Input file.
|
|
||||||
* \param out_file Output file.
|
|
||||||
*
|
|
||||||
* \return Exit status.
|
|
||||||
*/
|
|
||||||
int compile(const std::filesystem::path& in_file, const std::filesystem::path& out_file);
|
|
||||||
}
|
|
@ -1,212 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <optional>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
#include "elna/source/result.hpp"
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Union type representing a single token.
|
|
||||||
*/
|
|
||||||
struct token
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Token type.
|
|
||||||
*/
|
|
||||||
enum class type : std::uint16_t
|
|
||||||
{
|
|
||||||
dot,
|
|
||||||
number,
|
|
||||||
boolean,
|
|
||||||
term_operator,
|
|
||||||
let,
|
|
||||||
identifier,
|
|
||||||
equals,
|
|
||||||
var,
|
|
||||||
semicolon,
|
|
||||||
left_paren,
|
|
||||||
right_paren,
|
|
||||||
comma,
|
|
||||||
factor_operator,
|
|
||||||
eof,
|
|
||||||
begin,
|
|
||||||
end,
|
|
||||||
assignment,
|
|
||||||
colon,
|
|
||||||
when,
|
|
||||||
then,
|
|
||||||
loop,
|
|
||||||
_do,
|
|
||||||
procedure,
|
|
||||||
comparison_operator,
|
|
||||||
hat,
|
|
||||||
at
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of the token value.
|
|
||||||
*/
|
|
||||||
union value
|
|
||||||
{
|
|
||||||
value();
|
|
||||||
value(std::int32_t value);
|
|
||||||
value(const std::string& value);
|
|
||||||
~value();
|
|
||||||
|
|
||||||
std::nullptr_t nil;
|
|
||||||
std::int32_t number;
|
|
||||||
std::string identifier;
|
|
||||||
};
|
|
||||||
|
|
||||||
token(type of, elna::source::position position);
|
|
||||||
token(type of, std::int32_t value, const elna::source::position position);
|
|
||||||
token(type of, const std::string& value, const elna::source::position position);
|
|
||||||
token(type of, value&& value, const elna::source::position position);
|
|
||||||
token(const token& that);
|
|
||||||
token(token&& that);
|
|
||||||
~token();
|
|
||||||
|
|
||||||
token& operator=(const token& that);
|
|
||||||
token& operator=(token&& that);
|
|
||||||
|
|
||||||
type of() const noexcept;
|
|
||||||
const std::string& identifier() const;
|
|
||||||
std::int32_t number() const;
|
|
||||||
const elna::source::position& position() const noexcept;
|
|
||||||
|
|
||||||
std::string to_string() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
type m_type;
|
|
||||||
value m_value{};
|
|
||||||
elna::source::position m_position;
|
|
||||||
|
|
||||||
bool has_identifier() const noexcept;
|
|
||||||
bool is_numeric() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class unexpected_character final : public error
|
|
||||||
{
|
|
||||||
std::string character;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param character Unexpected character.
|
|
||||||
* \param path Source file name.
|
|
||||||
* \param position Unexpected token position.
|
|
||||||
*/
|
|
||||||
unexpected_character(const std::string& character, const std::filesystem::path& path,
|
|
||||||
const source::position position);
|
|
||||||
|
|
||||||
std::string what() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class unexpected_token final : public error
|
|
||||||
{
|
|
||||||
token m_token;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param token Unexpected token.
|
|
||||||
* \param path Source file name.
|
|
||||||
*/
|
|
||||||
unexpected_token(const token& token, const std::filesystem::path& path);
|
|
||||||
|
|
||||||
std::string what() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct lexer
|
|
||||||
{
|
|
||||||
lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path);
|
|
||||||
lexer(const lexer&) = delete;
|
|
||||||
lexer(lexer&&) = default;
|
|
||||||
|
|
||||||
lexer& operator=(const lexer&) = delete;
|
|
||||||
lexer& operator=(lexer&&) = default;
|
|
||||||
|
|
||||||
lexer& operator++();
|
|
||||||
const token& operator*() const;
|
|
||||||
const token *operator->() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function never fails and returns \ref token::type::eof at the
|
|
||||||
* end of the token stream.
|
|
||||||
*
|
|
||||||
* \return Current token.
|
|
||||||
*/
|
|
||||||
const token& current() const noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \param token_type Token type.
|
|
||||||
* \return Whether the current token is \a token_type.
|
|
||||||
*/
|
|
||||||
bool current(const token::type token_type) const noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds an \ref unexpected_token error to the error list.
|
|
||||||
*
|
|
||||||
* \param expected The token was expected.
|
|
||||||
*/
|
|
||||||
void add_error(const token& expected);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expects the current token to be \a token_type. In this case returns
|
|
||||||
* this token and advances to the next token in the stream.
|
|
||||||
*
|
|
||||||
* \param token_type Expected token type.
|
|
||||||
* \return Current token.
|
|
||||||
*/
|
|
||||||
std::optional<std::reference_wrapper<const token>> advance(const token::type token_type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns that follows the current token. If the current token is
|
|
||||||
* \ref token::type::eof, returns it.
|
|
||||||
*
|
|
||||||
* \return The token that follows the current one.
|
|
||||||
*/
|
|
||||||
const token& look_ahead() const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tells whether the token following the current one is \a token_type.
|
|
||||||
*
|
|
||||||
* \param token_type Token type.
|
|
||||||
* \return Whether the next token is \a token_type.
|
|
||||||
*/
|
|
||||||
bool look_ahead(const token::type token_type) const;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Skips one token if it is of type \a token_type. Adds an
|
|
||||||
* \ref unexpected_token error otherwise.
|
|
||||||
*
|
|
||||||
* \param token_type The token type was expected.
|
|
||||||
* \return Whether the current token was \a token_type.
|
|
||||||
*/
|
|
||||||
bool skip(const token::type token_type);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets produced errors.
|
|
||||||
*
|
|
||||||
* \return Produced error list.
|
|
||||||
*/
|
|
||||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::vector<token> tokens;
|
|
||||||
std::vector<token>::const_iterator iterator;
|
|
||||||
std::list<std::unique_ptr<error>> m_errors;
|
|
||||||
std::filesystem::path source_file;
|
|
||||||
token eof;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Splits the source text into tokens.
|
|
||||||
*
|
|
||||||
* \param path Source file location.
|
|
||||||
* \return Tokens or error.
|
|
||||||
*/
|
|
||||||
elna::source::result<lexer> tokenize(const std::filesystem::path& path);
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <unordered_map>
|
|
||||||
#include "elna/source/parser.hpp"
|
|
||||||
#include "elna/source/symbol_table.hpp"
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
enum class quadruple_operator
|
|
||||||
{
|
|
||||||
start,
|
|
||||||
stop,
|
|
||||||
add,
|
|
||||||
sub,
|
|
||||||
mul,
|
|
||||||
div,
|
|
||||||
eq,
|
|
||||||
neq,
|
|
||||||
lt,
|
|
||||||
ge,
|
|
||||||
gt,
|
|
||||||
le,
|
|
||||||
load,
|
|
||||||
ref,
|
|
||||||
beqz,
|
|
||||||
j,
|
|
||||||
label,
|
|
||||||
assign,
|
|
||||||
param,
|
|
||||||
call
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Single instruction representation.
|
|
||||||
*/
|
|
||||||
struct quadruple
|
|
||||||
{
|
|
||||||
quadruple(const quadruple_operator operation, std::shared_ptr<operand> operand1 = nullptr,
|
|
||||||
std::shared_ptr<operand> operand2 = nullptr, std::shared_ptr<operand> operand3 = nullptr);
|
|
||||||
|
|
||||||
quadruple_operator operation() const noexcept;
|
|
||||||
std::shared_ptr<operand> operand1();
|
|
||||||
std::shared_ptr<operand> operand2();
|
|
||||||
std::shared_ptr<operand> operand3();
|
|
||||||
|
|
||||||
private:
|
|
||||||
quadruple_operator m_operation;
|
|
||||||
std::shared_ptr<operand> m_operand1;
|
|
||||||
std::shared_ptr<operand> m_operand2;
|
|
||||||
std::shared_ptr<operand> m_operand3;
|
|
||||||
};
|
|
||||||
|
|
||||||
class intermediate_code final
|
|
||||||
{
|
|
||||||
std::vector<quadruple> instructions;
|
|
||||||
std::uint32_t m_variable_counter;
|
|
||||||
std::uint32_t m_label_counter;
|
|
||||||
|
|
||||||
public:
|
|
||||||
intermediate_code();
|
|
||||||
|
|
||||||
void emplace_back(const quadruple_operator operation, std::shared_ptr<operand> operand1 = nullptr,
|
|
||||||
std::shared_ptr<operand> operand2 = nullptr, std::shared_ptr<operand> operand3 = nullptr);
|
|
||||||
void clear();
|
|
||||||
|
|
||||||
std::vector<quadruple>::iterator begin();
|
|
||||||
std::vector<quadruple>::iterator end();
|
|
||||||
|
|
||||||
std::int32_t variable_counter() const noexcept;
|
|
||||||
std::int32_t increment_variable() noexcept;
|
|
||||||
std::int32_t label_counter() const noexcept;
|
|
||||||
std::int32_t increment_label() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class intermediate_code_generator final : public empty_visitor
|
|
||||||
{
|
|
||||||
std::unordered_map<std::string, intermediate_code> code;
|
|
||||||
intermediate_code current;
|
|
||||||
std::shared_ptr<symbol_table> table;
|
|
||||||
|
|
||||||
quadruple_operator convert(const binary_operator operation) const;
|
|
||||||
quadruple_operator convert(const unary_operator operation) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
intermediate_code_generator(std::shared_ptr<symbol_table> table);
|
|
||||||
|
|
||||||
std::unordered_map<std::string, intermediate_code>::iterator begin();
|
|
||||||
std::unordered_map<std::string, intermediate_code>::iterator end();
|
|
||||||
|
|
||||||
void visit(declaration *declaration) override;
|
|
||||||
void visit(constant_definition *definition) override;
|
|
||||||
void visit(procedure_definition *definition) override;
|
|
||||||
void visit(call_statement *statement) override;
|
|
||||||
void visit(assign_statement *statement) override;
|
|
||||||
void visit(if_statement *statement) override;
|
|
||||||
void visit(while_statement *statement) override;
|
|
||||||
void visit(block *block) override;
|
|
||||||
void visit(program *program) override;
|
|
||||||
void visit(type_expression *variable) override;
|
|
||||||
void visit(variable_expression *variable) override;
|
|
||||||
void visit(binary_expression *expression) override;
|
|
||||||
void visit(unary_expression *expression) override;
|
|
||||||
void visit(integer_literal *number) override;
|
|
||||||
void visit(boolean_literal *number) override;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,526 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <elna/source/lexer.hpp>
|
|
||||||
#include "elna/source/types.hpp"
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
enum class binary_operator
|
|
||||||
{
|
|
||||||
sum,
|
|
||||||
subtraction,
|
|
||||||
multiplication,
|
|
||||||
division,
|
|
||||||
equals,
|
|
||||||
not_equals,
|
|
||||||
less,
|
|
||||||
greater,
|
|
||||||
less_equal,
|
|
||||||
greater_equal
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class unary_operator
|
|
||||||
{
|
|
||||||
reference,
|
|
||||||
dereference
|
|
||||||
};
|
|
||||||
|
|
||||||
class declaration;
|
|
||||||
class constant_definition;
|
|
||||||
class procedure_definition;
|
|
||||||
class call_statement;
|
|
||||||
class compound_statement;
|
|
||||||
class assign_statement;
|
|
||||||
class if_statement;
|
|
||||||
class while_statement;
|
|
||||||
class block;
|
|
||||||
class program;
|
|
||||||
class binary_expression;
|
|
||||||
class unary_expression;
|
|
||||||
class type_expression;
|
|
||||||
class variable_expression;
|
|
||||||
class integer_literal;
|
|
||||||
class boolean_literal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface for AST visitors.
|
|
||||||
*/
|
|
||||||
struct parser_visitor
|
|
||||||
{
|
|
||||||
virtual void visit(declaration *) = 0;
|
|
||||||
virtual void visit(constant_definition *) = 0;
|
|
||||||
virtual void visit(procedure_definition *) = 0;
|
|
||||||
virtual void visit(call_statement *) = 0;
|
|
||||||
virtual void visit(compound_statement *) = 0;
|
|
||||||
virtual void visit(assign_statement *) = 0;
|
|
||||||
virtual void visit(if_statement *) = 0;
|
|
||||||
virtual void visit(while_statement *) = 0;
|
|
||||||
virtual void visit(block *) = 0;
|
|
||||||
virtual void visit(program *) = 0;
|
|
||||||
virtual void visit(binary_expression *) = 0;
|
|
||||||
virtual void visit(unary_expression *) = 0;
|
|
||||||
virtual void visit(type_expression *) = 0;
|
|
||||||
virtual void visit(variable_expression *) = 0;
|
|
||||||
virtual void visit(integer_literal *) = 0;
|
|
||||||
virtual void visit(boolean_literal *) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A visitor which visits all nodes but does nothing.
|
|
||||||
*/
|
|
||||||
struct empty_visitor : parser_visitor
|
|
||||||
{
|
|
||||||
virtual void visit(declaration *declaration) override;
|
|
||||||
virtual void visit(constant_definition *definition) override;
|
|
||||||
virtual void visit(procedure_definition *definition) override;
|
|
||||||
virtual void visit(call_statement *statement) override;
|
|
||||||
virtual void visit(compound_statement *statement) override;
|
|
||||||
virtual void visit(assign_statement *statement) override;
|
|
||||||
virtual void visit(if_statement *) override;
|
|
||||||
virtual void visit(while_statement *) override;
|
|
||||||
virtual void visit(block *block) override;
|
|
||||||
virtual void visit(program *program) override;
|
|
||||||
virtual void visit(binary_expression *expression) override;
|
|
||||||
virtual void visit(unary_expression *expression) override;
|
|
||||||
virtual void visit(type_expression *variable) override;
|
|
||||||
virtual void visit(variable_expression *variable) override;
|
|
||||||
virtual void visit(integer_literal *number) override;
|
|
||||||
virtual void visit(boolean_literal *boolean) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Operand representing a subexpression in the 3 address code.
|
|
||||||
*/
|
|
||||||
struct operand
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~operand() noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct integer_operand final : public operand
|
|
||||||
{
|
|
||||||
std::int32_t m_value;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit integer_operand(const std::int32_t value);
|
|
||||||
|
|
||||||
std::int32_t value() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class variable_operand final : public operand
|
|
||||||
{
|
|
||||||
std::string m_name;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit variable_operand(const std::string& name);
|
|
||||||
|
|
||||||
const std::string& name() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct temporary_variable final : public operand
|
|
||||||
{
|
|
||||||
std::size_t m_counter;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit temporary_variable(const std::size_t counter);
|
|
||||||
|
|
||||||
std::size_t counter() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct label_operand final : public operand
|
|
||||||
{
|
|
||||||
std::size_t m_counter;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit label_operand(const std::size_t counter);
|
|
||||||
|
|
||||||
std::size_t counter() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AST node.
|
|
||||||
*/
|
|
||||||
class node
|
|
||||||
{
|
|
||||||
const struct position source_position;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
*/
|
|
||||||
explicit node(const position position);
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~node() noexcept = default;
|
|
||||||
virtual void accept(parser_visitor *) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \return Node position in the source code.
|
|
||||||
*/
|
|
||||||
const struct position& position() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class statement : public node
|
|
||||||
{
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
*/
|
|
||||||
explicit statement(const struct position position);
|
|
||||||
};
|
|
||||||
|
|
||||||
class expression : public node
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::shared_ptr<operand> place;
|
|
||||||
std::shared_ptr<const type> data_type;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
*/
|
|
||||||
explicit expression(const struct position position);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Symbol definition.
|
|
||||||
*/
|
|
||||||
class definition : public node
|
|
||||||
{
|
|
||||||
std::string m_identifier;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Constructs a definition identified by some name.
|
|
||||||
*
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param identifier Definition name.
|
|
||||||
*/
|
|
||||||
definition(const struct position position, const std::string& identifier);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \return Definition name.
|
|
||||||
*/
|
|
||||||
std::string& identifier() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Expression defining a composed type like pointer or an array.
|
|
||||||
*/
|
|
||||||
class type_expression : public node
|
|
||||||
{
|
|
||||||
std::string m_base;
|
|
||||||
bool m_pointer{ false };
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param name Type name.
|
|
||||||
* \param is_pointer Whether it is a pointer type.
|
|
||||||
*/
|
|
||||||
type_expression(const struct position position, const std::string& name, const bool is_pointer = false);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \return Name of the base type.
|
|
||||||
*/
|
|
||||||
const std::string& base() const noexcept;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \return Whether the type is a pointer.
|
|
||||||
*/
|
|
||||||
bool is_pointer() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Variable declaration.
|
|
||||||
*/
|
|
||||||
class declaration : public definition
|
|
||||||
{
|
|
||||||
std::unique_ptr<type_expression> m_type;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Constructs a declaration with a name and a type.
|
|
||||||
*
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param identifier Definition name.
|
|
||||||
* \param type Declared type.
|
|
||||||
*/
|
|
||||||
declaration(const struct position position, const std::string& identifier,
|
|
||||||
std::unique_ptr<type_expression>&& type);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
type_expression& type() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant definition.
|
|
||||||
*/
|
|
||||||
class constant_definition : public definition
|
|
||||||
{
|
|
||||||
std::unique_ptr<integer_literal> m_body;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param identifier Constant name.
|
|
||||||
* \param body Constant value.
|
|
||||||
*/
|
|
||||||
constant_definition(const struct position position, const std::string& identifier,
|
|
||||||
std::unique_ptr<integer_literal>&& body);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
integer_literal& body();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Procedure definition.
|
|
||||||
*/
|
|
||||||
class procedure_definition : public definition
|
|
||||||
{
|
|
||||||
std::unique_ptr<block> m_body;
|
|
||||||
std::vector<std::unique_ptr<declaration>> m_parameters;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param identifier Procedure name.
|
|
||||||
* \param body Procedure body.
|
|
||||||
*/
|
|
||||||
procedure_definition(const struct position position, const std::string& identifier,
|
|
||||||
std::unique_ptr<block>&& body);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
block& body();
|
|
||||||
std::vector<std::unique_ptr<declaration>>& parameters() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Call statement.
|
|
||||||
*/
|
|
||||||
class call_statement : public statement
|
|
||||||
{
|
|
||||||
std::string m_name;
|
|
||||||
std::vector<std::unique_ptr<expression>> m_arguments;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param name Callable's name.
|
|
||||||
*/
|
|
||||||
call_statement(const struct position position, const std::string& name);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
std::string& name() noexcept;
|
|
||||||
std::vector<std::unique_ptr<expression>>& arguments() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class compound_statement : public statement
|
|
||||||
{
|
|
||||||
std::vector<std::unique_ptr<statement>> m_statements;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit compound_statement(const struct position position);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<statement>>& statements();
|
|
||||||
};
|
|
||||||
|
|
||||||
class assign_statement : public statement
|
|
||||||
{
|
|
||||||
std::string m_lvalue;
|
|
||||||
std::unique_ptr<expression> m_rvalue;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param lvalue Left-hand side.
|
|
||||||
* \param rvalue Assigned expression.
|
|
||||||
*/
|
|
||||||
assign_statement(const struct position position, const std::string& lvalue,
|
|
||||||
std::unique_ptr<expression>&& rvalue);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
std::string& lvalue() noexcept;
|
|
||||||
expression& rvalue();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If-statement.
|
|
||||||
*/
|
|
||||||
class if_statement : public statement
|
|
||||||
{
|
|
||||||
std::unique_ptr<expression> m_prerequisite;
|
|
||||||
std::unique_ptr<statement> m_body;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param prerequisite Condition.
|
|
||||||
* \param body Statement executed if the condition is met.
|
|
||||||
*/
|
|
||||||
if_statement(const struct position position, std::unique_ptr<expression>&& prerequisite,
|
|
||||||
std::unique_ptr<statement>&& body);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
expression& prerequisite();
|
|
||||||
statement& body();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* While-statement.
|
|
||||||
*/
|
|
||||||
class while_statement : public statement
|
|
||||||
{
|
|
||||||
std::unique_ptr<expression> m_prerequisite;
|
|
||||||
std::unique_ptr<statement> m_body;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param position Source code position.
|
|
||||||
* \param prerequisite Condition.
|
|
||||||
* \param body Statement executed while the condition is met.
|
|
||||||
*/
|
|
||||||
while_statement(const struct position position, std::unique_ptr<expression>&& prerequisite,
|
|
||||||
std::unique_ptr<statement>&& body);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
expression& prerequisite();
|
|
||||||
statement& body();
|
|
||||||
};
|
|
||||||
|
|
||||||
class block : public node
|
|
||||||
{
|
|
||||||
std::unique_ptr<statement> m_body;
|
|
||||||
std::vector<std::unique_ptr<definition>> m_definitions;
|
|
||||||
std::vector<std::unique_ptr<declaration>> m_declarations;
|
|
||||||
|
|
||||||
public:
|
|
||||||
block(const struct position position, std::vector<std::unique_ptr<definition>>&& definitions,
|
|
||||||
std::vector<std::unique_ptr<declaration>>&& declarations,
|
|
||||||
std::unique_ptr<statement>&& body);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
statement& body();
|
|
||||||
std::vector<std::unique_ptr<definition>>& definitions() noexcept;
|
|
||||||
std::vector<std::unique_ptr<declaration>>& declarations() noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class program : public block
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
program(const struct position position, std::vector<std::unique_ptr<definition>>&& definitions,
|
|
||||||
std::vector<std::unique_ptr<declaration>>&& declarations,
|
|
||||||
std::unique_ptr<statement>&& body);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
class integer_literal : public expression
|
|
||||||
{
|
|
||||||
std::int32_t m_number;
|
|
||||||
|
|
||||||
public:
|
|
||||||
integer_literal(const struct position position, const std::int32_t value);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
std::int32_t number() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class boolean_literal : public expression
|
|
||||||
{
|
|
||||||
bool m_boolean;
|
|
||||||
|
|
||||||
public:
|
|
||||||
boolean_literal(const struct position position, const bool value);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
bool boolean() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class variable_expression : public expression
|
|
||||||
{
|
|
||||||
std::string m_name;
|
|
||||||
|
|
||||||
public:
|
|
||||||
variable_expression(const struct position position, const std::string& name);
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
|
|
||||||
const std::string& name() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class binary_expression : public expression
|
|
||||||
{
|
|
||||||
std::unique_ptr<expression> m_lhs;
|
|
||||||
std::unique_ptr<expression> m_rhs;
|
|
||||||
binary_operator m_operator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
binary_expression(const struct position position, std::unique_ptr<expression>&& lhs,
|
|
||||||
std::unique_ptr<expression>&& rhs, const unsigned char operation);
|
|
||||||
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
expression& lhs();
|
|
||||||
expression& rhs();
|
|
||||||
binary_operator operation() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class unary_expression : public expression
|
|
||||||
{
|
|
||||||
std::unique_ptr<expression> m_operand;
|
|
||||||
unary_operator m_operator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
unary_expression(const struct position position, std::unique_ptr<expression>&& operand,
|
|
||||||
const unsigned char operation);
|
|
||||||
|
|
||||||
virtual void accept(parser_visitor *visitor) override;
|
|
||||||
expression& operand();
|
|
||||||
unary_operator operation() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
class parser
|
|
||||||
{
|
|
||||||
std::unique_ptr<expression> parse_unary_expression();
|
|
||||||
std::unique_ptr<expression> parse_factor();
|
|
||||||
std::unique_ptr<expression> parse_term();
|
|
||||||
std::unique_ptr<expression> parse_expression();
|
|
||||||
std::unique_ptr<expression> parse_condition();
|
|
||||||
std::unique_ptr<constant_definition> parse_constant_definition();
|
|
||||||
std::unique_ptr<procedure_definition> parse_procedure_definition();
|
|
||||||
std::unique_ptr<type_expression> parse_type_expression();
|
|
||||||
std::unique_ptr<declaration> parse_declaration();
|
|
||||||
std::unique_ptr<statement> parse_statement();
|
|
||||||
std::unique_ptr<call_statement> parse_call_statement();
|
|
||||||
std::unique_ptr<compound_statement> parse_compound_statement();
|
|
||||||
std::unique_ptr<assign_statement> parse_assign_statement();
|
|
||||||
std::unique_ptr<if_statement> parse_if_statement();
|
|
||||||
std::unique_ptr<while_statement> parse_while_statement();
|
|
||||||
std::vector<std::unique_ptr<constant_definition>> parse_constant_definitions();
|
|
||||||
std::vector<std::unique_ptr<procedure_definition>> parse_procedure_definitions();
|
|
||||||
std::vector<std::unique_ptr<declaration>> parse_declarations();
|
|
||||||
std::unique_ptr<block> parse_block();
|
|
||||||
|
|
||||||
lexer iterator;
|
|
||||||
|
|
||||||
public:
|
|
||||||
parser(lexer&& tokens);
|
|
||||||
parser(const parser&) = delete;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses a source text.
|
|
||||||
*
|
|
||||||
* \return Parsed program or nothing if an error occurred.
|
|
||||||
*/
|
|
||||||
std::unique_ptr<program> parse();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets produced errors.
|
|
||||||
*
|
|
||||||
* \return Produced error list.
|
|
||||||
*/
|
|
||||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstddef>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <variant>
|
|
||||||
#include <list>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <type_traits>
|
|
||||||
#include "elna/source/types.hpp"
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Position in the source text.
|
|
||||||
*/
|
|
||||||
struct position
|
|
||||||
{
|
|
||||||
/// Line.
|
|
||||||
std::size_t line = 1;
|
|
||||||
|
|
||||||
/// Column.
|
|
||||||
std::size_t column = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A compilation error consists of an error message and position.
|
|
||||||
*/
|
|
||||||
class error
|
|
||||||
{
|
|
||||||
position m_position;
|
|
||||||
std::filesystem::path m_path;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Constructs an error.
|
|
||||||
*
|
|
||||||
* \param path Source file name.
|
|
||||||
* \param position Error position in the source text.
|
|
||||||
*/
|
|
||||||
error(const std::filesystem::path& path, const position position);
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ~error() noexcept = default;
|
|
||||||
|
|
||||||
/// Error text.
|
|
||||||
virtual std::string what() const = 0;
|
|
||||||
|
|
||||||
/// Error line in the source text.
|
|
||||||
std::size_t line() const noexcept;
|
|
||||||
|
|
||||||
/// Error column in the source text.
|
|
||||||
std::size_t column() const noexcept;
|
|
||||||
|
|
||||||
/// Source file name.
|
|
||||||
const std::filesystem::path& path() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct result
|
|
||||||
{
|
|
||||||
using E = std::list<std::unique_ptr<source::error>>;
|
|
||||||
|
|
||||||
template<typename... Args, typename = std::enable_if<std::is_constructible_v<T, Args...>>>
|
|
||||||
explicit result(std::in_place_t, Args&&... arguments)
|
|
||||||
: payload(std::in_place_type<T>, std::forward<Args>(arguments)...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit result(T&& result)
|
|
||||||
: payload(std::move(result))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename U, typename = std::enable_if<std::is_base_of_v<error, U>>>
|
|
||||||
explicit result(U&& first)
|
|
||||||
: payload(E())
|
|
||||||
{
|
|
||||||
errors().emplace_back(std::make_unique<U>(first));
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit result(E&& errors)
|
|
||||||
: payload(std::move(errors))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool has_errors() const noexcept
|
|
||||||
{
|
|
||||||
return std::holds_alternative<E>(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_success() const noexcept
|
|
||||||
{
|
|
||||||
return std::holds_alternative<T>(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
T& success()
|
|
||||||
{
|
|
||||||
return std::get<T>(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
E& errors()
|
|
||||||
{
|
|
||||||
return std::get<E>(payload);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::variant<T, E> payload;
|
|
||||||
};
|
|
||||||
|
|
||||||
class name_collision final : public error
|
|
||||||
{
|
|
||||||
position previous;
|
|
||||||
std::string name;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param name Symbol name.
|
|
||||||
* \param path Source file name.
|
|
||||||
* \param current Current symbol position.
|
|
||||||
* \param previous Position of the previously defined symbol.
|
|
||||||
*/
|
|
||||||
name_collision(const std::string& name, const std::filesystem::path& path,
|
|
||||||
const position current, const position previous);
|
|
||||||
|
|
||||||
std::string what() const override;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct type_mismatch final : public error
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Kind of the operation on the type.
|
|
||||||
*/
|
|
||||||
enum class operation
|
|
||||||
{
|
|
||||||
dereference,
|
|
||||||
argument,
|
|
||||||
arithmetic,
|
|
||||||
comparison,
|
|
||||||
condition,
|
|
||||||
assignment
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \param name Given type.
|
|
||||||
* \param kind Kind of the operation on the type.
|
|
||||||
* \param path Source file name.
|
|
||||||
* \param position Operation position.
|
|
||||||
*/
|
|
||||||
type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path,
|
|
||||||
const struct position position);
|
|
||||||
|
|
||||||
std::string what() const override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
std::shared_ptr<const type> got;
|
|
||||||
operation kind;
|
|
||||||
};
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
struct writer
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Writes data to the table and saves it under the given label.
|
|
||||||
*
|
|
||||||
* \param label Symbol name.
|
|
||||||
* \param data Data to be written.
|
|
||||||
* \param size Data size.
|
|
||||||
*
|
|
||||||
* \return New size of the table.
|
|
||||||
*/
|
|
||||||
virtual std::size_t sink(const std::string& label, const T *data, std::size_t size) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates an external symbol.
|
|
||||||
*/
|
|
||||||
virtual void sink(const std::string& label) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \return Actual size of the text section.
|
|
||||||
*/
|
|
||||||
virtual std::size_t size() const = 0;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,93 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "elna/source/parser.hpp"
|
|
||||||
#include "elna/source/symbol_table.hpp"
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
class name_analysis_visitor final : public empty_visitor
|
|
||||||
{
|
|
||||||
std::shared_ptr<symbol_table> table;
|
|
||||||
const std::filesystem::path filename;
|
|
||||||
std::list<std::unique_ptr<error>> m_errors;
|
|
||||||
const std::size_t pointer_size;
|
|
||||||
|
|
||||||
std::shared_ptr<const type> convert_declaration_type(const type_expression& ast_type) const;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param table Symbol table.
|
|
||||||
* \param path Source filename.
|
|
||||||
* \param target_pointer_size Pointer size on the target platform.
|
|
||||||
*/
|
|
||||||
name_analysis_visitor(std::shared_ptr<symbol_table> table, const std::filesystem::path& filename,
|
|
||||||
const std::size_t target_pointer_size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \return Collected errors.
|
|
||||||
*/
|
|
||||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
|
||||||
|
|
||||||
void visit(constant_definition *definition) override;
|
|
||||||
void visit(declaration *declaration) override;
|
|
||||||
void visit(program *program) override;
|
|
||||||
void visit(procedure_definition *procedure) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Visitor which allocates registers and stack space for variables and
|
|
||||||
* parameters.
|
|
||||||
*/
|
|
||||||
class allocator_visitor final : public empty_visitor
|
|
||||||
{
|
|
||||||
std::ptrdiff_t local_offset;
|
|
||||||
std::ptrdiff_t argument_offset;
|
|
||||||
std::shared_ptr<symbol_table> table;
|
|
||||||
|
|
||||||
public:
|
|
||||||
allocator_visitor(std::shared_ptr<symbol_table> table);
|
|
||||||
|
|
||||||
void visit(declaration *declaration) override;
|
|
||||||
void visit(program *program) override;
|
|
||||||
void visit(procedure_definition *procedure) override;
|
|
||||||
void visit(call_statement *statement) override;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This visitor performs the type checking.
|
|
||||||
*/
|
|
||||||
class type_analysis_visitor final : public empty_visitor
|
|
||||||
{
|
|
||||||
std::shared_ptr<symbol_table> table;
|
|
||||||
const std::filesystem::path filename;
|
|
||||||
const std::size_t pointer_size;
|
|
||||||
std::list<std::unique_ptr<error>> m_errors;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \param table Symbol table.
|
|
||||||
* \param path Source filename.
|
|
||||||
* \param target_pointer_size Pointer size on the target platform.
|
|
||||||
*/
|
|
||||||
type_analysis_visitor(std::shared_ptr<symbol_table> table, const std::filesystem::path& filename,
|
|
||||||
const std::size_t target_pointer_size);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \return Collected errors.
|
|
||||||
*/
|
|
||||||
const std::list<std::unique_ptr<error>>& errors() const noexcept;
|
|
||||||
|
|
||||||
void visit(program *program) override;
|
|
||||||
void visit(procedure_definition *procedure) override;
|
|
||||||
void visit(integer_literal *literal) override;
|
|
||||||
void visit(boolean_literal *literal) override;
|
|
||||||
void visit(variable_expression *expression) override;
|
|
||||||
void visit(unary_expression *expression) override;
|
|
||||||
void visit(binary_expression *expression) override;
|
|
||||||
void visit(call_statement *statement) override;
|
|
||||||
void visit(constant_definition *definition) override;
|
|
||||||
void visit(while_statement *statement) override;
|
|
||||||
void visit(if_statement *statement) override;
|
|
||||||
void visit(assign_statement *statement) override;
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,161 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <cstdint>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <string>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
class symbol_table;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generic language entity information.
|
|
||||||
*/
|
|
||||||
class info
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
virtual ~info() = 0;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
info();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type information.
|
|
||||||
*/
|
|
||||||
class type_info final : public info
|
|
||||||
{
|
|
||||||
std::shared_ptr<class type> m_type;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit type_info(const class type& type);
|
|
||||||
~type_info() override;
|
|
||||||
std::shared_ptr<const class type> type() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Information for a typed symbol.
|
|
||||||
*/
|
|
||||||
class typed_info : public info
|
|
||||||
{
|
|
||||||
std::shared_ptr<const class type> m_type;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
typed_info(std::shared_ptr<const class type> type);
|
|
||||||
|
|
||||||
public:
|
|
||||||
~typed_info() override;
|
|
||||||
|
|
||||||
std::shared_ptr<const class type> type() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constant information.
|
|
||||||
*/
|
|
||||||
class constant_info final : public typed_info
|
|
||||||
{
|
|
||||||
std::int32_t m_value;
|
|
||||||
|
|
||||||
public:
|
|
||||||
constant_info(std::shared_ptr<const class type> type, const std::int32_t value);
|
|
||||||
|
|
||||||
std::int32_t value() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Variable information.
|
|
||||||
*/
|
|
||||||
class variable_info final : public typed_info
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::ptrdiff_t offset{ 0 };
|
|
||||||
|
|
||||||
explicit variable_info(std::shared_ptr<const class type> type);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Procedure parameter information.
|
|
||||||
*/
|
|
||||||
class parameter_info final : public typed_info
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
std::ptrdiff_t offset{ 0 };
|
|
||||||
|
|
||||||
explicit parameter_info(std::shared_ptr<const class type> type);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Intrinsic and external procedure information.
|
|
||||||
*/
|
|
||||||
class intrinsic_info : public info
|
|
||||||
{
|
|
||||||
std::shared_ptr<const class procedure_type> m_type;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit intrinsic_info(const class procedure_type& type);
|
|
||||||
~intrinsic_info() override;
|
|
||||||
|
|
||||||
std::shared_ptr<const class procedure_type> type() const noexcept;
|
|
||||||
std::size_t parameter_stack_size() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Procedure information.
|
|
||||||
*/
|
|
||||||
class procedure_info final : public intrinsic_info
|
|
||||||
{
|
|
||||||
std::shared_ptr<symbol_table> local_table;
|
|
||||||
|
|
||||||
public:
|
|
||||||
std::size_t local_stack_size{ 0 };
|
|
||||||
std::size_t argument_stack_size{ 0 };
|
|
||||||
|
|
||||||
procedure_info(const class procedure_type& type, std::shared_ptr<symbol_table> outer_scope);
|
|
||||||
~procedure_info() override;
|
|
||||||
|
|
||||||
std::shared_ptr<symbol_table> scope();
|
|
||||||
std::size_t stack_size() const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Symbol table.
|
|
||||||
*/
|
|
||||||
class symbol_table
|
|
||||||
{
|
|
||||||
std::unordered_map<std::string, std::shared_ptr<info>> entries;
|
|
||||||
std::shared_ptr<symbol_table> outer_scope;
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Constructs a new symbol with an optional outer scope.
|
|
||||||
*
|
|
||||||
* \param scope Outer scope.
|
|
||||||
*/
|
|
||||||
explicit symbol_table(std::shared_ptr<symbol_table> scope = nullptr);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks for symbol in the table by name. Returns nullptr if the symbol
|
|
||||||
* can not be found.
|
|
||||||
*
|
|
||||||
* \param name Symbol name.
|
|
||||||
* \return Symbol from the table if found.
|
|
||||||
*/
|
|
||||||
std::shared_ptr<info> lookup(const std::string& name);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Registers new symbol.
|
|
||||||
*
|
|
||||||
* \param name Symbol name.
|
|
||||||
* \param entry Symbol information.
|
|
||||||
*/
|
|
||||||
void enter(const std::string& name, std::shared_ptr<info> entry);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the outer scope or nullptr if the this is the global scope.
|
|
||||||
*
|
|
||||||
* \return Outer scope.
|
|
||||||
*/
|
|
||||||
std::shared_ptr<symbol_table> scope();
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,99 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
/**
|
|
||||||
* Type representation.
|
|
||||||
*/
|
|
||||||
class type
|
|
||||||
{
|
|
||||||
const std::size_t byte_size;
|
|
||||||
|
|
||||||
protected:
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* \param byte_size The type size in bytes.
|
|
||||||
*/
|
|
||||||
explicit type(const std::size_t byte_size);
|
|
||||||
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* \return The type size in bytes.
|
|
||||||
*/
|
|
||||||
virtual std::size_t size() const noexcept;
|
|
||||||
|
|
||||||
friend bool operator==(const type& lhs, const type& rhs) noexcept;
|
|
||||||
friend bool operator!=(const type& lhs, const type& rhs) noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Built-in type representation.
|
|
||||||
*/
|
|
||||||
struct primitive_type : public type
|
|
||||||
{
|
|
||||||
/// Type name.
|
|
||||||
const std::string type_name;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* \param type_name Type name.
|
|
||||||
* \param byte_size The type size in bytes.
|
|
||||||
*/
|
|
||||||
primitive_type(const std::string& type_name, const std::size_t byte_size);
|
|
||||||
|
|
||||||
bool operator==(const primitive_type& that) const noexcept;
|
|
||||||
bool operator!=(const primitive_type& that) const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Typed pointer.
|
|
||||||
*/
|
|
||||||
struct pointer_type : public type
|
|
||||||
{
|
|
||||||
/// Pointer target type.
|
|
||||||
std::shared_ptr<const type> base_type;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* \param base_type Pointer target type.
|
|
||||||
* \param byte_size The type size in bytes.
|
|
||||||
*/
|
|
||||||
pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size);
|
|
||||||
|
|
||||||
bool operator==(const pointer_type& that) const noexcept;
|
|
||||||
bool operator!=(const pointer_type& that) const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type of a procedure.
|
|
||||||
*/
|
|
||||||
struct procedure_type : public type
|
|
||||||
{
|
|
||||||
/// Argument types.
|
|
||||||
std::vector<std::shared_ptr<const type>> arguments;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* \param arguments Argument types.
|
|
||||||
* \param byte_size Function pointer size.
|
|
||||||
*/
|
|
||||||
procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_size);
|
|
||||||
|
|
||||||
bool operator==(const procedure_type& that) const noexcept;
|
|
||||||
bool operator!=(const procedure_type& that) const noexcept;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool operator==(const type& lhs, const type& rhs) noexcept;
|
|
||||||
bool operator!=(const type& lhs, const type& rhs) noexcept;
|
|
||||||
|
|
||||||
inline const primitive_type boolean_type{ "Boolean", 1 };
|
|
||||||
inline const primitive_type int_type{ "Int", 4 };
|
|
||||||
}
|
|
322
lib/Language/Elna/Architecture/RiscV.hs
Normal file
322
lib/Language/Elna/Architecture/RiscV.hs
Normal file
@ -0,0 +1,322 @@
|
|||||||
|
module Language.Elna.Architecture.RiscV
|
||||||
|
( BaseOpcode(..)
|
||||||
|
, RelocationType(..)
|
||||||
|
, Funct3(..)
|
||||||
|
, Funct7(..)
|
||||||
|
, Funct12(..)
|
||||||
|
, Instruction(..)
|
||||||
|
, Type(..)
|
||||||
|
, XRegister(..)
|
||||||
|
, baseOpcode
|
||||||
|
, funct3
|
||||||
|
, funct12
|
||||||
|
, instruction
|
||||||
|
, xRegister
|
||||||
|
) where
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Builder as ByteString.Builder
|
||||||
|
import Data.Bits (Bits(..))
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Word (Word8, Word32)
|
||||||
|
|
||||||
|
data XRegister
|
||||||
|
= Zero
|
||||||
|
| RA
|
||||||
|
| SP
|
||||||
|
| GP
|
||||||
|
| TP
|
||||||
|
| T0
|
||||||
|
| T1
|
||||||
|
| T2
|
||||||
|
| S0
|
||||||
|
| S1
|
||||||
|
| A0
|
||||||
|
| A1
|
||||||
|
| A2
|
||||||
|
| A3
|
||||||
|
| A4
|
||||||
|
| A5
|
||||||
|
| A6
|
||||||
|
| A7
|
||||||
|
| S2
|
||||||
|
| S3
|
||||||
|
| S4
|
||||||
|
| S5
|
||||||
|
| S6
|
||||||
|
| S7
|
||||||
|
| S8
|
||||||
|
| S9
|
||||||
|
| S10
|
||||||
|
| S11
|
||||||
|
| T3
|
||||||
|
| T4
|
||||||
|
| T5
|
||||||
|
| T6
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data Funct3
|
||||||
|
= ADDI
|
||||||
|
| SLTI
|
||||||
|
| SLTIU
|
||||||
|
| ANDI
|
||||||
|
| ORI
|
||||||
|
| XORI
|
||||||
|
| SLLI
|
||||||
|
| SRLI
|
||||||
|
| SRAI
|
||||||
|
| ADD
|
||||||
|
| SLT
|
||||||
|
| SLTU
|
||||||
|
| AND
|
||||||
|
| OR
|
||||||
|
| XOR
|
||||||
|
| SLL
|
||||||
|
| SRL
|
||||||
|
| SUB
|
||||||
|
| SRA
|
||||||
|
| BEQ
|
||||||
|
| BNE
|
||||||
|
| BLT
|
||||||
|
| BLTU
|
||||||
|
| BGE
|
||||||
|
| BGEU
|
||||||
|
| FENCE
|
||||||
|
| FENCEI
|
||||||
|
| CSRRW
|
||||||
|
| CSRRS
|
||||||
|
| CSRRC
|
||||||
|
| CSRRWI
|
||||||
|
| CSRRSI
|
||||||
|
| CSRRCI
|
||||||
|
| PRIV
|
||||||
|
| SB
|
||||||
|
| SH
|
||||||
|
| SW
|
||||||
|
| LB
|
||||||
|
| LH
|
||||||
|
| LW
|
||||||
|
| LBU
|
||||||
|
| LHU
|
||||||
|
| JALR
|
||||||
|
| MUL
|
||||||
|
| MULH
|
||||||
|
| MULHSU
|
||||||
|
| MULHU
|
||||||
|
| DIV
|
||||||
|
| DIVU
|
||||||
|
| REM
|
||||||
|
| REMU
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data Funct12
|
||||||
|
= ECALL
|
||||||
|
| EBREAK
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
newtype Funct7 = Funct7
|
||||||
|
{ funct7 :: Word8
|
||||||
|
} deriving Eq
|
||||||
|
|
||||||
|
data BaseOpcode
|
||||||
|
= OpImm
|
||||||
|
| Lui
|
||||||
|
| Auipc
|
||||||
|
| Op
|
||||||
|
| Jal
|
||||||
|
| Jalr
|
||||||
|
| Branch
|
||||||
|
| Load
|
||||||
|
| Store
|
||||||
|
| MiscMem
|
||||||
|
| System
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data Type
|
||||||
|
= I XRegister Funct3 XRegister Word32
|
||||||
|
| S Word32 Funct3 XRegister XRegister
|
||||||
|
| B Word32 Funct3 XRegister XRegister
|
||||||
|
| R XRegister Funct3 XRegister XRegister Funct7
|
||||||
|
| U XRegister Word32
|
||||||
|
| J XRegister Word32
|
||||||
|
| Type XRegister Funct3 XRegister Funct12 -- Privileged.
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data RelocationType
|
||||||
|
= RLower12I XRegister Funct3 XRegister Text
|
||||||
|
| RLower12S Text Funct3 XRegister XRegister
|
||||||
|
| RHigher20 XRegister Text -- Type U.
|
||||||
|
| RBranch Text Funct3 XRegister XRegister -- Type B.
|
||||||
|
| RJal XRegister Text -- Type J.
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data Instruction
|
||||||
|
= BaseInstruction BaseOpcode Type
|
||||||
|
| RelocatableInstruction BaseOpcode RelocationType
|
||||||
|
| CallInstruction Text
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
xRegister :: XRegister -> Word8
|
||||||
|
xRegister Zero = 0
|
||||||
|
xRegister RA = 1
|
||||||
|
xRegister SP = 2
|
||||||
|
xRegister GP = 3
|
||||||
|
xRegister TP = 4
|
||||||
|
xRegister T0 = 5
|
||||||
|
xRegister T1 = 6
|
||||||
|
xRegister T2 = 7
|
||||||
|
xRegister S0 = 8
|
||||||
|
xRegister S1 = 9
|
||||||
|
xRegister A0 = 10
|
||||||
|
xRegister A1 = 11
|
||||||
|
xRegister A2 = 12
|
||||||
|
xRegister A3 = 13
|
||||||
|
xRegister A4 = 14
|
||||||
|
xRegister A5 = 15
|
||||||
|
xRegister A6 = 16
|
||||||
|
xRegister A7 = 17
|
||||||
|
xRegister S2 = 18
|
||||||
|
xRegister S3 = 19
|
||||||
|
xRegister S4 = 20
|
||||||
|
xRegister S5 = 21
|
||||||
|
xRegister S6 = 22
|
||||||
|
xRegister S7 = 23
|
||||||
|
xRegister S8 = 24
|
||||||
|
xRegister S9 = 25
|
||||||
|
xRegister S10 = 26
|
||||||
|
xRegister S11 = 27
|
||||||
|
xRegister T3 = 28
|
||||||
|
xRegister T4 = 29
|
||||||
|
xRegister T5 = 30
|
||||||
|
xRegister T6 = 31
|
||||||
|
|
||||||
|
funct3 :: Funct3 -> Word8
|
||||||
|
funct3 ADDI = 0b000
|
||||||
|
funct3 SLTI = 0b001
|
||||||
|
funct3 SLTIU = 0b011
|
||||||
|
funct3 ANDI = 0b111
|
||||||
|
funct3 ORI = 0b110
|
||||||
|
funct3 XORI = 0b100
|
||||||
|
funct3 SLLI = 0b000
|
||||||
|
funct3 SRLI = 0b101
|
||||||
|
funct3 SRAI = 0b101
|
||||||
|
funct3 ADD = 0b000
|
||||||
|
funct3 SLT = 0b010
|
||||||
|
funct3 SLTU = 0b011
|
||||||
|
funct3 AND = 0b111
|
||||||
|
funct3 OR = 0b110
|
||||||
|
funct3 XOR = 0b100
|
||||||
|
funct3 SLL = 0b001
|
||||||
|
funct3 SRL = 0b101
|
||||||
|
funct3 SUB = 0b000
|
||||||
|
funct3 SRA = 0b101
|
||||||
|
funct3 BEQ = 0b000
|
||||||
|
funct3 BNE = 0b001
|
||||||
|
funct3 BLT = 0b100
|
||||||
|
funct3 BLTU = 0b110
|
||||||
|
funct3 BGE = 0b101
|
||||||
|
funct3 BGEU = 0b111
|
||||||
|
funct3 FENCE = 0b000
|
||||||
|
funct3 FENCEI = 0b001
|
||||||
|
funct3 CSRRW = 0b001
|
||||||
|
funct3 CSRRS = 0b010
|
||||||
|
funct3 CSRRC = 0b011
|
||||||
|
funct3 CSRRWI = 0b101
|
||||||
|
funct3 CSRRSI = 0b110
|
||||||
|
funct3 CSRRCI = 0b111
|
||||||
|
funct3 PRIV = 0b000
|
||||||
|
funct3 SB = 0b000
|
||||||
|
funct3 SH = 0b001
|
||||||
|
funct3 SW = 0b010
|
||||||
|
funct3 LB = 0b000
|
||||||
|
funct3 LH = 0b001
|
||||||
|
funct3 LW = 0b010
|
||||||
|
funct3 LBU = 0b100
|
||||||
|
funct3 LHU = 0b101
|
||||||
|
funct3 JALR = 0b000
|
||||||
|
funct3 MUL = 0b000
|
||||||
|
funct3 MULH = 0b001
|
||||||
|
funct3 MULHSU = 0b010
|
||||||
|
funct3 MULHU = 0b011
|
||||||
|
funct3 DIV = 0b100
|
||||||
|
funct3 DIVU = 0b101
|
||||||
|
funct3 REM = 0b110
|
||||||
|
funct3 REMU = 0b111
|
||||||
|
|
||||||
|
funct12 :: Funct12 -> Word8
|
||||||
|
funct12 ECALL = 0b000000000000
|
||||||
|
funct12 EBREAK = 0b000000000001
|
||||||
|
|
||||||
|
baseOpcode :: BaseOpcode -> Word8
|
||||||
|
baseOpcode OpImm = 0b0010011
|
||||||
|
baseOpcode Lui = 0b0110111
|
||||||
|
baseOpcode Auipc = 0b0010111
|
||||||
|
baseOpcode Op = 0b0110011
|
||||||
|
baseOpcode Jal = 0b1101111
|
||||||
|
baseOpcode Jalr = 0b1100111
|
||||||
|
baseOpcode Branch = 0b1100011
|
||||||
|
baseOpcode Load = 0b0000011
|
||||||
|
baseOpcode Store = 0b0100011
|
||||||
|
baseOpcode MiscMem = 0b0001111
|
||||||
|
baseOpcode System = 0b1110011
|
||||||
|
|
||||||
|
type' :: Type -> Word32
|
||||||
|
type' (I rd funct3' rs1 immediate)
|
||||||
|
= (fromIntegral (xRegister rd) `shiftL` 7)
|
||||||
|
.|. (fromIntegral (funct3 funct3') `shiftL` 12)
|
||||||
|
.|. (fromIntegral (xRegister rs1) `shiftL` 15)
|
||||||
|
.|. (immediate `shiftL` 20);
|
||||||
|
type' (S immediate funct3' rs1 rs2)
|
||||||
|
= ((immediate .&. 0x1f) `shiftL` 7)
|
||||||
|
.|. (fromIntegral (funct3 funct3') `shiftL` 12)
|
||||||
|
.|. (fromIntegral (xRegister rs1) `shiftL` 15)
|
||||||
|
.|. (fromIntegral (xRegister rs2) `shiftL` 20)
|
||||||
|
.|. ((immediate .&. 0xfe0) `shiftL` 20)
|
||||||
|
type' (B immediate funct3' rs1 rs2)
|
||||||
|
= ((immediate .&. 0x800) `shiftR` 4)
|
||||||
|
.|. ((immediate .&. 0x1e) `shiftL` 7)
|
||||||
|
.|. (fromIntegral (funct3 funct3') `shiftL` 12)
|
||||||
|
.|. (fromIntegral (xRegister rs1) `shiftL` 15)
|
||||||
|
.|. (fromIntegral (xRegister rs2) `shiftL` 20)
|
||||||
|
.|. ((immediate .&. 0x7e0) `shiftL` 20)
|
||||||
|
.|. ((immediate .&. 0x1000) `shiftL` 19)
|
||||||
|
type' (R rd funct3' rs1 rs2 funct7')
|
||||||
|
= (fromIntegral (xRegister rd) `shiftL` 7)
|
||||||
|
.|. (fromIntegral (funct3 funct3') `shiftL` 12)
|
||||||
|
.|. (fromIntegral (xRegister rs1) `shiftL` 15)
|
||||||
|
.|. (fromIntegral (xRegister rs2) `shiftL` 20)
|
||||||
|
.|. (fromIntegral (funct7 funct7') `shiftL` 25);
|
||||||
|
type' (U rd immediate)
|
||||||
|
= (fromIntegral (xRegister rd) `shiftL` 7)
|
||||||
|
.|. (immediate `shiftL` 12)
|
||||||
|
type' (J rd immediate)
|
||||||
|
= (fromIntegral (xRegister rd) `shiftL` 7)
|
||||||
|
.|. (immediate .&. 0xff000)
|
||||||
|
.|. ((immediate .&. 0x800) `shiftL` 9)
|
||||||
|
.|. ((immediate .&. 0x7fe) `shiftL` 20)
|
||||||
|
.|. ((immediate .&. 0x100000) `shiftL` 11);
|
||||||
|
type' (Type rd funct3' rs1 funct12')
|
||||||
|
= (fromIntegral (xRegister rd) `shiftL` 7)
|
||||||
|
.|. (fromIntegral (funct3 funct3') `shiftL` 12)
|
||||||
|
.|. (fromIntegral (xRegister rs1) `shiftL` 15)
|
||||||
|
.|. (fromIntegral (funct12 funct12') `shiftL` 20);
|
||||||
|
|
||||||
|
relocationType :: RelocationType -> Word32
|
||||||
|
relocationType (RLower12I rd funct3' rs1 _) = type' $ I rd funct3' rs1 0
|
||||||
|
relocationType (RLower12S _ funct3' rs1 rs2) = type' $ S 0 funct3' rs1 rs2
|
||||||
|
relocationType (RHigher20 rd _) = type' $ U rd 0
|
||||||
|
relocationType (RBranch _ funct3' rs1 rs2) = type' $ B 0 funct3' rs1 rs2
|
||||||
|
relocationType (RJal rd _) = type' $ J rd 0
|
||||||
|
|
||||||
|
instruction :: Instruction -> ByteString.Builder.Builder
|
||||||
|
instruction = \case
|
||||||
|
(BaseInstruction base instructionType) -> go base $ type' instructionType
|
||||||
|
(RelocatableInstruction base instructionType) -> go base $ relocationType instructionType
|
||||||
|
(CallInstruction _) -> foldMap instruction
|
||||||
|
[ BaseInstruction Auipc $ U RA 0
|
||||||
|
, BaseInstruction Jalr $ I RA JALR RA 0
|
||||||
|
]
|
||||||
|
where
|
||||||
|
go base instructionType
|
||||||
|
= ByteString.Builder.word32LE
|
||||||
|
$ fromIntegral (baseOpcode base)
|
||||||
|
.|. instructionType
|
133
lib/Language/Elna/Backend/Allocator.hs
Normal file
133
lib/Language/Elna/Backend/Allocator.hs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
module Language.Elna.Backend.Allocator
|
||||||
|
( MachineConfiguration(..)
|
||||||
|
, Store(..)
|
||||||
|
, allocate
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import Data.Word (Word32)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import Language.Elna.Backend.Intermediate
|
||||||
|
( ProcedureQuadruples(..)
|
||||||
|
, Operand(..)
|
||||||
|
, Quadruple(..)
|
||||||
|
, Variable(..)
|
||||||
|
)
|
||||||
|
import Language.Elna.Location (Identifier(..))
|
||||||
|
import Control.Monad.Trans.Reader (ReaderT, asks, runReaderT)
|
||||||
|
import Control.Monad.Trans.State (State, runState)
|
||||||
|
import GHC.Records (HasField(..))
|
||||||
|
|
||||||
|
data Store r
|
||||||
|
= RegisterStore r
|
||||||
|
| StackStore Word32 r
|
||||||
|
|
||||||
|
newtype MachineConfiguration r = MachineConfiguration
|
||||||
|
{ temporaryRegisters :: [r]
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype Allocator r a = Allocator
|
||||||
|
{ runAllocator :: ReaderT (MachineConfiguration r) (State Word32) a
|
||||||
|
}
|
||||||
|
|
||||||
|
instance forall r. Functor (Allocator r)
|
||||||
|
where
|
||||||
|
fmap f = Allocator . fmap f . runAllocator
|
||||||
|
|
||||||
|
instance forall r. Applicative (Allocator r)
|
||||||
|
where
|
||||||
|
pure = Allocator . pure
|
||||||
|
(Allocator x) <*> (Allocator y) = Allocator $ x <*> y
|
||||||
|
|
||||||
|
instance forall r. Monad (Allocator r)
|
||||||
|
where
|
||||||
|
(Allocator allocator) >>= f = Allocator $ allocator >>= (runAllocator . f)
|
||||||
|
|
||||||
|
allocate
|
||||||
|
:: forall r
|
||||||
|
. MachineConfiguration r
|
||||||
|
-> HashMap Identifier (Vector (Quadruple Variable))
|
||||||
|
-> HashMap Identifier (ProcedureQuadruples (Store r))
|
||||||
|
allocate machineConfiguration = fmap function
|
||||||
|
where
|
||||||
|
function :: Vector (Quadruple Variable) -> ProcedureQuadruples (Store r)
|
||||||
|
function quadruples' = ProcedureQuadruples
|
||||||
|
{ quadruples = fst
|
||||||
|
$ flip runState 0
|
||||||
|
$ flip runReaderT machineConfiguration
|
||||||
|
$ runAllocator
|
||||||
|
$ mapM quadruple quadruples'
|
||||||
|
, stackSize = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
quadruple :: Quadruple Variable -> Allocator r (Quadruple (Store r))
|
||||||
|
quadruple = \case
|
||||||
|
StartQuadruple -> pure StartQuadruple
|
||||||
|
StopQuadruple -> pure StopQuadruple
|
||||||
|
ParameterQuadruple operand1 -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
pure $ ParameterQuadruple operand1'
|
||||||
|
CallQuadruple name count -> pure $ CallQuadruple name count
|
||||||
|
AddQuadruple operand1 operand2 variable -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
AddQuadruple operand1' operand2' <$> storeVariable variable
|
||||||
|
SubtractionQuadruple operand1 operand2 variable -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
SubtractionQuadruple operand1' operand2' <$> storeVariable variable
|
||||||
|
NegationQuadruple operand1 variable -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
NegationQuadruple operand1' <$> storeVariable variable
|
||||||
|
ProductQuadruple operand1 operand2 variable -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
ProductQuadruple operand1' operand2' <$> storeVariable variable
|
||||||
|
DivisionQuadruple operand1 operand2 variable -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
DivisionQuadruple operand1' operand2' <$> storeVariable variable
|
||||||
|
LabelQuadruple label -> pure $ LabelQuadruple label
|
||||||
|
GoToQuadruple label -> pure $ GoToQuadruple label
|
||||||
|
EqualQuadruple operand1 operand2 goToLabel -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
pure $ EqualQuadruple operand1' operand2' goToLabel
|
||||||
|
NonEqualQuadruple operand1 operand2 goToLabel -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
pure $ NonEqualQuadruple operand1' operand2' goToLabel
|
||||||
|
LessQuadruple operand1 operand2 goToLabel -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
pure $ LessQuadruple operand1' operand2' goToLabel
|
||||||
|
GreaterQuadruple operand1 operand2 goToLabel -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
pure $ GreaterQuadruple operand1' operand2' goToLabel
|
||||||
|
LessOrEqualQuadruple operand1 operand2 goToLabel -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
pure $ LessOrEqualQuadruple operand1' operand2' goToLabel
|
||||||
|
GreaterOrEqualQuadruple operand1 operand2 goToLabel -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
operand2' <- operand operand2
|
||||||
|
pure $ GreaterOrEqualQuadruple operand1' operand2' goToLabel
|
||||||
|
AssignQuadruple operand1 variable -> do
|
||||||
|
operand1' <- operand operand1
|
||||||
|
AssignQuadruple operand1' <$> storeVariable variable
|
||||||
|
|
||||||
|
operand :: Operand Variable -> Allocator r (Operand (Store r))
|
||||||
|
operand (IntOperand x) = pure $ IntOperand x
|
||||||
|
operand (VariableOperand variableOperand) =
|
||||||
|
VariableOperand <$> storeVariable variableOperand
|
||||||
|
|
||||||
|
storeVariable :: Variable -> Allocator r (Store r)
|
||||||
|
storeVariable (TempVariable index) = do
|
||||||
|
temporaryRegisters' <- Allocator $ asks $ getField @"temporaryRegisters"
|
||||||
|
pure $ RegisterStore
|
||||||
|
$ temporaryRegisters' !! fromIntegral index
|
||||||
|
storeVariable (LocalVariable index) = do
|
||||||
|
temporaryRegisters' <- Allocator $ asks $ getField @"temporaryRegisters"
|
||||||
|
pure $ RegisterStore
|
||||||
|
$ temporaryRegisters' !! pred (length temporaryRegisters' - fromIntegral index)
|
61
lib/Language/Elna/Backend/Intermediate.hs
Normal file
61
lib/Language/Elna/Backend/Intermediate.hs
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
module Language.Elna.Backend.Intermediate
|
||||||
|
( ProcedureQuadruples(..)
|
||||||
|
, Operand(..)
|
||||||
|
, Quadruple(..)
|
||||||
|
, Label(..)
|
||||||
|
, Variable(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Int (Int32)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import Data.Word (Word32)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as Text
|
||||||
|
|
||||||
|
newtype Label = Label { unLabel :: Text }
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Label
|
||||||
|
where
|
||||||
|
show (Label label) = '.' : Text.unpack label
|
||||||
|
|
||||||
|
data Variable = TempVariable Word32 | LocalVariable Word32
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Variable
|
||||||
|
where
|
||||||
|
show (LocalVariable variable) = '@' : show variable
|
||||||
|
show (TempVariable variable) = '$' : show variable
|
||||||
|
|
||||||
|
data Operand v
|
||||||
|
= IntOperand Int32
|
||||||
|
| VariableOperand v
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
data ProcedureQuadruples v = ProcedureQuadruples
|
||||||
|
{ quadruples :: Vector (Quadruple v)
|
||||||
|
, stackSize :: Word32
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
data Quadruple v
|
||||||
|
= StartQuadruple
|
||||||
|
| StopQuadruple
|
||||||
|
| ParameterQuadruple (Operand v)
|
||||||
|
| CallQuadruple Text Word32
|
||||||
|
| AddQuadruple (Operand v) (Operand v) v
|
||||||
|
| SubtractionQuadruple (Operand v) (Operand v) v
|
||||||
|
| NegationQuadruple (Operand v) v
|
||||||
|
| ProductQuadruple (Operand v) (Operand v) v
|
||||||
|
| DivisionQuadruple (Operand v) (Operand v) v
|
||||||
|
| GoToQuadruple Label
|
||||||
|
| AssignQuadruple (Operand v) v
|
||||||
|
{-| ArrayQuadruple Variable Operand Variable
|
||||||
|
| ArrayAssignQuadruple Operand Operand Variable -}
|
||||||
|
| LessOrEqualQuadruple (Operand v) (Operand v) Label
|
||||||
|
| GreaterOrEqualQuadruple (Operand v) (Operand v) Label
|
||||||
|
| GreaterQuadruple (Operand v) (Operand v) Label
|
||||||
|
| LessQuadruple (Operand v) (Operand v) Label
|
||||||
|
| NonEqualQuadruple (Operand v) (Operand v) Label
|
||||||
|
| EqualQuadruple (Operand v) (Operand v) Label
|
||||||
|
| LabelQuadruple Label
|
||||||
|
deriving (Eq, Show)
|
44
lib/Language/Elna/CommandLine.hs
Normal file
44
lib/Language/Elna/CommandLine.hs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
module Language.Elna.CommandLine
|
||||||
|
( CommandLine(..)
|
||||||
|
, commandLine
|
||||||
|
, execParser
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Options.Applicative
|
||||||
|
( Parser
|
||||||
|
, ParserInfo(..)
|
||||||
|
, argument
|
||||||
|
, execParser
|
||||||
|
, fullDesc
|
||||||
|
, help
|
||||||
|
, helper
|
||||||
|
, info
|
||||||
|
, long
|
||||||
|
, metavar
|
||||||
|
, optional
|
||||||
|
, progDesc
|
||||||
|
, short
|
||||||
|
, str
|
||||||
|
, strOption
|
||||||
|
)
|
||||||
|
import Control.Applicative ((<**>))
|
||||||
|
|
||||||
|
data CommandLine = CommandLine
|
||||||
|
{ input :: FilePath
|
||||||
|
, output :: Maybe FilePath
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
parser :: Parser CommandLine
|
||||||
|
parser = CommandLine
|
||||||
|
<$> argument str inFile
|
||||||
|
<*> optional (strOption outFile)
|
||||||
|
where
|
||||||
|
inFile = metavar "INFILE" <> help "Input file."
|
||||||
|
outFile = long "output"
|
||||||
|
<> short 'o'
|
||||||
|
<> metavar "OUTFILE"
|
||||||
|
<> help "Output file."
|
||||||
|
|
||||||
|
commandLine :: ParserInfo CommandLine
|
||||||
|
commandLine = info (parser <**> helper)
|
||||||
|
$ fullDesc <> progDesc "Elna compiler."
|
206
lib/Language/Elna/Frontend/AST.hs
Normal file
206
lib/Language/Elna/Frontend/AST.hs
Normal file
@ -0,0 +1,206 @@
|
|||||||
|
module Language.Elna.Frontend.AST
|
||||||
|
( Declaration(..)
|
||||||
|
, Identifier(..)
|
||||||
|
, Parameter(..)
|
||||||
|
, Program(..)
|
||||||
|
, Statement(..)
|
||||||
|
, TypeExpression(..)
|
||||||
|
, VariableDeclaration(..)
|
||||||
|
, VariableAccess(..)
|
||||||
|
, Condition(..)
|
||||||
|
, Expression(..)
|
||||||
|
, Literal(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Char (chr)
|
||||||
|
import Data.Int (Int32)
|
||||||
|
import Data.List (intercalate)
|
||||||
|
import Data.Word (Word8)
|
||||||
|
import Language.Elna.Location (Identifier(..), showArrayType)
|
||||||
|
import Numeric (showHex)
|
||||||
|
import Data.Bifunctor (Bifunctor(bimap))
|
||||||
|
|
||||||
|
newtype Program = Program [Declaration]
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Program
|
||||||
|
where
|
||||||
|
show (Program declarations) = unlines (show <$> declarations)
|
||||||
|
|
||||||
|
data Declaration
|
||||||
|
= ProcedureDeclaration Identifier [Parameter] [VariableDeclaration] [Statement]
|
||||||
|
| TypeDefinition Identifier TypeExpression
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Declaration
|
||||||
|
where
|
||||||
|
show (TypeDefinition identifier typeExpression) =
|
||||||
|
concat ["type ", show identifier, " = ", show typeExpression, ";"]
|
||||||
|
show (ProcedureDeclaration procedureName parameters variables body)
|
||||||
|
= "proc " <> show procedureName <> showParameters parameters <> " {\n"
|
||||||
|
<> unlines ((" " <>) . show <$> variables)
|
||||||
|
<> unlines ((" " <>) . show <$> body)
|
||||||
|
<> "}"
|
||||||
|
|
||||||
|
data Parameter = Parameter Identifier TypeExpression Bool
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Parameter
|
||||||
|
where
|
||||||
|
show (Parameter identifier typeName ref) = concat
|
||||||
|
[ if ref then "ref " else ""
|
||||||
|
, show identifier, ": ", show typeName
|
||||||
|
]
|
||||||
|
|
||||||
|
showParameters :: [Parameter] -> String
|
||||||
|
showParameters parameters =
|
||||||
|
"(" <> intercalate ", " (show <$> parameters) <> ")"
|
||||||
|
|
||||||
|
data TypeExpression
|
||||||
|
= NamedType Identifier
|
||||||
|
| ArrayType Literal TypeExpression
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show TypeExpression
|
||||||
|
where
|
||||||
|
show (NamedType typeName) = show typeName
|
||||||
|
show (ArrayType elementCount typeName) =
|
||||||
|
showArrayType elementCount typeName
|
||||||
|
|
||||||
|
data Statement
|
||||||
|
= EmptyStatement
|
||||||
|
| IfStatement Condition Statement (Maybe Statement)
|
||||||
|
| AssignmentStatement VariableAccess Expression
|
||||||
|
-- | WhileStatement Condition Statement
|
||||||
|
| CompoundStatement [Statement]
|
||||||
|
| CallStatement Identifier [Expression]
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Statement
|
||||||
|
where
|
||||||
|
show EmptyStatement = ";"
|
||||||
|
show (IfStatement condition if' else') = concat
|
||||||
|
[ "if (", show condition, ") "
|
||||||
|
, show if'
|
||||||
|
, maybe "" ((<> " else ") . show) else'
|
||||||
|
]
|
||||||
|
show (AssignmentStatement lhs rhs) =
|
||||||
|
concat [show lhs, " := ", show rhs, ";"]
|
||||||
|
{-show (WhileStatement expression statement) =
|
||||||
|
concat ["while (", show expression, ") ", show statement, ";"]-}
|
||||||
|
show (CompoundStatement statements) =
|
||||||
|
concat ["{\n", unlines (show <$> statements), " }"]
|
||||||
|
show (CallStatement name parameters) = show name <> "("
|
||||||
|
<> intercalate ", " (show <$> parameters) <> ")"
|
||||||
|
|
||||||
|
data VariableDeclaration =
|
||||||
|
VariableDeclaration Identifier TypeExpression
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data Literal
|
||||||
|
= DecimalLiteral Int32
|
||||||
|
| HexadecimalLiteral Int32
|
||||||
|
| CharacterLiteral Word8
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Literal
|
||||||
|
where
|
||||||
|
show (DecimalLiteral integer) = show integer
|
||||||
|
show (HexadecimalLiteral integer) = '0' : 'x' : showHex integer ""
|
||||||
|
show (CharacterLiteral character) =
|
||||||
|
'\'' : chr (fromEnum character) : ['\'']
|
||||||
|
|
||||||
|
instance Ord Literal
|
||||||
|
where
|
||||||
|
compare x y = compare (int32Literal x) (int32Literal y)
|
||||||
|
|
||||||
|
instance Num Literal
|
||||||
|
where
|
||||||
|
x + y = DecimalLiteral $ int32Literal x + int32Literal y
|
||||||
|
x * y = DecimalLiteral $ int32Literal x * int32Literal y
|
||||||
|
abs (DecimalLiteral x) = DecimalLiteral $ abs x
|
||||||
|
abs (HexadecimalLiteral x) = HexadecimalLiteral $ abs x
|
||||||
|
abs (CharacterLiteral x) = CharacterLiteral $ abs x
|
||||||
|
negate (DecimalLiteral x) = DecimalLiteral $ negate x
|
||||||
|
negate (HexadecimalLiteral x) = HexadecimalLiteral $ negate x
|
||||||
|
negate (CharacterLiteral x) = CharacterLiteral $ negate x
|
||||||
|
signum (DecimalLiteral x) = DecimalLiteral $ signum x
|
||||||
|
signum (HexadecimalLiteral x) = HexadecimalLiteral $ signum x
|
||||||
|
signum (CharacterLiteral x) = CharacterLiteral $ signum x
|
||||||
|
fromInteger = DecimalLiteral . fromInteger
|
||||||
|
|
||||||
|
instance Real Literal
|
||||||
|
where
|
||||||
|
toRational (DecimalLiteral integer) = toRational integer
|
||||||
|
toRational (HexadecimalLiteral integer) = toRational integer
|
||||||
|
toRational (CharacterLiteral integer) = toRational integer
|
||||||
|
|
||||||
|
instance Enum Literal
|
||||||
|
where
|
||||||
|
toEnum = DecimalLiteral . fromIntegral
|
||||||
|
fromEnum = fromEnum . int32Literal
|
||||||
|
|
||||||
|
instance Integral Literal
|
||||||
|
where
|
||||||
|
toInteger = toInteger . int32Literal
|
||||||
|
quotRem x y = bimap DecimalLiteral DecimalLiteral
|
||||||
|
$ quotRem (int32Literal x) (int32Literal y)
|
||||||
|
|
||||||
|
int32Literal :: Literal -> Int32
|
||||||
|
int32Literal (DecimalLiteral integer) = integer
|
||||||
|
int32Literal (HexadecimalLiteral integer) = integer
|
||||||
|
int32Literal (CharacterLiteral integer) = fromIntegral integer
|
||||||
|
|
||||||
|
instance Show VariableDeclaration
|
||||||
|
where
|
||||||
|
show (VariableDeclaration identifier typeExpression) =
|
||||||
|
concat ["var ", show identifier, ": " <> show typeExpression, ";"]
|
||||||
|
|
||||||
|
data Expression
|
||||||
|
= LiteralExpression Literal
|
||||||
|
| SumExpression Expression Expression
|
||||||
|
| SubtractionExpression Expression Expression
|
||||||
|
| NegationExpression Expression
|
||||||
|
| ProductExpression Expression Expression
|
||||||
|
| DivisionExpression Expression Expression
|
||||||
|
| VariableExpression VariableAccess
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Expression
|
||||||
|
where
|
||||||
|
show (LiteralExpression literal) = show literal
|
||||||
|
show (SumExpression lhs rhs) = concat [show lhs, " + ", show rhs]
|
||||||
|
show (SubtractionExpression lhs rhs) = concat [show lhs, " - ", show rhs]
|
||||||
|
show (NegationExpression negation) = '-' : show negation
|
||||||
|
show (ProductExpression lhs rhs) = concat [show lhs, " * ", show rhs]
|
||||||
|
show (DivisionExpression lhs rhs) = concat [show lhs, " / ", show rhs]
|
||||||
|
show (VariableExpression variable) = show variable
|
||||||
|
|
||||||
|
newtype VariableAccess
|
||||||
|
= VariableAccess Identifier
|
||||||
|
-- | ArrayAccess VariableAccess Expression
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show VariableAccess
|
||||||
|
where
|
||||||
|
show (VariableAccess variableName) = show variableName
|
||||||
|
{- show (ArrayAccess arrayAccess elementIndex) =
|
||||||
|
concat [show arrayAccess, "[", show elementIndex, "]"] -}
|
||||||
|
|
||||||
|
data Condition
|
||||||
|
= EqualCondition Expression Expression
|
||||||
|
| NonEqualCondition Expression Expression
|
||||||
|
| LessCondition Expression Expression
|
||||||
|
| GreaterCondition Expression Expression
|
||||||
|
| LessOrEqualCondition Expression Expression
|
||||||
|
| GreaterOrEqualCondition Expression Expression
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Condition
|
||||||
|
where
|
||||||
|
show (EqualCondition lhs rhs) = concat [show lhs, " = ", show rhs]
|
||||||
|
show (NonEqualCondition lhs rhs) = concat [show lhs, " # ", show rhs]
|
||||||
|
show (LessCondition lhs rhs) = concat [show lhs, " < ", show rhs]
|
||||||
|
show (GreaterCondition lhs rhs) = concat [show lhs, " > ", show rhs]
|
||||||
|
show (LessOrEqualCondition lhs rhs) = concat [show lhs, " <= ", show rhs]
|
||||||
|
show (GreaterOrEqualCondition lhs rhs) = concat [show lhs, " >= ", show rhs]
|
219
lib/Language/Elna/Frontend/NameAnalysis.hs
Normal file
219
lib/Language/Elna/Frontend/NameAnalysis.hs
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
module Language.Elna.Frontend.NameAnalysis
|
||||||
|
( nameAnalysis
|
||||||
|
, Error(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import qualified Data.List.NonEmpty as NonEmpty
|
||||||
|
import qualified Data.Vector as Vector
|
||||||
|
import qualified Language.Elna.Frontend.AST as AST
|
||||||
|
import qualified Language.Elna.Frontend.SymbolTable as SymbolTable
|
||||||
|
import Language.Elna.Frontend.SymbolTable
|
||||||
|
( SymbolTable
|
||||||
|
, Info(..)
|
||||||
|
, ParameterInfo(..)
|
||||||
|
)
|
||||||
|
import Control.Monad.Trans.Except (Except, runExcept, throwE)
|
||||||
|
import Data.Functor ((<&>))
|
||||||
|
import Language.Elna.Location (Identifier(..))
|
||||||
|
import Language.Elna.Frontend.Types (Type(..))
|
||||||
|
import Data.Foldable (traverse_)
|
||||||
|
import Control.Monad (foldM, unless)
|
||||||
|
|
||||||
|
data Error
|
||||||
|
= UndefinedTypeError Identifier
|
||||||
|
| UnexpectedTypeInfoError Info
|
||||||
|
| IdentifierAlreadyDefinedError Identifier
|
||||||
|
| UndefinedSymbolError Identifier
|
||||||
|
| UnexpectedArrayByValue Identifier
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Error
|
||||||
|
where
|
||||||
|
show (UndefinedTypeError identifier) =
|
||||||
|
concat ["Type \"", show identifier, "\" is not defined"]
|
||||||
|
show (UnexpectedTypeInfoError info) = show info
|
||||||
|
<> " expected to be a type"
|
||||||
|
show (IdentifierAlreadyDefinedError identifier) =
|
||||||
|
concat ["The identifier \"", show identifier, "\" is already defined"]
|
||||||
|
show (UndefinedSymbolError identifier) =
|
||||||
|
concat ["Symbol \"", show identifier, "\" is not defined"]
|
||||||
|
show (UnexpectedArrayByValue identifier) = concat
|
||||||
|
[ "Array \""
|
||||||
|
, show identifier
|
||||||
|
, "\" cannot be passed by value, only by reference"
|
||||||
|
]
|
||||||
|
|
||||||
|
newtype NameAnalysis a = NameAnalysis
|
||||||
|
{ runNameAnalysis :: Except Error a
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Functor NameAnalysis
|
||||||
|
where
|
||||||
|
fmap f (NameAnalysis x) = NameAnalysis $ f <$> x
|
||||||
|
|
||||||
|
instance Applicative NameAnalysis
|
||||||
|
where
|
||||||
|
pure = NameAnalysis . pure
|
||||||
|
(NameAnalysis f) <*> (NameAnalysis x) = NameAnalysis $ f <*> x
|
||||||
|
|
||||||
|
instance Monad NameAnalysis
|
||||||
|
where
|
||||||
|
(NameAnalysis x) >>= f = NameAnalysis $ x >>= (runNameAnalysis . f)
|
||||||
|
|
||||||
|
nameAnalysis :: AST.Program -> Either Error SymbolTable
|
||||||
|
nameAnalysis = runExcept
|
||||||
|
. runNameAnalysis
|
||||||
|
. program SymbolTable.builtInSymbolTable
|
||||||
|
|
||||||
|
program :: SymbolTable -> AST.Program -> NameAnalysis SymbolTable
|
||||||
|
program symbolTable (AST.Program declarations) = do
|
||||||
|
globalTable <- foldM procedureDeclaration symbolTable declarations
|
||||||
|
foldM declaration globalTable declarations
|
||||||
|
|
||||||
|
procedureDeclaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
|
||||||
|
procedureDeclaration globalTable = \case
|
||||||
|
(AST.ProcedureDeclaration identifier parameters _ _)
|
||||||
|
-> mapM (parameter globalTable) parameters
|
||||||
|
>>= enterOrFail identifier
|
||||||
|
. ProcedureInfo SymbolTable.empty
|
||||||
|
. Vector.fromList
|
||||||
|
(AST.TypeDefinition identifier typeExpression)
|
||||||
|
-> dataType globalTable typeExpression
|
||||||
|
>>= enterOrFail identifier . SymbolTable.TypeInfo
|
||||||
|
where
|
||||||
|
enterOrFail identifier declarationInfo =
|
||||||
|
maybe (NameAnalysis $ throwE $ IdentifierAlreadyDefinedError identifier) pure
|
||||||
|
$ SymbolTable.enter identifier declarationInfo globalTable
|
||||||
|
|
||||||
|
declaration :: SymbolTable -> AST.Declaration -> NameAnalysis SymbolTable
|
||||||
|
declaration globalTable (AST.ProcedureDeclaration identifier parameters variables body) = do
|
||||||
|
variableInfo <- mapM (variableDeclaration globalTable) variables
|
||||||
|
parameterInfo <- mapM (parameterToVariableInfo globalTable) parameters
|
||||||
|
procedureTable <- fmap (SymbolTable.scope globalTable)
|
||||||
|
$ either (NameAnalysis . throwE . IdentifierAlreadyDefinedError . NonEmpty.head) pure
|
||||||
|
$ SymbolTable.fromList
|
||||||
|
$ parameterInfo <> variableInfo
|
||||||
|
traverse_ (statement procedureTable) body
|
||||||
|
pure $ SymbolTable.update (updater procedureTable) identifier globalTable
|
||||||
|
where
|
||||||
|
updater procedureTable (ProcedureInfo _ parameters') = Just
|
||||||
|
$ ProcedureInfo procedureTable parameters'
|
||||||
|
updater _ _ = Nothing
|
||||||
|
declaration globalTable (AST.TypeDefinition _ _) = pure globalTable
|
||||||
|
|
||||||
|
parameterToVariableInfo :: SymbolTable -> AST.Parameter -> NameAnalysis (Identifier, Info)
|
||||||
|
parameterToVariableInfo symbolTable (AST.Parameter identifier typeExpression isReferenceParameter')
|
||||||
|
= (identifier,) . VariableInfo isReferenceParameter'
|
||||||
|
<$> dataType symbolTable typeExpression
|
||||||
|
|
||||||
|
variableDeclaration :: SymbolTable -> AST.VariableDeclaration -> NameAnalysis (Identifier, Info)
|
||||||
|
variableDeclaration globalTable (AST.VariableDeclaration identifier typeExpression)
|
||||||
|
= (identifier,) . VariableInfo False
|
||||||
|
<$> dataType globalTable typeExpression
|
||||||
|
|
||||||
|
parameter :: SymbolTable -> AST.Parameter -> NameAnalysis ParameterInfo
|
||||||
|
parameter environmentSymbolTable (AST.Parameter identifier typeExpression isReferenceParameter') = do
|
||||||
|
parameterType <- dataType environmentSymbolTable typeExpression
|
||||||
|
case parameterType of
|
||||||
|
ArrayType _ _
|
||||||
|
| not isReferenceParameter' -> NameAnalysis
|
||||||
|
$ throwE $ UnexpectedArrayByValue identifier
|
||||||
|
_ ->
|
||||||
|
let parameterInfo = ParameterInfo
|
||||||
|
{ name = identifier
|
||||||
|
, type' = parameterType
|
||||||
|
, isReferenceParameter = isReferenceParameter'
|
||||||
|
}
|
||||||
|
in pure parameterInfo
|
||||||
|
|
||||||
|
dataType :: SymbolTable -> AST.TypeExpression -> NameAnalysis Type
|
||||||
|
dataType environmentSymbolTable (AST.NamedType baseType) = do
|
||||||
|
case SymbolTable.lookup baseType environmentSymbolTable of
|
||||||
|
Just baseInfo
|
||||||
|
| TypeInfo baseType' <- baseInfo -> pure baseType'
|
||||||
|
| otherwise -> NameAnalysis $ throwE $ UnexpectedTypeInfoError baseInfo
|
||||||
|
_ -> NameAnalysis $ throwE $ UndefinedTypeError baseType
|
||||||
|
dataType environmentSymbolTable (AST.ArrayType arraySize baseType) =
|
||||||
|
dataType environmentSymbolTable baseType <&> ArrayType (fromIntegral arraySize)
|
||||||
|
|
||||||
|
checkSymbol :: SymbolTable -> Identifier -> NameAnalysis ()
|
||||||
|
checkSymbol globalTable identifier
|
||||||
|
= unless (SymbolTable.member identifier globalTable)
|
||||||
|
$ NameAnalysis $ throwE
|
||||||
|
$ UndefinedSymbolError identifier
|
||||||
|
|
||||||
|
expression :: SymbolTable -> AST.Expression -> NameAnalysis ()
|
||||||
|
expression _ (AST.LiteralExpression _) = pure ()
|
||||||
|
expression globalTable (AST.SumExpression lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
expression globalTable (AST.SubtractionExpression lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
expression globalTable (AST.NegationExpression negation) =
|
||||||
|
expression globalTable negation
|
||||||
|
expression globalTable (AST.ProductExpression lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
expression globalTable (AST.DivisionExpression lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
expression globalTable (AST.VariableExpression variableExpression) =
|
||||||
|
variableAccess globalTable variableExpression
|
||||||
|
|
||||||
|
statement :: SymbolTable -> AST.Statement -> NameAnalysis ()
|
||||||
|
statement _ AST.EmptyStatement = pure ()
|
||||||
|
statement globalTable (AST.CallStatement name arguments)
|
||||||
|
= checkSymbol globalTable name
|
||||||
|
>> traverse_ (expression globalTable) arguments
|
||||||
|
statement globalTable (AST.CompoundStatement statements) =
|
||||||
|
traverse_ (statement globalTable) statements
|
||||||
|
statement globalTable (AST.IfStatement ifCondition ifStatement elseStatement)
|
||||||
|
= condition globalTable ifCondition
|
||||||
|
>> statement globalTable ifStatement
|
||||||
|
>> maybe (pure ()) (statement globalTable) elseStatement
|
||||||
|
statement globalTable (AST.AssignmentStatement lvalue rvalue)
|
||||||
|
= variableAccess globalTable lvalue
|
||||||
|
>> expression globalTable rvalue
|
||||||
|
--statement globalTable (AST.WhileStatement whileCondition loop)
|
||||||
|
-- = condition globalTable whileCondition
|
||||||
|
-- >> statement globalTable loop
|
||||||
|
|
||||||
|
condition :: SymbolTable -> AST.Condition -> NameAnalysis ()
|
||||||
|
condition globalTable (AST.EqualCondition lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
condition globalTable (AST.NonEqualCondition lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
condition globalTable (AST.LessCondition lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
condition globalTable (AST.GreaterCondition lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
condition globalTable (AST.LessOrEqualCondition lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
condition globalTable (AST.GreaterOrEqualCondition lhs rhs)
|
||||||
|
= expression globalTable lhs
|
||||||
|
>> expression globalTable rhs
|
||||||
|
|
||||||
|
variableAccess :: SymbolTable -> AST.VariableAccess -> NameAnalysis ()
|
||||||
|
variableAccess globalTable (AST.VariableAccess identifier) =
|
||||||
|
checkSymbol globalTable identifier
|
||||||
|
{- variableAccess globalTable (AST.ArrayAccess arrayExpression indexExpression)
|
||||||
|
= variableAccess globalTable arrayExpression
|
||||||
|
>> expression globalTable indexExpression
|
||||||
|
|
||||||
|
enter :: Identifier -> Info -> SymbolTable -> NameAnalysis SymbolTable
|
||||||
|
enter identifier info table
|
||||||
|
= maybe (identifierAlreadyDefinedError identifier) pure
|
||||||
|
$ SymbolTable.enter identifier info table
|
||||||
|
|
||||||
|
identifierAlreadyDefinedError :: Identifier -> NameAnalysis a
|
||||||
|
identifierAlreadyDefinedError = NameAnalysis
|
||||||
|
. lift
|
||||||
|
. throwE
|
||||||
|
. IdentifierAlreadyDefinedError
|
||||||
|
-}
|
223
lib/Language/Elna/Frontend/Parser.hs
Normal file
223
lib/Language/Elna/Frontend/Parser.hs
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
module Language.Elna.Frontend.Parser
|
||||||
|
( Parser
|
||||||
|
, programP
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad (void)
|
||||||
|
import Control.Monad.Combinators.Expr (Operator(..), makeExprParser)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as Text
|
||||||
|
import Data.Void (Void)
|
||||||
|
import Language.Elna.Frontend.AST
|
||||||
|
( Declaration(..)
|
||||||
|
, Identifier(..)
|
||||||
|
, Parameter(..)
|
||||||
|
, Program(..)
|
||||||
|
, Statement(..)
|
||||||
|
, TypeExpression(..)
|
||||||
|
, VariableDeclaration(..)
|
||||||
|
, VariableAccess(..)
|
||||||
|
, Condition(..)
|
||||||
|
, Expression(..)
|
||||||
|
, Literal(..)
|
||||||
|
)
|
||||||
|
import Text.Megaparsec
|
||||||
|
( Parsec
|
||||||
|
, (<?>)
|
||||||
|
, MonadParsec(..)
|
||||||
|
, eof
|
||||||
|
, optional
|
||||||
|
, between
|
||||||
|
, sepBy
|
||||||
|
, choice
|
||||||
|
)
|
||||||
|
import qualified Text.Megaparsec.Char.Lexer as Lexer
|
||||||
|
import Text.Megaparsec.Char
|
||||||
|
( alphaNumChar
|
||||||
|
, char
|
||||||
|
, letterChar
|
||||||
|
, space1
|
||||||
|
, string
|
||||||
|
)
|
||||||
|
import Control.Applicative (Alternative(..))
|
||||||
|
import Data.Maybe (isJust)
|
||||||
|
|
||||||
|
type Parser = Parsec Void Text
|
||||||
|
|
||||||
|
literalP :: Parser Literal
|
||||||
|
literalP
|
||||||
|
= HexadecimalLiteral <$> Lexer.signed space hexadecimalP
|
||||||
|
<|> DecimalLiteral <$> Lexer.signed space decimalP
|
||||||
|
<|> CharacterLiteral <$> lexeme charP
|
||||||
|
where
|
||||||
|
charP = fromIntegral . fromEnum
|
||||||
|
<$> between (char '\'') (char '\'') Lexer.charLiteral
|
||||||
|
|
||||||
|
typeDefinitionP :: Parser Declaration
|
||||||
|
typeDefinitionP = TypeDefinition
|
||||||
|
<$> (symbol "type" *> identifierP)
|
||||||
|
<*> (symbol "=" *> typeExpressionP)
|
||||||
|
<* semicolonP
|
||||||
|
<?> "type definition"
|
||||||
|
|
||||||
|
termP :: Parser Expression
|
||||||
|
termP = choice
|
||||||
|
[ parensP expressionP
|
||||||
|
, LiteralExpression <$> literalP
|
||||||
|
, VariableExpression <$> variableAccessP
|
||||||
|
]
|
||||||
|
|
||||||
|
operatorTable :: [[Operator Parser Expression]]
|
||||||
|
operatorTable =
|
||||||
|
[ unaryOperator
|
||||||
|
, factorOperator
|
||||||
|
, termOperator
|
||||||
|
]
|
||||||
|
where
|
||||||
|
unaryOperator =
|
||||||
|
[ prefix "-" NegationExpression
|
||||||
|
, prefix "+" id
|
||||||
|
]
|
||||||
|
factorOperator =
|
||||||
|
[ binary "*" ProductExpression
|
||||||
|
, binary "/" DivisionExpression
|
||||||
|
]
|
||||||
|
termOperator =
|
||||||
|
[ binary "+" SumExpression
|
||||||
|
, binary "-" SubtractionExpression
|
||||||
|
]
|
||||||
|
prefix name f = Prefix (f <$ symbol name)
|
||||||
|
binary name f = InfixL (f <$ symbol name)
|
||||||
|
|
||||||
|
expressionP :: Parser Expression
|
||||||
|
expressionP = makeExprParser termP operatorTable
|
||||||
|
|
||||||
|
variableAccessP :: Parser VariableAccess
|
||||||
|
variableAccessP = VariableAccess <$> identifierP {- do
|
||||||
|
identifier <- identifierP
|
||||||
|
indices <- many $ bracketsP expressionP
|
||||||
|
pure $ foldr (flip ArrayAccess) (VariableAccess identifier) indices -}
|
||||||
|
|
||||||
|
conditionP :: Parser Condition
|
||||||
|
conditionP = do
|
||||||
|
lhs <- expressionP
|
||||||
|
conditionCons <- choice comparisonOperator
|
||||||
|
conditionCons lhs <$> expressionP
|
||||||
|
where
|
||||||
|
comparisonOperator =
|
||||||
|
[ symbol "<=" >> pure LessOrEqualCondition
|
||||||
|
, symbol "<" >> pure LessCondition
|
||||||
|
, symbol ">=" >> pure GreaterOrEqualCondition
|
||||||
|
, symbol ">" >> pure GreaterCondition
|
||||||
|
, symbol "=" >> pure EqualCondition
|
||||||
|
, symbol "#" >> pure NonEqualCondition
|
||||||
|
]
|
||||||
|
|
||||||
|
symbol :: Text -> Parser Text
|
||||||
|
symbol = Lexer.symbol space
|
||||||
|
|
||||||
|
space :: Parser ()
|
||||||
|
space = Lexer.space space1 (Lexer.skipLineComment "//")
|
||||||
|
$ Lexer.skipBlockComment "/*" "*/"
|
||||||
|
|
||||||
|
lexeme :: forall a. Parser a -> Parser a
|
||||||
|
lexeme = Lexer.lexeme space
|
||||||
|
|
||||||
|
blockP :: forall a. Parser a -> Parser a
|
||||||
|
blockP = between (symbol "{") (symbol "}")
|
||||||
|
|
||||||
|
parensP :: forall a. Parser a -> Parser a
|
||||||
|
parensP = between (symbol "(") (symbol ")")
|
||||||
|
|
||||||
|
bracketsP :: forall a. Parser a -> Parser a
|
||||||
|
bracketsP = between (symbol "[") (symbol "]")
|
||||||
|
|
||||||
|
colonP :: Parser ()
|
||||||
|
colonP = void $ symbol ":"
|
||||||
|
|
||||||
|
commaP :: Parser ()
|
||||||
|
commaP = void $ symbol ","
|
||||||
|
|
||||||
|
semicolonP :: Parser ()
|
||||||
|
semicolonP = void $ symbol ";"
|
||||||
|
|
||||||
|
decimalP :: Integral a => Parser a
|
||||||
|
decimalP = lexeme Lexer.decimal
|
||||||
|
|
||||||
|
hexadecimalP :: Integral a => Parser a
|
||||||
|
hexadecimalP = string "0x" *> lexeme Lexer.hexadecimal
|
||||||
|
|
||||||
|
identifierP :: Parser Identifier
|
||||||
|
identifierP =
|
||||||
|
let wordParser = (:) <$> letterChar <*> many alphaNumChar <?> "identifier"
|
||||||
|
in fmap Identifier $ lexeme $ Text.pack <$> wordParser
|
||||||
|
|
||||||
|
procedureP :: Parser ()
|
||||||
|
procedureP = void $ symbol "proc"
|
||||||
|
|
||||||
|
parameterP :: Parser Parameter
|
||||||
|
parameterP = paramCons
|
||||||
|
<$> optional (symbol "ref")
|
||||||
|
<*> identifierP
|
||||||
|
<*> (colonP *> typeExpressionP)
|
||||||
|
where
|
||||||
|
paramCons ref name typeName = Parameter name typeName (isJust ref)
|
||||||
|
|
||||||
|
typeExpressionP :: Parser TypeExpression
|
||||||
|
typeExpressionP = arrayTypeExpression
|
||||||
|
<|> NamedType <$> identifierP
|
||||||
|
<?> "type expression"
|
||||||
|
where
|
||||||
|
arrayTypeExpression = ArrayType
|
||||||
|
<$> (symbol "array" *> bracketsP literalP)
|
||||||
|
<*> (symbol "of" *> typeExpressionP)
|
||||||
|
|
||||||
|
procedureDeclarationP :: Parser Declaration
|
||||||
|
procedureDeclarationP = procedureCons
|
||||||
|
<$> (procedureP *> identifierP)
|
||||||
|
<*> parensP (sepBy parameterP commaP)
|
||||||
|
<*> blockP ((,) <$> many variableDeclarationP <*> many statementP)
|
||||||
|
<?> "procedure definition"
|
||||||
|
where
|
||||||
|
procedureCons procedureName parameters (variables, body) =
|
||||||
|
ProcedureDeclaration procedureName parameters variables body
|
||||||
|
|
||||||
|
statementP :: Parser Statement
|
||||||
|
statementP
|
||||||
|
= EmptyStatement <$ semicolonP
|
||||||
|
<|> ifElseP
|
||||||
|
<|> CompoundStatement <$> blockP (many statementP)
|
||||||
|
<|> try assignmentP
|
||||||
|
-- <|> try whileP
|
||||||
|
<|> callP
|
||||||
|
<?> "statement"
|
||||||
|
where
|
||||||
|
callP = CallStatement
|
||||||
|
<$> identifierP
|
||||||
|
<*> parensP (sepBy expressionP commaP)
|
||||||
|
<* semicolonP
|
||||||
|
ifElseP = IfStatement
|
||||||
|
<$> (symbol "if" *> parensP conditionP)
|
||||||
|
<*> statementP
|
||||||
|
<*> optional (symbol "else" *> statementP)
|
||||||
|
{-whileP = WhileStatement
|
||||||
|
<$> (symbol "while" *> parensP conditionP)
|
||||||
|
<*> statementP -}
|
||||||
|
assignmentP = AssignmentStatement
|
||||||
|
<$> variableAccessP
|
||||||
|
<* symbol ":="
|
||||||
|
<*> expressionP
|
||||||
|
<* semicolonP
|
||||||
|
|
||||||
|
variableDeclarationP :: Parser VariableDeclaration
|
||||||
|
variableDeclarationP = VariableDeclaration
|
||||||
|
<$> (symbol "var" *> identifierP)
|
||||||
|
<*> (colonP *> typeExpressionP)
|
||||||
|
<* semicolonP
|
||||||
|
<?> "variable declaration"
|
||||||
|
|
||||||
|
declarationP :: Parser Declaration
|
||||||
|
declarationP = procedureDeclarationP <|> typeDefinitionP
|
||||||
|
|
||||||
|
programP :: Parser Program
|
||||||
|
programP = Program <$> many declarationP <* eof
|
101
lib/Language/Elna/Frontend/SymbolTable.hs
Normal file
101
lib/Language/Elna/Frontend/SymbolTable.hs
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
module Language.Elna.Frontend.SymbolTable
|
||||||
|
( SymbolTable
|
||||||
|
, Info(..)
|
||||||
|
, ParameterInfo(..)
|
||||||
|
, builtInSymbolTable
|
||||||
|
, empty
|
||||||
|
, enter
|
||||||
|
, fromList
|
||||||
|
, lookup
|
||||||
|
, member
|
||||||
|
, scope
|
||||||
|
, toMap
|
||||||
|
, update
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import qualified Data.HashMap.Strict as HashMap
|
||||||
|
import Data.List (sort)
|
||||||
|
import Data.List.NonEmpty (NonEmpty)
|
||||||
|
import qualified Data.List.NonEmpty as NonEmpty
|
||||||
|
import Data.Maybe (isJust)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import qualified Data.Vector as Vector
|
||||||
|
import Language.Elna.Location (Identifier(..))
|
||||||
|
import Language.Elna.Frontend.Types (Type(..), intType)
|
||||||
|
import Prelude hiding (lookup)
|
||||||
|
|
||||||
|
data SymbolTable = SymbolTable (Maybe SymbolTable) (HashMap Identifier Info)
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
empty :: SymbolTable
|
||||||
|
empty = SymbolTable Nothing HashMap.empty
|
||||||
|
|
||||||
|
update :: (Info -> Maybe Info) -> Identifier -> SymbolTable -> SymbolTable
|
||||||
|
update updater key (SymbolTable parent mappings) = SymbolTable parent
|
||||||
|
$ HashMap.update updater key mappings
|
||||||
|
|
||||||
|
scope :: SymbolTable -> SymbolTable -> SymbolTable
|
||||||
|
scope parent (SymbolTable _ mappings) = SymbolTable (Just parent) mappings
|
||||||
|
|
||||||
|
builtInSymbolTable :: SymbolTable
|
||||||
|
builtInSymbolTable = SymbolTable Nothing $ HashMap.fromList
|
||||||
|
[ ("printi", ProcedureInfo empty (Vector.singleton printiX))
|
||||||
|
, ("printc", ProcedureInfo empty (Vector.singleton printcI))
|
||||||
|
, ("exit", ProcedureInfo empty Vector.empty)
|
||||||
|
, ("int", TypeInfo intType)
|
||||||
|
]
|
||||||
|
where
|
||||||
|
printiX = ParameterInfo
|
||||||
|
{ name = "x"
|
||||||
|
, type' = intType
|
||||||
|
, isReferenceParameter = False
|
||||||
|
}
|
||||||
|
printcI = ParameterInfo
|
||||||
|
{ name = "i"
|
||||||
|
, type' = intType
|
||||||
|
, isReferenceParameter = False
|
||||||
|
}
|
||||||
|
|
||||||
|
toMap :: SymbolTable -> HashMap Identifier Info
|
||||||
|
toMap (SymbolTable _ map') = map'
|
||||||
|
|
||||||
|
enter :: Identifier -> Info -> SymbolTable -> Maybe SymbolTable
|
||||||
|
enter identifier info table@(SymbolTable parent hashTable)
|
||||||
|
| member identifier table = Nothing
|
||||||
|
| otherwise = Just
|
||||||
|
$ SymbolTable parent (HashMap.insert identifier info hashTable)
|
||||||
|
|
||||||
|
lookup :: Identifier -> SymbolTable -> Maybe Info
|
||||||
|
lookup identifier (SymbolTable parent table)
|
||||||
|
| Just found <- HashMap.lookup identifier table = Just found
|
||||||
|
| Just parent' <- parent = lookup identifier parent'
|
||||||
|
| otherwise = Nothing
|
||||||
|
|
||||||
|
member :: Identifier -> SymbolTable -> Bool
|
||||||
|
member identifier table =
|
||||||
|
isJust $ lookup identifier table
|
||||||
|
|
||||||
|
fromList :: [(Identifier, Info)] -> Either (NonEmpty Identifier) SymbolTable
|
||||||
|
fromList elements
|
||||||
|
| Just identifierDuplicates' <- identifierDuplicates =
|
||||||
|
Left identifierDuplicates'
|
||||||
|
| otherwise = Right $ SymbolTable Nothing $ HashMap.fromList elements
|
||||||
|
where
|
||||||
|
identifierDuplicates = NonEmpty.nonEmpty
|
||||||
|
$ fmap NonEmpty.head
|
||||||
|
$ filter ((> 1) . NonEmpty.length)
|
||||||
|
$ NonEmpty.group . sort
|
||||||
|
$ fst <$> elements
|
||||||
|
|
||||||
|
data ParameterInfo = ParameterInfo
|
||||||
|
{ name :: Identifier
|
||||||
|
, type' :: Type
|
||||||
|
, isReferenceParameter :: Bool
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
data Info
|
||||||
|
= TypeInfo Type
|
||||||
|
| VariableInfo Bool Type
|
||||||
|
| ProcedureInfo SymbolTable (Vector ParameterInfo)
|
||||||
|
deriving (Eq, Show)
|
201
lib/Language/Elna/Frontend/TypeAnalysis.hs
Normal file
201
lib/Language/Elna/Frontend/TypeAnalysis.hs
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
module Language.Elna.Frontend.TypeAnalysis
|
||||||
|
( typeAnalysis
|
||||||
|
, -- Error(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Applicative (Alternative(..))
|
||||||
|
import Control.Monad (unless)
|
||||||
|
import Control.Monad.Trans.Class (MonadTrans(..))
|
||||||
|
import Control.Monad.Trans.Except (Except, runExcept, throwE)
|
||||||
|
import Control.Monad.Trans.Reader (ReaderT, runReaderT, withReaderT, ask, asks)
|
||||||
|
import Data.Foldable (traverse_)
|
||||||
|
import qualified Data.Vector as Vector
|
||||||
|
import qualified Language.Elna.Frontend.AST as AST
|
||||||
|
import Language.Elna.Frontend.SymbolTable (Info(..), {-ParameterInfo(..), -}SymbolTable)
|
||||||
|
import qualified Language.Elna.Frontend.SymbolTable as SymbolTable
|
||||||
|
import Language.Elna.Frontend.Types (Type(..), booleanType, intType)
|
||||||
|
import Language.Elna.Location (Identifier(..))
|
||||||
|
|
||||||
|
typeAnalysis :: SymbolTable -> AST.Program -> Maybe Error
|
||||||
|
typeAnalysis globalTable = either Just (const Nothing)
|
||||||
|
. runExcept
|
||||||
|
. flip runReaderT globalTable
|
||||||
|
. runTypeAnalysis
|
||||||
|
. program
|
||||||
|
|
||||||
|
data Error
|
||||||
|
= UnexpectedProcedureInfoError Info
|
||||||
|
| UndefinedSymbolError Identifier
|
||||||
|
| ParameterCountMismatchError Int Int
|
||||||
|
| UnexpectedVariableInfoError Info
|
||||||
|
| ArithmeticExpressionError Type
|
||||||
|
| ComparisonExpressionError Type Type
|
||||||
|
| InvalidConditionTypeError Type
|
||||||
|
{- | InvalidAssignmentError Type
|
||||||
|
| ExpectedLvalueError AST.Expression
|
||||||
|
| ArgumentTypeMismatchError Type Type
|
||||||
|
| ArrayIndexError Type
|
||||||
|
| ArrayAccessError Type -}
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Error
|
||||||
|
where
|
||||||
|
show (UnexpectedProcedureInfoError info) =
|
||||||
|
"Expected to encounter a procedure, got: " <> show info
|
||||||
|
show (UndefinedSymbolError identifier) =
|
||||||
|
concat ["Symbol \"", show identifier, "\" is not defined"]
|
||||||
|
show (ParameterCountMismatchError parameterCount argumentCount)
|
||||||
|
= "The function was expected to receive " <> show argumentCount
|
||||||
|
<> " arguments, but got " <> show parameterCount
|
||||||
|
show (UnexpectedVariableInfoError info) =
|
||||||
|
"Expected to encounter a variable, got: " <> show info
|
||||||
|
show (ArithmeticExpressionError got) =
|
||||||
|
"Expected an arithmetic expression to be an integer, got: " <> show got
|
||||||
|
show (ComparisonExpressionError lhs rhs)
|
||||||
|
= "Expected an arithmetic expression to be an integer, got \""
|
||||||
|
<> show lhs <> "\" and \"" <> show rhs <> "\""
|
||||||
|
show (InvalidConditionTypeError got) =
|
||||||
|
"Expected a condition to be a boolean, got: " <> show got
|
||||||
|
|
||||||
|
newtype TypeAnalysis a = TypeAnalysis
|
||||||
|
{ runTypeAnalysis :: ReaderT SymbolTable (Except Error) a
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Functor TypeAnalysis
|
||||||
|
where
|
||||||
|
fmap f (TypeAnalysis x) = TypeAnalysis $ f <$> x
|
||||||
|
|
||||||
|
instance Applicative TypeAnalysis
|
||||||
|
where
|
||||||
|
pure = TypeAnalysis . pure
|
||||||
|
(TypeAnalysis f) <*> (TypeAnalysis x) = TypeAnalysis $ f <*> x
|
||||||
|
|
||||||
|
instance Monad TypeAnalysis
|
||||||
|
where
|
||||||
|
(TypeAnalysis x) >>= f = TypeAnalysis $ x >>= (runTypeAnalysis . f)
|
||||||
|
|
||||||
|
program :: AST.Program -> TypeAnalysis ()
|
||||||
|
program (AST.Program declarations) = traverse_ declaration declarations
|
||||||
|
|
||||||
|
declaration :: AST.Declaration -> TypeAnalysis ()
|
||||||
|
declaration (AST.ProcedureDeclaration procedureName _ _ body) = do
|
||||||
|
globalTable <- TypeAnalysis ask
|
||||||
|
case SymbolTable.lookup procedureName globalTable of
|
||||||
|
Just (ProcedureInfo localTable _) -> TypeAnalysis
|
||||||
|
$ withReaderT (const localTable)
|
||||||
|
$ runTypeAnalysis
|
||||||
|
$ traverse_ (statement globalTable) body
|
||||||
|
Just anotherInfo -> TypeAnalysis $ lift $ throwE
|
||||||
|
$ UnexpectedProcedureInfoError anotherInfo
|
||||||
|
Nothing -> TypeAnalysis $ lift $ throwE
|
||||||
|
$ UndefinedSymbolError procedureName
|
||||||
|
declaration (AST.TypeDefinition _ _) = pure ()
|
||||||
|
|
||||||
|
statement :: SymbolTable -> AST.Statement -> TypeAnalysis ()
|
||||||
|
statement globalTable = \case
|
||||||
|
AST.EmptyStatement -> pure ()
|
||||||
|
AST.AssignmentStatement lhs rhs -> do
|
||||||
|
lhsType <- variableAccess globalTable lhs
|
||||||
|
rhsType <- expression globalTable rhs
|
||||||
|
unless (lhsType == intType)
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError lhsType
|
||||||
|
unless (rhsType == intType)
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError rhsType
|
||||||
|
{- AST.WhileStatement whileCondition whileStatement -> do
|
||||||
|
conditionType <- condition globalTable whileCondition
|
||||||
|
unless (conditionType == booleanType)
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError conditionType
|
||||||
|
statement globalTable whileStatement -}
|
||||||
|
AST.IfStatement ifCondition ifStatement elseStatement -> do
|
||||||
|
conditionType <- condition globalTable ifCondition
|
||||||
|
unless (conditionType == booleanType)
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ InvalidConditionTypeError conditionType
|
||||||
|
statement globalTable ifStatement
|
||||||
|
maybe (pure ()) (statement globalTable) elseStatement
|
||||||
|
AST.CompoundStatement statements -> traverse_ (statement globalTable) statements
|
||||||
|
AST.CallStatement procedureName arguments ->
|
||||||
|
case SymbolTable.lookup procedureName globalTable of
|
||||||
|
Just (ProcedureInfo _ parameters)
|
||||||
|
| parametersLength <- Vector.length parameters
|
||||||
|
, argumentsLength <- length arguments
|
||||||
|
, Vector.length parameters /= length arguments -> TypeAnalysis $ lift $ throwE
|
||||||
|
$ ParameterCountMismatchError parametersLength argumentsLength
|
||||||
|
| otherwise -> traverse_ (uncurry checkArgument)
|
||||||
|
$ Vector.zip parameters (Vector.fromList arguments)
|
||||||
|
Just anotherInfo -> TypeAnalysis $ lift $ throwE
|
||||||
|
$ UnexpectedVariableInfoError anotherInfo
|
||||||
|
Nothing -> TypeAnalysis $ lift $ throwE
|
||||||
|
$ UndefinedSymbolError procedureName
|
||||||
|
where
|
||||||
|
checkArgument SymbolTable.ParameterInfo{} _argument = pure () {-
|
||||||
|
argumentType <- expression globalTable argument
|
||||||
|
unless (argumentType == type')
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ ArgumentTypeMismatchError type' argumentType
|
||||||
|
when (isReferenceParameter && not (isLvalue argument))
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ ExpectedLvalueError argument
|
||||||
|
isLvalue (AST.VariableExpression _) = True
|
||||||
|
isLvalue _ = False -}
|
||||||
|
|
||||||
|
variableAccess :: SymbolTable -> AST.VariableAccess -> TypeAnalysis Type
|
||||||
|
variableAccess globalTable (AST.VariableAccess identifier) = do
|
||||||
|
localLookup <- TypeAnalysis $ asks $ SymbolTable.lookup identifier
|
||||||
|
case localLookup <|> SymbolTable.lookup identifier globalTable of
|
||||||
|
Just (VariableInfo _ variableType) -> pure variableType
|
||||||
|
Just anotherInfo -> TypeAnalysis $ lift $ throwE
|
||||||
|
$ UnexpectedVariableInfoError anotherInfo
|
||||||
|
Nothing -> TypeAnalysis $ lift $ throwE
|
||||||
|
$ UndefinedSymbolError identifier
|
||||||
|
{-variableAccess globalTable (AST.ArrayAccess arrayExpression indexExpression) = do
|
||||||
|
arrayType <- variableAccess globalTable arrayExpression
|
||||||
|
indexType <- expression globalTable indexExpression
|
||||||
|
unless (indexType == intType)
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ ArrayIndexError indexType
|
||||||
|
case arrayType of
|
||||||
|
ArrayType _ baseType -> pure baseType
|
||||||
|
nonArrayType -> TypeAnalysis $ lift $ throwE
|
||||||
|
$ ArrayAccessError nonArrayType
|
||||||
|
-}
|
||||||
|
expression :: SymbolTable -> AST.Expression -> TypeAnalysis Type
|
||||||
|
expression globalTable = \case
|
||||||
|
AST.VariableExpression variableExpression ->
|
||||||
|
variableAccess globalTable variableExpression
|
||||||
|
AST.LiteralExpression literal' -> literal literal'
|
||||||
|
AST.NegationExpression negation -> do
|
||||||
|
operandType <- expression globalTable negation
|
||||||
|
if operandType == intType
|
||||||
|
then pure intType
|
||||||
|
else TypeAnalysis $ lift $ throwE $ ArithmeticExpressionError operandType
|
||||||
|
AST.SumExpression lhs rhs -> arithmeticExpression lhs rhs
|
||||||
|
AST.SubtractionExpression lhs rhs -> arithmeticExpression lhs rhs
|
||||||
|
AST.ProductExpression lhs rhs -> arithmeticExpression lhs rhs
|
||||||
|
AST.DivisionExpression lhs rhs -> arithmeticExpression lhs rhs
|
||||||
|
where
|
||||||
|
arithmeticExpression lhs rhs = do
|
||||||
|
lhsType <- expression globalTable lhs
|
||||||
|
unless (lhsType == intType)
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ ArithmeticExpressionError lhsType
|
||||||
|
rhsType <- expression globalTable rhs
|
||||||
|
unless (rhsType == intType)
|
||||||
|
$ TypeAnalysis $ lift $ throwE $ ArithmeticExpressionError rhsType
|
||||||
|
pure intType
|
||||||
|
|
||||||
|
condition :: SymbolTable -> AST.Condition -> TypeAnalysis Type
|
||||||
|
condition globalTable = \case
|
||||||
|
AST.EqualCondition lhs rhs -> comparisonExpression lhs rhs
|
||||||
|
AST.NonEqualCondition lhs rhs -> comparisonExpression lhs rhs
|
||||||
|
AST.LessCondition lhs rhs -> comparisonExpression lhs rhs
|
||||||
|
AST.GreaterCondition lhs rhs -> comparisonExpression lhs rhs
|
||||||
|
AST.LessOrEqualCondition lhs rhs -> comparisonExpression lhs rhs
|
||||||
|
AST.GreaterOrEqualCondition lhs rhs -> comparisonExpression lhs rhs
|
||||||
|
where
|
||||||
|
comparisonExpression lhs rhs = do
|
||||||
|
lhsType <- expression globalTable lhs
|
||||||
|
rhsType <- expression globalTable rhs
|
||||||
|
if lhsType == intType && rhsType == intType
|
||||||
|
then pure booleanType
|
||||||
|
else TypeAnalysis $ lift $ throwE $ ComparisonExpressionError lhsType rhsType
|
||||||
|
|
||||||
|
literal :: AST.Literal -> TypeAnalysis Type
|
||||||
|
literal (AST.DecimalLiteral _) = pure intType
|
||||||
|
literal (AST.HexadecimalLiteral _) = pure intType
|
||||||
|
literal (AST.CharacterLiteral _) = pure intType
|
29
lib/Language/Elna/Frontend/Types.hs
Normal file
29
lib/Language/Elna/Frontend/Types.hs
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
module Language.Elna.Frontend.Types
|
||||||
|
( Type(..)
|
||||||
|
, addressByteSize
|
||||||
|
, booleanType
|
||||||
|
, intType
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Data.Word (Word32)
|
||||||
|
import Language.Elna.Location (showArrayType)
|
||||||
|
|
||||||
|
addressByteSize :: Int
|
||||||
|
addressByteSize = 4
|
||||||
|
|
||||||
|
data Type
|
||||||
|
= PrimitiveType Text Int
|
||||||
|
| ArrayType Word32 Type
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Type
|
||||||
|
where
|
||||||
|
show (PrimitiveType typeName _) = show typeName
|
||||||
|
show (ArrayType elementCount typeName) = showArrayType elementCount typeName
|
||||||
|
|
||||||
|
intType :: Type
|
||||||
|
intType = PrimitiveType "int" 4
|
||||||
|
|
||||||
|
booleanType :: Type
|
||||||
|
booleanType = PrimitiveType "boolean" 1
|
294
lib/Language/Elna/Glue.hs
Normal file
294
lib/Language/Elna/Glue.hs
Normal file
@ -0,0 +1,294 @@
|
|||||||
|
module Language.Elna.Glue
|
||||||
|
( glue
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.Trans.State (State, gets, modify', runState)
|
||||||
|
import Data.Bifunctor (Bifunctor(..))
|
||||||
|
import Data.Foldable (Foldable(..), traverse_)
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import qualified Data.HashMap.Strict as HashMap
|
||||||
|
import Data.Maybe (catMaybes)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import qualified Data.Text.Lazy.Builder.Int as Text.Builder
|
||||||
|
import qualified Data.Text.Lazy.Builder as Text.Builder
|
||||||
|
import qualified Data.Text.Lazy as Text.Lazy
|
||||||
|
import qualified Data.Vector as Vector
|
||||||
|
import Data.Word (Word32)
|
||||||
|
import qualified Language.Elna.Frontend.AST as AST
|
||||||
|
import Language.Elna.Frontend.Types (Type(..))
|
||||||
|
import Language.Elna.Backend.Intermediate
|
||||||
|
( Label(..)
|
||||||
|
, Operand(..)
|
||||||
|
, Quadruple(..)
|
||||||
|
, Variable(..)
|
||||||
|
)
|
||||||
|
import Language.Elna.Frontend.SymbolTable (Info(..), SymbolTable)
|
||||||
|
import qualified Language.Elna.Frontend.SymbolTable as SymbolTable
|
||||||
|
import GHC.Records (HasField(..))
|
||||||
|
import Language.Elna.Frontend.AST (Identifier(..))
|
||||||
|
|
||||||
|
data Paste = Paste
|
||||||
|
{ temporaryCounter :: Word32
|
||||||
|
, labelCounter :: Word32
|
||||||
|
, localMap :: HashMap Identifier Variable
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype Glue a = Glue
|
||||||
|
{ runGlue :: State Paste a }
|
||||||
|
|
||||||
|
instance Functor Glue
|
||||||
|
where
|
||||||
|
fmap f (Glue x) = Glue $ f <$> x
|
||||||
|
|
||||||
|
instance Applicative Glue
|
||||||
|
where
|
||||||
|
pure = Glue . pure
|
||||||
|
(Glue f) <*> (Glue x) = Glue $ f <*> x
|
||||||
|
|
||||||
|
instance Monad Glue
|
||||||
|
where
|
||||||
|
(Glue x) >>= f = Glue $ x >>= (runGlue . f)
|
||||||
|
|
||||||
|
glue :: SymbolTable -> AST.Program -> HashMap Identifier (Vector (Quadruple Variable))
|
||||||
|
glue globalTable
|
||||||
|
= fst
|
||||||
|
. flip runState emptyPaste
|
||||||
|
. runGlue
|
||||||
|
. program globalTable
|
||||||
|
where
|
||||||
|
emptyPaste = Paste
|
||||||
|
{ temporaryCounter = 0
|
||||||
|
, labelCounter = 0
|
||||||
|
, localMap = mempty
|
||||||
|
}
|
||||||
|
|
||||||
|
program :: SymbolTable -> AST.Program -> Glue (HashMap Identifier (Vector (Quadruple Variable)))
|
||||||
|
program globalTable (AST.Program declarations)
|
||||||
|
= HashMap.fromList . catMaybes
|
||||||
|
<$> traverse (declaration globalTable) declarations
|
||||||
|
|
||||||
|
declaration
|
||||||
|
:: SymbolTable
|
||||||
|
-> AST.Declaration
|
||||||
|
-> Glue (Maybe (AST.Identifier, Vector (Quadruple Variable)))
|
||||||
|
declaration globalTable (AST.ProcedureDeclaration procedureName _ variableDeclarations statements)
|
||||||
|
= traverse_ registerVariable variableDeclarations
|
||||||
|
>> nameQuadruplesTuple <$> traverse (statement globalTable) statements
|
||||||
|
where
|
||||||
|
registerVariable (AST.VariableDeclaration identifier _) = do
|
||||||
|
currentCounter <- fmap (fromIntegral . HashMap.size)
|
||||||
|
$ Glue $ gets $ getField @"localMap"
|
||||||
|
Glue $ modify' $ modifier identifier $ LocalVariable currentCounter
|
||||||
|
modifier identifier currentCounter generator = generator
|
||||||
|
{ localMap = HashMap.insert identifier currentCounter
|
||||||
|
$ getField @"localMap" generator
|
||||||
|
}
|
||||||
|
nameQuadruplesTuple quadrupleList = Just
|
||||||
|
( procedureName
|
||||||
|
, Vector.cons StartQuadruple
|
||||||
|
$ flip Vector.snoc StopQuadruple
|
||||||
|
$ fold quadrupleList
|
||||||
|
)
|
||||||
|
declaration _ (AST.TypeDefinition _ _) = pure Nothing
|
||||||
|
|
||||||
|
statement :: SymbolTable -> AST.Statement -> Glue (Vector (Quadruple Variable))
|
||||||
|
statement _ AST.EmptyStatement = pure mempty
|
||||||
|
statement localTable (AST.CallStatement (AST.Identifier callName) arguments) = do
|
||||||
|
visitedArguments <- traverse (expression localTable) arguments
|
||||||
|
let (parameterStatements, argumentStatements)
|
||||||
|
= bimap (Vector.fromList . fmap ParameterQuadruple) Vector.concat
|
||||||
|
$ unzip visitedArguments
|
||||||
|
in pure
|
||||||
|
$ Vector.snoc (argumentStatements <> parameterStatements)
|
||||||
|
$ CallQuadruple callName
|
||||||
|
$ fromIntegral
|
||||||
|
$ Vector.length argumentStatements
|
||||||
|
statement localTable (AST.CompoundStatement statements) =
|
||||||
|
fold <$> traverse (statement localTable) statements
|
||||||
|
statement localTable (AST.IfStatement ifCondition ifStatement elseStatement) = do
|
||||||
|
(conditionStatements, jumpConstructor) <- condition localTable ifCondition
|
||||||
|
ifLabel <- createLabel
|
||||||
|
endLabel <- createLabel
|
||||||
|
ifStatements <- statement localTable ifStatement
|
||||||
|
possibleElseStatements <- traverse (statement localTable) elseStatement
|
||||||
|
pure $ conditionStatements <> case possibleElseStatements of
|
||||||
|
Just elseStatements -> Vector.cons (jumpConstructor ifLabel) elseStatements
|
||||||
|
<> Vector.fromList [GoToQuadruple endLabel, LabelQuadruple ifLabel]
|
||||||
|
<> Vector.snoc ifStatements (LabelQuadruple endLabel)
|
||||||
|
Nothing -> Vector.fromList [jumpConstructor ifLabel, GoToQuadruple endLabel, LabelQuadruple ifLabel]
|
||||||
|
<> Vector.snoc ifStatements (LabelQuadruple endLabel)
|
||||||
|
statement localTable (AST.AssignmentStatement variableAccess' assignee) = do
|
||||||
|
(rhsOperand, rhsStatements) <- expression localTable assignee
|
||||||
|
let variableType' = variableType variableAccess' localTable
|
||||||
|
accessResult <- variableAccess localTable variableAccess' Nothing variableType' mempty
|
||||||
|
lhsStatements <- case accessResult of
|
||||||
|
{-(AST.Identifier identifier, Just accumulatedIndex, accumulatedStatements) ->
|
||||||
|
Vector.snoc accumulatedStatements
|
||||||
|
$ ArrayAssignQuadruple rhsOperand accumulatedIndex
|
||||||
|
$ LocalVariable identifier -}
|
||||||
|
(identifier, _Nothing, accumulatedStatements)
|
||||||
|
-> Vector.snoc accumulatedStatements
|
||||||
|
. AssignQuadruple rhsOperand
|
||||||
|
<$> lookupLocal identifier
|
||||||
|
pure $ rhsStatements <> lhsStatements
|
||||||
|
{- statement localTable (AST.WhileStatement whileCondition whileStatement) = do
|
||||||
|
(conditionStatements, jumpConstructor) <- condition localTable whileCondition
|
||||||
|
startLabel <- createLabel
|
||||||
|
endLabel <- createLabel
|
||||||
|
conditionLabel <- createLabel
|
||||||
|
whileStatements <- statement localTable whileStatement
|
||||||
|
pure $ Vector.fromList [LabelQuadruple conditionLabel]
|
||||||
|
<> conditionStatements
|
||||||
|
<> Vector.fromList [jumpConstructor startLabel, GoToQuadruple endLabel, LabelQuadruple startLabel]
|
||||||
|
<> whileStatements
|
||||||
|
<> Vector.fromList [GoToQuadruple conditionLabel, LabelQuadruple endLabel] -}
|
||||||
|
|
||||||
|
createTemporary :: Glue Variable
|
||||||
|
createTemporary = do
|
||||||
|
currentCounter <- Glue $ gets $ getField @"temporaryCounter"
|
||||||
|
Glue $ modify' modifier
|
||||||
|
pure $ TempVariable currentCounter
|
||||||
|
where
|
||||||
|
modifier generator = generator
|
||||||
|
{ temporaryCounter = getField @"temporaryCounter" generator + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
lookupLocal :: Identifier -> Glue Variable
|
||||||
|
lookupLocal identifier =
|
||||||
|
fmap (HashMap.! identifier) $ Glue $ gets $ getField @"localMap"
|
||||||
|
|
||||||
|
createLabel :: Glue Label
|
||||||
|
createLabel = do
|
||||||
|
currentCounter <- Glue $ gets $ getField @"labelCounter"
|
||||||
|
Glue $ modify' modifier
|
||||||
|
pure $ Label
|
||||||
|
$ Text.Lazy.toStrict
|
||||||
|
$ Text.Builder.toLazyText
|
||||||
|
$ ".L" <> Text.Builder.decimal currentCounter
|
||||||
|
where
|
||||||
|
modifier generator = generator
|
||||||
|
{ labelCounter = getField @"labelCounter" generator + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
condition
|
||||||
|
:: SymbolTable
|
||||||
|
-> AST.Condition
|
||||||
|
-> Glue (Vector (Quadruple Variable), Label -> Quadruple Variable)
|
||||||
|
condition localTable (AST.EqualCondition lhs rhs) = do
|
||||||
|
(lhsOperand, lhsStatements) <- expression localTable lhs
|
||||||
|
(rhsOperand, rhsStatements) <- expression localTable rhs
|
||||||
|
pure
|
||||||
|
( lhsStatements <> rhsStatements
|
||||||
|
, EqualQuadruple lhsOperand rhsOperand
|
||||||
|
)
|
||||||
|
condition localTable (AST.NonEqualCondition lhs rhs) = do
|
||||||
|
(lhsOperand, lhsStatements) <- expression localTable lhs
|
||||||
|
(rhsOperand, rhsStatements) <- expression localTable rhs
|
||||||
|
pure
|
||||||
|
( lhsStatements <> rhsStatements
|
||||||
|
, NonEqualQuadruple lhsOperand rhsOperand
|
||||||
|
)
|
||||||
|
condition localTable (AST.LessCondition lhs rhs) = do
|
||||||
|
(lhsOperand, lhsStatements) <- expression localTable lhs
|
||||||
|
(rhsOperand, rhsStatements) <- expression localTable rhs
|
||||||
|
pure (lhsStatements <> rhsStatements, LessQuadruple lhsOperand rhsOperand)
|
||||||
|
condition localTable (AST.GreaterCondition lhs rhs) = do
|
||||||
|
(lhsOperand, lhsStatements) <- expression localTable lhs
|
||||||
|
(rhsOperand, rhsStatements) <- expression localTable rhs
|
||||||
|
pure
|
||||||
|
( lhsStatements <> rhsStatements
|
||||||
|
, GreaterQuadruple lhsOperand rhsOperand
|
||||||
|
)
|
||||||
|
condition localTable (AST.LessOrEqualCondition lhs rhs) = do
|
||||||
|
(lhsOperand, lhsStatements) <- expression localTable lhs
|
||||||
|
(rhsOperand, rhsStatements) <- expression localTable rhs
|
||||||
|
pure
|
||||||
|
( lhsStatements <> rhsStatements
|
||||||
|
, LessOrEqualQuadruple lhsOperand rhsOperand
|
||||||
|
)
|
||||||
|
condition localTable (AST.GreaterOrEqualCondition lhs rhs) = do
|
||||||
|
(lhsOperand, lhsStatements) <- expression localTable lhs
|
||||||
|
(rhsOperand, rhsStatements) <- expression localTable rhs
|
||||||
|
pure
|
||||||
|
( lhsStatements <> rhsStatements
|
||||||
|
, GreaterOrEqualQuadruple lhsOperand rhsOperand
|
||||||
|
)
|
||||||
|
|
||||||
|
variableAccess
|
||||||
|
:: SymbolTable
|
||||||
|
-> AST.VariableAccess
|
||||||
|
-> Maybe (Operand Variable)
|
||||||
|
-> Type
|
||||||
|
-> Vector (Quadruple Variable)
|
||||||
|
-> Glue (AST.Identifier, Maybe (Operand Variable), Vector (Quadruple Variable))
|
||||||
|
variableAccess _ (AST.VariableAccess identifier) accumulatedIndex _ accumulatedStatements =
|
||||||
|
pure (identifier, accumulatedIndex, accumulatedStatements)
|
||||||
|
{- variableAccess localTable (AST.ArrayAccess access1 index1) Nothing (ArrayType _ baseType) _ = do
|
||||||
|
(indexPlace, statements) <- expression localTable index1
|
||||||
|
variableAccess localTable access1 (Just indexPlace) baseType statements
|
||||||
|
variableAccess localTable (AST.ArrayAccess arrayAccess' arrayIndex) (Just baseIndex) (ArrayType arraySize baseType) statements = do
|
||||||
|
(indexPlace, statements') <- expression localTable arrayIndex
|
||||||
|
resultVariable <- createTemporary
|
||||||
|
let resultOperand = VariableOperand resultVariable
|
||||||
|
indexCalculation = Vector.fromList
|
||||||
|
[ ProductQuadruple (IntOperand $ fromIntegral arraySize) baseIndex resultVariable
|
||||||
|
, AddQuadruple indexPlace resultOperand resultVariable
|
||||||
|
]
|
||||||
|
in variableAccess localTable arrayAccess' (Just resultOperand) baseType
|
||||||
|
$ statements <> indexCalculation <> statements'
|
||||||
|
variableAccess _ _ _ _ _ = error "Array access operator doesn't match the type."
|
||||||
|
-}
|
||||||
|
variableType :: AST.VariableAccess -> SymbolTable -> Type
|
||||||
|
variableType (AST.VariableAccess identifier) symbolTable
|
||||||
|
| Just (TypeInfo type') <- SymbolTable.lookup identifier symbolTable = type'
|
||||||
|
| otherwise = error "Undefined type."
|
||||||
|
{-variableType (AST.ArrayAccess arrayAccess' _) symbolTable =
|
||||||
|
variableType arrayAccess' symbolTable -}
|
||||||
|
|
||||||
|
expression :: SymbolTable -> AST.Expression -> Glue (Operand Variable, Vector (Quadruple Variable))
|
||||||
|
expression localTable = \case
|
||||||
|
(AST.LiteralExpression literal') -> pure (literal literal', mempty)
|
||||||
|
(AST.SumExpression lhs rhs) -> binaryExpression AddQuadruple lhs rhs
|
||||||
|
(AST.SubtractionExpression lhs rhs) ->
|
||||||
|
binaryExpression SubtractionQuadruple lhs rhs
|
||||||
|
(AST.NegationExpression negation) -> do
|
||||||
|
(operand, statements) <- expression localTable negation
|
||||||
|
tempVariable <- createTemporary
|
||||||
|
let negationQuadruple = NegationQuadruple operand tempVariable
|
||||||
|
pure
|
||||||
|
( VariableOperand tempVariable
|
||||||
|
, Vector.snoc statements negationQuadruple
|
||||||
|
)
|
||||||
|
(AST.ProductExpression lhs rhs) ->
|
||||||
|
binaryExpression ProductQuadruple lhs rhs
|
||||||
|
(AST.DivisionExpression lhs rhs) ->
|
||||||
|
binaryExpression DivisionQuadruple lhs rhs
|
||||||
|
(AST.VariableExpression variableExpression) -> do
|
||||||
|
let variableType' = variableType variableExpression localTable
|
||||||
|
variableAccess' <- variableAccess localTable variableExpression Nothing variableType' mempty
|
||||||
|
case variableAccess' of
|
||||||
|
(identifier, _Nothing, statements)
|
||||||
|
-> (, statements) . VariableOperand
|
||||||
|
<$> lookupLocal identifier
|
||||||
|
{-(AST.Identifier identifier, Just operand, statements) -> do
|
||||||
|
arrayAddress <- createTemporary
|
||||||
|
let arrayStatement = ArrayQuadruple (Variable identifier) operand arrayAddress
|
||||||
|
pure
|
||||||
|
( VariableOperand arrayAddress
|
||||||
|
, Vector.snoc statements arrayStatement
|
||||||
|
) -}
|
||||||
|
where
|
||||||
|
binaryExpression f lhs rhs = do
|
||||||
|
(lhsOperand, lhsStatements) <- expression localTable lhs
|
||||||
|
(rhsOperand, rhsStatements) <- expression localTable rhs
|
||||||
|
tempVariable <- createTemporary
|
||||||
|
let newQuadruple = f lhsOperand rhsOperand tempVariable
|
||||||
|
pure
|
||||||
|
( VariableOperand tempVariable
|
||||||
|
, Vector.snoc (lhsStatements <> rhsStatements) newQuadruple
|
||||||
|
)
|
||||||
|
|
||||||
|
literal :: AST.Literal -> Operand Variable
|
||||||
|
literal (AST.DecimalLiteral integer) = IntOperand integer
|
||||||
|
literal (AST.HexadecimalLiteral integer) = IntOperand integer
|
||||||
|
literal (AST.CharacterLiteral character) = IntOperand $ fromIntegral character
|
58
lib/Language/Elna/Location.hs
Normal file
58
lib/Language/Elna/Location.hs
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
module Language.Elna.Location
|
||||||
|
( Identifier(..)
|
||||||
|
, Location(..)
|
||||||
|
, Node(..)
|
||||||
|
, showArrayType
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Hashable (Hashable(..))
|
||||||
|
import Data.String (IsString(..))
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text as Text
|
||||||
|
import Data.Word (Word32)
|
||||||
|
|
||||||
|
data Location = Location
|
||||||
|
{ line :: Word32
|
||||||
|
, column :: Word32
|
||||||
|
} deriving (Eq, Show)
|
||||||
|
|
||||||
|
instance Semigroup Location
|
||||||
|
where
|
||||||
|
(Location thisLine thisColumn) <> (Location thatLine thatColumn) = Location
|
||||||
|
{ line = thisLine + thatLine
|
||||||
|
, column = thisColumn + thatColumn
|
||||||
|
}
|
||||||
|
|
||||||
|
instance Monoid Location
|
||||||
|
where
|
||||||
|
mempty = Location{ line = 1, column = 1 }
|
||||||
|
|
||||||
|
data Node a = Node a Location
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
instance Functor Node
|
||||||
|
where
|
||||||
|
fmap f (Node node location) = Node (f node) location
|
||||||
|
|
||||||
|
newtype Identifier = Identifier { unIdentifier :: Text }
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show Identifier
|
||||||
|
where
|
||||||
|
show (Identifier identifier) = Text.unpack identifier
|
||||||
|
|
||||||
|
instance IsString Identifier
|
||||||
|
where
|
||||||
|
fromString = Identifier . Text.pack
|
||||||
|
|
||||||
|
instance Ord Identifier
|
||||||
|
where
|
||||||
|
compare (Identifier lhs) (Identifier rhs) = compare lhs rhs
|
||||||
|
|
||||||
|
instance Hashable Identifier
|
||||||
|
where
|
||||||
|
hashWithSalt salt (Identifier identifier) = hashWithSalt salt identifier
|
||||||
|
|
||||||
|
showArrayType :: (Show a, Show b) => a -> b -> String
|
||||||
|
showArrayType elementCount typeName = concat
|
||||||
|
["array[", show elementCount, "] of ", show typeName]
|
488
lib/Language/Elna/Object/Elf.hs
Normal file
488
lib/Language/Elna/Object/Elf.hs
Normal file
@ -0,0 +1,488 @@
|
|||||||
|
module Language.Elna.Object.Elf
|
||||||
|
( ByteOrder(..)
|
||||||
|
, Elf32_Addr
|
||||||
|
, Elf32_Off
|
||||||
|
, Elf32_Half
|
||||||
|
, Elf32_Word
|
||||||
|
, Elf32_Sword
|
||||||
|
, Elf32_Ehdr(..)
|
||||||
|
, Elf32_Rel(..)
|
||||||
|
, Elf32_Rela(..)
|
||||||
|
, Elf32_Shdr(..)
|
||||||
|
, Elf32_Sym(..)
|
||||||
|
, ElfEncodingError(..)
|
||||||
|
, ElfIdentification(..)
|
||||||
|
, ElfMachine(..)
|
||||||
|
, ElfVersion(..)
|
||||||
|
, ElfClass(..)
|
||||||
|
, ElfData(..)
|
||||||
|
, ElfType(..)
|
||||||
|
, ElfSectionType(..)
|
||||||
|
, ElfSymbolBinding(..)
|
||||||
|
, ElfSymbolType(..)
|
||||||
|
, byteOrder
|
||||||
|
, elf32Addr
|
||||||
|
, elf32Half
|
||||||
|
, elf32Off
|
||||||
|
, elf32Shdr
|
||||||
|
, elf32Sword
|
||||||
|
, elf32Word
|
||||||
|
, elf32Ehdr
|
||||||
|
, elf32Rel
|
||||||
|
, elf32Rela
|
||||||
|
, elf32Sym
|
||||||
|
, elfIdentification
|
||||||
|
, rInfo
|
||||||
|
, shfWrite
|
||||||
|
, shfAlloc
|
||||||
|
, shfExecinstr
|
||||||
|
, shfMascproc
|
||||||
|
, shfInfoLink
|
||||||
|
, stInfo
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Exception (Exception(..))
|
||||||
|
import Data.Bits (Bits(..))
|
||||||
|
import qualified Data.ByteString.Builder as ByteString.Builder
|
||||||
|
import Data.Int (Int32)
|
||||||
|
import Data.Word (Word8, Word16, Word32)
|
||||||
|
import qualified Data.ByteString as ByteString
|
||||||
|
|
||||||
|
-- * Data types.
|
||||||
|
|
||||||
|
type Elf32_Addr = Word32 -- ^ Unsigned program address.
|
||||||
|
type Elf32_Half = Word16 -- ^ Unsigned medium integer.
|
||||||
|
type Elf32_Off = Word32 -- ^ Unsigned file offset.
|
||||||
|
type Elf32_Sword = Int32 -- ^ Signed large integer.
|
||||||
|
type Elf32_Word = Word32 -- ^ Unsigned large integer.
|
||||||
|
|
||||||
|
data ElfClass
|
||||||
|
= ELFCLASSNONE -- ^ Invalid class.
|
||||||
|
| ELFCLASS32 -- ^ 32-bit objects.
|
||||||
|
| ELFCLASS64 -- ^ 64-bit objects.
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show ElfClass
|
||||||
|
where
|
||||||
|
show ELFCLASSNONE = "ELFCLASSNONE"
|
||||||
|
show ELFCLASS32 = "ELFCLASS32"
|
||||||
|
show ELFCLASS64 = "ELFCLASS64"
|
||||||
|
|
||||||
|
instance Enum ElfClass
|
||||||
|
where
|
||||||
|
toEnum 0 = ELFCLASSNONE
|
||||||
|
toEnum 1 = ELFCLASS32
|
||||||
|
toEnum 2 = ELFCLASS64
|
||||||
|
toEnum _ = error "Unknown Elf class"
|
||||||
|
fromEnum ELFCLASSNONE = 0
|
||||||
|
fromEnum ELFCLASS32 = 1
|
||||||
|
fromEnum ELFCLASS64 = 1
|
||||||
|
|
||||||
|
-- | Data encoding.
|
||||||
|
data ElfData
|
||||||
|
= ELFDATANONE
|
||||||
|
| ELFDATA2LSB
|
||||||
|
| ELFDATA2MSB
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show ElfData
|
||||||
|
where
|
||||||
|
show ELFDATANONE = "ELFDATANONE"
|
||||||
|
show ELFDATA2LSB = "ELFDATA2LSB"
|
||||||
|
show ELFDATA2MSB = "ELFDATA2MSB"
|
||||||
|
|
||||||
|
instance Enum ElfData
|
||||||
|
where
|
||||||
|
toEnum 0 = ELFDATANONE
|
||||||
|
toEnum 1 = ELFDATA2LSB
|
||||||
|
toEnum 2 = ELFDATA2MSB
|
||||||
|
toEnum _ = error "Unknown elf data"
|
||||||
|
fromEnum ELFDATANONE = 0
|
||||||
|
fromEnum ELFDATA2LSB = 1
|
||||||
|
fromEnum ELFDATA2MSB = 2
|
||||||
|
|
||||||
|
data ElfIdentification = ElfIdentification ElfClass ElfData
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
-- | ELF header.
|
||||||
|
data Elf32_Ehdr = Elf32_Ehdr
|
||||||
|
{ e_ident :: ElfIdentification
|
||||||
|
, e_type :: ElfType
|
||||||
|
, e_machine :: ElfMachine
|
||||||
|
, e_version :: ElfVersion
|
||||||
|
, e_entry :: Elf32_Addr
|
||||||
|
, e_phoff :: Elf32_Off
|
||||||
|
, e_shoff :: Elf32_Off
|
||||||
|
, e_flags :: Elf32_Word
|
||||||
|
, e_ehsize :: Elf32_Half
|
||||||
|
, e_phentsize :: Elf32_Half
|
||||||
|
, e_phnum :: Elf32_Half
|
||||||
|
, e_shentsize :: Elf32_Half
|
||||||
|
, e_shnum :: Elf32_Half
|
||||||
|
, e_shstrndx :: Elf32_Half
|
||||||
|
} deriving Eq
|
||||||
|
|
||||||
|
-- | Section header.
|
||||||
|
data Elf32_Shdr = Elf32_Shdr
|
||||||
|
{ sh_name :: Elf32_Word
|
||||||
|
, sh_type :: ElfSectionType
|
||||||
|
, sh_flags :: Elf32_Word
|
||||||
|
, sh_addr :: Elf32_Addr
|
||||||
|
, sh_offset :: Elf32_Off
|
||||||
|
, sh_size :: Elf32_Word
|
||||||
|
, sh_link :: Elf32_Word
|
||||||
|
, sh_info :: Elf32_Word
|
||||||
|
, sh_addralign :: Elf32_Word
|
||||||
|
, sh_entsize :: Elf32_Word
|
||||||
|
} deriving Eq
|
||||||
|
|
||||||
|
data ElfMachine
|
||||||
|
= ElfMachine Elf32_Half
|
||||||
|
| EM_NONE -- ^ No machine.
|
||||||
|
| EM_M32 -- ^ AT&T WE 32100.
|
||||||
|
| EM_SPARC -- ^ SPARC.
|
||||||
|
| EM_386 -- ^ Intel Architecture.
|
||||||
|
| EM_68K -- ^ Motorola 68000.
|
||||||
|
| EM_88K -- ^ Motorola 88000.
|
||||||
|
| EM_860 -- ^ Intel 80860.
|
||||||
|
| EM_MIPS -- ^ MIPS RS3000 Big-Endian.
|
||||||
|
| EM_MIPS_RS4_BE -- ^ MIPS RS4000 Big-Endian.
|
||||||
|
| EM_RISCV -- ^ RISC-V.
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Enum ElfMachine
|
||||||
|
where
|
||||||
|
toEnum 0x0 = EM_NONE
|
||||||
|
toEnum 0x1 = EM_M32
|
||||||
|
toEnum 0x2 = EM_SPARC
|
||||||
|
toEnum 0x3 = EM_386
|
||||||
|
toEnum 0x4 = EM_68K
|
||||||
|
toEnum 0x5 = EM_88K
|
||||||
|
toEnum 0x7 = EM_860
|
||||||
|
toEnum 0x8 = EM_MIPS
|
||||||
|
toEnum 0xa = EM_MIPS_RS4_BE
|
||||||
|
toEnum 0xf3 = EM_RISCV
|
||||||
|
toEnum x = ElfMachine $ fromIntegral x
|
||||||
|
fromEnum EM_NONE = 0x0
|
||||||
|
fromEnum EM_M32 = 0x1
|
||||||
|
fromEnum EM_SPARC = 0x2
|
||||||
|
fromEnum EM_386 = 0x3
|
||||||
|
fromEnum EM_68K = 0x4
|
||||||
|
fromEnum EM_88K = 0x5
|
||||||
|
fromEnum EM_860 = 0x7
|
||||||
|
fromEnum EM_MIPS = 0x8
|
||||||
|
fromEnum EM_MIPS_RS4_BE = 0xa
|
||||||
|
fromEnum EM_RISCV = 0xf3
|
||||||
|
fromEnum (ElfMachine x) = fromIntegral x
|
||||||
|
|
||||||
|
data ElfVersion
|
||||||
|
= ElfVersion Elf32_Word
|
||||||
|
| EV_NONE -- ^ Invalid versionn.
|
||||||
|
| EV_CURRENT -- ^ Current version.
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Enum ElfVersion
|
||||||
|
where
|
||||||
|
toEnum 0 = EV_NONE
|
||||||
|
toEnum 1 = EV_CURRENT
|
||||||
|
toEnum x = ElfVersion $ fromIntegral x
|
||||||
|
fromEnum EV_NONE = 0
|
||||||
|
fromEnum EV_CURRENT = 1
|
||||||
|
fromEnum (ElfVersion x) = fromIntegral x
|
||||||
|
|
||||||
|
data ElfType
|
||||||
|
= ElfType Elf32_Half
|
||||||
|
| ET_NONE -- ^ No file type.
|
||||||
|
| ET_REL -- ^ Relocatable file.
|
||||||
|
| ET_EXEC -- ^ Executable file.
|
||||||
|
| ET_DYN -- ^ Shared object file.
|
||||||
|
| ET_CORE -- ^ Core file.
|
||||||
|
| ET_LOPROC -- ^ Processor-specific.
|
||||||
|
| ET_HIPROC -- ^ Processor-specific.
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Enum ElfType
|
||||||
|
where
|
||||||
|
toEnum 0 = ET_NONE
|
||||||
|
toEnum 1 = ET_REL
|
||||||
|
toEnum 2 = ET_EXEC
|
||||||
|
toEnum 3 = ET_DYN
|
||||||
|
toEnum 4 = ET_CORE
|
||||||
|
toEnum 0xff00 = ET_LOPROC
|
||||||
|
toEnum 0xffff = ET_HIPROC
|
||||||
|
toEnum x = ElfType $ fromIntegral x
|
||||||
|
fromEnum ET_NONE = 0
|
||||||
|
fromEnum ET_REL = 1
|
||||||
|
fromEnum ET_EXEC = 2
|
||||||
|
fromEnum ET_DYN = 3
|
||||||
|
fromEnum ET_CORE = 4
|
||||||
|
fromEnum ET_LOPROC = 0xff00
|
||||||
|
fromEnum ET_HIPROC = 0xffff
|
||||||
|
fromEnum (ElfType x) = fromIntegral x
|
||||||
|
|
||||||
|
data Elf32_Sym = Elf32_Sym
|
||||||
|
{ st_name :: Elf32_Word
|
||||||
|
, st_value :: Elf32_Addr
|
||||||
|
, st_size :: Elf32_Word
|
||||||
|
, st_info :: Word8
|
||||||
|
, st_other :: Word8
|
||||||
|
, st_shndx :: Elf32_Half
|
||||||
|
} deriving Eq
|
||||||
|
|
||||||
|
data ElfSymbolBinding
|
||||||
|
= ElfSymbolBinding Word8
|
||||||
|
| STB_LOCAL
|
||||||
|
| STB_GLOBAL
|
||||||
|
| STB_WEAK
|
||||||
|
| STB_LOPROC
|
||||||
|
| STB_HIPROC
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Enum ElfSymbolBinding
|
||||||
|
where
|
||||||
|
toEnum 0 = STB_LOCAL
|
||||||
|
toEnum 1 = STB_GLOBAL
|
||||||
|
toEnum 2 = STB_WEAK
|
||||||
|
toEnum 13 = STB_LOPROC
|
||||||
|
toEnum 15 = STB_HIPROC
|
||||||
|
toEnum x = ElfSymbolBinding $ fromIntegral x
|
||||||
|
fromEnum STB_LOCAL = 0
|
||||||
|
fromEnum STB_GLOBAL = 1
|
||||||
|
fromEnum STB_WEAK = 2
|
||||||
|
fromEnum STB_LOPROC = 13
|
||||||
|
fromEnum STB_HIPROC = 15
|
||||||
|
fromEnum (ElfSymbolBinding x) = fromIntegral x
|
||||||
|
|
||||||
|
data ElfSymbolType
|
||||||
|
= ElfSymbolType Word8
|
||||||
|
| STT_NOTYPE
|
||||||
|
| STT_OBJECT
|
||||||
|
| STT_FUNC
|
||||||
|
| STT_SECTION
|
||||||
|
| STT_FILE
|
||||||
|
| STT_LOPROC
|
||||||
|
| STT_HIPROC
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Enum ElfSymbolType
|
||||||
|
where
|
||||||
|
toEnum 0 = STT_NOTYPE
|
||||||
|
toEnum 1 = STT_OBJECT
|
||||||
|
toEnum 2 = STT_FUNC
|
||||||
|
toEnum 3 = STT_SECTION
|
||||||
|
toEnum 4 = STT_FILE
|
||||||
|
toEnum 13 = STT_LOPROC
|
||||||
|
toEnum 15 = STT_HIPROC
|
||||||
|
toEnum x = ElfSymbolType $ fromIntegral x
|
||||||
|
fromEnum STT_NOTYPE = 0
|
||||||
|
fromEnum STT_OBJECT = 1
|
||||||
|
fromEnum STT_FUNC = 2
|
||||||
|
fromEnum STT_SECTION = 3
|
||||||
|
fromEnum STT_FILE = 4
|
||||||
|
fromEnum STT_LOPROC = 13
|
||||||
|
fromEnum STT_HIPROC = 15
|
||||||
|
fromEnum (ElfSymbolType x) = fromIntegral x
|
||||||
|
|
||||||
|
data Elf32_Rel = Elf32_Rel
|
||||||
|
{ r_offset :: Elf32_Addr
|
||||||
|
, r_info :: Elf32_Word
|
||||||
|
} deriving Eq
|
||||||
|
|
||||||
|
data Elf32_Rela = Elf32_Rela
|
||||||
|
{ r_offset :: Elf32_Addr
|
||||||
|
, r_info :: Elf32_Word
|
||||||
|
, r_addend :: Elf32_Sword
|
||||||
|
} deriving Eq
|
||||||
|
|
||||||
|
data ElfSectionType
|
||||||
|
= ElfSectionType Elf32_Word
|
||||||
|
| SHT_NULL
|
||||||
|
| SHT_PROGBITS
|
||||||
|
| SHT_SYMTAB
|
||||||
|
| SHT_STRTAB
|
||||||
|
| SHT_RELA
|
||||||
|
| SHT_HASH
|
||||||
|
| SHT_DYNAMIC
|
||||||
|
| SHT_NOTE
|
||||||
|
| SHT_NOBITS
|
||||||
|
| SHT_REL
|
||||||
|
| SHT_SHLIB
|
||||||
|
| SHT_DYNSYM
|
||||||
|
| SHT_LOPROC
|
||||||
|
| SHT_HIPROC
|
||||||
|
| SHT_LOUSER
|
||||||
|
| SHT_HIUSER
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Enum ElfSectionType
|
||||||
|
where
|
||||||
|
toEnum 0 = SHT_NULL
|
||||||
|
toEnum 1 = SHT_PROGBITS
|
||||||
|
toEnum 2 = SHT_SYMTAB
|
||||||
|
toEnum 3 = SHT_STRTAB
|
||||||
|
toEnum 4 = SHT_RELA
|
||||||
|
toEnum 5 = SHT_HASH
|
||||||
|
toEnum 6 = SHT_DYNAMIC
|
||||||
|
toEnum 7 = SHT_NOTE
|
||||||
|
toEnum 8 = SHT_NOBITS
|
||||||
|
toEnum 9 = SHT_REL
|
||||||
|
toEnum 10 = SHT_SHLIB
|
||||||
|
toEnum 11 = SHT_DYNSYM
|
||||||
|
toEnum 0x70000000 = SHT_LOPROC
|
||||||
|
toEnum 0x7fffffff = SHT_HIPROC
|
||||||
|
toEnum 0x80000000 = SHT_LOUSER
|
||||||
|
toEnum 0xffffffff = SHT_HIUSER
|
||||||
|
toEnum x = ElfSectionType $ fromIntegral x
|
||||||
|
fromEnum SHT_NULL = 0
|
||||||
|
fromEnum SHT_PROGBITS = 1
|
||||||
|
fromEnum SHT_SYMTAB = 2
|
||||||
|
fromEnum SHT_STRTAB = 3
|
||||||
|
fromEnum SHT_RELA = 4
|
||||||
|
fromEnum SHT_HASH = 5
|
||||||
|
fromEnum SHT_DYNAMIC = 6
|
||||||
|
fromEnum SHT_NOTE = 7
|
||||||
|
fromEnum SHT_NOBITS = 8
|
||||||
|
fromEnum SHT_REL = 9
|
||||||
|
fromEnum SHT_SHLIB = 10
|
||||||
|
fromEnum SHT_DYNSYM = 11
|
||||||
|
fromEnum SHT_LOPROC = 0x70000000
|
||||||
|
fromEnum SHT_HIPROC = 0x7fffffff
|
||||||
|
fromEnum SHT_LOUSER = 0x80000000
|
||||||
|
fromEnum SHT_HIUSER = 0xffffffff
|
||||||
|
fromEnum (ElfSectionType x) = fromIntegral x
|
||||||
|
|
||||||
|
-- * Constants.
|
||||||
|
|
||||||
|
shfWrite :: Elf32_Word
|
||||||
|
shfWrite = 0x1
|
||||||
|
|
||||||
|
shfAlloc :: Elf32_Word
|
||||||
|
shfAlloc = 0x2
|
||||||
|
|
||||||
|
shfExecinstr:: Elf32_Word
|
||||||
|
shfExecinstr = 0x4
|
||||||
|
|
||||||
|
shfMascproc :: Elf32_Word
|
||||||
|
shfMascproc = 0xf0000000
|
||||||
|
|
||||||
|
shfInfoLink :: Elf32_Word
|
||||||
|
shfInfoLink = 0x40
|
||||||
|
|
||||||
|
-- * Encoding functions.
|
||||||
|
|
||||||
|
elf32Addr :: ByteOrder -> Elf32_Addr -> ByteString.Builder.Builder
|
||||||
|
elf32Addr LSB = ByteString.Builder.word32LE
|
||||||
|
elf32Addr MSB = ByteString.Builder.word32BE
|
||||||
|
|
||||||
|
elf32Half :: ByteOrder -> Elf32_Half -> ByteString.Builder.Builder
|
||||||
|
elf32Half LSB = ByteString.Builder.word16LE
|
||||||
|
elf32Half MSB = ByteString.Builder.word16BE
|
||||||
|
|
||||||
|
elf32Off :: ByteOrder -> Elf32_Off -> ByteString.Builder.Builder
|
||||||
|
elf32Off LSB = ByteString.Builder.word32LE
|
||||||
|
elf32Off MSB = ByteString.Builder.word32BE
|
||||||
|
|
||||||
|
elf32Sword :: ByteOrder -> Elf32_Sword -> ByteString.Builder.Builder
|
||||||
|
elf32Sword LSB = ByteString.Builder.int32LE
|
||||||
|
elf32Sword MSB = ByteString.Builder.int32BE
|
||||||
|
|
||||||
|
elf32Word :: ByteOrder -> Elf32_Word -> ByteString.Builder.Builder
|
||||||
|
elf32Word LSB = ByteString.Builder.word32LE
|
||||||
|
elf32Word MSB = ByteString.Builder.word32BE
|
||||||
|
|
||||||
|
elfIdentification :: ElfIdentification -> ByteString.Builder.Builder
|
||||||
|
elfIdentification (ElfIdentification elfClass elfData)
|
||||||
|
= ByteString.Builder.word8 0x7f
|
||||||
|
<> ByteString.Builder.string7 "ELF"
|
||||||
|
<> ByteString.Builder.word8 (fromIntegralEnum elfClass)
|
||||||
|
<> ByteString.Builder.word8 (fromIntegralEnum elfData)
|
||||||
|
<> ByteString.Builder.word8 (fromIntegralEnum EV_CURRENT)
|
||||||
|
<> ByteString.Builder.byteString (ByteString.replicate 9 0)
|
||||||
|
|
||||||
|
elf32Ehdr :: Elf32_Ehdr -> Either ElfEncodingError ByteString.Builder.Builder
|
||||||
|
elf32Ehdr Elf32_Ehdr{..} = encode <$> byteOrder e_ident
|
||||||
|
where
|
||||||
|
encode byteOrder'
|
||||||
|
= elfIdentification e_ident
|
||||||
|
<> elf32Half byteOrder' (fromIntegralEnum e_type)
|
||||||
|
<> elf32Half byteOrder' (fromIntegralEnum e_machine)
|
||||||
|
<> elf32Word byteOrder' (fromIntegralEnum e_version)
|
||||||
|
<> elf32Addr byteOrder' e_entry
|
||||||
|
<> elf32Off byteOrder' e_phoff
|
||||||
|
<> elf32Off byteOrder' e_shoff
|
||||||
|
<> elf32Word byteOrder' e_flags
|
||||||
|
<> elf32Half byteOrder' e_ehsize
|
||||||
|
<> elf32Half byteOrder' e_phentsize
|
||||||
|
<> elf32Half byteOrder' e_phnum
|
||||||
|
<> elf32Half byteOrder' e_shentsize
|
||||||
|
<> elf32Half byteOrder' e_shnum
|
||||||
|
<> elf32Half byteOrder' e_shstrndx
|
||||||
|
|
||||||
|
byteOrder :: ElfIdentification -> Either ElfEncodingError ByteOrder
|
||||||
|
byteOrder (ElfIdentification class' _)
|
||||||
|
| class' /= ELFCLASS32 = Left $ ElfUnsupportedClassError class'
|
||||||
|
byteOrder (ElfIdentification _ ELFDATA2MSB) = Right MSB
|
||||||
|
byteOrder (ElfIdentification _ ELFDATA2LSB) = Right LSB
|
||||||
|
byteOrder (ElfIdentification _ ELFDATANONE) = Left ElfInvalidByteOrderError
|
||||||
|
|
||||||
|
elf32Shdr :: ByteOrder -> Elf32_Shdr -> ByteString.Builder.Builder
|
||||||
|
elf32Shdr byteOrder' Elf32_Shdr{..}
|
||||||
|
= elf32Word byteOrder' sh_name
|
||||||
|
<> elf32Word byteOrder' (fromIntegralEnum sh_type)
|
||||||
|
<> elf32Word byteOrder' sh_flags
|
||||||
|
<> elf32Addr byteOrder' sh_addr
|
||||||
|
<> elf32Off byteOrder' sh_offset
|
||||||
|
<> elf32Word byteOrder' sh_size
|
||||||
|
<> elf32Word byteOrder' sh_link
|
||||||
|
<> elf32Word byteOrder' sh_info
|
||||||
|
<> elf32Word byteOrder' sh_addralign
|
||||||
|
<> elf32Word byteOrder' sh_entsize
|
||||||
|
|
||||||
|
elf32Sym :: ByteOrder -> Elf32_Sym -> ByteString.Builder.Builder
|
||||||
|
elf32Sym byteOrder' Elf32_Sym{..}
|
||||||
|
= elf32Word byteOrder' st_name
|
||||||
|
<> elf32Addr byteOrder' st_value
|
||||||
|
<> elf32Word byteOrder' st_size
|
||||||
|
<> ByteString.Builder.word8 st_info
|
||||||
|
<> ByteString.Builder.word8 st_other
|
||||||
|
<> elf32Half byteOrder' st_shndx
|
||||||
|
|
||||||
|
elf32Rel :: ByteOrder -> Elf32_Rel -> ByteString.Builder.Builder
|
||||||
|
elf32Rel byteOrder' Elf32_Rel{..}
|
||||||
|
= elf32Addr byteOrder' r_offset
|
||||||
|
<> elf32Word byteOrder' r_info
|
||||||
|
|
||||||
|
elf32Rela :: ByteOrder -> Elf32_Rela -> ByteString.Builder.Builder
|
||||||
|
elf32Rela byteOrder' Elf32_Rela{..}
|
||||||
|
= elf32Addr byteOrder' r_offset
|
||||||
|
<> elf32Word byteOrder' r_info
|
||||||
|
<> elf32Sword byteOrder' r_addend
|
||||||
|
|
||||||
|
stInfo :: ElfSymbolBinding -> ElfSymbolType -> Word8
|
||||||
|
stInfo binding type' = fromIntegralEnum binding `shiftL` 4
|
||||||
|
.|. (fromIntegralEnum type' .&. 0xf)
|
||||||
|
|
||||||
|
rInfo :: Elf32_Word -> Word8 -> Elf32_Word
|
||||||
|
rInfo symbol type' = symbol `shiftL` 8
|
||||||
|
.|. fromIntegralEnum type'
|
||||||
|
|
||||||
|
-- * Help types and functions.
|
||||||
|
|
||||||
|
data ByteOrder = LSB | MSB
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
data ElfEncodingError
|
||||||
|
= ElfInvalidByteOrderError
|
||||||
|
| ElfUnsupportedClassError ElfClass
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Show ElfEncodingError
|
||||||
|
where
|
||||||
|
show ElfInvalidByteOrderError = "Invalid byte order."
|
||||||
|
show (ElfUnsupportedClassError class') =
|
||||||
|
concat ["Elf class \"", show class', "\" is not supported."]
|
||||||
|
|
||||||
|
instance Exception ElfEncodingError
|
||||||
|
|
||||||
|
fromIntegralEnum :: (Enum a, Num b) => a -> b
|
||||||
|
fromIntegralEnum = fromIntegral . fromEnum
|
144
lib/Language/Elna/Object/ElfCoder.hs
Normal file
144
lib/Language/Elna/Object/ElfCoder.hs
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
-- | Object file generation.
|
||||||
|
module Language.Elna.Object.ElfCoder
|
||||||
|
( ElfEnvironment(..)
|
||||||
|
, ElfWriter(..)
|
||||||
|
, ElfHeaderResult(..)
|
||||||
|
, UnresolvedRelocation(..)
|
||||||
|
, UnresolvedRelocations(..)
|
||||||
|
, addHeaderToResult
|
||||||
|
, addSectionHeader
|
||||||
|
, elfHeaderSize
|
||||||
|
, elfObject
|
||||||
|
, elfSectionsSize
|
||||||
|
, putSectionHeader
|
||||||
|
, partitionSymbols
|
||||||
|
, module Language.Elna.Object.Elf
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Exception (throwIO)
|
||||||
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
|
import Control.Monad.Trans.State (StateT, runStateT, modify', gets)
|
||||||
|
import Data.Bits (Bits(..))
|
||||||
|
import Data.ByteString (StrictByteString)
|
||||||
|
import qualified Data.ByteString as ByteString
|
||||||
|
import qualified Data.ByteString.Builder as ByteString.Builder
|
||||||
|
import Data.Word (Word8)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import qualified Data.Vector as Vector
|
||||||
|
import System.IO (Handle, IOMode(..), SeekMode(..), hSeek, withFile)
|
||||||
|
import Data.Foldable (traverse_)
|
||||||
|
import Language.Elna.Object.Elf
|
||||||
|
import Language.Elna.Object.StringTable (StringTable)
|
||||||
|
import qualified Language.Elna.Object.StringTable as StringTable
|
||||||
|
import GHC.Records (HasField(..))
|
||||||
|
|
||||||
|
data UnresolvedRelocation = UnresolvedRelocation StrictByteString Elf32_Addr Word8
|
||||||
|
data UnresolvedRelocations =
|
||||||
|
UnresolvedRelocations (Vector UnresolvedRelocation) (ElfHeaderResult Elf32_Sym) Elf32_Word
|
||||||
|
|
||||||
|
data ElfEnvironment = ElfEnvironment
|
||||||
|
{ objectHeaders :: ElfHeaderResult Elf32_Shdr
|
||||||
|
, objectHandle :: Handle
|
||||||
|
}
|
||||||
|
|
||||||
|
newtype ElfWriter a = ElfWriter
|
||||||
|
{ runElfWriter :: StateT ElfEnvironment IO a
|
||||||
|
}
|
||||||
|
|
||||||
|
data ElfHeaderResult a = ElfHeaderResult
|
||||||
|
{ sectionNames :: StringTable
|
||||||
|
, sectionHeaders :: Vector a
|
||||||
|
} deriving Eq
|
||||||
|
|
||||||
|
instance Functor ElfWriter
|
||||||
|
where
|
||||||
|
fmap f (ElfWriter x) = ElfWriter $ f <$> x
|
||||||
|
|
||||||
|
instance Applicative ElfWriter
|
||||||
|
where
|
||||||
|
pure = ElfWriter . pure
|
||||||
|
(ElfWriter f) <*> (ElfWriter x) = ElfWriter $ f <*> x
|
||||||
|
|
||||||
|
instance Monad ElfWriter
|
||||||
|
where
|
||||||
|
(ElfWriter x) >>= f = ElfWriter $ x >>= (runElfWriter . f)
|
||||||
|
|
||||||
|
instance MonadIO ElfWriter
|
||||||
|
where
|
||||||
|
liftIO = ElfWriter . liftIO
|
||||||
|
|
||||||
|
partitionSymbols :: ElfHeaderResult Elf32_Sym -> (Vector Elf32_Sym, Vector Elf32_Sym)
|
||||||
|
partitionSymbols = Vector.partition go . getField @"sectionHeaders"
|
||||||
|
where
|
||||||
|
go Elf32_Sym{ st_info } = (st_info .&. 0xf0) == 0
|
||||||
|
|
||||||
|
-- | ELF header size.
|
||||||
|
elfHeaderSize :: Elf32_Off
|
||||||
|
elfHeaderSize = 52
|
||||||
|
|
||||||
|
-- | Calculates the size of all sections based on the 'sh_size' in the given
|
||||||
|
-- headers and adds 'elfHeaderSize' to it.
|
||||||
|
elfSectionsSize :: Vector Elf32_Shdr -> Elf32_Off
|
||||||
|
elfSectionsSize = (elfHeaderSize +)
|
||||||
|
. Vector.foldr ((+) . sh_size) 0
|
||||||
|
|
||||||
|
addHeaderToResult :: StrictByteString -> a -> ElfHeaderResult a -> ElfHeaderResult a
|
||||||
|
addHeaderToResult name newHeader accumulator@ElfHeaderResult{..} = accumulator
|
||||||
|
{ sectionHeaders = Vector.snoc sectionHeaders newHeader
|
||||||
|
, sectionNames = StringTable.append name sectionNames
|
||||||
|
}
|
||||||
|
|
||||||
|
addSectionHeader :: StrictByteString -> Elf32_Shdr -> ElfWriter ()
|
||||||
|
addSectionHeader name newHeader = ElfWriter $ modify' modifier
|
||||||
|
where
|
||||||
|
modifier elfEnvironment@ElfEnvironment{ objectHeaders } = elfEnvironment
|
||||||
|
{ objectHeaders = addHeaderToResult name newHeader objectHeaders
|
||||||
|
}
|
||||||
|
|
||||||
|
putSectionHeader :: StrictByteString -> Elf32_Shdr -> StrictByteString -> ElfWriter ()
|
||||||
|
putSectionHeader name newHeader encoded = do
|
||||||
|
objectHandle' <- ElfWriter $ gets $ getField @"objectHandle"
|
||||||
|
liftIO $ ByteString.hPut objectHandle' encoded
|
||||||
|
addSectionHeader name newHeader
|
||||||
|
|
||||||
|
-- Writes an ELF object to the provided file path. The callback writes the
|
||||||
|
-- sections, collects headers for those sections and returns the ELF header.
|
||||||
|
elfObject :: FilePath -> ElfWriter Elf32_Ehdr -> IO ()
|
||||||
|
elfObject outFile putContents = withFile outFile WriteMode withObjectFile
|
||||||
|
where
|
||||||
|
withObjectFile objectHandle
|
||||||
|
= hSeek objectHandle AbsoluteSeek (fromIntegral elfHeaderSize)
|
||||||
|
>> putContents' objectHandle
|
||||||
|
>>= uncurry afterContents
|
||||||
|
putContents' objectHandle
|
||||||
|
= flip runStateT (initialState objectHandle)
|
||||||
|
$ runElfWriter putContents
|
||||||
|
zeroHeader = Elf32_Shdr
|
||||||
|
{ sh_type = SHT_NULL
|
||||||
|
, sh_size = 0
|
||||||
|
, sh_offset = 0
|
||||||
|
, sh_name = 0
|
||||||
|
, sh_link = 0
|
||||||
|
, sh_info = 0
|
||||||
|
, sh_flags = 0
|
||||||
|
, sh_entsize = 0
|
||||||
|
, sh_addralign = 0
|
||||||
|
, sh_addr = 0
|
||||||
|
}
|
||||||
|
initialState objectHandle = ElfEnvironment
|
||||||
|
{ objectHeaders = ElfHeaderResult
|
||||||
|
{ sectionHeaders = Vector.singleton zeroHeader
|
||||||
|
, sectionNames = mempty
|
||||||
|
}
|
||||||
|
, objectHandle = objectHandle
|
||||||
|
}
|
||||||
|
afterContents header ElfEnvironment{ objectHeaders = ElfHeaderResult{..}, ..} =
|
||||||
|
let hPutBuilder = ByteString.Builder.hPutBuilder objectHandle
|
||||||
|
writeSectionHeaders byteOrder' =
|
||||||
|
traverse_ (hPutBuilder . elf32Shdr byteOrder') sectionHeaders
|
||||||
|
in either throwIO pure (byteOrder (e_ident header))
|
||||||
|
>>= writeSectionHeaders
|
||||||
|
>> either throwIO (putHeaders objectHandle) (elf32Ehdr header)
|
||||||
|
putHeaders objectHandle encodedHeader
|
||||||
|
= hSeek objectHandle AbsoluteSeek 0
|
||||||
|
>> ByteString.Builder.hPutBuilder objectHandle encodedHeader
|
44
lib/Language/Elna/Object/StringTable.hs
Normal file
44
lib/Language/Elna/Object/StringTable.hs
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
module Language.Elna.Object.StringTable
|
||||||
|
( StringTable
|
||||||
|
, append
|
||||||
|
, elem
|
||||||
|
, index
|
||||||
|
, encode
|
||||||
|
, size
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.ByteString (StrictByteString)
|
||||||
|
import qualified Data.ByteString as ByteString
|
||||||
|
import Language.Elna.Object.Elf
|
||||||
|
import Prelude hiding (elem)
|
||||||
|
|
||||||
|
newtype StringTable = StringTable StrictByteString
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
instance Semigroup StringTable
|
||||||
|
where
|
||||||
|
(StringTable x) <> (StringTable y) = StringTable $ x <> ByteString.drop 1 y
|
||||||
|
|
||||||
|
instance Monoid StringTable
|
||||||
|
where
|
||||||
|
mempty = StringTable "\0"
|
||||||
|
|
||||||
|
size :: StringTable -> Elf32_Word
|
||||||
|
size (StringTable container) =
|
||||||
|
fromIntegral $ ByteString.length container
|
||||||
|
|
||||||
|
elem :: StrictByteString -> StringTable -> Bool
|
||||||
|
elem needle (StringTable container) =
|
||||||
|
("\0" <> needle <> "\0") `ByteString.isInfixOf` container
|
||||||
|
|
||||||
|
append :: StrictByteString -> StringTable -> StringTable
|
||||||
|
append element (StringTable container) =
|
||||||
|
StringTable $ container <> element <> "\0"
|
||||||
|
|
||||||
|
index :: Elf32_Word -> StringTable -> StrictByteString
|
||||||
|
index stringTableIndex (StringTable stringTable)
|
||||||
|
= ByteString.takeWhile (/= 0)
|
||||||
|
$ ByteString.drop (fromIntegral stringTableIndex) stringTable
|
||||||
|
|
||||||
|
encode :: StringTable -> StrictByteString
|
||||||
|
encode (StringTable container) = container
|
444
lib/Language/Elna/RiscV/CodeGenerator.hs
Normal file
444
lib/Language/Elna/RiscV/CodeGenerator.hs
Normal file
@ -0,0 +1,444 @@
|
|||||||
|
module Language.Elna.RiscV.CodeGenerator
|
||||||
|
( Directive(..)
|
||||||
|
, Statement(..)
|
||||||
|
, generateRiscV
|
||||||
|
, riscVConfiguration
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Control.Monad.Trans.State (State, get, evalState, modify')
|
||||||
|
import Data.HashMap.Strict (HashMap)
|
||||||
|
import qualified Data.HashMap.Strict as HashMap
|
||||||
|
import Data.Int (Int32)
|
||||||
|
import Data.Word (Word32)
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import qualified Data.Vector as Vector
|
||||||
|
import qualified Language.Elna.Architecture.RiscV as RiscV
|
||||||
|
import Language.Elna.Backend.Allocator (MachineConfiguration(..), Store(..))
|
||||||
|
import Language.Elna.Backend.Intermediate
|
||||||
|
( Label(..)
|
||||||
|
, Operand(..)
|
||||||
|
, ProcedureQuadruples(..)
|
||||||
|
, Quadruple(..)
|
||||||
|
)
|
||||||
|
import Language.Elna.Location (Identifier(..))
|
||||||
|
import Data.Bits (Bits(..))
|
||||||
|
import Data.Foldable (Foldable(..), foldlM)
|
||||||
|
import Data.Text (Text)
|
||||||
|
import qualified Data.Text.Lazy.Builder.Int as Text.Builder
|
||||||
|
import qualified Data.Text.Lazy.Builder as Text.Builder
|
||||||
|
import qualified Data.Text.Lazy as Text.Lazy
|
||||||
|
|
||||||
|
data Directive
|
||||||
|
= GlobalDirective
|
||||||
|
| FunctionDirective
|
||||||
|
deriving (Eq, Show)
|
||||||
|
|
||||||
|
data Statement
|
||||||
|
= Instruction RiscV.Instruction
|
||||||
|
| JumpLabel Text [Directive]
|
||||||
|
deriving Eq
|
||||||
|
|
||||||
|
riscVConfiguration :: MachineConfiguration RiscV.XRegister
|
||||||
|
riscVConfiguration = MachineConfiguration
|
||||||
|
{ temporaryRegisters =
|
||||||
|
[ RiscV.T0
|
||||||
|
, RiscV.T1
|
||||||
|
, RiscV.T2
|
||||||
|
, RiscV.T3
|
||||||
|
, RiscV.T4
|
||||||
|
, RiscV.T5
|
||||||
|
, RiscV.T6
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
-- | Reserved register used for calculations to save an immediate temporary.
|
||||||
|
immediateRegister :: RiscV.XRegister
|
||||||
|
immediateRegister = RiscV.A7
|
||||||
|
|
||||||
|
type RiscVStore = Store RiscV.XRegister
|
||||||
|
type RiscVQuadruple = Quadruple RiscVStore
|
||||||
|
type RiscVOperand = Operand RiscVStore
|
||||||
|
|
||||||
|
newtype RiscVGenerator a = RiscVGenerator
|
||||||
|
{ runRiscVGenerator :: State Word32 a }
|
||||||
|
|
||||||
|
instance Functor RiscVGenerator
|
||||||
|
where
|
||||||
|
fmap f (RiscVGenerator x) = RiscVGenerator $ f <$> x
|
||||||
|
|
||||||
|
instance Applicative RiscVGenerator
|
||||||
|
where
|
||||||
|
pure = RiscVGenerator . pure
|
||||||
|
(RiscVGenerator f) <*> (RiscVGenerator x) = RiscVGenerator $ f <*> x
|
||||||
|
|
||||||
|
instance Monad RiscVGenerator
|
||||||
|
where
|
||||||
|
(RiscVGenerator x) >>= f = RiscVGenerator $ x >>= (runRiscVGenerator . f)
|
||||||
|
|
||||||
|
createLabel :: RiscVGenerator Text
|
||||||
|
createLabel = do
|
||||||
|
currentCounter <- RiscVGenerator get
|
||||||
|
RiscVGenerator $ modify' (+ 1)
|
||||||
|
pure
|
||||||
|
$ mappend ".A"
|
||||||
|
$ Text.Lazy.toStrict
|
||||||
|
$ Text.Builder.toLazyText
|
||||||
|
$ Text.Builder.decimal currentCounter
|
||||||
|
|
||||||
|
generateRiscV :: HashMap Identifier (ProcedureQuadruples RiscVStore) -> Vector Statement
|
||||||
|
generateRiscV = flip evalState 0
|
||||||
|
. runRiscVGenerator
|
||||||
|
. foldlM go Vector.empty
|
||||||
|
. HashMap.toList
|
||||||
|
where
|
||||||
|
go accumulator (Identifier key, ProcedureQuadruples{ quadruples = value }) =
|
||||||
|
let code = Vector.cons (JumpLabel key [GlobalDirective, FunctionDirective])
|
||||||
|
. fold <$> mapM quadruple value
|
||||||
|
in (accumulator <>) <$> code
|
||||||
|
|
||||||
|
quadruple :: RiscVQuadruple -> RiscVGenerator (Vector Statement)
|
||||||
|
quadruple StartQuadruple = pure $ Vector.fromList
|
||||||
|
[ Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.SP RiscV.ADDI RiscV.SP (negate 4))
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.Store $ RiscV.S 0 RiscV.SW RiscV.SP RiscV.S0)
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.Store $ RiscV.S 4 RiscV.SW RiscV.SP RiscV.RA)
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.S0 RiscV.ADDI RiscV.SP 4)
|
||||||
|
]
|
||||||
|
quadruple (ParameterQuadruple operand1) =
|
||||||
|
let (operandRegister, statements) = loadImmediateOrRegister operand1 RiscV.A0
|
||||||
|
in pure $ mappend statements $ Vector.fromList
|
||||||
|
[ Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.SP RiscV.ADDI RiscV.SP (negate 4))
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.Store $ RiscV.S 0 RiscV.SW RiscV.SP operandRegister)
|
||||||
|
]
|
||||||
|
quadruple (CallQuadruple callName numberOfArguments) = pure $ Vector.fromList
|
||||||
|
[ Instruction (RiscV.CallInstruction callName)
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.SP RiscV.ADDI RiscV.SP (numberOfArguments * 4))
|
||||||
|
]
|
||||||
|
quadruple StopQuadruple = pure $ Vector.fromList
|
||||||
|
[ Instruction (RiscV.BaseInstruction RiscV.Load $ RiscV.I RiscV.S0 RiscV.LW RiscV.SP 0)
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.Load $ RiscV.I RiscV.RA RiscV.LW RiscV.SP 4)
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.SP RiscV.ADDI RiscV.SP 4)
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.Jalr $ RiscV.I RiscV.RA RiscV.JALR RiscV.Zero 0)
|
||||||
|
]
|
||||||
|
quadruple (AddQuadruple operand1 operand2 store) =
|
||||||
|
commutativeBinary (+) RiscV.ADD (RiscV.Funct7 0b0000000) (operand1, operand2) store
|
||||||
|
quadruple (ProductQuadruple operand1 operand2 store) =
|
||||||
|
commutativeBinary (*) RiscV.MUL (RiscV.Funct7 0b0000001) (operand1, operand2) store
|
||||||
|
quadruple (SubtractionQuadruple operand1 operand2 store)
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
in pure $ lui (immediateOperand1 - immediateOperand2) storeRegister <> storeStatements
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
instruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister RiscV.SUB operandRegister1 operandRegister2
|
||||||
|
$ RiscV.Funct7 0b0100000
|
||||||
|
in pure $ statements1 <> statements2 <> Vector.cons instruction storeStatements
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
statements1 = lui immediateOperand1 storeRegister
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
instruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister RiscV.SUB storeRegister operandRegister2
|
||||||
|
$ RiscV.Funct7 0b0100000
|
||||||
|
in pure $ statements1 <> statements2 <> Vector.cons instruction storeStatements
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
statements2 = lui (negate immediateOperand2) storeRegister
|
||||||
|
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
instruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister RiscV.ADD storeRegister operandRegister1
|
||||||
|
$ RiscV.Funct7 0b0000000
|
||||||
|
in pure $ statements1 <> statements2 <> Vector.cons instruction storeStatements
|
||||||
|
quadruple (NegationQuadruple operand1 store)
|
||||||
|
| IntOperand immediateOperand1 <- operand1 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
in pure $ lui (negate immediateOperand1) storeRegister <> storeStatements
|
||||||
|
| VariableOperand variableOperand1 <- operand1 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
instruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister RiscV.SUB RiscV.Zero operandRegister1
|
||||||
|
$ RiscV.Funct7 0b0100000
|
||||||
|
in pure $ statements1 <> Vector.cons instruction storeStatements
|
||||||
|
quadruple (DivisionQuadruple operand1 operand2 store)
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
if immediateOperand2 == 0
|
||||||
|
then pure $ Vector.singleton
|
||||||
|
$ Instruction (RiscV.CallInstruction "_divide_by_zero_error")
|
||||||
|
else
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
in pure $ lui (quot immediateOperand1 immediateOperand2) storeRegister <> storeStatements
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 = do
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
divisionInstruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister RiscV.DIV operandRegister1 operandRegister2 (RiscV.Funct7 0b0000001)
|
||||||
|
branchLabel <- createLabel
|
||||||
|
let branchInstruction = Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch branchLabel RiscV.BNE RiscV.Zero operandRegister2
|
||||||
|
pure $ statements1 <> statements2 <> Vector.fromList
|
||||||
|
[ branchInstruction
|
||||||
|
, Instruction (RiscV.CallInstruction "_divide_by_zero_error")
|
||||||
|
, JumpLabel branchLabel []
|
||||||
|
, divisionInstruction
|
||||||
|
] <> storeStatements
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
statements2 = lui immediateOperand2 storeRegister
|
||||||
|
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
operationInstruction
|
||||||
|
| immediateOperand2 == 0 =
|
||||||
|
RiscV.CallInstruction "_divide_by_zero_error"
|
||||||
|
| otherwise = RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister RiscV.DIV operandRegister1 storeRegister
|
||||||
|
$ RiscV.Funct7 0b0000001
|
||||||
|
in pure $ statements1 <> statements2
|
||||||
|
<> Vector.cons (Instruction operationInstruction) storeStatements
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 = do
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
statements1 = lui immediateOperand1 storeRegister
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
divisionInstruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister RiscV.DIV storeRegister operandRegister2 (RiscV.Funct7 0b0000001)
|
||||||
|
branchLabel <- createLabel
|
||||||
|
let branchInstruction = Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch branchLabel RiscV.BNE RiscV.Zero operandRegister2
|
||||||
|
pure $ statements1 <> statements2 <> Vector.fromList
|
||||||
|
[ branchInstruction
|
||||||
|
, Instruction (RiscV.CallInstruction "_divide_by_zero_error")
|
||||||
|
, JumpLabel branchLabel []
|
||||||
|
, divisionInstruction
|
||||||
|
] <> storeStatements
|
||||||
|
quadruple (LabelQuadruple (Label label)) = pure $ Vector.singleton $ JumpLabel label mempty
|
||||||
|
quadruple (GoToQuadruple label) = pure $ Vector.singleton $ unconditionalJal label
|
||||||
|
quadruple (EqualQuadruple operand1 operand2 goToLabel) =
|
||||||
|
commutativeComparison (==) RiscV.BEQ (operand1, operand2) goToLabel
|
||||||
|
quadruple (NonEqualQuadruple operand1 operand2 goToLabel) =
|
||||||
|
commutativeComparison (/=) RiscV.BNE (operand1, operand2) goToLabel
|
||||||
|
quadruple (LessQuadruple operand1 operand2 goToLabel) =
|
||||||
|
lessThan (operand1, operand2) goToLabel
|
||||||
|
quadruple (GreaterQuadruple operand1 operand2 goToLabel) =
|
||||||
|
lessThan (operand2, operand1) goToLabel
|
||||||
|
quadruple (LessOrEqualQuadruple operand1 operand2 goToLabel) =
|
||||||
|
lessOrEqualThan (operand1, operand2) goToLabel
|
||||||
|
quadruple (GreaterOrEqualQuadruple operand1 operand2 goToLabel) =
|
||||||
|
lessOrEqualThan (operand2, operand1) goToLabel
|
||||||
|
quadruple (AssignQuadruple operand1 store)
|
||||||
|
| IntOperand immediateOperand1 <- operand1 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
in pure $ lui immediateOperand1 storeRegister <> storeStatements
|
||||||
|
| VariableOperand variableOperand1 <- operand1 =
|
||||||
|
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
(storeRegister, storeStatements) = storeToStore store
|
||||||
|
instruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.OpImm
|
||||||
|
$ RiscV.I storeRegister RiscV.ADDI operandRegister1 0
|
||||||
|
in pure $ statements1 <> Vector.cons instruction storeStatements
|
||||||
|
|
||||||
|
unconditionalJal :: Label -> Statement
|
||||||
|
unconditionalJal (Label goToLabel) = Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Jal
|
||||||
|
$ RiscV.RJal RiscV.Zero goToLabel
|
||||||
|
|
||||||
|
loadImmediateOrRegister :: RiscVOperand -> RiscV.XRegister -> (RiscV.XRegister, Vector Statement)
|
||||||
|
loadImmediateOrRegister (IntOperand intValue) targetRegister =
|
||||||
|
(targetRegister, lui intValue targetRegister)
|
||||||
|
loadImmediateOrRegister (VariableOperand store) _ = loadFromStore store
|
||||||
|
|
||||||
|
lui :: Int32 -> RiscV.XRegister -> Vector Statement
|
||||||
|
lui intValue targetRegister
|
||||||
|
| intValue >= -2048
|
||||||
|
, intValue <= 2047 = Vector.singleton
|
||||||
|
$ Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I targetRegister RiscV.ADDI RiscV.Zero lo)
|
||||||
|
| intValue .&. 0x800 /= 0 = Vector.fromList
|
||||||
|
[ Instruction (RiscV.BaseInstruction RiscV.Lui $ RiscV.U targetRegister $ fromIntegral $ succ hi)
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I targetRegister RiscV.ADDI targetRegister lo)
|
||||||
|
]
|
||||||
|
| otherwise = Vector.fromList
|
||||||
|
[ Instruction (RiscV.BaseInstruction RiscV.Lui $ RiscV.U targetRegister $ fromIntegral hi)
|
||||||
|
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I targetRegister RiscV.ADDI targetRegister lo)
|
||||||
|
]
|
||||||
|
where
|
||||||
|
hi = intValue `shiftR` 12
|
||||||
|
lo = fromIntegral intValue
|
||||||
|
|
||||||
|
commutativeBinary
|
||||||
|
:: (Int32 -> Int32 -> Int32)
|
||||||
|
-> RiscV.Funct3
|
||||||
|
-> RiscV.Funct7
|
||||||
|
-> (Operand RiscVStore, Operand RiscVStore)
|
||||||
|
-> Store RiscV.XRegister
|
||||||
|
-> RiscVGenerator (Vector Statement)
|
||||||
|
commutativeBinary immediateOperation funct3 funct7 (operand1, operand2) store
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
immediateOperation' = immediateOperation immediateOperand1 immediateOperand2
|
||||||
|
in pure $ lui immediateOperation' storeRegister <> storeStatements
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 =
|
||||||
|
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
(storeRegister, storeStatements) = storeToStore store
|
||||||
|
instruction = Instruction $ RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister funct3 operandRegister1 operandRegister2 funct7
|
||||||
|
in pure $ statements1 <> statements2
|
||||||
|
<> Vector.cons instruction storeStatements
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
commutativeImmediateRegister variableOperand1 immediateOperand2
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 =
|
||||||
|
commutativeImmediateRegister variableOperand2 immediateOperand1
|
||||||
|
where
|
||||||
|
commutativeImmediateRegister variableOperand immediateOperand =
|
||||||
|
let (storeRegister, storeStatements) = storeToStore store
|
||||||
|
immediateStatements = lui immediateOperand storeRegister
|
||||||
|
(operandRegister, registerStatements) = loadFromStore variableOperand
|
||||||
|
instruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Op
|
||||||
|
$ RiscV.R storeRegister funct3 storeRegister operandRegister funct7
|
||||||
|
in pure $ immediateStatements <> registerStatements
|
||||||
|
<> Vector.cons instruction storeStatements
|
||||||
|
|
||||||
|
commutativeComparison
|
||||||
|
:: (Int32 -> Int32 -> Bool)
|
||||||
|
-> RiscV.Funct3
|
||||||
|
-> (Operand RiscVStore, Operand RiscVStore)
|
||||||
|
-> Label
|
||||||
|
-> RiscVGenerator (Vector Statement)
|
||||||
|
commutativeComparison immediateOperation funct3 (operand1, operand2) goToLabel
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
if immediateOperation immediateOperand1 immediateOperand2
|
||||||
|
then pure $ Vector.singleton $ unconditionalJal goToLabel
|
||||||
|
else pure Vector.empty
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 = do
|
||||||
|
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
Label goToLabel' = goToLabel
|
||||||
|
pure $ Vector.snoc (statements1 <> statements2)
|
||||||
|
$ Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch goToLabel' funct3 operandRegister1 operandRegister2
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
compareImmediateRegister variableOperand1 immediateOperand2
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 =
|
||||||
|
compareImmediateRegister variableOperand2 immediateOperand1
|
||||||
|
where
|
||||||
|
compareImmediateRegister variableOperand immediateOperand =
|
||||||
|
let immediateStatements = lui immediateOperand immediateRegister
|
||||||
|
(operandRegister, registerStatements) = loadFromStore variableOperand
|
||||||
|
Label goToLabel' = goToLabel
|
||||||
|
in pure $ Vector.snoc (immediateStatements <> registerStatements)
|
||||||
|
$ Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch goToLabel' funct3 operandRegister immediateRegister
|
||||||
|
|
||||||
|
lessThan :: (Operand RiscVStore, Operand RiscVStore) -> Label -> RiscVGenerator (Vector Statement)
|
||||||
|
lessThan (operand1, operand2) goToLabel
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
if immediateOperand1 < immediateOperand2
|
||||||
|
then pure $ Vector.singleton $ unconditionalJal goToLabel
|
||||||
|
else pure Vector.empty
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 = do
|
||||||
|
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
Label goToLabel' = goToLabel
|
||||||
|
pure $ Vector.snoc (statements1 <> statements2)
|
||||||
|
$ Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch goToLabel' RiscV.BLT operandRegister1 operandRegister2
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
let statements2 = lui immediateOperand2 immediateRegister
|
||||||
|
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
Label goToLabel' = goToLabel
|
||||||
|
in pure $ Vector.snoc (statements1 <> statements2)
|
||||||
|
$ Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch goToLabel' RiscV.BLT operandRegister1 immediateRegister
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 =
|
||||||
|
let statements1 = lui immediateOperand1 immediateRegister
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
Label goToLabel' = goToLabel
|
||||||
|
in pure $ Vector.snoc (statements1 <> statements2)
|
||||||
|
$ Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch goToLabel' RiscV.BLT immediateRegister operandRegister2
|
||||||
|
|
||||||
|
lessOrEqualThan :: (Operand RiscVStore, Operand RiscVStore) -> Label -> RiscVGenerator (Vector Statement)
|
||||||
|
lessOrEqualThan (operand1, operand2) goToLabel
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
if immediateOperand1 <= immediateOperand2
|
||||||
|
then pure $ Vector.singleton $ unconditionalJal goToLabel
|
||||||
|
else pure Vector.empty
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 = do
|
||||||
|
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
Label goToLabel' = goToLabel
|
||||||
|
pure $ Vector.snoc (statements1 <> statements2)
|
||||||
|
$ Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch goToLabel' RiscV.BGE operandRegister2 operandRegister1
|
||||||
|
| VariableOperand variableOperand1 <- operand1
|
||||||
|
, IntOperand immediateOperand2 <- operand2 =
|
||||||
|
let statements2 = lui immediateOperand2 immediateRegister
|
||||||
|
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||||
|
Label goToLabel' = goToLabel
|
||||||
|
in pure $ Vector.snoc (statements1 <> statements2)
|
||||||
|
$ Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch goToLabel' RiscV.BGE immediateRegister operandRegister1
|
||||||
|
| IntOperand immediateOperand1 <- operand1
|
||||||
|
, VariableOperand variableOperand2 <- operand2 =
|
||||||
|
let statements1 = lui immediateOperand1 immediateRegister
|
||||||
|
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||||
|
Label goToLabel' = goToLabel
|
||||||
|
in pure $ Vector.snoc (statements1 <> statements2)
|
||||||
|
$ Instruction
|
||||||
|
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||||
|
$ RiscV.RBranch goToLabel' RiscV.BGE operandRegister2 immediateRegister
|
||||||
|
|
||||||
|
loadFromStore :: RiscVStore -> (RiscV.XRegister, Vector Statement)
|
||||||
|
loadFromStore (RegisterStore register) = (register, mempty)
|
||||||
|
loadFromStore (StackStore offset register) =
|
||||||
|
let loadInstruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Load
|
||||||
|
$ RiscV.I register RiscV.LW RiscV.SP offset
|
||||||
|
in (register, Vector.singleton loadInstruction)
|
||||||
|
|
||||||
|
storeToStore :: RiscVStore -> (RiscV.XRegister, Vector Statement)
|
||||||
|
storeToStore (RegisterStore register) = (register, mempty)
|
||||||
|
storeToStore (StackStore offset register) =
|
||||||
|
let storeInstruction = Instruction
|
||||||
|
$ RiscV.BaseInstruction RiscV.Store
|
||||||
|
$ RiscV.S offset RiscV.SW RiscV.SP register
|
||||||
|
in (register, Vector.singleton storeInstruction)
|
334
lib/Language/Elna/RiscV/ElfWriter.hs
Normal file
334
lib/Language/Elna/RiscV/ElfWriter.hs
Normal file
@ -0,0 +1,334 @@
|
|||||||
|
-- | Writer assembler to an object file.
|
||||||
|
module Language.Elna.RiscV.ElfWriter
|
||||||
|
( riscv32Elf
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.ByteString (StrictByteString)
|
||||||
|
import qualified Data.ByteString as ByteString
|
||||||
|
import qualified Data.ByteString.Builder as ByteString.Builder
|
||||||
|
import Data.ByteString.Lazy (LazyByteString)
|
||||||
|
import qualified Data.ByteString.Lazy as LazyByteString
|
||||||
|
import Data.Vector (Vector)
|
||||||
|
import qualified Data.Vector as Vector
|
||||||
|
import Language.Elna.Object.ElfCoder
|
||||||
|
( ByteOrder(..)
|
||||||
|
, Elf32_Ehdr(..)
|
||||||
|
, Elf32_Half
|
||||||
|
, Elf32_Word
|
||||||
|
, Elf32_Sym(..)
|
||||||
|
, ElfMachine(..)
|
||||||
|
, ElfType(..)
|
||||||
|
, ElfVersion(..)
|
||||||
|
, ElfIdentification(..)
|
||||||
|
, ElfClass(..)
|
||||||
|
, ElfData(..)
|
||||||
|
, Elf32_Shdr(..)
|
||||||
|
, ElfSectionType(..)
|
||||||
|
, ElfSymbolBinding(..)
|
||||||
|
, ElfSymbolType(..)
|
||||||
|
, Elf32_Rel(..)
|
||||||
|
, ElfWriter(..)
|
||||||
|
, ElfHeaderResult(..)
|
||||||
|
, ElfEnvironment(..)
|
||||||
|
, UnresolvedRelocation(..)
|
||||||
|
, UnresolvedRelocations(..)
|
||||||
|
, addHeaderToResult
|
||||||
|
, addSectionHeader
|
||||||
|
, elf32Sym
|
||||||
|
, elfHeaderSize
|
||||||
|
, elfSectionsSize
|
||||||
|
, stInfo
|
||||||
|
, rInfo
|
||||||
|
, elf32Rel
|
||||||
|
, shfInfoLink
|
||||||
|
, partitionSymbols
|
||||||
|
, putSectionHeader
|
||||||
|
)
|
||||||
|
import qualified Language.Elna.Architecture.RiscV as RiscV
|
||||||
|
import qualified Data.Text.Encoding as Text
|
||||||
|
import Control.Monad.IO.Class (MonadIO(..))
|
||||||
|
import Control.Monad.Trans.State (get, gets)
|
||||||
|
import Language.Elna.RiscV.CodeGenerator (Directive(..), Statement(..))
|
||||||
|
import Language.Elna.Object.StringTable (StringTable)
|
||||||
|
import qualified Language.Elna.Object.StringTable as StringTable
|
||||||
|
import Data.HashSet (HashSet)
|
||||||
|
import qualified Data.HashSet as HashSet
|
||||||
|
import GHC.Records (HasField(..))
|
||||||
|
|
||||||
|
data TextAccumulator = TextAccumulator
|
||||||
|
{ encodedAccumulator :: LazyByteString
|
||||||
|
, relocationAccumulator :: Vector UnresolvedRelocation
|
||||||
|
, symbolAccumulator :: ElfHeaderResult Elf32_Sym
|
||||||
|
, definitionAccumulator :: HashSet StrictByteString
|
||||||
|
}
|
||||||
|
|
||||||
|
riscv32Elf :: Vector Statement -> ElfWriter Elf32_Ehdr
|
||||||
|
riscv32Elf code = text code
|
||||||
|
>>= symtab
|
||||||
|
>>= uncurry symrel
|
||||||
|
>>= strtab
|
||||||
|
>> shstrtab
|
||||||
|
>>= riscv32Header
|
||||||
|
where
|
||||||
|
riscv32Header :: Elf32_Half -> ElfWriter Elf32_Ehdr
|
||||||
|
riscv32Header shstrndx = do
|
||||||
|
ElfHeaderResult{..} <- ElfWriter $ gets $ getField @"objectHeaders"
|
||||||
|
pure $ Elf32_Ehdr
|
||||||
|
{ e_version = EV_CURRENT
|
||||||
|
, e_type = ET_REL
|
||||||
|
, e_shstrndx = shstrndx
|
||||||
|
, e_shoff = elfSectionsSize sectionHeaders
|
||||||
|
, e_shnum = fromIntegral (Vector.length sectionHeaders)
|
||||||
|
, e_shentsize = 40
|
||||||
|
, e_phoff = 0
|
||||||
|
, e_phnum = 0
|
||||||
|
, e_phentsize = 32
|
||||||
|
, e_machine = EM_RISCV
|
||||||
|
, e_ident = ElfIdentification ELFCLASS32 ELFDATA2LSB
|
||||||
|
, e_flags = 0x4 -- EF_RISCV_FLOAT_ABI_DOUBLE
|
||||||
|
, e_entry = 0
|
||||||
|
, e_ehsize = fromIntegral elfHeaderSize
|
||||||
|
}
|
||||||
|
|
||||||
|
text :: Vector Statement -> ElfWriter UnresolvedRelocations
|
||||||
|
text code = do
|
||||||
|
ElfHeaderResult{..} <- ElfWriter $ gets $ getField @"objectHeaders"
|
||||||
|
let textTabIndex = fromIntegral $ Vector.length sectionHeaders
|
||||||
|
initialHeaders = ElfHeaderResult mempty
|
||||||
|
$ Vector.singleton
|
||||||
|
$ Elf32_Sym
|
||||||
|
{ st_value = 0
|
||||||
|
, st_size = 0
|
||||||
|
, st_shndx = 0
|
||||||
|
, st_other = 0
|
||||||
|
, st_name = 0
|
||||||
|
, st_info = 0
|
||||||
|
}
|
||||||
|
TextAccumulator{..} = encodeFunctions textTabIndex code
|
||||||
|
$ TextAccumulator
|
||||||
|
{ encodedAccumulator = mempty
|
||||||
|
, relocationAccumulator = Vector.empty
|
||||||
|
, symbolAccumulator = initialHeaders
|
||||||
|
, definitionAccumulator = HashSet.empty
|
||||||
|
}
|
||||||
|
size = fromIntegral $ LazyByteString.length encodedAccumulator
|
||||||
|
newHeader = Elf32_Shdr
|
||||||
|
{ sh_type = SHT_PROGBITS
|
||||||
|
, sh_size = size
|
||||||
|
, sh_offset = elfSectionsSize sectionHeaders
|
||||||
|
, sh_name = StringTable.size sectionNames
|
||||||
|
, sh_link = 0
|
||||||
|
, sh_info = 0
|
||||||
|
, sh_flags = 0b110
|
||||||
|
, sh_entsize = 0
|
||||||
|
, sh_addralign = 4
|
||||||
|
, sh_addr = 0
|
||||||
|
}
|
||||||
|
putSectionHeader ".text" newHeader $ LazyByteString.toStrict encodedAccumulator
|
||||||
|
let filterPredicate :: StrictByteString -> Bool
|
||||||
|
filterPredicate = not
|
||||||
|
. (`StringTable.elem` getField @"sectionNames" symbolAccumulator)
|
||||||
|
symbolResult = HashSet.foldl' encodeEmptyDefinitions symbolAccumulator
|
||||||
|
$ HashSet.filter filterPredicate definitionAccumulator
|
||||||
|
pure $ UnresolvedRelocations relocationAccumulator symbolResult
|
||||||
|
$ fromIntegral $ Vector.length sectionHeaders
|
||||||
|
where
|
||||||
|
encodeEmptyDefinitions (ElfHeaderResult names entries) definition =
|
||||||
|
let nextEntry = Elf32_Sym
|
||||||
|
{ st_value = 0
|
||||||
|
, st_size = 0
|
||||||
|
, st_shndx = 0
|
||||||
|
, st_other = 0
|
||||||
|
, st_name = StringTable.size names
|
||||||
|
, st_info = stInfo STB_GLOBAL STT_FUNC
|
||||||
|
}
|
||||||
|
in ElfHeaderResult (StringTable.append definition names)
|
||||||
|
$ Vector.snoc entries nextEntry
|
||||||
|
encodeFunctions shndx instructions textAccumulator
|
||||||
|
| Just (instruction, rest) <- Vector.uncons instructions =
|
||||||
|
case instruction of
|
||||||
|
Instruction _ ->
|
||||||
|
let (textAccumulator', rest') = encodeInstructions shndx (textAccumulator, instructions)
|
||||||
|
in encodeFunctions shndx rest' textAccumulator'
|
||||||
|
JumpLabel labelName directives ->
|
||||||
|
let (TextAccumulator{..}, rest') =
|
||||||
|
encodeInstructions shndx (textAccumulator, rest)
|
||||||
|
newEntry = Elf32_Sym
|
||||||
|
{ st_value = fromIntegral
|
||||||
|
$ LazyByteString.length
|
||||||
|
$ getField @"encodedAccumulator" textAccumulator
|
||||||
|
, st_size = fromIntegral $ LazyByteString.length encodedAccumulator
|
||||||
|
, st_shndx = shndx
|
||||||
|
, st_other = 0
|
||||||
|
, st_name = StringTable.size $ getField @"sectionNames" symbolAccumulator
|
||||||
|
, st_info = stInfo (directivesBinding directives) STT_FUNC
|
||||||
|
}
|
||||||
|
in encodeFunctions shndx rest'
|
||||||
|
$ TextAccumulator
|
||||||
|
{ encodedAccumulator = encodedAccumulator
|
||||||
|
, relocationAccumulator = relocationAccumulator
|
||||||
|
, symbolAccumulator =
|
||||||
|
addHeaderToResult (Text.encodeUtf8 labelName) newEntry symbolAccumulator
|
||||||
|
, definitionAccumulator = definitionAccumulator
|
||||||
|
}
|
||||||
|
| otherwise = textAccumulator
|
||||||
|
directivesBinding directives
|
||||||
|
| GlobalDirective `elem` directives = STB_GLOBAL
|
||||||
|
| otherwise = STB_LOCAL
|
||||||
|
encodeInstructions shndx (TextAccumulator encoded relocations symbolResult definitions, instructions)
|
||||||
|
| Just (Instruction instruction, rest) <- Vector.uncons instructions =
|
||||||
|
let offset = fromIntegral $ LazyByteString.length encoded
|
||||||
|
unresolvedRelocation = case instruction of
|
||||||
|
RiscV.RelocatableInstruction _ instructionType
|
||||||
|
| RiscV.RHigher20 _ symbolName <- instructionType
|
||||||
|
-> Just -- R_RISCV_HI20
|
||||||
|
$ UnresolvedRelocation (Text.encodeUtf8 symbolName) offset 26
|
||||||
|
| RiscV.RLower12I _ _ _ symbolName <- instructionType
|
||||||
|
-> Just -- R_RISCV_LO12_I
|
||||||
|
$ UnresolvedRelocation (Text.encodeUtf8 symbolName) offset 27
|
||||||
|
| RiscV.RLower12S symbolName _ _ _ <- instructionType
|
||||||
|
-> Just -- R_RISCV_LO12_S
|
||||||
|
$ UnresolvedRelocation (Text.encodeUtf8 symbolName) offset 28
|
||||||
|
| RiscV.RBranch symbolName _ _ _ <- instructionType
|
||||||
|
-> Just -- R_RISCV_BRANCH
|
||||||
|
$ UnresolvedRelocation (Text.encodeUtf8 symbolName) offset 16
|
||||||
|
| RiscV.RJal _ symbolName <- instructionType
|
||||||
|
-> Just -- R_RISCV_JAL
|
||||||
|
$ UnresolvedRelocation (Text.encodeUtf8 symbolName) offset 17
|
||||||
|
RiscV.CallInstruction symbolName
|
||||||
|
-> Just -- R_RISCV_CALL_PLT
|
||||||
|
$ UnresolvedRelocation (Text.encodeUtf8 symbolName) offset 19
|
||||||
|
RiscV.BaseInstruction _ _ -> Nothing
|
||||||
|
chunk = ByteString.Builder.toLazyByteString
|
||||||
|
$ RiscV.instruction instruction
|
||||||
|
result = TextAccumulator
|
||||||
|
(encoded <> chunk)
|
||||||
|
(maybe relocations (Vector.snoc relocations) unresolvedRelocation)
|
||||||
|
symbolResult
|
||||||
|
(addDefinition unresolvedRelocation definitions)
|
||||||
|
in encodeInstructions shndx (result, rest)
|
||||||
|
| Just (JumpLabel labelName directives , rest) <- Vector.uncons instructions
|
||||||
|
, FunctionDirective `notElem` directives =
|
||||||
|
let newEntry = Elf32_Sym
|
||||||
|
{ st_value = fromIntegral $ LazyByteString.length encoded
|
||||||
|
, st_size = 0
|
||||||
|
, st_shndx = shndx
|
||||||
|
, st_other = 0
|
||||||
|
, st_name = StringTable.size $ getField @"sectionNames" symbolResult
|
||||||
|
, st_info = stInfo (directivesBinding directives) STT_NOTYPE
|
||||||
|
}
|
||||||
|
result = TextAccumulator
|
||||||
|
encoded
|
||||||
|
relocations
|
||||||
|
(addHeaderToResult (Text.encodeUtf8 labelName) newEntry symbolResult)
|
||||||
|
definitions
|
||||||
|
in encodeInstructions shndx (result, rest)
|
||||||
|
| otherwise = (TextAccumulator encoded relocations symbolResult definitions, instructions)
|
||||||
|
addDefinition (Just (UnresolvedRelocation symbolName _ _)) =
|
||||||
|
HashSet.insert symbolName
|
||||||
|
addDefinition Nothing = id
|
||||||
|
|
||||||
|
shstrtab :: ElfWriter Elf32_Half
|
||||||
|
shstrtab = do
|
||||||
|
ElfHeaderResult{..} <- ElfWriter $ gets $ getField @"objectHeaders"
|
||||||
|
let stringTable = ".shstrtab"
|
||||||
|
currentNamesSize = StringTable.size sectionNames
|
||||||
|
nextHeader = Elf32_Shdr
|
||||||
|
{ sh_type = SHT_STRTAB
|
||||||
|
, sh_size = currentNamesSize -- Adding trailing null character.
|
||||||
|
+ fromIntegral (succ $ ByteString.length stringTable)
|
||||||
|
, sh_offset = elfSectionsSize sectionHeaders
|
||||||
|
, sh_name = currentNamesSize
|
||||||
|
, sh_link = 0
|
||||||
|
, sh_info = 0
|
||||||
|
, sh_flags = 0
|
||||||
|
, sh_entsize = 0
|
||||||
|
, sh_addralign = 1
|
||||||
|
, sh_addr = 0
|
||||||
|
}
|
||||||
|
addSectionHeader stringTable nextHeader
|
||||||
|
|
||||||
|
ElfEnvironment{..} <- ElfWriter get
|
||||||
|
liftIO $ ByteString.hPut objectHandle
|
||||||
|
$ StringTable.encode
|
||||||
|
$ getField @"sectionNames" objectHeaders
|
||||||
|
pure $ fromIntegral $ Vector.length sectionHeaders
|
||||||
|
|
||||||
|
symtab :: UnresolvedRelocations -> ElfWriter (Elf32_Word, UnresolvedRelocations)
|
||||||
|
symtab (UnresolvedRelocations relocationList symbolResult index) = do
|
||||||
|
ElfHeaderResult{..} <- ElfWriter $ gets $ getField @"objectHeaders"
|
||||||
|
let (localSymbols, globalSymbols) = partitionSymbols symbolResult
|
||||||
|
sortedSymbols = localSymbols <> globalSymbols
|
||||||
|
sortedResult = symbolResult{ sectionHeaders = sortedSymbols }
|
||||||
|
encodedSymbols = LazyByteString.toStrict
|
||||||
|
$ ByteString.Builder.toLazyByteString
|
||||||
|
$ foldMap (elf32Sym LSB) sortedSymbols
|
||||||
|
symHeader = Elf32_Shdr
|
||||||
|
{ sh_type = SHT_SYMTAB
|
||||||
|
, sh_size = fromIntegral $ ByteString.length encodedSymbols
|
||||||
|
, sh_offset = elfSectionsSize sectionHeaders
|
||||||
|
, sh_name = StringTable.size sectionNames
|
||||||
|
, sh_link = fromIntegral $ Vector.length sectionHeaders + 2
|
||||||
|
, sh_info = fromIntegral $ Vector.length localSymbols
|
||||||
|
, sh_flags = 0
|
||||||
|
, sh_entsize = 16
|
||||||
|
, sh_addralign = 4
|
||||||
|
, sh_addr = 0
|
||||||
|
}
|
||||||
|
putSectionHeader ".symtab" symHeader encodedSymbols
|
||||||
|
pure
|
||||||
|
( fromIntegral $ Vector.length sectionHeaders
|
||||||
|
, UnresolvedRelocations relocationList sortedResult index
|
||||||
|
)
|
||||||
|
|
||||||
|
symrel :: Elf32_Word -> UnresolvedRelocations -> ElfWriter StringTable
|
||||||
|
symrel sectionHeadersLength relocations = do
|
||||||
|
ElfHeaderResult{..} <- ElfWriter $ gets $ getField @"objectHeaders"
|
||||||
|
|
||||||
|
let UnresolvedRelocations relocationList symbols index = relocations
|
||||||
|
encodedRelocations = LazyByteString.toStrict
|
||||||
|
$ ByteString.Builder.toLazyByteString
|
||||||
|
$ Vector.foldMap (either (const mempty) (elf32Rel LSB))
|
||||||
|
$ resolveRelocation symbols <$> relocationList
|
||||||
|
relHeader = Elf32_Shdr
|
||||||
|
{ sh_type = SHT_REL
|
||||||
|
, sh_size = fromIntegral $ ByteString.length encodedRelocations
|
||||||
|
, sh_offset = elfSectionsSize sectionHeaders
|
||||||
|
, sh_name = StringTable.size sectionNames
|
||||||
|
, sh_link = sectionHeadersLength
|
||||||
|
, sh_info = index
|
||||||
|
, sh_flags = shfInfoLink
|
||||||
|
, sh_entsize = 8
|
||||||
|
, sh_addralign = 4
|
||||||
|
, sh_addr = 0
|
||||||
|
}
|
||||||
|
putSectionHeader ".rel.text" relHeader encodedRelocations
|
||||||
|
pure $ getField @"sectionNames" symbols
|
||||||
|
where
|
||||||
|
takeStringZ stringTable Elf32_Sym{ st_name }
|
||||||
|
= StringTable.index st_name stringTable
|
||||||
|
resolveRelocation (ElfHeaderResult stringTable entries) unresolvedRelocation
|
||||||
|
| UnresolvedRelocation symbolName offset type' <- unresolvedRelocation
|
||||||
|
, Just entry <- Vector.findIndex ((== symbolName) . takeStringZ stringTable) entries =
|
||||||
|
Right $ Elf32_Rel
|
||||||
|
{ r_offset = offset
|
||||||
|
, r_info = rInfo (fromIntegral entry) type'
|
||||||
|
}
|
||||||
|
| otherwise = Left unresolvedRelocation
|
||||||
|
|
||||||
|
strtab :: StringTable -> ElfWriter ()
|
||||||
|
strtab stringTable = do
|
||||||
|
ElfHeaderResult{..} <- ElfWriter $ gets $ getField @"objectHeaders"
|
||||||
|
let strHeader = Elf32_Shdr
|
||||||
|
{ sh_type = SHT_STRTAB
|
||||||
|
, sh_size = StringTable.size stringTable
|
||||||
|
, sh_offset = elfSectionsSize sectionHeaders
|
||||||
|
, sh_name = StringTable.size sectionNames
|
||||||
|
, sh_link = 0
|
||||||
|
, sh_info = 0
|
||||||
|
, sh_flags = 0
|
||||||
|
, sh_entsize = 0
|
||||||
|
, sh_addralign = 1
|
||||||
|
, sh_addr = 0
|
||||||
|
}
|
||||||
|
putSectionHeader ".strtab" strHeader $ StringTable.encode stringTable
|
462
package-lock.json
generated
462
package-lock.json
generated
@ -1,462 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "elna",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"lockfileVersion": 3,
|
|
||||||
"requires": true,
|
|
||||||
"packages": {
|
|
||||||
"": {
|
|
||||||
"name": "elna",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"license": "MPL-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"chalk": "^5.3.0",
|
|
||||||
"glob": "^10.4.1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@isaacs/cliui": {
|
|
||||||
"version": "8.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
|
|
||||||
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
|
|
||||||
"dependencies": {
|
|
||||||
"string-width": "^5.1.2",
|
|
||||||
"string-width-cjs": "npm:string-width@^4.2.0",
|
|
||||||
"strip-ansi": "^7.0.1",
|
|
||||||
"strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
|
|
||||||
"wrap-ansi": "^8.1.0",
|
|
||||||
"wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@pkgjs/parseargs": {
|
|
||||||
"version": "0.11.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
|
|
||||||
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
|
|
||||||
"optional": true,
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-regex": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/ansi-regex?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ansi-styles": {
|
|
||||||
"version": "6.2.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
|
|
||||||
"integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/balanced-match": {
|
|
||||||
"version": "1.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
|
||||||
},
|
|
||||||
"node_modules/brace-expansion": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
|
|
||||||
"dependencies": {
|
|
||||||
"balanced-match": "^1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/chalk": {
|
|
||||||
"version": "5.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
|
|
||||||
"integrity": "sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==",
|
|
||||||
"engines": {
|
|
||||||
"node": "^12.17.0 || ^14.13 || >=16.0.0"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-convert": {
|
|
||||||
"version": "2.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
|
||||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"color-name": "~1.1.4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=7.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/color-name": {
|
|
||||||
"version": "1.1.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
|
||||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
|
||||||
},
|
|
||||||
"node_modules/cross-spawn": {
|
|
||||||
"version": "7.0.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
|
||||||
"integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
|
|
||||||
"dependencies": {
|
|
||||||
"path-key": "^3.1.0",
|
|
||||||
"shebang-command": "^2.0.0",
|
|
||||||
"which": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/eastasianwidth": {
|
|
||||||
"version": "0.2.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
|
|
||||||
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="
|
|
||||||
},
|
|
||||||
"node_modules/emoji-regex": {
|
|
||||||
"version": "9.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
|
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="
|
|
||||||
},
|
|
||||||
"node_modules/foreground-child": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
|
|
||||||
"dependencies": {
|
|
||||||
"cross-spawn": "^7.0.0",
|
|
||||||
"signal-exit": "^4.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/glob": {
|
|
||||||
"version": "10.4.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.1.tgz",
|
|
||||||
"integrity": "sha512-2jelhlq3E4ho74ZyVLN03oKdAZVUa6UDZzFLVH1H7dnoax+y9qyaq8zBkfDIggjniU19z0wU18y16jMB2eyVIw==",
|
|
||||||
"dependencies": {
|
|
||||||
"foreground-child": "^3.1.0",
|
|
||||||
"jackspeak": "^3.1.2",
|
|
||||||
"minimatch": "^9.0.4",
|
|
||||||
"minipass": "^7.1.2",
|
|
||||||
"path-scurry": "^1.11.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"glob": "dist/esm/bin.mjs"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16 || 14 >=14.18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/is-fullwidth-code-point": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/isexe": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
|
|
||||||
},
|
|
||||||
"node_modules/jackspeak": {
|
|
||||||
"version": "3.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.1.2.tgz",
|
|
||||||
"integrity": "sha512-kWmLKn2tRtfYMF/BakihVVRzBKOxz4gJMiL2Rj91WnAB5TPZumSH99R/Yf1qE1u4uRimvCSJfm6hnxohXeEXjQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"@isaacs/cliui": "^8.0.2"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
},
|
|
||||||
"optionalDependencies": {
|
|
||||||
"@pkgjs/parseargs": "^0.11.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/lru-cache": {
|
|
||||||
"version": "10.2.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz",
|
|
||||||
"integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": "14 || >=16.14"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minimatch": {
|
|
||||||
"version": "9.0.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz",
|
|
||||||
"integrity": "sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==",
|
|
||||||
"dependencies": {
|
|
||||||
"brace-expansion": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16 || 14 >=14.17"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/minipass": {
|
|
||||||
"version": "7.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
|
|
||||||
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16 || 14 >=14.17"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-key": {
|
|
||||||
"version": "3.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
|
||||||
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/path-scurry": {
|
|
||||||
"version": "1.11.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
|
|
||||||
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
|
|
||||||
"dependencies": {
|
|
||||||
"lru-cache": "^10.2.0",
|
|
||||||
"minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16 || 14 >=14.18"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/shebang-command": {
|
|
||||||
"version": "2.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
|
||||||
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
|
|
||||||
"dependencies": {
|
|
||||||
"shebang-regex": "^3.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/shebang-regex": {
|
|
||||||
"version": "3.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
|
|
||||||
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/signal-exit": {
|
|
||||||
"version": "4.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
|
|
||||||
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-width": {
|
|
||||||
"version": "5.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
|
|
||||||
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
|
|
||||||
"dependencies": {
|
|
||||||
"eastasianwidth": "^0.2.0",
|
|
||||||
"emoji-regex": "^9.2.2",
|
|
||||||
"strip-ansi": "^7.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/sponsors/sindresorhus"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-width-cjs": {
|
|
||||||
"name": "string-width",
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^8.0.0",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"strip-ansi": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-width-cjs/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/string-width-cjs/node_modules/emoji-regex": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
|
||||||
},
|
|
||||||
"node_modules/string-width-cjs/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/strip-ansi": {
|
|
||||||
"version": "7.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
|
|
||||||
"integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/strip-ansi-cjs": {
|
|
||||||
"name": "strip-ansi",
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/which": {
|
|
||||||
"version": "2.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
|
|
||||||
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
|
|
||||||
"dependencies": {
|
|
||||||
"isexe": "^2.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"node-which": "bin/node-which"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi": {
|
|
||||||
"version": "8.1.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
|
|
||||||
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-styles": "^6.1.0",
|
|
||||||
"string-width": "^5.0.1",
|
|
||||||
"strip-ansi": "^7.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi-cjs": {
|
|
||||||
"name": "wrap-ansi",
|
|
||||||
"version": "7.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
|
||||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-styles": "^4.0.0",
|
|
||||||
"string-width": "^4.1.0",
|
|
||||||
"strip-ansi": "^6.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/wrap-ansi?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
|
|
||||||
"version": "5.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
|
|
||||||
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
|
|
||||||
"version": "4.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
|
||||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
|
||||||
"dependencies": {
|
|
||||||
"color-convert": "^2.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
|
|
||||||
"version": "8.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
|
||||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
|
|
||||||
"version": "4.2.3",
|
|
||||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
|
|
||||||
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
|
|
||||||
"dependencies": {
|
|
||||||
"emoji-regex": "^8.0.0",
|
|
||||||
"is-fullwidth-code-point": "^3.0.0",
|
|
||||||
"strip-ansi": "^6.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
|
|
||||||
"version": "6.0.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
|
|
||||||
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
|
|
||||||
"dependencies": {
|
|
||||||
"ansi-regex": "^5.0.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
17
package.json
17
package.json
@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "elna",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "tools/index.js",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"start": "node tools/cross.js",
|
|
||||||
"test": "node tools/tester.js"
|
|
||||||
},
|
|
||||||
"author": "Eugen Wissner <belka@caraus.de>",
|
|
||||||
"license": "MPL-2.0",
|
|
||||||
"dependencies": {
|
|
||||||
"chalk": "^5.3.0",
|
|
||||||
"glob": "^10.4.1"
|
|
||||||
}
|
|
||||||
}
|
|
331
rakelib/cross.rake
Normal file
331
rakelib/cross.rake
Normal file
@ -0,0 +1,331 @@
|
|||||||
|
require 'pathname'
|
||||||
|
require 'uri'
|
||||||
|
require 'net/http'
|
||||||
|
require 'rake/clean'
|
||||||
|
require 'open3'
|
||||||
|
require 'etc'
|
||||||
|
require_relative 'shared'
|
||||||
|
|
||||||
|
GCC_VERSION = "14.2.0"
|
||||||
|
BINUTILS_VERSION = '2.43.1'
|
||||||
|
GLIBC_VERSION = '2.40'
|
||||||
|
KERNEL_VERSION = '5.15.166'
|
||||||
|
|
||||||
|
CLOBBER.include TMP
|
||||||
|
|
||||||
|
class BuildTarget
|
||||||
|
attr_accessor(:build, :gcc, :target, :tmp)
|
||||||
|
|
||||||
|
def gxx
|
||||||
|
@gcc.gsub 'c', '+'
|
||||||
|
end
|
||||||
|
|
||||||
|
def sysroot
|
||||||
|
tmp + 'sysroot'
|
||||||
|
end
|
||||||
|
|
||||||
|
def rootfs
|
||||||
|
tmp + 'rootfs'
|
||||||
|
end
|
||||||
|
|
||||||
|
def tools
|
||||||
|
tmp + 'tools'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def gcc_verbose(gcc_binary)
|
||||||
|
read, write = IO.pipe
|
||||||
|
sh({'LANG' => 'C'}, gcc_binary, '--verbose', err: write)
|
||||||
|
write.close
|
||||||
|
output = read.read
|
||||||
|
read.close
|
||||||
|
output
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_build_target(gcc_version, task)
|
||||||
|
gcc_binary = 'gcc'
|
||||||
|
output = gcc_verbose gcc_binary
|
||||||
|
|
||||||
|
if output.start_with? 'Apple clang'
|
||||||
|
gcc_binary = "gcc-#{gcc_version.split('.').first}"
|
||||||
|
output = gcc_verbose gcc_binary
|
||||||
|
end
|
||||||
|
result = output
|
||||||
|
.lines
|
||||||
|
.each_with_object(BuildTarget.new) do |line, accumulator|
|
||||||
|
if line.start_with? 'Target: '
|
||||||
|
accumulator.build = line.split(' ').last.strip
|
||||||
|
elsif line.start_with? 'COLLECT_GCC'
|
||||||
|
accumulator.gcc = line.split('=').last.strip
|
||||||
|
end
|
||||||
|
end
|
||||||
|
result.tmp = TMP
|
||||||
|
task.with_defaults target: 'riscv32-unknown-linux-gnu'
|
||||||
|
result.target = task[:target]
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def download_and_unarchive(url, target)
|
||||||
|
case File.extname url.path
|
||||||
|
when '.bz2'
|
||||||
|
archive_type = '-j'
|
||||||
|
root_directory = File.basename url.path, '.tar.bz2'
|
||||||
|
when '.xz'
|
||||||
|
archive_type = '-J'
|
||||||
|
root_directory = File.basename url.path, '.tar.xz'
|
||||||
|
else
|
||||||
|
raise "Unsupported archive type #{url.path}."
|
||||||
|
end
|
||||||
|
|
||||||
|
Net::HTTP.start(url.host, url.port, use_ssl: url.scheme == 'https') do |http|
|
||||||
|
request = Net::HTTP::Get.new url.request_uri
|
||||||
|
|
||||||
|
http.request request do |response|
|
||||||
|
case response
|
||||||
|
when Net::HTTPRedirection
|
||||||
|
download_and_unarchive URI.parse(response['location'])
|
||||||
|
when Net::HTTPSuccess
|
||||||
|
Open3.popen2 'tar', '-C', target.to_path, archive_type, '-xv' do |stdin, stdout, wait_thread|
|
||||||
|
Thread.new do
|
||||||
|
stdout.each { |line| puts line }
|
||||||
|
end
|
||||||
|
|
||||||
|
response.read_body do |chunk|
|
||||||
|
stdin.write chunk
|
||||||
|
end
|
||||||
|
stdin.close
|
||||||
|
|
||||||
|
wait_thread.value
|
||||||
|
end
|
||||||
|
else
|
||||||
|
response.error!
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
target + root_directory
|
||||||
|
end
|
||||||
|
|
||||||
|
namespace :cross do
|
||||||
|
desc 'Build cross binutils'
|
||||||
|
task :binutils, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://ftp.gnu.org/gnu/binutils/binutils-#{BINUTILS_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
cwd = source_directory.dirname + 'build-binutils'
|
||||||
|
cwd.mkpath
|
||||||
|
options.rootfs.mkpath
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'CC' => options.gcc,
|
||||||
|
'CXX' => options.gxx
|
||||||
|
}
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--target=#{options.target}",
|
||||||
|
'--disable-nls',
|
||||||
|
'--enable-gprofng=no',
|
||||||
|
'--disable-werror',
|
||||||
|
'--enable-default-hash-style=gnu',
|
||||||
|
'--disable-libquadmath'
|
||||||
|
]
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build stage 1 GCC'
|
||||||
|
task :gcc1, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://gcc.gnu.org/pub/gcc/releases/gcc-#{GCC_VERSION}/gcc-#{GCC_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
cwd = source_directory.dirname + 'build-gcc'
|
||||||
|
cwd.mkpath
|
||||||
|
options.rootfs.mkpath
|
||||||
|
options.sysroot.mkpath
|
||||||
|
|
||||||
|
sh 'contrib/download_prerequisites', chdir: source_directory.to_path
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--with-sysroot=#{options.sysroot.realpath}",
|
||||||
|
'--enable-languages=c,c++',
|
||||||
|
'--disable-shared',
|
||||||
|
'--with-arch=rv32imafdc',
|
||||||
|
'--with-abi=ilp32d',
|
||||||
|
'--with-tune=rocket',
|
||||||
|
'--with-isa-spec=20191213',
|
||||||
|
'--disable-bootstrap',
|
||||||
|
'--disable-multilib',
|
||||||
|
'--disable-libmudflap',
|
||||||
|
'--disable-libssp',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--disable-libsanitizer',
|
||||||
|
'--disable-threads',
|
||||||
|
'--disable-libatomic',
|
||||||
|
'--disable-libgomp',
|
||||||
|
'--disable-libvtv',
|
||||||
|
'--disable-libstdcxx',
|
||||||
|
'--disable-nls',
|
||||||
|
'--with-newlib',
|
||||||
|
'--without-headers',
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--host=#{options.build}"
|
||||||
|
]
|
||||||
|
flags = '-O2 -fPIC'
|
||||||
|
env = {
|
||||||
|
'CC' => options.gcc,
|
||||||
|
'CXX' => options.gxx,
|
||||||
|
'CFLAGS' => flags,
|
||||||
|
'CXXFLAGS' => flags,
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||||
|
}
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Copy glibc headers'
|
||||||
|
task :headers, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
|
||||||
|
source_directory = download_and_unarchive(
|
||||||
|
URI.parse("https://ftp.gnu.org/gnu/glibc/glibc-#{GLIBC_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
include_directory = options.tools + 'include'
|
||||||
|
|
||||||
|
include_directory.mkpath
|
||||||
|
cp (source_directory + 'elf/elf.h'), (include_directory + 'elf.h')
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build linux kernel'
|
||||||
|
task :kernel, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
options.tools.mkpath
|
||||||
|
|
||||||
|
cwd = download_and_unarchive(
|
||||||
|
URI.parse("https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-#{KERNEL_VERSION}.tar.xz"),
|
||||||
|
options.tools)
|
||||||
|
|
||||||
|
env = {
|
||||||
|
'CROSS_COMPILE' => "#{options.target}-",
|
||||||
|
'ARCH' => 'riscv',
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}",
|
||||||
|
'HOSTCFLAGS' => "-D_UUID_T -D__GETHOSTUUID_H -I#{options.tools.realpath + 'include'}"
|
||||||
|
}
|
||||||
|
sh env, 'make', 'rv32_defconfig', chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'headers', chdir: cwd.to_path
|
||||||
|
|
||||||
|
user_directory = options.sysroot + 'usr'
|
||||||
|
|
||||||
|
user_directory.mkpath
|
||||||
|
cp_r (cwd + 'usr/include'), (user_directory + 'include')
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build glibc'
|
||||||
|
task :glibc, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
source_directory = options.tools + "glibc-#{GLIBC_VERSION}"
|
||||||
|
configure_options = [
|
||||||
|
'--prefix=/usr',
|
||||||
|
"--host=#{options.target}",
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--enable-kernel=#{KERNEL_VERSION}",
|
||||||
|
"--with-headers=#{options.sysroot.realpath + 'usr/include'}",
|
||||||
|
'--disable-nscd',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--disable-libitm',
|
||||||
|
'--disable-werror',
|
||||||
|
'libc_cv_forced_unwind=yes'
|
||||||
|
]
|
||||||
|
bin = options.rootfs.realpath + 'bin'
|
||||||
|
env = {
|
||||||
|
'PATH' => "#{bin}:#{ENV['PATH']}",
|
||||||
|
'MAKE' => 'make' # Otherwise it uses gnumake which can be different and too old.
|
||||||
|
}
|
||||||
|
cwd = source_directory.dirname + 'build-glibc'
|
||||||
|
cwd.mkpath
|
||||||
|
|
||||||
|
configure = source_directory.relative_path_from(cwd) +'./configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', "install_root=#{options.sysroot.realpath}", 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build stage 2 GCC'
|
||||||
|
task :gcc2, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
source_directory = options.tools + "gcc-#{GCC_VERSION}"
|
||||||
|
cwd = options.tools + 'build-gcc'
|
||||||
|
|
||||||
|
rm_rf cwd
|
||||||
|
cwd.mkpath
|
||||||
|
|
||||||
|
configure_options = [
|
||||||
|
"--prefix=#{options.rootfs.realpath}",
|
||||||
|
"--with-sysroot=#{options.sysroot.realpath}",
|
||||||
|
'--enable-languages=c,c++,lto',
|
||||||
|
'--enable-lto',
|
||||||
|
'--enable-shared',
|
||||||
|
'--with-arch=rv32imafdc',
|
||||||
|
'--with-abi=ilp32d',
|
||||||
|
'--with-tune=rocket',
|
||||||
|
'--with-isa-spec=20191213',
|
||||||
|
'--disable-bootstrap',
|
||||||
|
'--disable-multilib',
|
||||||
|
'--enable-checking=release',
|
||||||
|
'--disable-libssp',
|
||||||
|
'--disable-libquadmath',
|
||||||
|
'--enable-threads=posix',
|
||||||
|
'--with-default-libstdcxx-abi=new',
|
||||||
|
'--disable-nls',
|
||||||
|
"--target=#{options.target}",
|
||||||
|
"--build=#{options.build}",
|
||||||
|
"--host=#{options.build}"
|
||||||
|
|
||||||
|
]
|
||||||
|
flags = '-O2 -fPIC'
|
||||||
|
env = {
|
||||||
|
'CFLAGS' => flags,
|
||||||
|
'CXXFLAGS' => flags,
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||||
|
}
|
||||||
|
configure = source_directory.relative_path_from(cwd) + 'configure'
|
||||||
|
sh env, configure.to_path, *configure_options, chdir: cwd.to_path
|
||||||
|
sh env, 'make', '-j', Etc.nprocessors.to_s, chdir: cwd.to_path
|
||||||
|
sh env, 'make', 'install', chdir: cwd.to_path
|
||||||
|
end
|
||||||
|
|
||||||
|
task :init, [:target] do |_, args|
|
||||||
|
options = find_build_target GCC_VERSION, args
|
||||||
|
env = {
|
||||||
|
'PATH' => "#{options.rootfs.realpath + 'bin'}:#{ENV['PATH']}"
|
||||||
|
}
|
||||||
|
sh env, 'riscv32-unknown-linux-gnu-gcc',
|
||||||
|
'-ffreestanding', '-static',
|
||||||
|
'-o', (options.tools + 'init').to_path,
|
||||||
|
'tools/init.c'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
desc 'Build cross toolchain'
|
||||||
|
task cross: [
|
||||||
|
'cross:binutils',
|
||||||
|
'cross:gcc1',
|
||||||
|
'cross:headers',
|
||||||
|
'cross:kernel',
|
||||||
|
'cross:glibc',
|
||||||
|
'cross:gcc2',
|
||||||
|
'cross:init'
|
||||||
|
] do
|
||||||
|
end
|
1
rakelib/shared.rb
Normal file
1
rakelib/shared.rb
Normal file
@ -0,0 +1 @@
|
|||||||
|
TMP = Pathname.new('./build')
|
96
rakelib/tester.rake
Normal file
96
rakelib/tester.rake
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
require 'open3'
|
||||||
|
require 'rake/clean'
|
||||||
|
require_relative 'shared'
|
||||||
|
|
||||||
|
CLEAN.include(TMP + 'riscv')
|
||||||
|
|
||||||
|
LINKER = 'build/rootfs/riscv32-unknown-linux-gnu/bin/ld'
|
||||||
|
AS = 'build/rootfs/riscv32-unknown-linux-gnu/bin/as'
|
||||||
|
|
||||||
|
namespace :test do
|
||||||
|
test_sources = FileList['tests/vm/*.elna', 'tests/vm/*.s']
|
||||||
|
compiler = `cabal list-bin elna`.strip
|
||||||
|
object_directory = TMP + 'riscv/tests'
|
||||||
|
root_directory = TMP + 'riscv/root'
|
||||||
|
executable_directory = root_directory + 'tests'
|
||||||
|
expectation_directory = root_directory + 'expectations'
|
||||||
|
init = TMP + 'riscv/root/init'
|
||||||
|
builtin = TMP + 'riscv/builtin.o'
|
||||||
|
|
||||||
|
directory root_directory
|
||||||
|
directory object_directory
|
||||||
|
directory executable_directory
|
||||||
|
directory expectation_directory
|
||||||
|
|
||||||
|
file builtin => ['tools/builtin.s', object_directory] do |task|
|
||||||
|
sh AS, '-o', task.name, task.prerequisites.first
|
||||||
|
end
|
||||||
|
|
||||||
|
test_files = test_sources.flat_map do |test_source|
|
||||||
|
test_basename = File.basename(test_source, '.*')
|
||||||
|
test_object = object_directory + test_basename.ext('.o')
|
||||||
|
|
||||||
|
file test_object => [test_source, object_directory] do |task|
|
||||||
|
case File.extname(task.prerequisites.first)
|
||||||
|
when '.s'
|
||||||
|
sh AS, '-mno-relax', '-o', task.name, task.prerequisites.first
|
||||||
|
when '.elna'
|
||||||
|
sh compiler, '--output', task.name, task.prerequisites.first
|
||||||
|
else
|
||||||
|
raise "Unknown source file extension #{task.prerequisites.first}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
test_executable = executable_directory + test_basename
|
||||||
|
|
||||||
|
file test_executable => [test_object, executable_directory, builtin] do |task|
|
||||||
|
objects = task.prerequisites.filter { |prerequisite| File.file? prerequisite }
|
||||||
|
|
||||||
|
sh LINKER, '-o', test_executable.to_path, *objects
|
||||||
|
end
|
||||||
|
expectation_name = test_basename.ext '.txt'
|
||||||
|
source_expectation = "tests/expectations/#{expectation_name}"
|
||||||
|
target_expectation = expectation_directory + expectation_name
|
||||||
|
|
||||||
|
file target_expectation => [source_expectation, expectation_directory] do
|
||||||
|
cp source_expectation, target_expectation
|
||||||
|
end
|
||||||
|
|
||||||
|
[test_executable, target_expectation]
|
||||||
|
end
|
||||||
|
|
||||||
|
file init => [root_directory] do |task|
|
||||||
|
cp (TMP + 'tools/init'), task.name
|
||||||
|
end
|
||||||
|
# Directories should come first.
|
||||||
|
test_files.unshift executable_directory, expectation_directory, init
|
||||||
|
|
||||||
|
file (TMP + 'riscv/root.cpio') => test_files do |task|
|
||||||
|
root_files = task.prerequisites
|
||||||
|
.map { |prerequisite| Pathname.new(prerequisite).relative_path_from(root_directory).to_path }
|
||||||
|
|
||||||
|
File.open task.name, 'wb' do |cpio_file|
|
||||||
|
cpio_options = {
|
||||||
|
chdir: root_directory.to_path
|
||||||
|
}
|
||||||
|
cpio_stream = Open3.popen2 'cpio', '-o', '--format=newc', cpio_options do |stdin, stdout, wait_thread|
|
||||||
|
stdin.write root_files.join("\n")
|
||||||
|
stdin.close
|
||||||
|
stdout.each { |chunk| cpio_file.write chunk }
|
||||||
|
wait_thread.value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
task :vm => (TMP + 'riscv/root.cpio') do |task|
|
||||||
|
kernels = FileList.glob(TMP + 'tools/linux-*/arch/riscv/boot/Image')
|
||||||
|
|
||||||
|
sh 'qemu-system-riscv32',
|
||||||
|
'-nographic',
|
||||||
|
'-M', 'virt',
|
||||||
|
'-bios', 'default',
|
||||||
|
'-kernel', kernels.first,
|
||||||
|
'-append', 'quiet panic=1',
|
||||||
|
'-initrd', task.prerequisites.first,
|
||||||
|
'-no-reboot'
|
||||||
|
end
|
||||||
|
end
|
329
source/lexer.cpp
329
source/lexer.cpp
@ -1,329 +0,0 @@
|
|||||||
#include "elna/source/lexer.hpp"
|
|
||||||
#include <cassert>
|
|
||||||
#include <variant>
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
using source_position = elna::source::position;
|
|
||||||
using source_error = elna::source::error;
|
|
||||||
|
|
||||||
token::value::value()
|
|
||||||
: nil(nullptr)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
token::value::value(std::int32_t value)
|
|
||||||
: number(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
token::value::value(const std::string& value)
|
|
||||||
: identifier(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
token::value::~value()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
token::token(const type of, const std::string& value, const source_position position)
|
|
||||||
: m_type(of), m_value(value), m_position(position)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
token::token(const type of, std::int32_t number, const source_position position)
|
|
||||||
: m_type(of), m_value(number), m_position(position)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
token::token(type of, value&& value, const elna::source::position position)
|
|
||||||
: m_type(of), m_position(position)
|
|
||||||
{
|
|
||||||
if (has_identifier())
|
|
||||||
{
|
|
||||||
new((void *) &m_value.identifier) std::string(std::move(value.identifier));
|
|
||||||
}
|
|
||||||
else if (is_numeric())
|
|
||||||
{
|
|
||||||
m_value.number = value.number;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_value.nil = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
token::token(const type of, source_position position)
|
|
||||||
: m_type(of), m_position(position)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
token::token(const token& that)
|
|
||||||
{
|
|
||||||
*this = that;
|
|
||||||
}
|
|
||||||
|
|
||||||
token::token(token&& that)
|
|
||||||
{
|
|
||||||
*this = std::move(that);
|
|
||||||
}
|
|
||||||
|
|
||||||
token::~token()
|
|
||||||
{
|
|
||||||
if (has_identifier())
|
|
||||||
{
|
|
||||||
m_value.identifier.~basic_string();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
token& token::operator=(const token& that)
|
|
||||||
{
|
|
||||||
if (has_identifier())
|
|
||||||
{
|
|
||||||
m_value.identifier.~basic_string();
|
|
||||||
}
|
|
||||||
m_type = that.of();
|
|
||||||
m_position = that.position();
|
|
||||||
if (that.has_identifier())
|
|
||||||
{
|
|
||||||
new((void *) &m_value.identifier) std::string(that.identifier());
|
|
||||||
}
|
|
||||||
else if (that.is_numeric())
|
|
||||||
{
|
|
||||||
m_value.number = that.number();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_value.nil = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
token& token::operator=(token&& that)
|
|
||||||
{
|
|
||||||
if (has_identifier())
|
|
||||||
{
|
|
||||||
m_value.identifier.~basic_string();
|
|
||||||
}
|
|
||||||
m_type = that.of();
|
|
||||||
m_position = that.position();
|
|
||||||
if (that.has_identifier())
|
|
||||||
{
|
|
||||||
new((void *) &m_value.identifier) std::string(std::move(that.identifier()));
|
|
||||||
}
|
|
||||||
else if (that.is_numeric())
|
|
||||||
{
|
|
||||||
m_value.number = that.number();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_value.nil = nullptr;
|
|
||||||
}
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
token::type token::of() const noexcept
|
|
||||||
{
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::string& token::identifier() const
|
|
||||||
{
|
|
||||||
if (!has_identifier())
|
|
||||||
{
|
|
||||||
throw std::bad_variant_access();
|
|
||||||
}
|
|
||||||
return m_value.identifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::int32_t token::number() const
|
|
||||||
{
|
|
||||||
if (!is_numeric())
|
|
||||||
{
|
|
||||||
throw std::bad_variant_access();
|
|
||||||
}
|
|
||||||
return m_value.number;
|
|
||||||
}
|
|
||||||
|
|
||||||
const source_position& token::position() const noexcept
|
|
||||||
{
|
|
||||||
return m_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool token::has_identifier() const noexcept
|
|
||||||
{
|
|
||||||
return of() == type::identifier
|
|
||||||
|| of() == type::term_operator
|
|
||||||
|| of() == type::factor_operator
|
|
||||||
|| of() == type::comparison_operator;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool token::is_numeric() const noexcept
|
|
||||||
{
|
|
||||||
return of() == type::number
|
|
||||||
|| of() == type::boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string token::to_string() const
|
|
||||||
{
|
|
||||||
switch (this->m_type)
|
|
||||||
{
|
|
||||||
case type::number:
|
|
||||||
return "«number»";
|
|
||||||
case type::boolean:
|
|
||||||
return "«boolean»";
|
|
||||||
case type::term_operator:
|
|
||||||
return "«term_operator»";
|
|
||||||
case type::let:
|
|
||||||
return "«const»";
|
|
||||||
case type::identifier:
|
|
||||||
return "«identifier»";
|
|
||||||
case type::equals:
|
|
||||||
return "«=»";
|
|
||||||
case type::var:
|
|
||||||
return "«var»";
|
|
||||||
case type::semicolon:
|
|
||||||
return "«;»";
|
|
||||||
case type::left_paren:
|
|
||||||
return "«(»";
|
|
||||||
case type::right_paren:
|
|
||||||
return "«)»";
|
|
||||||
case type::dot:
|
|
||||||
return "«)»";
|
|
||||||
case type::comma:
|
|
||||||
return "«,»";
|
|
||||||
case type::factor_operator:
|
|
||||||
return "«*»";
|
|
||||||
case type::eof:
|
|
||||||
return "«EOF»";
|
|
||||||
case type::begin:
|
|
||||||
return "«begin»";
|
|
||||||
case type::end:
|
|
||||||
return "«end»";
|
|
||||||
case type::assignment:
|
|
||||||
return "«:=»";
|
|
||||||
case type::colon:
|
|
||||||
return "«:»";
|
|
||||||
case type::when:
|
|
||||||
return "«if»";
|
|
||||||
case type::then:
|
|
||||||
return "«then»";
|
|
||||||
case type::loop:
|
|
||||||
return "«while»";
|
|
||||||
case type::_do:
|
|
||||||
return "«do»";
|
|
||||||
case type::procedure:
|
|
||||||
return "«proc»";
|
|
||||||
case type::comparison_operator:
|
|
||||||
return "«comparison_operator»";
|
|
||||||
case type::hat:
|
|
||||||
return "«^»";
|
|
||||||
case type::at:
|
|
||||||
return "«@»";
|
|
||||||
};
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
unexpected_character::unexpected_character(const std::string& character, const std::filesystem::path& path,
|
|
||||||
const source::position position)
|
|
||||||
: error(path, position), character(character)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string unexpected_character::what() const
|
|
||||||
{
|
|
||||||
std::string ss{ "Unexpected character '" };
|
|
||||||
|
|
||||||
ss.insert(ss.cend(), character.cbegin(), character.cend());
|
|
||||||
ss.push_back('\'');
|
|
||||||
|
|
||||||
return ss;
|
|
||||||
}
|
|
||||||
|
|
||||||
unexpected_token::unexpected_token(const token& token, const std::filesystem::path& path)
|
|
||||||
: error(path, token.position()), m_token(token)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string unexpected_token::what() const
|
|
||||||
{
|
|
||||||
return "Unexpected token " + m_token.to_string();
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer::lexer(std::vector<token>&& tokens, const position last_position, const std::filesystem::path& path)
|
|
||||||
: tokens(std::move(tokens)), iterator(this->tokens.cbegin()), eof(token(token::type::eof, last_position)),
|
|
||||||
source_file(path)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
lexer& lexer::operator++()
|
|
||||||
{
|
|
||||||
++iterator;
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
const token& lexer::operator*() const
|
|
||||||
{
|
|
||||||
return *iterator;
|
|
||||||
}
|
|
||||||
|
|
||||||
const token *lexer::operator->() const
|
|
||||||
{
|
|
||||||
return iterator.base();
|
|
||||||
}
|
|
||||||
|
|
||||||
const token& lexer::current() const noexcept
|
|
||||||
{
|
|
||||||
if (iterator == tokens.cend())
|
|
||||||
{
|
|
||||||
return this->eof;
|
|
||||||
}
|
|
||||||
return *iterator;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lexer::current(const token::type token_type) const noexcept
|
|
||||||
{
|
|
||||||
return current().of() == token_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void lexer::add_error(const token& expected)
|
|
||||||
{
|
|
||||||
m_errors.push_back(std::make_unique<unexpected_token>(expected, this->source_file));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<std::reference_wrapper<const token>> lexer::advance(const token::type token_type)
|
|
||||||
{
|
|
||||||
if (iterator != tokens.cend() && iterator->of() == token_type)
|
|
||||||
{
|
|
||||||
return std::make_optional<>(std::cref(*iterator++));
|
|
||||||
}
|
|
||||||
add_error(current());
|
|
||||||
return std::optional<std::reference_wrapper<const token>>();
|
|
||||||
}
|
|
||||||
|
|
||||||
const token& lexer::look_ahead() const
|
|
||||||
{
|
|
||||||
auto tmp = iterator;
|
|
||||||
++tmp;
|
|
||||||
if (iterator == tokens.cend() || tmp == tokens.cend())
|
|
||||||
{
|
|
||||||
return eof;
|
|
||||||
}
|
|
||||||
return *tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lexer::look_ahead(const token::type token_type) const
|
|
||||||
{
|
|
||||||
return look_ahead().of() == token_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool lexer::skip(const token::type token_type)
|
|
||||||
{
|
|
||||||
return advance(token_type).has_value();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::list<std::unique_ptr<error>>& lexer::errors() const noexcept
|
|
||||||
{
|
|
||||||
return m_errors;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,267 +0,0 @@
|
|||||||
#include "elna/source/optimizer.hpp"
|
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
quadruple::quadruple(const quadruple_operator operation, std::shared_ptr<operand> operand1,
|
|
||||||
std::shared_ptr<operand> operand2, std::shared_ptr<operand> operand3)
|
|
||||||
: m_operation(operation), m_operand1(operand1), m_operand2(operand2), m_operand3(operand3)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
quadruple_operator quadruple::operation() const noexcept
|
|
||||||
{
|
|
||||||
return m_operation;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<operand> quadruple::operand1()
|
|
||||||
{
|
|
||||||
return m_operand1;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<operand> quadruple::operand2()
|
|
||||||
{
|
|
||||||
return m_operand2;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<operand> quadruple::operand3()
|
|
||||||
{
|
|
||||||
return m_operand3;
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate_code::intermediate_code()
|
|
||||||
{
|
|
||||||
clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code::emplace_back(const quadruple_operator operation, std::shared_ptr<operand> operand1,
|
|
||||||
std::shared_ptr<operand> operand2, std::shared_ptr<operand> operand3)
|
|
||||||
{
|
|
||||||
this->instructions.emplace_back(operation, operand1, operand2, operand3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code::clear()
|
|
||||||
{
|
|
||||||
this->instructions.clear();
|
|
||||||
this->m_variable_counter = 1;
|
|
||||||
this->m_label_counter = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<quadruple>::iterator intermediate_code::begin()
|
|
||||||
{
|
|
||||||
return this->instructions.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<quadruple>::iterator intermediate_code::end()
|
|
||||||
{
|
|
||||||
return this->instructions.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::int32_t intermediate_code::variable_counter() const noexcept
|
|
||||||
{
|
|
||||||
return m_variable_counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::int32_t intermediate_code::increment_variable() noexcept
|
|
||||||
{
|
|
||||||
return m_variable_counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::int32_t intermediate_code::label_counter() const noexcept
|
|
||||||
{
|
|
||||||
return m_label_counter;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::int32_t intermediate_code::increment_label() noexcept
|
|
||||||
{
|
|
||||||
return m_label_counter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
intermediate_code_generator::intermediate_code_generator(std::shared_ptr<symbol_table> table)
|
|
||||||
: table(table)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
quadruple_operator intermediate_code_generator::convert(const binary_operator operation) const
|
|
||||||
{
|
|
||||||
switch (operation)
|
|
||||||
{
|
|
||||||
case binary_operator::sum:
|
|
||||||
return quadruple_operator::add;
|
|
||||||
case binary_operator::subtraction:
|
|
||||||
return quadruple_operator::sub;
|
|
||||||
case binary_operator::multiplication:
|
|
||||||
return quadruple_operator::mul;
|
|
||||||
case binary_operator::division:
|
|
||||||
return quadruple_operator::div;
|
|
||||||
case binary_operator::equals:
|
|
||||||
return quadruple_operator::eq;
|
|
||||||
case source::binary_operator::not_equals:
|
|
||||||
return quadruple_operator::neq;
|
|
||||||
case source::binary_operator::less:
|
|
||||||
return quadruple_operator::lt;
|
|
||||||
case source::binary_operator::greater_equal:
|
|
||||||
return quadruple_operator::ge;
|
|
||||||
case source::binary_operator::greater:
|
|
||||||
return quadruple_operator::gt;
|
|
||||||
case source::binary_operator::less_equal:
|
|
||||||
return quadruple_operator::le;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
quadruple_operator intermediate_code_generator::convert(const unary_operator operation) const
|
|
||||||
{
|
|
||||||
switch (operation)
|
|
||||||
{
|
|
||||||
case unary_operator::reference:
|
|
||||||
return quadruple_operator::ref;
|
|
||||||
case unary_operator::dereference:
|
|
||||||
return quadruple_operator::load;
|
|
||||||
default:
|
|
||||||
assert(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(source::declaration *declaration)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(source::constant_definition *definition)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(procedure_definition *definition)
|
|
||||||
{
|
|
||||||
this->current.emplace_back(quadruple_operator::start);
|
|
||||||
definition->body().accept(this);
|
|
||||||
this->current.emplace_back(quadruple_operator::stop);
|
|
||||||
code[definition->identifier()] = std::move(this->current);
|
|
||||||
this->current.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<std::string, intermediate_code>::iterator intermediate_code_generator::begin()
|
|
||||||
{
|
|
||||||
return code.begin();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unordered_map<std::string, intermediate_code>::iterator intermediate_code_generator::end()
|
|
||||||
{
|
|
||||||
return code.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(block *block)
|
|
||||||
{
|
|
||||||
block->body().accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(program *program)
|
|
||||||
{
|
|
||||||
for (auto& definition : program->definitions())
|
|
||||||
{
|
|
||||||
definition->accept(this);
|
|
||||||
}
|
|
||||||
this->current.emplace_back(quadruple_operator::start);
|
|
||||||
program->body().accept(this);
|
|
||||||
this->current.emplace_back(quadruple_operator::stop);
|
|
||||||
code["_start"] = std::move(this->current);
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(call_statement *statement)
|
|
||||||
{
|
|
||||||
for (auto& argument : statement->arguments())
|
|
||||||
{
|
|
||||||
argument->accept(this);
|
|
||||||
this->current.emplace_back(quadruple_operator::param, argument->place, nullptr, nullptr);
|
|
||||||
}
|
|
||||||
this->current.emplace_back(quadruple_operator::call,
|
|
||||||
std::make_shared<variable_operand>(statement->name()),
|
|
||||||
std::make_shared<integer_operand>(statement->arguments().size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(assign_statement *statement)
|
|
||||||
{
|
|
||||||
statement->rvalue().accept(this);
|
|
||||||
|
|
||||||
this->current.emplace_back(quadruple_operator::assign, statement->rvalue().place, nullptr,
|
|
||||||
std::make_shared<variable_operand>(statement->lvalue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(if_statement *statement)
|
|
||||||
{
|
|
||||||
statement->prerequisite().accept(this);
|
|
||||||
|
|
||||||
auto end_label = std::make_shared<label_operand>(this->current.increment_label());
|
|
||||||
this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label);
|
|
||||||
|
|
||||||
statement->body().accept(this);
|
|
||||||
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(while_statement *statement)
|
|
||||||
{
|
|
||||||
auto condition_label = std::make_shared<label_operand>(this->current.increment_label());
|
|
||||||
|
|
||||||
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, condition_label);
|
|
||||||
statement->prerequisite().accept(this);
|
|
||||||
|
|
||||||
auto end_label = std::make_shared<label_operand>(this->current.increment_label());
|
|
||||||
this->current.emplace_back(quadruple_operator::beqz, statement->prerequisite().place, nullptr, end_label);
|
|
||||||
|
|
||||||
statement->body().accept(this);
|
|
||||||
this->current.emplace_back(quadruple_operator::j, nullptr, nullptr, condition_label);
|
|
||||||
this->current.emplace_back(quadruple_operator::label, nullptr, nullptr, end_label);
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(type_expression *type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(variable_expression *variable)
|
|
||||||
{
|
|
||||||
auto symbol = table->lookup(variable->name());
|
|
||||||
if (auto constant_symbol = std::dynamic_pointer_cast<source::constant_info>(symbol))
|
|
||||||
{
|
|
||||||
variable->place = std::make_shared<integer_operand>(constant_symbol->value());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
variable->place = std::make_shared<variable_operand>(variable->name());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(binary_expression *expression)
|
|
||||||
{
|
|
||||||
expression->lhs().accept(this);
|
|
||||||
auto left = expression->lhs().place;
|
|
||||||
|
|
||||||
expression->rhs().accept(this);
|
|
||||||
auto right = expression->rhs().place;
|
|
||||||
auto operation = convert(expression->operation());
|
|
||||||
auto new_place = std::make_shared<temporary_variable>(this->current.increment_variable());
|
|
||||||
|
|
||||||
this->current.emplace_back(operation, left, right, new_place);
|
|
||||||
expression->place = new_place;
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(unary_expression *expression)
|
|
||||||
{
|
|
||||||
expression->operand().accept(this);
|
|
||||||
auto operation = convert(expression->operation());
|
|
||||||
auto new_place = std::make_shared<temporary_variable>(this->current.increment_variable());
|
|
||||||
|
|
||||||
this->current.emplace_back(operation, expression->operand().place, nullptr, new_place);
|
|
||||||
expression->place = new_place;
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(integer_literal *number)
|
|
||||||
{
|
|
||||||
number->place = std::make_shared<integer_operand>(number->number());
|
|
||||||
}
|
|
||||||
|
|
||||||
void intermediate_code_generator::visit(boolean_literal *number)
|
|
||||||
{
|
|
||||||
number->place = std::make_shared<integer_operand>(number->boolean());
|
|
||||||
}
|
|
||||||
}
|
|
1063
source/parser.cpp
1063
source/parser.cpp
File diff suppressed because it is too large
Load Diff
@ -1,46 +0,0 @@
|
|||||||
#include "elna/source/result.hpp"
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
error::error(const std::filesystem::path& path, const position position)
|
|
||||||
: m_position(position), m_path(path)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t error::line() const noexcept
|
|
||||||
{
|
|
||||||
return this->m_position.line;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t error::column() const noexcept
|
|
||||||
{
|
|
||||||
return this->m_position.column;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::filesystem::path& error::path() const noexcept
|
|
||||||
{
|
|
||||||
return this->m_path;
|
|
||||||
}
|
|
||||||
|
|
||||||
name_collision::name_collision(const std::string& name, const std::filesystem::path& path,
|
|
||||||
const position current, const position previous)
|
|
||||||
: error(path, current), name(name), previous(previous)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string name_collision::what() const
|
|
||||||
{
|
|
||||||
return "Name '" + name + "' was already defined";
|
|
||||||
}
|
|
||||||
|
|
||||||
type_mismatch::type_mismatch(std::shared_ptr<const type> got, operation kind, const std::filesystem::path& path,
|
|
||||||
const struct position position)
|
|
||||||
: error(path, position), kind(kind), got(got)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string type_mismatch::what() const
|
|
||||||
{
|
|
||||||
return "Type cannot be used here.";
|
|
||||||
}
|
|
||||||
}
|
|
204
source/scanner.l
204
source/scanner.l
@ -1,204 +0,0 @@
|
|||||||
%{
|
|
||||||
#define YY_NO_UNISTD_H
|
|
||||||
#define YY_USER_ACTION token_position = elna::source::position{ line_no, column_no }; column_no += yyleng;
|
|
||||||
|
|
||||||
#include <fstream>
|
|
||||||
#include "elna/source/lexer.hpp"
|
|
||||||
|
|
||||||
elna::source::token::value yylval{};
|
|
||||||
elna::source::position token_position{};
|
|
||||||
static std::size_t column_no = 1;
|
|
||||||
static std::size_t line_no = 1;
|
|
||||||
%}
|
|
||||||
|
|
||||||
%option noyywrap
|
|
||||||
%option never-interactive
|
|
||||||
%%
|
|
||||||
\-\-.* {
|
|
||||||
/* Skip the comment */
|
|
||||||
}
|
|
||||||
[\ \t\r] {
|
|
||||||
/* Skip the whitespaces */
|
|
||||||
}
|
|
||||||
\n {
|
|
||||||
++line_no;
|
|
||||||
column_no = 1;
|
|
||||||
}
|
|
||||||
if {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::when);
|
|
||||||
}
|
|
||||||
then {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::then);
|
|
||||||
}
|
|
||||||
while {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::loop);
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::_do);
|
|
||||||
}
|
|
||||||
proc {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::procedure);
|
|
||||||
}
|
|
||||||
begin {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::begin);
|
|
||||||
}
|
|
||||||
end {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::end);
|
|
||||||
}
|
|
||||||
const {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::let);
|
|
||||||
}
|
|
||||||
var {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::var);
|
|
||||||
}
|
|
||||||
True {
|
|
||||||
yylval.number = 1;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::boolean);
|
|
||||||
}
|
|
||||||
False {
|
|
||||||
yylval.number = 0;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::boolean);
|
|
||||||
}
|
|
||||||
[A-Za-z_][A-Za-z0-9_]* {
|
|
||||||
new((void *) &yylval.identifier) std::string(yytext);
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::identifier);
|
|
||||||
}
|
|
||||||
[0-9]+ {
|
|
||||||
yylval.number = strtol(yytext, NULL, 10);
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::number);
|
|
||||||
}
|
|
||||||
\( {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::left_paren);
|
|
||||||
}
|
|
||||||
\) {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::right_paren);
|
|
||||||
}
|
|
||||||
\>= {
|
|
||||||
new((void *) &yylval.identifier) std::string(1, 'g');
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::comparison_operator);
|
|
||||||
}
|
|
||||||
\<= {
|
|
||||||
new((void *) &yylval.identifier) std::string(1, 'l');
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::comparison_operator);
|
|
||||||
}
|
|
||||||
(>|<) {
|
|
||||||
new((void *) &yylval.identifier) std::string(yytext);
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::comparison_operator);
|
|
||||||
}
|
|
||||||
\/= {
|
|
||||||
new((void *) &yylval.identifier) std::string(1, 'n');
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::comparison_operator);
|
|
||||||
}
|
|
||||||
= {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::equals);
|
|
||||||
}
|
|
||||||
; {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::semicolon);
|
|
||||||
}
|
|
||||||
\. {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::dot);
|
|
||||||
}
|
|
||||||
, {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::comma);
|
|
||||||
}
|
|
||||||
(\+|\-) {
|
|
||||||
new((void *) &yylval.identifier) std::string(yytext);
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::term_operator);
|
|
||||||
}
|
|
||||||
(\*|\/) {
|
|
||||||
new((void *) &yylval.identifier) std::string(yytext);
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::factor_operator);
|
|
||||||
}
|
|
||||||
:= {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::assignment);
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::colon);
|
|
||||||
}
|
|
||||||
\^ {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::hat);
|
|
||||||
}
|
|
||||||
@ {
|
|
||||||
yylval.nil = nullptr;
|
|
||||||
|
|
||||||
return static_cast<int>(elna::source::token::type::at);
|
|
||||||
}
|
|
||||||
. {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
%%
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
|
|
||||||
result<lexer> tokenize(const std::filesystem::path& path)
|
|
||||||
{
|
|
||||||
int yytoken;
|
|
||||||
std::vector<token> tokens;
|
|
||||||
|
|
||||||
yyin = fopen(path.c_str(), "rb");
|
|
||||||
if (yyin == nullptr)
|
|
||||||
{
|
|
||||||
throw std::ios_base::failure("File does not exist");
|
|
||||||
}
|
|
||||||
do
|
|
||||||
{
|
|
||||||
yytoken = yylex();
|
|
||||||
|
|
||||||
if (yytoken < 0)
|
|
||||||
{
|
|
||||||
return result<lexer>(unexpected_character{ std::string{ yytext[0] }, path, token_position });
|
|
||||||
}
|
|
||||||
tokens.emplace_back(static_cast<token::type>(yytoken), std::move(yylval), token_position);
|
|
||||||
}
|
|
||||||
while (yytoken != 0);
|
|
||||||
|
|
||||||
return result<lexer>(std::in_place, std::move(tokens), position{ line_no, column_no }, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,325 +0,0 @@
|
|||||||
#include "elna/source/semantic.hpp"
|
|
||||||
#include "elna/source/result.hpp"
|
|
||||||
#include <cstdlib>
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
name_analysis_visitor::name_analysis_visitor(std::shared_ptr<symbol_table> table,
|
|
||||||
const std::filesystem::path& filename, const std::size_t target_pointer_size)
|
|
||||||
: table(table), filename(filename), pointer_size(target_pointer_size)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void name_analysis_visitor::visit(constant_definition *definition)
|
|
||||||
{
|
|
||||||
auto constant_type = std::make_shared<const class type>(int_type);
|
|
||||||
this->table->enter(definition->identifier(),
|
|
||||||
std::make_shared<constant_info>(constant_type, definition->body().number()));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const type> name_analysis_visitor::convert_declaration_type(const type_expression& ast_type) const
|
|
||||||
{
|
|
||||||
auto variable_type = std::dynamic_pointer_cast<type_info>(table->lookup(ast_type.base()))
|
|
||||||
->type();
|
|
||||||
std::shared_ptr<type> declaration_type;
|
|
||||||
|
|
||||||
if (ast_type.is_pointer())
|
|
||||||
{
|
|
||||||
return std::make_shared<pointer_type>(variable_type, 4);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return variable_type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void name_analysis_visitor::visit(declaration *declaration)
|
|
||||||
{
|
|
||||||
std::shared_ptr<const type> declaration_type = convert_declaration_type(declaration->type());
|
|
||||||
|
|
||||||
this->table->enter(declaration->identifier(),
|
|
||||||
std::make_shared<variable_info>(declaration_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
void name_analysis_visitor::visit(program *program)
|
|
||||||
{
|
|
||||||
class procedure_type main_type{ std::vector<std::shared_ptr<const class type>>(), this->pointer_size };
|
|
||||||
this->table->enter("_start", std::make_shared<procedure_info>(main_type, this->table));
|
|
||||||
empty_visitor::visit(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
void name_analysis_visitor::visit(procedure_definition *procedure)
|
|
||||||
{
|
|
||||||
std::vector<std::shared_ptr<const type>> arguments;
|
|
||||||
|
|
||||||
for (auto& parameter : procedure->parameters())
|
|
||||||
{
|
|
||||||
auto declaration_type = convert_declaration_type(parameter->type());
|
|
||||||
arguments.push_back(declaration_type);
|
|
||||||
}
|
|
||||||
procedure_type definition_type{ std::move(arguments), this->pointer_size };
|
|
||||||
auto info = std::make_shared<procedure_info>(definition_type, this->table);
|
|
||||||
|
|
||||||
this->table->enter(procedure->identifier(), info);
|
|
||||||
this->table = info->scope();
|
|
||||||
|
|
||||||
for (std::size_t i = 0; i < procedure->parameters().size(); ++i)
|
|
||||||
{
|
|
||||||
this->table->enter(procedure->parameters().at(i)->identifier(),
|
|
||||||
std::make_shared<parameter_info>(definition_type.arguments.at(i)));
|
|
||||||
}
|
|
||||||
procedure->body().accept(this);
|
|
||||||
|
|
||||||
this->table = info->scope()->scope();
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::list<std::unique_ptr<error>>& name_analysis_visitor::errors() const noexcept
|
|
||||||
{
|
|
||||||
return m_errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
allocator_visitor::allocator_visitor(std::shared_ptr<symbol_table> table)
|
|
||||||
: table(table)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocator_visitor::visit(declaration *declaration)
|
|
||||||
{
|
|
||||||
auto declaration_info = this->table->lookup(declaration->identifier());
|
|
||||||
|
|
||||||
if (auto variable = std::dynamic_pointer_cast<variable_info>(declaration_info))
|
|
||||||
{
|
|
||||||
this->local_offset -= sizeof(std::int32_t);
|
|
||||||
variable->offset = this->local_offset;
|
|
||||||
}
|
|
||||||
else if (auto parameter = std::dynamic_pointer_cast<parameter_info>(declaration_info))
|
|
||||||
{
|
|
||||||
parameter->offset = this->argument_offset;
|
|
||||||
this->argument_offset += sizeof(std::int32_t);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocator_visitor::visit(program *program)
|
|
||||||
{
|
|
||||||
this->local_offset = 0;
|
|
||||||
this->argument_offset = 0;
|
|
||||||
|
|
||||||
empty_visitor::visit(program);
|
|
||||||
std::dynamic_pointer_cast<procedure_info>(table->lookup("_start"))->local_stack_size =
|
|
||||||
std::abs(this->local_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocator_visitor::visit(procedure_definition *procedure)
|
|
||||||
{
|
|
||||||
this->local_offset = 0;
|
|
||||||
this->argument_offset = 0;
|
|
||||||
auto info = std::dynamic_pointer_cast<procedure_info>(this->table->lookup(procedure->identifier()));
|
|
||||||
this->table = info->scope();
|
|
||||||
|
|
||||||
empty_visitor::visit(procedure);
|
|
||||||
|
|
||||||
this->table = info->scope()->scope();
|
|
||||||
info->local_stack_size = std::abs(this->local_offset);
|
|
||||||
info->argument_stack_size = this->argument_offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
void allocator_visitor::visit(call_statement *statement)
|
|
||||||
{
|
|
||||||
auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name()));
|
|
||||||
|
|
||||||
this->argument_offset = std::max(static_cast<std::size_t>(this->argument_offset),
|
|
||||||
call_info->parameter_stack_size());
|
|
||||||
}
|
|
||||||
|
|
||||||
type_analysis_visitor::type_analysis_visitor(std::shared_ptr<symbol_table> table,
|
|
||||||
const std::filesystem::path& filename, const std::size_t target_pointer_size)
|
|
||||||
: table(table), filename(filename), pointer_size(target_pointer_size)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(program *program)
|
|
||||||
{
|
|
||||||
for (auto& definition : program->definitions())
|
|
||||||
{
|
|
||||||
if (dynamic_cast<procedure_definition *>(definition.get()) != nullptr)
|
|
||||||
{
|
|
||||||
definition->accept(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
program->body().accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(procedure_definition *procedure)
|
|
||||||
{
|
|
||||||
auto info = std::dynamic_pointer_cast<procedure_info>(this->table->lookup(procedure->identifier()));
|
|
||||||
this->table = info->scope();
|
|
||||||
|
|
||||||
procedure->body().accept(this);
|
|
||||||
|
|
||||||
this->table = info->scope()->scope();
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(integer_literal *literal)
|
|
||||||
{
|
|
||||||
literal->data_type = std::dynamic_pointer_cast<type_info>(table->lookup("Int"))->type();
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(boolean_literal *literal)
|
|
||||||
{
|
|
||||||
literal->data_type = std::dynamic_pointer_cast<type_info>(table->lookup("Boolean"))->type();
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(variable_expression *expression)
|
|
||||||
{
|
|
||||||
expression->data_type = std::dynamic_pointer_cast<typed_info>(table->lookup(expression->name()))->type();
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(unary_expression *expression)
|
|
||||||
{
|
|
||||||
empty_visitor::visit(expression);
|
|
||||||
|
|
||||||
switch (expression->operation())
|
|
||||||
{
|
|
||||||
case unary_operator::reference:
|
|
||||||
expression->data_type = std::make_shared<const pointer_type>(expression->operand().data_type,
|
|
||||||
this->pointer_size);
|
|
||||||
break;
|
|
||||||
case unary_operator::dereference:
|
|
||||||
auto operand_type = expression->operand().data_type;
|
|
||||||
|
|
||||||
if (auto referenced_type = std::dynamic_pointer_cast<const pointer_type>(operand_type))
|
|
||||||
{
|
|
||||||
expression->data_type = referenced_type;
|
|
||||||
}
|
|
||||||
else if (operand_type != nullptr)
|
|
||||||
{
|
|
||||||
auto new_error = std::make_unique<type_mismatch>(operand_type,
|
|
||||||
type_mismatch::operation::dereference, this->filename, expression->position());
|
|
||||||
m_errors.push_back(std::move(new_error));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(binary_expression *expression)
|
|
||||||
{
|
|
||||||
empty_visitor::visit(expression);
|
|
||||||
|
|
||||||
switch (expression->operation())
|
|
||||||
{
|
|
||||||
case binary_operator::sum:
|
|
||||||
case binary_operator::subtraction:
|
|
||||||
case binary_operator::multiplication:
|
|
||||||
case binary_operator::division:
|
|
||||||
case binary_operator::less:
|
|
||||||
case binary_operator::greater:
|
|
||||||
case binary_operator::less_equal:
|
|
||||||
case binary_operator::greater_equal:
|
|
||||||
if (expression->lhs().data_type != nullptr && expression->rhs().data_type != nullptr)
|
|
||||||
{
|
|
||||||
auto lhs_type = std::dynamic_pointer_cast<const primitive_type>(expression->lhs().data_type);
|
|
||||||
auto rhs_type = std::dynamic_pointer_cast<const primitive_type>(expression->rhs().data_type);
|
|
||||||
|
|
||||||
std::unique_ptr<type_mismatch> new_error;
|
|
||||||
if (lhs_type == nullptr || *lhs_type != int_type)
|
|
||||||
{
|
|
||||||
new_error = std::make_unique<type_mismatch>(lhs_type,
|
|
||||||
type_mismatch::operation::arithmetic, this->filename, expression->lhs().position());
|
|
||||||
}
|
|
||||||
if (rhs_type == nullptr || *rhs_type != int_type)
|
|
||||||
{
|
|
||||||
new_error = std::make_unique<type_mismatch>(rhs_type,
|
|
||||||
type_mismatch::operation::arithmetic, this->filename, expression->rhs().position());
|
|
||||||
}
|
|
||||||
if (new_error != nullptr)
|
|
||||||
{
|
|
||||||
m_errors.push_back(std::move(new_error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case binary_operator::equals:
|
|
||||||
case binary_operator::not_equals:
|
|
||||||
if (expression->lhs().data_type != nullptr && expression->rhs().data_type != nullptr)
|
|
||||||
{
|
|
||||||
if (expression->lhs().data_type != expression->rhs().data_type)
|
|
||||||
{
|
|
||||||
auto new_error = std::make_unique<type_mismatch>(expression->rhs().data_type,
|
|
||||||
type_mismatch::operation::comparison, this->filename, expression->rhs().position());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(call_statement *statement)
|
|
||||||
{
|
|
||||||
auto call_info = std::dynamic_pointer_cast<intrinsic_info>(this->table->lookup(statement->name()));
|
|
||||||
|
|
||||||
std::size_t i{ 0 };
|
|
||||||
for (const auto& argument : statement->arguments())
|
|
||||||
{
|
|
||||||
argument->accept(this);
|
|
||||||
|
|
||||||
if (argument->data_type != nullptr && i < call_info->type()->arguments.size()
|
|
||||||
&& call_info->type()->arguments[i] != argument->data_type)
|
|
||||||
{
|
|
||||||
auto new_error = std::make_unique<type_mismatch>(argument->data_type,
|
|
||||||
type_mismatch::operation::argument, this->filename, argument->position());
|
|
||||||
m_errors.push_back(std::move(new_error));
|
|
||||||
}
|
|
||||||
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(constant_definition *definition)
|
|
||||||
{
|
|
||||||
definition->body().accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(while_statement *statement)
|
|
||||||
{
|
|
||||||
statement->prerequisite().accept(this);
|
|
||||||
auto condition_type = std::dynamic_pointer_cast<const primitive_type>(statement->prerequisite().data_type);
|
|
||||||
|
|
||||||
if (condition_type != nullptr && *condition_type != boolean_type)
|
|
||||||
{
|
|
||||||
auto new_error = std::make_unique<type_mismatch>(condition_type,
|
|
||||||
type_mismatch::operation::condition, this->filename, statement->prerequisite().position());
|
|
||||||
m_errors.push_back(std::move(new_error));
|
|
||||||
}
|
|
||||||
statement->body().accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(if_statement *statement)
|
|
||||||
{
|
|
||||||
statement->prerequisite().accept(this);
|
|
||||||
auto condition_type = std::dynamic_pointer_cast<const primitive_type>(statement->prerequisite().data_type);
|
|
||||||
|
|
||||||
if (condition_type != nullptr && *condition_type != boolean_type)
|
|
||||||
{
|
|
||||||
auto new_error = std::make_unique<type_mismatch>(condition_type,
|
|
||||||
type_mismatch::operation::condition, this->filename, statement->prerequisite().position());
|
|
||||||
m_errors.push_back(std::move(new_error));
|
|
||||||
}
|
|
||||||
statement->body().accept(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void type_analysis_visitor::visit(assign_statement *statement)
|
|
||||||
{
|
|
||||||
statement->rvalue().accept(this);
|
|
||||||
auto lvalue_info = std::dynamic_pointer_cast<typed_info>(this->table->lookup(statement->lvalue()));
|
|
||||||
|
|
||||||
if (statement->rvalue().data_type != nullptr && lvalue_info->type() == statement->rvalue().data_type)
|
|
||||||
{
|
|
||||||
auto new_error = std::make_unique<type_mismatch>(statement->rvalue().data_type,
|
|
||||||
type_mismatch::operation::assignment, this->filename, statement->position());
|
|
||||||
m_errors.push_back(std::move(new_error));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::list<std::unique_ptr<error>>& type_analysis_visitor::errors() const noexcept
|
|
||||||
{
|
|
||||||
return m_errors;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,129 +0,0 @@
|
|||||||
#include "elna/source/types.hpp"
|
|
||||||
#include "elna/source/symbol_table.hpp"
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
symbol_table::symbol_table(std::shared_ptr<symbol_table> scope)
|
|
||||||
: outer_scope(scope)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<info> symbol_table::lookup(const std::string& name)
|
|
||||||
{
|
|
||||||
auto entry = entries.find(name);
|
|
||||||
|
|
||||||
if (entry != entries.cend())
|
|
||||||
{
|
|
||||||
return entry->second;
|
|
||||||
}
|
|
||||||
if (this->outer_scope != nullptr)
|
|
||||||
{
|
|
||||||
return this->outer_scope->lookup(name);
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void symbol_table::enter(const std::string& name, std::shared_ptr<info> entry)
|
|
||||||
{
|
|
||||||
entries.insert_or_assign(name, entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<symbol_table> symbol_table::scope()
|
|
||||||
{
|
|
||||||
return this->outer_scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
info::~info()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
info::info()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
type_info::type_info(const class type& type)
|
|
||||||
: info(), m_type(std::make_shared<class type>(type))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
type_info::~type_info()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const class type> type_info::type() const noexcept
|
|
||||||
{
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
typed_info::typed_info(std::shared_ptr<const class type> type)
|
|
||||||
: m_type(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
typed_info::~typed_info()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const class type> typed_info::type() const noexcept
|
|
||||||
{
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
constant_info::constant_info(const std::shared_ptr<const class type> type, const std::int32_t value)
|
|
||||||
: typed_info(type), m_value(value)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::int32_t constant_info::value() const noexcept
|
|
||||||
{
|
|
||||||
return m_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
variable_info::variable_info(std::shared_ptr<const class type> type)
|
|
||||||
: typed_info(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
parameter_info::parameter_info(std::shared_ptr<const class type> type)
|
|
||||||
: typed_info(type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
intrinsic_info::intrinsic_info(const class procedure_type& type)
|
|
||||||
: m_type(std::make_shared<procedure_type>(type))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
intrinsic_info::~intrinsic_info()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<const class procedure_type> intrinsic_info::type() const noexcept
|
|
||||||
{
|
|
||||||
return m_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t intrinsic_info::parameter_stack_size() const noexcept
|
|
||||||
{
|
|
||||||
return type()->arguments.size() * sizeof(std::int32_t);
|
|
||||||
}
|
|
||||||
|
|
||||||
procedure_info::procedure_info(const class procedure_type& type, std::shared_ptr<symbol_table> outer_scope)
|
|
||||||
: intrinsic_info(type), local_table(std::make_shared<symbol_table>(outer_scope))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
procedure_info::~procedure_info()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<symbol_table> procedure_info::scope()
|
|
||||||
{
|
|
||||||
return local_table;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t procedure_info::stack_size() const noexcept
|
|
||||||
{
|
|
||||||
return local_stack_size + argument_stack_size;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,96 +0,0 @@
|
|||||||
#include <elna/source/types.hpp>
|
|
||||||
|
|
||||||
namespace elna::source
|
|
||||||
{
|
|
||||||
type::type(const std::size_t byte_size)
|
|
||||||
: byte_size(byte_size)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
std::size_t type::size() const noexcept
|
|
||||||
{
|
|
||||||
return this->byte_size;
|
|
||||||
}
|
|
||||||
|
|
||||||
primitive_type::primitive_type(const std::string& type_name, const std::size_t byte_size)
|
|
||||||
: type(byte_size), type_name(type_name)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool primitive_type::operator==(const primitive_type& that) const noexcept
|
|
||||||
{
|
|
||||||
return this->type_name == that.type_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool primitive_type::operator!=(const primitive_type& that) const noexcept
|
|
||||||
{
|
|
||||||
return this->type_name != that.type_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer_type::pointer_type(std::shared_ptr<const type> base_type, const std::size_t byte_size)
|
|
||||||
: type(byte_size), base_type(base_type)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pointer_type::operator==(const pointer_type& that) const noexcept
|
|
||||||
{
|
|
||||||
return this->base_type == that.base_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool pointer_type::operator!=(const pointer_type& that) const noexcept
|
|
||||||
{
|
|
||||||
return this->base_type != that.base_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
procedure_type::procedure_type(std::vector<std::shared_ptr<const type>> arguments, const std::size_t byte_size)
|
|
||||||
: arguments(std::move(arguments)), type(byte_size)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
bool procedure_type::operator==(const procedure_type &that) const noexcept
|
|
||||||
{
|
|
||||||
return this->arguments == that.arguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool procedure_type::operator!=(const procedure_type &that) const noexcept
|
|
||||||
{
|
|
||||||
return this->arguments != that.arguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator==(const type& lhs, const type& rhs) noexcept
|
|
||||||
{
|
|
||||||
{
|
|
||||||
auto lhs_type = dynamic_cast<const primitive_type *>(&lhs);
|
|
||||||
auto rhs_type = dynamic_cast<const primitive_type *>(&rhs);
|
|
||||||
|
|
||||||
if (lhs_type != nullptr && rhs_type != nullptr)
|
|
||||||
{
|
|
||||||
return *lhs_type == *rhs_type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto lhs_type = dynamic_cast<const pointer_type *>(&lhs);
|
|
||||||
auto rhs_type = dynamic_cast<const pointer_type *>(&rhs);
|
|
||||||
|
|
||||||
if (lhs_type != nullptr && rhs_type != nullptr)
|
|
||||||
{
|
|
||||||
return *lhs_type == *rhs_type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
{
|
|
||||||
auto lhs_type = dynamic_cast<const procedure_type *>(&lhs);
|
|
||||||
auto rhs_type = dynamic_cast<const procedure_type *>(&rhs);
|
|
||||||
|
|
||||||
if (lhs_type != nullptr && rhs_type != nullptr)
|
|
||||||
{
|
|
||||||
return *lhs_type == *rhs_type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator!=(const type& lhs, const type& rhs) noexcept
|
|
||||||
{
|
|
||||||
return !(lhs == rhs);
|
|
||||||
}
|
|
||||||
}
|
|
53
src/Main.hs
Normal file
53
src/Main.hs
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
module Main
|
||||||
|
( main
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Language.Elna.CommandLine (CommandLine(..), commandLine, execParser)
|
||||||
|
import Language.Elna.Object.ElfCoder (elfObject)
|
||||||
|
import Language.Elna.Backend.Allocator (allocate)
|
||||||
|
import Language.Elna.Glue (glue)
|
||||||
|
import Language.Elna.Frontend.NameAnalysis (nameAnalysis)
|
||||||
|
import Language.Elna.Frontend.Parser (programP)
|
||||||
|
import Language.Elna.Frontend.TypeAnalysis (typeAnalysis)
|
||||||
|
import Language.Elna.RiscV.CodeGenerator (generateRiscV, riscVConfiguration)
|
||||||
|
import Language.Elna.RiscV.ElfWriter (riscv32Elf)
|
||||||
|
import Data.Maybe (fromMaybe)
|
||||||
|
import System.FilePath (replaceExtension, takeFileName)
|
||||||
|
import Text.Megaparsec (runParser, errorBundlePretty)
|
||||||
|
import qualified Data.Text.IO as Text
|
||||||
|
import System.Exit (ExitCode(..), exitWith)
|
||||||
|
import Control.Exception (IOException, catch)
|
||||||
|
|
||||||
|
-- * Error codes
|
||||||
|
--
|
||||||
|
-- 1 - Command line parsing failed and other errors.
|
||||||
|
-- 2 - The input could not be read.
|
||||||
|
-- 3 - Parse error.
|
||||||
|
-- 4 - Name analysis error.
|
||||||
|
-- 5 - Type error.
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = execParser commandLine >>= withCommandLine
|
||||||
|
where
|
||||||
|
withCommandLine CommandLine{..} =
|
||||||
|
let defaultOutputName = replaceExtension (takeFileName input) "o"
|
||||||
|
outputName = fromMaybe defaultOutputName output
|
||||||
|
in catch (Text.readFile input) (printAndExit 2 :: IOException -> IO a)
|
||||||
|
>>= withParsedInput outputName
|
||||||
|
. runParser programP input
|
||||||
|
withParsedInput output (Right program)
|
||||||
|
= either (printAndExit 4) (withSymbolTable output program)
|
||||||
|
$ nameAnalysis program
|
||||||
|
withParsedInput _ (Left errorBundle)
|
||||||
|
= putStrLn (errorBundlePretty errorBundle)
|
||||||
|
>> exitWith (ExitFailure 3)
|
||||||
|
withSymbolTable output program symbolTable
|
||||||
|
| Just typeError <- typeAnalysis symbolTable program =
|
||||||
|
printAndExit 5 typeError
|
||||||
|
| otherwise =
|
||||||
|
let instructions = generateRiscV
|
||||||
|
$ allocate riscVConfiguration
|
||||||
|
$ glue symbolTable program
|
||||||
|
in elfObject output $ riscv32Elf instructions
|
||||||
|
printAndExit :: Show b => forall a. Int -> b -> IO a
|
||||||
|
printAndExit failureCode e = print e >> exitWith (ExitFailure failureCode)
|
78
tests/Language/Elna/NameAnalysisSpec.hs
Normal file
78
tests/Language/Elna/NameAnalysisSpec.hs
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
module Language.Elna.NameAnalysisSpec
|
||||||
|
( spec
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Data.Text (Text)
|
||||||
|
import Text.Megaparsec (runParser)
|
||||||
|
import Test.Hspec
|
||||||
|
( Spec
|
||||||
|
, describe
|
||||||
|
, expectationFailure
|
||||||
|
, it
|
||||||
|
, shouldBe
|
||||||
|
, shouldSatisfy
|
||||||
|
)
|
||||||
|
import Language.Elna.NameAnalysis (Error(..), nameAnalysis)
|
||||||
|
import Language.Elna.SymbolTable (Info(..), SymbolTable)
|
||||||
|
import qualified Language.Elna.SymbolTable as SymbolTable
|
||||||
|
import qualified Language.Elna.Parser as AST
|
||||||
|
import Language.Elna.Types (intType)
|
||||||
|
import Control.Exception (throwIO)
|
||||||
|
|
||||||
|
nameAnalysisOnText :: Text -> IO (Either Error SymbolTable)
|
||||||
|
nameAnalysisOnText sourceText = nameAnalysis
|
||||||
|
<$> either throwIO pure (runParser AST.programP "" sourceText)
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec = describe "nameAnalysis" $ do
|
||||||
|
it "adds type to the symbol table" $ do
|
||||||
|
let given = "type A = int;"
|
||||||
|
expected = Right $ Just $ TypeInfo intType
|
||||||
|
actual <- nameAnalysisOnText given
|
||||||
|
|
||||||
|
actual `shouldSatisfy` (expected ==) . fmap (SymbolTable.lookup "A")
|
||||||
|
|
||||||
|
it "errors when the aliased type is not defined" $ do
|
||||||
|
let given = "type A = B;"
|
||||||
|
expected = Left $ UndefinedTypeError "B"
|
||||||
|
actual <- nameAnalysisOnText given
|
||||||
|
|
||||||
|
actual `shouldBe` expected
|
||||||
|
|
||||||
|
it "errors if the aliased identifier is not a type" $ do
|
||||||
|
let given = "proc main() {} type A = main;"
|
||||||
|
expected = Left
|
||||||
|
$ UnexpectedTypeInfoError
|
||||||
|
$ ProcedureInfo mempty mempty
|
||||||
|
actual <- nameAnalysisOnText given
|
||||||
|
|
||||||
|
actual `shouldBe` expected
|
||||||
|
|
||||||
|
it "replaces the alias with an equivalent base type" $ do
|
||||||
|
let given = "type A = int; type B = A; type C = B;"
|
||||||
|
expected = Right $ Just $ TypeInfo intType
|
||||||
|
actual <- nameAnalysisOnText given
|
||||||
|
|
||||||
|
actual `shouldSatisfy` (expected ==) . fmap (SymbolTable.lookup "C")
|
||||||
|
|
||||||
|
it "puts parameters into the local symbol table" $ do
|
||||||
|
let given = "proc main(ref param: int) {}"
|
||||||
|
expected = SymbolTable.enter "param" (VariableInfo True intType) SymbolTable.empty
|
||||||
|
actual <- nameAnalysisOnText given
|
||||||
|
|
||||||
|
case SymbolTable.lookup "main" <$> actual of
|
||||||
|
Right lookupResult
|
||||||
|
| Just (ProcedureInfo localTable _) <- lookupResult ->
|
||||||
|
Just localTable `shouldBe` expected
|
||||||
|
_ -> expectationFailure "Procedure symbol not found"
|
||||||
|
|
||||||
|
it "puts variables into the local symbol table" $ do
|
||||||
|
let given = "proc main() { var var1: int; }"
|
||||||
|
expected = SymbolTable.enter "var1" (VariableInfo False intType) SymbolTable.empty
|
||||||
|
actual <- nameAnalysisOnText given
|
||||||
|
|
||||||
|
case SymbolTable.lookup "main" <$> actual of
|
||||||
|
Right lookupResult
|
||||||
|
| Just (ProcedureInfo localTable _) <- lookupResult ->
|
||||||
|
Just localTable `shouldBe` expected
|
||||||
|
_ -> expectationFailure "Procedure symbol not found"
|
142
tests/Language/Elna/ParserSpec.hs
Normal file
142
tests/Language/Elna/ParserSpec.hs
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
module Language.Elna.ParserSpec
|
||||||
|
( spec
|
||||||
|
) where
|
||||||
|
|
||||||
|
import Test.Hspec (Spec, describe, it)
|
||||||
|
import Test.Hspec.Megaparsec (shouldParse, shouldSucceedOn, parseSatisfies)
|
||||||
|
import Language.Elna.Parser (programP)
|
||||||
|
import Text.Megaparsec (parse)
|
||||||
|
import Language.Elna.AST
|
||||||
|
( Declaration(..)
|
||||||
|
, Expression(..)
|
||||||
|
, Literal(..)
|
||||||
|
, Statement(..)
|
||||||
|
, Parameter(..)
|
||||||
|
, Program(..)
|
||||||
|
, VariableDeclaration(..)
|
||||||
|
, TypeExpression(..)
|
||||||
|
)
|
||||||
|
|
||||||
|
spec :: Spec
|
||||||
|
spec =
|
||||||
|
describe "programP" $ do
|
||||||
|
it "parses an empty main function" $
|
||||||
|
parse programP "" `shouldSucceedOn` "proc main() {}"
|
||||||
|
|
||||||
|
it "parses type definition for a type starting like array" $
|
||||||
|
let expected = Program [TypeDefinition "t" $ NamedType "arr"]
|
||||||
|
actual = parse programP "" "type t = arr;"
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses array type definition" $
|
||||||
|
let expected = Program [TypeDefinition "t" $ ArrayType 10 (NamedType "int")]
|
||||||
|
actual = parse programP "" "type t = array[10] of int;"
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses parameters" $
|
||||||
|
let given = "proc main(x: int) {}"
|
||||||
|
parameters = [Parameter "x" (NamedType "int") False]
|
||||||
|
expected = Program [ProcedureDefinition "main" parameters [] []]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses ref parameters" $
|
||||||
|
let given = "proc main(x: int, ref y: boolean) {}"
|
||||||
|
parameters =
|
||||||
|
[ Parameter "x" (NamedType "int") False
|
||||||
|
, Parameter "y" (NamedType "boolean") True
|
||||||
|
]
|
||||||
|
expected = Program [ProcedureDefinition "main" parameters [] []]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses variable declaration" $
|
||||||
|
let given = "proc main() { var x: int; }"
|
||||||
|
expected (Program [ProcedureDefinition _ _ variables _]) =
|
||||||
|
not $ null variables
|
||||||
|
expected _ = False
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `parseSatisfies` expected
|
||||||
|
|
||||||
|
it "parses negation" $
|
||||||
|
let given = "proc main(x: int) { var y: int; y := -x; }"
|
||||||
|
parameters = pure $ Parameter "x" (NamedType "int") False
|
||||||
|
variables = pure
|
||||||
|
$ VariableDeclaration "y"
|
||||||
|
$ NamedType "int"
|
||||||
|
body = pure
|
||||||
|
$ AssignmentStatement (VariableExpression "y")
|
||||||
|
$ NegationExpression
|
||||||
|
$ VariableExpression "x"
|
||||||
|
expected = Program
|
||||||
|
[ProcedureDefinition "main" parameters variables body]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses comparison with lower precedence than other binary operators" $
|
||||||
|
let given = "proc main() { var x: boolean; x := 1 + 2 = 3 * 4; }"
|
||||||
|
variables = pure
|
||||||
|
$ VariableDeclaration "x"
|
||||||
|
$ NamedType "boolean"
|
||||||
|
lhs = SumExpression (LiteralExpression (IntegerLiteral 1))
|
||||||
|
$ LiteralExpression (IntegerLiteral 2)
|
||||||
|
rhs = ProductExpression (LiteralExpression (IntegerLiteral 3))
|
||||||
|
$ LiteralExpression (IntegerLiteral 4)
|
||||||
|
body = pure
|
||||||
|
$ AssignmentStatement (VariableExpression "x")
|
||||||
|
$ EqualExpression lhs rhs
|
||||||
|
expected = Program
|
||||||
|
[ProcedureDefinition "main" [] variables body]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses hexadecimals" $
|
||||||
|
let given = "proc main() { var x: int; x := 0x10; }"
|
||||||
|
variables = pure
|
||||||
|
$ VariableDeclaration "x"
|
||||||
|
$ NamedType "int"
|
||||||
|
body = pure
|
||||||
|
$ AssignmentStatement (VariableExpression "x")
|
||||||
|
$ LiteralExpression (HexadecimalLiteral 16)
|
||||||
|
expected = Program
|
||||||
|
[ProcedureDefinition "main" [] variables body]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses procedure calls" $
|
||||||
|
let given = "proc main() { f('c'); }"
|
||||||
|
body = pure
|
||||||
|
$ CallStatement "f" [LiteralExpression (CharacterLiteral 99)]
|
||||||
|
expected = Program
|
||||||
|
[ProcedureDefinition "main" [] [] body]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses an if statement" $
|
||||||
|
let given = "proc main() { if (true) ; }"
|
||||||
|
body = pure
|
||||||
|
$ IfStatement (LiteralExpression $ BooleanLiteral True) EmptyStatement Nothing
|
||||||
|
expected = Program
|
||||||
|
[ProcedureDefinition "main" [] [] body]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "associates else with the nearst if statement" $
|
||||||
|
let given = "proc main() { if (true) if (false) ; else ; }"
|
||||||
|
if' = IfStatement (LiteralExpression $ BooleanLiteral False) EmptyStatement
|
||||||
|
$ Just EmptyStatement
|
||||||
|
body = pure
|
||||||
|
$ IfStatement (LiteralExpression $ BooleanLiteral True) if' Nothing
|
||||||
|
expected = Program
|
||||||
|
[ProcedureDefinition "main" [] [] body]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
||||||
|
|
||||||
|
it "parses a while statement" $
|
||||||
|
let given = "proc main() { while (true) ; }"
|
||||||
|
body = pure
|
||||||
|
$ WhileStatement (LiteralExpression $ BooleanLiteral True) EmptyStatement
|
||||||
|
expected = Program
|
||||||
|
[ProcedureDefinition "main" [] [] body]
|
||||||
|
actual = parse programP "" given
|
||||||
|
in actual `shouldParse` expected
|
1
tests/Spec.hs
Normal file
1
tests/Spec.hs
Normal file
@ -0,0 +1 @@
|
|||||||
|
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
|
@ -1,3 +0,0 @@
|
|||||||
const a = 1, b = 2;
|
|
||||||
writei(a + b)
|
|
||||||
.
|
|
@ -1,5 +0,0 @@
|
|||||||
var x: Int;
|
|
||||||
begin
|
|
||||||
x := 5;
|
|
||||||
writei(x)
|
|
||||||
end.
|
|
@ -1 +0,0 @@
|
|||||||
writei(6 / 3)
|
|
1
tests/expectations/add_products.txt
Normal file
1
tests/expectations/add_products.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
38
|
1
tests/expectations/add_to_variable.txt
Normal file
1
tests/expectations/add_to_variable.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
58
|
1
tests/expectations/define_variable.txt
Normal file
1
tests/expectations/define_variable.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
58
|
1
tests/expectations/exit_between_statements.txt
Normal file
1
tests/expectations/exit_between_statements.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
c
|
@ -1,2 +0,0 @@
|
|||||||
8
|
|
||||||
9
|
|
@ -1 +0,0 @@
|
|||||||
8
|
|
@ -1 +0,0 @@
|
|||||||
8
|
|
@ -1 +0,0 @@
|
|||||||
H
|
|
1
tests/expectations/parse_negative_numbers.txt
Normal file
1
tests/expectations/parse_negative_numbers.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
-8
|
@ -1 +0,0 @@
|
|||||||
4
|
|
1
tests/expectations/print0.txt
Normal file
1
tests/expectations/print0.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
0
|
1
tests/expectations/print_2_digits.txt
Normal file
1
tests/expectations/print_2_digits.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
13
|
2
tests/expectations/print_2_statements.txt
Normal file
2
tests/expectations/print_2_statements.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
13
|
||||||
|
2097150
|
@ -1,2 +0,0 @@
|
|||||||
t
|
|
||||||
f
|
|
1
tests/expectations/print_char.txt
Normal file
1
tests/expectations/print_char.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
x
|
@ -1,12 +0,0 @@
|
|||||||
t
|
|
||||||
f
|
|
||||||
t
|
|
||||||
f
|
|
||||||
f
|
|
||||||
t
|
|
||||||
t
|
|
||||||
f
|
|
||||||
t
|
|
||||||
f
|
|
||||||
f
|
|
||||||
t
|
|
@ -1,5 +0,0 @@
|
|||||||
1
|
|
||||||
2
|
|
||||||
3
|
|
||||||
4
|
|
||||||
5
|
|
1
tests/expectations/print_more_20_bits.txt
Normal file
1
tests/expectations/print_more_20_bits.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
2097150
|
1
tests/expectations/print_negate.txt
Normal file
1
tests/expectations/print_negate.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
-8
|
1
tests/expectations/print_product.txt
Normal file
1
tests/expectations/print_product.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
1000
|
1
tests/expectations/print_subtraction.txt
Normal file
1
tests/expectations/print_subtraction.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
-8
|
1
tests/expectations/print_sum.txt
Normal file
1
tests/expectations/print_sum.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
18
|
1
tests/expectations/printi_hex.txt
Normal file
1
tests/expectations/printi_hex.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
129
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user