aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
Diffstat (limited to 'boot')
-rw-r--r--boot/ast.cc4
-rw-r--r--boot/evaluator.cc112
-rw-r--r--boot/lexer.ll26
-rw-r--r--boot/materialization.cc35
-rw-r--r--boot/name_analysis.cc6
-rw-r--r--boot/parser.yy13
-rw-r--r--boot/result.cc87
-rw-r--r--boot/symbol.cc39
-rw-r--r--boot/type_check.cc19
9 files changed, 231 insertions, 110 deletions
diff --git a/boot/ast.cc b/boot/ast.cc
index 9b21f56..287b7aa 100644
--- a/boot/ast.cc
+++ b/boot/ast.cc
@@ -186,7 +186,7 @@ namespace elna::boot
__builtin_unreachable();
}
- void empty_visitor::visit(literal<double> *)
+ void empty_visitor::visit(literal<float_literal> *)
{
__builtin_unreachable();
}
@@ -509,7 +509,7 @@ namespace elna::boot
{
}
- void walking_visitor::visit(literal<double> *)
+ void walking_visitor::visit(literal<float_literal> *)
{
}
diff --git a/boot/evaluator.cc b/boot/evaluator.cc
index d43ef11..1d10346 100644
--- a/boot/evaluator.cc
+++ b/boot/evaluator.cc
@@ -237,9 +237,9 @@ namespace elna::boot
{
return constant_value{ integer_subject->value };
}
- else if (auto *double_subject = subject.is_a<double>())
+ else if (auto *float_subject = subject.is_a<float_literal>())
{
- return constant_value{ double_subject->value };
+ return constant_value{ float_subject->value };
}
else if (auto *boolean_subject = subject.is_a<bool>())
{
@@ -395,7 +395,7 @@ namespace elna::boot
}
return std::nullopt;
}
- if constexpr (std::is_same_v<T, double>)
+ if constexpr (std::is_same_v<T, float_literal>)
{
return constant_value{ -value };
}
@@ -433,7 +433,7 @@ namespace elna::boot
template<typename T>
static std::optional<T> add_overflow(T lhs, T rhs)
{
- if constexpr (is_integral<T>)
+ if constexpr (is_integral_v<T>)
{
T result;
return __builtin_add_overflow(lhs, rhs, &result)
@@ -446,7 +446,7 @@ namespace elna::boot
template<typename T>
static std::optional<T> sub_overflow(T lhs, T rhs)
{
- if constexpr (is_integral<T>)
+ if constexpr (is_integral_v<T>)
{
T result;
return __builtin_sub_overflow(lhs, rhs, &result)
@@ -459,7 +459,7 @@ namespace elna::boot
template<typename T>
static std::optional<T> mul_overflow(T lhs, T rhs)
{
- if constexpr (is_integral<T>)
+ if constexpr (is_integral_v<T>)
{
T result;
return __builtin_mul_overflow(lhs, rhs, &result)
@@ -498,7 +498,7 @@ namespace elna::boot
return constant_value{ result.value() };
}
}
- else if constexpr (std::is_floating_point_v<T>)
+ else if constexpr (std::is_same_v<T, float_literal>)
{
if (auto result = add_overflow(lhs, rhs))
{
@@ -514,7 +514,7 @@ namespace elna::boot
return constant_value{ result.value() };
}
}
- else if constexpr (std::is_floating_point_v<T>)
+ else if constexpr (std::is_same_v<T, float_literal>)
{
if (auto result = sub_overflow(lhs, rhs))
{
@@ -530,7 +530,7 @@ namespace elna::boot
return constant_value{ result.value() };
}
}
- else if constexpr (std::is_floating_point_v<T>)
+ else if constexpr (std::is_same_v<T, float_literal>)
{
if (auto result = mul_overflow(lhs, rhs))
{
@@ -546,12 +546,9 @@ namespace elna::boot
return constant_value{ result.value() };
}
}
- else if constexpr (std::is_floating_point_v<T>)
+ else if constexpr (std::is_same_v<T, float_literal>)
{
- if (rhs != static_cast<T>(0))
- {
- return constant_value{ lhs / rhs };
- }
+ return constant_value{ lhs / rhs };
}
return std::nullopt;
case remainder:
@@ -625,25 +622,25 @@ namespace elna::boot
case not_equals:
return constant_value{ lhs != rhs };
case less:
- if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, integer_literal>)
+ if constexpr (std::is_same_v<T, float_literal> || std::is_same_v<T, integer_literal>)
{
return constant_value{ lhs < rhs };
}
return std::nullopt;
case greater:
- if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, integer_literal>)
+ if constexpr (std::is_same_v<T, float_literal> || std::is_same_v<T, integer_literal>)
{
return constant_value{ lhs > rhs };
}
return std::nullopt;
case less_equal:
- if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, integer_literal>)
+ if constexpr (std::is_same_v<T, float_literal> || std::is_same_v<T, integer_literal>)
{
return constant_value{ lhs <= rhs };
}
return std::nullopt;
case greater_equal:
- if constexpr (std::is_floating_point_v<T> || std::is_same_v<T, integer_literal>)
+ if constexpr (std::is_same_v<T, float_literal> || std::is_same_v<T, integer_literal>)
{
return constant_value{ lhs >= rhs };
}
@@ -789,23 +786,23 @@ namespace elna::boot
{
type const resolved = resolve_underlying_type(subject.types.front());
- if (auto resolved_primitive = resolved.get<primitive_type>())
+ if (is_integer_type(resolved))
{
- if (resolved_primitive->identifier.starts_with("Int"))
- {
- const std::size_t bits = resolved_primitive->properties.size * CHAR_BIT;
- const integer_literal one = integer_literal::from<std::ptrdiff_t>(
- resolved_primitive->properties.size, 1U).value();
+ const std::shared_ptr<primitive_type> resolved_primitive = resolved.get<primitive_type>();
+ const std::size_t bits = resolved_primitive->properties.size * CHAR_BIT;
+ const integer_literal one = integer_literal::from<std::ptrdiff_t>(
+ resolved_primitive->properties.size, 1U).value();
- return constant_value{
- one.shl(integer_literal::from<std::size_t>(bits - 1U)).value()
- };
- }
- if (resolved_primitive->identifier.starts_with("Word"))
- {
- return constant_value{ integer_literal::from<std::size_t>(
- resolved_primitive->properties.size, 0U).value() };
- }
+ return constant_value{
+ one.shl(integer_literal::from<std::size_t>(bits - 1U)).value()
+ };
+ }
+ if (is_word_type(resolved))
+ {
+ const std::shared_ptr<primitive_type> resolved_primitive = resolved.get<primitive_type>();
+
+ return constant_value{ integer_literal::from<std::size_t>(
+ resolved_primitive->properties.size, 0U).value() };
}
if (is_primitive_type(resolved, "Char"))
{
@@ -815,9 +812,13 @@ namespace elna::boot
{
return constant_value{ false };
}
- if (is_primitive_type(resolved, "Float"))
+ if (is_primitive_type(resolved, "Single"))
{
- return constant_value{ -std::numeric_limits<double>::max() };
+ return constant_value{ float_literal::from(std::numeric_limits<float>::min()) };
+ }
+ if (is_primitive_type(resolved, "Double"))
+ {
+ return constant_value{ float_literal::from(std::numeric_limits<double>::min()) };
}
if (auto enumeration = resolved.get<enumeration_type>())
{
@@ -828,26 +829,25 @@ namespace elna::boot
{
type const resolved = resolve_underlying_type(subject.types.front());
- if (auto resolved_primitive = resolved.get<primitive_type>())
+ if (is_integer_type(resolved))
{
- if (resolved_primitive->identifier.starts_with("Int"))
- {
- const std::size_t bits = resolved_primitive->properties.size * CHAR_BIT;
- const integer_literal one = integer_literal::from<std::ptrdiff_t>(
- resolved_primitive->properties.size, 1U).value();
- const std::optional<integer_literal> minimum = one.shl(
- integer_literal::from<std::size_t>(bits - 1U));
+ const std::shared_ptr<primitive_type> resolved_primitive = resolved.get<primitive_type>();
+ const std::size_t bits = resolved_primitive->properties.size * CHAR_BIT;
+ const integer_literal one = integer_literal::from<std::ptrdiff_t>(
+ resolved_primitive->properties.size, 1U).value();
+ const std::optional<integer_literal> minimum = one.shl(
+ integer_literal::from<std::size_t>(bits - 1U));
- return constant_value{ ~minimum.value() };
- }
- if (resolved_primitive->identifier.starts_with("Word"))
- {
- // All bits set.
- std::optional<integer_literal> zero = integer_literal::from<std::size_t>(
- resolved_primitive->properties.size, 0U);
+ return constant_value{ ~minimum.value() };
+ }
+ if (is_word_type(resolved))
+ {
+ const std::shared_ptr<primitive_type> resolved_primitive = resolved.get<primitive_type>();
+ // All bits set.
+ std::optional<integer_literal> zero = integer_literal::from<std::size_t>(
+ resolved_primitive->properties.size, 0U);
- return constant_value{ ~zero.value() };
- }
+ return constant_value{ ~zero.value() };
}
if (is_primitive_type(resolved, "Char"))
{
@@ -857,9 +857,13 @@ namespace elna::boot
{
return constant_value{ true };
}
- if (is_primitive_type(resolved, "Float"))
+ if (is_primitive_type(resolved, "Single"))
+ {
+ return constant_value{ float_literal::from(std::numeric_limits<float>::max()) };
+ }
+ if (is_primitive_type(resolved, "Double"))
{
- return constant_value{ std::numeric_limits<double>::max() };
+ return constant_value{ float_literal::from(std::numeric_limits<double>::max()) };
}
if (auto enumeration = resolved.get<enumeration_type>())
{
diff --git a/boot/lexer.ll b/boot/lexer.ll
index 888c17d..eb1bbd3 100644
--- a/boot/lexer.ll
+++ b/boot/lexer.ll
@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
} while (0);
#include <sstream>
+#include <cmath>
#include "parser.hh"
#undef YY_DECL
@@ -50,6 +51,7 @@ HIGIT [0-9a-fA-F]
HEXADECIMAL 0[xX]{HIGIT}+
BIGIT [01]
NATURAL [1-9][[:digit:]]*
+EXPONENT [eE][+-]?[[:digit:]]+
%%
%{
@@ -325,31 +327,17 @@ to {
REJECT;
}
}
-[[:digit:]]+\.[[:digit:]]+([eE][+-]?[[:digit:]]+)? {
+[[:digit:]]+(\.[[:digit:]]+{EXPONENT}?|{EXPONENT}) {
errno = 0;
double result = strtod(yytext, NULL);
- if (errno == ERANGE)
- {
- REJECT;
- }
- else
- {
- return yy::parser::make_FLOAT(result, this->location);
- }
+ return yy::parser::make_DOUBLE(result, this->location);
}
-[[:digit:]]+[eE][+-]?[[:digit:]]+ {
+[[:digit:]]+(\.[[:digit:]]+{EXPONENT}?|{EXPONENT})f {
errno = 0;
- double result = strtod(yytext, NULL);
+ float result = strtof(yytext, NULL);
- if (errno == ERANGE)
- {
- REJECT;
- }
- else
- {
- return yy::parser::make_FLOAT(result, this->location);
- }
+ return yy::parser::make_SINGLE(result, this->location);
}
'([^'\\\n]|\\.|\\\n)+' {
std::optional<std::uint8_t> result = parse_byte_literal(yytext);
diff --git a/boot/materialization.cc b/boot/materialization.cc
index 8c436b3..4b58ac2 100644
--- a/boot/materialization.cc
+++ b/boot/materialization.cc
@@ -19,14 +19,22 @@ along with GCC; see the file COPYING3. If not see
namespace elna::boot
{
- materialization_error::materialization_error(const source_position position)
- : diagnostic(position)
+ materialization_error::materialization_error(const source_position position, kind error_kind)
+ : diagnostic(position), m_kind(error_kind)
{
}
std::string materialization_error::what() const
{
- return "Integer literal overflows";
+ switch (this->m_kind)
+ {
+ using enum kind;
+ case integer_overflow:
+ return "Integer literal overflows";
+ case real_overflow:
+ return "Real literal overflows";
+ }
+ __builtin_unreachable();
}
materialization_visitor::materialization_visitor(const target_info& target)
@@ -46,12 +54,14 @@ namespace elna::boot
if (!literal->has_explicit_size
&& !literal->value.fit_into(true, target.int_properties.front().size))
{
- add_error<materialization_error>(literal->position());
+ add_error<materialization_error>(literal->position(),
+ materialization_error::kind::integer_overflow);
}
}
else
{
- add_error<materialization_error>(literal->position());
+ add_error<materialization_error>(literal->position(),
+ materialization_error::kind::integer_overflow);
}
break;
case unmarked:
@@ -62,7 +72,8 @@ namespace elna::boot
if (!literal->value.fit_into(true, target_size))
{
- add_error<materialization_error>(literal->position());
+ add_error<materialization_error>(literal->position(),
+ materialization_error::kind::integer_overflow);
}
break;
}
@@ -70,9 +81,19 @@ namespace elna::boot
if (!literal->has_explicit_size
&& !literal->value.fit_into(false, target.word_properties.front().size))
{
- add_error<materialization_error>(literal->position());
+ add_error<materialization_error>(literal->position(),
+ materialization_error::kind::integer_overflow);
}
break;
}
}
+
+ void materialization_visitor::visit(literal<float_literal> *literal)
+ {
+ if (!literal->value.is_finite())
+ {
+ add_error<materialization_error>(literal->position(),
+ materialization_error::kind::real_overflow);
+ }
+ }
}
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 4b69f9c..1ad66a8 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -912,9 +912,11 @@ namespace elna::boot
this->current_type = type();
}
- void name_analysis_visitor::visit(literal<double> *literal)
+ void name_analysis_visitor::visit(literal<float_literal> *literal)
{
- literal->type_decoration = lookup_primitive_type("Float");
+ literal->type_decoration = literal->value.format() == float_literal::format_kind::binary32
+ ? lookup_primitive_type("Single")
+ : lookup_primitive_type("Double");
this->current_type = type();
}
diff --git a/boot/parser.yy b/boot/parser.yy
index 411922d..c45fdbc 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -82,7 +82,8 @@ along with GCC; see the file COPYING3. If not see
%token <std::pair<std::uint8_t, elna::boot::integer_sign>> INTEGER8 WORD8
%token <std::pair<std::uint16_t, elna::boot::integer_sign>> INTEGER16 WORD16
%token <std::pair<std::uint32_t, elna::boot::integer_sign>> INTEGER32 WORD32
-%token <double> FLOAT
+%token <float> SINGLE
+%token <double> DOUBLE
%token <std::uint32_t> CHARACTER
%token <std::string> STRING
%token <bool> BOOLEAN
@@ -298,9 +299,15 @@ literal:
$$ = new boot::literal<boot::integer_literal>(boot::make_position(@$),
boot::integer_literal::from(magnitude), wants_signed);
}
- | FLOAT
+ | SINGLE
{
- $$ = new boot::literal<double>(boot::make_position(@$), $1, boot::integer_sign::unmarked);
+ $$ = new boot::literal<boot::float_literal>(boot::make_position(@$),
+ boot::float_literal::from($1), boot::integer_sign::unmarked);
+ }
+ | DOUBLE
+ {
+ $$ = new boot::literal<boot::float_literal>(boot::make_position(@$),
+ boot::float_literal::from($1), boot::integer_sign::unmarked);
}
| BOOLEAN
{
diff --git a/boot/result.cc b/boot/result.cc
index 3970b1f..d25b85b 100644
--- a/boot/result.cc
+++ b/boot/result.cc
@@ -18,6 +18,8 @@ along with GCC; see the file COPYING3. If not see
#include "elna/boot/result.h"
#include <algorithm>
+#include <charconv>
+#include <cmath>
#include <cstring>
#include <numeric>
@@ -467,6 +469,85 @@ namespace elna::boot
return mpz_sgn(this->raw) < 0;
}
+ float_literal::float_literal(format_kind binary_format, double value)
+ : m_format(binary_format), raw(value)
+ {
+ }
+
+ float_literal float_literal::rounded(format_kind binary_format, double value)
+ {
+ if (binary_format == format_kind::binary32)
+ {
+ return float_literal(format_kind::binary32, static_cast<float>(value));
+ }
+ return float_literal(binary_format, value);
+ }
+
+ float_literal float_literal::operator+(const float_literal& that) const
+ {
+ return rounded(this->m_format, this->raw + that.raw);
+ }
+
+ float_literal float_literal::operator-(const float_literal& that) const
+ {
+ return rounded(this->m_format, this->raw - that.raw);
+ }
+
+ float_literal float_literal::operator*(const float_literal& that) const
+ {
+ return rounded(this->m_format, this->raw * that.raw);
+ }
+
+ float_literal float_literal::operator/(const float_literal& that) const
+ {
+ return rounded(this->m_format, this->raw / that.raw);
+ }
+
+ float_literal float_literal::operator-() const
+ {
+ return rounded(this->m_format, -this->raw);
+ }
+
+ bool float_literal::operator==(const float_literal& that) const
+ {
+ return this->raw == that.raw;
+ }
+
+ std::partial_ordering float_literal::operator<=>(const float_literal& that) const
+ {
+ return this->raw <=> that.raw;
+ }
+
+ float_literal::format_kind float_literal::format() const
+ {
+ return this->m_format;
+ }
+
+ std::string float_literal::to_string() const
+ {
+ constexpr std::size_t shortest_round_trip = 24;
+ std::array<char, shortest_round_trip + 4> buffer;
+
+ auto float_chars = std::to_chars(buffer.begin(), buffer.end(), this->raw);
+
+ return std::string(buffer.begin(), float_chars.ptr);
+ }
+
+ float_literal float_literal::cast_to(format_kind binary_format) const
+ {
+ return rounded(binary_format, this->raw);
+ }
+
+ double float_literal::value() const
+ {
+ return this->raw;
+ }
+
+ bool float_literal::is_finite() const
+ {
+ return std::isfinite(this->raw);
+ }
+
std::size_t constant_value_hash::operator()(const elna::boot::constant_value& value) const noexcept
{
return std::visit([](auto&& alternative) -> std::size_t {
@@ -541,3 +622,9 @@ std::size_t std::hash<elna::boot::integer_literal>::operator()(const elna::boot:
}
return 0;
}
+
+std::size_t std::hash<elna::boot::float_literal>::operator()(const elna::boot::float_literal& key) const noexcept
+{
+ // -0.0 and 0.0 compare equal, so their hashes must agree.
+ return std::hash<double>{}(key == 0.0 ? 0.0 : key.value());
+}
diff --git a/boot/symbol.cc b/boot/symbol.cc
index 05eea31..fdf6a65 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -326,8 +326,10 @@ namespace elna::boot
const type pointer = type(std::make_shared<primitive_type>("Pointer", target.pointer_properties));
result->enter("Pointer", std::make_shared<type_info>(pointer));
- result->enter("Float",
- std::make_shared<type_info>(type(std::make_shared<primitive_type>("Float", target.float_properties))));
+ result->enter("Single",
+ std::make_shared<type_info>(type(std::make_shared<primitive_type>("Single", target.single_properties))));
+ result->enter("Double",
+ std::make_shared<type_info>(type(std::make_shared<primitive_type>("Double", target.double_properties))));
const type boolean = type(std::make_shared<primitive_type>("Bool", target.bool_properties));
result->enter("Bool", std::make_shared<type_info>(boolean));
@@ -462,22 +464,41 @@ namespace elna::boot
return false;
}
- bool is_numeric_type(const type& checked)
+ bool is_integer_type(const type& checked)
{
- return is_integral_type(checked)
- || is_primitive_type(checked, "Float");
+ if (auto primitive_checked = checked.get<primitive_type>())
+ {
+ return primitive_checked->identifier.starts_with("Int");
+ }
+ return false;
}
- bool is_integral_type(const type& checked)
+ bool is_word_type(const type& checked)
{
if (auto primitive_checked = checked.get<primitive_type>())
{
- return primitive_checked->identifier.starts_with("Int")
- || primitive_checked->identifier.starts_with("Word");
+ return primitive_checked->identifier.starts_with("Word");
}
return false;
}
+ bool is_float_type(const type& checked)
+ {
+ return is_primitive_type(checked, "Single")
+ || is_primitive_type(checked, "Double");
+ }
+
+ bool is_numeric_type(const type& checked)
+ {
+ return is_integral_type(checked)
+ || is_float_type(checked);
+ }
+
+ bool is_integral_type(const type& checked)
+ {
+ return is_integer_type(checked) || is_word_type(checked);
+ }
+
bool is_discrete_type(const type& checked)
{
return is_integral_type(checked)
@@ -495,7 +516,7 @@ namespace elna::boot
bool is_scalar_type(const type& checked)
{
return is_discrete_type(checked)
- || is_primitive_type(checked, "Float")
+ || is_float_type(checked)
|| is_any_pointer_type(checked)
|| checked.get<enumeration_type>() != nullptr;
}
diff --git a/boot/type_check.cc b/boot/type_check.cc
index d830763..2d82579 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -833,7 +833,7 @@ namespace elna::boot
auto operation = expression->operation();
type const resolved = resolve_underlying_type(expression->operand().type_decoration);
- if (operation == unary_operator::plus)
+ if (operation == unary_operator::plus || operation == unary_operator::minus)
{
if (!is_numeric_type(resolved))
{
@@ -842,17 +842,6 @@ namespace elna::boot
type_mismatch_error::unary{ .operation = operation });
}
}
- else if (operation == unary_operator::minus)
- {
- auto signed_primitive = resolved.get<primitive_type>();
- if ((signed_primitive == nullptr || !signed_primitive->identifier.starts_with("Int"))
- && !is_primitive_type(resolved, "Float"))
- {
- add_error<type_mismatch_error>(expression->position(),
- expression->operand().type_decoration,
- type_mismatch_error::unary{ .operation = operation });
- }
- }
else if (operation == unary_operator::negation)
{
if (is_primitive_type(resolved, "Bool"))
@@ -933,10 +922,12 @@ namespace elna::boot
|| (is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved);
break;
case division:
- case remainder:
case multiplication:
valid = is_numeric_type(lhs_resolved) && lhs_resolved == rhs_resolved;
break;
+ case remainder:
+ valid = is_integral_type(lhs_resolved) && lhs_resolved == rhs_resolved;
+ break;
case less:
case greater:
case less_equal:
@@ -1032,7 +1023,7 @@ namespace elna::boot
const type resolved = resolve_underlying_type(trait->type_decoration);
if (resolved.get<enumeration_type>() == nullptr
- && !is_primitive_type(resolved, "Float")
+ && !is_float_type(resolved)
&& !is_discrete_type(resolved))
{
add_error<trait_error>(trait->name.position(), trait->name.name(),