Compare commits

...

No commits in common. "haskell" and "bison-gen" have entirely different histories.

171 changed files with 13585 additions and 4673 deletions

1
.gitignore vendored
View File

@ -3,4 +3,3 @@
CMakeFiles/
CMakeCache.txt
node_modules/
/dist-newstyle/

View File

@ -1 +0,0 @@
3.3.5

46
CMakeLists.txt Normal file
View File

@ -0,0 +1,46 @@
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 program_options REQUIRED)
find_package(FLEX REQUIRED)
find_package(BISON REQUIRED)
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})
FLEX_TARGET(lexer parser/lexer.ll ${CMAKE_CURRENT_BINARY_DIR}/lexer.cpp)
BISON_TARGET(parser parser/parser.yy ${CMAKE_CURRENT_BINARY_DIR}/parser.cpp)
add_flex_bison_dependency(lexer parser)
add_executable(test parser/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
${BISON_parser_OUTPUTS} ${FLEX_lexer_OUTPUTS}
)
target_include_directories(test PRIVATE ${CMAKE_CURRENT_BINARY_DIR} parser include)
# target_link_libraries(test ${FLEX_LIBRARIES})

373
LICENSE
View File

@ -1,373 +0,0 @@
Mozilla Public License Version 2.0
==================================
1. Definitions
--------------
1.1. "Contributor"
means each individual or legal entity that creates, contributes to
the creation of, or owns Covered Software.
1.2. "Contributor Version"
means the combination of the Contributions of others (if any) used
by a Contributor and that particular Contributor's Contribution.
1.3. "Contribution"
means Covered Software of a particular Contributor.
1.4. "Covered Software"
means Source Code Form to which the initial Contributor has attached
the notice in Exhibit A, the Executable Form of such Source Code
Form, and Modifications of such Source Code Form, in each case
including portions thereof.
1.5. "Incompatible With Secondary Licenses"
means
(a) that the initial Contributor has attached the notice described
in Exhibit B to the Covered Software; or
(b) that the Covered Software was made available under the terms of
version 1.1 or earlier of the License, but not also under the
terms of a Secondary License.
1.6. "Executable Form"
means any form of the work other than Source Code Form.
1.7. "Larger Work"
means a work that combines Covered Software with other material, in
a separate file or files, that is not Covered Software.
1.8. "License"
means this document.
1.9. "Licensable"
means having the right to grant, to the maximum extent possible,
whether at the time of the initial grant or subsequently, any and
all of the rights conveyed by this License.
1.10. "Modifications"
means any of the following:
(a) any file in Source Code Form that results from an addition to,
deletion from, or modification of the contents of Covered
Software; or
(b) any new file in Source Code Form that contains any Covered
Software.
1.11. "Patent Claims" of a Contributor
means any patent claim(s), including without limitation, method,
process, and apparatus claims, in any patent Licensable by such
Contributor that would be infringed, but for the grant of the
License, by the making, using, selling, offering for sale, having
made, import, or transfer of either its Contributions or its
Contributor Version.
1.12. "Secondary License"
means either the GNU General Public License, Version 2.0, the GNU
Lesser General Public License, Version 2.1, the GNU Affero General
Public License, Version 3.0, or any later versions of those
licenses.
1.13. "Source Code Form"
means the form of the work preferred for making modifications.
1.14. "You" (or "Your")
means an individual or a legal entity exercising rights under this
License. For legal entities, "You" includes any entity that
controls, is controlled by, or is under common control with You. For
purposes of this definition, "control" means (a) the power, direct
or indirect, to cause the direction or management of such entity,
whether by contract or otherwise, or (b) ownership of more than
fifty percent (50%) of the outstanding shares or beneficial
ownership of such entity.
2. License Grants and Conditions
--------------------------------
2.1. Grants
Each Contributor hereby grants You a world-wide, royalty-free,
non-exclusive license:
(a) under intellectual property rights (other than patent or trademark)
Licensable by such Contributor to use, reproduce, make available,
modify, display, perform, distribute, and otherwise exploit its
Contributions, either on an unmodified basis, with Modifications, or
as part of a Larger Work; and
(b) under Patent Claims of such Contributor to make, use, sell, offer
for sale, have made, import, and otherwise transfer either its
Contributions or its Contributor Version.
2.2. Effective Date
The licenses granted in Section 2.1 with respect to any Contribution
become effective for each Contribution on the date the Contributor first
distributes such Contribution.
2.3. Limitations on Grant Scope
The licenses granted in this Section 2 are the only rights granted under
this License. No additional rights or licenses will be implied from the
distribution or licensing of Covered Software under this License.
Notwithstanding Section 2.1(b) above, no patent license is granted by a
Contributor:
(a) for any code that a Contributor has removed from Covered Software;
or
(b) for infringements caused by: (i) Your and any other third party's
modifications of Covered Software, or (ii) the combination of its
Contributions with other software (except as part of its Contributor
Version); or
(c) under Patent Claims infringed by Covered Software in the absence of
its Contributions.
This License does not grant any rights in the trademarks, service marks,
or logos of any Contributor (except as may be necessary to comply with
the notice requirements in Section 3.4).
2.4. Subsequent Licenses
No Contributor makes additional grants as a result of Your choice to
distribute the Covered Software under a subsequent version of this
License (see Section 10.2) or under the terms of a Secondary License (if
permitted under the terms of Section 3.3).
2.5. Representation
Each Contributor represents that the Contributor believes its
Contributions are its original creation(s) or it has sufficient rights
to grant the rights to its Contributions conveyed by this License.
2.6. Fair Use
This License is not intended to limit any rights You have under
applicable copyright doctrines of fair use, fair dealing, or other
equivalents.
2.7. Conditions
Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
in Section 2.1.
3. Responsibilities
-------------------
3.1. Distribution of Source Form
All distribution of Covered Software in Source Code Form, including any
Modifications that You create or to which You contribute, must be under
the terms of this License. You must inform recipients that the Source
Code Form of the Covered Software is governed by the terms of this
License, and how they can obtain a copy of this License. You may not
attempt to alter or restrict the recipients' rights in the Source Code
Form.
3.2. Distribution of Executable Form
If You distribute Covered Software in Executable Form then:
(a) such Covered Software must also be made available in Source Code
Form, as described in Section 3.1, and You must inform recipients of
the Executable Form how they can obtain a copy of such Source Code
Form by reasonable means in a timely manner, at a charge no more
than the cost of distribution to the recipient; and
(b) You may distribute such Executable Form under the terms of this
License, or sublicense it under different terms, provided that the
license for the Executable Form does not attempt to limit or alter
the recipients' rights in the Source Code Form under this License.
3.3. Distribution of a Larger Work
You may create and distribute a Larger Work under terms of Your choice,
provided that You also comply with the requirements of this License for
the Covered Software. If the Larger Work is a combination of Covered
Software with a work governed by one or more Secondary Licenses, and the
Covered Software is not Incompatible With Secondary Licenses, this
License permits You to additionally distribute such Covered Software
under the terms of such Secondary License(s), so that the recipient of
the Larger Work may, at their option, further distribute the Covered
Software under the terms of either this License or such Secondary
License(s).
3.4. Notices
You may not remove or alter the substance of any license notices
(including copyright notices, patent notices, disclaimers of warranty,
or limitations of liability) contained within the Source Code Form of
the Covered Software, except that You may alter any license notices to
the extent required to remedy known factual inaccuracies.
3.5. Application of Additional Terms
You may choose to offer, and to charge a fee for, warranty, support,
indemnity or liability obligations to one or more recipients of Covered
Software. However, You may do so only on Your own behalf, and not on
behalf of any Contributor. You must make it absolutely clear that any
such warranty, support, indemnity, or liability obligation is offered by
You alone, and You hereby agree to indemnify every Contributor for any
liability incurred by such Contributor as a result of warranty, support,
indemnity or liability terms You offer. You may include additional
disclaimers of warranty and limitations of liability specific to any
jurisdiction.
4. Inability to Comply Due to Statute or Regulation
---------------------------------------------------
If it is impossible for You to comply with any of the terms of this
License with respect to some or all of the Covered Software due to
statute, judicial order, or regulation then You must: (a) comply with
the terms of this License to the maximum extent possible; and (b)
describe the limitations and the code they affect. Such description must
be placed in a text file included with all distributions of the Covered
Software under this License. Except to the extent prohibited by statute
or regulation, such description must be sufficiently detailed for a
recipient of ordinary skill to be able to understand it.
5. Termination
--------------
5.1. The rights granted under this License will terminate automatically
if You fail to comply with any of its terms. However, if You become
compliant, then the rights granted under this License from a particular
Contributor are reinstated (a) provisionally, unless and until such
Contributor explicitly and finally terminates Your grants, and (b) on an
ongoing basis, if such Contributor fails to notify You of the
non-compliance by some reasonable means prior to 60 days after You have
come back into compliance. Moreover, Your grants from a particular
Contributor are reinstated on an ongoing basis if such Contributor
notifies You of the non-compliance by some reasonable means, this is the
first time You have received notice of non-compliance with this License
from such Contributor, and You become compliant prior to 30 days after
Your receipt of the notice.
5.2. If You initiate litigation against any entity by asserting a patent
infringement claim (excluding declaratory judgment actions,
counter-claims, and cross-claims) alleging that a Contributor Version
directly or indirectly infringes any patent, then the rights granted to
You by any and all Contributors for the Covered Software under Section
2.1 of this License shall terminate.
5.3. In the event of termination under Sections 5.1 or 5.2 above, all
end user license agreements (excluding distributors and resellers) which
have been validly granted by You or Your distributors under this License
prior to termination shall survive termination.
************************************************************************
* *
* 6. Disclaimer of Warranty *
* ------------------------- *
* *
* Covered Software is provided under this License on an "as is" *
* basis, without warranty of any kind, either expressed, implied, or *
* statutory, including, without limitation, warranties that the *
* Covered Software is free of defects, merchantable, fit for a *
* particular purpose or non-infringing. The entire risk as to the *
* quality and performance of the Covered Software is with You. *
* Should any Covered Software prove defective in any respect, You *
* (not any Contributor) assume the cost of any necessary servicing, *
* repair, or correction. This disclaimer of warranty constitutes an *
* essential part of this License. No use of any Covered Software is *
* authorized under this License except under this disclaimer. *
* *
************************************************************************
************************************************************************
* *
* 7. Limitation of Liability *
* -------------------------- *
* *
* Under no circumstances and under no legal theory, whether tort *
* (including negligence), contract, or otherwise, shall any *
* Contributor, or anyone who distributes Covered Software as *
* permitted above, be liable to You for any direct, indirect, *
* special, incidental, or consequential damages of any character *
* including, without limitation, damages for lost profits, loss of *
* goodwill, work stoppage, computer failure or malfunction, or any *
* and all other commercial damages or losses, even if such party *
* shall have been informed of the possibility of such damages. This *
* limitation of liability shall not apply to liability for death or *
* personal injury resulting from such party's negligence to the *
* extent applicable law prohibits such limitation. Some *
* jurisdictions do not allow the exclusion or limitation of *
* incidental or consequential damages, so this exclusion and *
* limitation may not apply to You. *
* *
************************************************************************
8. Litigation
-------------
Any litigation relating to this License may be brought only in the
courts of a jurisdiction where the defendant maintains its principal
place of business and such litigation shall be governed by laws of that
jurisdiction, without reference to its conflict-of-law provisions.
Nothing in this Section shall prevent a party's ability to bring
cross-claims or counter-claims.
9. Miscellaneous
----------------
This License represents the complete agreement concerning the subject
matter hereof. If any provision of this License is held to be
unenforceable, such provision shall be reformed only to the extent
necessary to make it enforceable. Any law or regulation which provides
that the language of a contract shall be construed against the drafter
shall not be used to construe this License against a Contributor.
10. Versions of the License
---------------------------
10.1. New Versions
Mozilla Foundation is the license steward. Except as provided in Section
10.3, no one other than the license steward has the right to modify or
publish new versions of this License. Each version will be given a
distinguishing version number.
10.2. Effect of New Versions
You may distribute the Covered Software under the terms of the version
of the License under which You originally received the Covered Software,
or under the terms of any subsequent version published by the license
steward.
10.3. Modified Versions
If you create software not governed by this License, and you want to
create a new license for such software, you may create and use a
modified version of this License if you rename the license and remove
any references to the name of the license steward (except to note that
such modified license differs from this License).
10.4. Distributing Source Code Form that is Incompatible With Secondary
Licenses
If You choose to distribute Source Code Form that is Incompatible With
Secondary Licenses under the terms of this version of the License, the
notice described in Exhibit B of this License must be attached.
Exhibit A - Source Code Form License Notice
-------------------------------------------
This Source Code Form is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
If it is not possible or desirable to put the notice in a particular
file, then You may include the notice in a location (such as a LICENSE
file in a relevant directory) where a recipient would be likely to look
for such a notice.
You may add additional accurate notices of copyright ownership.
Exhibit B - "Incompatible With Secondary Licenses" Notice
---------------------------------------------------------
This Source Code Form is "Incompatible With Secondary Licenses", as
defined by the Mozilla Public License, v. 2.0.

