From c61eefb152c73600408683084ef59e4525c78d35 Mon Sep 17 00:00:00 2001 From: bialger Date: Wed, 14 Jan 2026 23:17:48 +0300 Subject: [PATCH 1/3] feat: enhance BytecodeVisitor and TypeChecker with improved type handling and method signatures - Added support for void return types in built-in methods for File and Array operations. - Updated type resolution for wrapper types in index access and method calls. - Corrected return types from Bool to bool for consistency across built-in methods. - Introduced a new test case for file operations to validate the changes. --- lib/parser/ast/visitors/BytecodeVisitor.cpp | 112 ++++++++++++++++++-- lib/parser/ast/visitors/TypeChecker.cpp | 26 ++--- tests/main_test.cpp | 4 + tests/test_data/examples | 2 +- 4 files changed, 123 insertions(+), 21 deletions(-) diff --git a/lib/parser/ast/visitors/BytecodeVisitor.cpp b/lib/parser/ast/visitors/BytecodeVisitor.cpp index 647eefb..25e8bd5 100644 --- a/lib/parser/ast/visitors/BytecodeVisitor.cpp +++ b/lib/parser/ast/visitors/BytecodeVisitor.cpp @@ -1044,6 +1044,22 @@ void BytecodeVisitor::Visit(ExprStmt& node) { } } + // Check if this is a builtin type method that returns void + if (!object_type.empty() && kBuiltinTypeNames.contains(object_type)) { + if (object_type == "File") { + if (method_name == "Open" || method_name == "Close" || method_name == "WriteLine" || + method_name == "Seek") { + should_pop = false; + } + } else if (object_type.find("Array") != std::string::npos) { + if (method_name == "Add" || method_name == "RemoveAt" || method_name == "InsertAt" || + method_name == "SetAt" || method_name == "Clear" || method_name == "Reserve" || + method_name == "ShrinkToFit") { + should_pop = false; + } + } + } + std::string method_key; if (!object_type.empty() && !kBuiltinTypeNames.contains(object_type)) { method_key = object_type + "::" + method_name; @@ -1470,6 +1486,14 @@ void BytecodeVisitor::Visit(Assign& node) { EmitTypeConversionIfNeeded(elem_type, value_type_name); index_access->MutableIndexExpr().Accept(*this); + + // Unwrap the index if it's a wrapper type (e.g., Int -> int) + // Array SetAt methods expect int, not Int wrapper + std::string index_type = GetTypeNameForExpr(&index_access->MutableIndexExpr()); + if (IsPrimitiveWrapper(index_type)) { + EmitCommand("Unwrap"); + } + index_access->MutableObject().Accept(*this); std::string method_name = GenerateArraySetAtMethodName(array_type); @@ -2114,6 +2138,14 @@ void BytecodeVisitor::Visit(FieldAccess& node) { void BytecodeVisitor::Visit(IndexAccess& node) { node.MutableIndexExpr().Accept(*this); + + // Unwrap the index if it's a wrapper type (e.g., Int -> int) + // Array GetAt methods expect int, not Int wrapper + std::string index_type = GetTypeNameForExpr(&node.MutableIndexExpr()); + if (IsPrimitiveWrapper(index_type)) { + EmitCommand("Unwrap"); + } + node.MutableObject().Accept(*this); std::string array_type = GetTypeNameForExpr(&node.MutableObject()); @@ -2675,6 +2707,31 @@ BytecodeVisitor::OperandType BytecodeVisitor::DetermineOperandType(Expr* expr) { } } + if (auto* index_access = dynamic_cast(expr)) { + // Get the element type of the array + std::string array_type = GetTypeNameForExpr(&index_access->MutableObject()); + std::string elem_type = GetElementTypeForArray(array_type); + + if (elem_type == "int") { + return OperandType::kInt; + } + if (elem_type == "float") { + return OperandType::kFloat; + } + if (elem_type == "byte") { + return OperandType::kByte; + } + if (elem_type == "bool") { + return OperandType::kBool; + } + if (elem_type == "char") { + return OperandType::kChar; + } + if (elem_type == "String") { + return OperandType::kString; + } + } + if (auto* binary = dynamic_cast(expr)) { OperandType lhs_type = DetermineOperandType(&binary->MutableLhs()); OperandType rhs_type = DetermineOperandType(&binary->MutableRhs()); @@ -2904,7 +2961,7 @@ std::string BytecodeVisitor::GetTypeNameForExpr(Expr* expr) { return "String"; } if (ns_name == "ReadChar" && call->Args().size() == 0) { - return "Char"; + return "char"; } // Time and Date Operations if (ns_name == "FormatDateTime" && call->Args().size() == 2) { @@ -2924,16 +2981,16 @@ std::string BytecodeVisitor::GetTypeNameForExpr(Expr* expr) { return "Bool"; } if (ns_name == "DeleteFile" && call->Args().size() == 1) { - return "Bool"; + return "bool"; } if (ns_name == "DeleteDirectory" && call->Args().size() == 1) { - return "Bool"; + return "bool"; } if (ns_name == "MoveFile" && call->Args().size() == 2) { - return "Bool"; + return "bool"; } if (ns_name == "CopyFile" && call->Args().size() == 2) { - return "Bool"; + return "bool"; } if (ns_name == "ListDirectory" && call->Args().size() == 1) { return "StringArray"; @@ -2942,7 +2999,7 @@ std::string BytecodeVisitor::GetTypeNameForExpr(Expr* expr) { return "String"; } if (ns_name == "ChangeDirectory" && call->Args().size() == 1) { - return "Bool"; + return "bool"; } // Process Control if (ns_name == "Sleep" && call->Args().size() == 1) { @@ -2961,7 +3018,7 @@ std::string BytecodeVisitor::GetTypeNameForExpr(Expr* expr) { return "String?"; // Returns Nullable } if (ns_name == "SetEnvironmentVar" && call->Args().size() == 2) { - return "Bool"; + return "bool"; } // Random Number Generation if (ns_name == "SeedRandom" && call->Args().size() == 1) { @@ -3019,6 +3076,17 @@ std::string BytecodeVisitor::GetTypeNameForExpr(Expr* expr) { if (method_name == "GetHash") { return "int"; } + if (method_name == "Equals") { + return "bool"; + } + if (method_name == "IsLess") { + return "bool"; + } + if (method_name == "Add" || method_name == "RemoveAt" || method_name == "InsertAt" || + method_name == "SetAt" || method_name == "Clear" || method_name == "Reserve" || + method_name == "ShrinkToFit") { + return "void"; + } } // Handle String methods if (object_type == "String") { @@ -3034,6 +3102,12 @@ std::string BytecodeVisitor::GetTypeNameForExpr(Expr* expr) { if (method_name == "ToUtf8Bytes") { return "ByteArray"; } + if (method_name == "Equals") { + return "bool"; + } + if (method_name == "IsLess") { + return "bool"; + } } // Handle wrapper type methods (Int, Float, etc.) if (method_name == "ToString") { @@ -3042,6 +3116,30 @@ std::string BytecodeVisitor::GetTypeNameForExpr(Expr* expr) { if (method_name == "GetHash") { return "int"; } + if (method_name == "Equals") { + return "bool"; + } + if (method_name == "IsLess") { + return "bool"; + } + // Handle File methods + if (object_type == "File") { + if (method_name == "Open" || method_name == "Close" || method_name == "WriteLine" || method_name == "Seek") { + return "void"; + } + if (method_name == "IsOpen" || method_name == "Eof") { + return "bool"; + } + if (method_name == "Read") { + return "ByteArray"; + } + if (method_name == "ReadLine") { + return "String"; + } + if (method_name == "Tell" || method_name == "Write") { + return "int"; + } + } } // Handle method calls on user-defined types diff --git a/lib/parser/ast/visitors/TypeChecker.cpp b/lib/parser/ast/visitors/TypeChecker.cpp index 725e4bf..ed3fa49 100644 --- a/lib/parser/ast/visitors/TypeChecker.cpp +++ b/lib/parser/ast/visitors/TypeChecker.cpp @@ -126,21 +126,21 @@ const std::unordered_map kBuiltinReturnTypes = { {"ParseDateTime", "Int"}, {"FormatDateTime", "String"}, // File operations - {"FileExists", "Bool"}, - {"DirectoryExists", "Bool"}, - {"CreateDirectory", "Bool"}, - {"DeleteFile", "Bool"}, - {"DeleteDirectory", "Bool"}, - {"MoveFile", "Bool"}, - {"CopyFile", "Bool"}, + {"FileExists", "bool"}, + {"DirectoryExists", "bool"}, + {"CreateDirectory", "bool"}, + {"DeleteFile", "bool"}, + {"DeleteDirectory", "bool"}, + {"MoveFile", "bool"}, + {"CopyFile", "bool"}, {"ListDirectory", "StringArray"}, {"GetCurrentDirectory", "String"}, - {"ChangeDirectory", "Bool"}, + {"ChangeDirectory", "bool"}, // I/O functions {"ReadLine", "String"}, - {"ReadChar", "Char"}, - {"ReadInt", "Int"}, - {"ReadFloat", "Float"}, + {"ReadChar", "char"}, + {"ReadInt", "int"}, + {"ReadFloat", "float"}, // System information {"GetOsName", "String"}, {"GetOsVersion", "String"}, @@ -148,7 +148,7 @@ const std::unordered_map kBuiltinReturnTypes = { {"GetUserName", "String"}, {"GetHomeDirectory", "String"}, // Environment - {"SetEnvironmentVar", "Bool"}, + {"SetEnvironmentVar", "bool"}, // Math functions {"Sqrt", "float"}, {"ToString", "String"}, @@ -2628,7 +2628,7 @@ void TypeChecker::InitializeBuiltinMethods() { { BuiltinMethodSignature sig; sig.param_types = {TypeReference("ByteArray")}; - sig.return_type = nullptr; // void + sig.return_type = std::make_unique("int"); // int builtin_methods_["File"]["Write"] = std::move(sig); } diff --git a/tests/main_test.cpp b/tests/main_test.cpp index af2c33c..c1ef31a 100644 --- a/tests/main_test.cpp +++ b/tests/main_test.cpp @@ -116,6 +116,10 @@ TEST_F(ProjectIntegrationTestSuite, ExampleFileNBody) { CompileAndCompareExample("nbody"); } +TEST_F(ProjectIntegrationTestSuite, ExampleFileFile) { + CompileAndCompareExample("file"); +} + // Integrational files compilation tests TEST_F(ProjectIntegrationTestSuite, IntegrationalFileQuadratic) { CompileAndCompareIntegrational("quadratic"); diff --git a/tests/test_data/examples b/tests/test_data/examples index 1c168c2..e14bfe0 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit 1c168c29c0d6a9003a0d1b873791fd30f69a98fc +Subproject commit e14bfe0e528292cf2a596813b89213126884fb9f From 4360627e942299569a4f599e45815d698b72f083 Mon Sep 17 00:00:00 2001 From: bialger Date: Thu, 15 Jan 2026 17:16:04 +0300 Subject: [PATCH 2/3] docs: update prerequisites and usage instructions in README.md --- README.md | 9 +++++---- tests/test_data/examples | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 226180e..0502503 100644 --- a/README.md +++ b/README.md @@ -15,8 +15,8 @@ Ovum is a strongly statically typed, single-threaded programming language focuse ## Prerequisites -- **CMake** 3.12 or higher -- **C++17** compatible compiler (GCC, Clang, or MSVC) +- **CMake** 3.25 or higher +- **C++23** compatible compiler (GCC, Clang, or MSVC) - **Git** (for dependency management) ## Quick Start @@ -59,7 +59,7 @@ Once built, the `ovumc` compiler can be used to compile Ovum source files: ```bash # Compile an Ovum source file -ovumc input.ovum -o output.bytecode +ovumc -m input.ovum -o output.oil -I ./ -D SOME_FLAG # Show help ovumc -h @@ -108,7 +108,8 @@ We welcome contributions! Please see our [Contributing Guidelines](CONTRIBUTING. ## Related Projects - **[OvumLanguage](https://github.com/Ovum-Programming-Language/OvumLanguage)** - Main language repository with documentation and specifications -- **[OvumVM](https://github.com/Ovum-Programming-Language/OvumVM)** - Virtual Machine implementation (planned) +- **[OvumVM](https://github.com/Ovum-Programming-Language/OvumVM)** - Virtual Machine implementation +- **[OvumExamples](https://github.com/Ovum-Programming-Language/OvumExamples)** - Examples of code in Ovum with bytecode ## License diff --git a/tests/test_data/examples b/tests/test_data/examples index 1c168c2..28c2ab6 160000 --- a/tests/test_data/examples +++ b/tests/test_data/examples @@ -1 +1 @@ -Subproject commit 1c168c29c0d6a9003a0d1b873791fd30f69a98fc +Subproject commit 28c2ab6170e182bb13a1ad9edd2308f5cb95c895 From 5602e1bc8598e8e5c3a042a2c479856fb8314239 Mon Sep 17 00:00:00 2001 From: bialger Date: Thu, 15 Jan 2026 17:42:43 +0300 Subject: [PATCH 3/3] fix: standardize return type for file operation methods to 'bool' --- lib/parser/ast/visitors/BytecodeVisitor.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/parser/ast/visitors/BytecodeVisitor.cpp b/lib/parser/ast/visitors/BytecodeVisitor.cpp index 25e8bd5..49085ce 100644 --- a/lib/parser/ast/visitors/BytecodeVisitor.cpp +++ b/lib/parser/ast/visitors/BytecodeVisitor.cpp @@ -2972,13 +2972,13 @@ std::string BytecodeVisitor::GetTypeNameForExpr(Expr* expr) { } // File Operations if (ns_name == "FileExists" && call->Args().size() == 1) { - return "Bool"; + return "bool"; } if (ns_name == "DirectoryExists" && call->Args().size() == 1) { - return "Bool"; + return "bool"; } if (ns_name == "CreateDirectory" && call->Args().size() == 1) { - return "Bool"; + return "bool"; } if (ns_name == "DeleteFile" && call->Args().size() == 1) { return "bool";