diff options
Diffstat (limited to 'boot')
| -rw-r--r-- | boot/lexer.ll | 3 | ||||
| -rw-r--r-- | boot/name_analysis.cc | 112 | ||||
| -rw-r--r-- | boot/parser.yy | 48 | ||||
| -rw-r--r-- | boot/symbol.cc | 8 | ||||
| -rw-r--r-- | boot/type_check.cc | 44 |
5 files changed, 131 insertions, 84 deletions
diff --git a/boot/lexer.ll b/boot/lexer.ll index 17fe0ca..e8dde7c 100644 --- a/boot/lexer.ll +++ b/boot/lexer.ll @@ -184,6 +184,9 @@ program { #\[ { return yy::parser::make_GENERIC_OPEN(this->location); } +#\{ { + return yy::parser::make_ATTRIBUTE_OPEN(this->location); +} (0|{NATURAL})u|{HEXADECIMAL} { if (auto result = parse_integer<std::uint64_t>(yytext, 0)) { diff --git a/boot/name_analysis.cc b/boot/name_analysis.cc index caff18b..4a1a0ca 100644 --- a/boot/name_analysis.cc +++ b/boot/name_analysis.cc @@ -69,17 +69,21 @@ namespace elna::boot { using enum kind; case unknown: - return "Attribute '#" + this->name + "' not declared"; + return "Attribute '" + this->name + "' not declared"; case duplicate: - return "Attribute '#" + this->name + "' specified more than once"; + return "Attribute '" + this->name + "' specified more than once"; case unsupported: - return "Attribute '#" + this->name + "' is not supported in this position"; + return "Attribute '" + this->name + "' is not supported in this position"; case argument_count: - return "Attribute '#" + this->name + "' takes exactly one argument"; + return "Attribute '" + this->name + "' takes exactly one argument"; case not_constant: - return "Attribute '#" + this->name + "' requires a constant expression"; + return "Attribute '" + this->name + "' requires a constant expression"; case invalid_alignment: return "Alignment must be a power of two"; + case unexpected_arguments: + return "Attribute '" + this->name + "' takes no arguments"; + case missing_parameter: + return "Attribute '" + this->name + "' requires at least one parameter"; default: __builtin_unreachable(); } @@ -320,7 +324,7 @@ namespace elna::boot } std::pair<procedure_type, std::vector<std::string>> resolving_visitor::build_procedure( - procedure_type_expression& expression) + procedure_type_expression& expression, const bool variadic_supported) { const procedure_type::return_t result_return = build_return_type(expression.return_type); std::pair<procedure_type, std::vector<std::string>> result_type{ @@ -332,65 +336,70 @@ namespace elna::boot for (const auto& parameter_name : parameter_names) { - evaluate_alignment(parameter_name.attributes, false); + evaluate_attributes<>(parameter_name.attributes); result_type.first.parameters.push_back(parameter_type); result_type.second.push_back(parameter_name.name()); } } + result_type.first.variadic = variadic_supported + ? evaluate_attributes<attribute_kind::varargs>(expression.attributes).variadic + : evaluate_attributes<>(expression.attributes).variadic; + + if (result_type.first.variadic && result_type.first.parameters.empty()) + { + add_error<attribute_error>(expression.position(), "varargs", + attribute_error::kind::missing_parameter); + } return result_type; } - std::optional<std::size_t> resolving_visitor::evaluate_alignment( - const std::vector<attribute>& attributes, const bool supported) + std::optional<attribute_kind> resolving_visitor::parse_attribute(const std::string& name) { - std::optional<std::size_t> result; + if (name == "aligned") + { + return attribute_kind::aligned; + } + if (name == "varargs") + { + return attribute_kind::varargs; + } + return std::nullopt; + } - for (const attribute& written : attributes) + void resolving_visitor::evaluate_alignment(const attribute& written, std::optional<std::size_t>& alignment) + { + if (written.arguments().size() != 1) { - if (written.name().name() != "aligned") - { - add_error<attribute_error>(written.name().position(), written.name().name(), - attribute_error::kind::unknown); - } - else if (!supported) - { - add_error<attribute_error>(written.name().position(), written.name().name(), - attribute_error::kind::unsupported); - } - else if (written.arguments().size() != 1) - { - add_error<attribute_error>(written.name().position(), written.name().name(), - attribute_error::kind::argument_count); - } - else if (result.has_value()) - { - add_error<attribute_error>(written.name().position(), written.name().name(), - attribute_error::kind::duplicate); - } - else if (auto value = this->constant_evaluator.evaluate_index(*written.arguments().front())) + add_error<attribute_error>(written.name().position(), written.name().name(), + attribute_error::kind::argument_count); + } + else if (alignment.has_value()) + { + add_error<attribute_error>(written.name().position(), written.name().name(), + attribute_error::kind::duplicate); + } + else if (auto value = this->constant_evaluator.evaluate_index(*written.arguments().front())) + { + if (value.value() == 0 || (value.value() & (value.value() - 1)) != 0) { - if (value.value() == 0 || (value.value() & (value.value() - 1)) != 0) - { - add_error<attribute_error>(written.arguments().front()->position(), - written.name().name(), attribute_error::kind::invalid_alignment); - } - else - { - result = value; - } + add_error<attribute_error>(written.arguments().front()->position(), + written.name().name(), attribute_error::kind::invalid_alignment); } else { - add_error<attribute_error>(written.arguments().front()->position(), - written.name().name(), attribute_error::kind::not_constant); + alignment = value; } } - return result; + else + { + add_error<attribute_error>(written.arguments().front()->position(), + written.name().name(), attribute_error::kind::not_constant); + } } ordered_map<field_info> resolving_visitor::build_composite_type( const std::vector<field_declaration>& fields, - ordered_map<field_origin>& field_names, const type& aggregate, const bool supported) + ordered_map<field_origin>& field_names, const type& aggregate) { ordered_map<field_info> result; @@ -401,7 +410,7 @@ namespace elna::boot for (const identifier_definition& field_name : field.first) { const std::optional<std::size_t> alignment = - evaluate_alignment(field_name.attributes, supported); + evaluate_attributes<attribute_kind::aligned>(field_name.attributes).alignment; auto [existing, inserted] = field_names.insert(field_name.name(), field_origin{ .position = field.second->position(), .base_type = type() }); @@ -522,7 +531,8 @@ namespace elna::boot auto variable_symbol = register_variable(variable_identifier.name(), variable_type, declaration->position(), declaration->is_extern); variable_symbol->exported = variable_identifier.exported(); - variable_symbol->alignment = evaluate_alignment(variable_identifier.attributes, true); + variable_symbol->alignment = + evaluate_attributes<attribute_kind::aligned>(variable_identifier.attributes).alignment; variable_symbol->value = computed; } } @@ -690,7 +700,8 @@ namespace elna::boot ordered_map<field_origin> field_names; collect_field_names(result_type->base, field_names); result_type->fields = build_composite_type(expression->fields, field_names, type(result_type)); - result_type->alignment = evaluate_alignment(expression->attributes, true); + result_type->alignment = + evaluate_attributes<attribute_kind::aligned>(expression->attributes).alignment; this->current_type = type(result_type); } @@ -698,7 +709,7 @@ namespace elna::boot void resolving_visitor::visit(procedure_type_expression *expression) { std::shared_ptr<procedure_type> const result_type = - std::make_shared<procedure_type>(std::move(build_procedure(*expression).first)); + std::make_shared<procedure_type>(std::move(build_procedure(*expression, true).first)); this->current_type = type(result_type); } @@ -1418,7 +1429,8 @@ namespace elna::boot // is why the body sees them too. const std::shared_ptr<symbol_table> scope = this->bag.enter(); const std::vector<type> type_parameters = enter_parameters(declaration->parameters); - auto [heading, parameter_names] = build_procedure(declaration->heading()); + auto [heading, parameter_names] = + build_procedure(declaration->heading(), !declaration->body.has_value()); std::shared_ptr<procedure_info> info; if (declaration->body.has_value()) diff --git a/boot/parser.yy b/boot/parser.yy index 21ff827..cc0a46c 100644 --- a/boot/parser.yy +++ b/boot/parser.yy @@ -88,7 +88,7 @@ along with GCC; see the file COPYING3. If not see %token <std::string> STRING %token <bool> BOOLEAN %token LEFT_PAREN "(" RIGHT_PAREN ")" LEFT_SQUARE "[" RIGHT_SQUARE "]" - LEFT_BRACE "{" RIGHT_BRACE "}" GENERIC_OPEN "#[" + LEFT_BRACE "{" RIGHT_BRACE "}" GENERIC_OPEN "#[" ATTRIBUTE_OPEN "#{" %token ASSIGNMENT ":=" EXCLAMATION "!" ARROW "->" AT "@" HAT "^" @@ -190,9 +190,9 @@ identifier_definition: IDENTIFIER "*" { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), true); } | IDENTIFIER { $$ = std::make_unique<boot::identifier_definition>($1, boot::make_position(@1), false); } attribute: - TRAIT "(" required_expressions ")" + "#{" IDENTIFIER optional_expressions "}" { - $$ = std::make_unique<boot::attribute>(boot::identifier($1, boot::make_position(@1)), $3); + $$ = std::make_unique<boot::attribute>(boot::identifier($2, boot::make_position(@2)), $3); } attributes: %empty {} @@ -202,17 +202,10 @@ attributes: $$.emplace($$.cbegin(), std::move(*$1)); } attributed_identifier: - attributes identifier_definition + identifier_definition attributes { - $$ = $2; - $$->attributes = $1; - - // An empty rule is located where the previous token ended, which - // would push the declaration's position back over it. - if ($$->attributes.empty()) - { - @$ = @2; - } + $$ = $1; + $$->attributes = $2; } identifier_definitions: attributed_identifier "," identifier_definitions @@ -225,8 +218,11 @@ return_declaration: %empty {} | "->" "!" { $$ = boot::procedure_type_expression::return_t(std::monostate{}); } | "->" type_expression { $$ = boot::procedure_type_expression::return_t($2); } -procedure_heading: "(" field_declarations ")" return_declaration - { $$ = std::make_unique<boot::procedure_type_expression>(boot::make_position(@$), $2, $4); } +procedure_heading: "(" field_declarations ")" attributes return_declaration + { + $$ = std::make_unique<boot::procedure_type_expression>(boot::make_position(@$), $2, $5); + $$->attributes = $4; + } procedure_declaration: "proc" identifier_definition generic_parameters procedure_heading procedure_body { @@ -616,33 +612,21 @@ type_expression: { $$ = new boot::pointer_type_expression(boot::make_position(@$), $2); } - | attributes "record" field_declarations "end" + | "record" attributes field_declarations "end" { - std::vector<boot::attribute> written = $1; - - if (written.empty()) - { - @$ = @2 + @4; - } auto *record = new boot::record_type_expression(boot::make_position(@$), $3); - record->attributes = std::move(written); + record->attributes = $2; $$ = record; } - | attributes "record" "(" name_reference ")" field_declarations "end" + | "record" "(" name_reference ")" attributes field_declarations "end" { - std::vector<boot::attribute> written = $1; - - if (written.empty()) - { - @$ = @2 + @7; - } - boot::named_expression *reference = $4; + boot::named_expression *reference = $3; auto *record = new boot::record_type_expression(boot::make_position(@$), $6, boot::identifier(reference->name, reference->position()), std::move(reference->arguments)); - record->attributes = std::move(written); + record->attributes = $5; delete reference; $$ = record; } diff --git a/boot/symbol.cc b/boot/symbol.cc index 0814217..466715b 100644 --- a/boot/symbol.cc +++ b/boot/symbol.cc @@ -117,7 +117,8 @@ namespace elna::boot auto right_procedure = resolved_that.get<procedure_type>(); return right_procedure != nullptr && left_procedure->return_type == right_procedure->return_type - && left_procedure->parameters == right_procedure->parameters; + && left_procedure->parameters == right_procedure->parameters + && left_procedure->variadic == right_procedure->variadic; } if (auto left_parameter = resolved_this.get<parameter_type>()) { @@ -190,6 +191,10 @@ namespace elna::boot { std::string result = "proc(" + join(payload->parameters) + ")"; + if (payload->variadic) + { + result += " #{varargs}"; + } if (payload->return_type.no_return) { result += " -> !"; @@ -578,6 +583,7 @@ namespace elna::boot } auto result = std::make_shared<procedure_type>(std::move(result_return)); + result->variadic = procedure->variadic; result->parameters.reserve(procedure->parameters.size()); for (const type& parameter_type : procedure->parameters) { diff --git a/boot/type_check.cc b/boot/type_check.cc index 8847693..5cdd2a2 100644 --- a/boot/type_check.cc +++ b/boot/type_check.cc @@ -19,6 +19,7 @@ along with GCC; see the file COPYING3. If not see #include "elna/boot/evaluator.h" #include "elna/boot/name_analysis.h" +#include <climits> #include <ranges> #include <utility> @@ -286,6 +287,9 @@ namespace elna::boot case not_addressable: return "Expression of type '" + this->actual.to_string() + "' is not addressable"; + case variadic_argument: + return "Type '" + this->actual.to_string() + + "' cannot be passed as a variadic argument"; default: __builtin_unreachable(); } @@ -1337,6 +1341,34 @@ namespace elna::boot procedure.parameters, bindings); } + type type_analysis_visitor::promote_variadic_argument(const expression& argument) + { + const type resolved = resolve_underlying_type(argument.type_decoration); + + if (resolved.get<array_type>() != nullptr || resolved.get<slice_type>() != nullptr) + { + add_error<type_requirement_error>(argument.position(), argument.type_decoration, + type_requirement_error::kind::variadic_argument); + return type(); + } + if (is_primitive_type(resolved, "Single")) + { + return this->bag.lookup("Double")->is_type()->symbol; + } + if (is_discrete_type(resolved)) + { + const std::optional<type_properties> properties = get_type_properties(resolved, this->target); + + if (properties.has_value() && properties->size < this->target.c_int_properties.size) + { + const std::size_t bit_size = this->target.c_int_properties.size * CHAR_BIT; + + return this->bag.lookup("Int" + std::to_string(bit_size))->is_type()->symbol; + } + } + return argument.type_decoration; + } + void type_analysis_visitor::visit(procedure_call *call) { const named_expression *reference = call->callable().is_named(); @@ -1388,7 +1420,17 @@ namespace elna::boot ++argument_iterator; ++type_iterator; } - if (call->arguments.size() != procedure->parameters.size()) + while (procedure->variadic && argument_iterator != std::cend(call->arguments)) + { + if (inferred == nullptr) + { + (*argument_iterator)->accept(this); + } + call->variadic_types.push_back(promote_variadic_argument(**argument_iterator)); + ++argument_iterator; + } + if (call->arguments.size() < procedure->parameters.size() + || (!procedure->variadic && call->arguments.size() > procedure->parameters.size())) { if (auto *procedure_name = call->callable().is_named()) { |