34
TODO
View File

@ -1,22 +1,14 @@
# Intermediate code generation
# Compiler
- To access named parameters inside a procedure, IR should be able to reference
them. During the generation the needed information (e.g. offsets or registers)
can be extracted from the symbol table and saved in the operands.
# ELF generation
- Don't ignore relocations where the symbol is not defined in the symbol table.
Report an error about an undefined symbol.
# Register allocation
- 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.
- Catch exceptions thrown by the argument parser and print them normally.
- Structs or records.
- Unions.
- Support immediates greater than 12 bits.
- It seems instructions are correctly encoded only if the compiler is running
on a little endian architecture.
- Assignment to a pointer.
- Static array.
- Syscalls.
- Error message with an empty file wrongly says that a ")" is expected.
- Support any expressions for constants.
- Name analysis should fail if there are undefined symbols.

479
backend/riscv.cpp Normal file
View File

@ -0,0 +1,479 @@
#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;
}
}

230
backend/target.cpp Normal file
View File

@ -0,0 +1,230 @@
#include "elna/backend/target.hpp"
#include "elna/backend/riscv.hpp"
#include <cstring>
namespace elna::riscv
{
elfio_section_writer::iterator::reference elfio_section_writer::iterator::operator*() const noexcept
{
return payload;
}
elfio_section_writer::iterator::pointer elfio_section_writer::iterator::operator->() const noexcept
{
return &payload;
}
elfio_section_writer::iterator& elfio_section_writer::iterator::operator++()
{
this->payload.data += *this->sizes;
this->payload.label = *(++this->labels);
this->payload.size = *(++this->sizes);
return *this;
}
elfio_section_writer::iterator& elfio_section_writer::iterator::operator++(int)
{
auto tmp = *this;
++(*this);
return *this;
}
bool elfio_section_writer::iterator::operator==(const iterator& that) const
{
return this->labels == that.labels;
}
bool elfio_section_writer::iterator::operator!=(const iterator& that) const
{
return !(*this == that);
}
elfio_section_writer::elfio_section_writer(ELFIO::section *section)
: m_section(section)
{
}
void elfio_section_writer::operator()(const std::string& label, const std::byte *data, std::size_t size)
{
labels.push_back(label);
sizes.push_back(size);
m_section->append_data(reinterpret_cast<const char *>(data), size);
}
std::pair<std::string_view, bool> elfio_section_writer::operator()(const std::byte *data, std::size_t size)
{
auto found = std::find_if(begin(), end(),
[data, size](elfio_section_writer::entry entry) {
return size == entry.size && std::memcmp(entry.data, data, size) == 0;
});
if (found == end())
{
(*this)(".CL" + std::to_string(labels.size()), data, size);
return std::pair<std::string_view, bool>(labels.back(), true);
}
return std::pair<std::string_view, bool>(found->label, false);
}
elfio_section_writer::iterator elfio_section_writer::begin() const
{
return elfio_section_writer::iterator(labels.cbegin(), sizes.cbegin(),
reinterpret_cast<const std::byte *>(m_section->get_data()));
}
elfio_section_writer::iterator elfio_section_writer::end() const
{
return elfio_section_writer::iterator(labels.cend(), sizes.cend());
}
ELFIO::section *elfio_section_writer::section() noexcept
{
return m_section;
}
elfio_writer::elfio_writer(ELFIO::section *text, ELFIO::section *read_only,
ELFIO::symbol_section_accessor symbol_accessor, ELFIO::string_section_accessor string_accessor)
: text(text), read_only(elfio_section_writer(read_only)),
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();
}
std::string_view elfio_writer::sink(const std::byte *data, std::size_t size)
{
auto offset = read_only.section()->get_size();
auto [result, inserted] = read_only(data, size);
if (inserted)
{
this->symbol_accessor.add_symbol(this->string_accessor, result.data(), offset,
result.size(), ELFIO::STB_LOCAL, ELFIO::STT_NOTYPE, 0,
read_only.section()->get_index());
}
return result;
}
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 read only data section
ELFIO::section* ro_sec = writer.sections.add(".rodata");
ro_sec->set_type(ELFIO::SHT_PROGBITS);
ro_sec->set_flags(ELFIO::SHF_ALLOC);
ro_sec->set_addr_align(0x4);
// 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, ro_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 Normal file
View File

