diff options
| author | Eugen Wissner <belka@caraus.de> | 2026-08-13 22:55:06 +0200 |
|---|---|---|
| committer | Eugen Wissner <belka@caraus.de> | 2026-08-13 22:55:06 +0200 |
| commit | bf7416a3ef9f0b787dcaebf816f3f3e6e385ff42 (patch) | |
| tree | 26359c705133dbd8e98f06483008a720d0dae9d7 /boot | |
| parent | ae29de96c94e5c582f4424804464cdd29bf2a013 (diff) | |
| download | elna-bf7416a3ef9f0b787dcaebf816f3f3e6e385ff42.tar.gz | |
Implement fixed-size integers
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/dependency.cc | 15 | ||||
| -rw-r--r-- | boot/evaluator.cc | 70 | ||||
| -rw-r--r-- | boot/lexer.ll | 146 | ||||
| -rw-r--r-- | boot/materialization.cc | 62 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 117 | ||||
| -rw-r--r-- | boot/parser.yy | 116 | ||||
| -rw-r--r-- | boot/result.cc | 86 | ||||
| -rw-r--r-- | boot/symbol.cc | 59 | ||||
| -rw-r--r-- | boot/type_check.cc | 4 | ||||
| -rw-r--r-- | boot/validation.cc | 56 |
10 files changed, 572 insertions, 159 deletions
diff --git a/boot/dependency.cc b/boot/dependency.cc index 9ab5141..bd1251a 100644 --- a/boot/dependency.cc +++ b/boot/dependency.cc @@ -18,6 +18,7 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/dependency.h" #include "elna/boot/driver.h" +#include "elna/boot/materialization.h" #include "elna/boot/name_analysis.h" #include "elna/boot/type_check.h" #include "elna/boot/validation.h" @@ -25,7 +26,7 @@ along with GCC; see the file COPYING3. If not see namespace elna::boot { - dependency read_source(std::istream& entry_point) + dependency read_source(std::istream& entry_point, const target_info& target) { driver parse_driver; lexer tokenizer(entry_point); @@ -41,10 +42,18 @@ namespace elna::boot { std::swap(outcome.tree, parse_driver.tree); } - declaration_visitor declaration_visitor; + materialization_visitor materialization_visitor(target); + outcome.tree->accept(&materialization_visitor); + + if (materialization_visitor.has_errors()) + { + std::swap(outcome.errors(), materialization_visitor.errors()); + return outcome; + } + declaration_visitor declaration_visitor{}; outcome.tree->accept(&declaration_visitor); - if (!declaration_visitor.errors().empty()) + if (declaration_visitor.has_errors()) { std::swap(outcome.errors(), declaration_visitor.errors()); } diff --git a/boot/evaluator.cc b/boot/evaluator.cc index 14ca889..d8d9ddc 100644 --- a/boot/evaluator.cc +++ b/boot/evaluator.cc @@ -73,31 +73,18 @@ namespace elna::boot { auto resolved = resolve_underlying_type(subject); - if (is_primitive_type(resolved, "Int") - || resolved.get<enumeration_type>() != nullptr) + if (resolved.get<enumeration_type>() != nullptr) { - return target.int_properties; + return target.word_properties.front(); } - else if (is_primitive_type(resolved, "Word")) - { - return target.word_properties; - } - else if (is_primitive_type(resolved, "Char")) + else if (auto resolved_primitive = resolved.get<primitive_type>()) { - return target.char_properties; - } - else if (is_primitive_type(resolved, "Float")) - { - return target.float_properties; - } - else if (is_primitive_type(resolved, "Bool")) - { - return target.bool_properties; + return resolved_primitive->properties; } else if (resolved.get<slice_type>() != nullptr) { return type_properties{ - .size = target.pointer_properties.size + target.word_properties.size, + .size = target.pointer_properties.size + target.word_properties.front().size, .alignment = target.pointer_properties.alignment }; } @@ -812,13 +799,23 @@ namespace elna::boot { type const resolved = resolve_underlying_type(subject.types.front()); - if (is_primitive_type(resolved, "Int")) - { - return constant_value{ integer_literal::from(std::numeric_limits<std::ptrdiff_t>::min()) }; - } - if (is_primitive_type(resolved, "Word")) + if (auto resolved_primitive = resolved.get<primitive_type>()) { - return constant_value{ integer_literal::from<std::size_t>(0) }; + 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(); + + 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() }; + } } if (is_primitive_type(resolved, "Char")) { @@ -841,13 +838,26 @@ namespace elna::boot { type const resolved = resolve_underlying_type(subject.types.front()); - if (is_primitive_type(resolved, "Int")) + if (auto resolved_primitive = resolved.get<primitive_type>()) { - return constant_value{ integer_literal::from(std::numeric_limits<std::ptrdiff_t>::max()) }; - } - if (is_primitive_type(resolved, "Word")) - { - return constant_value{ integer_literal::from(std::numeric_limits<std::size_t>::max()) }; + 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)); + + 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{ ~zero.value() }; + } } if (is_primitive_type(resolved, "Char")) { diff --git a/boot/lexer.ll b/boot/lexer.ll index e944560..691034a 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -47,7 +47,9 @@ along with GCC; see the file COPYING3. If not see ID1 [A-Za-z_] ID2 [A-Za-z0-9_] HIGIT [0-9a-fA-F] +HEXADECIMAL 0[xX]{HIGIT}+ BIGIT [01] +NATURAL [1-9][[:digit:]]* %% %{ @@ -173,55 +175,158 @@ to { #{ID1}{ID2}* { return yy::parser::make_TRAIT(yytext + 1, this->location); } -[[:digit:]]+u { - std::uint64_t result = strtoull(yytext, NULL, 10); - - if (errno == ERANGE) +(0|{NATURAL})u|{HEXADECIMAL} { + if (auto result = parse_integer<std::uint64_t>(yytext, 0)) + { + return yy::parser::make_WORD(result.value(), this->location); + } + else { REJECT; } +} +(0|{NATURAL}|{HEXADECIMAL})u8 { + if (auto result = parse_integer<std::uint8_t>(yytext, 0)) + { + return yy::parser::make_WORD8(result.value(), this->location); + } else { - return yy::parser::make_WORD(result, this->location); + REJECT; } } -[[:digit:]]+ { - std::int64_t result = strtoll(yytext, NULL, 10); - - if (errno == ERANGE) +(0|{NATURAL}|{HEXADECIMAL})u16 { + if (auto result = parse_integer<std::uint16_t>(yytext, 0)) + { + return yy::parser::make_WORD16(result.value(), this->location); + } + else { REJECT; } +} +(0|{NATURAL}|{HEXADECIMAL})u32 { + if (auto result = parse_integer<std::uint32_t>(yytext, 0)) + { + return yy::parser::make_WORD32(result.value(), this->location); + } else { - return yy::parser::make_INTEGER(result, this->location); + REJECT; } } -0[x|X]{HIGIT}+ { - std::uint64_t result = strtoull(yytext, NULL, 16); - - if (errno == ERANGE) +(0|{NATURAL}|{HEXADECIMAL})u64 { + if (auto result = parse_integer<std::uint64_t>(yytext, 0)) + { + return yy::parser::make_WORD64(result.value(), this->location); + } + else { REJECT; } +} +0|{NATURAL} { + if (auto result = parse_integer<std::int64_t>(yytext, 10)) + { + return yy::parser::make_INTEGER(result.value(), this->location); + } else { - return yy::parser::make_WORD(result, this->location); + REJECT; } } -0[b|B]{BIGIT}+ { - std::uint64_t result = strtoull(yytext + 2, NULL, 2); - - if (errno == ERANGE) +(0|{NATURAL})i8 { + if (auto result = parse_integer<std::int8_t>(yytext, 10)) + { + return yy::parser::make_INTEGER8(result.value(), this->location); + } + else { REJECT; } +} +(0|{NATURAL})i16 { + if (auto result = parse_integer<std::int16_t>(yytext, 10)) + { + return yy::parser::make_INTEGER16(result.value(), this->location); + } + else + { + REJECT; + } +} +(0|{NATURAL})i32 { + if (auto result = parse_integer<std::int32_t>(yytext, 10)) + { + return yy::parser::make_INTEGER32(result.value(), this->location); + } else { - return yy::parser::make_WORD(result, this->location); + REJECT; + } +} +(0|{NATURAL})i64 { + if (auto result = parse_integer<std::int64_t>(yytext, 10)) + { + return yy::parser::make_INTEGER64(result.value(), this->location); + } + else + { + REJECT; + } +} +0[bB]{BIGIT}+ { + if (auto result = parse_integer<std::uint64_t>(yytext + 2, 2)) + { + return yy::parser::make_WORD(result.value(), this->location); + } + else + { + REJECT; + } +} +0[bB]{BIGIT}+u8 { + if (auto result = parse_integer<std::uint8_t>(yytext + 2, 2)) + { + return yy::parser::make_WORD8(result.value(), this->location); + } + else + { + REJECT; + } +} +0[bB]{BIGIT}+u16 { + if (auto result = parse_integer<std::uint16_t>(yytext + 2, 2)) + { + return yy::parser::make_WORD16(result.value(), this->location); + } + else + { + REJECT; + } +} +0[bB]{BIGIT}+u32 { + if (auto result = parse_integer<std::uint32_t>(yytext + 2, 2)) + { + return yy::parser::make_WORD32(result.value(), this->location); + } + else + { + REJECT; + } +} +0[bB]{BIGIT}+u64 { + if (auto result = parse_integer<std::uint64_t>(yytext + 2, 2)) + { + return yy::parser::make_WORD64(result.value(), this->location); + } + else + { + REJECT; } } [[:digit:]]+\.[[:digit:]]+([eE][+-]?[[:digit:]]+)? { + errno = 0; double result = strtod(yytext, NULL); if (errno == ERANGE) @@ -234,6 +339,7 @@ to { } } [[:digit:]]+[eE][+-]?[[:digit:]]+ { + errno = 0; double result = strtod(yytext, NULL); if (errno == ERANGE) diff --git a/boot/materialization.cc b/boot/materialization.cc new file mode 100644 index 0000000..e055dfd --- /dev/null +++ b/boot/materialization.cc @@ -0,0 +1,62 @@ +/* Literal sign folding. + 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 +<http://www.gnu.org/licenses/>. */ + +#include "elna/boot/materialization.h" + +namespace elna::boot +{ + materialization_error::materialization_error(const source_position position) + : error(position) + { + } + + std::string materialization_error::what() const + { + return "Integer literal overflows"; + } + + materialization_visitor::materialization_visitor(const target_info& target) + : target(target) + { + } + + void materialization_visitor::visit(literal<integer_literal> *literal) + { + switch (literal->wants_signed) + { + using enum integer_sign; + case negative: + if (auto negated_literal = literal->value.negate()) + { + literal->value = negated_literal.value(); + } + else + { + add_error<materialization_error>(literal->position()); + } + break; + case unmarked: + if (!literal->value.fit_into(true, literal->value.size())) + { + add_error<materialization_error>(literal->position()); + } + break; + case _unsigned: + break; + } + } +} diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index 56ea399..ae6810d 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -69,23 +69,53 @@ namespace elna::boot return std::nullopt; } - const_qualifier_error::const_qualifier_error(const source_position position, kind error_kind) - : error(position), error_kind(error_kind) + name_analysis_error::name_analysis_error(const source_position position, payload_type payload) + : error(position), payload(std::move(payload)) { } - std::string const_qualifier_error::what() const + std::string name_analysis_error::what() const { - switch (error_kind) - { - using enum kind; - case array_position: - return "const must be written before the array size, not after"; - case duplicate: - return "Duplicate 'const' qualifier is not allowed"; - default: - __builtin_unreachable(); - } + return std::visit([](const auto& payload) -> std::string { + using T = std::decay_t<decltype(payload)>; + + if constexpr (std::is_same_v<T, not_initialized>) + { + return "All constants should be initialized"; + } + else if constexpr (std::is_same_v<T, kind>) + { + switch (payload) + { + using enum kind; + case array_position: + return "const must be written before the array size, not after"; + case duplicate: + return "Duplicate 'const' qualifier is not allowed"; + default: + __builtin_unreachable(); + } + } + }, this->payload); + } + + std::optional<std::pair<std::string, source_position>> name_analysis_error::note() const + { + return std::visit([](const auto& payload) -> std::optional<std::pair<std::string, source_position>> { + using T = std::decay_t<decltype(payload)>; + + if constexpr (std::is_same_v<T, not_initialized>) + { + auto position_span = source_position(payload.identifiers.front().position().start(), + payload.identifiers.back().position().end()); + + return std::make_optional(std::make_pair(join(payload.identifiers), position_span)); + } + else + { + return std::nullopt; + } + }, this->payload); } member_error::member_error(const source_position position, const std::string& name, @@ -164,23 +194,6 @@ namespace elna::boot return std::nullopt; } - not_initialized_error::not_initialized_error(const source_position position, std::vector<identifier> identifiers) - : error(position), identifiers(std::move(identifiers)) - { - } - - std::string not_initialized_error::what() const - { - return "All constants should be initialized"; - } - - std::optional<std::pair<std::string, source_position>> not_initialized_error::note() const - { - auto position_span = source_position(this->identifiers.front().position().start(), - this->identifiers.back().position().end()); - return std::make_pair(join(this->identifiers), position_span); - } - // Members of a constant aggregate are constant themselves. static type qualify_member_type(const type& element, const type& aggregate) { @@ -304,8 +317,8 @@ namespace elna::boot if (this->current_type.get<constant_type>() != nullptr) { - add_error<const_qualifier_error>(expression->position(), - const_qualifier_error::kind::duplicate); + add_error<name_analysis_error>(expression->position(), + name_analysis_error::kind::duplicate); } this->current_type = type(std::make_shared<constant_type>(this->current_type)); } @@ -317,8 +330,8 @@ namespace elna::boot if (array_base.get<constant_type>() != nullptr) { - add_error<const_qualifier_error>(expression->position(), - const_qualifier_error::kind::array_position); + add_error<name_analysis_error>(expression->position(), + name_analysis_error::kind::array_position); } expression->dimensions().accept(this); const auto size_constant = this->constant_evaluator.evaluate_index(expression->dimensions()); @@ -567,8 +580,8 @@ namespace elna::boot { auto position_span = source_position(declaration->identifiers.front().id().position().start(), declaration->identifiers.back().id().position().end()); - add_error<not_initialized_error>(position_span, - extract_identifiers(declaration->identifiers)); + add_error<name_analysis_error>(position_span, + name_analysis_error::not_initialized{ extract_identifiers(declaration->identifiers) }); } for (const identifier_definition& variable_identifier : declaration->identifiers) { @@ -881,11 +894,27 @@ namespace elna::boot { if (literal->value.is_signed()) { - literal->type_decoration = lookup_primitive_type("Int"); + if (literal->has_explicit_size) + { + literal->type_decoration = lookup_primitive_type( + "Int" + std::to_string(literal->value.size() * CHAR_BIT)); + } + else + { + literal->type_decoration = lookup_primitive_type("Int"); + } } else { - literal->type_decoration = lookup_primitive_type("Word"); + if (literal->has_explicit_size) + { + literal->type_decoration = lookup_primitive_type( + "Word" + std::to_string(literal->value.size() * CHAR_BIT)); + } + else + { + literal->type_decoration = lookup_primitive_type("Word"); + } } this->current_type = type(); } @@ -921,20 +950,8 @@ namespace elna::boot this->current_type = type(); } - declaration_visitor::declaration_visitor() - { - } - - void declaration_visitor::visit(import_declaration *) - { - } - void declaration_visitor::visit(unit *unit) { - for (import_declaration *const _import : unit->imports) - { - _import->accept(this); - } for (type_declaration *const type : unit->types) { type->accept(this); diff --git a/boot/parser.yy b/boot/parser.yy index a28b6cb..b48510b 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -78,8 +78,10 @@ along with GCC; see the file COPYING3. If not see %token <std::string> IDENTIFIER %token <std::string> TRAIT -%token <std::int64_t> INTEGER -%token <std::uint64_t> WORD +%token <std::pair<std::uint64_t, elna::boot::integer_sign>> INTEGER INTEGER64 WORD WORD64 +%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 <std::string> CHARACTER %token <std::string> STRING @@ -130,8 +132,7 @@ along with GCC; see the file COPYING3. If not see %type <std::vector<elna::boot::variable_declaration *>> variable_declarations variable_part; %type <elna::boot::type_expression *> type_expression; %type <std::vector<elna::boot::type_expression *>> type_expressions; -%type <elna::boot::expression *> expression operand simple_expression procedure_return; -%type <elna::boot::unary_expression *> unary_expression; +%type <elna::boot::expression *> expression operand simple_expression procedure_return unary_expression; %type <elna::boot::binary_expression *> binary_expression; %type <std::vector<elna::boot::expression *>> expressions actual_parameter_list; %type <elna::boot::designator_expression *> designator_expression; @@ -237,13 +238,86 @@ procedure_return: "return" expression { $$ = $2; } | "return" { $$ = nullptr; } literal: - INTEGER { $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), boot::integer_literal::from($1)); } - | WORD { $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), boot::integer_literal::from($1)); } - | FLOAT { $$ = new boot::literal<double>(boot::make_position(@$), $1); } - | BOOLEAN { $$ = new boot::literal<bool>(boot::make_position(@$), $1); } - | CHARACTER { $$ = new boot::literal<unsigned char>(boot::make_position(@$), $1.at(0)); } - | "nil" { $$ = new boot::literal<std::nullptr_t>(boot::make_position(@$), nullptr); } - | STRING { $$ = new boot::literal<std::string>(boot::make_position(@$), $1); } + INTEGER + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed, false); + } + | INTEGER8 + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed); + } + | INTEGER16 + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed); + } + | INTEGER32 + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed); + } + | INTEGER64 + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed); + } + | WORD + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed, false); + } + | WORD8 + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed); + } + | WORD16 + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed); + } + | WORD32 + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed); + } + | WORD64 + { + auto [magnitude, wants_signed] = $1; + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + boot::integer_literal::from(magnitude), wants_signed); + } + | FLOAT + { + $$ = new boot::literal<double>(boot::make_position(@$), $1, boot::integer_sign::unmarked); + } + | BOOLEAN + { + $$ = new boot::literal<bool>(boot::make_position(@$), $1, boot::integer_sign::_unsigned); + } + | CHARACTER + { + $$ = new boot::literal<unsigned char>(boot::make_position(@$), $1.at(0), boot::integer_sign::_unsigned); + } + | "nil" + { + $$ = new boot::literal<std::nullptr_t>(boot::make_position(@$), nullptr, boot::integer_sign::_unsigned); + } + | STRING + { + $$ = new boot::literal<std::string>(boot::make_position(@$), $1, boot::integer_sign::_unsigned); + } simple_expression: literal { $$ = $1; } | designator_expression { $$ = $1; } @@ -348,7 +422,25 @@ unary_expression: } | "-" operand { - $$ = new boot::unary_expression(boot::make_position(@$), $2, boot::unary_operator::minus); + auto operand = $2; + auto integer_operand = operand->is_literal() == nullptr + ? nullptr + : operand->is_literal()->is_a<boot::integer_literal>(); + + if (integer_operand != nullptr + && integer_operand->wants_signed == boot::integer_sign::unmarked) + { + // Rebuild the literal with a position spanning the minus and the + // integer token. + $$ = new boot::literal<boot::integer_literal>(boot::make_position(@$), + integer_operand->value, boot::integer_sign::negative, + integer_operand->has_explicit_size); + delete integer_operand; + } + else + { + $$ = new boot::unary_expression(boot::make_position(@$), operand, boot::unary_operator::minus); + } } | "+" operand { diff --git a/boot/result.cc b/boot/result.cc index 30b4576..15fe8b4 100644 --- a/boot/result.cc +++ b/boot/result.cc @@ -226,7 +226,7 @@ namespace elna::boot std::optional<integer_literal> integer_literal::neg() const { - if (!is_signed() || is_negative_minimum()) + if (!is_signed() || is_negative_minimum(bits())) { return std::nullopt; } @@ -237,32 +237,44 @@ namespace elna::boot return result; } + std::optional<integer_literal> integer_literal::negate() const + { + integer_literal result{ true, this->m_size }; + + mpz_set(result.raw, this->raw); + mpz_neg(result.raw, result.raw); + + return std::move(result).check(); + } + std::optional<integer_literal> 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<mp_bitcnt_t>(mpz_get_ui(that.raw))); + else + { + integer_literal result = *this; - return std::move(result).check(); + mpz_mul_2exp(result.raw, this->raw, static_cast<mp_bitcnt_t>(mpz_get_ui(that.raw))); + return std::make_optional(std::move(result).cast_to(is_signed(), size())); + } } std::optional<integer_literal> integer_literal::shr(const integer_literal& that) const { - integer_literal result = *this; - if (that >= bits()) { - mpz_set_si(result.raw, *this > 0 ? 0 : -1); + return std::nullopt; } else { + integer_literal result = *this; + mpz_fdiv_q_2exp(result.raw, this->raw, static_cast<mp_bitcnt_t>(mpz_get_ui(that.raw))); + return std::make_optional(std::move(result)); } - return std::make_optional(std::move(result)); } integer_literal integer_literal::operator|(const integer_literal& that) const @@ -298,7 +310,7 @@ namespace elna::boot mpz_com(result.raw, this->raw); - return result; + return std::move(result).cast_to(is_signed(), size()); } bool integer_literal::operator==(const integer_literal& that) const @@ -328,16 +340,49 @@ namespace elna::boot return *this; } - bool integer_literal::fit_into(const std::size_t target_size) + bool integer_literal::fit_into(bool target_signed, const std::size_t target_size) { - if (fits_in(target_size * CHAR_BIT)) + if (fits_in(target_signed, target_size * CHAR_BIT)) { + this->m_signed = target_signed; this->m_size = target_size; return true; } return false; } + bool integer_literal::fit_into(const std::size_t target_size) + { + return fit_into(is_signed(), target_size); + } + + integer_literal integer_literal::cast_to(bool target_signed, std::size_t target_size) const + { + integer_literal result{ target_signed, target_size }; + const std::size_t bits = target_size * CHAR_BIT; + + // Reduce to the unsigned residue in [0, 2^bits). This is the bit pattern + // resulting from truncating or zero-extending in two's complement, + // regardless of the source's sign. + mpz_fdiv_r_2exp(result.raw, this->raw, bits); + + // Since GMP doesn't store the value as 2's complement, if the value is + // signed it should be converted manually. + if (target_signed && mpz_tstbit(result.raw, bits - 1)) + { + mpz_t modulus; + + mpz_init(modulus); + mpz_set_ui(modulus, 1); + mpz_mul_2exp(modulus, modulus, bits); + + mpz_sub(result.raw, result.raw, modulus); + + mpz_clear(modulus); + } + return result; + } + bool integer_literal::is_signed() const { return this->m_signed; @@ -367,30 +412,27 @@ namespace elna::boot std::swap(lhs.m_size, rhs.m_size); } - bool integer_literal::fits_in(const std::size_t bits) const + bool integer_literal::fits_in(bool target_signed, const std::size_t bits) const { std::size_t required_bits = mpz_sizeinbase(this->raw, 2); - if (!is_negative() || !is_negative_minimum(bits)) + if (target_signed && !is_negative_minimum(bits)) { ++required_bits; // Add one bit for the sign. } - return required_bits <= bits && (!is_negative() || is_signed()); + return required_bits <= bits && (!is_negative() || target_signed); } std::optional<integer_literal> integer_literal::check() && { - return fits_in(bits()) ? std::make_optional(std::move(*this)) : std::nullopt; + return fits_in(is_signed(), 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()); + return is_negative() && mpz_scan1(this->raw, 0) == bits - 1; } std::size_t integer_literal::bits() const diff --git a/boot/symbol.cc b/boot/symbol.cc index 1cc652c..9854acc 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -184,8 +184,8 @@ namespace elna::boot }, payload); } - alias_type::alias_type(const std::string& name) - : name(name) + alias_type::alias_type(const std::string& name, type referent) + : name(name), referent(std::move(referent)) { } @@ -209,8 +209,8 @@ namespace elna::boot { } - primitive_type::primitive_type(const std::string& identifier) - : identifier(identifier) + primitive_type::primitive_type(const std::string& identifier, const type_properties& properties) + : identifier(identifier), properties(properties) { } @@ -282,16 +282,42 @@ namespace elna::boot return std::static_pointer_cast<variable_info>(shared_from_this()); } - std::shared_ptr<symbol_table> builtin_symbol_table() + static void builtin_integers(const std::shared_ptr<symbol_table>& symbols, + const std::array<type_properties, target_integer_count>& properties, + const std::string& integer_name) + { + for (std::size_t i = 1; i < properties.size(); ++i) + { + const std::size_t bit_size = properties[i].size * CHAR_BIT; + const std::string type_name = integer_name + std::to_string(bit_size); + const type variant_type = type(std::make_shared<primitive_type>(type_name, properties[i])); + + symbols->enter(type_name, std::make_shared<type_info>(variant_type)); + } + if (!symbols->contains(integer_name)) + { + auto variant_type = type(std::make_shared<primitive_type>(integer_name, properties.front())); + + symbols->enter(integer_name, std::make_shared<type_info>(variant_type)); + } + } + + std::shared_ptr<symbol_table> builtin_symbol_table(const target_info& target) { auto result = std::make_shared<symbol_table>(); - result->enter("Int", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Int")))); - result->enter("Word", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Word")))); - result->enter("Char", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Char")))); - result->enter("Pointer", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Pointer")))); - result->enter("Float", std::make_shared<type_info>(type(std::make_shared<primitive_type>("Float")))); - type const boolean = type(std::make_shared<primitive_type>("Bool")); + builtin_integers(result, target.int_properties, "Int"); + builtin_integers(result, target.word_properties, "Word"); + + result->enter("Char", + std::make_shared<type_info>(type(std::make_shared<primitive_type>("Char", target.char_properties)))); + + 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)))); + + const type boolean = type(std::make_shared<primitive_type>("Bool", target.bool_properties)); result->enter("Bool", std::make_shared<type_info>(boolean)); procedure_type assert_symbol{ procedure_type::return_t() }; @@ -426,15 +452,18 @@ namespace elna::boot bool is_numeric_type(const type& checked) { - return is_primitive_type(checked, "Int") - || is_primitive_type(checked, "Word") + return is_integral_type(checked) || is_primitive_type(checked, "Float"); } bool is_integral_type(const type& checked) { - return is_primitive_type(checked, "Int") - || is_primitive_type(checked, "Word"); + if (auto primitive_checked = checked.get<primitive_type>()) + { + return primitive_checked->identifier.starts_with("Int") + || primitive_checked->identifier.starts_with("Word"); + } + return false; } bool is_discrete_type(const type& checked) diff --git a/boot/type_check.cc b/boot/type_check.cc index 78e975a..b1739d9 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -1038,11 +1038,11 @@ namespace elna::boot bool narrowed{ false }; if (expression->value.is_signed()) { - narrowed = expression->value.fit_into(target.int_properties.size); + narrowed = expression->value.fit_into(target.int_properties.front().size); } else { - narrowed = expression->value.fit_into(target.word_properties.size); + narrowed = expression->value.fit_into(target.word_properties.front().size); } if (!narrowed) { diff --git a/boot/validation.cc b/boot/validation.cc index b29381a..0c03fd0 100644 --- a/boot/validation.cc +++ b/boot/validation.cc @@ -55,6 +55,38 @@ namespace elna::boot { } + void validation_visitor::visit(assign_statement *) + { + } + + void validation_visitor::visit(if_statement *) + { + } + + void validation_visitor::visit(while_statement *) + { + } + + void validation_visitor::visit(repeat_statement *) + { + } + + void validation_visitor::visit(for_statement *) + { + } + + void validation_visitor::visit(defer_statement *) + { + } + + void validation_visitor::visit(empty_statement *) + { + } + + void validation_visitor::visit(procedure_call *) + { + } + void validation_visitor::visit(unit *unit) { for (procedure_declaration *procedure : unit->procedures) @@ -73,17 +105,31 @@ namespace elna::boot { auto procedure = this->bag.lookup(declaration->identifier.name())->is_procedure(); this->bag.enter(procedure->scope); - } - walking_visitor::visit(declaration); - if (declaration->body.has_value()) - { + for (auto *statement : declaration->body.value().entry_point) + { + statement->accept(this); + } this->bag.leave(); } } void validation_visitor::visit(case_statement *statement) { - walking_visitor::visit(statement); + for (const switch_case& case_block : statement->cases) + { + for (auto *block_statement : case_block.statements) + { + block_statement->accept(this); + } + } + if (statement->alternative != nullptr) + { + for (auto *block_statement : *statement->alternative) + { + block_statement->accept(this); + } + } + std::unordered_map<constant_value, source_position, constant_value_hash> seen; for (const auto& case_block : statement->cases) { |
