aboutsummaryrefslogtreecommitdiff
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
parentae29de96c94e5c582f4424804464cdd29bf2a013 (diff)
downloadelna-bf7416a3ef9f0b787dcaebf816f3f3e6e385ff42.tar.gz
Implement fixed-size integers
-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
-rw-r--r--gcc/Make-lang.in1
-rw-r--r--gcc/gcc/elna-builtins.cc45
-rw-r--r--gcc/gcc/elna-tree.cc23
-rw-r--r--gcc/gcc/elna1.cc8
-rw-r--r--include/elna/boot/ast.h75
-rw-r--r--include/elna/boot/dependency.h6
-rw-r--r--include/elna/boot/driver.h16
-rw-r--r--include/elna/boot/materialization.h41
-rw-r--r--include/elna/boot/name_analysis.h40
-rw-r--r--include/elna/boot/result.h162
-rw-r--r--include/elna/boot/symbol.h19
-rw-r--r--include/elna/boot/validation.h11
-rw-r--r--include/elna/gcc/elna-builtins.h2
-rw-r--r--include/elna/gcc/elna-tree.h2
-rw-r--r--testsuite/fail_compilation/negative_integer_literal_overflow.elna4
-rw-r--r--testsuite/runnable/fixed_int_max.elna6
-rw-r--r--testsuite/runnable/fixed_int_min.elna6
-rw-r--r--testsuite/runnable/fixed_word_max.elna6
28 files changed, 957 insertions, 247 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)
{
diff --git a/gcc/Make-lang.in b/gcc/Make-lang.in
index 690d047..bf1893d 100644
--- a/gcc/Make-lang.in
+++ b/gcc/Make-lang.in
@@ -58,6 +58,7 @@ elna_OBJS = \
elna/symbol.o \
elna/result.o \
elna/validation.o \
+ elna/materialization.o \
$(END)
elna1$(exeext): attribs.o $(elna_OBJS) $(BACKEND) $(LIBDEPS)
diff --git a/gcc/gcc/elna-builtins.cc b/gcc/gcc/elna-builtins.cc
index 7c4545e..4f3db65 100644
--- a/gcc/gcc/elna-builtins.cc
+++ b/gcc/gcc/elna-builtins.cc
@@ -25,6 +25,43 @@ along with GCC; see the file COPYING3. If not see
namespace elna::gcc
{
+ static constexpr boot::type_properties get_host_numeric_properties(tree node)
+ {
+ return boot::type_properties{
+ .size = static_cast<std::size_t>(TYPE_PRECISION(node) / BITS_PER_UNIT),
+ .alignment = TYPE_ALIGN_UNIT(node)
+ };
+ }
+
+ const boot::target_info& get_host_target()
+ {
+ static const boot::target_info info = boot::target_info{
+ .int_properties = {
+ get_host_numeric_properties(elna_int_type_node),
+ get_host_numeric_properties(intQI_type_node),
+ get_host_numeric_properties(intHI_type_node),
+ get_host_numeric_properties(intSI_type_node),
+ get_host_numeric_properties(intDI_type_node)
+ },
+ .word_properties = {
+ get_host_numeric_properties(elna_word_type_node),
+ get_host_numeric_properties(unsigned_intQI_type_node),
+ get_host_numeric_properties(unsigned_intHI_type_node),
+ get_host_numeric_properties(unsigned_intSI_type_node),
+ get_host_numeric_properties(unsigned_intDI_type_node)
+ },
+ .pointer_properties = get_host_numeric_properties(ptr_type_node),
+ .char_properties = get_host_numeric_properties(elna_char_type_node),
+ .float_properties = get_host_numeric_properties(elna_float_type_node),
+ .bool_properties = {
+ .size = static_cast<std::size_t>(
+ (TYPE_PRECISION(elna_bool_type_node) + BITS_PER_UNIT - 1) / BITS_PER_UNIT),
+ .alignment = TYPE_ALIGN_UNIT(elna_bool_type_node)
+ }
+ };
+ return info;
+ }
+
void init_ttree()
{
elna_int_type_node = ptrdiff_type_node;
@@ -56,7 +93,15 @@ namespace elna::gcc
auto builtin_table = std::make_shared<symbol_table>();
declare_builtin_type(builtin_table, "Int", elna_int_type_node);
+ declare_builtin_type(builtin_table, "Int8", intQI_type_node);
+ declare_builtin_type(builtin_table, "Int16", intHI_type_node);
+ declare_builtin_type(builtin_table, "Int32", intSI_type_node);
+ declare_builtin_type(builtin_table, "Int64", intDI_type_node);
declare_builtin_type(builtin_table, "Word", elna_word_type_node);
+ declare_builtin_type(builtin_table, "Word8", unsigned_intQI_type_node);
+ declare_builtin_type(builtin_table, "Word16", unsigned_intHI_type_node);
+ declare_builtin_type(builtin_table, "Word32", unsigned_intSI_type_node);
+ declare_builtin_type(builtin_table, "Word64", unsigned_intDI_type_node);
declare_builtin_type(builtin_table, "Char", elna_char_type_node);
declare_builtin_type(builtin_table, "Bool", elna_bool_type_node);
declare_builtin_type(builtin_table, "Pointer", elna_pointer_type_node);
diff --git a/gcc/gcc/elna-tree.cc b/gcc/gcc/elna-tree.cc
index 05690d6..688fc4a 100644
--- a/gcc/gcc/elna-tree.cc
+++ b/gcc/gcc/elna-tree.cc
@@ -28,29 +28,6 @@ along with GCC; see the file COPYING3. If not see
namespace elna::gcc
{
- const elna::boot::target_info& get_host_target()
- {
- static const elna::boot::target_info info = []{
- elna::boot::target_info target_info;
-
- target_info.int_properties.size = TYPE_PRECISION(elna_int_type_node) / BITS_PER_UNIT;
- target_info.int_properties.alignment = TYPE_ALIGN_UNIT(elna_int_type_node);
- target_info.word_properties.size = TYPE_PRECISION(elna_word_type_node) / BITS_PER_UNIT;
- target_info.word_properties.alignment = TYPE_ALIGN_UNIT(elna_word_type_node);
- target_info.pointer_properties.size = TYPE_PRECISION(ptr_type_node) / BITS_PER_UNIT;
- target_info.pointer_properties.alignment = TYPE_ALIGN_UNIT(ptr_type_node);
- target_info.char_properties.size = TYPE_PRECISION(elna_char_type_node) / BITS_PER_UNIT;
- target_info.char_properties.alignment = TYPE_ALIGN_UNIT(elna_char_type_node);
- target_info.float_properties.size = TYPE_PRECISION(elna_float_type_node) / BITS_PER_UNIT;
- target_info.float_properties.alignment = TYPE_ALIGN_UNIT(elna_float_type_node);
- target_info.bool_properties.size = (TYPE_PRECISION(elna_bool_type_node) + BITS_PER_UNIT - 1) / BITS_PER_UNIT;
- target_info.bool_properties.alignment = TYPE_ALIGN_UNIT(elna_bool_type_node);
-
- return target_info;
- }();
- return info;
- }
-
bool is_integral_type(tree type)
{
gcc_assert(TYPE_P(type));
diff --git a/gcc/gcc/elna1.cc b/gcc/gcc/elna1.cc
index 4eb01cc..180c839 100644
--- a/gcc/gcc/elna1.cc
+++ b/gcc/gcc/elna1.cc
@@ -102,7 +102,8 @@ static elna::boot::dependency elna_parse_file(dependency_state& state, const cha
fatal_error(UNKNOWN_LOCATION, "Cannot open filename %s: %m", filename);
}
const elna::gcc::linemap_guard guard(filename);
- elna::boot::dependency outcome = elna::boot::read_source(entry_point);
+ elna::boot::dependency outcome = elna::boot::read_source(entry_point,
+ elna::gcc::get_host_target());
elna::boot::symbol_bag outcome_bag{ std::move(outcome.unresolved), state.globals };
@@ -139,7 +140,8 @@ static elna::boot::dependency elna_parse_file(dependency_state& state, const cha
static void elna_langhook_parse_file()
{
- dependency_state state{ elna::gcc::builtin_symbol_table() };
+ elna::boot::target_info target = elna::gcc::get_host_target();
+ dependency_state state{ elna::gcc::builtin_symbol_table(), target };
for (unsigned int i = 0; i < num_in_fnames; i++)
{
@@ -149,7 +151,7 @@ static void elna_langhook_parse_file()
{
linemap_add(line_table, LC_ENTER, 0, in_fnames[i], 1);
elna::gcc::generic_visitor generic_visitor{ state.custom,
- state.find(in_fnames[i])->second, elna::gcc::get_host_target() };
+ state.find(in_fnames[i])->second, target };
outcome.tree->accept(&generic_visitor);
linemap_add(line_table, LC_LEAVE, 0, nullptr, 0);
}
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index 1ea17fb..2645026 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -62,6 +62,55 @@ namespace elna::boot
plus
};
+ enum class integer_sign
+ {
+ _unsigned,
+ unmarked,
+ negative
+ };
+
+ template<typename T>
+ struct literal_type_id;
+
+ template<>
+ struct literal_type_id<integer_literal>
+ {
+ static constexpr int value = 1;
+ };
+
+ template<>
+ struct literal_type_id<double>
+ {
+ static constexpr int value = 2;
+ };
+
+ template<>
+ struct literal_type_id<bool>
+ {
+ static constexpr int value = 3;
+ };
+
+ template<>
+ struct literal_type_id<unsigned char>
+ {
+ static constexpr int value = 4;
+ };
+
+ template<>
+ struct literal_type_id<std::nullptr_t>
+ {
+ static constexpr int value = 5;
+ };
+
+ template<>
+ struct literal_type_id<std::string>
+ {
+ static constexpr int value = 6;
+ };
+
+ template<typename T>
+ concept literal_type = requires { literal_type_id<T>::value; };
+
class variable_declaration;
class procedure_declaration;
class type_declaration;
@@ -95,7 +144,7 @@ namespace elna::boot
class dereference_expression;
class designator_expression;
class literal_expression;
- template<typename T>
+ template<literal_type T>
class literal;
class defer_statement;
class empty_statement;
@@ -488,6 +537,16 @@ namespace elna::boot
{
public:
literal_expression *is_literal() override;
+
+ virtual int tag() const = 0;
+
+ template<literal_type T>
+ literal<T> *is_a()
+ {
+ return tag() == literal_type_id<T>::value
+ ? static_cast<literal<T > *>(this)
+ : nullptr;
+ }
};
/**
@@ -893,14 +952,17 @@ namespace elna::boot
~unit() override;
};
- template<typename T>
+ template<literal_type T>
class literal : public literal_expression
{
public:
T value;
+ const integer_sign wants_signed;
+ const bool has_explicit_size;
- literal(const source_position position, const T& value)
- : node(position), value(value)
+ literal(const source_position position, const T& value, const integer_sign wants_signed,
+ const bool has_explicit_size = true)
+ : node(position), value(value), wants_signed(wants_signed), has_explicit_size(has_explicit_size)
{
}
@@ -908,6 +970,11 @@ namespace elna::boot
{
visitor->visit(this);
}
+
+ int tag() const override
+ {
+ return literal_type_id<T>::value;
+ }
};
class defer_statement : public statement
diff --git a/include/elna/boot/dependency.h b/include/elna/boot/dependency.h
index b3c1155..854c661 100644
--- a/include/elna/boot/dependency.h
+++ b/include/elna/boot/dependency.h
@@ -37,7 +37,7 @@ namespace elna::boot
dependency() = default;
};
- dependency read_source(std::istream& entry_point);
+ dependency read_source(std::istream& entry_point, const target_info& target);
std::filesystem::path build_path(const std::vector<std::string>& segments);
error_list analyze_semantics(std::unique_ptr<unit>& tree, symbol_bag& bag,
const target_info& target);
@@ -54,8 +54,8 @@ namespace elna::boot
using iterator = std::unordered_map<std::filesystem::path, symbol_bag>::iterator;
using const_iterator = std::unordered_map<std::filesystem::path, symbol_bag>::const_iterator;
- explicit dependency_state(T custom)
- : globals(builtin_symbol_table()), custom(custom)
+ explicit dependency_state(T custom, const target_info& target)
+ : globals(builtin_symbol_table(target)), custom(custom)
{
}
diff --git a/include/elna/boot/driver.h b/include/elna/boot/driver.h
index 60d40fb..2503e39 100644
--- a/include/elna/boot/driver.h
+++ b/include/elna/boot/driver.h
@@ -54,4 +54,20 @@ namespace elna::boot
*/
void normalize_newlines(std::string& string);
std::optional<std::string> escape_string(const char *escape);
+
+ template<typename T>
+ std::optional<std::pair<std::make_unsigned_t<T>, integer_sign>>
+ parse_integer(const char *text, const int base = 10)
+ {
+ using unsigned_t = std::make_unsigned_t<T>;
+ errno = 0;
+ std::uint64_t result = strtoull(text, nullptr, base);
+
+ if (errno == ERANGE || result > std::numeric_limits<unsigned_t>::max())
+ {
+ return std::nullopt;
+ }
+ return std::make_pair(static_cast<unsigned_t>(result),
+ std::is_signed_v<T> ? integer_sign::unmarked : integer_sign::_unsigned);
+ }
}
diff --git a/include/elna/boot/materialization.h b/include/elna/boot/materialization.h
new file mode 100644
index 0000000..21a217d
--- /dev/null
+++ b/include/elna/boot/materialization.h
@@ -0,0 +1,41 @@
+/* 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/>. */
+
+#pragma once
+
+#include "elna/boot/ast.h"
+
+namespace elna::boot
+{
+ class materialization_error final : public error
+ {
+ public:
+ materialization_error(const source_position position);
+
+ std::string what() const override;
+ };
+
+ class materialization_visitor final : public walking_visitor, public error_container
+ {
+ const target_info& target;
+
+ public:
+ explicit materialization_visitor(const target_info& target);
+
+ void visit(literal<integer_literal> *literal) override;
+ };
+}
diff --git a/include/elna/boot/name_analysis.h b/include/elna/boot/name_analysis.h
index f6663ea..9e10fab 100644
--- a/include/elna/boot/name_analysis.h
+++ b/include/elna/boot/name_analysis.h
@@ -36,7 +36,13 @@ namespace elna::boot
class declaration_error final : public error
{
public:
- enum class kind { undeclared_type, undeclared_trait, undeclared_symbol, local_export };
+ enum class kind
+ {
+ undeclared_type,
+ undeclared_trait,
+ undeclared_symbol,
+ local_export
+ };
struct redefinition
{
std::optional<source_position> original;
@@ -57,17 +63,27 @@ namespace elna::boot
* \c const qualifier used incorrectly — wrong position or
* duplicate.
*/
- class const_qualifier_error final : public error
+ class name_analysis_error final : public error
{
public:
- enum class kind { array_position, duplicate };
+ enum class kind
+ {
+ array_position,
+ duplicate
+ };
+ struct not_initialized
+ {
+ std::vector<identifier> identifiers;
+ };
+ using payload_type = std::variant<not_initialized, kind>;
- const_qualifier_error(const source_position position, kind error_kind);
+ name_analysis_error(const source_position position, payload_type payload);
+ std::optional<std::pair<std::string, source_position>> note() const override;
std::string what() const override;
private:
- kind error_kind;
+ payload_type payload;
};
/**
@@ -95,17 +111,6 @@ namespace elna::boot
payload_type payload;
};
- class not_initialized_error final : public error
- {
- std::vector<identifier> identifiers;
-
- public:
- not_initialized_error(const source_position position, std::vector<identifier> identifiers);
-
- std::string what() const override;
- std::optional<std::pair<std::string, source_position>> note() const override;
- };
-
/**
* Origin of a field in a composite type.
*/
@@ -183,9 +188,6 @@ namespace elna::boot
public:
forward_table unresolved;
- explicit declaration_visitor();
-
- void visit(import_declaration *) override;
void visit(unit *unit) override;
void visit(type_declaration *declaration) override;
void visit(procedure_declaration *declaration) override;
diff --git a/include/elna/boot/result.h b/include/elna/boot/result.h
index ee73f33..03d589e 100644
--- a/include/elna/boot/result.h
+++ b/include/elna/boot/result.h
@@ -273,40 +273,117 @@ namespace elna::boot
integer_literal(const integer_literal& that);
~integer_literal();
+ /**
+ * \param that Summand.
+ * \return Sum of \c this and \p that, or \c nullopt on overflow.
+ */
std::optional<integer_literal> add(const integer_literal& that) const;
+
+ /**
+ * \param that Subtrahend.
+ * \return Difference of \c this and \p that, or \c nullopt on overflow.
+ */
std::optional<integer_literal> sub(const integer_literal& that) const;
+
+ /**
+ * \return Product of \c this and \p that, or \c nullopt on overflow.
+ */
std::optional<integer_literal> mul(const integer_literal& that) const;
+
+ /**
+ * \param that Factor.
+ * \return Quotient of \c this and \p that, or \c nullopt if \p that is zero.
+ */
std::optional<integer_literal> div(const integer_literal& that) const;
+
+ /**
+ * \param that Divisor.
+ * \return Remainder of \c this divided by \p that, or \c nullopt if \p that is zero.
+ */
std::optional<integer_literal> mod(const integer_literal& that) const;
+
+ /**
+ * \return Arithmetic negation, or \c nullopt if unsigned or overflowing.
+ */
std::optional<integer_literal> neg() const;
+
+ /**
+ * \return Signed negation of an unsigned magnitude, or \c nullopt on overflow.
+ */
+ std::optional<integer_literal> negate() const;
+
+ /**
+ * \param that Bit count.
+ * \return \c this shifted left by \p that bits, wrapping within the current size.
+ */
std::optional<integer_literal> shl(const integer_literal& that) const;
+
+ /**
+ * \param that Bit count.
+ * \return \c this shifted right by \p that bits, or \c nullopt if the shift is out of range.
+ */
std::optional<integer_literal> shr(const integer_literal& that) const;
+ /**
+ * \param that Operand.
+ * \return Bitwise OR of \c this and \p that.
+ */
integer_literal operator|(const integer_literal& that) const;
+
+ /**
+ * \param that Operand.
+ * \return Bitwise AND of \c this and \p that.
+ */
integer_literal operator&(const integer_literal& that) const;
+
+ /**
+ * \param that Operand.
+ * \return Bitwise XOR of \c this and \p that.
+ */
integer_literal operator^(const integer_literal& that) const;
+
+ /**
+ * \return Bitwise complement of \c this.
+ */
integer_literal operator~() const;
+ /**
+ * \param that Comparand.
+ * \return Whether \c this and \p that hold the same value.
+ */
bool operator==(const integer_literal& that) const;
- std::weak_ordering operator<=>(const integer_literal& that) const;
+
+ /// \overload
template<typename U>
bool operator==(U that) const
requires(is_unsigned_v<U> && sizeof(U) <= sizeof(unsigned long int))
{
return mpz_cmp_ui(this->raw, that) == 0;
}
+
+ /// \overload
template<typename U>
bool operator==(U that) const
requires(is_signed_v<U> && sizeof(U) <= sizeof(signed long int))
{
return mpz_cmp_si(this->raw, that) == 0;
}
+
+ /**
+ * \param that Comparand.
+ * \return Ordering of \c this relative to \p that.
+ */
+ std::weak_ordering operator<=>(const integer_literal& that) const;
+
+ /// \overload
template<typename U>
std::weak_ordering operator<=>(U that) const
requires(is_unsigned_v<U> && sizeof(U) <= sizeof(unsigned long int))
{
return mpz_cmp_ui(this->raw, that) <=> 0;
}
+
+ /// \overload
template<typename U>
std::weak_ordering operator<=>(U that) const
requires(is_signed_v<U> && sizeof(U) <= sizeof(signed long int))
@@ -317,10 +394,21 @@ namespace elna::boot
integer_literal& operator=(integer_literal&& that) noexcept;
integer_literal& operator=(const integer_literal& that);
+ /// \return Whether the literal is signed.
bool is_signed() const;
+
+ /// \return Storage size in bytes.
std::size_t size() const;
+
+ /**
+ * \param base Base.
+ * \return String representation in the given \p base.
+ */
std::string to_string(const std::uint8_t base = 10U) const;
+ /// \return Whether the stored value is negative.
+ bool is_negative() const;
+
/**
* Exports the stored value as a host \p T.
*
@@ -359,10 +447,29 @@ namespace elna::boot
* returned.
*
* \param target_size Size of the target type in bytes.
+ * \param target_signed Signedness of the target type.
* \return Whether the value fits and has been changed.
*/
+ bool fit_into(bool target_signed, const std::size_t target_size);
+
+ /// \overload
bool fit_into(const std::size_t target_size);
+ /**
+ * Cast the value to the given size and signedness, discarding bits if
+ * needed and reinterpreting bits on sign change.
+ *
+ * \param target_signed Result signedness.
+ * \param target_size Result size.
+ * \return Cast result.
+ */
+ integer_literal cast_to(bool target_signed, std::size_t target_size) const;
+
+ /**
+ * \tparam T Initializer type.
+ * \param initial Initial literal value.
+ * \return A literal constructed from the host value \p initial.
+ */
template<typename T>
static integer_literal from(T initial)
requires is_integral<T>
@@ -374,6 +481,29 @@ namespace elna::boot
return result;
}
+ /**
+ * Constructs a literal of \p size bytes from \p initial inheriting its
+ * signedness.
+ *
+ * \tparam T Initializer type.
+ * \return A literal of \p size bytes constructed from \p initial, or \c nullopt on overflow.
+ */
+ template<typename T>
+ static std::optional<integer_literal> from(std::size_t size, T initial)
+ requires is_integral<T>
+ {
+ integer_literal result{ std::is_signed_v<T>, size };
+
+ mpz_import(result.raw, 1, 1, sizeof(T), 0, 0, &initial);
+
+ return std::move(result).check();
+ }
+
+ /**
+ * \param lhs Left hand side.
+ * \param lhs Right hand side.
+ * Swaps the contents of \p lhs and \p rhs.
+ */
friend void swap(integer_literal& lhs, integer_literal& rhs) noexcept;
private:
@@ -382,11 +512,9 @@ namespace elna::boot
mpz_t raw;
integer_literal(bool is_signed, std::size_t size);
- bool fits_in(const std::size_t bits) const;
+ bool fits_in(bool starget_signed, const std::size_t bits) const;
std::optional<integer_literal> check() &&;
- bool is_negative() const;
bool is_negative_minimum(const std::size_t bits) const;
- bool is_negative_minimum() const;
std::size_t bits() const;
template<typename T>
@@ -474,9 +602,7 @@ namespace elna::boot
}
}
- /**
- * \overload
- */
+ /// \overload
const_iterator find(const key_type& key) const
{
auto search_result = this->index_map.find(key);
@@ -509,9 +635,7 @@ namespace elna::boot
return { this->payload.begin() + insert_result.first->second, insert_result.second };
}
- /**
- * \overload
- */
+ /// \overload
std::pair<iterator, bool> insert(const key_type& key, mapped_type&& value)
{
auto insert_result = this->index_map.emplace(key, this->payload.size());
@@ -532,9 +656,7 @@ namespace elna::boot
return this->payload.begin();
}
- /**
- * \overload
- */
+ /// \overload
const_iterator begin() const
{
return this->payload.cbegin();
@@ -550,9 +672,7 @@ namespace elna::boot
return this->payload.end();
}
- /**
- * \overload
- */
+ /// \overload
const_iterator end() const
{
return this->payload.cend();
@@ -577,9 +697,7 @@ namespace elna::boot
return this->payload[this->index_map.find(key)->second].second;
}
- /**
- * \overload
- */
+ /// \overload
const mapped_type& operator[](const key_type& key) const
{
return this->payload[this->index_map.find(key)->second].second;
@@ -605,13 +723,15 @@ namespace elna::boot
std::size_t alignment;
};
+ constexpr std::size_t target_integer_count = 5;
+
/**
* Target machine information, populated by the compiler backend glue layer.
*/
struct target_info
{
- type_properties int_properties;
- type_properties word_properties;
+ std::array<type_properties, target_integer_count> int_properties;
+ std::array<type_properties, target_integer_count> word_properties;
type_properties pointer_properties;
type_properties char_properties;
type_properties float_properties;
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index bd07a7e..f048016 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -123,7 +123,7 @@ namespace elna::boot
const std::string name;
type referent;
- explicit alias_type(const std::string& name);
+ explicit alias_type(const std::string& name, type referent = type());
};
struct pointer_type
@@ -158,8 +158,9 @@ namespace elna::boot
struct primitive_type
{
const std::string identifier;
+ const type_properties properties;
- explicit primitive_type(const std::string& identifier);
+ primitive_type(const std::string& identifier, const type_properties& properties);
};
struct record_type
@@ -243,9 +244,7 @@ namespace elna::boot
return this->entries.begin();
}
- /**
- * \overload
- */
+ /// \overload
const_iterator begin() const
{
return this->entries.cbegin();
@@ -261,9 +260,7 @@ namespace elna::boot
return this->entries.end();
}
- /**
- * \overload
- */
+ /// \overload
const_iterator end() const
{
return this->entries.cend();
@@ -405,7 +402,7 @@ namespace elna::boot
std::shared_ptr<variable_info> is_variable() override;
};
- std::shared_ptr<symbol_table> builtin_symbol_table();
+ std::shared_ptr<symbol_table> builtin_symbol_table(const target_info& target);
/**
* Symbol bag contains:
@@ -520,9 +517,7 @@ namespace elna::boot
*/
type resolve_underlying_type(const type& alias);
- /**
- * \overload
- */
+ /// \overload
type resolve_underlying_type(const std::shared_ptr<alias_type>& alias);
/**
diff --git a/include/elna/boot/validation.h b/include/elna/boot/validation.h
index 4a256fa..171ac1d 100644
--- a/include/elna/boot/validation.h
+++ b/include/elna/boot/validation.h
@@ -49,7 +49,7 @@ namespace elna::boot
* Validates:
* - case label uniqueness
*/
- class validation_visitor final : public walking_visitor, public error_container
+ class validation_visitor final : public empty_visitor, public error_container
{
symbol_bag& bag;
evaluator constant_evaluator;
@@ -60,5 +60,14 @@ namespace elna::boot
void visit(unit *unit) override;
void visit(procedure_declaration *declaration) override;
void visit(case_statement *statement) override;
+
+ void visit(assign_statement *) override;
+ void visit(if_statement *) override;
+ void visit(while_statement *) override;
+ void visit(repeat_statement *) override;
+ void visit(for_statement *) override;
+ void visit(defer_statement *) override;
+ void visit(empty_statement *) override;
+ void visit(procedure_call *) override;
};
}
diff --git a/include/elna/gcc/elna-builtins.h b/include/elna/gcc/elna-builtins.h
index 846a8db..9f24b00 100644
--- a/include/elna/gcc/elna-builtins.h
+++ b/include/elna/gcc/elna-builtins.h
@@ -27,6 +27,8 @@ along with GCC; see the file COPYING3. If not see
namespace elna::gcc
{
+ const elna::boot::target_info& get_host_target();
+
void init_ttree();
std::shared_ptr<symbol_table> builtin_symbol_table();
diff --git a/include/elna/gcc/elna-tree.h b/include/elna/gcc/elna-tree.h
index 8f7e306..504b14c 100644
--- a/include/elna/gcc/elna-tree.h
+++ b/include/elna/gcc/elna-tree.h
@@ -60,8 +60,6 @@ namespace elna::gcc
tree build_slice(tree slice_type, tree ptr, tree length);
tree build_enumeration_type(const std::vector<std::string>& members);
- const elna::boot::target_info& get_host_target();
-
tree extract_constant(tree expression);
tree constant_to_tree(const boot::constant_value& value,
const std::shared_ptr<symbol_table>& symbols, tree type = NULL_TREE);
diff --git a/testsuite/fail_compilation/negative_integer_literal_overflow.elna b/testsuite/fail_compilation/negative_integer_literal_overflow.elna
new file mode 100644
index 0000000..7db4888
--- /dev/null
+++ b/testsuite/fail_compilation/negative_integer_literal_overflow.elna
@@ -0,0 +1,4 @@
+var
+ a: Int8 := -200i8 (* @Error Integer literal overflows *)
+
+end.
diff --git a/testsuite/runnable/fixed_int_max.elna b/testsuite/runnable/fixed_int_max.elna
new file mode 100644
index 0000000..0564dd6
--- /dev/null
+++ b/testsuite/runnable/fixed_int_max.elna
@@ -0,0 +1,6 @@
+begin
+ assert(#max(Int8) = 127i8);
+ assert(#max(Int16) = 32767i16);
+ assert(#max(Int32) = 2147483647i32);
+ assert(#max(Int64) = 9223372036854775807i64)
+end.
diff --git a/testsuite/runnable/fixed_int_min.elna b/testsuite/runnable/fixed_int_min.elna
new file mode 100644
index 0000000..de83d1a
--- /dev/null
+++ b/testsuite/runnable/fixed_int_min.elna
@@ -0,0 +1,6 @@
+begin
+ assert(#min(Int8) = -128i8);
+ assert(#min(Int16) = -32768i16);
+ assert(#min(Int32) = -2147483648i32);
+ assert(#min(Int64) = -9223372036854775808i64)
+end.
diff --git a/testsuite/runnable/fixed_word_max.elna b/testsuite/runnable/fixed_word_max.elna
new file mode 100644
index 0000000..76026a2
--- /dev/null
+++ b/testsuite/runnable/fixed_word_max.elna
@@ -0,0 +1,6 @@
+begin
+ assert(#max(Word8) = 255u8);
+ assert(#max(Word16) = 65535u16);
+ assert(#max(Word32) = 4294967295u32);
+ assert(#max(Word64) = 18446744073709551615u64)
+end.