@ -0,0 +1,78 @@
#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 Normal file
View File

@ -0,0 +1,47 @@
#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);
}

View File

@ -1,87 +0,0 @@
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

1347
include/elfio/elf_types.hpp Normal file

File diff suppressed because it is too large Load Diff

1096
include/elfio/elfio.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,88 @@
/*
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

1346
include/elfio/elfio_dump.hpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,274 @@
/*
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

View File

@ -0,0 +1,153 @@
/*
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

View File

@ -0,0 +1,124 @@
/*
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

View File

@ -0,0 +1,184 @@
/*
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, &section::get_size>;
using const_note_section_accessor =
note_section_accessor_template<const section, &section::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

View File

@ -0,0 +1,460 @@
/*
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

View File

@ -0,0 +1,367 @@
/*
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

View File

@ -0,0 +1,254 @@
/*
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

View File

@ -0,0 +1,97 @@
/*
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

View File

@ -0,0 +1,562 @@
/*
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

View File

@ -0,0 +1,302 @@
/*
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

View File

@ -0,0 +1 @@
#define ELFIO_VERSION "3.12"

View File

@ -0,0 +1,179 @@
/*
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

View File

@ -0,0 +1,163 @@
#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);
}

View File

@ -0,0 +1,107 @@
#pragma once
#include "elna/source/parser.hpp"
#include "elna/source/optimizer.hpp"
#include <filesystem>
#include <elfio/elfio.hpp>
namespace elna::riscv
{
/**
* Writer to a single label.
*/
struct elfio_section_writer
{
struct entry
{
std::string_view label;
const std::byte *data{ nullptr };
std::size_t size{ 0 };
};
/**
* An iterator over label and string pairs.
*/
struct iterator
{
using iterator_category = std::forward_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = entry;
using pointer = const value_type *;
using reference = const value_type&;
reference operator*() const noexcept;
pointer operator->() const noexcept;
iterator& operator++();
iterator& operator++(int);
bool operator==(const iterator& that) const;
bool operator!=(const iterator& that) const;
private:
std::vector<std::string>::const_iterator labels;
std::vector<std::size_t>::const_iterator sizes;
value_type payload;
iterator(std::vector<std::string>::const_iterator labels, std::vector<std::size_t>::const_iterator sizes,
const std::byte *data)
: labels(labels), sizes(sizes)
{
if (data != nullptr)
{
payload = { *this->labels, data, *this->sizes};
}
}
iterator(std::vector<std::string>::const_iterator labels, std::vector<std::size_t>::const_iterator sizes)
: labels(labels), sizes(sizes), payload{}
{
}
friend elfio_section_writer;
};
explicit elfio_section_writer(ELFIO::section *section);
void operator()(const std::string& label, const std::byte *data, std::size_t size);
std::pair<std::string_view, bool> operator()(const std::byte *data, std::size_t size);
iterator begin() const;
iterator end() const;
ELFIO::section *section() noexcept;
private:
std::vector<std::string> labels;
std::vector<std::size_t> sizes;
ELFIO::section *m_section;
};
class elfio_writer final : public source::writer<std::byte>
{
ELFIO::section *text;
elfio_section_writer read_only;
ELFIO::symbol_section_accessor symbol_accessor;
ELFIO::string_section_accessor string_accessor;
public:
elfio_writer(ELFIO::section *text, ELFIO::section *read_only, 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;
std::string_view sink(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);
}

37
include/elna/cli/cl.hpp Normal file
View File

@ -0,0 +1,37 @@
#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);
}

