aboutsummaryrefslogtreecommitdiff
path: root/boot
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-08-13 22:55:06 +0200
committerEugen Wissner <belka@caraus.de>2026-08-13 22:55:06 +0200
commitbf7416a3ef9f0b787dcaebf816f3f3e6e385ff42 (patch)
tree26359c705133dbd8e98f06483008a720d0dae9d7 /boot
parentae29de96c94e5c582f4424804464cdd29bf2a013 (diff)
downloadelna-bf7416a3ef9f0b787dcaebf816f3f3e6e385ff42.tar.gz
Implement fixed-size integers
Diffstat (limited to 'boot')
-rw-r--r--boot/dependency.cc15
-rw-r--r--boot/evaluator.cc70
-rw-r--r--boot/lexer.ll146
-rw-r--r--boot/materialization.cc62
-rw-r--r--boot/name_analysis.cc117
-rw-r--r--boot/parser.yy116
-rw-r--r--boot/result.cc86
-rw-r--r--boot/symbol.cc59
-rw-r--r--boot/type_check.cc4
-rw-r--r--boot/validation.cc56
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)
{