Compare commits
No commits in common. "haskell" and "bison-gen" have entirely different histories.
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,4 +3,3 @@
|
||||
CMakeFiles/
|
||||
CMakeCache.txt
|
||||
node_modules/
|
||||
/dist-newstyle/
|
||||
|
@ -1 +0,0 @@
|
||||
3.3.6
|
46
CMakeLists.txt
Normal file
46
CMakeLists.txt
Normal 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
373
LICENSE
@ -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.
|
22
TODO
22
TODO
@ -1,10 +1,14 @@
|
||||
# ELF generation
|
||||
# Compiler
|
||||
|
||||
- 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.
|
||||
- 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
479
backend/riscv.cpp
Normal 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
230
backend/target.cpp
Normal 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
78
cli/cl.cpp
Normal 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
47
cli/main.cpp
Normal 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);
|
||||
}
|
87
elna.cabal
87
elna.cabal
@ -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
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
1096
include/elfio/elfio.hpp
Normal file
File diff suppressed because it is too large
Load Diff
88
include/elfio/elfio_array.hpp
Normal file
88
include/elfio/elfio_array.hpp
Normal 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
1346
include/elfio/elfio_dump.hpp
Normal file
File diff suppressed because it is too large
Load Diff
274
include/elfio/elfio_dynamic.hpp
Normal file
274
include/elfio/elfio_dynamic.hpp
Normal 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
|
153
include/elfio/elfio_header.hpp
Normal file
153
include/elfio/elfio_header.hpp
Normal 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
|
124
include/elfio/elfio_modinfo.hpp
Normal file
124
include/elfio/elfio_modinfo.hpp
Normal 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
|
184
include/elfio/elfio_note.hpp
Normal file
184
include/elfio/elfio_note.hpp
Normal 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, §ion::get_size>;
|
||||
using const_note_section_accessor =
|
||||
note_section_accessor_template<const section, §ion::get_size>;
|
||||
using note_segment_accessor =
|
||||
note_section_accessor_template<segment, &segment::get_file_size>;
|
||||
using const_note_segment_accessor =
|
||||
note_section_accessor_template<const segment, &segment::get_file_size>;
|
||||
|
||||
} // namespace ELFIO
|
||||
|
||||
#endif // ELFIO_NOTE_HPP
|
460
include/elfio/elfio_relocation.hpp
Normal file
460
include/elfio/elfio_relocation.hpp
Normal 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
|
367
include/elfio/elfio_section.hpp
Normal file
367
include/elfio/elfio_section.hpp
Normal 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
|
254
include/elfio/elfio_segment.hpp
Normal file
254
include/elfio/elfio_segment.hpp
Normal 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
|
97
include/elfio/elfio_strings.hpp
Normal file
97
include/elfio/elfio_strings.hpp
Normal 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
|
562
include/elfio/elfio_symbols.hpp
Normal file
562
include/elfio/elfio_symbols.hpp
Normal 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
|
302
include/elfio/elfio_utils.hpp
Normal file
302
include/elfio/elfio_utils.hpp
Normal 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
|
1
include/elfio/elfio_version.hpp
Normal file
1
include/elfio/elfio_version.hpp
Normal file
@ -0,0 +1 @@
|
||||
#define ELFIO_VERSION "3.12"
|
179
include/elfio/elfio_versym.hpp
Normal file
179
include/elfio/elfio_versym.hpp
Normal 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
|
163
include/elna/backend/riscv.hpp
Normal file
163
include/elna/backend/riscv.hpp
Normal 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);
|
||||
}
|
107
include/elna/backend/target.hpp
Normal file
107
include/elna/backend/target.hpp
Normal 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
37
include/elna/cli/cl.hpp
Normal 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);
|
||||
}
|
212
include/elna/source/lexer.hpp
Normal file
212
include/elna/source/lexer.hpp
Normal 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);
|
||||
}
|
106
include/elna/source/optimizer.hpp
Normal file
106
include/elna/source/optimizer.hpp
Normal 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;
|
||||
};
|
||||
}
|
526
include/elna/source/parser.hpp
Normal file
526
include/elna/source/parser.hpp
Normal 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;
|
||||
};
|
||||
}
|
194
include/elna/source/result.hpp
Normal file
194
include/elna/source/result.hpp
Normal 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;
|
||||
};
|
||||
}
|
93
include/elna/source/semantic.hpp
Normal file
93
include/elna/source/semantic.hpp
Normal 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;
|
||||
};
|
||||
}
|
161
include/elna/source/symbol_table.hpp
Normal file
161
include/elna/source/symbol_table.hpp
Normal 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();
|
||||
};
|
||||
}
|
99
include/elna/source/types.hpp
Normal file
99
include/elna/source/types.hpp
Normal 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 };
|
||||
}
|
@ -1,326 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
@ -1,182 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.0/. -}
|
||||
|
||||
module Language.Elna.Backend.Allocator
|
||||
( MachineConfiguration(..)
|
||||
, Store(..)
|
||||
, allocate
|
||||
) where
|
||||
|
||||
import Data.HashMap.Strict (HashMap)
|
||||
import qualified Data.HashMap.Strict as HashMap
|
||||
import Data.Int (Int32)
|
||||
import Data.Vector (Vector)
|
||||
import Language.Elna.Backend.Intermediate
|
||||
( ProcedureQuadruples(..)
|
||||
, Operand(..)
|
||||
, Quadruple(..)
|
||||
, Variable(..)
|
||||
)
|
||||
import Language.Elna.Location (Identifier(..))
|
||||
import Control.Monad.Trans.Reader (ReaderT, asks, runReaderT)
|
||||
import Control.Monad.Trans.State (State, runState)
|
||||
import GHC.Records (HasField(..))
|
||||
import Control.Monad.Trans.Class (MonadTrans(..))
|
||||
import Control.Monad.Trans.Except (ExceptT(..), runExceptT, throwE)
|
||||
import Data.List ((!?))
|
||||
import Language.Elna.Frontend.SymbolTable (Info(..), SymbolTable)
|
||||
import qualified Language.Elna.Frontend.SymbolTable as SymbolTable
|
||||
|
||||
data Store r
|
||||
= RegisterStore r
|
||||
| StackStore Int32 r
|
||||
|
||||
data AllocationError
|
||||
= OutOfRegistersError
|
||||
| UnexpectedProcedureInfoError Info
|
||||
| UndefinedSymbolError Identifier
|
||||
deriving Eq
|
||||
|
||||
instance Show AllocationError
|
||||
where
|
||||
show OutOfRegistersError = "Ran out of registers during register allocation"
|
||||
show (UnexpectedProcedureInfoError info) =
|
||||
"Expected to encounter a procedure, got: " <> show info
|
||||
show (UndefinedSymbolError identifier) =
|
||||
concat ["Symbol \"", show identifier, "\" is not defined"]
|
||||
|
||||
newtype MachineConfiguration r = MachineConfiguration
|
||||
{ temporaryRegisters :: [r]
|
||||
}
|
||||
|
||||
newtype MachineState = MachineState
|
||||
{ symbolTable :: SymbolTable
|
||||
} deriving (Eq, Show)
|
||||
|
||||
newtype Allocator r a = Allocator
|
||||
{ runAllocator :: ExceptT AllocationError (ReaderT (MachineConfiguration r) (State MachineState)) a
|
||||
}
|
||||
|
||||
instance forall r. Functor (Allocator r)
|
||||
where
|
||||
fmap f = Allocator . fmap f . runAllocator
|
||||
|
||||
instance forall r. Applicative (Allocator r)
|
||||
where
|
||||
pure = Allocator . pure
|
||||
(Allocator x) <*> (Allocator y) = Allocator $ x <*> y
|
||||
|
||||
instance forall r. Monad (Allocator r)
|
||||
where
|
||||
(Allocator allocator) >>= f = Allocator $ allocator >>= (runAllocator . f)
|
||||
|
||||
allocate
|
||||
:: forall r
|
||||
. MachineConfiguration r
|
||||
-> SymbolTable
|
||||
-> HashMap Identifier (Vector (Quadruple Variable))
|
||||
-> Either AllocationError (HashMap Identifier (ProcedureQuadruples (Store r)))
|
||||
allocate machineConfiguration globalTable = HashMap.traverseWithKey function
|
||||
where
|
||||
run localTable = flip runState (MachineState{ symbolTable = localTable })
|
||||
. flip runReaderT machineConfiguration
|
||||
. runExceptT
|
||||
. runAllocator
|
||||
. mapM quadruple
|
||||
function :: Identifier -> Vector (Quadruple Variable) -> Either AllocationError (ProcedureQuadruples (Store r))
|
||||
function procedureName quadruples' =
|
||||
case SymbolTable.lookup procedureName globalTable of
|
||||
Just (ProcedureInfo localTable _) ->
|
||||
let (result, lastState) = run localTable quadruples'
|
||||
in makeResult lastState <$> result
|
||||
Just anotherInfo -> Left $ UnexpectedProcedureInfoError anotherInfo
|
||||
Nothing -> Left $ UndefinedSymbolError procedureName
|
||||
|
||||
makeResult MachineState{ symbolTable } result = ProcedureQuadruples
|
||||
{ quadruples = result
|
||||
, stackSize = fromIntegral $ SymbolTable.size symbolTable * 4
|
||||
}
|
||||
|
||||
quadruple :: Quadruple Variable -> Allocator r (Quadruple (Store r))
|
||||
quadruple = \case
|
||||
StartQuadruple -> pure StartQuadruple
|
||||
StopQuadruple -> pure StopQuadruple
|
||||
ParameterQuadruple operand1 -> ParameterQuadruple
|
||||
<$> operand operand1
|
||||
CallQuadruple name count -> pure $ 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 -> pure $ LabelQuadruple label
|
||||
GoToQuadruple label -> pure $ GoToQuadruple label
|
||||
EqualQuadruple operand1 operand2 goToLabel -> EqualQuadruple
|
||||
<$> operand operand1
|
||||
<*> operand operand2
|
||||
<*> pure goToLabel
|
||||
NonEqualQuadruple operand1 operand2 goToLabel -> NonEqualQuadruple
|
||||
<$> operand operand1
|
||||
<*> operand operand2
|
||||
<*> pure goToLabel
|
||||
LessQuadruple operand1 operand2 goToLabel -> LessQuadruple
|
||||
<$> operand operand1
|
||||
<*> operand operand2
|
||||
<*> pure goToLabel
|
||||
GreaterQuadruple operand1 operand2 goToLabel -> do
|
||||
operand1' <- operand operand1
|
||||
operand2' <- operand operand2
|
||||
pure $ GreaterQuadruple operand1' operand2' goToLabel
|
||||
LessOrEqualQuadruple operand1 operand2 goToLabel -> LessOrEqualQuadruple
|
||||
<$> operand operand1
|
||||
<*> operand operand2
|
||||
<*> pure goToLabel
|
||||
GreaterOrEqualQuadruple operand1 operand2 goToLabel -> GreaterOrEqualQuadruple
|
||||
<$> operand operand1
|
||||
<*> operand operand2
|
||||
<*> pure goToLabel
|
||||
AssignQuadruple operand1 variable -> AssignQuadruple
|
||||
<$> operand operand1
|
||||
<*> storeVariable variable
|
||||
ArrayAssignQuadruple operand1 operand2 variable -> ArrayAssignQuadruple
|
||||
<$> operand operand1
|
||||
<*> operand operand2
|
||||
<*> storeVariable variable
|
||||
ArrayQuadruple variable1 operand1 variable2 -> ArrayQuadruple
|
||||
<$> storeVariable variable1
|
||||
<*> operand operand1
|
||||
<*> storeVariable variable2
|
||||
|
||||
operand :: Operand Variable -> Allocator r (Operand (Store r))
|
||||
operand (IntOperand literalOperand) = pure $ IntOperand literalOperand
|
||||
operand (VariableOperand variableOperand) =
|
||||
VariableOperand <$> storeVariable variableOperand
|
||||
|
||||
storeVariable :: Variable -> Allocator r (Store r)
|
||||
storeVariable (TempVariable index) = do
|
||||
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
|
||||
maybe (Allocator $ throwE OutOfRegistersError) (pure . RegisterStore)
|
||||
$ temporaryRegisters' !? fromIntegral index
|
||||
storeVariable (LocalVariable index) = do
|
||||
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
|
||||
maybe (Allocator $ throwE OutOfRegistersError) (pure . StackStore (fromIntegral (succ index) * (-4)))
|
||||
$ temporaryRegisters' !? pred (length temporaryRegisters' - fromIntegral index)
|
||||
storeVariable (ParameterVariable index) = do
|
||||
temporaryRegisters' <- Allocator $ lift $ asks $ getField @"temporaryRegisters"
|
||||
maybe (Allocator $ throwE OutOfRegistersError) (pure . StackStore (fromIntegral index * 4))
|
||||
$ temporaryRegisters' !? fromIntegral index
|
@ -1,66 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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 | ParameterVariable Word32
|
||||
deriving Eq
|
||||
|
||||
instance Show Variable
|
||||
where
|
||||
show (LocalVariable variable) = '@' : show variable
|
||||
show (TempVariable variable) = '$' : show variable
|
||||
show (ParameterVariable 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 v (Operand v) v
|
||||
| ArrayAssignQuadruple (Operand v) (Operand v) v
|
||||
| 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)
|
@ -1,48 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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."
|
@ -1,210 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
||||
|
||||
data 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]
|
@ -1,211 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
@ -1,227 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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 = 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
|
@ -1,109 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.0/. -}
|
||||
|
||||
module Language.Elna.Frontend.SymbolTable
|
||||
( SymbolTable
|
||||
, Info(..)
|
||||
, ParameterInfo(..)
|
||||
, builtInSymbolTable
|
||||
, empty
|
||||
, enter
|
||||
, fromList
|
||||
, lookup
|
||||
, member
|
||||
, scope
|
||||
, size
|
||||
, 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
|
||||
|
||||
size :: SymbolTable -> Int
|
||||
size (SymbolTable _ map') = HashMap.size map'
|
||||
|
||||
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)
|
@ -1,208 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
||||
{- | 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
|
||||
show (ArrayIndexError got) =
|
||||
"Expected an array index expression to be an integer, got: " <> show got
|
||||
show (ArrayAccessError got) =
|
||||
"Expected to encounter an array, 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 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
|
@ -1,33 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
@ -1,312 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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(..))
|
||||
import Debug.Trace (traceShow)
|
||||
|
||||
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 parameters variableDeclarations statements) =
|
||||
let Just (ProcedureInfo localTable _) = SymbolTable.lookup procedureName globalTable
|
||||
in Glue (modify' resetTemporaryCounter)
|
||||
>> traverseWithIndex registerVariable variableDeclarations
|
||||
>> traverseWithIndex registerParameter (reverse parameters)
|
||||
>> nameQuadruplesTuple <$> traverse (statement localTable) statements
|
||||
where
|
||||
traverseWithIndex f = traverse_ (uncurry f) . zip [0..]
|
||||
registerParameter index (AST.Parameter identifier _ _) =
|
||||
Glue $ modify' $ modifier identifier $ ParameterVariable index
|
||||
registerVariable index (AST.VariableDeclaration identifier _) =
|
||||
Glue $ modify' $ modifier identifier $ LocalVariable index
|
||||
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
|
||||
)
|
||||
resetTemporaryCounter paste = paste
|
||||
{ temporaryCounter = 0
|
||||
, localMap = mempty
|
||||
}
|
||||
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
|
||||
$ length arguments
|
||||
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
|
||||
(identifier, Just accumulatedIndex, accumulatedStatements)
|
||||
-> Vector.snoc accumulatedStatements
|
||||
. ArrayAssignQuadruple rhsOperand accumulatedIndex
|
||||
<$> lookupLocal 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 accessKind accumulatedIndex arrayType statements
|
||||
| (AST.ArrayAccess access1 index1) <- accessKind
|
||||
, (ArrayType arraySize baseType) <- arrayType = do
|
||||
(indexPlace, statements') <- expression localTable index1
|
||||
case accumulatedIndex of
|
||||
Just baseIndex -> do
|
||||
resultVariable <- createTemporary
|
||||
let resultOperand = VariableOperand resultVariable
|
||||
indexCalculation = Vector.fromList
|
||||
[ ProductQuadruple (IntOperand $ fromIntegral arraySize) baseIndex resultVariable
|
||||
, AddQuadruple indexPlace resultOperand resultVariable
|
||||
]
|
||||
in variableAccess localTable access1 (Just resultOperand) baseType
|
||||
$ statements <> indexCalculation <> statements'
|
||||
Nothing ->
|
||||
variableAccess localTable access1 (Just indexPlace) baseType 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'
|
||||
| Just (VariableInfo _ type') <- SymbolTable.lookup identifier symbolTable = type'
|
||||
| otherwise = traceShow identifier $ 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
|
||||
(identifier, Just operand, statements) -> do
|
||||
arrayAddress <- createTemporary
|
||||
localVariable <- lookupLocal identifier
|
||||
let arrayStatement = ArrayQuadruple localVariable 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
|
@ -1,62 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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]
|
@ -1,492 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
@ -1,148 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
@ -1,48 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
@ -1,519 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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{ stackSize, quadruples = value }) =
|
||||
let code = Vector.cons (JumpLabel key [GlobalDirective, FunctionDirective])
|
||||
. fold <$> mapM (quadruple stackSize) value
|
||||
in (accumulator <>) <$> code
|
||||
|
||||
quadruple :: Word32 -> RiscVQuadruple -> RiscVGenerator (Vector Statement)
|
||||
quadruple stackSize StartQuadruple =
|
||||
let totalStackSize = stackSize + 8
|
||||
in pure $ Vector.fromList
|
||||
[ Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.SP RiscV.ADDI RiscV.SP (negate totalStackSize))
|
||||
, 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 totalStackSize)
|
||||
]
|
||||
quadruple stackSize StopQuadruple =
|
||||
let totalStackSize = stackSize + 8
|
||||
in 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 totalStackSize)
|
||||
, Instruction (RiscV.BaseInstruction RiscV.Jalr $ RiscV.I RiscV.RA RiscV.JALR RiscV.Zero 0)
|
||||
]
|
||||
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) =
|
||||
let restoreStackSize = numberOfArguments * 4
|
||||
in pure $ Vector.fromList
|
||||
[ Instruction (RiscV.CallInstruction callName)
|
||||
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I RiscV.SP RiscV.ADDI RiscV.SP restoreStackSize)
|
||||
]
|
||||
quadruple _ (AddQuadruple operand1 operand2 store) =
|
||||
commutativeBinary (+) RiscV.ADD (RiscV.Funct7 0b0000000) (operand1, operand2) store
|
||||
quadruple _ (ProductQuadruple operand1 operand2 store) =
|
||||
commutativeBinary (*) RiscV.MUL (RiscV.Funct7 0b0000001) (operand1, operand2) store
|
||||
quadruple _ (SubtractionQuadruple operand1 operand2 store)
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
in pure $ lui (immediateOperand1 - immediateOperand2) storeRegister <> storeStatements
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
instruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister RiscV.SUB operandRegister1 operandRegister2
|
||||
$ RiscV.Funct7 0b0100000
|
||||
in pure $ statements1 <> statements2 <> Vector.cons instruction storeStatements
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
statements1 = lui immediateOperand1 storeRegister
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
instruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister RiscV.SUB storeRegister operandRegister2
|
||||
$ RiscV.Funct7 0b0100000
|
||||
in pure $ statements1 <> statements2 <> Vector.cons instruction storeStatements
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
statements2 = lui (negate immediateOperand2) storeRegister
|
||||
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
instruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister RiscV.ADD storeRegister operandRegister1
|
||||
$ RiscV.Funct7 0b0000000
|
||||
in pure $ statements1 <> statements2 <> Vector.cons instruction storeStatements
|
||||
quadruple _ (NegationQuadruple operand1 store)
|
||||
| IntOperand immediateOperand1 <- operand1 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
in pure $ lui (negate immediateOperand1) storeRegister <> storeStatements
|
||||
| VariableOperand variableOperand1 <- operand1 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
instruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister RiscV.SUB RiscV.Zero operandRegister1
|
||||
$ RiscV.Funct7 0b0100000
|
||||
in pure $ statements1 <> Vector.cons instruction storeStatements
|
||||
quadruple _ (DivisionQuadruple operand1 operand2 store)
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
if immediateOperand2 == 0
|
||||
then pure $ Vector.singleton
|
||||
$ Instruction (RiscV.CallInstruction "_divide_by_zero_error")
|
||||
else
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
in pure $ lui (quot immediateOperand1 immediateOperand2) storeRegister <> storeStatements
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 = do
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
divisionInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister RiscV.DIV operandRegister1 operandRegister2 (RiscV.Funct7 0b0000001)
|
||||
branchLabel <- createLabel
|
||||
let branchInstruction = Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch branchLabel RiscV.BNE RiscV.Zero operandRegister2
|
||||
pure $ statements1 <> statements2 <> Vector.fromList
|
||||
[ branchInstruction
|
||||
, Instruction (RiscV.CallInstruction "_divide_by_zero_error")
|
||||
, JumpLabel branchLabel []
|
||||
, divisionInstruction
|
||||
] <> storeStatements
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
statements2 = lui immediateOperand2 storeRegister
|
||||
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
operationInstruction
|
||||
| immediateOperand2 == 0 =
|
||||
RiscV.CallInstruction "_divide_by_zero_error"
|
||||
| otherwise = RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister RiscV.DIV operandRegister1 storeRegister
|
||||
$ RiscV.Funct7 0b0000001
|
||||
in pure $ statements1 <> statements2
|
||||
<> Vector.cons (Instruction operationInstruction) storeStatements
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 = do
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
statements1 = lui immediateOperand1 storeRegister
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
divisionInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister RiscV.DIV storeRegister operandRegister2 (RiscV.Funct7 0b0000001)
|
||||
branchLabel <- createLabel
|
||||
let branchInstruction = Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch branchLabel RiscV.BNE RiscV.Zero operandRegister2
|
||||
pure $ statements1 <> statements2 <> Vector.fromList
|
||||
[ branchInstruction
|
||||
, Instruction (RiscV.CallInstruction "_divide_by_zero_error")
|
||||
, JumpLabel branchLabel []
|
||||
, divisionInstruction
|
||||
] <> storeStatements
|
||||
quadruple _ (LabelQuadruple (Label label)) = pure $ Vector.singleton $ JumpLabel label mempty
|
||||
quadruple _ (GoToQuadruple label) = pure $ Vector.singleton $ unconditionalJal label
|
||||
quadruple _ (EqualQuadruple operand1 operand2 goToLabel) =
|
||||
commutativeComparison (==) RiscV.BEQ (operand1, operand2) goToLabel
|
||||
quadruple _ (NonEqualQuadruple operand1 operand2 goToLabel) =
|
||||
commutativeComparison (/=) RiscV.BNE (operand1, operand2) goToLabel
|
||||
quadruple _ (LessQuadruple operand1 operand2 goToLabel) =
|
||||
lessThan (operand1, operand2) goToLabel
|
||||
quadruple _ (GreaterQuadruple operand1 operand2 goToLabel) =
|
||||
lessThan (operand2, operand1) goToLabel
|
||||
quadruple _ (LessOrEqualQuadruple operand1 operand2 goToLabel) =
|
||||
lessOrEqualThan (operand1, operand2) goToLabel
|
||||
quadruple _ (GreaterOrEqualQuadruple operand1 operand2 goToLabel) =
|
||||
lessOrEqualThan (operand2, operand1) goToLabel
|
||||
quadruple _ (AssignQuadruple operand1 store)
|
||||
| IntOperand immediateOperand1 <- operand1 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
in pure $ lui immediateOperand1 storeRegister <> storeStatements
|
||||
| VariableOperand variableOperand1 <- operand1 =
|
||||
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
(storeRegister, storeStatements) = storeToStore store
|
||||
instruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.OpImm
|
||||
$ RiscV.I storeRegister RiscV.ADDI operandRegister1 0
|
||||
in pure $ statements1 <> Vector.cons instruction storeStatements
|
||||
quadruple _ (ArrayAssignQuadruple assigneeOperand indexOperand store)
|
||||
| IntOperand immediateAssigneeOperand <- assigneeOperand =
|
||||
let (storeRegister, storeStatements) = storeWithOffset store indexOperand
|
||||
in pure $ lui immediateAssigneeOperand storeRegister <> storeStatements
|
||||
| VariableOperand variableAssigneeOperand <- assigneeOperand =
|
||||
let (assigneeOperandRegister, assigneeStatements) = loadFromStore variableAssigneeOperand
|
||||
(storeRegister, storeStatements) = storeWithOffset store indexOperand
|
||||
instruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.OpImm
|
||||
$ RiscV.I storeRegister RiscV.ADDI assigneeOperandRegister 0
|
||||
in pure $ assigneeStatements <> Vector.cons instruction storeStatements
|
||||
where
|
||||
storeWithOffset :: RiscVStore -> Operand RiscVStore -> (RiscV.XRegister, Vector Statement)
|
||||
storeWithOffset (RegisterStore register) _ = (register, mempty)
|
||||
storeWithOffset (StackStore offset register) (IntOperand indexOffset) =
|
||||
let storeInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Store
|
||||
$ RiscV.S (fromIntegral $ offset + indexOffset * 4) RiscV.SW RiscV.S0 register
|
||||
in (register, Vector.singleton storeInstruction)
|
||||
storeWithOffset (StackStore offset register) (VariableOperand indexOffset) =
|
||||
let storeInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Store
|
||||
$ RiscV.S (fromIntegral offset) RiscV.SW immediateRegister register
|
||||
statements = calculateIndexOffset indexOffset
|
||||
in (register, Vector.snoc statements storeInstruction)
|
||||
quadruple _ (ArrayQuadruple assigneeVariable indexOperand store) =
|
||||
let (operandRegister1, statements1) = loadWithOffset assigneeVariable indexOperand
|
||||
(storeRegister, storeStatements) = storeToStore store
|
||||
instruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.OpImm
|
||||
$ RiscV.I storeRegister RiscV.ADDI operandRegister1 0
|
||||
in pure $ statements1 <> Vector.cons instruction storeStatements
|
||||
where
|
||||
loadWithOffset :: RiscVStore -> Operand RiscVStore -> (RiscV.XRegister, Vector Statement)
|
||||
loadWithOffset (RegisterStore register) _ = (register, mempty)
|
||||
loadWithOffset (StackStore offset register) (IntOperand indexOffset) =
|
||||
let loadInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Load
|
||||
$ RiscV.I register RiscV.LW RiscV.S0 (fromIntegral $ offset + indexOffset * 4)
|
||||
in (register, Vector.singleton loadInstruction)
|
||||
loadWithOffset (StackStore offset register) (VariableOperand indexOffset) =
|
||||
let loadInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Load
|
||||
$ RiscV.I register RiscV.SW immediateRegister (fromIntegral offset)
|
||||
statements = calculateIndexOffset indexOffset
|
||||
in (register, Vector.snoc statements loadInstruction)
|
||||
|
||||
calculateIndexOffset :: RiscVStore -> Vector Statement
|
||||
calculateIndexOffset indexOffset =
|
||||
let (indexRegister, indexStatements) = loadFromStore indexOffset
|
||||
baseRegisterInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.OpImm
|
||||
$ RiscV.I immediateRegister RiscV.ADDI RiscV.Zero 4
|
||||
indexRelativeOffset = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R immediateRegister RiscV.MUL immediateRegister indexRegister (RiscV.Funct7 0b0000001)
|
||||
registerWithOffset = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R immediateRegister RiscV.ADD immediateRegister RiscV.S0 (RiscV.Funct7 0b0000000)
|
||||
statements = Vector.fromList
|
||||
[ baseRegisterInstruction
|
||||
, indexRelativeOffset
|
||||
, registerWithOffset
|
||||
]
|
||||
in indexStatements <> statements
|
||||
|
||||
unconditionalJal :: Label -> Statement
|
||||
unconditionalJal (Label goToLabel) = Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Jal
|
||||
$ RiscV.RJal RiscV.Zero goToLabel
|
||||
|
||||
loadImmediateOrRegister :: RiscVOperand -> RiscV.XRegister -> (RiscV.XRegister, Vector Statement)
|
||||
loadImmediateOrRegister (IntOperand intValue) targetRegister =
|
||||
(targetRegister, lui intValue targetRegister)
|
||||
loadImmediateOrRegister (VariableOperand store) _ = loadFromStore store
|
||||
|
||||
lui :: Int32 -> RiscV.XRegister -> Vector Statement
|
||||
lui intValue targetRegister
|
||||
| intValue >= -2048
|
||||
, intValue <= 2047 = Vector.singleton
|
||||
$ Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I targetRegister RiscV.ADDI RiscV.Zero lo)
|
||||
| intValue .&. 0x800 /= 0 = Vector.fromList
|
||||
[ Instruction (RiscV.BaseInstruction RiscV.Lui $ RiscV.U targetRegister $ fromIntegral $ succ hi)
|
||||
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I targetRegister RiscV.ADDI targetRegister lo)
|
||||
]
|
||||
| otherwise = Vector.fromList
|
||||
[ Instruction (RiscV.BaseInstruction RiscV.Lui $ RiscV.U targetRegister $ fromIntegral hi)
|
||||
, Instruction (RiscV.BaseInstruction RiscV.OpImm $ RiscV.I targetRegister RiscV.ADDI targetRegister lo)
|
||||
]
|
||||
where
|
||||
hi = intValue `shiftR` 12
|
||||
lo = fromIntegral intValue
|
||||
|
||||
commutativeBinary
|
||||
:: (Int32 -> Int32 -> Int32)
|
||||
-> RiscV.Funct3
|
||||
-> RiscV.Funct7
|
||||
-> (Operand RiscVStore, Operand RiscVStore)
|
||||
-> Store RiscV.XRegister
|
||||
-> RiscVGenerator (Vector Statement)
|
||||
commutativeBinary immediateOperation funct3 funct7 (operand1, operand2) store
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
immediateOperation' = immediateOperation immediateOperand1 immediateOperand2
|
||||
in pure $ lui immediateOperation' storeRegister <> storeStatements
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 =
|
||||
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
(storeRegister, storeStatements) = storeToStore store
|
||||
instruction = Instruction $ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister funct3 operandRegister1 operandRegister2 funct7
|
||||
in pure $ statements1 <> statements2
|
||||
<> Vector.cons instruction storeStatements
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
commutativeImmediateRegister variableOperand1 immediateOperand2
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 =
|
||||
commutativeImmediateRegister variableOperand2 immediateOperand1
|
||||
where
|
||||
commutativeImmediateRegister variableOperand immediateOperand =
|
||||
let (storeRegister, storeStatements) = storeToStore store
|
||||
immediateStatements = lui immediateOperand storeRegister
|
||||
(operandRegister, registerStatements) = loadFromStore variableOperand
|
||||
instruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Op
|
||||
$ RiscV.R storeRegister funct3 storeRegister operandRegister funct7
|
||||
in pure $ immediateStatements <> registerStatements
|
||||
<> Vector.cons instruction storeStatements
|
||||
|
||||
commutativeComparison
|
||||
:: (Int32 -> Int32 -> Bool)
|
||||
-> RiscV.Funct3
|
||||
-> (Operand RiscVStore, Operand RiscVStore)
|
||||
-> Label
|
||||
-> RiscVGenerator (Vector Statement)
|
||||
commutativeComparison immediateOperation funct3 (operand1, operand2) goToLabel
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
if immediateOperation immediateOperand1 immediateOperand2
|
||||
then pure $ Vector.singleton $ unconditionalJal goToLabel
|
||||
else pure Vector.empty
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 = do
|
||||
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
Label goToLabel' = goToLabel
|
||||
pure $ Vector.snoc (statements1 <> statements2)
|
||||
$ Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch goToLabel' funct3 operandRegister1 operandRegister2
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
compareImmediateRegister variableOperand1 immediateOperand2
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 =
|
||||
compareImmediateRegister variableOperand2 immediateOperand1
|
||||
where
|
||||
compareImmediateRegister variableOperand immediateOperand =
|
||||
let immediateStatements = lui immediateOperand immediateRegister
|
||||
(operandRegister, registerStatements) = loadFromStore variableOperand
|
||||
Label goToLabel' = goToLabel
|
||||
in pure $ Vector.snoc (immediateStatements <> registerStatements)
|
||||
$ Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch goToLabel' funct3 operandRegister immediateRegister
|
||||
|
||||
lessThan :: (Operand RiscVStore, Operand RiscVStore) -> Label -> RiscVGenerator (Vector Statement)
|
||||
lessThan (operand1, operand2) goToLabel
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
if immediateOperand1 < immediateOperand2
|
||||
then pure $ Vector.singleton $ unconditionalJal goToLabel
|
||||
else pure Vector.empty
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 = do
|
||||
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
Label goToLabel' = goToLabel
|
||||
pure $ Vector.snoc (statements1 <> statements2)
|
||||
$ Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch goToLabel' RiscV.BLT operandRegister1 operandRegister2
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
let statements2 = lui immediateOperand2 immediateRegister
|
||||
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
Label goToLabel' = goToLabel
|
||||
in pure $ Vector.snoc (statements1 <> statements2)
|
||||
$ Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch goToLabel' RiscV.BLT operandRegister1 immediateRegister
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 =
|
||||
let statements1 = lui immediateOperand1 immediateRegister
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
Label goToLabel' = goToLabel
|
||||
in pure $ Vector.snoc (statements1 <> statements2)
|
||||
$ Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch goToLabel' RiscV.BLT immediateRegister operandRegister2
|
||||
|
||||
lessOrEqualThan :: (Operand RiscVStore, Operand RiscVStore) -> Label -> RiscVGenerator (Vector Statement)
|
||||
lessOrEqualThan (operand1, operand2) goToLabel
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
if immediateOperand1 <= immediateOperand2
|
||||
then pure $ Vector.singleton $ unconditionalJal goToLabel
|
||||
else pure Vector.empty
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 = do
|
||||
let (operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
Label goToLabel' = goToLabel
|
||||
pure $ Vector.snoc (statements1 <> statements2)
|
||||
$ Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch goToLabel' RiscV.BGE operandRegister2 operandRegister1
|
||||
| VariableOperand variableOperand1 <- operand1
|
||||
, IntOperand immediateOperand2 <- operand2 =
|
||||
let statements2 = lui immediateOperand2 immediateRegister
|
||||
(operandRegister1, statements1) = loadFromStore variableOperand1
|
||||
Label goToLabel' = goToLabel
|
||||
in pure $ Vector.snoc (statements1 <> statements2)
|
||||
$ Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch goToLabel' RiscV.BGE immediateRegister operandRegister1
|
||||
| IntOperand immediateOperand1 <- operand1
|
||||
, VariableOperand variableOperand2 <- operand2 =
|
||||
let statements1 = lui immediateOperand1 immediateRegister
|
||||
(operandRegister2, statements2) = loadFromStore variableOperand2
|
||||
Label goToLabel' = goToLabel
|
||||
in pure $ Vector.snoc (statements1 <> statements2)
|
||||
$ Instruction
|
||||
$ RiscV.RelocatableInstruction RiscV.Branch
|
||||
$ RiscV.RBranch goToLabel' RiscV.BGE operandRegister2 immediateRegister
|
||||
|
||||
loadFromStore :: RiscVStore -> (RiscV.XRegister, Vector Statement)
|
||||
loadFromStore (RegisterStore register) = (register, mempty)
|
||||
loadFromStore (StackStore offset register) =
|
||||
let loadInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Load
|
||||
$ RiscV.I register RiscV.LW RiscV.S0 (fromIntegral offset)
|
||||
in (register, Vector.singleton loadInstruction)
|
||||
|
||||
storeToStore :: RiscVStore -> (RiscV.XRegister, Vector Statement)
|
||||
storeToStore (RegisterStore register) = (register, mempty)
|
||||
storeToStore (StackStore offset register) =
|
||||
let storeInstruction = Instruction
|
||||
$ RiscV.BaseInstruction RiscV.Store
|
||||
$ RiscV.S (fromIntegral offset) RiscV.SW RiscV.S0 register
|
||||
in (register, Vector.singleton storeInstruction)
|
@ -1,338 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
462
package-lock.json
generated
Normal 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
17
package.json
Normal 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
131
parser/lexer.ll
Normal 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
46
parser/main.cpp
Normal 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
406
parser/parser.yy
Normal 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);
|
||||
}
|
@ -1,335 +0,0 @@
|
||||
# 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 https://mozilla.org/MPL/2.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
|
@ -1,5 +0,0 @@
|
||||
# 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 https://mozilla.org/MPL/2.0/. -}
|
||||
|
||||
TMP = Pathname.new('./build')
|
@ -1,100 +0,0 @@
|
||||
# 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 https://mozilla.org/MPL/2.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
329
source/lexer.cpp
Normal 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
267
source/optimizer.cpp
Normal 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
1063
source/parser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
46
source/result.cpp
Normal file
46
source/result.cpp
Normal 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
204
source/scanner.l
Normal 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
325
source/semantic.cpp
Normal 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
129
source/symbol_table.cpp
Normal 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
96
source/types.cpp
Normal 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);
|
||||
}
|
||||
}
|
58
src/Main.hs
58
src/Main.hs
@ -1,58 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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.
|
||||
-- 6 - Register allocation 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 makeObject = elfObject output . riscv32Elf . generateRiscV
|
||||
in either (printAndExit 6) makeObject
|
||||
$ allocate riscVConfiguration symbolTable
|
||||
$ glue symbolTable program
|
||||
printAndExit :: Show b => forall a. Int -> b -> IO a
|
||||
printAndExit failureCode e = print e >> exitWith (ExitFailure failureCode)
|
@ -1,82 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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"
|
@ -1,146 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.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
|
@ -1,5 +0,0 @@
|
||||
{- 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 https://mozilla.org/MPL/2.0/. -}
|
||||
|
||||
{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
|
3
tests/const_list.eln
Normal file
3
tests/const_list.eln
Normal file
@ -0,0 +1,3 @@
|
||||
const a = 1, b = 2;
|
||||
writei(a + b)
|
||||
.
|
5
tests/declare_variable.eln
Normal file
5
tests/declare_variable.eln
Normal file
@ -0,0 +1,5 @@
|
||||
var x: Int;
|
||||
begin
|
||||
x := 5;
|
||||
writei(x)
|
||||
end.
|
1
tests/divide.eln
Normal file
1
tests/divide.eln
Normal file
@ -0,0 +1 @@
|
||||
writei(6 / 3)
|
@ -1 +0,0 @@
|
||||
38
|
@ -1 +0,0 @@
|
||||
58
|
@ -1 +0,0 @@
|
||||
58
|
@ -1 +0,0 @@
|
||||
c
|
2
tests/expectations/if_condition.txt
Normal file
2
tests/expectations/if_condition.txt
Normal file
@ -0,0 +1,2 @@
|
||||
8
|
||||
9
|
1
tests/expectations/left_nested_sum.txt
Normal file
1
tests/expectations/left_nested_sum.txt
Normal file
@ -0,0 +1 @@
|
||||
8
|
1
tests/expectations/multiply.txt
Normal file
1
tests/expectations/multiply.txt
Normal file
@ -0,0 +1 @@
|
||||
8
|
1
tests/expectations/multiply_3.txt
Normal file
1
tests/expectations/multiply_3.txt
Normal file
@ -0,0 +1 @@
|
||||
H
|
@ -1 +0,0 @@
|
||||
-8
|
1
tests/expectations/plus_minus.txt
Normal file
1
tests/expectations/plus_minus.txt
Normal file
@ -0,0 +1 @@
|
||||
4
|
@ -1 +0,0 @@
|
||||
0
|
@ -1 +0,0 @@
|
||||
13
|
@ -1,2 +0,0 @@
|
||||
13
|
||||
2097150
|
2
tests/expectations/print_boolean.txt
Normal file
2
tests/expectations/print_boolean.txt
Normal file
@ -0,0 +1,2 @@
|
||||
t
|
||||
f
|
@ -1 +0,0 @@
|
||||
x
|
12
tests/expectations/print_equals.txt
Normal file
12
tests/expectations/print_equals.txt
Normal file
@ -0,0 +1,12 @@
|
||||
t
|
||||
f
|
||||
t
|
||||
f
|
||||
f
|
||||
t
|
||||
t
|
||||
f
|
||||
t
|
||||
f
|
||||
f
|
||||
t
|
5
tests/expectations/print_in_loop.txt
Normal file
5
tests/expectations/print_in_loop.txt
Normal file
@ -0,0 +1,5 @@
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
5
|
@ -1,2 +0,0 @@
|
||||
14
|
||||
8
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user