View File

@ -0,0 +1,212 @@
#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);
}

View File

@ -0,0 +1,106 @@
#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;
};
}

View File

@ -0,0 +1,526 @@
#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;
};
}

View File

@ -0,0 +1,194 @@
#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;
/**
* Writes data and returns a label under that the data can be accessed.
*
* \param data Data to be written.
* \param size Data size.
*
* \return Label for the symbol.
*/
virtual std::string_view sink(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;
};
}

View File

@ -0,0 +1,93 @@
#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;
};
}

View File

@ -0,0 +1,161 @@
#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();
};
}

View File

@ -0,0 +1,99 @@
#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 };
}

View File

@ -1,322 +0,0 @@
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

View File

@ -1,80 +0,0 @@
module Language.Elna.Backend.Allocator
( MachineConfiguration(..)
, Store(..)
, allocate
) where
import Data.HashMap.Strict (HashMap)
import Data.Vector (Vector)
import Language.Elna.Backend.Intermediate
( ProcedureQuadruples(..)
, Operand(..)
, Quadruple(..)
, Variable(..)
)
import Language.Elna.Location (Identifier(..))
newtype Store r = Store r
newtype MachineConfiguration r = MachineConfiguration
{ temporaryRegisters :: [r]
}
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 = quadruple <$> quadruples'
, stackSize = 0
}
quadruple :: Quadruple Variable -> Quadruple (Store r)
quadruple = \case
StartQuadruple -> StartQuadruple
StopQuadruple -> StopQuadruple
ParameterQuadruple operand1 ->
ParameterQuadruple (operand operand1)
CallQuadruple name count -> CallQuadruple name count
AddQuadruple operand1 operand2 variable
-> AddQuadruple (operand operand1) (operand operand2)
$ storeVariable variable
SubtractionQuadruple operand1 operand2 variable
-> SubtractionQuadruple (operand operand1) (operand operand2)
$ storeVariable variable
NegationQuadruple operand1 variable
-> NegationQuadruple (operand operand1)
$ storeVariable variable
ProductQuadruple operand1 operand2 variable
-> ProductQuadruple (operand operand1) (operand operand2)
$ storeVariable variable
DivisionQuadruple operand1 operand2 variable
-> DivisionQuadruple (operand operand1) (operand operand2)
$ storeVariable variable
LabelQuadruple label -> LabelQuadruple label
GoToQuadruple label -> GoToQuadruple label
EqualQuadruple operand1 operand2 goToLabel ->
EqualQuadruple (operand operand1) (operand operand2) goToLabel
NonEqualQuadruple operand1 operand2 goToLabel ->
NonEqualQuadruple (operand operand1) (operand operand2) goToLabel
LessQuadruple operand1 operand2 goToLabel ->
LessQuadruple (operand operand1) (operand operand2) goToLabel
GreaterQuadruple operand1 operand2 goToLabel ->
GreaterQuadruple (operand operand1) (operand operand2) goToLabel
LessOrEqualQuadruple operand1 operand2 goToLabel ->
LessOrEqualQuadruple (operand operand1) (operand operand2) goToLabel
GreaterOrEqualQuadruple operand1 operand2 goToLabel ->
GreaterOrEqualQuadruple (operand operand1) (operand operand2) goToLabel
AssignQuadruple operand1 variable ->
AssignQuadruple (operand operand1) $ storeVariable variable
operand :: Operand Variable -> Operand (Store r)
operand (IntOperand x) = IntOperand x
operand (VariableOperand variableOperand) =
VariableOperand $ storeVariable variableOperand
storeVariable (TempVariable index) = Store
$ temporaryRegisters !! fromIntegral index
storeVariable (LocalVariable index) = Store
$ temporaryRegisters !! pred (length temporaryRegisters - fromIntegral index)

View File

@ -1,61 +0,0 @@
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)

View File

@ -1,44 +0,0 @@
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."

View File

@ -1,206 +0,0 @@
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]

View File

@ -1,219 +0,0 @@
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
-}

View File

@ -1,223 +0,0 @@
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

View File

@ -1,101 +0,0 @@
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)

