From f60f5190d4b12c34a5c1a79835372eb043945e64 Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 18 May 2025 15:21:00 +0100 Subject: [PATCH 01/15] Renames benchmarks file --- ...{CountTo1MBenchmark.cpp => Benchmarks.cpp} | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) rename test/benchmark/{CountTo1MBenchmark.cpp => Benchmarks.cpp} (64%) diff --git a/test/benchmark/CountTo1MBenchmark.cpp b/test/benchmark/Benchmarks.cpp similarity index 64% rename from test/benchmark/CountTo1MBenchmark.cpp rename to test/benchmark/Benchmarks.cpp index 1ea3830..255d8a5 100644 --- a/test/benchmark/CountTo1MBenchmark.cpp +++ b/test/benchmark/Benchmarks.cpp @@ -13,8 +13,9 @@ #include "test/utility/Utility.hpp" #include - -static void BenchmarkParsingCountTo1M(benchmark::State &state) +namespace Loops +{ +static void ParseCountTo1M(benchmark::State &state) { auto path = (getTestDirPath() + "benchmark/data/CountToOneMillion.ek"); @@ -25,7 +26,7 @@ static void BenchmarkParsingCountTo1M(benchmark::State &state) } -static void BenchmarkEvaluatingCountTo1M(benchmark::State &state) +static void EvaluateCountTo1M(benchmark::State &state) { auto path = (getTestDirPath() + "benchmark/data/CountToOneMillion.ek"); @@ -38,8 +39,13 @@ static void BenchmarkEvaluatingCountTo1M(benchmark::State &state) } } +} // namespace Loops -static void BenchmarkEvaluateFibTo25(benchmark::State &state) + +namespace Functions +{ + +static void ParseAndEvaluateFibTo25(benchmark::State &state) { auto path = (getTestDirPath() + "benchmark/data/EvaluateFibToTwentyFive.ek"); @@ -49,6 +55,9 @@ static void BenchmarkEvaluateFibTo25(benchmark::State &state) } } -BENCHMARK(BenchmarkParsingCountTo1M)->Unit(benchmark::kMillisecond); -BENCHMARK(BenchmarkEvaluatingCountTo1M)->Unit(benchmark::kMillisecond); -BENCHMARK(BenchmarkEvaluateFibTo25)->Unit(benchmark::kMillisecond); \ No newline at end of file +} // namespace Functions + + +BENCHMARK(Loops::ParseCountTo1M)->Unit(benchmark::kMillisecond); +BENCHMARK(Loops::EvaluateCountTo1M)->Unit(benchmark::kMillisecond); +BENCHMARK(Functions::ParseAndEvaluateFibTo25)->Unit(benchmark::kMillisecond); \ No newline at end of file From 1ac49a3683e9a121fd267fa5a6ca11dee86cc09b Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 18 May 2025 16:02:11 +0100 Subject: [PATCH 02/15] Adds additional function benchmarks --- test/benchmark/FunctionBenchmarks.cpp | 55 +++++++++++++++++++ .../{Benchmarks.cpp => LoopBenchmarks.cpp} | 19 +------ .../data/SumMultiplesThreeFiveNaive.ek | 23 ++++++++ .../data/SumMultiplesThreeFiveOpt.ek | 26 +++++++++ 4 files changed, 105 insertions(+), 18 deletions(-) create mode 100644 test/benchmark/FunctionBenchmarks.cpp rename test/benchmark/{Benchmarks.cpp => LoopBenchmarks.cpp} (72%) create mode 100644 test/benchmark/data/SumMultiplesThreeFiveNaive.ek create mode 100644 test/benchmark/data/SumMultiplesThreeFiveOpt.ek diff --git a/test/benchmark/FunctionBenchmarks.cpp b/test/benchmark/FunctionBenchmarks.cpp new file mode 100644 index 0000000..5f82128 --- /dev/null +++ b/test/benchmark/FunctionBenchmarks.cpp @@ -0,0 +1,55 @@ +/** + * @file CountTo1MBenchmark.cpp + * @author Edward Palmer + * @date 2025-05-18 + * + * @copyright Copyright (c) 2025 + * + */ + +#include "EucleiaInterpreter.hpp" +#include "FileParser.hpp" +#include "Scope.hpp" +#include "test/utility/Utility.hpp" +#include + + +namespace Functions +{ + +static void ParseAndEvaluateFibTo25(benchmark::State &state) +{ + auto path = (getTestDirPath() + "benchmark/data/EvaluateFibToTwentyFive.ek"); + + for (auto _ : state) + { + Interpreter::evaluateFile(path); + } +} + +static void ParseAndEvaluateSumOfMultiplesOf3Or5To1000Naive(benchmark::State &state) +{ + auto path = (getTestDirPath() + "benchmark/data/SumMultiplesThreeFiveNaive.ek"); + + for (auto _ : state) + { + Interpreter::evaluateFile(path); + } +} + +static void ParseAndEvaluateSumOfMultiplesOf3Or5To1000Opt(benchmark::State &state) +{ + auto path = (getTestDirPath() + "benchmark/data/SumMultiplesThreeFiveOpt.ek"); + + for (auto _ : state) + { + Interpreter::evaluateFile(path); + } +} + +} // namespace Functions + + +BENCHMARK(Functions::ParseAndEvaluateFibTo25)->Unit(benchmark::kMillisecond); +BENCHMARK(Functions::ParseAndEvaluateSumOfMultiplesOf3Or5To1000Naive)->Unit(benchmark::kMillisecond); +BENCHMARK(Functions::ParseAndEvaluateSumOfMultiplesOf3Or5To1000Opt)->Unit(benchmark::kMillisecond); \ No newline at end of file diff --git a/test/benchmark/Benchmarks.cpp b/test/benchmark/LoopBenchmarks.cpp similarity index 72% rename from test/benchmark/Benchmarks.cpp rename to test/benchmark/LoopBenchmarks.cpp index 255d8a5..bdc1abc 100644 --- a/test/benchmark/Benchmarks.cpp +++ b/test/benchmark/LoopBenchmarks.cpp @@ -42,22 +42,5 @@ static void EvaluateCountTo1M(benchmark::State &state) } // namespace Loops -namespace Functions -{ - -static void ParseAndEvaluateFibTo25(benchmark::State &state) -{ - auto path = (getTestDirPath() + "benchmark/data/EvaluateFibToTwentyFive.ek"); - - for (auto _ : state) - { - Interpreter::evaluateFile(path); - } -} - -} // namespace Functions - - BENCHMARK(Loops::ParseCountTo1M)->Unit(benchmark::kMillisecond); -BENCHMARK(Loops::EvaluateCountTo1M)->Unit(benchmark::kMillisecond); -BENCHMARK(Functions::ParseAndEvaluateFibTo25)->Unit(benchmark::kMillisecond); \ No newline at end of file +BENCHMARK(Loops::EvaluateCountTo1M)->Unit(benchmark::kMillisecond); \ No newline at end of file diff --git a/test/benchmark/data/SumMultiplesThreeFiveNaive.ek b/test/benchmark/data/SumMultiplesThreeFiveNaive.ek new file mode 100644 index 0000000..edea4d4 --- /dev/null +++ b/test/benchmark/data/SumMultiplesThreeFiveNaive.ek @@ -0,0 +1,23 @@ +//import + +func computeSumOfMultiplesOfThreeOrFiveBelowOneThousand() +{ + int sum = 0; + + for (int i = 1; i < 1000; ++i) + { + if (i % 3 == 0) + { + sum = sum + i; + } + else if (i % 5 == 0) + { + sum = sum + i; + } + } + + return sum; +} + + +//TEST(computeSumOfMultiplesOfThreeOrFiveBelowOneThousand() == 233168, ""); \ No newline at end of file diff --git a/test/benchmark/data/SumMultiplesThreeFiveOpt.ek b/test/benchmark/data/SumMultiplesThreeFiveOpt.ek new file mode 100644 index 0000000..1d2b4d2 --- /dev/null +++ b/test/benchmark/data/SumMultiplesThreeFiveOpt.ek @@ -0,0 +1,26 @@ +//import + +func computeSumOfMultiplesOfThreeOrFiveBelowOneThousand() +{ + int sum = 0; + + for (int i = 15; i < 1000; i = i + 15) + { + sum = (sum - i); + } + + for (int i = 3; i < 1000; i = i + 3) + { + sum = (sum + i); + } + + for (int i = 5; i < 1000; i = i + 5) + { + sum = (sum + i); + } + + return sum; +} + + +//TEST(computeSumOfMultiplesOfThreeOrFiveBelowOneThousand() == 233168, ""); \ No newline at end of file From 50854d3fea95ac0f7b53f095cf2a0f1532c323c6 Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 18 May 2025 16:55:40 +0100 Subject: [PATCH 03/15] Enables Float -> Int conversion on assignment --- src/nodes/ModuleNodeFactory.cpp | 8 +++++--- src/objects/FloatObject.hpp | 8 +++++++- src/objects/IntObject.hpp | 7 ++++++- src/objects/StringObject.hpp | 1 + 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/nodes/ModuleNodeFactory.cpp b/src/nodes/ModuleNodeFactory.cpp index e5f16d7..2f2bfbc 100644 --- a/src/nodes/ModuleNodeFactory.cpp +++ b/src/nodes/ModuleNodeFactory.cpp @@ -74,10 +74,12 @@ AnyNode::Ptr createMathModuleNode() { assert(callArgs.size() == 2); - auto first = callArgs.front()->evaluate(scope); - auto second = callArgs.back()->evaluate(scope); + /* TODO: - bug if pass in an integer rather than a floatObject */ + + auto &first = callArgs.front()->evaluate(scope)->castObject(); + auto &second = callArgs.back()->evaluate(scope)->castObject(); - return ObjectFactory::allocate(pow(first->value(), second->value())); + return ObjectFactory::allocate(pow(first.value(), second.value())); }); return NodeFactory::createModuleNode("math", {doSqrt, doPow}); diff --git a/src/objects/FloatObject.hpp b/src/objects/FloatObject.hpp index cf72792..e1876ab 100644 --- a/src/objects/FloatObject.hpp +++ b/src/objects/FloatObject.hpp @@ -9,6 +9,7 @@ #pragma once #include "BaseObjectT.hpp" +#include "Exceptions.hpp" #include "IntObject.hpp" #include "ObjectFactory.hpp" #include @@ -27,7 +28,12 @@ class FloatObject : public BaseObjectT { if (this != &other) { - _value = FloatObject::value(other); + if (other.isObjectType()) + _value = IntObject::value(other); + else if (other.isObjectType()) + _value = FloatObject::value(other); + else + ThrowException("Cannot assign object to type FloatObject"); } return (*this); diff --git a/src/objects/IntObject.hpp b/src/objects/IntObject.hpp index 38060f8..2ab2a35 100644 --- a/src/objects/IntObject.hpp +++ b/src/objects/IntObject.hpp @@ -30,7 +30,12 @@ class IntObject : public BaseObjectT { if (this != &other) { - _value = IntObject::value(other); + if (other.isObjectType()) + _value = IntObject::value(other); + else if (other.isObjectType()) + _value = (long)BaseObjectT::value(other); + else + ThrowException("Cannot assign object to type FloatObject"); } return (*this); diff --git a/src/objects/StringObject.hpp b/src/objects/StringObject.hpp index 0552b97..26d2640 100644 --- a/src/objects/StringObject.hpp +++ b/src/objects/StringObject.hpp @@ -21,6 +21,7 @@ class StringObject : public BaseObjectT { if (this != &other) { + assert(other.isObjectType()); _value = StringObject::value(other); } From 460fd01d346217386138f2e708e95474947be16b Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 18 May 2025 16:55:53 +0100 Subject: [PATCH 04/15] Adds another benchmark testcase --- test/benchmark/FunctionBenchmarks.cpp | 13 ++++++++++- .../DifferenceSumOfSquaresAndSquareOfSum.ek | 22 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek diff --git a/test/benchmark/FunctionBenchmarks.cpp b/test/benchmark/FunctionBenchmarks.cpp index 5f82128..8f99fa3 100644 --- a/test/benchmark/FunctionBenchmarks.cpp +++ b/test/benchmark/FunctionBenchmarks.cpp @@ -47,9 +47,20 @@ static void ParseAndEvaluateSumOfMultiplesOf3Or5To1000Opt(benchmark::State &stat } } +static void ParseAndEvaluateDifferenceSumOfSquaresAndSquareOfSum(benchmark::State &state) +{ + auto path = (getTestDirPath() + "benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek"); + + for (auto _ : state) + { + Interpreter::evaluateFile(path); + } +} + } // namespace Functions BENCHMARK(Functions::ParseAndEvaluateFibTo25)->Unit(benchmark::kMillisecond); BENCHMARK(Functions::ParseAndEvaluateSumOfMultiplesOf3Or5To1000Naive)->Unit(benchmark::kMillisecond); -BENCHMARK(Functions::ParseAndEvaluateSumOfMultiplesOf3Or5To1000Opt)->Unit(benchmark::kMillisecond); \ No newline at end of file +BENCHMARK(Functions::ParseAndEvaluateSumOfMultiplesOf3Or5To1000Opt)->Unit(benchmark::kMillisecond); +BENCHMARK(Functions::ParseAndEvaluateDifferenceSumOfSquaresAndSquareOfSum)->Unit(benchmark::kMillisecond); \ No newline at end of file diff --git a/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek b/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek new file mode 100644 index 0000000..6c24f5c --- /dev/null +++ b/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek @@ -0,0 +1,22 @@ +import + +func DifferenceSumOfSquaresAndSquareOfSum(int max) +{ + float sum = 0; + float sumSquares = 0; + + for (int i = 1; i <= max; ++i) + { + float fCounter = i; + + sum = sum + fCounter; + sumSquares = sumSquares + pow(fCounter, 2.0); // Careful! Issues with passing Int to func expecting float! + } + + int lhs = pow(sum, 2.0); // Note the implicit conversion back. + int rhs = sumSquares; + + return (lhs - rhs); +} + +DifferenceSumOfSquaresAndSquareOfSum(100); // 25164150 From af678f76d98ade798fc6115453315e89785e2a09 Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 18 May 2025 17:35:50 +0100 Subject: [PATCH 05/15] Added simple AnyObject and ModuleFunctor --- src/objects/AnyObject.cpp | 13 ++++++ src/objects/AnyObject.hpp | 95 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 src/objects/AnyObject.cpp create mode 100644 src/objects/AnyObject.hpp diff --git a/src/objects/AnyObject.cpp b/src/objects/AnyObject.cpp new file mode 100644 index 0000000..2cecd6a --- /dev/null +++ b/src/objects/AnyObject.cpp @@ -0,0 +1,13 @@ +/** + * @file AnyObject.cpp + * @author Edward Palmer + * @date 2025-05-18 + * + * @copyright Copyright (c) 2025 + * + */ + +#include "AnyObject.hpp" + + + diff --git a/src/objects/AnyObject.hpp b/src/objects/AnyObject.hpp new file mode 100644 index 0000000..e767ab5 --- /dev/null +++ b/src/objects/AnyObject.hpp @@ -0,0 +1,95 @@ +/** + * @file Object.hpp + * @author Edward Palmer + * @date 2025-05-18 + * + * @copyright Copyright (c) 2025 + * + */ + +#pragma once +#include "BaseNode.hpp" +#include "Scope.hpp" +#include +#include +#include +#include +#include + +class AnyObject; /* Forward declaration */ + +class ModuleFunctor +{ +public: + using ModuleFunction = std::function(BaseNodePtrVector &, Scope &)>; + + ModuleFunctor(ModuleFunction &&function) : _function(std::move(function)) {} + + [[nodiscard]] inline std::shared_ptr operator()(BaseNodePtrVector &args, Scope &scope) + { + return !_function ? nullptr : _function(args, scope); + } + +private: + ModuleFunction _function{nullptr}; +}; + + +class AnyObject +{ +public: + using Ptr = std::shared_ptr; + using Vector = std::vector; + + enum Type + { + None = (-1), + Int, + Float, + String, + Array, + ModuleFunction + }; + + constexpr explicit AnyObject(long value) : _value(value) {} /* NB: require explicit to avoid implicit casting */ + constexpr explicit AnyObject(double value) : _value(value) {} + constexpr explicit AnyObject(AnyObject::Vector &&value) : _value(std::move(value)) {} + constexpr explicit AnyObject(ModuleFunctor &&value) : _value(std::move(value)) {} + + template + [[nodiscard]] inline TValue &getValue(); + + template + [[nodiscard]] inline const TValue &getValue() const; + + [[nodiscard]] inline Type getType() const; + + /* TODO: - think about copy constructors/assignment here. Will need to implement based on types */ + +protected: + AnyObject() = default; /* Prevent direct initialization */ + virtual ~AnyObject() = default; /* In case we subclass */ + +private: + std::variant _value; +}; + + +template +TValue &AnyObject::getValue() +{ + return std::get(_value); +} + + +template +const TValue &AnyObject::getValue() const +{ + return std::get(_value); +} + + +AnyObject::Type AnyObject::getType() const +{ + return Type(_value.index()); +} \ No newline at end of file From d27d77ebf9bf53c8a0fe67135decfe36edc5387f Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Mon, 19 May 2025 17:28:15 +0100 Subject: [PATCH 06/15] Single object class (very slow and inefficient) --- src/environment/Scope.cpp | 9 +- src/environment/Scope.hpp | 31 +-- src/eucleia.cpp | 2 +- src/interpreter/EucleiaInterpreter.cpp | 3 +- src/nodes/AddVariableNode.cpp | 80 +++----- src/nodes/AddVariableNode.hpp | 21 +-- src/nodes/AnyNode.cpp | 17 ++ src/nodes/AnyNode.hpp | 10 +- src/nodes/BaseNode.hpp | 26 +-- src/nodes/BinaryNode.cpp | 176 +++++++++++------- src/nodes/BinaryNode.hpp | 17 +- ...tionObject.cpp => ClassDefinitionNode.cpp} | 38 ++-- ...tionObject.hpp => ClassDefinitionNode.hpp} | 23 ++- src/nodes/ClassNode.cpp | 47 +++++ src/nodes/ClassNode.hpp | 36 ++++ src/nodes/FunctionCallNode.cpp | 20 +- src/nodes/FunctionCallNode.hpp | 7 +- src/nodes/FunctionNode.cpp | 8 +- src/nodes/FunctionNode.hpp | 4 +- src/nodes/LookupVariableNode.cpp | 4 +- src/nodes/LookupVariableNode.hpp | 3 +- src/nodes/ModuleNodeFactory.cpp | 55 +++--- src/nodes/NodeFactory.cpp | 96 +++++----- src/nodes/NodeFactory.hpp | 4 +- src/nodes/PropertyInterface.hpp | 6 +- ...ionObject.cpp => StructDefinitionNode.cpp} | 38 ++-- ...ionObject.hpp => StructDefinitionNode.hpp} | 35 ++-- src/nodes/StructNode.cpp | 70 +++++++ .../StructObject.hpp => nodes/StructNode.hpp} | 36 ++-- src/objects/AnyObject.cpp | 162 ++++++++++++++++ src/objects/AnyObject.hpp | 97 ++++++---- src/objects/ArrayObject.cpp | 78 -------- src/objects/ArrayObject.hpp | 38 ---- src/objects/BaseObject.hpp | 62 ------ src/objects/BaseObjectT.hpp | 76 -------- src/objects/ClassObject.cpp | 40 ---- src/objects/ClassObject.hpp | 35 ---- src/objects/FloatObject.cpp | 15 -- src/objects/FloatObject.hpp | 114 ------------ src/objects/FunctionObject.hpp | 30 --- src/objects/IntObject.hpp | 151 --------------- src/objects/ModuleFunctionObject.hpp | 37 ---- src/objects/ModuleFunctor.cpp | 18 ++ src/objects/ModuleFunctor.hpp | 33 ++++ src/objects/ObjectFactory.cpp | 26 ++- src/objects/ObjectFactory.hpp | 11 +- src/objects/ObjectTypes.cpp | 34 ---- src/objects/ObjectTypes.hpp | 30 --- src/objects/Objects.hpp | 71 ------- src/objects/StringObject.hpp | 58 ------ src/objects/StructObject.cpp | 70 ------- src/parsers/FileParser.cpp | 1 - src/parsers/FileParser.hpp | 8 +- src/subparsers/ClassSubParser.cpp | 16 +- src/subparsers/VariableSubParser.cpp | 4 +- src/subparsers/VariableSubParser.hpp | 5 +- src/utility/JumpPoints.cpp | 2 + src/utility/JumpPoints.hpp | 5 +- src/utility/Logger.hpp | 2 +- .../DifferenceSumOfSquaresAndSquareOfSum.ek | 12 +- test/functional/data/ArrayTests.ek | 5 + test/functional/data/LoopTests.ek | 2 +- 62 files changed, 851 insertions(+), 1419 deletions(-) create mode 100644 src/nodes/AnyNode.cpp rename src/nodes/{ClassDefinitionObject.cpp => ClassDefinitionNode.cpp} (54%) rename src/nodes/{ClassDefinitionObject.hpp => ClassDefinitionNode.hpp} (72%) create mode 100644 src/nodes/ClassNode.cpp create mode 100644 src/nodes/ClassNode.hpp rename src/nodes/{StructDefinitionObject.cpp => StructDefinitionNode.cpp} (60%) rename src/nodes/{StructDefinitionObject.hpp => StructDefinitionNode.hpp} (68%) create mode 100644 src/nodes/StructNode.cpp rename src/{objects/StructObject.hpp => nodes/StructNode.hpp} (62%) delete mode 100644 src/objects/ArrayObject.cpp delete mode 100644 src/objects/ArrayObject.hpp delete mode 100644 src/objects/BaseObject.hpp delete mode 100644 src/objects/BaseObjectT.hpp delete mode 100644 src/objects/ClassObject.cpp delete mode 100644 src/objects/ClassObject.hpp delete mode 100644 src/objects/FloatObject.cpp delete mode 100644 src/objects/FloatObject.hpp delete mode 100644 src/objects/FunctionObject.hpp delete mode 100644 src/objects/IntObject.hpp delete mode 100644 src/objects/ModuleFunctionObject.hpp create mode 100644 src/objects/ModuleFunctor.cpp create mode 100644 src/objects/ModuleFunctor.hpp delete mode 100644 src/objects/ObjectTypes.cpp delete mode 100644 src/objects/ObjectTypes.hpp delete mode 100644 src/objects/Objects.hpp delete mode 100644 src/objects/StringObject.hpp delete mode 100644 src/objects/StructObject.cpp diff --git a/src/environment/Scope.cpp b/src/environment/Scope.cpp index 304b0ed..4ea2272 100644 --- a/src/environment/Scope.cpp +++ b/src/environment/Scope.cpp @@ -8,6 +8,7 @@ */ #include "Scope.hpp" +#include "AnyObject.hpp" #include "Exceptions.hpp" #include @@ -22,7 +23,7 @@ Scope::Scope(const Scope *_parent) } -BaseObject::Ptr Scope::getOptionalNamedObject(const std::string &name) const +AnyObject::Ptr Scope::getOptionalNamedObject(const std::string &name) const { // Try in our scope (to handle variable shadowing). auto iter = linkedObjectForName.find(name); @@ -42,9 +43,9 @@ BaseObject::Ptr Scope::getOptionalNamedObject(const std::string &name) const } -BaseObject::Ptr Scope::getNamedObject(const std::string &name) const +AnyObject::Ptr Scope::getNamedObject(const std::string &name) const { - BaseObject::Ptr obj = getOptionalNamedObject(name); + AnyObject::Ptr obj = getOptionalNamedObject(name); if (!obj) { ThrowException("undefined variable " + name); @@ -60,7 +61,7 @@ bool Scope::hasNamedObject(const std::string &name) const } -void Scope::linkObject(const std::string &name, BaseObject::Ptr object) +void Scope::linkObject(const std::string &name, AnyObject::Ptr object) { assert(object != nullptr); diff --git a/src/environment/Scope.hpp b/src/environment/Scope.hpp index 1a36ea5..0e64f93 100644 --- a/src/environment/Scope.hpp +++ b/src/environment/Scope.hpp @@ -8,8 +8,7 @@ */ #pragma once -#include "BaseObject.hpp" -#include +#include #include #include #include @@ -28,30 +27,10 @@ class Scope /// Get a named object ("variable") in our scope or an outer scope. We work /// outwards from our scope to handle variable shadowing correctly. If the /// object is not found, return nullptr. - BaseObject::Ptr getOptionalNamedObject(const std::string &name) const; + std::shared_ptr getOptionalNamedObject(const std::string &name) const; /// Similar to getOptionalObject but has a check to ensure pointer is valid. - BaseObject::Ptr getNamedObject(const std::string &name) const; - - /// Get an object from the scope and cast to a subclass. - template - std::shared_ptr getNamedObject(const std::string &name) const - { - auto objectPtr = getNamedObject(name); - return std::static_pointer_cast(objectPtr); - } - - template - std::shared_ptr getOptionalNamedObject(const std::string &name) const - { - BaseObject::Ptr obj = getOptionalNamedObject(name); - if (!obj) - { - return nullptr; - } - - return std::static_pointer_cast(obj); - } + std::shared_ptr getNamedObject(const std::string &name) const; /// Returns non-const reference to parent scope. inline Scope *parentScope() { return parent; } @@ -60,13 +39,13 @@ class Scope void setParentScope(Scope *parent_) { parent = parent_; } /// Create a link between a variable name and an object in this scope. - void linkObject(const std::string &name, BaseObject::Ptr object); + void linkObject(const std::string &name, std::shared_ptr object); private: /// Stores a mapping from the variable name to a pointer to the object. These /// are only linked objects defined in this scope. This enables variable /// shadowing. - std::unordered_map linkedObjectForName; + std::unordered_map> linkedObjectForName; Scope *parent{nullptr}; }; diff --git a/src/eucleia.cpp b/src/eucleia.cpp index 6dd46cb..e558260 100644 --- a/src/eucleia.cpp +++ b/src/eucleia.cpp @@ -19,7 +19,7 @@ int main(int argc, const char *argv[]) CLIParser parser("eucleia"); parser.addFlagArg("--help", "display available options"); - parser.addFlagArg("--trace", "logs everything!"); + parser.addFlagArg("--trace", "logs everything!"); /* TODO: - enable user to set different log levels or disable */ parser.addPositionalArg("fileName"); parser.parseArgs(argc, argv); diff --git a/src/interpreter/EucleiaInterpreter.cpp b/src/interpreter/EucleiaInterpreter.cpp index 1ab9750..7ece2e9 100644 --- a/src/interpreter/EucleiaInterpreter.cpp +++ b/src/interpreter/EucleiaInterpreter.cpp @@ -6,9 +6,8 @@ // #include "EucleiaInterpreter.hpp" -#include "BaseObject.hpp" + #include "FileParser.hpp" -#include "Objects.hpp" #include "Scope.hpp" #include diff --git a/src/nodes/AddVariableNode.cpp b/src/nodes/AddVariableNode.cpp index 48c34bb..a5f689f 100644 --- a/src/nodes/AddVariableNode.cpp +++ b/src/nodes/AddVariableNode.cpp @@ -8,14 +8,17 @@ */ #include "AddVariableNode.hpp" -#include "ArrayObject.hpp" -#include "FloatObject.hpp" -#include "IntObject.hpp" +#include "Exceptions.hpp" #include "ObjectFactory.hpp" -#include "StringObject.hpp" +AddVariableNode::AddVariableNode(std::string name, AnyObject::Type type) + : LookupVariableNode(std::move(name)), + _variableType(type) +{ + setType(NodeType::AddVariable); +} -BaseObject::Ptr AddVariableNode::evaluate(Scope &scope) +AnyObject::Ptr AddVariableNode::evaluate(Scope &scope) { /* TODO: - add support for functions (to enable passing to other functions, etc) */ auto objectPtr = ObjectFactory::allocate(_variableType); @@ -26,23 +29,9 @@ BaseObject::Ptr AddVariableNode::evaluate(Scope &scope) /// Type checking. -bool AddVariableNode::passesAssignmentTypeCheck(const BaseObject &assignObject) const +bool AddVariableNode::passesAssignmentTypeCheck(const AnyObject &assignObject) const { - switch (_variableType) - { - case ObjectType::Int: - return assignObject.isObjectType(); - case ObjectType::Float: - return assignObject.isObjectType(); - case ObjectType::Bool: - return assignObject.isObjectType(); - case ObjectType::String: - return assignObject.isObjectType(); - case ObjectType::Array: - return assignObject.isObjectType(); - default: - return false; - } + return assignObject.isType(_variableType); } @@ -50,15 +39,15 @@ std::string AddVariableNode::description() const { switch (_variableType) { - case ObjectType::Bool: + case AnyObject::Bool: return "Bool"; - case ObjectType::Int: + case AnyObject::Int: return "Int"; - case ObjectType::Float: + case AnyObject::Float: return "Float"; - case ObjectType::String: + case AnyObject::String: return "String"; - case ObjectType::Array: + case AnyObject::Array: return "Array"; default: return "Unknown"; @@ -68,50 +57,23 @@ std::string AddVariableNode::description() const AddReferenceVariableNode::AddReferenceVariableNode(std::string referenceName_, std::string boundName_, - ObjectType boundType_) + AnyObject::Type boundType_) : AddVariableNode(boundName_, boundType_), referenceName(referenceName_) { } -BaseObject::Ptr AddReferenceVariableNode::evaluate(Scope &scope) +AnyObject::Ptr AddReferenceVariableNode::evaluate(Scope &scope) { // 1. Lookup the object associated with the variable name defined in this // scope or a parent scope (no issue with lifetimes such as to be bound // object going out of scope before our reference. - BaseObject::Ptr boundObject = scope.getNamedObject(name()); - - // 2. Type checking. The type of the reference must match that of the bound - // object. - bool passesTypeChecking{false}; - - // TODO: - add type checking for classes and struct references. - switch (_variableType) - { - case ObjectType::Int: - passesTypeChecking = boundObject->isObjectType(); - break; - case ObjectType::Float: - passesTypeChecking = boundObject->isObjectType(); - break; - case ObjectType::String: - passesTypeChecking = boundObject->isObjectType(); - break; - case ObjectType::Bool: - passesTypeChecking = boundObject->isObjectType(); - break; - case ObjectType::Array: - passesTypeChecking = boundObject->isObjectType(); - break; - case ObjectType::Struct: - case ObjectType::Class: - default: - passesTypeChecking = true; - break; // No type checking currently! - } + AnyObject::Ptr boundObject = scope.getNamedObject(name()); - if (!passesTypeChecking) + // TODO: - this will not work for classes/structs since they could point to different types. + // 2. Type checking. The type of the reference must match that of the bound object. + if (!passesAssignmentTypeCheck(*boundObject)) { ThrowException("Cannot bind reference " + referenceName + " to variable " + name() + ". Types do not match!"); } diff --git a/src/nodes/AddVariableNode.hpp b/src/nodes/AddVariableNode.hpp index a2ee7bd..ba24698 100644 --- a/src/nodes/AddVariableNode.hpp +++ b/src/nodes/AddVariableNode.hpp @@ -8,33 +8,28 @@ */ #pragma once -#include "BaseObject.hpp" +#include "AnyObject.hpp" #include "LookupVariableNode.hpp" -#include "ObjectTypes.hpp" #include "Scope.hpp" + class AddVariableNode : public LookupVariableNode { public: using Ptr = std::shared_ptr; - AddVariableNode(std::string name, ObjectType type) - : LookupVariableNode(std::move(name)), - _variableType(type) - { - setType(NodeType::AddVariable); - } + AddVariableNode(std::string name, AnyObject::Type type); // Creates a new empty variable of a given type to the scope (i.e. int a;). - BaseObject::Ptr evaluate(Scope &scope) override; + std::shared_ptr evaluate(Scope &scope) override; std::string description() const; // Type checking for variable assignment. - bool passesAssignmentTypeCheck(const BaseObject &assignObject) const; + bool passesAssignmentTypeCheck(const AnyObject &assignObject) const; protected: - const ObjectType _variableType; + const AnyObject::Type _variableType; }; @@ -50,14 +45,14 @@ class AddReferenceVariableNode : public AddVariableNode * @param boundName_ Name of the variable to be bound to the reference. * @param boundType_ Type of the bound variable. To be checked when evaluate() called. */ - AddReferenceVariableNode(std::string referenceName_, std::string boundName_, ObjectType boundType_); + AddReferenceVariableNode(std::string referenceName_, std::string boundName_, AnyObject::Type boundType_); /** * @param scope * @return Pointer to the object in the scope now bound to the variable name and * the reference name. */ - BaseObject::Ptr evaluate(Scope &scope) override; + std::shared_ptr evaluate(Scope &scope) override; protected: const std::string referenceName; diff --git a/src/nodes/AnyNode.cpp b/src/nodes/AnyNode.cpp new file mode 100644 index 0000000..772e357 --- /dev/null +++ b/src/nodes/AnyNode.cpp @@ -0,0 +1,17 @@ +/** + * @file AnyNode.cpp + * @author Edward Palmer + * @date 2025-05-19 + * + * @copyright Copyright (c) 2025 + * + */ + + +#include "AnyNode.hpp" +#include "AnyObject.hpp" + +std::shared_ptr AnyNode::evaluate(Scope &scope) +{ + return _evaluateFunc(scope); +} \ No newline at end of file diff --git a/src/nodes/AnyNode.hpp b/src/nodes/AnyNode.hpp index 7022921..1ff789d 100644 --- a/src/nodes/AnyNode.hpp +++ b/src/nodes/AnyNode.hpp @@ -9,7 +9,6 @@ #pragma once #include "BaseNode.hpp" -#include "BaseObject.hpp" #include "PropertyInterface.hpp" #include "Scope.hpp" #include @@ -21,15 +20,12 @@ class AnyNode : public BaseNode public: using Ptr = std::shared_ptr; - using EvaluateFunction = std::function; + using EvaluateFunction = std::function(Scope &)>; explicit AnyNode(NodeType type, EvaluateFunction &&evaluateFunc) : BaseNode(type), _evaluateFunc(std::move(evaluateFunc)) {} - BaseObject::Ptr evaluate(Scope &scope) final - { - return _evaluateFunc(scope); - } + std::shared_ptr evaluate(Scope &scope) final; private: EvaluateFunction _evaluateFunc; @@ -45,7 +41,7 @@ class AnyPropertyNode : public AnyNode, public PropertyInterface : AnyNode(type, std::move(evaluateFunc)), _evaluateNoCloneFunc(std::move(evaluateNoCloneFunc)) {} - BaseObject::Ptr evaluateNoClone(Scope &scope) final + std::shared_ptr evaluateNoClone(Scope &scope) final { return _evaluateNoCloneFunc(scope); } diff --git a/src/nodes/BaseNode.hpp b/src/nodes/BaseNode.hpp index 6820e83..79d8133 100644 --- a/src/nodes/BaseNode.hpp +++ b/src/nodes/BaseNode.hpp @@ -8,9 +8,6 @@ */ #pragma once -#include "BaseObject.hpp" -#include "BaseObjectT.hpp" -#include "Scope.hpp" #include #include @@ -83,28 +80,7 @@ class BaseNode return type() == other.type(); } - virtual BaseObject::Ptr evaluate(Scope &scope) = 0; /* TODO: - move to shared pointers */ - - /* Evaluates object */ - template - std::shared_ptr evaluate(Scope &scope) - { - return std::static_pointer_cast(evaluate(scope)); - } - - /* Evaluates object and returns the object's stored value directly */ - template - TValue &evaluateObject(Scope &scope) - { - /* BaseObject ptr */ - BaseObject::Ptr baseObjPtr = evaluate(scope); - - /* Up-cast */ - auto &upcastedObj = static_cast &>(*baseObjPtr); - - /* Apply operator overload to get stored value */ - return (*upcastedObj); - } + virtual std::shared_ptr evaluate(class Scope &scope) = 0; // TODO: - can this be const-cast? void setType(NodeType type) { diff --git a/src/nodes/BinaryNode.cpp b/src/nodes/BinaryNode.cpp index 35bcd89..320081e 100644 --- a/src/nodes/BinaryNode.cpp +++ b/src/nodes/BinaryNode.cpp @@ -8,9 +8,8 @@ */ #include "BinaryNode.hpp" -#include "ArrayObject.hpp" +#include "Exceptions.hpp" #include "ObjectFactory.hpp" -#include "Objects.hpp" #include @@ -47,7 +46,7 @@ BinaryOperatorType BinaryNode::toBinaryOperator(const std::string &operatorStrin } -BaseObject::Ptr BinaryNode::evaluate(Scope &scope) +AnyObject::Ptr BinaryNode::evaluate(Scope &scope) { auto leftEvaluated = _left->evaluate(scope); auto rightEvaluated = _right->evaluate(scope); @@ -57,119 +56,160 @@ BaseObject::Ptr BinaryNode::evaluate(Scope &scope) } -BaseObject::Ptr BinaryNode::applyOperator(const IntObject &left, const IntObject &right) const +AnyObject::Ptr BinaryNode::applyOperator(const AnyObject &left, const AnyObject &right) const +{ + if (left.isType(AnyObject::Bool) && right.isType(AnyObject::Bool)) + { + return applyOperator(left.getValue(), right.getValue()); + } + if (left.isType(AnyObject::Int) && right.isType(AnyObject::Int)) + { + return applyOperator(left.getValue(), right.getValue()); + } + else if (left.isType(AnyObject::Float) && right.isType(AnyObject::Float)) /* Implicit casts */ + { + return applyOperator(left.getValue(), right.getValue()); + } + else if (left.isType(AnyObject::Int) && right.isType(AnyObject::Float)) + { + return applyOperator((double)left.getValue(), right.getValue()); + } + else if (left.isType(AnyObject::Float) && right.isType(AnyObject::Int)) + { + return applyOperator(left.getValue(), (double)right.getValue()); + } + else if (left.isType(AnyObject::String) && right.isType(AnyObject::String)) + { + return applyOperator(left.getValue(), right.getValue()); + } + else if (left.isType(AnyObject::Array) && right.isType(AnyObject::Array)) + { + return applyOperator(left.getValue(), right.getValue()); + } + + std::stringstream oss; + oss << "cannot apply operator [" << int(_binaryOperator) << "] to objects [" << left << "] and [" << right << "]"; + + ThrowException(oss.str()); +} + + +AnyObject::Ptr BinaryNode::applyOperator(const AnyObject::Vector &left, const AnyObject::Vector &right) const +{ + switch (_binaryOperator) + { + case BinaryOperatorType::Add: + { + // TODO: - this will probably require us to copy the vector otherwise we're doing shallow copying + AnyObject::Vector result; + + result.reserve(left.size() + right.size()); + result.insert(result.end(), left.begin(), left.end()); + result.insert(result.end(), right.begin(), right.end()); + + return ObjectFactory::allocate(std::move(result)); + } + default: + ThrowException("cannot apply operator to types Array, Array"); + } +} + + +AnyObject::Ptr BinaryNode::applyOperator(bool left, bool right) const +{ + switch (_binaryOperator) + { + case BinaryOperatorType::Equal: + return ObjectFactory::allocate(left == right); + case BinaryOperatorType::NotEqual: + return ObjectFactory::allocate(left != right); + case BinaryOperatorType::And: + return ObjectFactory::allocate(left && right); + case BinaryOperatorType::Or: + return ObjectFactory::allocate(left != right); + default: + ThrowException("cannot apply operator to types Bool, Bool"); + } +} + + +AnyObject::Ptr BinaryNode::applyOperator(long left, long right) const { switch (_binaryOperator) { case BinaryOperatorType::Add: - return ObjectFactory::allocate(left + right); + return ObjectFactory::allocate(left + right); case BinaryOperatorType::Minus: - return ObjectFactory::allocate(left - right); + return ObjectFactory::allocate(left - right); case BinaryOperatorType::Multiply: - return ObjectFactory::allocate(left * right); + return ObjectFactory::allocate(left * right); case BinaryOperatorType::Divide: - return ObjectFactory::allocate(left / right); + return ObjectFactory::allocate(left / right); case BinaryOperatorType::Equal: - return ObjectFactory::allocate(left == right); + return ObjectFactory::allocate(left == right); case BinaryOperatorType::NotEqual: - return ObjectFactory::allocate(left != right); + return ObjectFactory::allocate(left != right); case BinaryOperatorType::GreaterOrEqual: - return ObjectFactory::allocate(left >= right); + return ObjectFactory::allocate(left >= right); case BinaryOperatorType::Greater: - return ObjectFactory::allocate(left > right); + return ObjectFactory::allocate(left > right); case BinaryOperatorType::LessOrEqual: - return ObjectFactory::allocate(left <= right); + return ObjectFactory::allocate(left <= right); case BinaryOperatorType::Less: - return ObjectFactory::allocate(left < right); + return ObjectFactory::allocate(left < right); case BinaryOperatorType::Modulo: - return ObjectFactory::allocate(left % right); + return ObjectFactory::allocate(left % right); case BinaryOperatorType::And: - return ObjectFactory::allocate(left && right); + return ObjectFactory::allocate(left && right); case BinaryOperatorType::Or: - return ObjectFactory::allocate(left || right); + return ObjectFactory::allocate(left || right); default: ThrowException("cannot apply operator to types Int, Int"); } } -BaseObject::Ptr BinaryNode::applyOperator(const FloatObject &left, const FloatObject &right) const +AnyObject::Ptr BinaryNode::applyOperator(double left, double right) const { switch (_binaryOperator) { case BinaryOperatorType::Add: - return ObjectFactory::allocate(left + right); + return ObjectFactory::allocate(left + right); case BinaryOperatorType::Minus: - return ObjectFactory::allocate(left - right); + return ObjectFactory::allocate(left - right); case BinaryOperatorType::Multiply: - return ObjectFactory::allocate(left * right); + return ObjectFactory::allocate(left * right); case BinaryOperatorType::Divide: - return ObjectFactory::allocate(left / right); + return ObjectFactory::allocate(left / right); case BinaryOperatorType::Equal: - return ObjectFactory::allocate(left == right); + return ObjectFactory::allocate(left == right); case BinaryOperatorType::NotEqual: - return ObjectFactory::allocate(left != right); + return ObjectFactory::allocate(left != right); case BinaryOperatorType::GreaterOrEqual: - return ObjectFactory::allocate(left >= right); + return ObjectFactory::allocate(left >= right); case BinaryOperatorType::Greater: - return ObjectFactory::allocate(left > right); + return ObjectFactory::allocate(left > right); case BinaryOperatorType::LessOrEqual: - return ObjectFactory::allocate(left <= right); + return ObjectFactory::allocate(left <= right); case BinaryOperatorType::Less: - return ObjectFactory::allocate(left < right); + return ObjectFactory::allocate(left < right); default: ThrowException("cannot apply operator to types Float, Float"); } } -BaseObject::Ptr BinaryNode::applyOperator(const StringObject &left, const StringObject &right) const +AnyObject::Ptr BinaryNode::applyOperator(const std::string &left, const std::string &right) const { switch (_binaryOperator) { case BinaryOperatorType::Add: - return ObjectFactory::allocate(left + right); + return ObjectFactory::allocate(left + right); case BinaryOperatorType::Equal: - return ObjectFactory::allocate(left == right); + return ObjectFactory::allocate(left == right); case BinaryOperatorType::NotEqual: - return ObjectFactory::allocate(left != right); + return ObjectFactory::allocate(left != right); default: ThrowException("cannot apply operator to types String, String"); } } - - -BaseObject::Ptr BinaryNode::applyOperator(const BaseObject &left, const BaseObject &right) const -{ - // TODO: - implement + operator for other object types. Will mean we don't need any if/else statements. - - if (left.isObjectType() && right.isObjectType()) - { - return applyOperator(left.castObject(), right.castObject()); - } - else if (left.isObjectType() && right.isObjectType()) - { - return applyOperator(left.castObject(), right.castObject()); - } - else if (left.isObjectType() && right.isObjectType()) - { - return applyOperator(left.castObject().castToFloat(), right.castObject()); - } - else if (left.isObjectType() && right.isObjectType()) - { - return applyOperator(left.castObject(), right.castObject().castToFloat()); - } - else if (left.isObjectType() && right.isObjectType()) - { - return applyOperator(left.castObject(), right.castObject()); - } - else if (left.isObjectType() && right.isObjectType()) - { - return ObjectFactory::allocate(left.castObject() + right.castObject()); - } - - std::stringstream oss; - oss << "cannot apply operator [" << int(_binaryOperator) << "] to objects [" << left << "] and [" << right << "]"; - - ThrowException(oss.str()); -} diff --git a/src/nodes/BinaryNode.hpp b/src/nodes/BinaryNode.hpp index ea4379b..abfc058 100644 --- a/src/nodes/BinaryNode.hpp +++ b/src/nodes/BinaryNode.hpp @@ -8,12 +8,9 @@ */ #pragma once +#include "AnyObject.hpp" #include "BaseNode.hpp" -#include "BaseObject.hpp" -#include "FloatObject.hpp" -#include "IntObject.hpp" #include "Scope.hpp" -#include "StringObject.hpp" #include enum class BinaryOperatorType : int @@ -46,14 +43,16 @@ class BinaryNode : public BaseNode setType(NodeType::Binary); } - BaseObject::Ptr evaluate(Scope &scope) override; + AnyObject::Ptr evaluate(Scope &scope) override; protected: - BaseObject::Ptr applyOperator(const BaseObject &left, const BaseObject &right) const; + AnyObject::Ptr applyOperator(const AnyObject &left, const AnyObject &right) const; - BaseObject::Ptr applyOperator(const IntObject &left, const IntObject &right) const; - BaseObject::Ptr applyOperator(const FloatObject &left, const FloatObject &right) const; - BaseObject::Ptr applyOperator(const StringObject &left, const StringObject &right) const; + AnyObject::Ptr applyOperator(bool left, bool right) const; + AnyObject::Ptr applyOperator(long left, long right) const; + AnyObject::Ptr applyOperator(double left, double right) const; + AnyObject::Ptr applyOperator(const std::string &left, const std::string &right) const; + AnyObject::Ptr applyOperator(const AnyObject::Vector &left, const AnyObject::Vector &right) const; /* Convert string to enum (faster if doing lost of comparisons) */ static BinaryOperatorType toBinaryOperator(const std::string &operatorString); diff --git a/src/nodes/ClassDefinitionObject.cpp b/src/nodes/ClassDefinitionNode.cpp similarity index 54% rename from src/nodes/ClassDefinitionObject.cpp rename to src/nodes/ClassDefinitionNode.cpp index bea772e..e70b26d 100644 --- a/src/nodes/ClassDefinitionObject.cpp +++ b/src/nodes/ClassDefinitionNode.cpp @@ -1,5 +1,5 @@ /** - * @file ClassDefinitionObject.cpp + * @file ClassDefinitionNode.cpp * @author Edward Palmer * @date 2024-11-30 * @@ -7,22 +7,25 @@ * */ -#include "ClassDefinitionObject.hpp" +#include "ClassDefinitionNode.hpp" +#include "AnyObject.hpp" +#include "ObjectFactory.hpp" -ClassDefinitionObject::ClassDefinitionObject(std::string typeName_, - std::string parentTypeName_, - std::vector variableDefs_, - std::vector methodDefs_) - : StructDefinitionObject(std::move(typeName_), std::move(parentTypeName_), std::move(variableDefs_)), + +ClassDefinitionNode::ClassDefinitionNode(std::string typeName_, + std::string parentTypeName_, + std::vector variableDefs_, + std::vector methodDefs_) + : StructDefinitionNode(std::move(typeName_), std::move(parentTypeName_), std::move(variableDefs_)), methodDefs(std::move(methodDefs_)) { setType(NodeType::ClassDefinition); } -BaseObject::Ptr ClassDefinitionObject::evaluate(Scope &scope) +AnyObject::Ptr ClassDefinitionNode::evaluate(Scope &scope) { - // NB: override method defined in StructDefinitionObject. + // NB: override method defined in StructDefinitionNode. if (active) { ThrowException(typeName + " is already defined"); @@ -30,19 +33,20 @@ BaseObject::Ptr ClassDefinitionObject::evaluate(Scope &scope) active = true; - // TODO: - would be nice to have single method we override form StructDefinitionObject + // TODO: - would be nice to have single method we override form StructDefinitionNode // and then we can call base method and just extend it. buildVariableDefHashMap(scope); buildMethodDefsHashMap(scope); - // NB: scope cannot manage lifetime of this definition currently since it - // is owned by the AST. - scope.linkObject(typeName, shared_from_this()); - return shared_from_this(); + /* NB: wrap-up in an object shared pointer */ + auto objectWrapper = ObjectFactory::allocate(shared_from_this(), AnyObject::_ClassDefinition); + + scope.linkObject(typeName, objectWrapper); + return objectWrapper; } -void ClassDefinitionObject::installMethodsInScope(Scope &scope) const +void ClassDefinitionNode::installMethodsInScope(Scope &scope) const { if (!active) { @@ -56,14 +60,14 @@ void ClassDefinitionObject::installMethodsInScope(Scope &scope) const } -void ClassDefinitionObject::buildMethodDefsHashMap(const Scope &scope) +void ClassDefinitionNode::buildMethodDefsHashMap(const Scope &scope) { if (!allMethodDefsMap.empty()) { return; } - auto parent = std::static_pointer_cast(lookupParent(scope)); + auto parent = std::static_pointer_cast(lookupParent(scope)); if (parent) { parent->buildMethodDefsHashMap(scope); // Unnecessary since to be installed, this will already have happened. diff --git a/src/nodes/ClassDefinitionObject.hpp b/src/nodes/ClassDefinitionNode.hpp similarity index 72% rename from src/nodes/ClassDefinitionObject.hpp rename to src/nodes/ClassDefinitionNode.hpp index 71c7a61..6197295 100644 --- a/src/nodes/ClassDefinitionObject.hpp +++ b/src/nodes/ClassDefinitionNode.hpp @@ -1,5 +1,5 @@ /** - * @file ClassDefinitionObject.hpp + * @file ClassDefinitionNode.hpp * @author Edward Palmer * @date 2024-11-28 * @@ -8,18 +8,21 @@ */ #pragma once -#include "BaseObject.hpp" +#include "AddVariableNode.hpp" #include "FunctionNode.hpp" -#include "StructDefinitionObject.hpp" +#include "Scope.hpp" +#include "StructDefinitionNode.hpp" +#include #include #include + /** * This class defines the format of a class and the variables and methods stored * inside. It will be stored in the scope along with the class name. We can then * use this to construct class instances. */ -class ClassDefinitionObject : public StructDefinitionObject +class ClassDefinitionNode : public StructDefinitionNode { public: /** @@ -27,20 +30,20 @@ class ClassDefinitionObject : public StructDefinitionObject * pass vectors to the variables and methods we actually own. The others * will be in a parent class if provided. */ - ClassDefinitionObject(std::string typeName_, - std::string parentTypeName_, - std::vector variableDefs_, - std::vector methodDefs_); + ClassDefinitionNode(std::string typeName_, + std::string parentTypeName_, + std::vector> variableDefs_, + std::vector> methodDefs_); /** * Destructor deletes all method definition nodes. */ - ~ClassDefinitionObject() override = default; + ~ClassDefinitionNode() override = default; /** * Install object in current scope. */ - BaseObject::Ptr evaluate(Scope &scope) override; + std::shared_ptr evaluate(Scope &scope) override; /** * Calls evaluate method on all method nodes. Installs them in argument scope. diff --git a/src/nodes/ClassNode.cpp b/src/nodes/ClassNode.cpp new file mode 100644 index 0000000..f06fc3c --- /dev/null +++ b/src/nodes/ClassNode.cpp @@ -0,0 +1,47 @@ +/** + * @file ClassNode.cpp + * @author Edward Palmer + * @date 2024-11-30 + * + * @copyright Copyright (c) 2024 + * + */ + +#include "ClassNode.hpp" +#include "AnyObject.hpp" +#include "FunctionNode.hpp" +#include "ObjectFactory.hpp" +#include + + +ClassNode::ClassNode(std::string typeName_, std::string name_) + : StructNode(std::move(typeName_), std::move(name_)) +{ +} + +AnyObject::Ptr ClassNode::evaluate(Scope &scope) +{ + // TODO: - inefficient, should have another method we can call to do most of StructNode::evaluate. + if (active) + { + ThrowException("ClassNode named " + name + " of type " + typeName + " is already active"); + } + + active = true; + + // Initialize our instance from the struct definition defined in the scope. + auto theObject = scope.getNamedObject(typeName); + + structDefinition = std::static_pointer_cast(theObject->getValue()); + structDefinition->installVariablesInScope(_instanceScope, variableNames); + + auto classDefinition = std::static_pointer_cast(structDefinition); + classDefinition->installMethodsInScope(_instanceScope); + + // Add the active struct instance to the scope. TODO: - transfer ownership + // to the scope. Will have to remove this class from AST to do this correctly. + auto wrappedClass = ObjectFactory::allocate(shared_from_this(), AnyObject::Class); + + scope.linkObject(name, wrappedClass); + return wrappedClass; +} \ No newline at end of file diff --git a/src/nodes/ClassNode.hpp b/src/nodes/ClassNode.hpp new file mode 100644 index 0000000..523e1c7 --- /dev/null +++ b/src/nodes/ClassNode.hpp @@ -0,0 +1,36 @@ +/** + * @file ClassNode.hpp + * @author Edward Palmer + * @date 2024-11-29 + * + * @copyright Copyright (c) 2024 + * + */ + +#pragma once +#include "ClassDefinitionNode.hpp" +#include "Exceptions.hpp" +#include "StructNode.hpp" +#include +#include + +/** + * An instance of a class defined by the ClassDefinitionNode. + */ +class ClassNode : public StructNode +{ +public: + ClassNode(std::string typeName_, std::string name_); + + // ClassNode &operator=(const BaseObject &) override + // { + // ThrowException("Not implemented"); + // } + + /** + * Finishes initializing the class object and links to the scope. + * @param scope The scope in which to add the instance. + * @return BaseObject* Pointer to itself + */ + std::shared_ptr evaluate(Scope &scope) override; +}; \ No newline at end of file diff --git a/src/nodes/FunctionCallNode.cpp b/src/nodes/FunctionCallNode.cpp index ad462c1..3f5a641 100644 --- a/src/nodes/FunctionCallNode.cpp +++ b/src/nodes/FunctionCallNode.cpp @@ -8,23 +8,26 @@ */ #include "FunctionCallNode.hpp" +#include "AddVariableNode.hpp" +#include "AnyObject.hpp" +#include "Exceptions.hpp" #include "FunctionNode.hpp" -#include "FunctionObject.hpp" #include "JumpPoints.hpp" -#include "ModuleFunctionObject.hpp" +#include "ModuleFunctor.hpp" +#include "Scope.hpp" -BaseObject::Ptr FunctionCallNode::evaluate(Scope &scope) +AnyObject::Ptr FunctionCallNode::evaluate(Scope &scope) { // 0. Any library functions that we wish to evaluate. - auto libraryFunc = scope.getOptionalNamedObject(_funcName); - if (libraryFunc && libraryFunc->isObjectType()) + auto moduleFunction = scope.getOptionalNamedObject(_funcName); + if (moduleFunction && moduleFunction->isType(AnyObject::_ModuleFunction)) { - return libraryFunc->castObject()(_funcArgs, scope); + return moduleFunction->getValue()(_funcArgs, scope); } // TODO: - finish implementing here. Should not be a shared pointer. // 1. Get a pointer to the function node stored in this scope. - auto funcNode = scope.getNamedObject(_funcName)->value(); + auto funcNode = std::static_pointer_cast(scope.getNamedObject(_funcName)->getValue()); // 2. Verify that the number of arguments matches those required for the // function we are calling. @@ -77,7 +80,8 @@ BaseObject::Ptr FunctionCallNode::evaluate(Scope &scope) return evaluateFunctionBody(*funcNode->funcBody, funcScope); } -BaseObject::Ptr FunctionCallNode::evaluateFunctionBody(BaseNode &funcBody, Scope &funcScope) + +AnyObject::Ptr FunctionCallNode::evaluateFunctionBody(BaseNode &funcBody, Scope &funcScope) { // Reset return value. gEnvironmentContext.returnValue = nullptr; diff --git a/src/nodes/FunctionCallNode.hpp b/src/nodes/FunctionCallNode.hpp index 6cba004..4cb9563 100644 --- a/src/nodes/FunctionCallNode.hpp +++ b/src/nodes/FunctionCallNode.hpp @@ -8,10 +8,7 @@ */ #pragma once -#include "AddVariableNode.hpp" #include "BaseNode.hpp" -#include "BaseObject.hpp" -#include "Scope.hpp" #include #include #include @@ -34,9 +31,9 @@ class FunctionCallNode : public BaseNode // TODO: - don't forget to do performance profiling for Fib sequence and see memory requirements for old and new version // TODO: - create a new PR after this for parser to store all nodes in AST in flat array using pointers with method to delete by walking along array. - BaseObject::Ptr evaluate(Scope &scope) override; + std::shared_ptr evaluate(class Scope &scope) override; - BaseObject::Ptr evaluateFunctionBody(BaseNode &funcBody, Scope &funcScope); + std::shared_ptr evaluateFunctionBody(BaseNode &funcBody, class Scope &funcScope); std::string _funcName; BaseNodePtrVector _funcArgs{nullptr}; diff --git a/src/nodes/FunctionNode.cpp b/src/nodes/FunctionNode.cpp index 3d0fd8a..b12052f 100644 --- a/src/nodes/FunctionNode.cpp +++ b/src/nodes/FunctionNode.cpp @@ -8,16 +8,16 @@ */ #include "FunctionNode.hpp" -#include "FunctionObject.hpp" +#include "AnyObject.hpp" #include "ObjectFactory.hpp" +#include "Scope.hpp" /// Create a new FunctionObject from a FunctionNode and register in current scope. -BaseObject::Ptr FunctionNode::evaluate(Scope &scope) +AnyObject::Ptr FunctionNode::evaluate(Scope &scope) { // TODO: - I think this creates a strong-reference cycle! Need to break the chain here - auto functionObject = ObjectFactory::allocate(shared_from_this()); + auto functionObject = ObjectFactory::allocate(shared_from_this(), AnyObject::_UserFunction); scope.linkObject(_funcName, functionObject); - return functionObject; } \ No newline at end of file diff --git a/src/nodes/FunctionNode.hpp b/src/nodes/FunctionNode.hpp index bb9663e..34d3ed0 100644 --- a/src/nodes/FunctionNode.hpp +++ b/src/nodes/FunctionNode.hpp @@ -9,8 +9,8 @@ #pragma once #include "BaseNode.hpp" -#include "BaseObject.hpp" #include "FunctionCallNode.hpp" +#include #include @@ -28,7 +28,7 @@ class FunctionNode : public FunctionCallNode, public std::enable_shared_from_thi ~FunctionNode() override = default; - BaseObject::Ptr evaluate(Scope &scope) override; + std::shared_ptr evaluate(class Scope &scope) override; BaseNode::Ptr funcBody{nullptr}; }; diff --git a/src/nodes/LookupVariableNode.cpp b/src/nodes/LookupVariableNode.cpp index 6afe264..ce44c2a 100644 --- a/src/nodes/LookupVariableNode.cpp +++ b/src/nodes/LookupVariableNode.cpp @@ -8,9 +8,9 @@ */ #include "LookupVariableNode.hpp" +#include "AnyObject.hpp" - -BaseObject::Ptr LookupVariableNode::evaluate(Scope &scope) +AnyObject::Ptr LookupVariableNode::evaluate(Scope &scope) { return scope.getNamedObject(_name); } \ No newline at end of file diff --git a/src/nodes/LookupVariableNode.hpp b/src/nodes/LookupVariableNode.hpp index f3687a8..f73854e 100644 --- a/src/nodes/LookupVariableNode.hpp +++ b/src/nodes/LookupVariableNode.hpp @@ -9,6 +9,7 @@ #pragma once #include "BaseNode.hpp" +#include "Scope.hpp" #include class LookupVariableNode : public BaseNode @@ -22,7 +23,7 @@ class LookupVariableNode : public BaseNode [[nodiscard]] inline const std::string &name() const; /* Returns the object in the scope associated with a variable name */ - BaseObject::Ptr evaluate(Scope &scope) override; + std::shared_ptr evaluate(Scope &scope) override; private: std::string _name; diff --git a/src/nodes/ModuleNodeFactory.cpp b/src/nodes/ModuleNodeFactory.cpp index 2f2bfbc..9cd1706 100644 --- a/src/nodes/ModuleNodeFactory.cpp +++ b/src/nodes/ModuleNodeFactory.cpp @@ -6,18 +6,17 @@ // #include "ModuleNodeFactory.hpp" -#include "ArrayObject.hpp" #include "BaseNode.hpp" -#include "FloatObject.hpp" -#include "IntObject.hpp" +#include "Exceptions.hpp" #include "Logger.hpp" #include "NodeFactory.hpp" -#include "Objects.hpp" -#include "StringObject.hpp" +#include "ObjectFactory.hpp" #include "Stringify.hpp" +#include #include #include + namespace NodeFactory { @@ -66,8 +65,8 @@ AnyNode::Ptr createMathModuleNode() { assert(callArgs.size() == 1); - auto first = callArgs.front()->evaluate(scope); - return ObjectFactory::allocate(sqrt(first->value())); + double first = callArgs.front()->evaluate(scope)->getValue(); + return ObjectFactory::allocate(sqrt(first)); }); auto doPow = std::pair("pow", [](BaseNodePtrVector &callArgs, Scope &scope) @@ -75,11 +74,17 @@ AnyNode::Ptr createMathModuleNode() assert(callArgs.size() == 2); /* TODO: - bug if pass in an integer rather than a floatObject */ + auto firstObject = callArgs.front()->evaluate(scope); + auto secondObject = callArgs.front()->evaluate(scope); - auto &first = callArgs.front()->evaluate(scope)->castObject(); - auto &second = callArgs.back()->evaluate(scope)->castObject(); - - return ObjectFactory::allocate(pow(first.value(), second.value())); + if (firstObject->isType(AnyObject::Int) && secondObject->isType(AnyObject::Int)) /* Implicit casts */ + { + return ObjectFactory::allocate(pow(firstObject->getValue(), secondObject->getValue())); + } + else /* Assume both are doubles */ + { + return ObjectFactory::allocate(pow(firstObject->getValue(), secondObject->getValue())); + } }); return NodeFactory::createModuleNode("math", {doSqrt, doPow}); @@ -92,8 +97,10 @@ AnyNode::Ptr createArrayModuleNode() { assert(callArgs.size() == 1); - auto arrayObject = callArgs.front()->evaluate(scope); - arrayObject->value().clear(); + auto theObject = callArgs.front()->evaluate(scope); /* Careful if using a reference! Need to not hold reference to garbage! */ + + auto &arrayObject = theObject->getValue(); + arrayObject.clear(); return nullptr; }); @@ -101,19 +108,23 @@ AnyNode::Ptr createArrayModuleNode() { assert(callArgs.size() == 1); - auto arrayObject = callArgs.front()->evaluate(scope); + auto theObject = callArgs.front()->evaluate(scope); - return ObjectFactory::allocate(arrayObject->value().size()); + auto &arrayObject = theObject->getValue(); + + return ObjectFactory::allocate((double)arrayObject.size()); }); auto doAppend = std::pair("append", [](BaseNodePtrVector &callArgs, Scope &scope) { assert(callArgs.size() == 2); - auto arrayObject = callArgs.front()->evaluate(scope); + auto theObject = callArgs.front()->evaluate(scope); + + auto &arrayObject = theObject->getValue(); auto someObject = callArgs.back()->evaluate(scope); - arrayObject->value().push_back(someObject->clone()); // NB: must clone! + arrayObject.push_back(someObject->clone()); // NB: must clone! return nullptr; }); @@ -133,14 +144,14 @@ AnyNode::Ptr createTestModuleNode() assert(callArgs.size() == 2); - auto result = callArgs.front()->evaluate(scope); - auto description = callArgs.back()->evaluate(scope); + bool result = callArgs.front()->evaluate(scope)->getValue(); + std::string description = callArgs.back()->evaluate(scope)->getValue(); /* Be very careful -> copy otherwise referencing garbage! */ // Print pass or fail depending on the test case. - const char *statusString = result->value() ? "PASSED" : "FAILED"; - const char *statusColor = result->value() ? PassColor : FailColor; + const char *statusString = result ? "PASSED" : "FAILED"; + const char *statusColor = result ? PassColor : FailColor; - std::cout << stringify("%-50s %s%s%s", description->value().c_str(), statusColor, statusString, ClearColor) << std::endl; + std::cout << stringify("%-50s %s%s%s", description.c_str(), statusColor, statusString, ClearColor) << std::endl; return nullptr; }); diff --git a/src/nodes/NodeFactory.cpp b/src/nodes/NodeFactory.cpp index 366292e..7706534 100644 --- a/src/nodes/NodeFactory.cpp +++ b/src/nodes/NodeFactory.cpp @@ -9,18 +9,13 @@ #include "NodeFactory.hpp" #include "AddVariableNode.hpp" -#include "ArrayObject.hpp" -#include "BaseObject.hpp" -#include "ClassObject.hpp" -#include "FloatObject.hpp" +#include "ClassNode.hpp" #include "FunctionCallNode.hpp" -#include "IntObject.hpp" #include "JumpPoints.hpp" #include "LookupVariableNode.hpp" #include "ObjectFactory.hpp" #include "Scope.hpp" -#include "StringObject.hpp" -#include "StructObject.hpp" +#include "StructNode.hpp" #include #include #include @@ -32,7 +27,7 @@ AnyNode::Ptr createBoolNode(bool state) { return std::make_shared(NodeType::Bool, [state](Scope &) { - return ObjectFactory::allocate(state); + return ObjectFactory::allocate(state); }); } @@ -40,7 +35,7 @@ AnyNode::Ptr createIntNode(long value) { return std::make_shared(NodeType::Int, [value](Scope &) { - return ObjectFactory::allocate(value); + return ObjectFactory::allocate(value); }); } @@ -48,7 +43,7 @@ AnyNode::Ptr createStringNode(std::string value) { return std::make_shared(NodeType::String, [value = std::move(value)](Scope &) { - return ObjectFactory::allocate(value); + return ObjectFactory::allocate(value); }); } @@ -56,7 +51,7 @@ AnyNode::Ptr createFloatNode(double value) { return std::make_shared(NodeType::Float, [value](Scope &) { - return ObjectFactory::allocate(value); + return ObjectFactory::allocate(value); }); } @@ -64,12 +59,12 @@ AnyNode::Ptr createIfNode(BaseNode::Ptr condition, BaseNode::Ptr thenBranch, Bas { return std::make_shared(NodeType::If, [condition, thenBranch, elseBranch](Scope &scope) /* Use shared pointer to manage ownership */ { - if (condition->evaluate(scope)->value()) + if (condition->evaluate(scope)->getValue()) return thenBranch->evaluate(scope); else if (elseBranch) return elseBranch->evaluate(scope); else - return BaseObject::Ptr(); + return AnyObject::Ptr(); }); } @@ -89,7 +84,7 @@ AnyNode::Ptr createForLoopNode(BaseNode::Ptr init, BaseNode::Ptr condition, Base if (setjmp(local) != 1) { for (; - condition->evaluate(loopScope)->value(); + condition->evaluate(loopScope)->getValue(); update->evaluate(loopScope)) { (void)body->evaluate(loopScope); @@ -114,7 +109,7 @@ AnyNode::Ptr createWhileLoopNode(BaseNode::Ptr condition, BaseNode::Ptr body) { Scope loopScope(scope); // Extend scope. - while (condition->evaluate(scope)->value()) /* Memory leak */ + while (condition->evaluate(scope)->getValue()) { (void)body->evaluate(loopScope); } @@ -142,7 +137,7 @@ AnyNode::Ptr createDoWhileLoopNode(BaseNode::Ptr condition, BaseNode::Ptr body) do { (void)body->evaluate(loopScope); - } while (condition->evaluate(scope)->value()); /* NB: evaluate in outerscope (no access to loop scope)*/ + } while (condition->evaluate(scope)->getValue()); /* NB: evaluate in outerscope (no access to loop scope) */ } // Restore original context. @@ -185,9 +180,9 @@ AnyNode::Ptr createNotNode(BaseNode::Ptr expression) { return std::make_shared(NodeType::Not, [expression](Scope &scope) { - auto result = expression->evaluate(scope); + AnyObject::Ptr result = expression->evaluate(scope); - return ObjectFactory::allocate(!result->value()); + return ObjectFactory::allocate(!result->getValue()); }); } @@ -228,10 +223,10 @@ AnyNode::Ptr createAssignNode(BaseNode::Ptr left, BaseNode::Ptr right) // Case 1: AddVariableNode -> we create default init object, add to scope and return. // Case 2: LookupVariableNode -> we object defined in scope (not cloned!) - TODO: - think about whether we should clone it. - BaseObject::Ptr objectLHS = left->evaluate(scope); + AnyObject::Ptr objectLHS = left->evaluate(scope); // Object we want to assign to LHS. - BaseObject::Ptr objectRHS = right->evaluate(scope); + AnyObject::Ptr objectRHS = right->evaluate(scope); // Update directly. TODO: - We will need to implement this for some object types still. *objectLHS = *objectRHS; @@ -245,7 +240,7 @@ AnyNode::Ptr createArrayNode(BaseNodePtrVector nodes) // TODO: - could treat as references in array? return std::make_shared(NodeType::Array, [nodes = std::move(nodes)](Scope &scope) { - BaseObjectPtrVector evaluatedObjects; + AnyObject::Vector evaluatedObjects; evaluatedObjects.reserve(nodes.size()); @@ -254,7 +249,7 @@ AnyNode::Ptr createArrayNode(BaseNodePtrVector nodes) evaluatedObjects.push_back(node->evaluate(scope)); } - return ObjectFactory::allocate(std::move(evaluatedObjects)); + return ObjectFactory::allocate(std::move(evaluatedObjects)); }); } @@ -282,14 +277,14 @@ AnyNode::Ptr createPrefixIncrementNode(BaseNode::Ptr expression) // 2. Object associated with variable name in scope must be integer or float. auto bodyEvaluated = expression->evaluate(scope); - if (bodyEvaluated->isObjectType()) + if (bodyEvaluated->isType(AnyObject::Int)) { - ++(bodyEvaluated->castObject()); + ++(bodyEvaluated->getValue()); return bodyEvaluated; } - else if (bodyEvaluated->isObjectType()) + else if (bodyEvaluated->isType(AnyObject::Float)) { - ++(bodyEvaluated->castObject()); + ++(bodyEvaluated->getValue()); return bodyEvaluated; } @@ -307,14 +302,14 @@ AnyNode::Ptr createPrefixDecrementNode(BaseNode::Ptr expression) // 2. Object associated with variable name in scope must be integer or float. auto bodyEvaluated = expression->evaluate(scope); - if (bodyEvaluated->isObjectType()) + if (bodyEvaluated->isType(AnyObject::Int)) { - --(bodyEvaluated->castObject()); + --(bodyEvaluated->getValue()); return bodyEvaluated; } - else if (bodyEvaluated->isObjectType()) + else if (bodyEvaluated->isType(AnyObject::Float)) { - --(bodyEvaluated->castObject()); + --(bodyEvaluated->getValue()); return bodyEvaluated; } @@ -329,12 +324,12 @@ AnyNode::Ptr createNegationNode(BaseNode::Ptr expression) { auto bodyEvaluated = expression->evaluate(scope); - BaseObject::Ptr result{nullptr}; + AnyObject::Ptr result{nullptr}; - if (bodyEvaluated->isObjectType()) - result = ObjectFactory::allocate(-bodyEvaluated->castObject()); - else if (bodyEvaluated->isObjectType()) - result = ObjectFactory::allocate(-bodyEvaluated->castObject()); + if (bodyEvaluated->isType(AnyObject::Int)) + result = ObjectFactory::allocate(-bodyEvaluated->getValue()); + else if (bodyEvaluated->isType(AnyObject::Float)) + result = ObjectFactory::allocate(-bodyEvaluated->getValue()); else ThrowException("invalid object type"); @@ -347,13 +342,16 @@ AnyPropertyNode::Ptr createStructAccessNode(std::string structVarName, std::stri { auto evaluateNoClone = [structVarName, memberVarName](Scope &scope) { - auto structObject = scope.getNamedObject(structVarName); - return structObject->instanceScope().getNamedObject(memberVarName); + auto theObject = scope.getNamedObject(structVarName); + + auto theStructObject = std::static_pointer_cast(theObject->getValue()); + + return theStructObject->instanceScope().getNamedObject(memberVarName); }; auto evaluate = [evaluateNoClone](Scope &scope) { - BaseObject::Ptr currentObject = evaluateNoClone(scope); + auto currentObject = evaluateNoClone(scope); return currentObject->clone(); }; @@ -369,14 +367,20 @@ AnyPropertyNode::Ptr createArrayAccessNode(BaseNode::Ptr arrayLookupNode, BaseNo auto evaluateNoClone = [arrayLookupNode, arrayIndexNode](Scope &scope) { // Lookup in array. - auto arrayObj = arrayLookupNode->evaluate(scope)->castObject(); + auto theArrayObject = arrayLookupNode->evaluate(scope); /* Careful with references! */ + + auto &arrayObj = theArrayObject->getValue(); + auto index = arrayIndexNode->evaluate(scope)->getValue(); - return arrayObj[arrayIndexNode->evaluateObject(scope)]; + if (index < 0 || index >= arrayObj.size()) + ThrowException("Array index [" + std::to_string(index) + "] is out of bounds!"); + + return arrayObj[index]; }; auto evaluate = [evaluateNoClone](Scope &scope) { - BaseObject::Ptr currentObject = evaluateNoClone(scope); + AnyObject::Ptr currentObject = evaluateNoClone(scope); return currentObject->clone(); }; @@ -384,13 +388,13 @@ AnyPropertyNode::Ptr createArrayAccessNode(BaseNode::Ptr arrayLookupNode, BaseNo } -AnyNode::Ptr createModuleNode(std::string moduleName, std::vector moduleFunctions) +AnyNode::Ptr createModuleNode(std::string moduleName, ModuleFunctor::Definitions moduleFunctions) { return std::make_shared(NodeType::Module, [moduleName = std::move(moduleName), moduleFunctions = std::move(moduleFunctions)](Scope &scope) { for (auto &it : moduleFunctions) /* Add to scope */ { - auto object = ObjectFactory::allocate(it.second); + auto object = ObjectFactory::allocate(ModuleFunctor(it.second)); scope.linkObject(it.first, object); } @@ -403,7 +407,9 @@ AnyNode::Ptr createClassMethodCallNode(std::string instanceName, FunctionCallNod return std::make_shared(NodeType::ClassMethodCall, [instanceName = std::move(instanceName), methodCallNode](Scope &scope) { - auto thisObject = scope.getNamedObject(instanceName); + auto anyObject = scope.getNamedObject(instanceName); + + auto thisObject = std::static_pointer_cast(anyObject->getValue()); // Important: to correctly evaluate the method, we need to add a parent scope // for the class instance temporarily each time we evaluate so function has @@ -411,7 +417,7 @@ AnyNode::Ptr createClassMethodCallNode(std::string instanceName, FunctionCallNod thisObject->instanceScope().setParentScope(&scope); /* But evaluate in class' instance scope */ - BaseObject::Ptr result = methodCallNode->evaluate(thisObject->instanceScope()); + AnyObject::Ptr result = methodCallNode->evaluate(thisObject->instanceScope()); // Set back to avoid problems if we forget to reset it in future. thisObject->instanceScope().setParentScope(nullptr); diff --git a/src/nodes/NodeFactory.hpp b/src/nodes/NodeFactory.hpp index 26b6634..ddcda7c 100644 --- a/src/nodes/NodeFactory.hpp +++ b/src/nodes/NodeFactory.hpp @@ -11,7 +11,7 @@ #include "AnyNode.hpp" #include "BaseNode.hpp" #include "FunctionCallNode.hpp" -#include "ModuleFunctionObject.hpp" +#include "ModuleFunctor.hpp" #include #include @@ -64,7 +64,7 @@ AnyPropertyNode::Ptr createStructAccessNode(std::string structVariableName, std: AnyPropertyNode::Ptr createArrayAccessNode(BaseNode::Ptr arrayLookupNode, BaseNode::Ptr arrayIndexNode); -AnyNode::Ptr createModuleNode(std::string moduleName = "", std::vector moduleFunctionPairs = {}); +AnyNode::Ptr createModuleNode(std::string moduleName = "", ModuleFunctor::Definitions moduleFunctionPairs = {}); AnyNode::Ptr createClassMethodCallNode(std::string instanceName, FunctionCallNode::Ptr methodCallNode); diff --git a/src/nodes/PropertyInterface.hpp b/src/nodes/PropertyInterface.hpp index 02456b5..14eb9f5 100644 --- a/src/nodes/PropertyInterface.hpp +++ b/src/nodes/PropertyInterface.hpp @@ -8,9 +8,7 @@ */ #pragma once - -class BaseObject; -class Scope; +#include "Scope.hpp" /** * @@ -28,7 +26,7 @@ struct PropertyInterface // virtual BaseObject *evaluate(Scope &scope) = 0; /* Return object directly for modifying value (setter) */ - virtual typename BaseObject::Ptr evaluateNoClone(Scope &scope) = 0; + virtual std::shared_ptr evaluateNoClone(Scope &scope) = 0; virtual ~PropertyInterface() = default; }; \ No newline at end of file diff --git a/src/nodes/StructDefinitionObject.cpp b/src/nodes/StructDefinitionNode.cpp similarity index 60% rename from src/nodes/StructDefinitionObject.cpp rename to src/nodes/StructDefinitionNode.cpp index 41b1b6d..bd2dd73 100644 --- a/src/nodes/StructDefinitionObject.cpp +++ b/src/nodes/StructDefinitionNode.cpp @@ -1,5 +1,5 @@ /** - * @file StructDefinitionObject.cpp + * @file StructDefinitionNode.cpp * @author Edward Palmer * @date 2024-11-30 * @@ -7,12 +7,15 @@ * */ -#include "StructDefinitionObject.hpp" +#include "StructDefinitionNode.hpp" +#include "AddVariableNode.hpp" +#include "AnyObject.hpp" +#include "ObjectFactory.hpp" +#include "Scope.hpp" - -StructDefinitionObject::StructDefinitionObject(std::string typeName_, - std::string parentTypeName_, - std::vector variableDefs_) +StructDefinitionNode::StructDefinitionNode(std::string typeName_, + std::string parentTypeName_, + std::vector variableDefs_) : typeName(std::move(typeName_)), parentTypeName(std::move(parentTypeName_)), variableDefs(std::move(variableDefs_)) @@ -21,18 +24,20 @@ StructDefinitionObject::StructDefinitionObject(std::string typeName_, } -std::shared_ptr StructDefinitionObject::lookupParent(const Scope &scope) const +std::shared_ptr StructDefinitionNode::lookupParent(const Scope &scope) const { if (parentTypeName.empty()) { return nullptr; } - return scope.getNamedObject(parentTypeName); + auto theObject = scope.getNamedObject(parentTypeName); + + return std::static_pointer_cast(theObject->getValue()); } -BaseObject::Ptr StructDefinitionObject::evaluate(Scope &scope) +AnyObject::Ptr StructDefinitionNode::evaluate(Scope &scope) { if (active) // Expect one definition only! { @@ -48,14 +53,15 @@ BaseObject::Ptr StructDefinitionObject::evaluate(Scope &scope) // prior to this. buildVariableDefHashMap(scope); - // NB: scope cannot manage lifetime of this definition currently since it - // is owned by the AST. TODO: - rectify this. - scope.linkObject(typeName, shared_from_this()); - return shared_from_this(); + /* NB: need to wrap-up in an object shared pointer */ + auto objectWrapper = ObjectFactory::allocate(shared_from_this(), AnyObject::_StructDefinition); + + scope.linkObject(typeName, objectWrapper); + return objectWrapper; } -void StructDefinitionObject::installVariablesInScope(Scope &scope, std::unordered_set &variableNames) const +void StructDefinitionNode::installVariablesInScope(Scope &scope, std::unordered_set &variableNames) const { if (!active) { @@ -72,14 +78,14 @@ void StructDefinitionObject::installVariablesInScope(Scope &scope, std::unordere } -void StructDefinitionObject::buildVariableDefHashMap(const Scope &scope) +void StructDefinitionNode::buildVariableDefHashMap(const Scope &scope) { if (!allVariableDefsMap.empty()) { return; // Already built. } - std::shared_ptr parent = lookupParent(scope); + auto parent = lookupParent(scope); if (parent) { parent->buildVariableDefHashMap(scope); // TODO: - unnecessary diff --git a/src/nodes/StructDefinitionObject.hpp b/src/nodes/StructDefinitionNode.hpp similarity index 68% rename from src/nodes/StructDefinitionObject.hpp rename to src/nodes/StructDefinitionNode.hpp index 0bcbc1c..8689a1b 100644 --- a/src/nodes/StructDefinitionObject.hpp +++ b/src/nodes/StructDefinitionNode.hpp @@ -1,5 +1,5 @@ /** - * @file StructDefinitionObject.hpp + * @file StructDefinitionNode.hpp * @author Edward Palmer * @date 2024-11-24 * @@ -8,9 +8,8 @@ */ #pragma once -#include "AddVariableNode.hpp" #include "BaseNode.hpp" -#include "BaseObject.hpp" +#include "Exceptions.hpp" #include #include #include @@ -21,38 +20,30 @@ * will be stored in the scope along with the struct name. We can then use this * to construct struct instances. */ -class StructDefinitionObject : public BaseObject, public BaseNode, public std::enable_shared_from_this +class StructDefinitionNode : public BaseNode, public std::enable_shared_from_this { public: + using Ptr = std::shared_ptr; + /** * Supply a vector of nodes for constructing the struct. This class will take * ownership and free these later. */ - StructDefinitionObject(std::string typeName_, - std::string parentTypeName_, - std::vector variableDefs_); + StructDefinitionNode(std::string typeName_, + std::string parentTypeName_, + std::vector> variableDefs_); /** * Destructor deletes all nodes in variable definitions. */ - ~StructDefinitionObject() override = default; + ~StructDefinitionNode() override = default; /** * Registers this class in the current scope with name. Ownership will pass * to the scope. Careful! Going this route means we don't have to create a * separate Node to create an Object. */ - BaseObject::Ptr evaluate(Scope &scope) override; - - /** - * No destructor provided. Should not be possible to copy the struct definition - * as you would expect. If this were to be implemented, all nodes we are - * storing would have to be copied. - */ - BaseObject::Ptr clone() const final - { - ThrowException("not implemented"); - } + std::shared_ptr evaluate(class Scope &scope) override; /** * Calls evaluate method on all variables in this struct and parents. Installs @@ -70,13 +61,13 @@ class StructDefinitionObject : public BaseObject, public BaseNode, public std::e /** * Returns a pointer to the parent struct or nullptr if not found. */ - std::shared_ptr lookupParent(const Scope &scope) const; + std::shared_ptr lookupParent(const Scope &scope) const; /** * Stores our owned variables and those of any parent variables we inherit. * To construct the object, we will call evaluate() method on each node. */ - std::unordered_map allVariableDefsMap; + std::unordered_map> allVariableDefsMap; /** * Type name for struct. @@ -93,7 +84,7 @@ class StructDefinitionObject : public BaseObject, public BaseNode, public std::e * ownership of all nodes. There may be additional nodes that are not in this * vector and will be stored in a parent class. */ - std::vector variableDefs; + std::vector> variableDefs; /** * We activate the definition once evaluate is called. This is when we can diff --git a/src/nodes/StructNode.cpp b/src/nodes/StructNode.cpp new file mode 100644 index 0000000..37e10e6 --- /dev/null +++ b/src/nodes/StructNode.cpp @@ -0,0 +1,70 @@ +/** + * @file StructNode.cpp + * @author Edward Palmer + * @date 2024-11-24 + * + * @copyright Copyright (c) 2024 + * + */ + + +#include "StructNode.hpp" +#include "AnyObject.hpp" +#include "ObjectFactory.hpp" +#include "Scope.hpp" +#include +#include + + +StructNode::StructNode(std::string typeName_, std::string name_) + : typeName(std::move(typeName_)), name(std::move(name_)) +{ +} + + +AnyObject::Ptr StructNode::evaluate(Scope &scope) +{ + if (active) + { + ThrowException("StructNode named " + name + " of type " + typeName + " is already active"); + } + + active = true; + + // Initialize our instance from the struct definition defined in the scope. + structDefinition = std::static_pointer_cast(scope.getNamedObject(typeName)->getValue()); + structDefinition->installVariablesInScope(_instanceScope, variableNames); + + // Add the active struct instance to the scope. TODO: - transfer ownership + // to the scope. Will have to remove this class from AST to do this correctly. + auto wrappedStruct = ObjectFactory::allocate(shared_from_this(), AnyObject::Struct); + scope.linkObject(name, wrappedStruct); + return wrappedStruct; +} + + +StructNode &StructNode::operator=(const StructNode &other) +{ + if (this == &other) + { + return (*this); + } + + // 1. check that both are instances of the same struct type. + if (other.structDefinition != structDefinition) + { + ThrowException("cannot assign Struct objects with different definitions"); + } + + // 2. iterate over the objects stored in the scopes and assign. + for (auto &variableName : variableNames) + { + AnyObject::Ptr thisObject = _instanceScope.getNamedObject(variableName); + AnyObject::Ptr otherObject = other._instanceScope.getNamedObject(variableName); + + // Attempt an assignment. Will fail if different types. + (*thisObject) = (*otherObject); + } + + return (*this); +} diff --git a/src/objects/StructObject.hpp b/src/nodes/StructNode.hpp similarity index 62% rename from src/objects/StructObject.hpp rename to src/nodes/StructNode.hpp index 199f706..d7f9eea 100644 --- a/src/objects/StructObject.hpp +++ b/src/nodes/StructNode.hpp @@ -1,5 +1,5 @@ /** - * @file StructObject.hpp + * @file StructNode.hpp * @author Edward Palmer * @date 2024-11-24 * @@ -9,10 +9,10 @@ #pragma once #include "BaseNode.hpp" -#include "BaseObject.hpp" + #include "Exceptions.hpp" #include "Scope.hpp" -#include "StructDefinitionObject.hpp" +#include "StructDefinitionNode.hpp" #include #include #include @@ -21,37 +21,31 @@ /** * struct SomeStruct a; */ -class StructObject : public BaseObject, public BaseNode, public std::enable_shared_from_this +class StructNode : public BaseNode, public std::enable_shared_from_this { public: - StructObject() = delete; + using Ptr = std::shared_ptr(); + + StructNode() = delete; /** - * Create a new empty StructObject with the name. + * Create a new empty StructNode with the name. */ - StructObject(std::string typeName_, std::string name_); + StructNode(std::string typeName_, std::string name_); /** * Assignment operator. */ - StructObject &operator=(const BaseObject &other) override; + StructNode &operator=(const StructNode &other); + + // TODO: - we will need to copy this assignment check and add it to the AnyNode + // type /** * Finishes initializing the struct object and links to the scope. Ownership * should pass to the scope from the AST. Returns a pointer to itself. */ - BaseObject::Ptr evaluate(Scope &scope) override; - - // TODO: - implement in future. - BaseObject::Ptr clone() const override - { - ThrowException("not implemented!"); - } - - /** - * Returns a description of the struct. - */ - friend std::ostream &operator<<(std::ostream &os, const BaseObject &); + std::shared_ptr evaluate(Scope &scope) override; [[nodiscard]] const Scope &instanceScope() const { return _instanceScope; } @@ -84,5 +78,5 @@ class StructObject : public BaseObject, public BaseNode, public std::enable_shar /** * Store the struct definition once active. */ - std::shared_ptr structDefinition{nullptr}; + std::shared_ptr structDefinition{nullptr}; }; diff --git a/src/objects/AnyObject.cpp b/src/objects/AnyObject.cpp index 2cecd6a..b07bc97 100644 --- a/src/objects/AnyObject.cpp +++ b/src/objects/AnyObject.cpp @@ -8,6 +8,168 @@ */ #include "AnyObject.hpp" +#include "Exceptions.hpp" +#include "StructNode.hpp" +AnyObject::Type AnyObject::getUserObjectType(const std::string &name) +{ + /* TODO: - use a static map to make more efficient */ + + /* Todo: enable also for functions, etc */ + if (name == "int") + return AnyObject::Int; + else if (name == "bool") + return AnyObject::Bool; + else if (name == "float") + return AnyObject::Float; + else if (name == "string") + return AnyObject::String; + else if (name == "array") + return AnyObject::Array; + else if (name == "struct") + return AnyObject::Struct; /* TODO: - add */ + else if (name == "class") + return AnyObject::Class; + + ThrowException("'" + name + "' is not a valid user object type"); +} + + +std::string AnyObject::typeToString() const +{ + static const std::unordered_map TypeToString = {{NotSet, "NotSet"}, + {Int, "Int"}, + {Bool, "Bool"}, + {Float, "Float"}, + {String, "String"}, + {Array, "Array"}, + {Struct, "Struct"}, + {Class, "Class"}, + {_UserFunction, "Function"}, + {_ModuleFunction, "ModuleFunction"}, + {_StructDefinition, "StructDef"}, + {_ClassDefinition, "ClassDef"}}; + + auto iter = TypeToString.find(_type); + if (iter != TypeToString.end()) + { + return (iter->second); + } + + return "Unknown"; +} + + +AnyObject &AnyObject::operator=(const AnyObject &other) +{ + /* TODO: - implement if required */ + AnyObject::Vector cloneVector(const AnyObject::Vector &); + + if (getType() != other.getType()) + { + ThrowException("Invalid assignment. Types do not match [LHS = " + typeToString() + ", RHS = " + other.typeToString() + "]"); + } + + switch (getType()) + { + case Int: + case Bool: + case Float: + case String: + _value = other._value; /* Standard copy assignment */ + break; + case Struct: + *std::static_pointer_cast(getValue()) = *std::static_pointer_cast(other.getValue()); + break; + case Array: /* Array is a vector of shared pointers --> need to clone for deep-copy */ + getValue() = cloneVector(other.getValue()); + break; + default: + ThrowException("Copy assignment not implemented for object type [" + typeToString() + "]"); + } + + return (*this); +} + + +AnyObject::Ptr AnyObject::clone() const +{ + AnyObject::Vector cloneVector(const AnyObject::Vector &); + + switch (getType()) + { + case Bool: + return std::make_shared(getValue()); + case Int: + return std::make_shared(getValue()); + case Float: + return std::make_shared(getValue()); + case String: + return std::make_shared(getValue()); + case Array: + return std::make_shared(cloneVector(getValue())); + default: + ThrowException("clone() is not implemented for object type [" + typeToString() + "]"); + } +} + +AnyObject::Vector cloneVector(const AnyObject::Vector &vector) +{ + AnyObject::Vector clone; + + clone.reserve(vector.size()); + + for (const auto &object : vector) + { + clone.push_back(object->clone()); /* Deep-copy */ + } + + return clone; +} + +std::ostream &operator<<(std::ostream &out, const AnyObject::Vector &array); + + +std::ostream &operator<<(std::ostream &out, const AnyObject &object) +{ + std::string printBoolean(const bool &value); + + switch (object.getType()) + { + case AnyObject::Bool: + return (out << printBoolean(object.getValue())); + case AnyObject::Int: + return (out << object.getValue()); + case AnyObject::Float: + return (out << object.getValue()); + case AnyObject::String: + return (out << object.getValue()); + case AnyObject::Array: + return (out << object.getValue()); + default: + return out; /* Don't print anything --> not supported */ + }; +} + + +std::string printBoolean(const bool &value) +{ + return (value ? "true" : "false"); +} + + +std::ostream &operator<<(std::ostream &out, const AnyObject::Vector &array) +{ + out << "["; + + for (const auto &object : array) + { + out << *object << ", "; + } + + out << "]"; + + return out; +} \ No newline at end of file diff --git a/src/objects/AnyObject.hpp b/src/objects/AnyObject.hpp index e767ab5..bba6e3c 100644 --- a/src/objects/AnyObject.hpp +++ b/src/objects/AnyObject.hpp @@ -9,31 +9,14 @@ #pragma once #include "BaseNode.hpp" -#include "Scope.hpp" -#include +#include "ModuleFunctor.hpp" +#include #include +#include #include #include #include -class AnyObject; /* Forward declaration */ - -class ModuleFunctor -{ -public: - using ModuleFunction = std::function(BaseNodePtrVector &, Scope &)>; - - ModuleFunctor(ModuleFunction &&function) : _function(std::move(function)) {} - - [[nodiscard]] inline std::shared_ptr operator()(BaseNodePtrVector &args, Scope &scope) - { - return !_function ? nullptr : _function(args, scope); - } - -private: - ModuleFunction _function{nullptr}; -}; - class AnyObject { @@ -41,20 +24,42 @@ class AnyObject using Ptr = std::shared_ptr; using Vector = std::vector; + virtual ~AnyObject() = default; /* In case we subclass */ + enum Type { - None = (-1), - Int, + NotSet = (-1), + Int, /* User-defined types */ + Bool, Float, String, Array, - ModuleFunction + Struct, + Class, + + _UserFunction, /* Private implementation-types */ + _ModuleFunction, + _StructDefinition, + _ClassDefinition, }; - constexpr explicit AnyObject(long value) : _value(value) {} /* NB: require explicit to avoid implicit casting */ - constexpr explicit AnyObject(double value) : _value(value) {} - constexpr explicit AnyObject(AnyObject::Vector &&value) : _value(std::move(value)) {} - constexpr explicit AnyObject(ModuleFunctor &&value) : _value(std::move(value)) {} + /* Converts user types like "int" --> AnyType::Int or returns None if not found */ + static AnyObject::Type getUserObjectType(const std::string &name); + + std::string typeToString() const; + + /* NB: require explicit to avoid implicit casting */ + explicit AnyObject(bool value) : _value(value), _type(Bool) {} + explicit AnyObject(long value) : _value(value), _type(Int) {} /* NB: require explicit to avoid implicit casting */ + explicit AnyObject(double value) : _value(value), _type(Float) {} + explicit AnyObject(std::string value) : _value(std::move(value)), _type(String) {} + explicit AnyObject(AnyObject::Vector value) : _value(std::move(value)), _type(Array) {} + explicit AnyObject(ModuleFunctor value) : _value(std::move(value)), _type(_ModuleFunction) {} + + /* _userFuntion, _StructDefinition, Struct, ...*/ + explicit AnyObject(std::shared_ptr value, Type type) : _value(std::move(value)), _type(type) {} + + AnyObject &operator=(const AnyObject &other); template [[nodiscard]] inline TValue &getValue(); @@ -64,32 +69,52 @@ class AnyObject [[nodiscard]] inline Type getType() const; - /* TODO: - think about copy constructors/assignment here. Will need to implement based on types */ + [[nodiscard]] inline bool isType(Type expectedType) const; + + [[nodiscard]] AnyObject::Ptr clone() const; + + friend std::ostream &operator<<(std::ostream &out, const AnyObject &object); protected: - AnyObject() = default; /* Prevent direct initialization */ - virtual ~AnyObject() = default; /* In case we subclass */ + AnyObject() = default; /* Prevent direct initialization */ private: - std::variant _value; + using ValueVariant = std::variant; + ValueVariant _value{}; + Type _type{Type::NotSet}; }; -template +template // No type checking!! TValue &AnyObject::getValue() { - return std::get(_value); + return std::get(_value); } template const TValue &AnyObject::getValue() const { - return std::get(_value); + return std::get(_value); } AnyObject::Type AnyObject::getType() const { - return Type(_value.index()); -} \ No newline at end of file + return _type; +} + + +bool AnyObject::isType(Type expectedType) const +{ + return (getType() == expectedType); +} + + +std::ostream &operator<<(std::ostream &out, const AnyObject &object); diff --git a/src/objects/ArrayObject.cpp b/src/objects/ArrayObject.cpp deleted file mode 100644 index 7880ed5..0000000 --- a/src/objects/ArrayObject.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/** - * @file ArrayObject.cpp - * @author Edward Palmer - * @date 2024-11-25 - * - * @copyright Copyright (c) 2024 - * - */ - -#include "ArrayObject.hpp" -#include "ObjectFactory.hpp" - -ArrayObject &ArrayObject::operator=(const BaseObject &other) -{ - if (this == &other) - { - return (*this); - } - - const ArrayObject &otherArray = other.castObject(); - - // NB: - when we copy the array, we clone all values inside and have the - // responsibility of managing their memory. The scope only owns the array - // and not the objects inside the array! - // 1. clear objects in existing vector. - _value.resize(otherArray.value().size()); - - for (size_t i = 0; i < otherArray.value().size(); ++i) - { - BaseObject::Ptr otherObj = otherArray[i]; - - _value[i] = otherObj->clone(); - } - - return (*this); -} - - -ArrayObject ArrayObject::operator+(const BaseObject &other) const -{ - const ArrayObject &otherArray = other.castObject(); - - // NB: special case where we add ourself to ourself. Currently, we will be - // cloning the same object twice which is okay. But might want to think about - // references in future. - - // Create an array to store objects from both. - BaseObjectPtrVector combinedArray; - combinedArray.reserve(_value.size() + otherArray.value().size()); - - // Now we iterate over array and add to vector. - for (auto &object : _value) - { - combinedArray.push_back(object->clone()); - } - - for (auto &object : otherArray.value()) - { - combinedArray.push_back(object->clone()); - } - - // Now create new array object. Caller responsible for handling memory. - // Ideally, should be added to scope of caller. - return ArrayObject(std::move(combinedArray)); -} - - -ArrayObject::Ptr ArrayObject::clone() const -{ - BaseObjectPtrVector cloneValues(value().size()); - - for (auto &obj : value()) - { - cloneValues.push_back(obj->clone()); - } - - return ObjectFactory::allocate(std::move(cloneValues)); -} \ No newline at end of file diff --git a/src/objects/ArrayObject.hpp b/src/objects/ArrayObject.hpp deleted file mode 100644 index 770864e..0000000 --- a/src/objects/ArrayObject.hpp +++ /dev/null @@ -1,38 +0,0 @@ -/** - * @file ArrayObject.hpp - * @author Edward Palmer - * @date 2024-11-17 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include "BaseObject.hpp" -#include "BaseObjectT.hpp" -#include -#include - -class ArrayObject : public BaseObjectT -{ -public: - ArrayObject() = default; - ~ArrayObject() override = default; - - ArrayObject(BaseObjectPtrVector objects) : BaseObjectT(std::move(objects)) {} - - /// Add two array objects and return an unmanaged pointer to result. - ArrayObject operator+(const BaseObject &other) const; - - ArrayObject &operator=(const BaseObject &other) override; - - /// Performs a deep copy of array. This will enable the array to be returned - /// by a function without objects (defined in function scope) being destroyed. - BaseObject::Ptr clone() const override; - - BaseObject::Ptr operator[](std::size_t index) const - { - assert(index < value().size()); - return (value().at(index)); - } -}; diff --git a/src/objects/BaseObject.hpp b/src/objects/BaseObject.hpp deleted file mode 100644 index 4338865..0000000 --- a/src/objects/BaseObject.hpp +++ /dev/null @@ -1,62 +0,0 @@ -/** - * @file BaseObject.hpp - * @author Edward Palmer - * @date 2024-11-17 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include "Exceptions.hpp" -#include -#include -#include -#include - -/** - * BaseObject. All objects are derived from this class. - */ -class BaseObject -{ -public: - using Ptr = std::shared_ptr; - - virtual ~BaseObject() = default; - - // Implement copy assignment in derived classes. - virtual BaseObject &operator=(const BaseObject &) - { - ThrowException("copy assignment not implemented"); - } - - /// Cast to object type. - template - [[nodiscard]] const TObject &castObject() const - { - return static_cast(*this); - } - - template - [[nodiscard]] TObject &castObject() - { - return static_cast(*this); - } - - /// Check object type. - template - [[nodiscard]] bool isObjectType() const - { - return typeid(*this) == typeid(TObject); - } - - [[nodiscard]] virtual BaseObject::Ptr clone() const - { - ThrowException("clone is not implemented"); - } - -protected: - BaseObject() = default; -}; - -using BaseObjectPtrVector = std::vector; \ No newline at end of file diff --git a/src/objects/BaseObjectT.hpp b/src/objects/BaseObjectT.hpp deleted file mode 100644 index 1ed6ad6..0000000 --- a/src/objects/BaseObjectT.hpp +++ /dev/null @@ -1,76 +0,0 @@ -/** - * @file BaseObjectT.hpp - * @author Edward Palmer - * @date 2025-04-13 - * - * @copyright Copyright (c) 2025 - * - */ - -#pragma once -#include "BaseObject.hpp" -#include "ObjectFactory.hpp" -#include - -/* Template wrapper around BaseObject with accessors */ -template -class BaseObjectT : public BaseObject -{ -public: - /* Use this to ensure that stored value type is correct */ - using Type = TValue; - - virtual BaseObject &operator=(const BaseObject &other) override - { - return BaseObject::operator=(other); - } - - /* Elegant wrapper to extract value from a base object */ - static const TValue &value(const BaseObject &obj) - { - const auto &upcast = static_cast &>(obj); - - return (*upcast); /* Return value */ - } - - static TValue &value(BaseObject &obj) - { - const auto &upcast = static_cast &>(obj); - - return (*upcast); /* Return value */ - } - - /* Constructor */ - explicit BaseObjectT(TValue value) : _value(std::move(value)) {} - - /* Returns non-const accessor */ - [[nodiscard]] TValue &operator*() - { - return _value; - } - - /* Returns const accessor */ - [[nodiscard]] const TValue &operator*() const - { - return _value; - } - - /* Const-accessor */ - [[nodiscard]] const TValue &value() const - { - return _value; - } - - /* Non-const accessor */ - [[nodiscard]] TValue &value() - { - return _value; - } - -protected: - /* Enable subclasses to use default constructor */ - BaseObjectT() = default; - - /* Stores some value */ - TValue _value; -}; diff --git a/src/objects/ClassObject.cpp b/src/objects/ClassObject.cpp deleted file mode 100644 index 8b7b4e7..0000000 --- a/src/objects/ClassObject.cpp +++ /dev/null @@ -1,40 +0,0 @@ -/** - * @file ClassObject.cpp - * @author Edward Palmer - * @date 2024-11-30 - * - * @copyright Copyright (c) 2024 - * - */ - -#include "ClassObject.hpp" -#include "FunctionNode.hpp" -#include - -ClassObject::ClassObject(std::string typeName_, std::string name_) - : StructObject(std::move(typeName_), std::move(name_)) -{ -} - -BaseObject::Ptr ClassObject::evaluate(Scope &scope) -{ - // TODO: - inefficient, should have another method we can call to do most of StructObject::evaluate. - if (active) - { - ThrowException("ClassObject named " + name + " of type " + typeName + " is already active"); - } - - active = true; - - // Initialize our instance from the struct definition defined in the scope. - structDefinition = scope.getNamedObject(typeName); - structDefinition->installVariablesInScope(_instanceScope, variableNames); - - auto classDefinition = std::static_pointer_cast(structDefinition); - classDefinition->installMethodsInScope(_instanceScope); - - // Add the active struct instance to the scope. TODO: - transfer ownership - // to the scope. Will have to remove this class from AST to do this correctly. - scope.linkObject(name, shared_from_this()); - return shared_from_this(); -} diff --git a/src/objects/ClassObject.hpp b/src/objects/ClassObject.hpp deleted file mode 100644 index 3df732c..0000000 --- a/src/objects/ClassObject.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/** - * @file ClassObject.hpp - * @author Edward Palmer - * @date 2024-11-29 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include "ClassDefinitionObject.hpp" -#include "Exceptions.hpp" -#include "StructObject.hpp" -#include - -/** - * An instance of a class defined by the ClassDefinitionObject. - */ -class ClassObject : public StructObject -{ -public: - ClassObject(std::string typeName_, std::string name_); - - ClassObject &operator=(const BaseObject &) override - { - ThrowException("Not implemented"); - } - - /** - * Finishes initializing the class object and links to the scope. - * @param scope The scope in which to add the instance. - * @return BaseObject* Pointer to itself - */ - BaseObject::Ptr evaluate(Scope &scope) override; -}; \ No newline at end of file diff --git a/src/objects/FloatObject.cpp b/src/objects/FloatObject.cpp deleted file mode 100644 index 3b23d71..0000000 --- a/src/objects/FloatObject.cpp +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @file FloatObject.cpp - * @author Edward Palmer - * @date 2024-11-17 - * - * @copyright Copyright (c) 2024 - * - */ - -#include "FloatObject.hpp" - -FloatObject IntObject::castToFloat() const -{ - return FloatObject((double)_value); -} diff --git a/src/objects/FloatObject.hpp b/src/objects/FloatObject.hpp deleted file mode 100644 index e1876ab..0000000 --- a/src/objects/FloatObject.hpp +++ /dev/null @@ -1,114 +0,0 @@ -/** - * @file FloatObject.hpp - * @author Edward Palmer - * @date 2024-11-17 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include "BaseObjectT.hpp" -#include "Exceptions.hpp" -#include "IntObject.hpp" -#include "ObjectFactory.hpp" -#include - -/** - * Class for double type. - */ -class FloatObject : public BaseObjectT -{ -public: - using Ptr = std::shared_ptr; - - FloatObject(double value = 0.0) : BaseObjectT(value) {} - - FloatObject &operator=(const BaseObject &other) override - { - if (this != &other) - { - if (other.isObjectType()) - _value = IntObject::value(other); - else if (other.isObjectType()) - _value = FloatObject::value(other); - else - ThrowException("Cannot assign object to type FloatObject"); - } - - return (*this); - } - - BaseObject::Ptr clone() const override - { - return ObjectFactory::allocate(_value); - } - - FloatObject &operator++() - { - ++_value; - return *this; - } - - FloatObject &operator--() - { - --_value; - return *this; - } - - FloatObject operator-() const // Negation. - { - return FloatObject(-_value); - } - - // Add addition / subtraction operations. - FloatObject operator+(const FloatObject &other) const - { - return FloatObject(_value + other._value); - } - - FloatObject operator-(const FloatObject &other) const - { - return FloatObject(_value - other._value); - } - - FloatObject operator*(const FloatObject &other) const - { - return FloatObject(_value * other._value); - } - - FloatObject operator/(const FloatObject &other) const - { - return FloatObject(_value / other._value); - } - - IntObject operator==(const FloatObject &other) const - { - return IntObject(_value == other._value); - } - - IntObject operator!=(const FloatObject &other) const - { - return IntObject(_value != other._value); - } - - IntObject operator>=(const FloatObject &other) const - { - return IntObject(_value >= other._value); - } - - IntObject operator>(const FloatObject &other) const - { - return IntObject(_value > other._value); - } - - IntObject operator<=(const FloatObject &other) const - { - return IntObject(_value <= other._value); - } - - IntObject operator<(const FloatObject &other) const - { - return IntObject(_value < other._value); - } -}; diff --git a/src/objects/FunctionObject.hpp b/src/objects/FunctionObject.hpp deleted file mode 100644 index 4e2f930..0000000 --- a/src/objects/FunctionObject.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file FunctionObject.hpp - * @author Edward Palmer - * @date 2024-11-17 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include "BaseObjectT.hpp" -#include "ObjectFactory.hpp" -#include - -/* Forward declaration */ -class FunctionNode; - -/// FunctionObject contains a pointer to the original function definition which -/// allows us to call its evaluate() method and perform type-checking of the -/// supplied function arguments with the expected arguments. -class FunctionObject : public BaseObjectT> -{ -public: - FunctionObject(std::shared_ptr function) : BaseObjectT>(function) {} - - FunctionObject::Ptr clone() const override - { - return ObjectFactory::allocate(_value); - } -}; \ No newline at end of file diff --git a/src/objects/IntObject.hpp b/src/objects/IntObject.hpp deleted file mode 100644 index 2ab2a35..0000000 --- a/src/objects/IntObject.hpp +++ /dev/null @@ -1,151 +0,0 @@ -/** - * @file IntObject.hpp - * @author Edward Palmer - * @date 2024-11-17 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include "BaseObjectT.hpp" -#include "ObjectFactory.hpp" -#include "PoolAllocator.hpp" -#include - -// 10 chunks per block. -static PoolAllocator allocator{10}; - -class FloatObject; - -/** - * @brief Long integer type. - */ -class IntObject : public BaseObjectT -{ -public: - IntObject(long value = 0) : BaseObjectT(value) {} - - IntObject &operator=(const BaseObject &other) override - { - if (this != &other) - { - if (other.isObjectType()) - _value = IntObject::value(other); - else if (other.isObjectType()) - _value = (long)BaseObjectT::value(other); - else - ThrowException("Cannot assign object to type FloatObject"); - } - - return (*this); - } - - void *operator new(size_t size) - { - return allocator.allocate(size); - } - - void operator delete(void *ptr) - { - allocator.deallocate(ptr); - } - - BaseObject::Ptr clone() const override - { - return ObjectFactory::allocate(_value); - } - - FloatObject castToFloat() const; - - IntObject &operator++() - { - ++_value; - return *this; - } - - IntObject &operator--() - { - --_value; - return *this; - } - - IntObject operator-() const // Negation. - { - return IntObject(-_value); - } - - IntObject operator!() const // Not. - { - bool state = (_value > 0); - return IntObject(!state); - } - - IntObject operator+(const IntObject &other) const - { - return IntObject(_value + other._value); - } - - IntObject operator-(const IntObject &other) const - { - return IntObject(_value - other._value); - } - - IntObject operator*(const IntObject &other) const - { - return IntObject(_value * other._value); - } - - IntObject operator/(const IntObject &other) const - { - return IntObject(_value / other._value); - } - - IntObject operator==(const IntObject &other) const - { - return IntObject(_value == other._value); - } - - IntObject operator!=(const IntObject &other) const - { - return IntObject(_value != other._value); - } - - IntObject operator>=(const IntObject &other) const - { - return IntObject(_value >= other._value); - } - - IntObject operator>(const IntObject &other) const - { - return IntObject(_value > other._value); - } - - IntObject operator<=(const IntObject &other) const - { - return IntObject(_value <= other._value); - } - - IntObject operator<(const IntObject &other) const - { - return IntObject(_value < other._value); - } - - IntObject operator%(const IntObject &other) const - { - assert(other._value > 0); - return IntObject(_value % other._value); - } - - IntObject operator&&(const IntObject &other) const - { - return IntObject(_value && other._value); - } - - IntObject operator||(const IntObject &other) const - { - return IntObject(_value || other._value); - } -}; - -using BoolObject = IntObject; diff --git a/src/objects/ModuleFunctionObject.hpp b/src/objects/ModuleFunctionObject.hpp deleted file mode 100644 index a94bb9e..0000000 --- a/src/objects/ModuleFunctionObject.hpp +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @file ModuleFunctionObject.hpp - * @author Edward Palmer - * @date 2024-11-17 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include "BaseObject.hpp" -#include "BaseObjectT.hpp" -#include -#include - -class Scope; - -using ModuleFunction = std::function; -using ModuleFunctionPair = std::pair; - -/// Library function allows us to define lambdas which wrap around existing stdlib -/// functions. These can then be added to a global scope after seeing "import <...>" -/// with angled-brackets. -class ModuleFunctionObject : public BaseObjectT -{ -public: - /* Set evaluate to be a reference to _value */ - ModuleFunctionObject(ModuleFunction function) : BaseObjectT(function) - { - } - - /* Elegant operator overload allowing user to call function */ - BaseObject::Ptr operator()(BaseNodePtrVector &args, Scope &scope) const - { - return _value(args, scope); - } -}; \ No newline at end of file diff --git a/src/objects/ModuleFunctor.cpp b/src/objects/ModuleFunctor.cpp new file mode 100644 index 0000000..f424b10 --- /dev/null +++ b/src/objects/ModuleFunctor.cpp @@ -0,0 +1,18 @@ +/** + * @file ModuleFunctor.cpp + * @author Edward Palmer + * @date 2025-05-19 + * + * @copyright Copyright (c) 2025 + * + */ + +#include "ModuleFunctor.hpp" +#include "AnyObject.hpp" + +ModuleFunctor::ModuleFunctor(Function function) : _function(function) {} + +std::shared_ptr ModuleFunctor::operator()(BaseNodePtrVector &args, Scope &scope) +{ + return !_function ? nullptr : _function(args, scope); +} \ No newline at end of file diff --git a/src/objects/ModuleFunctor.hpp b/src/objects/ModuleFunctor.hpp new file mode 100644 index 0000000..c92cd30 --- /dev/null +++ b/src/objects/ModuleFunctor.hpp @@ -0,0 +1,33 @@ +/** + * @file ModuleFunctor.hpp + * @author Edward Palmer + * @date 2025-05-18 + * + * @copyright Copyright (c) 2025 + * + */ + + +#pragma once +#include "BaseNode.hpp" +#include "Scope.hpp" +#include +#include +#include +#include + +class ModuleFunctor +{ +public: + using Name = std::string; + using Function = std::function(BaseNodePtrVector &, Scope &)>; + using Definition = std::pair; + using Definitions = std::vector; + + ModuleFunctor(ModuleFunctor::Function function); + + [[nodiscard]] std::shared_ptr operator()(BaseNodePtrVector &args, Scope &scope); + +private: + Function _function{nullptr}; +}; diff --git a/src/objects/ObjectFactory.cpp b/src/objects/ObjectFactory.cpp index c80baba..e15b8f1 100644 --- a/src/objects/ObjectFactory.cpp +++ b/src/objects/ObjectFactory.cpp @@ -8,28 +8,26 @@ */ #include "ObjectFactory.hpp" -#include "ArrayObject.hpp" #include "Exceptions.hpp" -#include "FloatObject.hpp" -#include "IntObject.hpp" -#include "StringObject.hpp" +#include namespace ObjectFactory { -BaseObject::Ptr allocate(ObjectType objectType) +AnyObject::Ptr allocate(AnyObject::Type objectType) { switch (objectType) { - case ObjectType::Int: - case ObjectType::Bool: - return allocate(); - case ObjectType::Float: - return allocate(); - case ObjectType::String: - return allocate(); - case ObjectType::Array: - return allocate(); + case AnyObject::Int: + return std::make_shared(0L); + case AnyObject::Bool: + return std::make_shared(false); + case AnyObject::Float: + return std::make_shared((double)0.0); + case AnyObject::String: + return std::make_shared(std::string()); + case AnyObject::Array: + return std::make_shared(AnyObject::Vector()); default: ThrowException("cannot allocate for object type!"); } diff --git a/src/objects/ObjectFactory.hpp b/src/objects/ObjectFactory.hpp index 1fe6a34..87d6e79 100644 --- a/src/objects/ObjectFactory.hpp +++ b/src/objects/ObjectFactory.hpp @@ -9,20 +9,19 @@ #pragma once -#include "BaseObject.hpp" -#include "ObjectTypes.hpp" +#include "AnyObject.hpp" #include #include namespace ObjectFactory { -template -[[nodiscard]] inline std::shared_ptr allocate(Args &&...args) +template +[[nodiscard]] inline AnyObject::Ptr allocate(Args &&...args) { - return std::make_shared(std::forward(args)...); + return std::make_shared(std::forward(args)...); } -BaseObject::Ptr allocate(ObjectType objectType); +AnyObject::Ptr allocate(AnyObject::Type objectType); } // namespace ObjectFactory \ No newline at end of file diff --git a/src/objects/ObjectTypes.cpp b/src/objects/ObjectTypes.cpp deleted file mode 100644 index dfa949b..0000000 --- a/src/objects/ObjectTypes.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/** - * @file ObjectTypes.cpp - * @author Edward Palmer - * @date 2025-01-01 - * - * @copyright Copyright (c) 2025 - * - */ - -#include "ObjectTypes.hpp" -#include "Exceptions.hpp" -#include - -ObjectType objectTypeForName(const std::string &typeName) -{ - // NB: mark as static to persist. - static std::unordered_map objectTypeMap{ - {"int", ObjectType::Int}, - {"bool", ObjectType::Bool}, - {"float", ObjectType::Float}, - {"string", ObjectType::String}, - {"array", ObjectType::Array}, - {"struct", ObjectType::Struct}, - {"class", ObjectType::Class}}; - - auto iter = objectTypeMap.find(typeName); - if (iter != objectTypeMap.end()) - { - return iter->second; - } - - // Uh-oh. Unrecognized type-name. - ThrowException("unrecognized type-name: " + typeName); -} \ No newline at end of file diff --git a/src/objects/ObjectTypes.hpp b/src/objects/ObjectTypes.hpp deleted file mode 100644 index d151a0a..0000000 --- a/src/objects/ObjectTypes.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/** - * @file ObjectTypes.hpp - * @author Edward Palmer - * @date 2024-11-30 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include - -/** - * enum for supported object types. - */ -enum class ObjectType -{ - Int, - Bool, - Float, - String, - Array, - Struct, - Class -}; - -/** - * Returns an object type for the type name. - */ -ObjectType objectTypeForName(const std::string &typeName); \ No newline at end of file diff --git a/src/objects/Objects.hpp b/src/objects/Objects.hpp deleted file mode 100644 index f4f2c07..0000000 --- a/src/objects/Objects.hpp +++ /dev/null @@ -1,71 +0,0 @@ -// -// Objects.hpp -// Eucleia -// -// Created by Edward on 14/01/2024. -// - -#pragma once -#include "ArrayObject.hpp" -#include "BaseObject.hpp" -#include "ClassDefinitionObject.hpp" -#include "ClassObject.hpp" -#include "Exceptions.hpp" -#include "FloatObject.hpp" -#include "FunctionObject.hpp" -#include "IntObject.hpp" -#include "ModuleFunctionObject.hpp" -#include "StringObject.hpp" -#include "StructDefinitionObject.hpp" -#include "StructObject.hpp" -#include - -inline std::ostream &operator<<(std::ostream &out, const BaseObject &object) -{ - if (object.isObjectType()) - return (out << IntObject::value(object)); - else if (object.isObjectType()) - return (out << FloatObject::value(object)); - else if (object.isObjectType()) - return (out << StringObject::value(object)); - else if (object.isObjectType()) - { - auto &arrayObject = object.castObject(); - - out << "["; - for (int i = 0; i < arrayObject.value().size(); i++) - { - out << *arrayObject[i] << ", "; - } - out << "]"; - return out; - } - else if (object.isObjectType()) - { - auto &structObject = object.castObject(); - - out << "("; - for (auto &name : structObject.variableNames) - { - BaseObject::Ptr obj = structObject._instanceScope.getNamedObject(name); - out << name << ": " << (*obj) << ", "; - } - out << ")"; - return out; - } - else if (object.isObjectType()) - { - auto &classObject = object.castObject(); - - out << "("; - for (auto &name : classObject.variableNames) - { - BaseObject::Ptr obj = classObject._instanceScope.getNamedObject(name); - out << name << ": " << (*obj) << ", "; - } - out << ")"; - return out; - } - else - return out; // Don't print anything. -} diff --git a/src/objects/StringObject.hpp b/src/objects/StringObject.hpp deleted file mode 100644 index 26d2640..0000000 --- a/src/objects/StringObject.hpp +++ /dev/null @@ -1,58 +0,0 @@ -/** - * @file StringObject.hpp - * @author Edward Palmer - * @date 2024-11-17 - * - * @copyright Copyright (c) 2024 - * - */ - -#pragma once -#include "BaseObjectT.hpp" -#include "IntObject.hpp" -#include - -class StringObject : public BaseObjectT -{ -public: - StringObject(std::string value = "") : BaseObjectT(value) {} - - StringObject &operator=(const BaseObject &other) override - { - if (this != &other) - { - assert(other.isObjectType()); - _value = StringObject::value(other); - } - - return (*this); - } - - BaseObject::Ptr clone() const override - { - return ObjectFactory::allocate(_value); - } - - StringObject operator+(const StringObject &other) const - { - return StringObject(_value + other.value()); - } - - StringObject &operator+=(const StringObject &other) - { - _value += other.value(); - return *this; - } - - - IntObject operator==(const StringObject &other) const - { - return IntObject((_value == other.value())); - } - - - IntObject operator!=(const StringObject &other) const - { - return IntObject((_value != other.value())); - } -}; \ No newline at end of file diff --git a/src/objects/StructObject.cpp b/src/objects/StructObject.cpp deleted file mode 100644 index fe33ac9..0000000 --- a/src/objects/StructObject.cpp +++ /dev/null @@ -1,70 +0,0 @@ -/** - * @file StructObject.cpp - * @author Edward Palmer - * @date 2024-11-24 - * - * @copyright Copyright (c) 2024 - * - */ - -#include "StructObject.hpp" -#include "FloatObject.hpp" -#include "IntObject.hpp" -#include "Scope.hpp" -#include "StringObject.hpp" -#include - -StructObject::StructObject(std::string typeName_, std::string name_) - : typeName(std::move(typeName_)), name(std::move(name_)) -{ -} - - -BaseObject::Ptr StructObject::evaluate(Scope &scope) -{ - if (active) - { - ThrowException("StructObject named " + name + " of type " + typeName + " is already active"); - } - - active = true; - - // Initialize our instance from the struct definition defined in the scope. - structDefinition = scope.getNamedObject(typeName); - structDefinition->installVariablesInScope(_instanceScope, variableNames); - - // Add the active struct instance to the scope. TODO: - transfer ownership - // to the scope. Will have to remove this class from AST to do this correctly. - scope.linkObject(name, shared_from_this()); - return shared_from_this(); -} - - -StructObject &StructObject::operator=(const BaseObject &other) -{ - if (this == &other) - { - return (*this); - } - - // 1. cast to struct object. - const StructObject &otherStruct = other.castObject(); - - // 2. check that both are instances of the same struct type. - if (otherStruct.structDefinition != structDefinition) - { - ThrowException("attempting to assign different struct types"); - } - - // 3. iterate over the objects stored in the scopes and assign. - for (auto &variableName : variableNames) - { - BaseObject::Ptr thisObject = _instanceScope.getNamedObject(variableName); - BaseObject::Ptr otherObject = otherStruct._instanceScope.getNamedObject(variableName); - - // Attempt an assignment. Will fail if different types. - (*thisObject) = (*otherObject); - } - - return (*this); -} diff --git a/src/parsers/FileParser.cpp b/src/parsers/FileParser.cpp index eac0e1b..ac5290c 100644 --- a/src/parsers/FileParser.cpp +++ b/src/parsers/FileParser.cpp @@ -10,7 +10,6 @@ #include "Grammar.hpp" #include "Logger.hpp" #include "NodeFactory.hpp" -#include "ObjectTypes.hpp" #include "ParserData.hpp" #include #include diff --git a/src/parsers/FileParser.hpp b/src/parsers/FileParser.hpp index 390aadf..8638569 100644 --- a/src/parsers/FileParser.hpp +++ b/src/parsers/FileParser.hpp @@ -11,8 +11,8 @@ #include "BaseParser.hpp" #include "BinaryNode.hpp" #include "BlockSubParser.hpp" -#include "ClassDefinitionObject.hpp" -#include "ClassObject.hpp" +#include "ClassDefinitionNode.hpp" +#include "ClassNode.hpp" #include "ControlFlowSubParser.hpp" #include "DataTypeSubParser.hpp" #include "FunctionCallNode.hpp" @@ -21,8 +21,8 @@ #include "ImportSubParser.hpp" #include "LookupVariableNode.hpp" #include "LoopSubParser.hpp" -#include "StructDefinitionObject.hpp" -#include "StructObject.hpp" +#include "StructDefinitionNode.hpp" +#include "StructNode.hpp" #include "SubParsers.hpp" #include "Tokenizer.hpp" #include "UnaryOperatorSubParser.hpp" diff --git a/src/subparsers/ClassSubParser.cpp b/src/subparsers/ClassSubParser.cpp index cb55181..6c5ee46 100644 --- a/src/subparsers/ClassSubParser.cpp +++ b/src/subparsers/ClassSubParser.cpp @@ -12,8 +12,8 @@ #include "Exceptions.hpp" #include "FileParser.hpp" #include "NodeFactory.hpp" -#include "StructDefinitionObject.hpp" -#include "StructObject.hpp" +#include "StructDefinitionNode.hpp" +#include "StructNode.hpp" #include @@ -45,19 +45,19 @@ BaseNode::Ptr ClassSubParser::parseStruct() variableDefs.push_back(std::reinterpret_pointer_cast(node)); } - return std::make_shared(structTypeName, structParentTypeName, variableDefs); + return std::make_shared(structTypeName, structParentTypeName, variableDefs); } else { // Case: "struct STRUCT_TYPE_NAME & STRUCT_REF_INSTANCE_NAME = STRUCT_VARIABLE_NAME_TO_BIND" if (equals(Token::Operator, "&")) { - return parent().subparsers().variable.parseReference(ObjectType::Struct); + return parent().subparsers().variable.parseReference(AnyObject::Struct); } auto structInstanceName = tokens().dequeue(); - return std::make_shared(structTypeName, structInstanceName); + return std::make_shared(structTypeName, structInstanceName); } } @@ -97,19 +97,19 @@ BaseNode::Ptr ClassSubParser::parseClass() ThrowException("unexpected node type for class definition " + classTypeName); } - return std::make_shared(classTypeName, classParentTypeName, classVariables, classMethods); + return std::make_shared(classTypeName, classParentTypeName, classVariables, classMethods); } else { // Case: "class CLASS_INSTANCE_NAME & CLASS_REF_NAME = CLASS_VARIABLE_NAME_TO_BIND" if (equals(Token::Operator, "&")) { - return parent().subparsers().variable.parseReference(ObjectType::Class); + return parent().subparsers().variable.parseReference(AnyObject::Class); } auto classInstanceName = tokens().dequeue(); - return std::make_shared(classTypeName, classInstanceName); + return std::make_shared(classTypeName, classInstanceName); } return nullptr; diff --git a/src/subparsers/VariableSubParser.cpp b/src/subparsers/VariableSubParser.cpp index 6251072..1cabc6e 100644 --- a/src/subparsers/VariableSubParser.cpp +++ b/src/subparsers/VariableSubParser.cpp @@ -18,7 +18,7 @@ BaseNode::Ptr VariableSubParser::parseVariableDefinition() Token typeToken = tokens().dequeue(); assert(typeToken.type() == Token::Keyword); - ObjectType typeOfObject = objectTypeForName(typeToken); + AnyObject::Type typeOfObject = AnyObject::getUserObjectType(typeToken); if (tokens().front() == "&") // Is reference. { @@ -32,7 +32,7 @@ BaseNode::Ptr VariableSubParser::parseVariableDefinition() } -BaseNode::Ptr VariableSubParser::parseReference(ObjectType boundVariableType) +BaseNode::Ptr VariableSubParser::parseReference(AnyObject::Type boundVariableType) { skip("&"); diff --git a/src/subparsers/VariableSubParser.hpp b/src/subparsers/VariableSubParser.hpp index bf1a30b..fb5886d 100644 --- a/src/subparsers/VariableSubParser.hpp +++ b/src/subparsers/VariableSubParser.hpp @@ -8,9 +8,8 @@ */ #pragma once - +#include "AnyObject.hpp" #include "BaseNode.hpp" -#include "ObjectTypes.hpp" #include "SubParser.hpp" class FileParser; @@ -31,7 +30,7 @@ class VariableSubParser : public SubParser * * Parse: VARIABLE_TO_BIND_TO_TYPE & REFERENCE_NAME = VARIABLE_TO_BIND_TO; */ - BaseNode::Ptr parseReference(ObjectType boundVariableType_); + BaseNode::Ptr parseReference(AnyObject::Type boundVariableType_); /* Parse: [variable name] */ BaseNode::Ptr parseVariable(); diff --git a/src/utility/JumpPoints.cpp b/src/utility/JumpPoints.cpp index 93956d2..3ad8b12 100644 --- a/src/utility/JumpPoints.cpp +++ b/src/utility/JumpPoints.cpp @@ -8,9 +8,11 @@ */ #include "JumpPoints.hpp" +#include "AnyObject.hpp" #include "Exceptions.hpp" #include + std::stack gBreakJumpPointStack; void pushBreakJumpPoint(jmp_buf *jumpPoint) diff --git a/src/utility/JumpPoints.hpp b/src/utility/JumpPoints.hpp index f2030c6..9c85e21 100644 --- a/src/utility/JumpPoints.hpp +++ b/src/utility/JumpPoints.hpp @@ -8,15 +8,16 @@ */ #pragma once -#include "BaseObject.hpp" #include +#include + struct GlobalEnvRec { jmp_buf *breakJumpPoint; jmp_buf *returnJumpPoint; - BaseObject::Ptr returnValue{nullptr}; + std::shared_ptr returnValue{nullptr}; }; extern GlobalEnvRec gEnvironmentContext; // TODO: - remove once return done as well. diff --git a/src/utility/Logger.hpp b/src/utility/Logger.hpp index 3ed0905..790c49c 100644 --- a/src/utility/Logger.hpp +++ b/src/utility/Logger.hpp @@ -29,7 +29,7 @@ enum class LogLevel Info = 2, Warning = 3, Error = 4, - Critical = 5 + Critical = 5, }; diff --git a/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek b/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek index 6c24f5c..a7ddcc5 100644 --- a/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek +++ b/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek @@ -2,18 +2,16 @@ import func DifferenceSumOfSquaresAndSquareOfSum(int max) { - float sum = 0; - float sumSquares = 0; + int sum = 0; + int sumSquares = 0; for (int i = 1; i <= max; ++i) { - float fCounter = i; - - sum = sum + fCounter; - sumSquares = sumSquares + pow(fCounter, 2.0); // Careful! Issues with passing Int to func expecting float! + sum = sum + i; + sumSquares = sumSquares + pow(i, 2); // Careful! Issues with passing Int to func expecting float! } - int lhs = pow(sum, 2.0); // Note the implicit conversion back. + int lhs = pow(sum, 2); // Note the implicit conversion back. int rhs = sumSquares; return (lhs - rhs); diff --git a/test/functional/data/ArrayTests.ek b/test/functional/data/ArrayTests.ek index 88cae33..e65cfb7 100644 --- a/test/functional/data/ArrayTests.ek +++ b/test/functional/data/ArrayTests.ek @@ -11,13 +11,17 @@ import TEST(first[index] == 1, "index using variable"); int val = first[0]; + print("The value is: ", val); + TEST(val == 1 && first[0] == 1, "assign to variable"); val = 2; TEST(val == 2 && first[0] == 1, "copy array value"); first[0] = first[1]; + print("Performing array assignment (setting)"); TEST(first[0] == 2, "array setter (int)"); + print(first); // Copy to second and modify. array second = first; @@ -25,6 +29,7 @@ import second[0] = 100; TEST(second[0] == 100 && first[0] == 2, "modify copied array"); + print(first, second); array a = [1, 2, 3]; array b = [4, 5, 6]; diff --git a/test/functional/data/LoopTests.ek b/test/functional/data/LoopTests.ek index 255b283..4148fec 100644 --- a/test/functional/data/LoopTests.ek +++ b/test/functional/data/LoopTests.ek @@ -4,7 +4,7 @@ import { int counter = 0; - while (1) + while (true) { if (++counter >= 10) { From 8ab97fe298446581a02280a17568efe3585366dc Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Mon, 19 May 2025 17:38:32 +0100 Subject: [PATCH 07/15] Adds support for PoolAllocator --- src/objects/AnyObject.hpp | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/objects/AnyObject.hpp b/src/objects/AnyObject.hpp index bba6e3c..f4f8eaa 100644 --- a/src/objects/AnyObject.hpp +++ b/src/objects/AnyObject.hpp @@ -10,13 +10,17 @@ #pragma once #include "BaseNode.hpp" #include "ModuleFunctor.hpp" +#include "PoolAllocator.hpp" #include #include +#include #include #include #include #include +// TODO: - profile and investigate using PoolAllocator +// static PoolAllocator allocator{10}; class AnyObject { @@ -43,6 +47,17 @@ class AnyObject _ClassDefinition, }; + // void *operator new(size_t size) + // { + // return allocator.allocate(size); + // } + + // void operator delete(void *ptr) + // { + // allocator.deallocate(ptr); + // } + + /* Converts user types like "int" --> AnyType::Int or returns None if not found */ static AnyObject::Type getUserObjectType(const std::string &name); From 455defe00cd71b80fd3520a8586a27034068d887 Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Mon, 19 May 2025 18:32:55 +0100 Subject: [PATCH 08/15] Adds explicit cast, fixes pow() function and test cases --- src/nodes/BaseNode.hpp | 3 +- src/nodes/ModuleNodeFactory.cpp | 11 +---- src/nodes/NodeFactory.cpp | 40 ++++++++++++++++++- src/nodes/NodeFactory.hpp | 4 ++ src/subparsers/VariableSubParser.cpp | 23 +++++++++-- src/subparsers/VariableSubParser.hpp | 3 ++ .../DifferenceSumOfSquaresAndSquareOfSum.ek | 7 ++-- test/functional/InterpreterTests.cpp | 6 +++ test/functional/data/CastTests.ek | 14 +++++++ 9 files changed, 93 insertions(+), 18 deletions(-) create mode 100644 test/functional/data/CastTests.ek diff --git a/src/nodes/BaseNode.hpp b/src/nodes/BaseNode.hpp index 79d8133..32d151e 100644 --- a/src/nodes/BaseNode.hpp +++ b/src/nodes/BaseNode.hpp @@ -44,7 +44,8 @@ enum class NodeType ClassMethodCall, ArrayAccess, AddVariable, - Module + Module, + Cast }; diff --git a/src/nodes/ModuleNodeFactory.cpp b/src/nodes/ModuleNodeFactory.cpp index 9cd1706..b20c829 100644 --- a/src/nodes/ModuleNodeFactory.cpp +++ b/src/nodes/ModuleNodeFactory.cpp @@ -75,16 +75,9 @@ AnyNode::Ptr createMathModuleNode() /* TODO: - bug if pass in an integer rather than a floatObject */ auto firstObject = callArgs.front()->evaluate(scope); - auto secondObject = callArgs.front()->evaluate(scope); + auto secondObject = callArgs.back()->evaluate(scope); - if (firstObject->isType(AnyObject::Int) && secondObject->isType(AnyObject::Int)) /* Implicit casts */ - { - return ObjectFactory::allocate(pow(firstObject->getValue(), secondObject->getValue())); - } - else /* Assume both are doubles */ - { - return ObjectFactory::allocate(pow(firstObject->getValue(), secondObject->getValue())); - } + return ObjectFactory::allocate(pow(firstObject->getValue(), secondObject->getValue())); }); return NodeFactory::createModuleNode("math", {doSqrt, doPow}); diff --git a/src/nodes/NodeFactory.cpp b/src/nodes/NodeFactory.cpp index 7706534..8ce289a 100644 --- a/src/nodes/NodeFactory.cpp +++ b/src/nodes/NodeFactory.cpp @@ -23,6 +23,44 @@ namespace NodeFactory { +AnyNode::Ptr createCastNode(BaseNode::Ptr expression, AnyObject::Type castToType) +{ + auto isCastable = [](AnyObject::Type castToType) -> bool + { + return (castToType == AnyObject::Int || castToType == AnyObject::Float); + }; + + if (!isCastable(castToType)) + { + ThrowException("Cannot create cast node. Unsupported cast type!"); + } + + return std::make_shared(NodeType::Cast, [isCastable, expression, castToType](Scope &scope) + { + auto evaluatedObject = expression->evaluate(scope); + if (!isCastable(evaluatedObject->getType())) + { + ThrowException("Unsupported cast type!"); + } + + if (evaluatedObject->getType() == castToType) /* Nothing to do */ + { + return evaluatedObject; + } + + if (castToType == AnyObject::Int) + { + long value = (long)expression->evaluate(scope)->getValue(); + return ObjectFactory::allocate(value); + } + else + { + double value = (double)expression->evaluate(scope)->getValue(); + return ObjectFactory::allocate(value); + } + }); +} + AnyNode::Ptr createBoolNode(bool state) { return std::make_shared(NodeType::Bool, [state](Scope &) @@ -372,7 +410,7 @@ AnyPropertyNode::Ptr createArrayAccessNode(BaseNode::Ptr arrayLookupNode, BaseNo auto &arrayObj = theArrayObject->getValue(); auto index = arrayIndexNode->evaluate(scope)->getValue(); - if (index < 0 || index >= arrayObj.size()) + if (index < 0 || index >= (long)arrayObj.size()) ThrowException("Array index [" + std::to_string(index) + "] is out of bounds!"); return arrayObj[index]; diff --git a/src/nodes/NodeFactory.hpp b/src/nodes/NodeFactory.hpp index ddcda7c..47c7fce 100644 --- a/src/nodes/NodeFactory.hpp +++ b/src/nodes/NodeFactory.hpp @@ -9,6 +9,7 @@ #pragma once #include "AnyNode.hpp" +#include "AnyObject.hpp" #include "BaseNode.hpp" #include "FunctionCallNode.hpp" #include "ModuleFunctor.hpp" @@ -18,6 +19,9 @@ namespace NodeFactory { + +AnyNode::Ptr createCastNode(BaseNode::Ptr expression, AnyObject::Type castToType); + AnyNode::Ptr createBoolNode(bool state); AnyNode::Ptr createIntNode(long value); diff --git a/src/subparsers/VariableSubParser.cpp b/src/subparsers/VariableSubParser.cpp index 1cabc6e..159443b 100644 --- a/src/subparsers/VariableSubParser.cpp +++ b/src/subparsers/VariableSubParser.cpp @@ -9,9 +9,13 @@ #include "VariableSubParser.hpp" #include "AddVariableNode.hpp" +#include "AnyNode.hpp" #include "FileParser.hpp" #include "LookupVariableNode.hpp" +#include "NodeFactory.hpp" #include "Token.hpp" +#include + BaseNode::Ptr VariableSubParser::parseVariableDefinition() { @@ -20,10 +24,14 @@ BaseNode::Ptr VariableSubParser::parseVariableDefinition() AnyObject::Type typeOfObject = AnyObject::getUserObjectType(typeToken); - if (tokens().front() == "&") // Is reference. + if (tokens().front() == "&") // Is reference: [type] & [name] = [another object] { return parseReference(typeOfObject); } + else if (tokens().front() == "(") // Is cast: i.e., float(1), float(intVar), bool(1), int(1.2) + { + return parseCast(typeOfObject); + } Token nameToken = tokens().dequeue(); assert(nameToken.type() == Token::Variable); @@ -34,7 +42,7 @@ BaseNode::Ptr VariableSubParser::parseVariableDefinition() BaseNode::Ptr VariableSubParser::parseReference(AnyObject::Type boundVariableType) { - skip("&"); + skip("&"); // TODO: - add type-checking Token referenceNameToken = tokens().dequeue(); assert(referenceNameToken.type() == Token::Variable); @@ -54,4 +62,13 @@ BaseNode::Ptr VariableSubParser::parseVariable() assert(token.type() == Token::Variable); return std::make_shared(token); -} \ No newline at end of file +} + + +BaseNode::Ptr VariableSubParser::parseCast(AnyObject::Type castType) +{ + /* Expression in ( expression ) to cast return type of */ + auto expression = parent().parseBrackets(); + + return NodeFactory::createCastNode(expression, castType); +} diff --git a/src/subparsers/VariableSubParser.hpp b/src/subparsers/VariableSubParser.hpp index fb5886d..9761eec 100644 --- a/src/subparsers/VariableSubParser.hpp +++ b/src/subparsers/VariableSubParser.hpp @@ -34,4 +34,7 @@ class VariableSubParser : public SubParser /* Parse: [variable name] */ BaseNode::Ptr parseVariable(); + + /* Parse: int([bool/int/float]), float([bool/int/float]) */ + BaseNode::Ptr parseCast(AnyObject::Type castType); }; diff --git a/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek b/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek index a7ddcc5..85cf0a5 100644 --- a/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek +++ b/test/benchmark/data/DifferenceSumOfSquaresAndSquareOfSum.ek @@ -8,13 +8,12 @@ func DifferenceSumOfSquaresAndSquareOfSum(int max) for (int i = 1; i <= max; ++i) { sum = sum + i; - sumSquares = sumSquares + pow(i, 2); // Careful! Issues with passing Int to func expecting float! + sumSquares = sumSquares + int(pow(float(i), 2.0)); } - int lhs = pow(sum, 2); // Note the implicit conversion back. - int rhs = sumSquares; + int lhs = int(pow(float(sum), 2.0)); // Note the explicit conversion back. - return (lhs - rhs); + return (lhs - sumSquares); } DifferenceSumOfSquaresAndSquareOfSum(100); // 25164150 diff --git a/test/functional/InterpreterTests.cpp b/test/functional/InterpreterTests.cpp index af8e3b7..7df386d 100644 --- a/test/functional/InterpreterTests.cpp +++ b/test/functional/InterpreterTests.cpp @@ -83,6 +83,12 @@ TEST(InterpreterTestSuite, StructTests) } +TEST(InterpreterTestSuite, CastTests) +{ + Interpreter::evaluateFile(testDataPath("CastTests.ek")); +} + + std::string testDataPath(std::string fileName) { return getTestDirPath() + "functional/data/" + fileName; diff --git a/test/functional/data/CastTests.ek b/test/functional/data/CastTests.ek new file mode 100644 index 0000000..6d2f3ed --- /dev/null +++ b/test/functional/data/CastTests.ek @@ -0,0 +1,14 @@ +import +import + +int a = int(5.2); +TEST(a == 5, "cast float to int"); + +float b = float(2); +TEST(b == 2.0, "cast int to float"); + +float c = 4.5; +TEST(int(c) == 4 && c == 4.5, "cast a float variable"); + +float &d = c; +TEST(int(d) == 4 && d == 4.5 && c == 4.5, "cast a float reference"); From ee99c4e0226ac6117b7e1537fd7f7510859fd1e8 Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 25 May 2025 11:41:18 +0100 Subject: [PATCH 09/15] Improves Logger formatting --- src/utility/Logger.cpp | 12 ++++++------ src/utility/Logger.hpp | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/utility/Logger.cpp b/src/utility/Logger.cpp index 266aec5..d2a8f88 100644 --- a/src/utility/Logger.cpp +++ b/src/utility/Logger.cpp @@ -68,17 +68,17 @@ std::string LoggerImpl::levelName(LogLevel level) const switch (level) { case LogLevel::Trace: - return "trace"; + return "Trace"; case LogLevel::Debug: - return "debug"; + return "Debug"; case LogLevel::Info: - return "info"; + return "Info"; case LogLevel::Warning: - return "warning"; + return "Warn"; case LogLevel::Error: - return "error"; + return "Error"; case LogLevel::Critical: - return "critical"; + return "Critical"; default: ThrowException("invalid Level enum"); } diff --git a/src/utility/Logger.hpp b/src/utility/Logger.hpp index 790c49c..27a8782 100644 --- a/src/utility/Logger.hpp +++ b/src/utility/Logger.hpp @@ -90,7 +90,7 @@ class LoggerImpl std::condition_variable _cv; /* ISO 8601 date time format */ - const std::string _timestampFormat{"%Y-%m-%dT%H:%M:%S"}; + const std::string _timestampFormat{"%Y%m%d %H:%M:%S"}; const std::string _logPath{"/var/log/eucleia.log"}; }; From a86fd3763ee7bab3d487dcf1f988e8b0e38b385a Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 25 May 2025 11:43:03 +0100 Subject: [PATCH 10/15] Adds token description string --- src/lexer/Token.cpp | 38 ++++++++++++++++++++++++++++++++++++++ src/lexer/Token.hpp | 4 +++- 2 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 src/lexer/Token.cpp diff --git a/src/lexer/Token.cpp b/src/lexer/Token.cpp new file mode 100644 index 0000000..769826d --- /dev/null +++ b/src/lexer/Token.cpp @@ -0,0 +1,38 @@ +/** + * @file Token.cpp + * @author Edward Palmer + * @date 2025-05-24 + * + * @copyright Copyright (c) 2025 + * + */ + +#include "Token.hpp" + + +std::string Token::typeToString() const /* TODO: - more efficient to have a static maps and return a reference to string */ +{ + switch (_type) + { + case NotSet: + return "NotSet"; + case EndOfFile: + return "EndOfFile"; + case Punctuation: + return "Punctuation"; + case Keyword: + return "Keyword"; + case Variable: + return "Variable"; + case String: + return "String"; + case Operator: + return "Operator"; + case Int: + return "Int"; + case Float: + return "Float"; + default: + ThrowException("Unknown token type"); + } +} \ No newline at end of file diff --git a/src/lexer/Token.hpp b/src/lexer/Token.hpp index 0caa944..f5abf78 100644 --- a/src/lexer/Token.hpp +++ b/src/lexer/Token.hpp @@ -31,6 +31,8 @@ class Token : public std::string Float }; + std::string typeToString() const; + /* Constructors */ Token(Type type) : _type(type) {} Token(std::string &value, Type type = NotSet) : std::string(value), _type(type) {} @@ -115,4 +117,4 @@ Token Tokens::dequeue() pop(); return next; -} \ No newline at end of file +} From cd2f1d1666ae3356289a3be01be919d3af425077 Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 25 May 2025 11:43:22 +0100 Subject: [PATCH 11/15] Improves tokenizer debug message --- src/lexer/Tokenizer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer/Tokenizer.cpp b/src/lexer/Tokenizer.cpp index f81e957..14574ec 100644 --- a/src/lexer/Tokenizer.cpp +++ b/src/lexer/Tokenizer.cpp @@ -24,7 +24,7 @@ Tokens Tokenizer::buildTokens(const std::string &path) while (!stream.isLast()) { Token token = buildNextToken(stream); - log().debug(stream.location() + ": " + token); + log().debug("Parsed '" + token + "' => " + token.typeToString() + " " + stream.location()); if (token.type() != Token::EndOfFile) tokens.push(std::move(token)); From f904baa791eabd18914908d06c8c6de4dc48e96b Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 25 May 2025 11:44:36 +0100 Subject: [PATCH 12/15] Short filename rather than path in location() --- src/lexer/CharStream.cpp | 3 ++- src/lexer/CharStream.hpp | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lexer/CharStream.cpp b/src/lexer/CharStream.cpp index baef7cc..c963a2f 100644 --- a/src/lexer/CharStream.cpp +++ b/src/lexer/CharStream.cpp @@ -14,6 +14,7 @@ #include "Stringify.hpp" #include #include +#include #include #include @@ -172,5 +173,5 @@ unsigned int CharStream::endCol(unsigned int lineNum) const std::string CharStream::location() const { - return eucleia::stringify("File \"%s\", Ln %d, Col %d", path.c_str(), line, col); + return eucleia::stringify("(%s:%d:%d)", path.filename().c_str(), line, col); } \ No newline at end of file diff --git a/src/lexer/CharStream.hpp b/src/lexer/CharStream.hpp index 5bd64b7..b6acc08 100644 --- a/src/lexer/CharStream.hpp +++ b/src/lexer/CharStream.hpp @@ -8,6 +8,7 @@ */ #pragma once +#include #include #include @@ -59,8 +60,7 @@ class CharStream private: unsigned int endCol(unsigned int lineNum) const; - - const std::string path; + const std::filesystem path; char *base{nullptr}; char *ptr{nullptr}; From 447cad745216802d5e2cb1b2761383c3f78233b6 Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 25 May 2025 11:47:32 +0100 Subject: [PATCH 13/15] Move to references --- src/environment/Scope.cpp | 86 +++++++++++------ src/environment/Scope.hpp | 49 +++++----- src/nodes/AddVariableNode.cpp | 60 +++++------- src/nodes/AddVariableNode.hpp | 15 +-- src/nodes/AnyNode.cpp | 10 +- src/nodes/AnyNode.hpp | 22 ++--- src/nodes/BaseNode.cpp | 24 +++++ src/nodes/BaseNode.hpp | 9 +- src/nodes/BinaryNode.cpp | 81 ++++++++-------- src/nodes/BinaryNode.hpp | 14 +-- src/nodes/ClassDefinitionNode.cpp | 13 ++- src/nodes/ClassDefinitionNode.hpp | 2 +- src/nodes/ClassNode.cpp | 11 +-- src/nodes/ClassNode.hpp | 2 +- src/nodes/FunctionCallNode.cpp | 23 ++--- src/nodes/FunctionCallNode.hpp | 7 +- src/nodes/FunctionNode.cpp | 16 ++-- src/nodes/FunctionNode.hpp | 3 +- src/nodes/LookupVariableNode.cpp | 10 +- src/nodes/LookupVariableNode.hpp | 3 +- src/nodes/ModuleNodeFactory.cpp | 39 ++++---- src/nodes/NodeFactory.cpp | 147 ++++++++++++++--------------- src/nodes/NodeFactory.hpp | 3 +- src/nodes/PropertyInterface.hpp | 32 ------- src/nodes/StructDefinitionNode.cpp | 11 +-- src/nodes/StructDefinitionNode.hpp | 2 +- src/nodes/StructNode.cpp | 14 ++- src/nodes/StructNode.hpp | 2 +- src/objects/AnyObject.cpp | 38 +++++--- src/objects/AnyObject.hpp | 28 ++++-- src/objects/ModuleFunctor.cpp | 5 +- src/objects/ModuleFunctor.hpp | 6 +- src/objects/ObjectFactory.cpp | 14 +-- src/objects/ObjectFactory.hpp | 8 +- src/parsers/BaseParser.cpp | 1 + src/utility/JumpPoints.hpp | 4 +- 36 files changed, 429 insertions(+), 385 deletions(-) create mode 100644 src/nodes/BaseNode.cpp delete mode 100644 src/nodes/PropertyInterface.hpp diff --git a/src/environment/Scope.cpp b/src/environment/Scope.cpp index 4ea2272..1b435b3 100644 --- a/src/environment/Scope.cpp +++ b/src/environment/Scope.cpp @@ -10,70 +10,94 @@ #include "Scope.hpp" #include "AnyObject.hpp" #include "Exceptions.hpp" +#include "Logger.hpp" #include +#include +#include -Scope::Scope(const Scope &_parent) - : Scope(&_parent) -{ -} -Scope::Scope(const Scope *_parent) - : parent(const_cast(_parent)) +Scope::Scope(const Scope &parentScope) + : _enclosingScope(const_cast(&parentScope)) { } -AnyObject::Ptr Scope::getOptionalNamedObject(const std::string &name) const +AnyObject *Scope::getObjectPtr(const VariableName &name) const { - // Try in our scope (to handle variable shadowing). - auto iter = linkedObjectForName.find(name); - if (iter != linkedObjectForName.end()) + auto iter = _objectPtrMap.find(name); + if (iter != _objectPtrMap.end()) { - return (iter->second); + return const_cast(iter->second); // TODO: - bit dodgy with const_cast } // Otherwise check if it is defined in our parent's scope? Keep working outwards. - if (parent) + if (_enclosingScope) { - return parent->getOptionalNamedObject(name); + return _enclosingScope->getObjectPtr(name); } - // Not defined. return nullptr; } +#include "Logger.hpp" -AnyObject::Ptr Scope::getNamedObject(const std::string &name) const +AnyObject &Scope::getObjectRef(const VariableName &name) const { - AnyObject::Ptr obj = getOptionalNamedObject(name); - if (!obj) + log().info("Getting object reference for name " + name); + auto ptr = getObjectPtr(name); + + if (ptr) { - ThrowException("undefined variable " + name); + log().debug("the pointer type is: <" + ptr->typeToString() + ">"); + + return *ptr; } - return obj; + // Not defined. + ThrowException("No variable defined in scope with name [" + name + "]"); } -bool Scope::hasNamedObject(const std::string &name) const +void Scope::checkForNameClashesInCurrentScope(const VariableName &name) const { - return (getOptionalNamedObject(name) != nullptr); + if (!_objectPtrMap.count(name)) + return; + + ThrowException("Variable [" + name + "] is already defined in current scope"); } -void Scope::linkObject(const std::string &name, AnyObject::Ptr object) +AnyObject::Ref Scope::alias(const VariableName &nameAlias, const VariableName &name) { - assert(object != nullptr); + checkForNameClashesInCurrentScope(nameAlias); - // 1. Check for name clashes. This is where we have two variables with - // the same name defined in the SAME scope. - auto iter = linkedObjectForName.find(name); - if (iter != linkedObjectForName.end()) + auto *object = getObjectPtr(name); + if (!object) { - ThrowException(name + " is already defined in current scope"); + ThrowException("No variable defined in scope with name [" + name + "]"); } - // 2. Add to map. This will ensure that we now ignore any outer-scope variables - // with this name (variable shadowing). - linkedObjectForName[name] = object; + _objectPtrMap[nameAlias] = object; + + return std::ref(*object); +} + + +AnyObject::Ref Scope::link(const VariableName &name, AnyObject &&object) +{ + log().debug("Adding variable '" + name + "' with type '" + object.typeToString() + "'"); + + assert(!name.empty() && object.getType() != AnyObject::NotSet); + + // 1. Check for name clashes. This is where we have two variables with the same name defined in the SAME scope. + // Note that it's okay to have the same variable defined multiple times if they're in different scopes -- + // this is 'variable shadowing'. + checkForNameClashesInCurrentScope(name); + + // 2. Add to map. This will ensure that we now ignore any outer-scope variables with this name (variable shadowing). + _objects.push_back(object); + + _objectPtrMap[name] = &_objects.back(); + + return _objects.back(); } diff --git a/src/environment/Scope.hpp b/src/environment/Scope.hpp index 0e64f93..347bbdc 100644 --- a/src/environment/Scope.hpp +++ b/src/environment/Scope.hpp @@ -8,44 +8,47 @@ */ #pragma once -#include #include #include -#include +#include class Scope { public: - Scope(const Scope &_parent); - Scope(const Scope *_parent = nullptr); - ~Scope() = default; + using VariableName = std::string; - /// Returns true if named object ("variable") is defined in our scope or in - /// a parent scope. - bool hasNamedObject(const std::string &name) const; + Scope() = default; + Scope(const Scope &parentScope); - /// Get a named object ("variable") in our scope or an outer scope. We work - /// outwards from our scope to handle variable shadowing correctly. If the - /// object is not found, return nullptr. - std::shared_ptr getOptionalNamedObject(const std::string &name) const; + /// Get a named object ("variable") in our scope or an outer scope. We work outwards from our scope to handle variable shadowing correctly. + class AnyObject &getObjectRef(const VariableName &name) const; - /// Similar to getOptionalObject but has a check to ensure pointer is valid. - std::shared_ptr getNamedObject(const std::string &name) const; + class AnyObject *getObjectPtr(const VariableName &name) const; + + /// Create a link between a variable name and an object in this scope. + class AnyObject &link(const VariableName &name, AnyObject &&object); + + /// Add a link between an already-defined object in this scope and another name to reference it. + class AnyObject &alias(const VariableName &nameAlias, const VariableName &name); /// Returns non-const reference to parent scope. - inline Scope *parentScope() { return parent; } + inline Scope *parentScope() { return _enclosingScope; } /// Set a new parent scope. Use with care! - void setParentScope(Scope *parent_) { parent = parent_; } + void setParentScope(Scope *parent) { _enclosingScope = parent; } - /// Create a link between a variable name and an object in this scope. - void linkObject(const std::string &name, std::shared_ptr object); +protected: + /// Throws if the name is already defined in this scope. + void checkForNameClashesInCurrentScope(const VariableName &name) const; private: - /// Stores a mapping from the variable name to a pointer to the object. These - /// are only linked objects defined in this scope. This enables variable - /// shadowing. - std::unordered_map> linkedObjectForName; + using AnyObjectPtrMap = std::unordered_map; + + /* Stores references (mapped to _linkedObjects vector) */ + AnyObjectPtrMap _objectPtrMap; + + /* Stores all objects added to scope */ + std::list _objects; - Scope *parent{nullptr}; + Scope *_enclosingScope{nullptr}; }; diff --git a/src/nodes/AddVariableNode.cpp b/src/nodes/AddVariableNode.cpp index a5f689f..1dce62f 100644 --- a/src/nodes/AddVariableNode.cpp +++ b/src/nodes/AddVariableNode.cpp @@ -10,6 +10,8 @@ #include "AddVariableNode.hpp" #include "Exceptions.hpp" #include "ObjectFactory.hpp" +#include "Scope.hpp" + AddVariableNode::AddVariableNode(std::string name, AnyObject::Type type) : LookupVariableNode(std::move(name)), @@ -18,40 +20,24 @@ AddVariableNode::AddVariableNode(std::string name, AnyObject::Type type) setType(NodeType::AddVariable); } -AnyObject::Ptr AddVariableNode::evaluate(Scope &scope) -{ - /* TODO: - add support for functions (to enable passing to other functions, etc) */ - auto objectPtr = ObjectFactory::allocate(_variableType); - scope.linkObject(name(), objectPtr); - return objectPtr; +AnyObject::Ref AddVariableNode::evaluateRef(Scope &scope) +{ + /* Construct an empty object of that type and add to the scope. We assume user will set value with copy assignment */ + return scope.link(name(), ObjectFactory::createEmptyObject(_variableType)); } -/// Type checking. -bool AddVariableNode::passesAssignmentTypeCheck(const AnyObject &assignObject) const +AnyObject AddVariableNode::evaluate(Scope &scope) { - return assignObject.isType(_variableType); + return evaluateRef(scope); } -std::string AddVariableNode::description() const +/// Type checking. +bool AddVariableNode::passesAssignmentTypeCheck(const AnyObject &assignObject) const { - switch (_variableType) - { - case AnyObject::Bool: - return "Bool"; - case AnyObject::Int: - return "Int"; - case AnyObject::Float: - return "Float"; - case AnyObject::String: - return "String"; - case AnyObject::Array: - return "Array"; - default: - return "Unknown"; - } + return assignObject.isType(_variableType); } @@ -64,23 +50,25 @@ AddReferenceVariableNode::AddReferenceVariableNode(std::string referenceName_, } -AnyObject::Ptr AddReferenceVariableNode::evaluate(Scope &scope) +AnyObject::Ref AddReferenceVariableNode::evaluateRef(Scope &scope) { - // 1. Lookup the object associated with the variable name defined in this - // scope or a parent scope (no issue with lifetimes such as to be bound - // object going out of scope before our reference. - AnyObject::Ptr boundObject = scope.getNamedObject(name()); + // 1. Lookup the object associated with the variable name defined in this scope or a parent scope (no issue with + // lifetimes such as to be bound object going out of scope before our reference. + AnyObject::Ref boundObject = scope.getObjectRef(name()); // TODO: - this will not work for classes/structs since they could point to different types. // 2. Type checking. The type of the reference must match that of the bound object. - if (!passesAssignmentTypeCheck(*boundObject)) + if (!passesAssignmentTypeCheck(boundObject)) { - ThrowException("Cannot bind reference " + referenceName + " to variable " + name() + ". Types do not match!"); + ThrowException("Cannot bind reference [" + referenceName + "] to variable [" + name() + "]. Types do not match!"); } - // 3. Instead of creating a new object, we add the reference name and link - // to this existing object in the scope. - scope.linkObject(referenceName, boundObject); + // 3. Instead of creating a new object, we add the reference name and link to this existing object in the scope. + return scope.alias(referenceName, name()); +} + - return boundObject; +AnyObject AddReferenceVariableNode::evaluate(Scope &scope) +{ + return evaluateRef(scope); /* Copy result */ } \ No newline at end of file diff --git a/src/nodes/AddVariableNode.hpp b/src/nodes/AddVariableNode.hpp index ba24698..d571bda 100644 --- a/src/nodes/AddVariableNode.hpp +++ b/src/nodes/AddVariableNode.hpp @@ -21,21 +21,22 @@ class AddVariableNode : public LookupVariableNode AddVariableNode(std::string name, AnyObject::Type type); // Creates a new empty variable of a given type to the scope (i.e. int a;). - std::shared_ptr evaluate(Scope &scope) override; - - std::string description() const; + AnyObject evaluate(Scope &scope) override; + AnyObject::Ref evaluateRef(Scope &scope) override; // Type checking for variable assignment. bool passesAssignmentTypeCheck(const AnyObject &assignObject) const; + AnyObject::Type variableType() const { return _variableType; } + protected: const AnyObject::Type _variableType; }; /** - * Construct a reference to an existing variable declared in the scope or a parent - * scope. This is similar to C++ and avoids unnecessary copies. + * Construct a reference to an existing variable declared in the scope or a parent scope. This is similar to C++ and + * avoids unnecessary copies. */ class AddReferenceVariableNode : public AddVariableNode { @@ -52,7 +53,9 @@ class AddReferenceVariableNode : public AddVariableNode * @return Pointer to the object in the scope now bound to the variable name and * the reference name. */ - std::shared_ptr evaluate(Scope &scope) override; + AnyObject::Ref evaluateRef(Scope &scope) override; + + AnyObject evaluate(Scope &scope); protected: const std::string referenceName; diff --git a/src/nodes/AnyNode.cpp b/src/nodes/AnyNode.cpp index 772e357..f425ea0 100644 --- a/src/nodes/AnyNode.cpp +++ b/src/nodes/AnyNode.cpp @@ -9,9 +9,15 @@ #include "AnyNode.hpp" -#include "AnyObject.hpp" -std::shared_ptr AnyNode::evaluate(Scope &scope) + +AnyObject AnyNode::evaluate(Scope &scope) { return _evaluateFunc(scope); +} + + +AnyObject &AnyPropertyNode::evaluateRef(Scope &scope) +{ + return _evaluateRefFunc(scope); } \ No newline at end of file diff --git a/src/nodes/AnyNode.hpp b/src/nodes/AnyNode.hpp index 1ff789d..a5fccff 100644 --- a/src/nodes/AnyNode.hpp +++ b/src/nodes/AnyNode.hpp @@ -8,11 +8,13 @@ */ #pragma once +#include "AnyObject.hpp" #include "BaseNode.hpp" -#include "PropertyInterface.hpp" #include "Scope.hpp" #include #include +#include + /* Generic node */ class AnyNode : public BaseNode @@ -20,32 +22,30 @@ class AnyNode : public BaseNode public: using Ptr = std::shared_ptr; - using EvaluateFunction = std::function(Scope &)>; + using EvaluateFunction = std::function; explicit AnyNode(NodeType type, EvaluateFunction &&evaluateFunc) : BaseNode(type), _evaluateFunc(std::move(evaluateFunc)) {} - std::shared_ptr evaluate(Scope &scope) final; + class AnyObject evaluate(Scope &scope) final; private: EvaluateFunction _evaluateFunc; }; -class AnyPropertyNode : public AnyNode, public PropertyInterface +class AnyPropertyNode : public AnyNode { public: using Ptr = std::shared_ptr; + using EvaluateRefFunction = std::function; - explicit AnyPropertyNode(NodeType type, EvaluateFunction &&evaluateFunc, EvaluateFunction &&evaluateNoCloneFunc) + explicit AnyPropertyNode(NodeType type, EvaluateFunction &&evaluateFunc, EvaluateRefFunction &&evaluateRefFunc) : AnyNode(type, std::move(evaluateFunc)), - _evaluateNoCloneFunc(std::move(evaluateNoCloneFunc)) {} + _evaluateRefFunc(std::move(evaluateRefFunc)) {} - std::shared_ptr evaluateNoClone(Scope &scope) final - { - return _evaluateNoCloneFunc(scope); - } + class AnyObject &evaluateRef(Scope &scope) final; private: - EvaluateFunction _evaluateNoCloneFunc; + EvaluateRefFunction _evaluateRefFunc; }; \ No newline at end of file diff --git a/src/nodes/BaseNode.cpp b/src/nodes/BaseNode.cpp new file mode 100644 index 0000000..9b230fd --- /dev/null +++ b/src/nodes/BaseNode.cpp @@ -0,0 +1,24 @@ +/** + * @file BaseNode.cpp + * @author Edward Palmer + * @date 2025-05-22 + * + * @copyright Copyright (c) 2025 + * + */ + +#include "BaseNode.hpp" +#include "AnyNode.hpp" +#include "Scope.hpp" + + +AnyObject BaseNode::evaluate(Scope &) +{ + ThrowException("Not implemented!"); +} + + +AnyObject::Ref BaseNode::evaluateRef(Scope &) +{ + ThrowException("Not implemented!"); +} diff --git a/src/nodes/BaseNode.hpp b/src/nodes/BaseNode.hpp index 32d151e..c1e4887 100644 --- a/src/nodes/BaseNode.hpp +++ b/src/nodes/BaseNode.hpp @@ -8,7 +8,10 @@ */ #pragma once +#include "Exceptions.hpp" +#include "Scope.hpp" #include +#include #include @@ -81,7 +84,11 @@ class BaseNode return type() == other.type(); } - virtual std::shared_ptr evaluate(class Scope &scope) = 0; // TODO: - can this be const-cast? + /* Returns a copy of an object (good for light-weight types) */ + virtual class AnyObject evaluate(Scope &); // TODO: - can this be const-cast? + + + virtual class AnyObject &evaluateRef(Scope &); void setType(NodeType type) { diff --git a/src/nodes/BinaryNode.cpp b/src/nodes/BinaryNode.cpp index 320081e..0965902 100644 --- a/src/nodes/BinaryNode.cpp +++ b/src/nodes/BinaryNode.cpp @@ -15,6 +15,7 @@ BinaryOperatorType BinaryNode::toBinaryOperator(const std::string &operatorString) { + // TODO: - make into static hashtable for speeding up if (operatorString == "+") return BinaryOperatorType::Add; else if (operatorString == "-") @@ -46,17 +47,17 @@ BinaryOperatorType BinaryNode::toBinaryOperator(const std::string &operatorStrin } -AnyObject::Ptr BinaryNode::evaluate(Scope &scope) +AnyObject BinaryNode::evaluate(Scope &scope) { auto leftEvaluated = _left->evaluate(scope); auto rightEvaluated = _right->evaluate(scope); // Persist result by storing in outer scope. - return applyOperator(*leftEvaluated, *rightEvaluated); + return applyOperator(leftEvaluated, rightEvaluated); } -AnyObject::Ptr BinaryNode::applyOperator(const AnyObject &left, const AnyObject &right) const +AnyObject BinaryNode::applyOperator(const AnyObject &left, const AnyObject &right) const { if (left.isType(AnyObject::Bool) && right.isType(AnyObject::Bool)) { @@ -94,9 +95,9 @@ AnyObject::Ptr BinaryNode::applyOperator(const AnyObject &left, const AnyObject } -AnyObject::Ptr BinaryNode::applyOperator(const AnyObject::Vector &left, const AnyObject::Vector &right) const +AnyObject BinaryNode::applyOperator(const AnyObject::Vector &left, const AnyObject::Vector &right) const { - switch (_binaryOperator) + switch (_binaryOperator) // TODO: - does this work? Test this code { case BinaryOperatorType::Add: { @@ -107,7 +108,7 @@ AnyObject::Ptr BinaryNode::applyOperator(const AnyObject::Vector &left, const An result.insert(result.end(), left.begin(), left.end()); result.insert(result.end(), right.begin(), right.end()); - return ObjectFactory::allocate(std::move(result)); + return AnyObject(std::move(result)); } default: ThrowException("cannot apply operator to types Array, Array"); @@ -115,100 +116,100 @@ AnyObject::Ptr BinaryNode::applyOperator(const AnyObject::Vector &left, const An } -AnyObject::Ptr BinaryNode::applyOperator(bool left, bool right) const +AnyObject BinaryNode::applyOperator(bool left, bool right) const { switch (_binaryOperator) { case BinaryOperatorType::Equal: - return ObjectFactory::allocate(left == right); + return AnyObject(left == right); case BinaryOperatorType::NotEqual: - return ObjectFactory::allocate(left != right); + return AnyObject(left != right); case BinaryOperatorType::And: - return ObjectFactory::allocate(left && right); + return AnyObject(left && right); case BinaryOperatorType::Or: - return ObjectFactory::allocate(left != right); + return AnyObject(left != right); default: ThrowException("cannot apply operator to types Bool, Bool"); } } -AnyObject::Ptr BinaryNode::applyOperator(long left, long right) const +AnyObject BinaryNode::applyOperator(long left, long right) const { switch (_binaryOperator) { case BinaryOperatorType::Add: - return ObjectFactory::allocate(left + right); + return AnyObject(left + right); case BinaryOperatorType::Minus: - return ObjectFactory::allocate(left - right); + return AnyObject(left - right); case BinaryOperatorType::Multiply: - return ObjectFactory::allocate(left * right); + return AnyObject(left * right); case BinaryOperatorType::Divide: - return ObjectFactory::allocate(left / right); + return AnyObject(left / right); case BinaryOperatorType::Equal: - return ObjectFactory::allocate(left == right); + return AnyObject(left == right); case BinaryOperatorType::NotEqual: - return ObjectFactory::allocate(left != right); + return AnyObject(left != right); case BinaryOperatorType::GreaterOrEqual: - return ObjectFactory::allocate(left >= right); + return AnyObject(left >= right); case BinaryOperatorType::Greater: - return ObjectFactory::allocate(left > right); + return AnyObject(left > right); case BinaryOperatorType::LessOrEqual: - return ObjectFactory::allocate(left <= right); + return AnyObject(left <= right); case BinaryOperatorType::Less: - return ObjectFactory::allocate(left < right); + return AnyObject(left < right); case BinaryOperatorType::Modulo: - return ObjectFactory::allocate(left % right); + return AnyObject(left % right); case BinaryOperatorType::And: - return ObjectFactory::allocate(left && right); + return AnyObject(left && right); case BinaryOperatorType::Or: - return ObjectFactory::allocate(left || right); + return AnyObject(left || right); default: ThrowException("cannot apply operator to types Int, Int"); } } -AnyObject::Ptr BinaryNode::applyOperator(double left, double right) const +AnyObject BinaryNode::applyOperator(double left, double right) const { switch (_binaryOperator) { case BinaryOperatorType::Add: - return ObjectFactory::allocate(left + right); + return AnyObject(left + right); case BinaryOperatorType::Minus: - return ObjectFactory::allocate(left - right); + return AnyObject(left - right); case BinaryOperatorType::Multiply: - return ObjectFactory::allocate(left * right); + return AnyObject(left * right); case BinaryOperatorType::Divide: - return ObjectFactory::allocate(left / right); + return AnyObject(left / right); case BinaryOperatorType::Equal: - return ObjectFactory::allocate(left == right); + return AnyObject(left == right); case BinaryOperatorType::NotEqual: - return ObjectFactory::allocate(left != right); + return AnyObject(left != right); case BinaryOperatorType::GreaterOrEqual: - return ObjectFactory::allocate(left >= right); + return AnyObject(left >= right); case BinaryOperatorType::Greater: - return ObjectFactory::allocate(left > right); + return AnyObject(left > right); case BinaryOperatorType::LessOrEqual: - return ObjectFactory::allocate(left <= right); + return AnyObject(left <= right); case BinaryOperatorType::Less: - return ObjectFactory::allocate(left < right); + return AnyObject(left < right); default: ThrowException("cannot apply operator to types Float, Float"); } } -AnyObject::Ptr BinaryNode::applyOperator(const std::string &left, const std::string &right) const +AnyObject BinaryNode::applyOperator(const std::string &left, const std::string &right) const { switch (_binaryOperator) { case BinaryOperatorType::Add: - return ObjectFactory::allocate(left + right); + return AnyObject(left + right); case BinaryOperatorType::Equal: - return ObjectFactory::allocate(left == right); + return AnyObject(left == right); case BinaryOperatorType::NotEqual: - return ObjectFactory::allocate(left != right); + return AnyObject(left != right); default: ThrowException("cannot apply operator to types String, String"); } diff --git a/src/nodes/BinaryNode.hpp b/src/nodes/BinaryNode.hpp index abfc058..c67f924 100644 --- a/src/nodes/BinaryNode.hpp +++ b/src/nodes/BinaryNode.hpp @@ -43,16 +43,16 @@ class BinaryNode : public BaseNode setType(NodeType::Binary); } - AnyObject::Ptr evaluate(Scope &scope) override; + AnyObject evaluate(Scope &scope) override; protected: - AnyObject::Ptr applyOperator(const AnyObject &left, const AnyObject &right) const; + AnyObject applyOperator(const AnyObject &left, const AnyObject &right) const; - AnyObject::Ptr applyOperator(bool left, bool right) const; - AnyObject::Ptr applyOperator(long left, long right) const; - AnyObject::Ptr applyOperator(double left, double right) const; - AnyObject::Ptr applyOperator(const std::string &left, const std::string &right) const; - AnyObject::Ptr applyOperator(const AnyObject::Vector &left, const AnyObject::Vector &right) const; + AnyObject applyOperator(bool left, bool right) const; + AnyObject applyOperator(long left, long right) const; + AnyObject applyOperator(double left, double right) const; + AnyObject applyOperator(const std::string &left, const std::string &right) const; + AnyObject applyOperator(const AnyObject::Vector &left, const AnyObject::Vector &right) const; /* Convert string to enum (faster if doing lost of comparisons) */ static BinaryOperatorType toBinaryOperator(const std::string &operatorString); diff --git a/src/nodes/ClassDefinitionNode.cpp b/src/nodes/ClassDefinitionNode.cpp index e70b26d..c89d0d6 100644 --- a/src/nodes/ClassDefinitionNode.cpp +++ b/src/nodes/ClassDefinitionNode.cpp @@ -23,8 +23,10 @@ ClassDefinitionNode::ClassDefinitionNode(std::string typeName_, } -AnyObject::Ptr ClassDefinitionNode::evaluate(Scope &scope) +AnyObject ClassDefinitionNode::evaluate(Scope &scope) { + log().debug("evaluating classdefinition node"); + // NB: override method defined in StructDefinitionNode. if (active) { @@ -39,15 +41,14 @@ AnyObject::Ptr ClassDefinitionNode::evaluate(Scope &scope) buildMethodDefsHashMap(scope); /* NB: wrap-up in an object shared pointer */ - auto objectWrapper = ObjectFactory::allocate(shared_from_this(), AnyObject::_ClassDefinition); - - scope.linkObject(typeName, objectWrapper); - return objectWrapper; + return scope.link(typeName, AnyObject(shared_from_this(), AnyObject::_ClassDefinition)); } void ClassDefinitionNode::installMethodsInScope(Scope &scope) const { + log().debug("Installing class method definitions in scope..."); + if (!active) { ThrowException("The class definition is inactive!"); @@ -55,6 +56,7 @@ void ClassDefinitionNode::installMethodsInScope(Scope &scope) const for (auto &[name, funcNode] : allMethodDefsMap) { + log().debug("installing " + funcNode->_funcName); (void)funcNode->evaluate(scope); } } @@ -81,6 +83,7 @@ void ClassDefinitionNode::buildMethodDefsHashMap(const Scope &scope) // the same name even with different numbers and types of arguments. for (FunctionNode::Ptr funcDef : methodDefs) { + log().debug("Installing new method definition: " + funcDef->_funcName); allMethodDefsMap[funcDef->_funcName] = funcDef; } } \ No newline at end of file diff --git a/src/nodes/ClassDefinitionNode.hpp b/src/nodes/ClassDefinitionNode.hpp index 6197295..ea054ec 100644 --- a/src/nodes/ClassDefinitionNode.hpp +++ b/src/nodes/ClassDefinitionNode.hpp @@ -43,7 +43,7 @@ class ClassDefinitionNode : public StructDefinitionNode /** * Install object in current scope. */ - std::shared_ptr evaluate(Scope &scope) override; + class AnyObject evaluate(Scope &scope) override; /** * Calls evaluate method on all method nodes. Installs them in argument scope. diff --git a/src/nodes/ClassNode.cpp b/src/nodes/ClassNode.cpp index f06fc3c..25c25f9 100644 --- a/src/nodes/ClassNode.cpp +++ b/src/nodes/ClassNode.cpp @@ -19,7 +19,7 @@ ClassNode::ClassNode(std::string typeName_, std::string name_) { } -AnyObject::Ptr ClassNode::evaluate(Scope &scope) +AnyObject ClassNode::evaluate(Scope &scope) { // TODO: - inefficient, should have another method we can call to do most of StructNode::evaluate. if (active) @@ -30,9 +30,9 @@ AnyObject::Ptr ClassNode::evaluate(Scope &scope) active = true; // Initialize our instance from the struct definition defined in the scope. - auto theObject = scope.getNamedObject(typeName); + auto &theObject = scope.getObjectRef(typeName); - structDefinition = std::static_pointer_cast(theObject->getValue()); + structDefinition = std::static_pointer_cast(theObject.getValue()); structDefinition->installVariablesInScope(_instanceScope, variableNames); auto classDefinition = std::static_pointer_cast(structDefinition); @@ -40,8 +40,5 @@ AnyObject::Ptr ClassNode::evaluate(Scope &scope) // Add the active struct instance to the scope. TODO: - transfer ownership // to the scope. Will have to remove this class from AST to do this correctly. - auto wrappedClass = ObjectFactory::allocate(shared_from_this(), AnyObject::Class); - - scope.linkObject(name, wrappedClass); - return wrappedClass; + return scope.link(name, AnyObject(shared_from_this(), AnyObject::Class)); } \ No newline at end of file diff --git a/src/nodes/ClassNode.hpp b/src/nodes/ClassNode.hpp index 523e1c7..6a69f3a 100644 --- a/src/nodes/ClassNode.hpp +++ b/src/nodes/ClassNode.hpp @@ -32,5 +32,5 @@ class ClassNode : public StructNode * @param scope The scope in which to add the instance. * @return BaseObject* Pointer to itself */ - std::shared_ptr evaluate(Scope &scope) override; + class AnyObject evaluate(Scope &scope) override; }; \ No newline at end of file diff --git a/src/nodes/FunctionCallNode.cpp b/src/nodes/FunctionCallNode.cpp index 3f5a641..dfdd2f8 100644 --- a/src/nodes/FunctionCallNode.cpp +++ b/src/nodes/FunctionCallNode.cpp @@ -16,18 +16,19 @@ #include "ModuleFunctor.hpp" #include "Scope.hpp" -AnyObject::Ptr FunctionCallNode::evaluate(Scope &scope) +AnyObject FunctionCallNode::evaluate(Scope &scope) { + AnyObject::Ref funcObject = scope.getObjectRef(_funcName); + // 0. Any library functions that we wish to evaluate. - auto moduleFunction = scope.getOptionalNamedObject(_funcName); - if (moduleFunction && moduleFunction->isType(AnyObject::_ModuleFunction)) + if (funcObject.isType(AnyObject::_ModuleFunction)) { - return moduleFunction->getValue()(_funcArgs, scope); + return funcObject.getValue()(_funcArgs, scope); } // TODO: - finish implementing here. Should not be a shared pointer. // 1. Get a pointer to the function node stored in this scope. - auto funcNode = std::static_pointer_cast(scope.getNamedObject(_funcName)->getValue()); + auto funcNode = std::static_pointer_cast(funcObject.getValue()); // TODO: - insanely ugly // 2. Verify that the number of arguments matches those required for the // function we are calling. @@ -54,25 +55,25 @@ AnyObject::Ptr FunctionCallNode::evaluate(Scope &scope) for (const auto &argNode : _funcArgs) { // Evaluate all function arguments in external scope (outside function). - auto evaluatedArg = argNode->evaluate(scope); + AnyObject evaluatedArg = argNode->evaluate(scope); // Check that the evaluatedArg type (RHS) is compatible with the corresponding // (LHS) variable. auto &argVariable = funcNode->_funcArgs[iarg++]->castNode(); - if (!argVariable.passesAssignmentTypeCheck(*evaluatedArg)) + if (!argVariable.passesAssignmentTypeCheck(evaluatedArg)) { char buffer[150]; snprintf(buffer, 150, "incorrect type for argument '%s' of function '%s'. Expected type '%s'.", argVariable.name().c_str(), _funcName.c_str(), - argVariable.description().c_str()); + AnyObject::typeToString(argVariable.variableType()).c_str()); ThrowException(buffer); } // Define variable in the function's scope. - funcScope.linkObject(argVariable.name(), evaluatedArg); + funcScope.link(argVariable.name(), std::move(evaluatedArg)); } // Evaluate the function body in our function scope now that we've added the @@ -81,10 +82,10 @@ AnyObject::Ptr FunctionCallNode::evaluate(Scope &scope) } -AnyObject::Ptr FunctionCallNode::evaluateFunctionBody(BaseNode &funcBody, Scope &funcScope) +AnyObject FunctionCallNode::evaluateFunctionBody(BaseNode &funcBody, Scope &funcScope) { // Reset return value. - gEnvironmentContext.returnValue = nullptr; + gEnvironmentContext.returnValue = AnyObject(); jmp_buf *original = gEnvironmentContext.returnJumpPoint; diff --git a/src/nodes/FunctionCallNode.hpp b/src/nodes/FunctionCallNode.hpp index 4cb9563..9eafbf8 100644 --- a/src/nodes/FunctionCallNode.hpp +++ b/src/nodes/FunctionCallNode.hpp @@ -10,7 +10,6 @@ #pragma once #include "BaseNode.hpp" #include -#include #include #include @@ -31,10 +30,10 @@ class FunctionCallNode : public BaseNode // TODO: - don't forget to do performance profiling for Fib sequence and see memory requirements for old and new version // TODO: - create a new PR after this for parser to store all nodes in AST in flat array using pointers with method to delete by walking along array. - std::shared_ptr evaluate(class Scope &scope) override; + class AnyObject evaluate(class Scope &scope) override; - std::shared_ptr evaluateFunctionBody(BaseNode &funcBody, class Scope &funcScope); + class AnyObject evaluateFunctionBody(BaseNode &funcBody, class Scope &funcScope); - std::string _funcName; + std::string _funcName; /* TODO: - hide */ BaseNodePtrVector _funcArgs{nullptr}; }; diff --git a/src/nodes/FunctionNode.cpp b/src/nodes/FunctionNode.cpp index b12052f..f3b4c53 100644 --- a/src/nodes/FunctionNode.cpp +++ b/src/nodes/FunctionNode.cpp @@ -9,15 +9,19 @@ #include "FunctionNode.hpp" #include "AnyObject.hpp" -#include "ObjectFactory.hpp" +#include "Logger.hpp" #include "Scope.hpp" + /// Create a new FunctionObject from a FunctionNode and register in current scope. -AnyObject::Ptr FunctionNode::evaluate(Scope &scope) +AnyObject FunctionNode::evaluate(Scope &scope) { + log().info("evaluating reference in FunctionNode: " + _funcName); + // TODO: - I think this creates a strong-reference cycle! Need to break the chain here - auto functionObject = ObjectFactory::allocate(shared_from_this(), AnyObject::_UserFunction); + auto functionObject = AnyObject(shared_from_this(), AnyObject::_UserFunction); + + log().debug("Linking function node with name " + _funcName); - scope.linkObject(_funcName, functionObject); - return functionObject; -} \ No newline at end of file + return scope.link(_funcName, std::move(functionObject)); // TODO: - this should be called in evaluateRef() +} diff --git a/src/nodes/FunctionNode.hpp b/src/nodes/FunctionNode.hpp index 34d3ed0..0ba2347 100644 --- a/src/nodes/FunctionNode.hpp +++ b/src/nodes/FunctionNode.hpp @@ -10,7 +10,6 @@ #pragma once #include "BaseNode.hpp" #include "FunctionCallNode.hpp" -#include #include @@ -28,7 +27,7 @@ class FunctionNode : public FunctionCallNode, public std::enable_shared_from_thi ~FunctionNode() override = default; - std::shared_ptr evaluate(class Scope &scope) override; + class AnyObject evaluate(class Scope &scope) override; BaseNode::Ptr funcBody{nullptr}; }; diff --git a/src/nodes/LookupVariableNode.cpp b/src/nodes/LookupVariableNode.cpp index ce44c2a..807d1cd 100644 --- a/src/nodes/LookupVariableNode.cpp +++ b/src/nodes/LookupVariableNode.cpp @@ -10,7 +10,13 @@ #include "LookupVariableNode.hpp" #include "AnyObject.hpp" -AnyObject::Ptr LookupVariableNode::evaluate(Scope &scope) +AnyObject::Ref LookupVariableNode::evaluateRef(Scope &scope) { - return scope.getNamedObject(_name); + return scope.getObjectRef(_name); +} + + +AnyObject LookupVariableNode::evaluate(Scope &scope) +{ + return evaluateRef(scope); /* Copy object. Avoid if possible */ } \ No newline at end of file diff --git a/src/nodes/LookupVariableNode.hpp b/src/nodes/LookupVariableNode.hpp index f73854e..b6a9cc1 100644 --- a/src/nodes/LookupVariableNode.hpp +++ b/src/nodes/LookupVariableNode.hpp @@ -23,7 +23,8 @@ class LookupVariableNode : public BaseNode [[nodiscard]] inline const std::string &name() const; /* Returns the object in the scope associated with a variable name */ - std::shared_ptr evaluate(Scope &scope) override; + class AnyObject &evaluateRef(Scope &scope) override; + class AnyObject evaluate(Scope &scope) override; private: std::string _name; diff --git a/src/nodes/ModuleNodeFactory.cpp b/src/nodes/ModuleNodeFactory.cpp index b20c829..c3a98e3 100644 --- a/src/nodes/ModuleNodeFactory.cpp +++ b/src/nodes/ModuleNodeFactory.cpp @@ -6,6 +6,7 @@ // #include "ModuleNodeFactory.hpp" +#include "AnyObject.hpp" #include "BaseNode.hpp" #include "Exceptions.hpp" #include "Logger.hpp" @@ -41,9 +42,9 @@ AnyNode::Ptr createIOModuleNode() { for (const auto &callArg : callArgs) { - auto evaluatedNode = callArg->evaluate(scope); + auto evaluatedNode = callArg->evaluate(scope); /* TODO: - its not the most efficient if we have to copy a reference */ - std::cout << *evaluatedNode; + std::cout << evaluatedNode; if (callArg != callArgs.back()) { @@ -52,7 +53,7 @@ AnyNode::Ptr createIOModuleNode() } std::cout << std::endl; - return nullptr; + return AnyObject(); }); return NodeFactory::createModuleNode("io", {doPrint}); @@ -65,8 +66,8 @@ AnyNode::Ptr createMathModuleNode() { assert(callArgs.size() == 1); - double first = callArgs.front()->evaluate(scope)->getValue(); - return ObjectFactory::allocate(sqrt(first)); + double first = callArgs.front()->evaluate(scope).getValue(); + return AnyObject(sqrt(first)); }); auto doPow = std::pair("pow", [](BaseNodePtrVector &callArgs, Scope &scope) @@ -77,7 +78,7 @@ AnyNode::Ptr createMathModuleNode() auto firstObject = callArgs.front()->evaluate(scope); auto secondObject = callArgs.back()->evaluate(scope); - return ObjectFactory::allocate(pow(firstObject->getValue(), secondObject->getValue())); + return AnyObject(pow(firstObject.getValue(), secondObject.getValue())); }); return NodeFactory::createModuleNode("math", {doSqrt, doPow}); @@ -90,35 +91,35 @@ AnyNode::Ptr createArrayModuleNode() { assert(callArgs.size() == 1); - auto theObject = callArgs.front()->evaluate(scope); /* Careful if using a reference! Need to not hold reference to garbage! */ + AnyObject::Ref theObject = callArgs.front()->evaluateRef(scope); /* Careful if using a reference! Need to not hold reference to garbage! */ - auto &arrayObject = theObject->getValue(); + auto &arrayObject = theObject.getValue(); arrayObject.clear(); - return nullptr; + return AnyObject(); }); auto doLength = std::pair("length", [](BaseNodePtrVector &callArgs, Scope &scope) { assert(callArgs.size() == 1); - auto theObject = callArgs.front()->evaluate(scope); + AnyObject::Ref theObject = callArgs.front()->evaluateRef(scope); - auto &arrayObject = theObject->getValue(); + auto &arrayObject = theObject.getValue(); - return ObjectFactory::allocate((double)arrayObject.size()); + return AnyObject((double)arrayObject.size()); }); auto doAppend = std::pair("append", [](BaseNodePtrVector &callArgs, Scope &scope) { assert(callArgs.size() == 2); - auto theObject = callArgs.front()->evaluate(scope); + AnyObject::Ref theObject = callArgs.front()->evaluateRef(scope); - auto &arrayObject = theObject->getValue(); + auto &arrayObject = theObject.getValue(); auto someObject = callArgs.back()->evaluate(scope); - arrayObject.push_back(someObject->clone()); // NB: must clone! - return nullptr; + arrayObject.push_back(someObject.clone()); // NB: must clone! + return AnyObject(); }); return NodeFactory::createModuleNode("array", {doClear, doLength, doAppend}); @@ -137,15 +138,15 @@ AnyNode::Ptr createTestModuleNode() assert(callArgs.size() == 2); - bool result = callArgs.front()->evaluate(scope)->getValue(); - std::string description = callArgs.back()->evaluate(scope)->getValue(); /* Be very careful -> copy otherwise referencing garbage! */ + bool result = callArgs.front()->evaluate(scope).getValue(); + std::string description = callArgs.back()->evaluate(scope).getValue(); /* Be very careful -> copy otherwise referencing garbage! */ // Print pass or fail depending on the test case. const char *statusString = result ? "PASSED" : "FAILED"; const char *statusColor = result ? PassColor : FailColor; std::cout << stringify("%-50s %s%s%s", description.c_str(), statusColor, statusString, ClearColor) << std::endl; - return nullptr; + return AnyObject(); }); return NodeFactory::createModuleNode("test", {doTest}); diff --git a/src/nodes/NodeFactory.cpp b/src/nodes/NodeFactory.cpp index 8ce289a..10e6337 100644 --- a/src/nodes/NodeFactory.cpp +++ b/src/nodes/NodeFactory.cpp @@ -9,6 +9,7 @@ #include "NodeFactory.hpp" #include "AddVariableNode.hpp" +#include "AnyObject.hpp" #include "ClassNode.hpp" #include "FunctionCallNode.hpp" #include "JumpPoints.hpp" @@ -20,6 +21,7 @@ #include #include + namespace NodeFactory { @@ -38,25 +40,25 @@ AnyNode::Ptr createCastNode(BaseNode::Ptr expression, AnyObject::Type castToType return std::make_shared(NodeType::Cast, [isCastable, expression, castToType](Scope &scope) { auto evaluatedObject = expression->evaluate(scope); - if (!isCastable(evaluatedObject->getType())) + if (!isCastable(evaluatedObject.getType())) { ThrowException("Unsupported cast type!"); } - if (evaluatedObject->getType() == castToType) /* Nothing to do */ + if (evaluatedObject.getType() == castToType) /* Nothing to do */ { return evaluatedObject; } if (castToType == AnyObject::Int) { - long value = (long)expression->evaluate(scope)->getValue(); - return ObjectFactory::allocate(value); + long value = (long)expression->evaluate(scope).getValue(); + return AnyObject(value); } else { - double value = (double)expression->evaluate(scope)->getValue(); - return ObjectFactory::allocate(value); + double value = (double)expression->evaluate(scope).getValue(); + return AnyObject(value); } }); } @@ -65,7 +67,7 @@ AnyNode::Ptr createBoolNode(bool state) { return std::make_shared(NodeType::Bool, [state](Scope &) { - return ObjectFactory::allocate(state); + return AnyObject(state); }); } @@ -73,7 +75,7 @@ AnyNode::Ptr createIntNode(long value) { return std::make_shared(NodeType::Int, [value](Scope &) { - return ObjectFactory::allocate(value); + return AnyObject(value); }); } @@ -81,7 +83,7 @@ AnyNode::Ptr createStringNode(std::string value) { return std::make_shared(NodeType::String, [value = std::move(value)](Scope &) { - return ObjectFactory::allocate(value); + return AnyObject(value); }); } @@ -89,7 +91,7 @@ AnyNode::Ptr createFloatNode(double value) { return std::make_shared(NodeType::Float, [value](Scope &) { - return ObjectFactory::allocate(value); + return AnyObject(value); }); } @@ -97,12 +99,12 @@ AnyNode::Ptr createIfNode(BaseNode::Ptr condition, BaseNode::Ptr thenBranch, Bas { return std::make_shared(NodeType::If, [condition, thenBranch, elseBranch](Scope &scope) /* Use shared pointer to manage ownership */ { - if (condition->evaluate(scope)->getValue()) + if (condition->evaluate(scope).getValue()) return thenBranch->evaluate(scope); else if (elseBranch) return elseBranch->evaluate(scope); else - return AnyObject::Ptr(); + return AnyObject(); }); } @@ -122,15 +124,15 @@ AnyNode::Ptr createForLoopNode(BaseNode::Ptr init, BaseNode::Ptr condition, Base if (setjmp(local) != 1) { for (; - condition->evaluate(loopScope)->getValue(); - update->evaluate(loopScope)) + condition->evaluate(loopScope).getValue(); + (void)update->evaluate(loopScope)) { (void)body->evaluate(loopScope); } } popBreakJumpPoint(); - return nullptr; + return AnyObject(); }); } @@ -147,7 +149,7 @@ AnyNode::Ptr createWhileLoopNode(BaseNode::Ptr condition, BaseNode::Ptr body) { Scope loopScope(scope); // Extend scope. - while (condition->evaluate(scope)->getValue()) + while (condition->evaluate(scope).getValue()) { (void)body->evaluate(loopScope); } @@ -156,7 +158,7 @@ AnyNode::Ptr createWhileLoopNode(BaseNode::Ptr condition, BaseNode::Ptr body) // Restore original context. popBreakJumpPoint(); - return nullptr; + return AnyObject(); }); } @@ -175,13 +177,13 @@ AnyNode::Ptr createDoWhileLoopNode(BaseNode::Ptr condition, BaseNode::Ptr body) do { (void)body->evaluate(loopScope); - } while (condition->evaluate(scope)->getValue()); /* NB: evaluate in outerscope (no access to loop scope) */ + } while (condition->evaluate(scope).getValue()); /* NB: evaluate in outerscope (no access to loop scope) */ } // Restore original context. popBreakJumpPoint(); - return nullptr; // Return nothing. + return AnyObject(); // Return nothing. }); } @@ -193,7 +195,7 @@ AnyNode::Ptr createBreakNode() (void)scope; jumpToBreakJumpPoint(); /* Jump to last set point */ - return nullptr; + return AnyObject(); }); } @@ -201,7 +203,7 @@ AnyNode::Ptr createReturnNode(BaseNode::Ptr returnNode) { return std::make_shared(NodeType::Return, [returnNode](Scope &scope) { - gEnvironmentContext.returnValue = nullptr; + gEnvironmentContext.returnValue = AnyObject(); if (returnNode != nullptr) // i.e. return true; { @@ -209,7 +211,7 @@ AnyNode::Ptr createReturnNode(BaseNode::Ptr returnNode) } longjmp(*gEnvironmentContext.returnJumpPoint, 1); - return nullptr; + return AnyObject(); }); } @@ -218,9 +220,9 @@ AnyNode::Ptr createNotNode(BaseNode::Ptr expression) { return std::make_shared(NodeType::Not, [expression](Scope &scope) { - AnyObject::Ptr result = expression->evaluate(scope); + AnyObject result = expression->evaluate(scope); - return ObjectFactory::allocate(!result->getValue()); + return AnyObject(!result.getValue()); }); } @@ -240,7 +242,7 @@ AnyNode::Ptr createBlockNode(BaseNodePtrVector nodes) } /* Any memory allocations cleared-up when we exit */ - return nullptr; + return AnyObject(); }); } @@ -253,22 +255,16 @@ AnyNode::Ptr createAssignNode(BaseNode::Ptr left, BaseNode::Ptr right) { auto &accessor = left->castNode(); - *(accessor.evaluateNoClone(scope)) = *(right->evaluate(scope)); - return nullptr; + accessor.evaluateRef(scope) = right->evaluate(scope); + return AnyObject(); } assert(left->isNodeType(NodeType::AddVariable) || left->isNodeType(NodeType::LookupVariable)); // Case 1: AddVariableNode -> we create default init object, add to scope and return. - // Case 2: LookupVariableNode -> we object defined in scope (not cloned!) - TODO: - think about whether we should clone it. - AnyObject::Ptr objectLHS = left->evaluate(scope); - - // Object we want to assign to LHS. - AnyObject::Ptr objectRHS = right->evaluate(scope); - - // Update directly. TODO: - We will need to implement this for some object types still. - *objectLHS = *objectRHS; - return nullptr; + // Case 2: LookupVariableNode -> object defined in scope (not cloned!) - TODO: - think about whether we should clone it. + left->evaluateRef(scope) = right->evaluate(scope); + return AnyObject(); }); } @@ -287,7 +283,7 @@ AnyNode::Ptr createArrayNode(BaseNodePtrVector nodes) evaluatedObjects.push_back(node->evaluate(scope)); } - return ObjectFactory::allocate(std::move(evaluatedObjects)); + return AnyObject(std::move(evaluatedObjects)); }); } @@ -301,7 +297,7 @@ AnyNode::Ptr createFileNode(BaseNodePtrVector nodes) (void)node->evaluate(scope); } - return nullptr; + return AnyObject(); }); } @@ -314,19 +310,19 @@ AnyNode::Ptr createPrefixIncrementNode(BaseNode::Ptr expression) assert(expression->isNodeType(NodeType::LookupVariable)); // 2. Object associated with variable name in scope must be integer or float. - auto bodyEvaluated = expression->evaluate(scope); - if (bodyEvaluated->isType(AnyObject::Int)) + AnyObject::Ref bodyEvaluated = expression->evaluateRef(scope); + if (bodyEvaluated.isType(AnyObject::Int)) { - ++(bodyEvaluated->getValue()); + ++(bodyEvaluated.getValue()); return bodyEvaluated; } - else if (bodyEvaluated->isType(AnyObject::Float)) + else if (bodyEvaluated.isType(AnyObject::Float)) { - ++(bodyEvaluated->getValue()); + ++(bodyEvaluated.getValue()); return bodyEvaluated; } - ThrowException("cannot use prefix operator on object of type"); + ThrowException("cannot use prefix operator on object of type [" + bodyEvaluated.typeToString() + "]"); }); } @@ -339,19 +335,19 @@ AnyNode::Ptr createPrefixDecrementNode(BaseNode::Ptr expression) assert(expression->isNodeType(NodeType::LookupVariable)); // 2. Object associated with variable name in scope must be integer or float. - auto bodyEvaluated = expression->evaluate(scope); - if (bodyEvaluated->isType(AnyObject::Int)) + AnyObject::Ref bodyEvaluated = expression->evaluateRef(scope); + if (bodyEvaluated.isType(AnyObject::Int)) { - --(bodyEvaluated->getValue()); + --(bodyEvaluated.getValue()); return bodyEvaluated; } - else if (bodyEvaluated->isType(AnyObject::Float)) + else if (bodyEvaluated.isType(AnyObject::Float)) { - --(bodyEvaluated->getValue()); + --(bodyEvaluated.getValue()); return bodyEvaluated; } - ThrowException("cannot use prefix operator on object of type."); + ThrowException("cannot use prefix operator on object of type [" + bodyEvaluated.typeToString() + "]"); }); } @@ -360,18 +356,14 @@ AnyNode::Ptr createNegationNode(BaseNode::Ptr expression) { return std::make_shared(NodeType::Negation, [expression](Scope &scope) { - auto bodyEvaluated = expression->evaluate(scope); + AnyObject bodyEvaluated = expression->evaluate(scope); - AnyObject::Ptr result{nullptr}; - - if (bodyEvaluated->isType(AnyObject::Int)) - result = ObjectFactory::allocate(-bodyEvaluated->getValue()); - else if (bodyEvaluated->isType(AnyObject::Float)) - result = ObjectFactory::allocate(-bodyEvaluated->getValue()); + if (bodyEvaluated.isType(AnyObject::Int)) + return AnyObject(-bodyEvaluated.getValue()); + else if (bodyEvaluated.isType(AnyObject::Float)) + return AnyObject(-bodyEvaluated.getValue()); else - ThrowException("invalid object type"); - - return result; + ThrowException("invalid object type [" + bodyEvaluated.typeToString() + "]"); }); } @@ -380,17 +372,18 @@ AnyPropertyNode::Ptr createStructAccessNode(std::string structVarName, std::stri { auto evaluateNoClone = [structVarName, memberVarName](Scope &scope) { - auto theObject = scope.getNamedObject(structVarName); + auto &theObject = scope.getObjectRef(structVarName); - auto theStructObject = std::static_pointer_cast(theObject->getValue()); + // TODO: - reimplement this + auto theStructObject = std::static_pointer_cast(theObject.getValue()); - return theStructObject->instanceScope().getNamedObject(memberVarName); + std::cout << "getting object reference from scope " << memberVarName << std::endl; + return std::ref(theStructObject->instanceScope().getObjectRef(memberVarName)); }; auto evaluate = [evaluateNoClone](Scope &scope) { - auto currentObject = evaluateNoClone(scope); - return currentObject->clone(); + return evaluateNoClone(scope); }; @@ -402,24 +395,23 @@ AnyPropertyNode::Ptr createArrayAccessNode(BaseNode::Ptr arrayLookupNode, BaseNo { assert(arrayLookupNode->isNodeType(NodeType::LookupVariable)); - auto evaluateNoClone = [arrayLookupNode, arrayIndexNode](Scope &scope) + auto evaluateNoClone = [arrayLookupNode, arrayIndexNode](Scope &scope) /* TODO: - can remove arrayLookupNode and just use name to find it in scope*/ { // Lookup in array. - auto theArrayObject = arrayLookupNode->evaluate(scope); /* Careful with references! */ + AnyObject::Ref theArrayObject = arrayLookupNode->evaluateRef(scope); /* Careful with references! */ - auto &arrayObj = theArrayObject->getValue(); - auto index = arrayIndexNode->evaluate(scope)->getValue(); + auto &arrayObj = theArrayObject.getValue(); + auto index = arrayIndexNode->evaluate(scope).getValue(); if (index < 0 || index >= (long)arrayObj.size()) ThrowException("Array index [" + std::to_string(index) + "] is out of bounds!"); - return arrayObj[index]; + return std::ref(arrayObj.at(index)); /* Returns a reference */ }; auto evaluate = [evaluateNoClone](Scope &scope) { - AnyObject::Ptr currentObject = evaluateNoClone(scope); - return currentObject->clone(); + return evaluateNoClone(scope); /* Returns a copy */ }; return std::make_shared(NodeType::ArrayAccess, std::move(evaluate), std::move(evaluateNoClone)); @@ -432,11 +424,10 @@ AnyNode::Ptr createModuleNode(std::string moduleName, ModuleFunctor::Definitions { for (auto &it : moduleFunctions) /* Add to scope */ { - auto object = ObjectFactory::allocate(ModuleFunctor(it.second)); - scope.linkObject(it.first, object); + (void)scope.link(it.first, AnyObject(ModuleFunctor(it.second))); } - return nullptr; + return AnyObject(); }); } @@ -445,9 +436,9 @@ AnyNode::Ptr createClassMethodCallNode(std::string instanceName, FunctionCallNod return std::make_shared(NodeType::ClassMethodCall, [instanceName = std::move(instanceName), methodCallNode](Scope &scope) { - auto anyObject = scope.getNamedObject(instanceName); + AnyObject::Ref anyObject = scope.getObjectRef(instanceName); - auto thisObject = std::static_pointer_cast(anyObject->getValue()); + auto thisObject = std::static_pointer_cast(anyObject.getValue()); // Important: to correctly evaluate the method, we need to add a parent scope // for the class instance temporarily each time we evaluate so function has @@ -455,7 +446,7 @@ AnyNode::Ptr createClassMethodCallNode(std::string instanceName, FunctionCallNod thisObject->instanceScope().setParentScope(&scope); /* But evaluate in class' instance scope */ - AnyObject::Ptr result = methodCallNode->evaluate(thisObject->instanceScope()); + AnyObject result = methodCallNode->evaluate(thisObject->instanceScope()); // Set back to avoid problems if we forget to reset it in future. thisObject->instanceScope().setParentScope(nullptr); diff --git a/src/nodes/NodeFactory.hpp b/src/nodes/NodeFactory.hpp index 47c7fce..677248d 100644 --- a/src/nodes/NodeFactory.hpp +++ b/src/nodes/NodeFactory.hpp @@ -9,7 +9,6 @@ #pragma once #include "AnyNode.hpp" -#include "AnyObject.hpp" #include "BaseNode.hpp" #include "FunctionCallNode.hpp" #include "ModuleFunctor.hpp" @@ -20,7 +19,7 @@ namespace NodeFactory { -AnyNode::Ptr createCastNode(BaseNode::Ptr expression, AnyObject::Type castToType); +AnyNode::Ptr createCastNode(BaseNode::Ptr expression, typename AnyObject::Type castToType); AnyNode::Ptr createBoolNode(bool state); diff --git a/src/nodes/PropertyInterface.hpp b/src/nodes/PropertyInterface.hpp deleted file mode 100644 index 14eb9f5..0000000 --- a/src/nodes/PropertyInterface.hpp +++ /dev/null @@ -1,32 +0,0 @@ -/** - * @file PropertyInterface.hpp - * @author Edward Palmer - * @date 2025-05-12 - * - * @copyright Copyright (c) 2025 - * - */ - -#pragma once -#include "Scope.hpp" - -/** - * - * 1. RHS of expression: return copy of object (getter). - * LHS = item.get(0); - * In this case, we return a copy of the object (we don't want a reference). - * - * 2. LHS of expression: set value with RHS (setter). - * item.set(0, RHS) - * - */ -struct PropertyInterface -{ - /* Return copy of object (getter) */ - // virtual BaseObject *evaluate(Scope &scope) = 0; - - /* Return object directly for modifying value (setter) */ - virtual std::shared_ptr evaluateNoClone(Scope &scope) = 0; - - virtual ~PropertyInterface() = default; -}; \ No newline at end of file diff --git a/src/nodes/StructDefinitionNode.cpp b/src/nodes/StructDefinitionNode.cpp index bd2dd73..51bc8ee 100644 --- a/src/nodes/StructDefinitionNode.cpp +++ b/src/nodes/StructDefinitionNode.cpp @@ -31,13 +31,13 @@ std::shared_ptr StructDefinitionNode::lookupParent(const S return nullptr; } - auto theObject = scope.getNamedObject(parentTypeName); + AnyObject::Ref theObject = scope.getObjectRef(parentTypeName); - return std::static_pointer_cast(theObject->getValue()); + return std::static_pointer_cast(theObject.getValue()); } -AnyObject::Ptr StructDefinitionNode::evaluate(Scope &scope) +AnyObject StructDefinitionNode::evaluate(Scope &scope) { if (active) // Expect one definition only! { @@ -54,10 +54,9 @@ AnyObject::Ptr StructDefinitionNode::evaluate(Scope &scope) buildVariableDefHashMap(scope); /* NB: need to wrap-up in an object shared pointer */ - auto objectWrapper = ObjectFactory::allocate(shared_from_this(), AnyObject::_StructDefinition); + auto objectWrapper = AnyObject(shared_from_this(), AnyObject::_StructDefinition); - scope.linkObject(typeName, objectWrapper); - return objectWrapper; + return scope.link(typeName, std::move(objectWrapper)); } diff --git a/src/nodes/StructDefinitionNode.hpp b/src/nodes/StructDefinitionNode.hpp index 8689a1b..690d45c 100644 --- a/src/nodes/StructDefinitionNode.hpp +++ b/src/nodes/StructDefinitionNode.hpp @@ -43,7 +43,7 @@ class StructDefinitionNode : public BaseNode, public std::enable_shared_from_thi * to the scope. Careful! Going this route means we don't have to create a * separate Node to create an Object. */ - std::shared_ptr evaluate(class Scope &scope) override; + class AnyObject evaluate(class Scope &scope) override; /** * Calls evaluate method on all variables in this struct and parents. Installs diff --git a/src/nodes/StructNode.cpp b/src/nodes/StructNode.cpp index 37e10e6..9aa2621 100644 --- a/src/nodes/StructNode.cpp +++ b/src/nodes/StructNode.cpp @@ -22,7 +22,7 @@ StructNode::StructNode(std::string typeName_, std::string name_) } -AnyObject::Ptr StructNode::evaluate(Scope &scope) +AnyObject StructNode::evaluate(Scope &scope) { if (active) { @@ -32,14 +32,12 @@ AnyObject::Ptr StructNode::evaluate(Scope &scope) active = true; // Initialize our instance from the struct definition defined in the scope. - structDefinition = std::static_pointer_cast(scope.getNamedObject(typeName)->getValue()); + structDefinition = std::static_pointer_cast(scope.getObjectRef(typeName).getValue()); structDefinition->installVariablesInScope(_instanceScope, variableNames); // Add the active struct instance to the scope. TODO: - transfer ownership // to the scope. Will have to remove this class from AST to do this correctly. - auto wrappedStruct = ObjectFactory::allocate(shared_from_this(), AnyObject::Struct); - scope.linkObject(name, wrappedStruct); - return wrappedStruct; + return scope.link(name, AnyObject(shared_from_this(), AnyObject::Struct)); } @@ -59,11 +57,11 @@ StructNode &StructNode::operator=(const StructNode &other) // 2. iterate over the objects stored in the scopes and assign. for (auto &variableName : variableNames) { - AnyObject::Ptr thisObject = _instanceScope.getNamedObject(variableName); - AnyObject::Ptr otherObject = other._instanceScope.getNamedObject(variableName); + AnyObject::Ref thisObject = _instanceScope.getObjectRef(variableName); + AnyObject::Ref otherObject = other._instanceScope.getObjectRef(variableName); // Attempt an assignment. Will fail if different types. - (*thisObject) = (*otherObject); + thisObject = otherObject; } return (*this); diff --git a/src/nodes/StructNode.hpp b/src/nodes/StructNode.hpp index d7f9eea..6a6140c 100644 --- a/src/nodes/StructNode.hpp +++ b/src/nodes/StructNode.hpp @@ -45,7 +45,7 @@ class StructNode : public BaseNode, public std::enable_shared_from_this evaluate(Scope &scope) override; + class AnyObject evaluate(Scope &scope) override; [[nodiscard]] const Scope &instanceScope() const { return _instanceScope; } diff --git a/src/objects/AnyObject.cpp b/src/objects/AnyObject.cpp index b07bc97..df2c050 100644 --- a/src/objects/AnyObject.cpp +++ b/src/objects/AnyObject.cpp @@ -37,7 +37,7 @@ AnyObject::Type AnyObject::getUserObjectType(const std::string &name) } -std::string AnyObject::typeToString() const +std::string AnyObject::typeToString(Type type) { static const std::unordered_map TypeToString = {{NotSet, "NotSet"}, {Int, "Int"}, @@ -52,13 +52,19 @@ std::string AnyObject::typeToString() const {_StructDefinition, "StructDef"}, {_ClassDefinition, "ClassDef"}}; - auto iter = TypeToString.find(_type); + auto iter = TypeToString.find(type); if (iter != TypeToString.end()) { return (iter->second); } - return "Unknown"; + ThrowException("Unknown AnyObjectType!"); +} + + +std::string AnyObject::typeToString() const +{ + return AnyObject::typeToString(_type); } @@ -67,7 +73,15 @@ AnyObject &AnyObject::operator=(const AnyObject &other) /* TODO: - implement if required */ AnyObject::Vector cloneVector(const AnyObject::Vector &); - if (getType() != other.getType()) + /* Handling not-set return type */ + if (getType() == AnyObject::NotSet || other.getType() == AnyObject::NotSet) + { + _value = other._value; + _type = other._type; + return (*this); + } + + if (getType() != other.getType()) /* TODO: - bit hacky. But return type can take any */ { ThrowException("Invalid assignment. Types do not match [LHS = " + typeToString() + ", RHS = " + other.typeToString() + "]"); } @@ -94,22 +108,22 @@ AnyObject &AnyObject::operator=(const AnyObject &other) } -AnyObject::Ptr AnyObject::clone() const +AnyObject AnyObject::clone() const { AnyObject::Vector cloneVector(const AnyObject::Vector &); switch (getType()) { case Bool: - return std::make_shared(getValue()); + return AnyObject(getValue()); /* TODO: - these are implicit. All nodes with evaluateRef implmemeneted can call it with evaluate() and just copy */ case Int: - return std::make_shared(getValue()); + return AnyObject(getValue()); /* TODO: - don't need any of these except vector */ case Float: - return std::make_shared(getValue()); + return AnyObject(getValue()); case String: - return std::make_shared(getValue()); + return AnyObject(getValue()); case Array: - return std::make_shared(cloneVector(getValue())); + return AnyObject(cloneVector(getValue())); default: ThrowException("clone() is not implemented for object type [" + typeToString() + "]"); } @@ -123,7 +137,7 @@ AnyObject::Vector cloneVector(const AnyObject::Vector &vector) for (const auto &object : vector) { - clone.push_back(object->clone()); /* Deep-copy */ + clone.push_back(object.clone()); /* Deep-copy */ } return clone; @@ -166,7 +180,7 @@ std::ostream &operator<<(std::ostream &out, const AnyObject::Vector &array) for (const auto &object : array) { - out << *object << ", "; + out << object << ", "; } out << "]"; diff --git a/src/objects/AnyObject.hpp b/src/objects/AnyObject.hpp index f4f8eaa..eb1d7c6 100644 --- a/src/objects/AnyObject.hpp +++ b/src/objects/AnyObject.hpp @@ -8,17 +8,19 @@ */ #pragma once -#include "BaseNode.hpp" +#include "Logger.hpp" #include "ModuleFunctor.hpp" #include "PoolAllocator.hpp" #include #include #include +#include #include #include #include #include + // TODO: - profile and investigate using PoolAllocator // static PoolAllocator allocator{10}; @@ -26,8 +28,10 @@ class AnyObject { public: using Ptr = std::shared_ptr; - using Vector = std::vector; + using Vector = std::vector; + using Ref = AnyObject &; + AnyObject() = default; /* None type */ virtual ~AnyObject() = default; /* In case we subclass */ enum Type @@ -61,6 +65,8 @@ class AnyObject /* Converts user types like "int" --> AnyType::Int or returns None if not found */ static AnyObject::Type getUserObjectType(const std::string &name); + static std::string typeToString(Type type); + std::string typeToString() const; /* NB: require explicit to avoid implicit casting */ @@ -72,7 +78,7 @@ class AnyObject explicit AnyObject(ModuleFunctor value) : _value(std::move(value)), _type(_ModuleFunction) {} /* _userFuntion, _StructDefinition, Struct, ...*/ - explicit AnyObject(std::shared_ptr value, Type type) : _value(std::move(value)), _type(type) {} + explicit AnyObject(std::shared_ptr value, Type type) : _value(std::move(value)), _type(type) {} AnyObject &operator=(const AnyObject &other); @@ -86,12 +92,14 @@ class AnyObject [[nodiscard]] inline bool isType(Type expectedType) const; - [[nodiscard]] AnyObject::Ptr clone() const; + [[nodiscard]] AnyObject clone() const; friend std::ostream &operator<<(std::ostream &out, const AnyObject &object); -protected: - AnyObject() = default; /* Prevent direct initialization */ + bool operator!() /* !(instance) == true if not initialized with a value */ + { + return (_type == Type::NotSet); + } private: using ValueVariant = std::variant; - ValueVariant _value{}; + std::shared_ptr>; + ValueVariant _value{false}; Type _type{Type::NotSet}; }; +// TODO: - should only return a reference IFF we're using evaluateRef() to get the object otherwise it's constructor +// will be called and the reference to its internal stored value may be garbage before we end-up using it + + template // No type checking!! TValue &AnyObject::getValue() { diff --git a/src/objects/ModuleFunctor.cpp b/src/objects/ModuleFunctor.cpp index f424b10..fa9412b 100644 --- a/src/objects/ModuleFunctor.cpp +++ b/src/objects/ModuleFunctor.cpp @@ -9,10 +9,11 @@ #include "ModuleFunctor.hpp" #include "AnyObject.hpp" +#include "BaseNode.hpp" ModuleFunctor::ModuleFunctor(Function function) : _function(function) {} -std::shared_ptr ModuleFunctor::operator()(BaseNodePtrVector &args, Scope &scope) +AnyObject ModuleFunctor::operator()(BaseNodePtrVector &args, Scope &scope) { - return !_function ? nullptr : _function(args, scope); + return !_function ? AnyObject() : _function(args, scope); } \ No newline at end of file diff --git a/src/objects/ModuleFunctor.hpp b/src/objects/ModuleFunctor.hpp index c92cd30..0fb3dce 100644 --- a/src/objects/ModuleFunctor.hpp +++ b/src/objects/ModuleFunctor.hpp @@ -9,24 +9,24 @@ #pragma once -#include "BaseNode.hpp" #include "Scope.hpp" #include #include #include #include + class ModuleFunctor { public: using Name = std::string; - using Function = std::function(BaseNodePtrVector &, Scope &)>; + using Function = std::function> &, Scope &)>; using Definition = std::pair; using Definitions = std::vector; ModuleFunctor(ModuleFunctor::Function function); - [[nodiscard]] std::shared_ptr operator()(BaseNodePtrVector &args, Scope &scope); + [[nodiscard]] class AnyObject operator()(std::vector> &args, Scope &scope); private: Function _function{nullptr}; diff --git a/src/objects/ObjectFactory.cpp b/src/objects/ObjectFactory.cpp index e15b8f1..5106a1d 100644 --- a/src/objects/ObjectFactory.cpp +++ b/src/objects/ObjectFactory.cpp @@ -14,22 +14,22 @@ namespace ObjectFactory { -AnyObject::Ptr allocate(AnyObject::Type objectType) +AnyObject createEmptyObject(AnyObject::Type objectType) { switch (objectType) { case AnyObject::Int: - return std::make_shared(0L); + return AnyObject(0L); case AnyObject::Bool: - return std::make_shared(false); + return AnyObject(false); case AnyObject::Float: - return std::make_shared((double)0.0); + return AnyObject((double)0.0); case AnyObject::String: - return std::make_shared(std::string()); + return AnyObject(std::string()); case AnyObject::Array: - return std::make_shared(AnyObject::Vector()); + return AnyObject(AnyObject::Vector()); default: - ThrowException("cannot allocate for object type!"); + ThrowException("cannot create empty AnyObject with type [" + AnyObject::typeToString(objectType) + "]"); } } diff --git a/src/objects/ObjectFactory.hpp b/src/objects/ObjectFactory.hpp index 87d6e79..7ef378f 100644 --- a/src/objects/ObjectFactory.hpp +++ b/src/objects/ObjectFactory.hpp @@ -16,12 +16,6 @@ namespace ObjectFactory { -template -[[nodiscard]] inline AnyObject::Ptr allocate(Args &&...args) -{ - return std::make_shared(std::forward(args)...); -} - -AnyObject::Ptr allocate(AnyObject::Type objectType); +AnyObject createEmptyObject(AnyObject::Type objectType); } // namespace ObjectFactory \ No newline at end of file diff --git a/src/parsers/BaseParser.cpp b/src/parsers/BaseParser.cpp index ab23b4d..5137c6f 100644 --- a/src/parsers/BaseParser.cpp +++ b/src/parsers/BaseParser.cpp @@ -8,6 +8,7 @@ */ #include "BaseParser.hpp" +#include "AnyObject.hpp" #include "Exceptions.hpp" diff --git a/src/utility/JumpPoints.hpp b/src/utility/JumpPoints.hpp index 9c85e21..5bfb5e2 100644 --- a/src/utility/JumpPoints.hpp +++ b/src/utility/JumpPoints.hpp @@ -8,8 +8,8 @@ */ #pragma once +#include "AnyObject.hpp" #include -#include struct GlobalEnvRec @@ -17,7 +17,7 @@ struct GlobalEnvRec jmp_buf *breakJumpPoint; jmp_buf *returnJumpPoint; - std::shared_ptr returnValue{nullptr}; + AnyObject returnValue; }; extern GlobalEnvRec gEnvironmentContext; // TODO: - remove once return done as well. From 6bc1fa1b5eefd0095f2c5725edeaada371d4b4da Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 25 May 2025 11:48:32 +0100 Subject: [PATCH 14/15] Fixes filesystem --- src/lexer/CharStream.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lexer/CharStream.hpp b/src/lexer/CharStream.hpp index b6acc08..e4d7503 100644 --- a/src/lexer/CharStream.hpp +++ b/src/lexer/CharStream.hpp @@ -60,7 +60,7 @@ class CharStream private: unsigned int endCol(unsigned int lineNum) const; - const std::filesystem path; + const std::filesystem::path path; char *base{nullptr}; char *ptr{nullptr}; From 640f41a65bb53fafc1dc702a2bca040f323ce92f Mon Sep 17 00:00:00 2001 From: Edward Palmer <{username}@users.noreply.github.com> Date: Sun, 25 May 2025 11:53:27 +0100 Subject: [PATCH 15/15] Removes std::out print-out --- src/nodes/NodeFactory.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/nodes/NodeFactory.cpp b/src/nodes/NodeFactory.cpp index 10e6337..f270a0e 100644 --- a/src/nodes/NodeFactory.cpp +++ b/src/nodes/NodeFactory.cpp @@ -377,7 +377,6 @@ AnyPropertyNode::Ptr createStructAccessNode(std::string structVarName, std::stri // TODO: - reimplement this auto theStructObject = std::static_pointer_cast(theObject.getValue()); - std::cout << "getting object reference from scope " << memberVarName << std::endl; return std::ref(theStructObject->instanceScope().getObjectRef(memberVarName)); };