aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2026-07-19 17:09:40 +0200
committerEugen Wissner <belka@caraus.de>2026-07-19 18:30:21 +0200
commitcdbc1695a93910df5729779de31912a3b4b4172b (patch)
tree43a4c5a192df7060f8a36c75d339be7ac5722f72
parentcd2ee0c97348444779a155909b7ec78d89cfd2e0 (diff)
downloadelna-cdbc1695a93910df5729779de31912a3b4b4172b.tar.gz
Support unary plus operation
-rw-r--r--boot/evaluator.cc4
-rw-r--r--boot/name_analysis.cc8
-rw-r--r--boot/parser.yy4
-rw-r--r--boot/symbol.cc27
-rw-r--r--boot/type_check.cc64
-rw-r--r--gcc/gcc/elna-generic.cc24
-rw-r--r--include/elna/boot/ast.h3
-rw-r--r--include/elna/boot/symbol.h28
-rw-r--r--include/elna/boot/type_check.h18
-rw-r--r--testsuite/fail_compilation/unary_minus_on_record.elna9
-rw-r--r--testsuite/fail_compilation/unary_negation_on_record.elna9
-rw-r--r--testsuite/fail_compilation/unary_plus_on_record.elna9
-rw-r--r--testsuite/runnable/unary_plus.elna8
13 files changed, 189 insertions, 26 deletions
diff --git a/boot/evaluator.cc b/boot/evaluator.cc
index f782dd6..1acadd3 100644
--- a/boot/evaluator.cc
+++ b/boot/evaluator.cc
@@ -185,6 +185,10 @@ namespace elna::boot
return std::nullopt;
}, operand.value());
}
+ if (subject.operation() == unary_operator::plus)
+ {
+ return operand;
+ }
return std::nullopt;
}
diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc
index 5255878..b13c043 100644
--- a/boot/name_analysis.cc
+++ b/boot/name_analysis.cc
@@ -538,14 +538,8 @@ namespace elna::boot
{
type resolved = resolve_underlying_type(trait->type_decoration);
bool is_enum = resolved.get<enumeration_type>() != nullptr;
- bool is_integral = false;
- if (auto prim = resolved.get<primitive_type>())
- {
- is_integral = prim->identifier == "Int" || prim->identifier == "Word"
- || prim->identifier == "Bool" || prim->identifier == "Char";
- }
- if (!is_enum && !is_integral)
+ if (!is_enum && !is_discrete_type(resolved))
{
add_error<unsupported_trait_type_error>(trait->name,
trait->type_decoration);
diff --git a/boot/parser.yy b/boot/parser.yy
index 1dd42a8..aad5769 100644
--- a/boot/parser.yy
+++ b/boot/parser.yy
@@ -351,6 +351,10 @@ unary_expression:
{
$$ = new boot::unary_expression(boot::make_position(@$), $2, boot::unary_operator::minus);
}
+ | "+" operand
+ {
+ $$ = new boot::unary_expression(boot::make_position(@$), $2, boot::unary_operator::plus);
+ }
expressions:
expression "," expressions
{
diff --git a/boot/symbol.cc b/boot/symbol.cc
index 6ad89b7..7b45612 100644
--- a/boot/symbol.cc
+++ b/boot/symbol.cc
@@ -420,6 +420,33 @@ namespace elna::boot
return false;
}
+ bool is_numeric_type(const type& checked)
+ {
+ type resolved = resolve_underlying_type(checked);
+
+ return is_primitive_type(resolved, "Int")
+ || is_primitive_type(resolved, "Word")
+ || is_primitive_type(resolved, "Float");
+ }
+
+ bool is_integral_type(const type& checked)
+ {
+ type resolved = resolve_underlying_type(checked);
+
+ return is_primitive_type(resolved, "Int")
+ || is_primitive_type(resolved, "Word");
+ }
+
+ bool is_discrete_type(const type& checked)
+ {
+ type resolved = resolve_underlying_type(checked);
+
+ return is_primitive_type(resolved, "Int")
+ || is_primitive_type(resolved, "Word")
+ || is_primitive_type(resolved, "Bool")
+ || is_primitive_type(resolved, "Char");
+ }
+
bool is_any_pointer_type(const type& checked)
{
return checked.get<pointer_type>() != nullptr
diff --git a/boot/type_check.cc b/boot/type_check.cc
index b310e34..d9f3071 100644
--- a/boot/type_check.cc
+++ b/boot/type_check.cc
@@ -200,6 +200,35 @@ namespace elna::boot
+ "' does not support trait '#" + trait_name + "'";
}
+ unary_operation_error::unary_operation_error(const source_position position,
+ type actual, unary_operator op)
+ : error(position), actual(actual), op(op)
+ {
+ }
+
+ char unary_operation_error::unary_operator_symbol(unary_operator op)
+ {
+ switch (op)
+ {
+ case unary_operator::reference:
+ return '@';
+ case unary_operator::negation:
+ return '~';
+ case unary_operator::minus:
+ return '-';
+ case unary_operator::plus:
+ return '+';
+ }
+ __builtin_unreachable();
+ }
+
+ std::string unary_operation_error::what() const
+ {
+ return "Type '" + actual.to_string()
+ + "' cannot be used with unary '"
+ + unary_operator_symbol(op) + "'";
+ }
+
/*
* Whether the type itself is constant or has a constant member at any
* nesting level, so that values of this type cannot be reassigned as a
@@ -597,4 +626,39 @@ namespace elna::boot
}
}
}
+
+ void type_analysis_visitor::visit(unary_expression *expression)
+ {
+ walking_visitor::visit(expression);
+
+ auto op = expression->operation();
+ type resolved = resolve_underlying_type(expression->operand().type_decoration);
+
+ if (op == unary_operator::plus)
+ {
+ if (!is_numeric_type(resolved))
+ {
+ add_error<unary_operation_error>(expression->position(),
+ expression->operand().type_decoration, op);
+ }
+ }
+ else if (op == unary_operator::minus)
+ {
+ if (!is_primitive_type(resolved, "Int")
+ && !is_primitive_type(resolved, "Float"))
+ {
+ add_error<unary_operation_error>(expression->position(),
+ expression->operand().type_decoration, op);
+ }
+ }
+ else if (op == unary_operator::negation)
+ {
+ if (!is_primitive_type(resolved, "Bool")
+ && !is_integral_type(resolved))
+ {
+ add_error<unary_operation_error>(expression->position(),
+ expression->operand().type_decoration, op);
+ }
+ }
+ }
}
diff --git a/gcc/gcc/elna-generic.cc b/gcc/gcc/elna-generic.cc
index ae04be9..b13fd2f 100644
--- a/gcc/gcc/elna-generic.cc
+++ b/gcc/gcc/elna-generic.cc
@@ -659,36 +659,24 @@ namespace elna::gcc
this->current_expression = build1_loc(location, TRUTH_NOT_EXPR,
boolean_type_node, this->current_expression);
}
- else if (is_integral_type(operand_type))
+ else
{
this->current_expression = build1_loc(location, BIT_NOT_EXPR,
operand_type, this->current_expression);
}
- else
- {
- error_at(location, "type '%s' cannot be negated",
- print_type(operand_type).c_str());
- this->current_expression = error_mark_node;
- }
break;
}
case boot::unary_operator::minus:
{
tree operand_type = TREE_TYPE(this->current_expression);
- if (is_integral_type(operand_type))
- {
- this->current_expression = fold_build1_loc(location, NEGATE_EXPR,
- operand_type, this->current_expression);
- }
- else
- {
- error_at(location, "type '%s' cannot be negated",
- print_type(operand_type).c_str());
- this->current_expression = error_mark_node;
- }
+ this->current_expression = fold_build1_loc(location, NEGATE_EXPR,
+ operand_type, this->current_expression);
break;
}
+ case boot::unary_operator::plus:
+ // Identity: operand already in current_expression.
+ break;
}
}
diff --git a/include/elna/boot/ast.h b/include/elna/boot/ast.h
index 9d978a3..30bc7c3 100644
--- a/include/elna/boot/ast.h
+++ b/include/elna/boot/ast.h
@@ -51,7 +51,8 @@ namespace elna::boot
{
reference,
negation,
- minus
+ minus,
+ plus
};
class variable_declaration;
diff --git a/include/elna/boot/symbol.h b/include/elna/boot/symbol.h
index 216021e..194c1cd 100644
--- a/include/elna/boot/symbol.h
+++ b/include/elna/boot/symbol.h
@@ -475,6 +475,34 @@ namespace elna::boot
bool is_primitive_type(const type& checked, const std::string& name);
/**
+ * Checks whether the given type is a numeric primitive type
+ * (\c Int, \c Word, or \c Float).
+ *
+ * \param checked The type to check.
+ * \return Whether the type is a numeric primitive.
+ */
+ bool is_numeric_type(const type& checked);
+
+ /**
+ * Checks whether the given type is an integral primitive type
+ * (\c Int or \c Word).
+ *
+ * \param checked The type to check.
+ * \return Whether the type is an integral primitive.
+ */
+ bool is_integral_type(const type& checked);
+
+ /**
+ * Checks whether the given type is a discrete primitive type
+ * (\c Int, \c Word, \c Bool, or \c Char), i.e. a type with a
+ * finite range of ordered values suitable for \c \#min/\c \#max.
+ *
+ * \param checked The type to check.
+ * \return Whether the type is a discrete primitive.
+ */
+ bool is_discrete_type(const type& checked);
+
+ /**
* Checks whether the given type is a pointer type.
*
* A pointer type is considered to be a variable pointer, a procedure
diff --git a/include/elna/boot/type_check.h b/include/elna/boot/type_check.h
index 5b6ed1c..8c7dd80 100644
--- a/include/elna/boot/type_check.h
+++ b/include/elna/boot/type_check.h
@@ -175,6 +175,23 @@ namespace elna::boot
};
/**
+ * A unary operator is applied to an unsupported type.
+ */
+ class unary_operation_error : public error
+ {
+ type actual;
+ unary_operator op;
+
+ static char unary_operator_symbol(unary_operator op);
+
+ public:
+ unary_operation_error(const source_position position, type actual,
+ unary_operator op);
+
+ std::string what() const override;
+ };
+
+ /**
* Chain of responsibility for type compatibility checks.
*
* Populate \c ctx with pre-resolved types, then call \c run().
@@ -237,5 +254,6 @@ namespace elna::boot
void visit(case_statement *statement) override;
void visit(record_constructor_expression *expression) override;
void visit(array_constructor_expression *expression) override;
+ void visit(unary_expression *expression) override;
};
}
diff --git a/testsuite/fail_compilation/unary_minus_on_record.elna b/testsuite/fail_compilation/unary_minus_on_record.elna
new file mode 100644
index 0000000..cfd76a2
--- /dev/null
+++ b/testsuite/fail_compilation/unary_minus_on_record.elna
@@ -0,0 +1,9 @@
+type
+ R = record
+ x: Int
+ end
+
+proc f(r: R): Int
+return -r (* @Error Type 'R' cannot be used with unary '-' *)
+
+end.
diff --git a/testsuite/fail_compilation/unary_negation_on_record.elna b/testsuite/fail_compilation/unary_negation_on_record.elna
new file mode 100644
index 0000000..370f04d
--- /dev/null
+++ b/testsuite/fail_compilation/unary_negation_on_record.elna
@@ -0,0 +1,9 @@
+type
+ R = record
+ x: Int
+ end
+
+proc f(r: R): Int
+return ~r (* @Error Type 'R' cannot be used with unary '~' *)
+
+end.
diff --git a/testsuite/fail_compilation/unary_plus_on_record.elna b/testsuite/fail_compilation/unary_plus_on_record.elna
new file mode 100644
index 0000000..7289873
--- /dev/null
+++ b/testsuite/fail_compilation/unary_plus_on_record.elna
@@ -0,0 +1,9 @@
+type
+ R = record
+ x: Int
+ end
+
+proc f(r: R): Int
+return +r (* @Error Type 'R' cannot be used with unary '\+' *)
+
+end.
diff --git a/testsuite/runnable/unary_plus.elna b/testsuite/runnable/unary_plus.elna
new file mode 100644
index 0000000..2bc69f3
--- /dev/null
+++ b/testsuite/runnable/unary_plus.elna
@@ -0,0 +1,8 @@
+proc f(x: Int): Int
+return +x
+
+begin
+ assert(+3 = 3);
+ assert(f(5) = 5);
+ assert(f(-7) = -7)
+end.