View File

@ -1,201 +0,0 @@
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

View File

@ -1,29 +0,0 @@
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

View File

@ -1,294 +0,0 @@
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

View File

@ -1,58 +0,0 @@
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]

View File

@ -1,488 +0,0 @@
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

View File

@ -1,144 +0,0 @@
-- | 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

View File

@ -1,44 +0,0 @@
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

View File

@ -1,408 +0,0 @@
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 register))
| IntOperand immediateOperand1 <- operand1
, IntOperand immediateOperand2 <- operand2 =
pure $ lui (immediateOperand1 - immediateOperand2) register
| VariableOperand variableOperand1 <- operand1
, VariableOperand variableOperand2 <- operand2 =
let Store operandRegister1 = variableOperand1
Store operandRegister2 = variableOperand2
in pure $ Vector.singleton $ Instruction
$ RiscV.BaseInstruction RiscV.Op
$ RiscV.R register RiscV.SUB operandRegister1 operandRegister2
$ RiscV.Funct7 0b0100000
| IntOperand immediateOperand1 <- operand1
, VariableOperand variableOperand2 <- operand2 =
let statements1 = lui immediateOperand1 register
Store operandRegister2 = variableOperand2
in pure $ Vector.snoc statements1
$ Instruction
$ RiscV.BaseInstruction RiscV.Op
$ RiscV.R register RiscV.SUB register operandRegister2
$ RiscV.Funct7 0b0100000
| VariableOperand variableOperand1 <- operand1
, IntOperand immediateOperand2 <- operand2 =
let statements2 = lui (negate immediateOperand2) register
Store operandRegister1 = variableOperand1
in pure $ Vector.snoc statements2
$ Instruction
$ RiscV.BaseInstruction RiscV.Op
$ RiscV.R register RiscV.ADD register operandRegister1
$ RiscV.Funct7 0b0000000
quadruple (NegationQuadruple operand1 (Store register))
| IntOperand immediateOperand1 <- operand1 =
pure $ lui (negate immediateOperand1) register
| VariableOperand variableOperand1 <- operand1 =
let Store operandRegister1 = variableOperand1
in pure $ Vector.singleton
$ Instruction
$ RiscV.BaseInstruction RiscV.Op
$ RiscV.R register RiscV.SUB RiscV.Zero operandRegister1
$ RiscV.Funct7 0b0100000
quadruple (DivisionQuadruple operand1 operand2 (Store register))
| IntOperand immediateOperand1 <- operand1
, IntOperand immediateOperand2 <- operand2 =
if immediateOperand2 == 0
then pure $ Vector.singleton
$ Instruction (RiscV.CallInstruction "_divide_by_zero_error")
else pure $ lui (quot immediateOperand1 immediateOperand2) register
| VariableOperand variableOperand1 <- operand1
, VariableOperand variableOperand2 <- operand2 = do
let Store operandRegister1 = variableOperand1
Store operandRegister2 = variableOperand2
divisionInstruction = Instruction
$ RiscV.BaseInstruction RiscV.Op
$ RiscV.R register 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 $ Vector.fromList
[ branchInstruction
, Instruction (RiscV.CallInstruction "_divide_by_zero_error")
, JumpLabel branchLabel []
, divisionInstruction
]
| VariableOperand variableOperand1 <- operand1
, IntOperand immediateOperand2 <- operand2 =
let statements2 = lui immediateOperand2 register
Store operandRegister1 = variableOperand1
operationInstruction
| immediateOperand2 == 0 =
RiscV.CallInstruction "_divide_by_zero_error"
| otherwise = RiscV.BaseInstruction RiscV.Op
$ RiscV.R register RiscV.DIV operandRegister1 register
$ RiscV.Funct7 0b0000001
in pure $ Vector.snoc statements2
$ Instruction operationInstruction
| IntOperand immediateOperand1 <- operand1
, VariableOperand variableOperand2 <- operand2 = do
let statements1 = lui immediateOperand1 register
Store operandRegister2 = variableOperand2
divisionInstruction = Instruction
$ RiscV.BaseInstruction RiscV.Op
$ RiscV.R register RiscV.DIV register operandRegister2 (RiscV.Funct7 0b0000001)
branchLabel <- createLabel
let branchInstruction = Instruction
$ RiscV.RelocatableInstruction RiscV.Branch
$ RiscV.RBranch branchLabel RiscV.BNE RiscV.Zero operandRegister2
pure $ mappend statements1 $ Vector.fromList
[ branchInstruction
, Instruction (RiscV.CallInstruction "_divide_by_zero_error")
, JumpLabel branchLabel []
, divisionInstruction
]
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 register))
| IntOperand immediateOperand1 <- operand1 = pure
$ lui immediateOperand1 register
| VariableOperand variableOperand1 <- operand1 =
let Store operandRegister1 = variableOperand1
in pure $ Vector.singleton
$ Instruction
$ RiscV.BaseInstruction RiscV.OpImm
$ RiscV.I register RiscV.ADDI operandRegister1 0
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 register)) _ = (register, Vector.empty)
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 register)
| IntOperand immediateOperand1 <- operand1
, IntOperand immediateOperand2 <- operand2 = pure
$ lui (immediateOperation immediateOperand1 immediateOperand2) register
| VariableOperand variableOperand1 <- operand1
, VariableOperand variableOperand2 <- operand2 =
let Store operandRegister1 = variableOperand1
Store operandRegister2 = variableOperand2
in pure $ Vector.singleton $ Instruction
$ RiscV.BaseInstruction RiscV.Op
$ RiscV.R register funct3 operandRegister1 operandRegister2 funct7
| VariableOperand variableOperand1 <- operand1
, IntOperand immediateOperand2 <- operand2 =
commutativeImmediateRegister variableOperand1 immediateOperand2
| IntOperand immediateOperand1 <- operand1
, VariableOperand variableOperand2 <- operand2 =
commutativeImmediateRegister variableOperand2 immediateOperand1
where
commutativeImmediateRegister variableOperand immediateOperand =
let statements = lui immediateOperand register
Store operandRegister = variableOperand
in pure $ Vector.snoc statements
$ Instruction
$ RiscV.BaseInstruction RiscV.Op
$ RiscV.R register funct3 register operandRegister funct7
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 Store operandRegister1 = variableOperand1
Store operandRegister2 = variableOperand2
Label goToLabel' = goToLabel
pure $ Vector.singleton
$ 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 statements = lui immediateOperand immediateRegister
Store operandRegister = variableOperand
Label goToLabel' = goToLabel
in pure $ Vector.snoc statements
$ 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 Store operandRegister1 = variableOperand1
Store operandRegister2 = variableOperand2
Label goToLabel' = goToLabel
pure $ Vector.singleton
$ Instruction
$ RiscV.RelocatableInstruction RiscV.Branch
$ RiscV.RBranch goToLabel' RiscV.BLT operandRegister1 operandRegister2
| VariableOperand variableOperand1 <- operand1
, IntOperand immediateOperand2 <- operand2 =
let statements2 = lui immediateOperand2 immediateRegister
Store operandRegister1 = variableOperand1
Label goToLabel' = goToLabel
in pure $ Vector.snoc statements2
$ Instruction
$ RiscV.RelocatableInstruction RiscV.Branch
$ RiscV.RBranch goToLabel' RiscV.BLT operandRegister1 immediateRegister
| IntOperand immediateOperand1 <- operand1
, VariableOperand variableOperand2 <- operand2 =
let statements1 = lui immediateOperand1 immediateRegister
Store operandRegister2 = variableOperand2
Label goToLabel' = goToLabel
in pure $ Vector.snoc statements1
$ 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 Store operandRegister1 = variableOperand1
Store operandRegister2 = variableOperand2
Label goToLabel' = goToLabel
pure $ Vector.singleton
$ Instruction
$ RiscV.RelocatableInstruction RiscV.Branch
$ RiscV.RBranch goToLabel' RiscV.BGE operandRegister2 operandRegister1
| VariableOperand variableOperand1 <- operand1
, IntOperand immediateOperand2 <- operand2 =
let statements2 = lui immediateOperand2 immediateRegister
Store operandRegister1 = variableOperand1
Label goToLabel' = goToLabel
in pure $ Vector.snoc statements2
$ Instruction
$ RiscV.RelocatableInstruction RiscV.Branch
$ RiscV.RBranch goToLabel' RiscV.BGE immediateRegister operandRegister1
| IntOperand immediateOperand1 <- operand1
, VariableOperand variableOperand2 <- operand2 =
let statements1 = lui immediateOperand1 immediateRegister
Store operandRegister2 = variableOperand2
Label goToLabel' = goToLabel
in pure $ Vector.snoc statements1
$ Instruction
$ RiscV.RelocatableInstruction RiscV.Branch
$ RiscV.RBranch goToLabel' RiscV.BGE operandRegister2 immediateRegister

