diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-08-21 00:05:03 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-08-21 00:05:03 +0200 |
| commit | 39269cc68a32f6e1a7524b00da36a1fe4cc7fb8b (patch) | |
| tree | 9558cd6107d60e22127faeddbdeac5031ee2fdbe /boot | |
| parent | 4f77ad5d019618893c59a0f8d5bfa6b98e50a3be (diff) | |
| download | elna-39269cc68a32f6e1a7524b00da36a1fe4cc7fb8b.tar.gz | |
Implement Single and Double floats
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/ast.cc | 4 | ||||
| -rw-r--r-- | boot/evaluator.cc | 112 | ||||
| -rw-r--r-- | boot/lexer.ll | 26 | ||||
| -rw-r--r-- | boot/materialization.cc | 35 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 6 | ||||
| -rw-r--r-- | boot/parser.yy | 13 | ||||
| -rw-r--r-- | boot/result.cc | 87 | ||||
| -rw-r--r-- | boot/symbol.cc | 39 | ||||
| -rw-r--r-- | boot/type_check.cc | 19 |
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(), |
