From 435aa61fe41ef18bdbe62aebb9c070e1936d7fe0 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Wed, 17 Dec 2025 15:46:51 +0300 Subject: [PATCH] Implement array types & array values --- libs/ast/include/pslang/ast/array.hpp | 21 +++ libs/ast/include/pslang/ast/expression.hpp | 5 +- libs/ast/include/pslang/ast/print.hpp | 2 + libs/ast/source/print.cpp | 18 +++ .../include/pslang/interpreter/value.hpp | 12 +- .../include/pslang/interpreter/value_fwd.hpp | 12 ++ libs/interpreter/source/eval.cpp | 140 +++++++++++++++++- libs/interpreter/source/value.cpp | 17 +++ libs/parser/CMakeLists.txt | 2 +- libs/parser/rules/pslang.l | 2 + libs/parser/rules/pslang.y | 26 +++- libs/type/include/pslang/type/array.hpp | 21 +++ libs/type/include/pslang/type/type.hpp | 6 +- libs/type/include/pslang/type/type_fwd.hpp | 1 + libs/type/source/print.cpp | 6 + 15 files changed, 273 insertions(+), 18 deletions(-) create mode 100644 libs/ast/include/pslang/ast/array.hpp create mode 100644 libs/interpreter/include/pslang/interpreter/value_fwd.hpp create mode 100644 libs/type/include/pslang/type/array.hpp diff --git a/libs/ast/include/pslang/ast/array.hpp b/libs/ast/include/pslang/ast/array.hpp new file mode 100644 index 0000000..05e9b89 --- /dev/null +++ b/libs/ast/include/pslang/ast/array.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +namespace pslang::ast +{ + + struct array + { + std::vector elements; + }; + + struct array_access + { + expression_ptr array; + expression_ptr index; + }; + +} diff --git a/libs/ast/include/pslang/ast/expression.hpp b/libs/ast/include/pslang/ast/expression.hpp index b23db1e..3b82150 100644 --- a/libs/ast/include/pslang/ast/expression.hpp +++ b/libs/ast/include/pslang/ast/expression.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include namespace pslang::ast @@ -29,7 +30,9 @@ namespace pslang::ast unary_operation, binary_operation, cast_operation, - function_call + function_call, + array, + array_access >; struct expression diff --git a/libs/ast/include/pslang/ast/print.hpp b/libs/ast/include/pslang/ast/print.hpp index 2cfbc8b..cb3eada 100644 --- a/libs/ast/include/pslang/ast/print.hpp +++ b/libs/ast/include/pslang/ast/print.hpp @@ -30,6 +30,8 @@ namespace pslang::ast void print(std::ostream & out, binary_operation const & node, print_options const & options = {}); void print(std::ostream & out, cast_operation const & node, print_options const & options = {}); void print(std::ostream & out, function_call const & node, print_options const & options = {}); + void print(std::ostream & out, array const & node, print_options const & options = {}); + void print(std::ostream & out, array_access const & node, print_options const & options = {}); void print(std::ostream & out, expression_ptr const & node, print_options const & options = {}); void print(std::ostream & out, assignment const & node, print_options const & options = {}); void print(std::ostream & out, variable_declaration const & node, print_options const & options = {}); diff --git a/libs/ast/source/print.cpp b/libs/ast/source/print.cpp index 187dfac..71e4587 100644 --- a/libs/ast/source/print.cpp +++ b/libs/ast/source/print.cpp @@ -152,6 +152,24 @@ namespace pslang::ast print(out, argument, child(options)); } + void print(std::ostream & out, array const & node, print_options const & options) + { + put_indent(out, options); + out << "array"; + newline(out); + for (auto const & element : node.elements) + print(out, element, child(options)); + } + + void print(std::ostream & out, array_access const & node, print_options const & options) + { + put_indent(out, options); + out << "array access"; + newline(out); + print(out, node.array, child(options)); + print(out, node.index, child(options)); + } + void print(std::ostream & out, expression_ptr const & node, print_options const & options) { std::visit([&](auto const & value){ print(out, value, options); }, *node); diff --git a/libs/interpreter/include/pslang/interpreter/value.hpp b/libs/interpreter/include/pslang/interpreter/value.hpp index a980aa5..33bf59f 100644 --- a/libs/interpreter/include/pslang/interpreter/value.hpp +++ b/libs/interpreter/include/pslang/interpreter/value.hpp @@ -1,10 +1,12 @@ #pragma once #include +#include #include #include #include +#include namespace pslang::interpreter { @@ -53,9 +55,17 @@ namespace pslang::interpreter using primitive_value_impl::primitive_value_impl; }; + struct array_value + { + // Can't infer type from elements in case of zero-sized array + type::type_ptr element_type; + std::vector elements; + }; + using value_impl = std::variant< unit_value, - primitive_value + primitive_value, + array_value >; struct value diff --git a/libs/interpreter/include/pslang/interpreter/value_fwd.hpp b/libs/interpreter/include/pslang/interpreter/value_fwd.hpp new file mode 100644 index 0000000..7b18a6f --- /dev/null +++ b/libs/interpreter/include/pslang/interpreter/value_fwd.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace pslang::interpreter +{ + + struct value; + + using value_ptr = std::shared_ptr; + +} diff --git a/libs/interpreter/source/eval.cpp b/libs/interpreter/source/eval.cpp index 25d6a43..e392380 100644 --- a/libs/interpreter/source/eval.cpp +++ b/libs/interpreter/source/eval.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace pslang::interpreter { @@ -102,12 +103,14 @@ namespace pslang::interpreter throw std::runtime_error("Identifier \"" + identifier.name + "\" is not defined"); } - value unary_operation_impl(ast::unary_operation_type type, unit_value const &) + template + value unary_operation_impl(ast::unary_operation_type type, Value const & value) { std::ostringstream os; os << "Cannot apply unary operator \""; print(os, type); - os << "\" to a value of type unit"; + os << "\" to a value of type "; + type::print(os, type_of(value)); throw std::runtime_error(os.str()); } @@ -260,12 +263,15 @@ namespace pslang::interpreter throw std::runtime_error(os.str()); } - value binary_operation_impl_same_type(ast::binary_operation_type type, unit_value const &, value const & arg2) + template + value binary_operation_impl_same_type(ast::binary_operation_type type, Value const & arg1, value const & arg2) { std::ostringstream os; os << "Cannot apply binary operator \""; print(os, type); - os << "\" to values of type unit and "; + os << "\" to values of type "; + type::print(os, type_of(arg1)); + os << " and "; type::print(os, type_of(arg2)); throw std::runtime_error(os.str()); } @@ -311,6 +317,14 @@ namespace pslang::interpreter throw std::runtime_error("Cannot cast unit type to anything"); } + value cast_impl(array_value const & value, type::type const & type) + { + if (type::equal(type, type_of(value))) + return value; + + throw std::runtime_error("Cannot cast array type to anything"); + } + template value cast_impl(primitive_value_base const & value, type::unit_type const &) { @@ -421,6 +435,124 @@ namespace pslang::interpreter throw std::runtime_error("Function \"" + function_call.name + "\" is not defined"); } + value eval_impl(context & context, ast::array const & array) + { + if (array.elements.empty()) + throw std::runtime_error("Internal error: array ast node cannot have zero elements"); + + type::type_ptr element_type; + std::vector elements; + for (std::size_t i = 0; i < array.elements.size(); ++i) + { + auto element = std::make_unique(eval(context, array.elements[i])); + if (i == 0) + element_type = std::make_unique(type_of(*element)); + else + { + auto new_type = type_of(*element); + if (!type::equal(*element_type, new_type)) + { + std::ostringstream os; + os << "Error forming array: inferred element type is "; + type::print(os, *element_type); + os << " but element #" << i << " type is "; + type::print(os, new_type); + throw std::runtime_error(os.str()); + } + } + + elements.push_back(std::move(element)); + } + + return array_value{.element_type = std::move(element_type), .elements = std::move(elements)}; + } + + value eval_impl(context & context, ast::array_access const & array_access) + { + auto array = eval(context, array_access.array); + auto index = eval(context, array_access.index); + + if (auto avalue = std::get_if(&array)) + { + std::optional index_unsigned; + std::optional index_signed; + + if (auto pvalue = std::get_if(&index)) + { + if (auto i8 = std::get_if(pvalue)) + { + index_signed = i8->value; + } + else if (auto u8 = std::get_if(pvalue)) + { + index_unsigned = u8->value; + } + else if (auto i16 = std::get_if(pvalue)) + { + index_signed = i16->value; + } + else if (auto u16 = std::get_if(pvalue)) + { + index_unsigned = u16->value; + } + else if (auto i32 = std::get_if(pvalue)) + { + index_signed = i32->value; + } + else if (auto u32 = std::get_if(pvalue)) + { + index_unsigned = u32->value; + } + else if (auto i64 = std::get_if(pvalue)) + { + index_signed = i64->value; + } + else if (auto u64 = std::get_if(pvalue)) + { + index_unsigned = u64->value; + } + } + + if (!index_signed && !index_unsigned) + { + std::ostringstream os; + os << "Cannot index into an array with an expression of type "; + type::print(os, type_of(index)); + throw std::runtime_error(os.str()); + } + + std::uint64_t final_index; + + if (index_unsigned) + { + if (*index_unsigned >= avalue->elements.size()) + { + std::ostringstream os; + os << "Array index " << *index_unsigned << " out of bounds " << avalue->elements.size(); + throw std::runtime_error(os.str()); + } + final_index = *index_unsigned; + } + else // if (index_signed) + { + if (*index_signed < 0 || *index_signed >= avalue->elements.size()) + { + std::ostringstream os; + os << "Array index " << *index_signed << " out of bounds " << avalue->elements.size(); + throw std::runtime_error(os.str()); + } + final_index = *index_signed; + } + + return *avalue->elements[final_index]; + } + + std::ostringstream os; + os << "Cannot index into a non-array of type "; + type::print(os, type_of(array)); + throw std::runtime_error(os.str()); + } + value eval_impl(context & context, ast::expression_ptr const & expression) { return std::visit([&](auto const & expression){ return eval_impl(context, expression); }, *expression); diff --git a/libs/interpreter/source/value.cpp b/libs/interpreter/source/value.cpp index dad9e92..8a566b1 100644 --- a/libs/interpreter/source/value.cpp +++ b/libs/interpreter/source/value.cpp @@ -24,6 +24,11 @@ namespace pslang::interpreter return std::visit([](auto const & value){ return type_of_impl(value); }, value); } + type::type type_of_impl(array_value const & value) + { + return type::array_type{.element_type = value.element_type, .size = value.elements.size()}; + } + type::type type_of_impl(value const & value) { return std::visit([](auto const & value){ return type_of_impl(value); }, value); @@ -68,6 +73,18 @@ namespace pslang::interpreter std::visit([&](auto const & value){ return print_impl(out, value); }, value); } + void print_impl(std::ostream & out, array_value const & value) + { + out << "["; + for (std::size_t i = 0; i < value.elements.size(); ++i) + { + if (i > 0) + out << ", "; + print(out, *value.elements[i]); + } + out << "]"; + } + void print_impl(std::ostream & out, value const & value) { std::visit([&](auto const & value){ return print_impl(out, value); }, value); diff --git a/libs/parser/CMakeLists.txt b/libs/parser/CMakeLists.txt index e8ed47b..e48f5ce 100644 --- a/libs/parser/CMakeLists.txt +++ b/libs/parser/CMakeLists.txt @@ -24,7 +24,7 @@ bison_target( ${PSLANG_PARSER_RULES_FILE} ${PSLANG_PARSER_SOURCE_FILE} DEFINES_FILE ${PSLANG_PARSER_HEADER_FILE} - COMPILE_FLAGS -Wcounterexamples + # COMPILE_FLAGS -Wcounterexamples ) add_flex_bison_dependency(generate-pslang-lexer generate-pslang-parser) diff --git a/libs/parser/rules/pslang.l b/libs/parser/rules/pslang.l index 9d7b33e..a154a27 100644 --- a/libs/parser/rules/pslang.l +++ b/libs/parser/rules/pslang.l @@ -55,6 +55,8 @@ f64 { return bp::make_f64(ctx.location); } "," { return bp::make_comma(ctx.location); } "(" { return bp::make_lparen(ctx.location); } ")" { return bp::make_rparen(ctx.location); } +"[" { return bp::make_lbracket(ctx.location); } +"]" { return bp::make_rbracket(ctx.location); } "+" { return bp::make_plus(ctx.location); } "-" { return bp::make_minus(ctx.location); } "*" { return bp::make_asterisk(ctx.location); } diff --git a/libs/parser/rules/pslang.y b/libs/parser/rules/pslang.y index ec3ae24..7dc449d 100644 --- a/libs/parser/rules/pslang.y +++ b/libs/parser/rules/pslang.y @@ -78,6 +78,8 @@ template %token comma "," %token lparen "(" %token rparen ")" +%token lbracket "[" +%token rbracket "]" %token plus "+" %token minus "-" %token asterisk "*" @@ -156,9 +158,10 @@ template %type sum_expression %type mult_expression %type not_expression +%type postfix_expression %type base_expression -%type > function_call_argument_list -%type > nonempty_function_call_argument_list +%type > comma_separated_expression_list +%type > nonempty_comma_separated_expression_list %type literal %% @@ -220,6 +223,7 @@ variable_keyword type_expression : unit { $$ = type::type(type::unit_type{}); } | primitive_type { $$ = type::type($1); } +| type_expression lbracket lit_i32 rbracket { $$ = type::array_type{std::make_unique($1), std::stoull($3)}; } ; primitive_type @@ -281,25 +285,31 @@ mult_expression ; not_expression +: postfix_expression +| exclamation postfix_expression { $$ = ast::unary_operation{ast::unary_operation_type::logical_not, std::make_unique($2) }; } +; + +postfix_expression : base_expression -| exclamation base_expression { $$ = ast::unary_operation{ast::unary_operation_type::logical_not, std::make_unique($2) }; } +| postfix_expression lbracket expression rbracket { $$ = ast::array_access{std::make_unique($1), std::make_unique($3)}; } ; base_expression : literal | name { $$ = ast::identifier{$1}; } | lparen expression rparen { $$ = $2; } -| name lparen function_call_argument_list rparen { $$ = ast::function_call{$1, $3}; } +| name lparen comma_separated_expression_list rparen { $$ = ast::function_call{$1, $3}; } +| lbracket nonempty_comma_separated_expression_list rbracket { $$ = ast::array{$2}; } ; -function_call_argument_list +comma_separated_expression_list : %empty { std::vector tmp; $$ = std::move(tmp); } -| nonempty_function_call_argument_list +| nonempty_comma_separated_expression_list ; -nonempty_function_call_argument_list +nonempty_comma_separated_expression_list : expression { std::vector tmp; tmp.push_back(std::make_unique($1)); $$ = std::move(tmp); } -| nonempty_function_call_argument_list comma expression { auto tmp = $1; tmp.push_back(std::make_unique($3)); $$ = std::move(tmp); } +| nonempty_comma_separated_expression_list comma expression { auto tmp = $1; tmp.push_back(std::make_unique($3)); $$ = std::move(tmp); } ; literal diff --git a/libs/type/include/pslang/type/array.hpp b/libs/type/include/pslang/type/array.hpp new file mode 100644 index 0000000..b51c695 --- /dev/null +++ b/libs/type/include/pslang/type/array.hpp @@ -0,0 +1,21 @@ +#pragma once + +#include + +#include + +namespace pslang::type +{ + + struct array_type + { + type_ptr element_type; + std::uint64_t size; + }; + + inline bool operator == (array_type const & t1, array_type const & t2) + { + return equal(*t1.element_type, *t2.element_type) && (t1.size == t2.size); + } + +} diff --git a/libs/type/include/pslang/type/type.hpp b/libs/type/include/pslang/type/type.hpp index fb9dd19..3232bdf 100644 --- a/libs/type/include/pslang/type/type.hpp +++ b/libs/type/include/pslang/type/type.hpp @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -11,7 +12,8 @@ namespace pslang::type using type_impl = std::variant< unit_type, - primitive_type + primitive_type, + array_type >; struct type @@ -20,6 +22,4 @@ namespace pslang::type using type_impl::type_impl; }; - bool equal(type const & t1, type const & t2); - } diff --git a/libs/type/include/pslang/type/type_fwd.hpp b/libs/type/include/pslang/type/type_fwd.hpp index 07b7084..e844f2d 100644 --- a/libs/type/include/pslang/type/type_fwd.hpp +++ b/libs/type/include/pslang/type/type_fwd.hpp @@ -9,4 +9,5 @@ namespace pslang::type using type_ptr = std::shared_ptr; + bool equal(type const & t1, type const & t2); } diff --git a/libs/type/source/print.cpp b/libs/type/source/print.cpp index cc67923..f08e164 100644 --- a/libs/type/source/print.cpp +++ b/libs/type/source/print.cpp @@ -72,6 +72,12 @@ namespace pslang::type std::visit([&](auto const & value){ print_impl(out, value); }, type); } + void print_impl(std::ostream & out, array_type const & type) + { + print(out, *type.element_type); + out << "[" << type.size << "]"; + } + void print_impl(std::ostream & out, type const & type) { std::visit([&](auto const & value){ print_impl(out, value); }, type);