View File

@ -1,334 +0,0 @@
-- | 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 Normal file
View File

@ -0,0 +1,462 @@
{
"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 Normal file
View File

@ -0,0 +1,17 @@
{
"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"
}
}

131
parser/lexer.ll Normal file
View File

@ -0,0 +1,131 @@
%{
#define YY_NO_UNISTD_H
#define YY_USER_ACTION this->location.columns(yyleng);
#include <sstream>
#include "parser.hpp"
#undef YY_DECL
#define YY_DECL yy::parser::symbol_type elna::syntax::FooLexer::lex()
#define yyterminate() return yy::parser::make_YYEOF(this->location)
%}
%option c++ noyywrap never-interactive
%option yyclass="elna::syntax::FooLexer"
%%
%{
this->location.step();
%}
\-\-.* {
/* Skip the comment */
}
[\ \t\r] ; /* Skip the whitespaces */
\n+ {
this->location.lines(yyleng);
this->location.step();
}
if {
return yy::parser::make_IF(this->location);
}
then {
return yy::parser::make_THEN(this->location);
}
while {
return yy::parser::make_WHILE(this->location);
}
do {
return yy::parser::make_DO(this->location);
}
proc {
return yy::parser::make_PROCEDURE(this->location);
}
begin {
return yy::parser::make_BEGIN_BLOCK(this->location);
}
end {
return yy::parser::make_END_BLOCK(this->location);
}
const {
return yy::parser::make_CONST(this->location);
}
var {
return yy::parser::make_VAR(this->location);
}
True {
return yy::parser::make_BOOLEAN(true, this->location);
}
False {
return yy::parser::make_BOOLEAN(false, this->location);
}
[A-Za-z_][A-Za-z0-9_]* {
return yy::parser::make_IDENTIFIER(yytext, this->location);
}
[0-9]+ {
return yy::parser::make_NUMBER(strtol(yytext, NULL, 10), this->location);
}
\( {
return yy::parser::make_LEFT_PAREN(this->location);
}
\) {
return yy::parser::make_RIGHT_PAREN(this->location);
}
\>= {
return yy::parser::make_GREATER_EQUAL(this->location);
}
\<= {
return yy::parser::make_LESS_EQUAL(this->location);
}
\> {
return yy::parser::make_GREATER_THAN(this->location);
}
\< {
return yy::parser::make_LESS_THAN(this->location);
}
\/= {
return yy::parser::make_NOT_EQUAL(this->location);
}
= {
return yy::parser::make_EQUALS(this->location);
}
; {
return yy::parser::make_SEMICOLON(this->location);
}
\. {
return yy::parser::make_DOT(this->location);
}
, {
return yy::parser::make_COMMA(this->location);
}
\+ {
return yy::parser::make_PLUS(this->location);
}
\- {
return yy::parser::make_MINUS(this->location);
}
\* {
return yy::parser::make_MULTIPLICATION(this->location);
}
\/ {
return yy::parser::make_DIVISION(this->location);
}
:= {
return yy::parser::make_ASSIGNMENT(this->location);
}
: {
return yy::parser::make_COLON(this->location);
}
\^ {
return yy::parser::make_HAT(this->location);
}
@ {
return yy::parser::make_AT(this->location);
}
. {
std::stringstream ss;
ss << "Illegal character 0x" << std::hex << static_cast<unsigned char>(yytext[0]);
throw yy::parser::syntax_error(this->location, ss.str());
}
%%

46
parser/main.cpp Normal file
View File

