/* Miscellaneous types used across stage boundaries.
Copyright (C) 2025 Free Software Foundation, Inc.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GCC; see the file COPYING3. If not see
. */
#include "elna/boot/result.h"
#include
#include
#include
namespace elna::boot
{
location::location(const std::size_t line, const std::size_t column)
: m_line(line), m_column(column)
{
}
std::size_t location::line() const
{
return this->m_line;
}
std::size_t location::column() const
{
return this->m_column;
}
bool location::available() const
{
return this->m_line != 0 || this->m_column != 0;
}
source_position::source_position(location start, location end)
: m_start(start), m_end(end)
{
}
const location& source_position::start() const
{
return this->m_start;
}
const location& source_position::end() const
{
return this->m_end;
}
bool source_position::is_span() const
{
return this->m_start != this->m_end;
}
error::error(const source_position position)
: position(position)
{
}
std::deque>& error_container::errors()
{
return m_errors;
}
bool error_container::has_errors() const
{
return !m_errors.empty();
}
identifier::identifier(const std::string& name, const source_position& position)
: m_name(name), m_position(position)
{
}
const std::string& identifier::name() const
{
return this->m_name;
}
std::string identifier::to_string() const
{
return name();
}
const source_position& identifier::position() const
{
return this->m_position;
}
bool identifier::operator==(const identifier& that) const
{
return this->m_name == that;
}
bool identifier::operator==(std::string_view that) const
{
return this->m_name == that;
}
identifier_definition::identifier_definition(const std::string& name,
const source_position& position, const bool exported)
: m_identifier(name, position), m_exported(exported)
{
}
const std::string& identifier_definition::name() const
{
return this->m_identifier.name();
}
const identifier& identifier_definition::id() const
{
return this->m_identifier;
}
bool identifier_definition::exported() const
{
return this->m_exported;
}
std::vector extract_identifiers(const std::vector& identifiers)
{
std::vector result;
result.reserve(identifiers.size());
std::ranges::transform(identifiers, std::back_inserter(result),
[](const auto& identifier) { return identifier.id(); });
return result;
}
integer_literal::integer_literal(bool is_signed, std::size_t size)
: m_signed(is_signed), m_size(size)
{
mpz_init(this->raw);
}
integer_literal::integer_literal(integer_literal&& that) noexcept
: m_signed(that.is_signed()), m_size(that.size())
{
mpz_init(this->raw);
mpz_swap(this->raw, that.raw);
}
integer_literal::integer_literal(const integer_literal& that)
: m_signed(that.is_signed()), m_size(that.size())
{
mpz_init(this->raw);
mpz_set(this->raw, that.raw);
}
integer_literal::~integer_literal()
{
mpz_clear(this->raw);
}
std::optional integer_literal::add(const integer_literal& that) const
{
integer_literal result = *this;
mpz_add(result.raw, this->raw, that.raw);
return std::move(result).check();
}
std::optional integer_literal::sub(const integer_literal& that) const
{
integer_literal result = *this;
mpz_sub(result.raw, this->raw, that.raw);
return std::move(result).check();
}
std::optional integer_literal::mul(const integer_literal& that) const
{
integer_literal result = *this;
mpz_mul(result.raw, this->raw, that.raw);
return std::move(result).check();
}
std::optional integer_literal::div(const integer_literal& that) const
{
if (mpz_cmp_ui(that.raw, 0U) == 0)
{
return std::nullopt;
}
else
{
integer_literal result = *this;
mpz_div(result.raw, this->raw, that.raw);
return std::make_optional(std::move(result));
}
}
std::optional integer_literal::mod(const integer_literal& that) const
{
if (mpz_cmp_ui(that.raw, 0U) == 0)
{
return std::nullopt;
}
else
{
integer_literal result = *this;
mpz_mod(result.raw, this->raw, that.raw);
return std::make_optional(std::move(result));
}
}
std::optional integer_literal::neg() const
{
if (!is_signed() || is_negative_minimum())
{
return std::nullopt;
}
integer_literal result = *this;
mpz_neg(result.raw, this->raw);
return result;
}
std::optional integer_literal::shl(const integer_literal& that) const
{
if (that >= bits())
{
return std::nullopt;
}
integer_literal result = *this;
mpz_mul_2exp(result.raw, this->raw, static_cast(mpz_get_ui(that.raw)));
return std::move(result).check();
}
std::optional integer_literal::shr(const integer_literal& that) const
{
integer_literal result = *this;
if (that >= bits())
{
mpz_set_si(result.raw, *this > 0 ? 0 : -1);
}
else
{
mpz_fdiv_q_2exp(result.raw, this->raw, static_cast(mpz_get_ui(that.raw)));
}
return std::make_optional(std::move(result));
}
integer_literal integer_literal::operator|(const integer_literal& that) const
{
integer_literal result = *this;
mpz_ior(result.raw, this->raw, that.raw);
return result;
}
integer_literal integer_literal::operator&(const integer_literal& that) const
{
integer_literal result = *this;
mpz_and(result.raw, this->raw, that.raw);
return result;
}
integer_literal integer_literal::operator^(const integer_literal& that) const
{
integer_literal result = *this;
mpz_xor(result.raw, this->raw, that.raw);
return result;
}
integer_literal integer_literal::operator~() const
{
integer_literal result = *this;
mpz_com(result.raw, this->raw);
return result;
}
bool integer_literal::operator==(const integer_literal& that) const
{
return mpz_cmp(this->raw, that.raw) == 0;
}
std::weak_ordering integer_literal::operator<=>(const integer_literal& that) const
{
return mpz_cmp(this->raw, that.raw) <=> 0;
}
integer_literal& integer_literal::operator=(integer_literal&& that) noexcept
{
swap(*this, that);
return *this;
}
integer_literal& integer_literal::operator=(const integer_literal& that)
{
if (this != &that)
{
integer_literal temp(that);
swap(*this, temp);
}
return *this;
}
std::optional integer_literal::to_signed() const
{
if (is_negative_minimum(sizeof(std::ptrdiff_t) * CHAR_BIT))
{
return std::numeric_limits::min();
}
auto [written, rop] = export_to_words();
if (written > 1 || rop < 0)
{
return std::nullopt;
}
return is_negative() ? -rop : rop;
}
std::optional integer_literal::to_unsigned() const
{
auto [written, rop] = export_to_words();
return written > 1 ? std::nullopt : std::make_optional(rop);
}
bool integer_literal::fit_into(const std::size_t target_size)
{
if (fits_in(target_size * CHAR_BIT))
{
this->m_size = target_size;
return true;
}
return false;
}
bool integer_literal::is_signed() const
{
return this->m_signed;
}
std::size_t integer_literal::size() const
{
return this->m_size;
}
std::string integer_literal::to_string(const std::uint8_t base) const
{
// +1 sign, +1 null terminator
const size_t buffer_size = mpz_sizeinbase(this->raw, static_cast(base)) + 2;
std::string result(buffer_size, '\0');
mpz_get_str(result.data(), base, this->raw);
result.resize(std::strlen(result.c_str()));
return result;
}
void swap(integer_literal& lhs, integer_literal& rhs) noexcept
{
mpz_swap(lhs.raw, rhs.raw);
std::swap(lhs.m_signed, rhs.m_signed);
std::swap(lhs.m_size, rhs.m_size);
}
bool integer_literal::fits_in(const std::size_t bits) const
{
std::size_t required_bits = mpz_sizeinbase(this->raw, 2);
if (!is_negative() || !is_negative_minimum(bits))
{
++required_bits; // Add one bit for the sign.
}
return required_bits <= bits && (!is_negative() || is_signed());
}
std::optional integer_literal::check() &&
{
return fits_in(bits()) ? std::make_optional(std::move(*this)) : std::nullopt;
}
bool integer_literal::is_negative_minimum(const std::size_t bits) const
{
return mpz_scan1(this->raw, 0) == bits - 1;
}
bool integer_literal::is_negative_minimum() const
{
return is_negative() && is_negative_minimum(bits());
}
std::size_t integer_literal::bits() const
{
return size() * CHAR_BIT;
}
bool integer_literal::is_negative() const
{
return mpz_sgn(this->raw) < 0;
}
std::size_t constant_value_hash::operator()(const elna::boot::constant_value& value) const noexcept
{
return std::visit([](auto&& alternative) -> std::size_t {
using T = std::decay_t;
return std::hash{}(alternative);
}, value);
}
hash_accumulator hash_accumulator::operator+(const std::size_t& that) const
{
hash_accumulator result{};
result.m_seed ^= that + golden_ratio
+ (this->m_seed << mix_shift_left)
+ (this->m_seed >> mix_shift_right);
return result;
}
std::size_t hash_accumulator::seed() const
{
return this->m_seed;
}
}
std::size_t std::hash::operator()(
const elna::boot::identifier& key) const noexcept
{
return std::hash{}(key.name());
}
std::size_t std::hash>::operator()(
const elna::boot::constant_aggregate& key) const noexcept
{
const elna::boot::constant_value_hash hasher{};
auto hash = std::accumulate(key->begin(), key->end(), elna::boot::hash_accumulator{},
[&hasher](const auto& accumulator, const auto& element) {
return accumulator + hasher(element);
});
return hash.seed();
}
std::size_t std::hash>::operator()(
const elna::boot::constant_aggregate& key) const noexcept
{
const elna::boot::constant_value_hash hasher{};
auto hash = std::accumulate(key->begin(), key->end(), elna::boot::hash_accumulator{},
[&hasher](const auto& accumulator, const auto& element) {
return accumulator + std::hash{}(element.first) + hasher(element.second);
});
return hash.seed();
}
std::size_t std::hash::operator()(const elna::boot::integer_literal& key) const noexcept
{
if (key.is_signed())
{
if (auto converted = key.to_signed())
{
return std::hash{}(*converted);
}
}
else
{
if (auto converted = key.to_unsigned())
{
return std::hash{}(*converted);
}
}
return 0;
}