@ -0,0 +1,46 @@
#include "parser.hpp"
#include <iostream>
#include <sstream>
int main()
{
std::istringstream inp(
"const world = 5, hello = 7;\n"
"var x: Int, y: ^Int;\n"
"begin\n"
"end.\n"
);
std::unique_ptr<elna::source::program> program;
int result{ 1 };
elna::syntax::FooLexer lexer(inp);
yy::parser parser(lexer, program);
try
{
result = parser();
}
catch (yy::parser::syntax_error& syntax_error)
{
std::cerr << syntax_error.location << ": " << syntax_error.what() << std::endl;
return result;
}
for (auto& definition : program->definitions())
{
auto const_definition = dynamic_cast<elna::source::constant_definition *>(definition.get());
std::cout << "const " << const_definition->identifier() << " = "
<< const_definition->body().number() << std::endl;
}
for (auto& declaration : program->declarations())
{
std::cout << "var " << declaration->identifier() << ": ";
if (declaration->type().is_pointer())
{
std::cout << '^';
}
std::cout << declaration->type().base() << std::endl;
}
return result;
}

406
parser/parser.yy Normal file
View File

@ -0,0 +1,406 @@
%require "3.2"
%language "c++"
%code requires {
#include <cstdint>
#include "elna/source/parser.hpp"
#if ! defined(yyFlexLexerOnce)
#include <FlexLexer.h>
#endif
namespace elna::syntax
{
class FooLexer;
}
}
%code provides {
namespace elna::syntax
{
class FooLexer : public yyFlexLexer
{
public:
yy::location location;
FooLexer(std::istream& arg_yyin)
: yyFlexLexer(&arg_yyin)
{
}
yy::parser::symbol_type lex();
};
}
}
%define api.token.raw
%define api.token.constructor
%define api.value.type variant
%define parse.assert
%parse-param {elna::syntax::FooLexer& lexer}
%parse-param {std::unique_ptr<elna::source::program>& program}
%locations
%header
%code {
#define yylex lexer.lex
}
%start program;
%token <std::string> IDENTIFIER "identifier"
%token <std::int32_t> NUMBER "number"
%token <bool> BOOLEAN
%token IF THEN WHILE DO
%token CONST VAR PROCEDURE
%token BEGIN_BLOCK END_BLOCK
%token TRUE FALSE
%token LEFT_PAREN RIGHT_PAREN SEMICOLON DOT COMMA
%token GREATER_EQUAL LESS_EQUAL LESS_THAN GREATER_THAN NOT_EQUAL EQUALS
%token PLUS MINUS MULTIPLICATION DIVISION
%token ASSIGNMENT COLON HAT AT
%type <std::unique_ptr<elna::source::integer_literal>> integer_literal;
%type <std::unique_ptr<elna::source::boolean_literal>> boolean_literal;
%type <std::unique_ptr<elna::source::variable_expression>> variable_expression;
%type <std::unique_ptr<elna::source::constant_definition>> constant_definition;
%type <std::unique_ptr<elna::source::unary_expression>> reference_expression dereference_expression;
%type <std::vector<std::unique_ptr<elna::source::constant_definition>>> constant_definition_part constant_definitions;
%type <std::unique_ptr<elna::source::type_expression>> type_expression;
%type <std::unique_ptr<elna::source::declaration>> variable_declaration;
%type <std::vector<std::unique_ptr<elna::source::declaration>>> variable_declaration_part variable_declarations;
%type <std::unique_ptr<elna::source::assign_statement>> assign_statement;
%type <std::unique_ptr<elna::source::expression>> expression factor term comparand;
%type <std::unique_ptr<elna::source::statement>> statement;
%type <std::vector<std::unique_ptr<elna::source::expression>>> arguments;
%type <std::unique_ptr<elna::source::call_statement>> call_statement;
%type <std::unique_ptr<elna::source::compound_statement>> compound_statement;
%type <std::vector<std::unique_ptr<elna::source::statement>>> statements;
%type <std::unique_ptr<elna::source::if_statement>> if_statement;
%type <std::unique_ptr<elna::source::while_statement>> while_statement;
%type <std::unique_ptr<elna::source::procedure_definition>> procedure_definition;
%type <std::vector<std::unique_ptr<elna::source::procedure_definition>>> procedure_definition_part procedure_definitions;
%%
program: constant_definition_part variable_declaration_part procedure_definition_part statement DOT
{
elna::source::position position;
std::vector<std::unique_ptr<elna::source::definition>> definitions($1.size());
std::vector<std::unique_ptr<elna::source::declaration>> declarations($2.size());
std::vector<std::unique_ptr<elna::source::definition>>::iterator definition = definitions.begin();
std::vector<std::unique_ptr<elna::source::declaration>>::iterator declaration = declarations.begin();
for (auto& constant : $1)
{
*definition++ = std::move(constant);
}
for (auto& variable : $2)
{
*declaration++ = std::move(variable);
}
program = std::make_unique<elna::source::program>(position,
std::move(definitions), std::move(declarations), std::move($4));
}
procedure_definition:
PROCEDURE IDENTIFIER SEMICOLON constant_definition_part variable_declaration_part statement SEMICOLON
{
elna::source::position position;
std::vector<std::unique_ptr<elna::source::definition>> definitions($4.size());
std::vector<std::unique_ptr<elna::source::declaration>> declarations($5.size());
std::vector<std::unique_ptr<elna::source::definition>>::iterator definition = definitions.begin();
std::vector<std::unique_ptr<elna::source::declaration>>::iterator declaration = declarations.begin();
for (auto& constant : $4)
{
*definition++ = std::move(constant);
}
for (auto& variable : $5)
{
*declaration++ = std::move(variable);
}
auto block = std::make_unique<elna::source::block>(position,
std::move(definitions), std::move(declarations), std::move($6));
$$ = std::make_unique<elna::source::procedure_definition>(position,
$2, std::move(block));
}
procedure_definition_part:
/* no procedure definitions. */ {}
| procedure_definitions { std::swap($1, $$); }
procedure_definitions:
procedure_definition procedure_definitions
{
std::swap($$, $2);
$$.emplace($$.cbegin(), std::move($1));
}
| procedure_definition { $$.emplace_back(std::move($1)); }
integer_literal: NUMBER
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::integer_literal>(position, $1);
}
boolean_literal: BOOLEAN
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::boolean_literal>(position, $1);
}
variable_expression: IDENTIFIER
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::variable_expression>(position, $1);
}
reference_expression: AT variable_expression
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::unary_expression>(position, std::move($2), '@');
}
dereference_expression: factor HAT
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::unary_expression>(position, std::move($1), '^');
}
expression:
comparand EQUALS comparand
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), '=');
}
| comparand NOT_EQUAL comparand
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), 'n');
}
| comparand GREATER_THAN comparand
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), '>');
}
| comparand LESS_THAN comparand
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), '<');
}
| comparand GREATER_EQUAL comparand
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), 'g');
}
| comparand LESS_EQUAL comparand
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), 'l');
}
comparand:
term PLUS term
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), '+');
}
| term MINUS term
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), '-');
}
| term { $$ = std::move($1); }
factor:
integer_literal { $$ = std::move($1); }
| boolean_literal { $$ = std::move($1); }
| variable_expression { $$ = std::move($1); }
| reference_expression { $$ = std::move($1); }
| dereference_expression { $$ = std::move($1); }
| LEFT_PAREN expression RIGHT_PAREN { $$ = std::move($2); }
term:
factor MULTIPLICATION factor
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), '*');
}
| factor DIVISION factor
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::binary_expression>(position,
std::move($1), std::move($3), '/');
}
| factor { $$ = std::move($1); }
type_expression:
HAT IDENTIFIER
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::type_expression>(position, $2, true);
}
| IDENTIFIER
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::type_expression>(position, $1, false);
}
assign_statement: IDENTIFIER ASSIGNMENT expression
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@2.begin.column)
};
$$ = std::make_unique<elna::source::assign_statement>(position, $1, std::move($3));
}
call_statement: IDENTIFIER LEFT_PAREN arguments RIGHT_PAREN
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::call_statement>(position, $1);
std::swap($3, $$->arguments());
}
compound_statement: BEGIN_BLOCK statements END_BLOCK
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::compound_statement>(position);
std::swap($2, $$->statements());
}
if_statement: IF expression THEN statement
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::if_statement>(position,
std::move($2), std::move($4));
}
while_statement: WHILE expression DO statement
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::while_statement>(position,
std::move($2), std::move($4));
}
statement:
assign_statement { $$ = std::move($1); }
| call_statement { $$ = std::move($1); }
| compound_statement { $$ = std::move($1); }
| if_statement { $$ = std::move($1); }
| while_statement { $$ = std::move($1); }
variable_declaration: IDENTIFIER COLON type_expression
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::declaration>(position, $1, std::move($3));
}
variable_declarations:
variable_declaration COMMA variable_declarations
{
std::swap($$, $3);
$$.emplace($$.cbegin(), std::move($1));
}
| variable_declaration { $$.emplace_back(std::move($1)); }
constant_definition: IDENTIFIER EQUALS integer_literal
{
elna::source::position position{
static_cast<std::size_t>(@1.begin.line),
static_cast<std::size_t>(@1.begin.column)
};
$$ = std::make_unique<elna::source::constant_definition>(position,
$1, std::move($3));
};
constant_definitions:
constant_definition COMMA constant_definitions
{
std::swap($$, $3);
$$.emplace($$.cbegin(), std::move($1));
}
| constant_definition { $$.emplace_back(std::move($1)); }
constant_definition_part:
/* no constant definitions */ {}
| CONST constant_definitions SEMICOLON { std::swap($$, $2); };
variable_declaration_part:
/* no constant declarations */ {}
| VAR variable_declarations SEMICOLON { std::swap($$, $2); };
arguments:
/* no arguments */ {}
| expression COMMA arguments
{
std::swap($$, $3);
$$.emplace($$.cbegin(), std::move($1));
}
| expression { $$.emplace_back(std::move($1)); }
statements:
/* no statements */ {}
| statement SEMICOLON statements
{
std::swap($$, $3);
$$.emplace($$.cbegin(), std::move($1));
}
| statement { $$.emplace_back(std::move($1)); }
%%
void yy::parser::error(const location_type& loc, const std::string &message)
{
throw yy::parser::syntax_error(loc, message);
}

View File

@ -1,331 +0,0 @@
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

View File

@ -1 +0,0 @@
TMP = Pathname.new('./build')

View File

@ -1,96 +0,0 @@
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 Normal file
View File

@ -0,0 +1,329 @@
#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;
}
}

267
source/optimizer.cpp Normal file
View File

@ -0,0 +1,267 @@
#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 Normal file

File diff suppressed because it is too large Load Diff

46
source/result.cpp Normal file
View File

@ -0,0 +1,46 @@
#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 Normal file
View File

@ -0,0 +1,204 @@
%{
#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);
}
}

325
source/semantic.cpp Normal file
View File

@ -0,0 +1,325 @@
#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;
}
}

129
source/symbol_table.cpp Normal file
View File

@ -0,0 +1,129 @@
#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;
}
}

96
source/types.cpp Normal file
View File

@ -0,0 +1,96 @@
#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);
}
}

View File

@ -1,53 +0,0 @@
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)

View File

@ -1,78 +0,0 @@
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"

View File

@ -1,142 +0,0 @@
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

View File

@ -1 +0,0 @@
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}

3
tests/const_list.eln Normal file
View File

@ -0,0 +1,3 @@
const a = 1, b = 2;
writei(a + b)
.

View File

@ -0,0 +1,5 @@
var x: Int;
begin
x := 5;
writei(x)
end.

1
tests/divide.eln Normal file
View File

@ -0,0 +1 @@
writei(6 / 3)

View File

@ -1 +0,0 @@
38

View File

@ -1 +0,0 @@
58

View File

@ -1 +0,0 @@
58

View File

@ -0,0 +1,2 @@
8
9

View File

@ -0,0 +1 @@
8

View File

@ -0,0 +1 @@
8

View File

@ -0,0 +1 @@
H

View File

@ -1 +0,0 @@
-8

View File

@ -0,0 +1 @@
4

View File

@ -1 +0,0 @@
0

View File

@ -1 +0,0 @@
13

View File

@ -1,2 +0,0 @@
13
2097150

View File

@ -0,0 +1,2 @@
t
f

View File

@ -1 +0,0 @@
x

View File

@ -0,0 +1,12 @@
t
f
t
f
f
t
t
f
t
f
f
t

View File

@ -0,0 +1,5 @@
1
2
3
4
5

View File

@ -1 +0,0 @@
2097150

Some files were not shown because too many files have changed in this diff Show More