From 3dfcefcea525d544940fb408de31780435f9cb9e Mon Sep 17 00:00:00 2001 From: heinezen Date: Tue, 13 Jun 2023 18:40:29 +0200 Subject: [PATCH 1/9] db: Fix dangling reference when returning member value. This happened because the member value is copied for the calculation of inherited member operations/assignments. --- nyan/object.cpp | 10 ++--- nyan/object.h | 106 ++++++++++++++++++++---------------------------- 2 files changed, 50 insertions(+), 66 deletions(-) diff --git a/nyan/object.cpp b/nyan/object.cpp index 75d808e..7f4bdcc 100644 --- a/nyan/object.cpp +++ b/nyan/object.cpp @@ -1,4 +1,4 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #include "object.h" @@ -59,7 +59,7 @@ value_float_t Object::get_float(const memberid_t &member, order_t t) const { } -const std::string &Object::get_text(const memberid_t &member, order_t t) const { +std::string Object::get_text(const memberid_t &member, order_t t) const { return *this->get(member, t); } @@ -69,17 +69,17 @@ bool Object::get_bool(const memberid_t &member, order_t t) const { } -const set_t &Object::get_set(const memberid_t &member, order_t t) const { +set_t Object::get_set(const memberid_t &member, order_t t) const { return this->get(member, t)->get(); } -const ordered_set_t &Object::get_orderedset(const memberid_t &member, order_t t) const { +ordered_set_t Object::get_orderedset(const memberid_t &member, order_t t) const { return this->get(member, t)->get(); } -const std::string &Object::get_file(const memberid_t &member, order_t t) const { +std::string Object::get_file(const memberid_t &member, order_t t) const { return this->get(member, t)->get(); } diff --git a/nyan/object.h b/nyan/object.h index 8493a61..2ca5ad0 100644 --- a/nyan/object.h +++ b/nyan/object.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #pragma once @@ -69,80 +69,76 @@ class Object { /** * Get the identifier of this object (fully-qualified object name). * - * @return Identifier of this object. + * @return fqon of this object. */ const fqon_t &get_name() const; /** - * Get the view this object was created in. + * Get the view of the database this object is associated with * - * @return View of this object. + * @return Database view. */ const std::shared_ptr &get_view() const; /** - * Get the calculated member value for a given member at a given time. + * Get a new value holder that contains the calculated member value + * for a given member at a given time. * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which we want to calculate the value. * - * @return ValueHolder with the value of the member. + * @return ValueHolder containing the raw value of the member. */ ValueHolder get_value(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. + * Get the calculated member value container for a given member at a given time. * - * Invokes the get_value function and then does a cast to type T. - * There's a special variant for T=nyan::Object which creates - * an object handle. - * Internally calls `get_optional` with `may_be_none=false`. + * Invokes the get_value function and then does a cast to type T which + * is a nyan value type. * - * @tparam T the value is casted to. + * @tparam T nyan type of the value. * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which we want to calculate the value. * - * @return Shared pointer with the value of the member. + * @return Value of the member. */ template std::shared_ptr get(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get a value which has optional type. - * The `may_be_none` template parameter is to patch out the optional check branch, - * since this function is used internally by `Object::get`. + * Get the calculated member value container for a given member at a given time. + * + * This variant of \p get() always explicitely tests if the member value + * is \p None (i.e. if there is an optional value). * - * @param member Identifier of the object member entry. + * @param member Member ID. * @param t Time to retrieve the member for. * - * @return std::optional wrapping the member value + * @return Value of the member. */ template std::optional> get_optional(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. + * Get the calculated member value for a number type member (\p int or \p float). * - * Casts to a number type T. - * - * @tparam Number type the value is casted to. + * @tparam Number type of the member. * @tparam Return type of the value. * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which we want to calculate the value. * - * @return Value of the member with type \p ret. + * @return Value of the member. */ template T, typename ret=typename T::storage_type> ret get_number(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. - * - * Casts to int. + * Get the calculated member value for an \p int type member. * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which we want to calculate the value. * * @return Value of the member. @@ -150,11 +146,11 @@ class Object { value_int_t get_int(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. + * Get the calculated member value for an \p float type member. * - * Casts to float. + * Note that this actually returns a double. * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which the value is calculated. * * @return Value of the member. @@ -162,23 +158,19 @@ class Object { value_float_t get_float(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. + * Get the calculated member value for an \p text type member. * - * Casts to std::string. - * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which the value is calculated. * * @return Value of the member. */ - const std::string &get_text(const memberid_t &member, order_t t=LATEST_T) const; + std::string get_text(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. + * Get the calculated member value for an \p bool type member. * - * Casts to bool. - * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which the value is calculated. * * @return Value of the member. @@ -186,47 +178,39 @@ class Object { bool get_bool(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. - * - * Casts to std::unordered_set. + * Get the calculated member value for an \p set type member. * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which the value is calculated. * * @return Value of the member. */ - const set_t &get_set(const memberid_t &member, order_t t=LATEST_T) const; + set_t get_set(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. + * Get the calculated member value for an \p orderedset type member. * - * Casts to nyan::OrderedSet. - * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which the value is calculated. * * @return Value of the member. */ - const ordered_set_t &get_orderedset(const memberid_t &member, order_t t=LATEST_T) const; + ordered_set_t get_orderedset(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. - * - * Casts to std::string. + * Get the calculated member value for an \p file type member. * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which the value is calculated. * * @return Value of the member. */ - const std::string &get_file(const memberid_t &member, order_t t=LATEST_T) const; + std::string get_file(const memberid_t &member, order_t t=LATEST_T) const; /** - * Get the calculated member value for a given member at a given time. + * Get the calculated member value container for an \p object type member. * - * Returns an Object. - * - * @param member Identifier of the member. + * @param member Member ID. * @param t Time for which the value is calculated. * * @return Value of the member. From 1c23b97026bd3cfeb7726256937e517764e3fd7a Mon Sep 17 00:00:00 2001 From: heinezen Date: Wed, 14 Jun 2023 21:48:01 +0200 Subject: [PATCH 2/9] db: Add `get_dict` method. --- nyan/CMakeLists.txt | 2 +- nyan/nyan.h | 3 ++- nyan/object.cpp | 4 ++++ nyan/object.h | 12 +++++++++++- nyan/value/container_types.cpp | 3 +++ nyan/value/{set_types.h => container_types.h} | 6 +++++- nyan/value/dict.h | 3 ++- nyan/value/orderedset.h | 4 ++-- nyan/value/set.h | 4 ++-- nyan/value/set_types.cpp | 3 --- 10 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 nyan/value/container_types.cpp rename nyan/value/{set_types.h => container_types.h} (69%) delete mode 100644 nyan/value/set_types.cpp diff --git a/nyan/CMakeLists.txt b/nyan/CMakeLists.txt index 50bf963..1e634d1 100644 --- a/nyan/CMakeLists.txt +++ b/nyan/CMakeLists.txt @@ -68,6 +68,7 @@ add_library(nyan SHARED util/flags.cpp value_token.cpp value/boolean.cpp + value/container_types.cpp value/container.cpp value/dict.cpp value/file.cpp @@ -77,7 +78,6 @@ add_library(nyan SHARED value/orderedset.cpp value/set.cpp value/set_base.cpp - value/set_types.cpp value/text.cpp value/value.cpp value/value_holder.cpp diff --git a/nyan/nyan.h b/nyan/nyan.h index 72c7baf..da47005 100644 --- a/nyan/nyan.h +++ b/nyan/nyan.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #pragma once @@ -30,6 +30,7 @@ #include "value/object.h" #include "value/orderedset.h" #include "value/set.h" +#include "value/dict.h" #include "value/text.h" #include "view.h" diff --git a/nyan/object.cpp b/nyan/object.cpp index 7f4bdcc..5276386 100644 --- a/nyan/object.cpp +++ b/nyan/object.cpp @@ -19,6 +19,7 @@ #include "value/object.h" #include "value/orderedset.h" #include "value/set.h" +#include "value/dict.h" #include "value/text.h" #include "view.h" @@ -78,6 +79,9 @@ ordered_set_t Object::get_orderedset(const memberid_t &member, order_t t) const return this->get(member, t)->get(); } +dict_t Object::get_dict(const memberid_t &member, order_t t) const { + return this->get(member, t)->get(); +} std::string Object::get_file(const memberid_t &member, order_t t) const { return this->get(member, t)->get(); diff --git a/nyan/object.h b/nyan/object.h index 2ca5ad0..13f30b5 100644 --- a/nyan/object.h +++ b/nyan/object.h @@ -13,7 +13,7 @@ #include "api_error.h" #include "config.h" #include "value/none.h" -#include "value/set_types.h" +#include "value/container_types.h" #include "value/value_holder.h" #include "object_notifier_types.h" #include "util.h" @@ -197,6 +197,16 @@ class Object { */ ordered_set_t get_orderedset(const memberid_t &member, order_t t=LATEST_T) const; + /** + * Get the calculated member value for an \p dict type member. + * + * @param member Member ID. + * @param t Time for which the value is calculated. + * + * @return Value of the member. + */ + dict_t get_dict(const memberid_t &member, order_t t=LATEST_T) const; + /** * Get the calculated member value for an \p file type member. * diff --git a/nyan/value/container_types.cpp b/nyan/value/container_types.cpp new file mode 100644 index 0000000..b720b53 --- /dev/null +++ b/nyan/value/container_types.cpp @@ -0,0 +1,3 @@ +// Copyright 2019-2023 the nyan authors, LGPLv3+. See copying.md for legal info. + +#include "container_types.h" diff --git a/nyan/value/set_types.h b/nyan/value/container_types.h similarity index 69% rename from nyan/value/set_types.h rename to nyan/value/container_types.h index f6294d7..57c8e2a 100644 --- a/nyan/value/set_types.h +++ b/nyan/value/container_types.h @@ -1,4 +1,4 @@ -// Copyright 2019-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2019-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #pragma once #include @@ -16,4 +16,8 @@ using set_t = std::unordered_set; /** datatype used for ordered set storage */ using ordered_set_t = datastructure::OrderedSet; + +/** datatype used for dict storage */ +using dict_t = std::unordered_map; + } // namespace nyan diff --git a/nyan/value/dict.h b/nyan/value/dict.h index 7883b4f..358c6dc 100644 --- a/nyan/value/dict.h +++ b/nyan/value/dict.h @@ -9,6 +9,7 @@ #include "../util.h" #include "container.h" #include "value.h" +#include "container_types.h" namespace nyan { @@ -19,7 +20,7 @@ namespace nyan { */ class Dict : public Value { public: - using value_storage = std::unordered_map; + using value_storage = dict_t; using key_type = typename value_storage::key_type; using value_type = typename value_storage::mapped_type; using element_type = typename value_storage::value_type; diff --git a/nyan/value/orderedset.h b/nyan/value/orderedset.h index a513d0b..4161060 100644 --- a/nyan/value/orderedset.h +++ b/nyan/value/orderedset.h @@ -1,9 +1,9 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #pragma once #include "set_base.h" -#include "set_types.h" +#include "container_types.h" #include "../ops.h" #include "value_holder.h" diff --git a/nyan/value/set.h b/nyan/value/set.h index b3685d7..f292e51 100644 --- a/nyan/value/set.h +++ b/nyan/value/set.h @@ -1,11 +1,11 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #pragma once #include #include "../ops.h" -#include "set_types.h" +#include "container_types.h" #include "set_base.h" diff --git a/nyan/value/set_types.cpp b/nyan/value/set_types.cpp deleted file mode 100644 index 586d078..0000000 --- a/nyan/value/set_types.cpp +++ /dev/null @@ -1,3 +0,0 @@ -// Copyright 2019-2019 the nyan authors, LGPLv3+. See copying.md for legal info. - -#include "set_types.h" From 708de128745b84a7170dc3f9b1b07c0584043764 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 15 Jun 2023 15:40:33 +0200 Subject: [PATCH 3/9] Add clang-format file from openage. --- .clang-format | 130 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..b870e0e --- /dev/null +++ b/.clang-format @@ -0,0 +1,130 @@ +--- +# SFT codestyle +# Tab indent + space alignment +# see documentation in doc/code_style/ for details and explainations. +Language: Cpp +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignArrayOfStructures: None +AlignConsecutiveAssignments: false +AlignConsecutiveBitFields: false +AlignConsecutiveDeclarations: false +AlignConsecutiveMacros: false +AlignEscapedNewlines: DontAlign +AlignOperands: Align +AlignTrailingComments: false +AllowAllArgumentsOnNextLine: true +AllowAllParametersOfDeclarationOnNextLine: true +AllowShortBlocksOnASingleLine: Never +AllowShortCaseLabelsOnASingleLine: false +AllowShortEnumsOnASingleLine: false +AllowShortFunctionsOnASingleLine: Empty +AllowShortIfStatementsOnASingleLine: Never +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: Yes +BinPackArguments: false +BinPackParameters: false +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterExternBlock: false + AfterFunction: false + AfterNamespace: false + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: true + BeforeElse: true + BeforeLambdaBody: false + BeforeWhile: true + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyNamespace: false + SplitEmptyRecord: false +BreakAfterJavaFieldAnnotations: true +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializers: AfterColon +BreakInheritanceList: BeforeComma +BreakStringLiterals: false +ColumnLimit: 0 +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 +Cpp11BracedListStyle: true +DeriveLineEnding: true +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: true +ForEachMacros: + - foreach + - Q_FOREACH + - BOOST_FOREACH +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 3 + SortPriority: 0 +IncludeIsMainRegex: '' +IncludeIsMainSourceRegex: '' +IndentCaseBlocks: false +IndentCaseLabels: false +IndentExternBlock: NoIndent +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 4 +IndentWrappedFunctionNames: false +# clang-format-16 InsertNewlineAtEOF: true +InsertTrailingCommas: Wrapped +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: None +PenaltyBreakAssignment: 2 +PenaltyBreakBeforeFirstCallParameter: 1 +PenaltyBreakComment: 300 +PenaltyBreakFirstLessLess: 120 +PenaltyBreakString: 1000 +PenaltyBreakTemplateDeclaration: 10 +PenaltyExcessCharacter: 1000000 +PenaltyReturnTypeOnItsOwnLine: 200 +PointerAlignment: Right +ReflowComments: false +SortIncludes: CaseInsensitive +SortUsingDeclarations: true +SpaceAfterCStyleCast: false +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCaseColon: false +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceBeforeSquareBrackets: false +SpaceInEmptyBlock: false +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: Never +SpacesInCStyleCastParentheses: false +SpacesInConditionalStatement: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Latest +TabWidth: 4 +UseCRLF: false +UseTab: AlignWithSpaces +... From e7bef9a3e92e8a3e660d1db3a679712742684a52 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 15 Jun 2023 18:34:25 +0200 Subject: [PATCH 4/9] value: Strip quotes from text/file inits. --- nyan/value/file.cpp | 24 +++++++++++------------- nyan/value/text.cpp | 28 ++++++++++++++-------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/nyan/value/file.cpp b/nyan/value/file.cpp index d68f12b..e008af9 100644 --- a/nyan/value/file.cpp +++ b/nyan/value/file.cpp @@ -1,35 +1,32 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #include "file.h" #include #include "../compiler.h" -#include "../lang_error.h" #include "../id_token.h" +#include "../lang_error.h" #include "../util.h" namespace nyan { -Filename::Filename(const std::string &path) - : +Filename::Filename(const std::string &path) : path{path} { - // TODO relative path resolution } -Filename::Filename(const IDToken &token) - : - Filename{token.get_first()} { - +Filename::Filename(const IDToken &token) { if (unlikely(token.get_type() != token_type::STRING)) { throw LangError{ token, - "invalid value for filename" - }; + "invalid value for filename"}; } + + // strip the quotes + this->path = token.get_first().substr(1, token.get_first().size() - 2); } @@ -50,7 +47,8 @@ bool Filename::apply_value(const Value &value, nyan_op operation) { switch (operation) { case nyan_op::ASSIGN: - this->path = change.path; break; + this->path = change.path; + break; default: throw Error{"unknown operation requested"}; @@ -61,7 +59,7 @@ bool Filename::apply_value(const Value &value, nyan_op operation) { std::string Filename::str() const { - return this->path; + return "\"" + this->path + "\""; } diff --git a/nyan/value/text.cpp b/nyan/value/text.cpp index 2f75def..59cb31a 100644 --- a/nyan/value/text.cpp +++ b/nyan/value/text.cpp @@ -1,33 +1,31 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #include "text.h" #include #include "../compiler.h" -#include "../lang_error.h" #include "../id_token.h" -#include "../util.h" +#include "../lang_error.h" #include "../token.h" +#include "../util.h" namespace nyan { -Text::Text(const std::string &value) - : +Text::Text(const std::string &value) : value{value} {} -Text::Text(const IDToken &token) - : - Text{token.get_first()} { - +Text::Text(const IDToken &token) { if (unlikely(token.get_type() != token_type::STRING)) { throw LangError{ token, - "invalid value for text" - }; + "invalid value for text"}; } + + // strip the quotes + this->value = token.get_first().substr(1, token.get_first().size() - 2); } @@ -41,10 +39,12 @@ bool Text::apply_value(const Value &value, nyan_op operation) { switch (operation) { case nyan_op::ASSIGN: - this->value = change.value; break; + this->value = change.value; + break; case nyan_op::ADD_ASSIGN: - this->value += change.value; break; + this->value += change.value; + break; default: throw InternalError{"unknown operation requested"}; @@ -55,7 +55,7 @@ bool Text::apply_value(const Value &value, nyan_op operation) { std::string Text::str() const { - return this->value; + return "\"" + this->value + "\""; } From 2ac2808129a813570dfd3ad6b132e10bae1941e5 Mon Sep 17 00:00:00 2001 From: heinezen Date: Thu, 15 Jun 2023 20:34:12 +0200 Subject: [PATCH 5/9] db: Allow getting file location of object. --- nyan/location.cpp | 27 ++++++++++++++------------- nyan/location.h | 15 ++++++++++++--- nyan/object.cpp | 16 ++++++++-------- 3 files changed, 34 insertions(+), 24 deletions(-) diff --git a/nyan/location.cpp b/nyan/location.cpp index 6b25e62..ef8433a 100644 --- a/nyan/location.cpp +++ b/nyan/location.cpp @@ -1,40 +1,37 @@ -// Copyright 2016-2020 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #include "location.h" #include "file.h" -#include "token.h" #include "id_token.h" +#include "token.h" namespace nyan { -Location::Location(const Token &token) - : +Location::Location(const Token &token) : Location{token.location} {} -Location::Location(const IDToken &token) - : +Location::Location(const IDToken &token) : Location{token.get_start_location()} { - // use the full id length as location length this->length = token.get_length(); } Location::Location(const std::shared_ptr &file, - int line, int line_offset, int length) - : + int line, + int line_offset, + int length) : file{file}, line{line}, line_offset{line_offset}, length{length} {} -Location::Location(const std::string &custom) - : +Location::Location(const std::string &custom) : _is_builtin{true}, msg{custom} {} @@ -68,6 +65,10 @@ std::string Location::get_line_content() const { return this->file->get_line(this->get_line()); } +const std::shared_ptr &Location::get_file() const { + return this->file; +} + void Location::str(std::ostringstream &builder) const { if (this->_is_builtin) { @@ -76,8 +77,8 @@ void Location::str(std::ostringstream &builder) const { } builder << this->file->get_name() << ":" - << this->line << ":" - << this->line_offset << ": "; + << this->line << ":" + << this->line_offset << ": "; } } // namespace nyan diff --git a/nyan/location.h b/nyan/location.h index 5c715ed..a70f761 100644 --- a/nyan/location.h +++ b/nyan/location.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #pragma once #include @@ -21,8 +21,10 @@ class Location { Location() = default; Location(const Token &token); Location(const IDToken &token); - Location(const std::shared_ptr &file, int line, - int line_offset, int length=0); + Location(const std::shared_ptr &file, + int line, + int line_offset, + int length = 0); explicit Location(const std::string &custom); ~Location() = default; @@ -69,6 +71,13 @@ class Location { */ std::string get_line_content() const; + /** + * Get the file for the location. + * + * @return File handle. Can be \p nullptr. + */ + const std::shared_ptr &get_file() const; + /** * Append the string representation of the location to a given output stream. * diff --git a/nyan/object.cpp b/nyan/object.cpp index 5276386..b73bdd1 100644 --- a/nyan/object.cpp +++ b/nyan/object.cpp @@ -14,20 +14,19 @@ #include "patch_info.h" #include "util.h" #include "value/boolean.h" +#include "value/dict.h" #include "value/file.h" #include "value/number.h" #include "value/object.h" #include "value/orderedset.h" #include "value/set.h" -#include "value/dict.h" #include "value/text.h" #include "view.h" namespace nyan { -Object::Object(const fqon_t &name, const std::shared_ptr &origin) - : +Object::Object(const fqon_t &name, const std::shared_ptr &origin) : origin{origin}, name{name} {} @@ -79,10 +78,12 @@ ordered_set_t Object::get_orderedset(const memberid_t &member, order_t t) const return this->get(member, t)->get(); } + dict_t Object::get_dict(const memberid_t &member, order_t t) const { - return this->get(member, t)->get(); + return this->get(member, t)->get(); } + std::string Object::get_file(const memberid_t &member, order_t t) const { return this->get(member, t)->get(); } @@ -101,8 +102,7 @@ std::shared_ptr Object::get(const memberid_t &member, order_t t) std::shared_ptr ret = std::make_shared( Object::Restricted{}, fqon, - this->origin - ); + this->origin); return ret; } @@ -118,8 +118,8 @@ std::optional> Object::get_optional(const member const fqon_t &fqon = obj_val->get_name(); std::shared_ptr ret = std::make_shared( Object::Restricted{}, - fqon, this->origin - ); + fqon, + this->origin); return ret; } From a88045ac7904a383db23643dabf964ddfbf28b65 Mon Sep 17 00:00:00 2001 From: heinezen Date: Fri, 16 Jun 2023 22:52:07 +0200 Subject: [PATCH 6/9] db: Advanced namespace integration. --- nyan/database.cpp | 281 +++++++++++++++++--------------------- nyan/database.h | 18 +-- nyan/id_token.cpp | 18 ++- nyan/id_token.h | 7 + nyan/namespace.cpp | 204 ++++++++++++++++++--------- nyan/namespace.h | 153 +++++++++++++++++---- nyan/namespace_finder.cpp | 18 ++- nyan/namespace_finder.h | 2 +- nyan/object_info.cpp | 18 ++- nyan/object_info.h | 16 ++- 10 files changed, 446 insertions(+), 289 deletions(-) diff --git a/nyan/database.cpp b/nyan/database.cpp index d3617c0..6a5e28e 100644 --- a/nyan/database.cpp +++ b/nyan/database.cpp @@ -3,8 +3,8 @@ #include "database.h" #include -#include #include +#include #include "c3.h" #include "compiler.h" @@ -26,8 +26,7 @@ std::shared_ptr Database::create() { } -Database::Database() - : +Database::Database() : state{std::make_shared()} {} @@ -49,7 +48,6 @@ static void ast_obj_walk_recurser(const ast_objwalk_cb_t &callback, const NamespaceFinder &scope, const Namespace &ns, const std::vector &objs) { - // go over all objects for (auto &astobj : objs) { Namespace objname{ns, astobj.get_name().get()}; @@ -65,7 +63,6 @@ static void ast_obj_walk_recurser(const ast_objwalk_cb_t &callback, static void ast_obj_walk(const namespace_lookup_t &imports, const ast_objwalk_cb_t &cb) { - // go over all the imported files for (auto &it : imports) { const Namespace &ns = it.first; @@ -80,7 +77,6 @@ static void ast_obj_walk(const namespace_lookup_t &imports, void Database::load(const std::string &filename, const filefetcher_t &filefetcher) { - Parser parser; // tracking of imported namespaces (with aliases) @@ -94,11 +90,8 @@ void Database::load(const std::string &filename, if (not this->meta_info.has_namespace(file_ns.to_fqon())) { // push the first namespace to import to_import.insert( - { - file_ns, - Location{" -> requested by native call to Database::load()"} - } - ); + {file_ns, + Location{" -> requested by native call to Database::load()"}}); } // descend to all imports and load the files @@ -117,8 +110,7 @@ void Database::load(const std::string &filename, try { // get the data and parse the file current_file = filefetcher( - namespace_to_import.to_filename() - ); + namespace_to_import.to_filepath()); } catch (FileReadError &err) { // the import request failed, @@ -128,17 +120,23 @@ void Database::load(const std::string &filename, // create import tracking entry for this file // and parse the file contents! - NamespaceFinder &new_ns = imports.insert({ - namespace_to_import, // name of the import - NamespaceFinder{ - parser.parse(current_file) // read the ast! - } - }).first->second; + NamespaceFinder &new_ns = imports.insert({namespace_to_import, // name of the import + NamespaceFinder{ + parser.parse(current_file) // read the ast! + }}) + .first->second; // enqueue all new imports of this file // and record import aliases for (auto &import : new_ns.get_ast().get_imports()) { - Namespace request{import.get()}; + std::vector components = import.get().get_components(); + std::vector dir_components; + dir_components.reserve(components.size() - 1); + for (size_t i = 0; i < components.size() - 1; ++i) { + dir_components.push_back(components[i].get()); + } + std::string filename = components.back().get(); + Namespace request{std::move(dir_components), std::move(filename)}; // either register the alias if (import.has_alias()) { @@ -153,9 +151,7 @@ void Database::load(const std::string &filename, auto was_imported = imports.find(request); auto import_requested = to_import.find(request); - if (was_imported == std::end(imports) and - import_requested == std::end(to_import)) { - + if (was_imported == std::end(imports) and import_requested == std::end(to_import)) { if (not this->meta_info.has_namespace(request.to_fqon())) { // add the request to the pending imports to_import.insert({std::move(request), import.get()}); @@ -174,9 +170,14 @@ void Database::load(const std::string &filename, size_t new_obj_count = 0; // first run: create empty object infos - ast_obj_walk(imports, std::bind(&Database::create_obj_info, - this, &new_obj_count, - _1, _2, _3, _4)); + ast_obj_walk(imports, + std::bind(&Database::create_obj_info, + this, + &new_obj_count, + _1, + _2, + _3, + _4)); std::vector new_objects; new_objects.reserve(new_obj_count); @@ -185,11 +186,15 @@ void Database::load(const std::string &filename, std::unordered_map> obj_children; // second run: fill object infos and its member type infos - ast_obj_walk(imports, std::bind(&Database::create_obj_content, - this, - &new_objects, - &obj_children, - _1, _2, _3, _4)); + ast_obj_walk(imports, + std::bind(&Database::create_obj_content, + this, + &new_objects, + &obj_children, + _1, + _2, + _3, + _4)); // linearize the parents of all new objects this->linearize_new(new_objects); @@ -201,9 +206,14 @@ void Database::load(const std::string &filename, std::vector> objs_in_values; // third run: state value creation, create object members/values - ast_obj_walk(imports, std::bind(&Database::create_obj_state, - this, &objs_in_values, - _1, _2, _3, _4)); + ast_obj_walk(imports, + std::bind(&Database::create_obj_state, + this, + &objs_in_values, + _1, + _2, + _3, + _4)); // verify hierarchy consistency this->check_hierarchy(new_objects, objs_in_values); @@ -221,7 +231,7 @@ void Database::load(const std::string &filename, info->add_children(std::move(children)); } - for (auto loaded: imports) { + for (auto loaded : imports) { this->meta_info.add_namespace(loaded.first); } @@ -234,7 +244,6 @@ void Database::create_obj_info(size_t *counter, const Namespace &, const Namespace &objname, const ASTObject &astobj) { - const std::string &name = astobj.name.get(); // object name must not be an alias @@ -243,14 +252,12 @@ void Database::create_obj_info(size_t *counter, throw NameError{ astobj.name, "object name conflicts with import", - name - }; + name}; } this->meta_info.add_object( objname.to_fqon(), - ObjectInfo{astobj.name} - ); + ObjectInfo{astobj.name, objname}); *counter += 1; } @@ -262,7 +269,6 @@ void Database::create_obj_content(std::vector *new_objs, const Namespace &ns, const Namespace &objname, const ASTObject &astobj) { - fqon_t obj_fqon = objname.to_fqon(); new_objs->push_back(obj_fqon); @@ -294,9 +300,9 @@ void Database::create_obj_content(std::vector *new_objs, auto ins = child_assignments->find(parent_id); if (ins == std::end(*child_assignments)) { ins = child_assignments->emplace( - parent_id, - std::unordered_set{} - ).first; + parent_id, + std::unordered_set{}) + .first; } ins->second.insert(obj_fqon); @@ -307,13 +313,10 @@ void Database::create_obj_content(std::vector *new_objs, this->state->add_object( obj_fqon, std::make_shared( - std::move(object_parents) - ) - ); + std::move(object_parents))); // create member types for (auto &astmember : astobj.members) { - // TODO: the member name requires advanced expansions // for conflict resolving // this explicit naming is not implemented yet @@ -321,8 +324,7 @@ void Database::create_obj_content(std::vector *new_objs, MemberInfo &member_info = info->add_member( astmember.name.str(), - MemberInfo{astmember.name} - ); + MemberInfo{astmember.name}); // it doesn't exist if the user didn't specify it. // it's not set @@ -336,9 +338,8 @@ void Database::create_obj_content(std::vector *new_objs, *astmember.type, scope, objname, - this->meta_info - ), - true // type was defined in the ast -> initial definition + this->meta_info), + true // type was defined in the ast -> initial definition ); } } @@ -358,12 +359,10 @@ void Database::linearize_new(const std::vector &new_objects) { obj_info->set_linearization( linearize_recurse( obj, - [this] (const fqon_t &name) -> const ObjectState & { + [this](const fqon_t &name) -> const ObjectState & { return **this->state->get(name); }, - &seen - ) - ); + &seen)); } } @@ -375,7 +374,6 @@ void Database::find_member(bool skip_first, const std::function &member_found) { - bool finished = false; // if the member is inherited, it can be prefixed with the ID @@ -393,7 +391,6 @@ void Database::find_member(bool skip_first, // member doesn't have type yet. find it. for (auto &obj : search_objs) { - // at the very beginning, we have to skip the object // we want to find the type for. it's the first in the linearization. if (skip_first) { @@ -449,15 +446,12 @@ void Database::find_member(bool skip_first, // recurse into the target. // check if the patch defines the member as well -> error. // otherwise, infer type from patch. - this->find_member(false, member_name, - obj_info->get_linearization(), - *obj_info, member_found); + this->find_member(false, member_name, obj_info->get_linearization(), *obj_info, member_found); } } void Database::resolve_types(const std::vector &new_objects) { - using namespace std::string_literals; // TODO: if inheritance parents are added, @@ -471,8 +465,7 @@ void Database::resolve_types(const std::vector &new_objects) { const auto &linearization = obj_info->get_linearization(); if (unlikely(linearization.size() < 1)) { throw InternalError{ - "Linearization doesn't contain obj itself." - }; + "Linearization doesn't contain obj itself."}; } auto it = std::begin(linearization); @@ -487,8 +480,7 @@ void Database::resolve_types(const std::vector &new_objects) { throw LangError{ obj_info->get_location(), "child patches can't declare a patch target", - {{parent_info->get_location(), "parent that declares the patch"}} - }; + {{parent_info->get_location(), "parent that declares the patch"}}}; } else { // this is patch because of inheritance. @@ -519,13 +511,13 @@ void Database::resolve_types(const std::vector &new_objects) { // start the recursion into the inheritance tree, // which includes the recursion into patch targets. this->find_member( - true, // make sure the object we search the type for isn't checked with itself. - member_id, linearization, *obj_info, - [&member_info, &type_found, &member_id] - (const fqon_t &parent, - const MemberInfo &source_member_info, - const Member *) { - + true, // make sure the object we search the type for isn't checked with itself. + member_id, + linearization, + *obj_info, + [&member_info, &type_found, &member_id](const fqon_t &parent, + const MemberInfo &source_member_info, + const Member *) { if (source_member_info.is_initial_def()) { const std::shared_ptr &new_type = source_member_info.get_type(); @@ -542,9 +534,8 @@ void Database::resolve_types(const std::vector &new_objects) { throw LangError{ member_info.get_location(), ("parent '"s + parent - + "' already defines type of '" + member_id + "'"), - {{source_member_info.get_location(), "parent that declares the member"}} - }; + + "' already defines type of '" + member_id + "'"), + {{source_member_info.get_location(), "parent that declares the member"}}}; } type_found = true; @@ -555,15 +546,13 @@ void Database::resolve_types(const std::vector &new_objects) { // we need to traverse all members and never stop early. return false; - } - ); + }); if (unlikely(not type_found)) { throw TypeError{ member_info.get_location(), "could not infer type of '"s + member_id - + "' from parents or patch target" - }; + + "' from parents or patch target"}; } } } @@ -572,10 +561,9 @@ void Database::resolve_types(const std::vector &new_objects) { void Database::create_obj_state(std::vector> *objs_in_values, const NamespaceFinder &scope, - const Namespace &/*containing namespace*/, + const Namespace & /*containing namespace*/, const Namespace &objname, const ASTObject &astobj) { - using namespace std::string_literals; if (astobj.members.size() == 0) { @@ -594,7 +582,6 @@ void Database::create_obj_state(std::vector> *objs_i // create member values for (auto &astmember : astobj.members) { - // member has no value if (not astmember.value.has_value()) { continue; @@ -620,42 +607,40 @@ void Database::create_obj_state(std::vector> *objs_i throw InternalError{"member has value but no operator"}; } - // create the member with operation, type and value - Member &new_member = members.emplace( - memberid, - Member{ - 0, // TODO: get override depth from AST (the @-count) - operation, - *member_type, - Value::from_ast( - *member_type, *astmember.value, - // function to determine object names used in values: - [&scope, &objname, this, &objs_in_values] - (const Type &target_type, const IDToken &token) -> fqon_t { - // find the desired object in the scope of the object - fqon_t obj_id = scope.find(objname, token, this->meta_info); - - if (not target_type.has_modifier(modifier_t::ABSTRACT)) { - // later we have to check if this object can be used as value - // i.e. has all members with value. - objs_in_values->push_back({obj_id, Location{token}}); - } + // function to determine object names used in values: + auto get_fqon_func = [&scope, &objname, this, &objs_in_values](const Type &target_type, const IDToken &token) -> fqon_t { + // find the desired object in the scope of the object + fqon_t obj_id = scope.find(objname, token, this->meta_info); - return obj_id; - }, - // function to retrieve an object's linearization - [this] - (const fqon_t &fqon) -> std::vector { - const ObjectInfo *obj_info = this->meta_info.get_object(fqon); - if (unlikely(obj_info == nullptr)) { - throw InternalError{"object info could not be retrieved"}; - } + if (not target_type.has_modifier(modifier_t::ABSTRACT)) { + // later we have to check if this object can be used as value + // i.e. has all members with value. + objs_in_values->push_back({obj_id, Location{token}}); + } - return obj_info->get_linearization(); - } - ) + return obj_id; + }; + + // function to retrieve an object's linearization + auto get_obj_lin_func = [this](const fqon_t &fqon) -> std::vector { + const ObjectInfo *obj_info = this->meta_info.get_object(fqon); + if (unlikely(obj_info == nullptr)) { + throw InternalError{"object info could not be retrieved"}; } - ).first->second; + + return obj_info->get_linearization(); + }; + + // create the member with operation, type and value + Member &new_member = members.emplace( + memberid, + Member{ + 0, // TODO: get override depth from AST (the @-count) + operation, + *member_type, + Value::from_ast( + *member_type, *astmember.value, get_fqon_func, get_obj_lin_func)}) + .first->second; // validate type + operator + value work together @@ -668,30 +653,21 @@ void Database::create_obj_state(std::vector> *objs_i throw TypeError{ astmember.value->values[0].get_start_location(), "member type "s - + member_type->str() - + " can't be used with value of type " - + new_value.get_type().str() - + ": invalid operator " - + op_to_string(operation) - + " because member type " - + ( - allowed_ops.size() > 0 - ? - " only allows operations:\n'"s - + util::strjoin( - ", ", allowed_ops, - [] (const nyan_op &op) { - return op_to_string(op); - } - ) - + "' " - : - " allows no operations " - ) - + "for value '" - + new_value.str() - + "'" - }; + + member_type->str() + + " can't be used with value of type " + + new_value.get_type().str() + + ": invalid operator " + + op_to_string(operation) + + " because member type " + + (allowed_ops.size() > 0 ? + " only allows operations:\n'"s + + util::strjoin( + ", ", allowed_ops, [](const nyan_op &op) { + return op_to_string(op); + }) + + "' " : + " allows no operations ") + + "for value '" + new_value.str() + "'"}; } } @@ -704,7 +680,6 @@ void Database::check_hierarchy(const std::vector &new_objs, using namespace std::string_literals; for (auto &obj : new_objs) { - ObjectInfo *obj_info = this->meta_info.get_object(obj); ObjectState *obj_state = this->state->get(obj)->get(); if (unlikely(obj_info == nullptr)) { @@ -719,8 +694,7 @@ void Database::check_hierarchy(const std::vector &new_objs, if (unlikely(not obj_info->is_patch())) { throw LangError{ obj_info->get_location(), - "Inheritance additions can only be done in patches." - }; + "Inheritance additions can only be done in patches."}; } } @@ -732,11 +706,11 @@ void Database::check_hierarchy(const std::vector &new_objs, bool other_op = false; this->find_member( - false, it.first, linearization, *obj_info, - [&assign_ok, &other_op] - (const fqon_t &, - const MemberInfo &, - const Member *member) { + false, + it.first, + linearization, + *obj_info, + [&assign_ok, &other_op](const fqon_t &, const MemberInfo &, const Member *member) { // member has no value if (member == nullptr) { return false; @@ -755,15 +729,13 @@ void Database::check_hierarchy(const std::vector &new_objs, throw InternalError{"member has invalid operator"}; } return false; - } - ); + }); if (unlikely(other_op and not assign_ok)) { const MemberInfo *member_info = obj_info->get_member(it.first); throw LangError{ member_info->get_location(), - "this member was never assigned a value." - }; + "this member was never assigned a value."}; } } @@ -852,8 +824,7 @@ void Database::check_hierarchy(const std::vector &new_objs, throw TypeError{ loc, "this object has members without values: "s - + util::strjoin(", ", pending_members) - }; + + util::strjoin(", ", pending_members)}; } } diff --git a/nyan/database.h b/nyan/database.h index ca50754..378a74d 100644 --- a/nyan/database.h +++ b/nyan/database.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #pragma once #include @@ -22,7 +22,6 @@ class State; class View; - /** * Be in a namespace, look up an alias, and get the original namespace. * namespace => {alias => original namespace} @@ -92,13 +91,12 @@ class Database : public std::enable_shared_from_this { } protected: - /** * Create the metadata information ObjectInfo for an object. * * @param counter Increments after ObjectInfo has been created. Used to count the created objects. * @param current_file NamespaceFinder to check object naming conflicts. - * @param ns Namespace the object is in? + * @param ns Namespace of the object. * @param objname Namespace created by the object. * @param astobj Parsed object data from the AST. */ @@ -107,8 +105,7 @@ class Database : public std::enable_shared_from_this { const NamespaceFinder ¤t_file, const Namespace &ns, const Namespace &objname, - const ASTObject &astobj - ); + const ASTObject &astobj); /** * Add an object's content to the metadata information ObjectInfo of @@ -128,8 +125,7 @@ class Database : public std::enable_shared_from_this { const NamespaceFinder ¤t_file, const Namespace &ns, const Namespace &objname, - const ASTObject &astobj - ); + const ASTObject &astobj); /** * Create the initial ObjectState of an object. @@ -145,8 +141,7 @@ class Database : public std::enable_shared_from_this { const NamespaceFinder ¤t_file, const Namespace &ns, const Namespace &objname, - const ASTObject &astobj - ); + const ASTObject &astobj); /** * Linearizes the parents of all given objects. @@ -169,8 +164,7 @@ class Database : public std::enable_shared_from_this { const memberid_t &member_id, const std::vector &search_objs, const ObjectInfo &obj_info, - const std::function &member_found - ); + const std::function &member_found); /** * Resolves types that are inherited by objects and members. This will diff --git a/nyan/id_token.cpp b/nyan/id_token.cpp index 74450c0..aa94979 100644 --- a/nyan/id_token.cpp +++ b/nyan/id_token.cpp @@ -7,13 +7,13 @@ #include "compiler.h" #include "error.h" #include "location.h" +#include "util.h" namespace nyan { IDToken::IDToken(const Token &first, TokenStream &tokens) { - this->ids.push_back(first); auto token = tokens.next(); @@ -34,10 +34,9 @@ std::string IDToken::str() const { return util::strjoin( ".", this->ids, - [] (const Token &tok) -> auto& { + [](const Token &tok) -> auto & { return tok.get(); - } - ); + }); } @@ -59,8 +58,7 @@ token_type IDToken::get_type() const { const Location &IDToken::get_start_location() const { if (unlikely(not this->exists())) { throw InternalError{ - "this IDToken doesn't exist, but you queried its location" - }; + "this IDToken doesn't exist, but you queried its location"}; } return this->ids.at(0).location; @@ -98,4 +96,12 @@ const std::string &IDToken::get_first() const { return this->ids[0].get(); } +fqon_t IDToken::to_fqon() const { + return util::strjoin(".", + this->ids, + [](const auto &in) -> const std::string & { + return in.get(); + }); +} + } // namespace nyan diff --git a/nyan/id_token.h b/nyan/id_token.h index 5655a60..8b258d2 100644 --- a/nyan/id_token.h +++ b/nyan/id_token.h @@ -71,6 +71,13 @@ class IDToken { */ const std::string &get_first() const; + /** + * Get an fqon from an IDToken. + * + * @return Fqon for IDToken. + */ + fqon_t to_fqon() const; + protected: /** * List of IDs defining the IDToken. diff --git a/nyan/namespace.cpp b/nyan/namespace.cpp index c7ed081..cc28bb3 100644 --- a/nyan/namespace.cpp +++ b/nyan/namespace.cpp @@ -13,43 +13,58 @@ namespace nyan { static const std::string extension = ".nyan"; - -Namespace::Namespace(const fqon_t &name) - : - components{util::split(name, '.')} {} - - -Namespace::Namespace(const IDToken &token) { - this->components.reserve(token.get_components().size()); - for (auto &tok : token.get_components()) { - this->components.push_back(tok.get()); +Namespace::Namespace(std::vector &&dir_components, + std::string &&filename, + std::vector &&obj_components) : + dir_components{std::move(dir_components)}, + filename{std::move(filename)}, + obj_components{std::move(obj_components)} { + // If we have objects, there needs to be a file that contains them. + if (not this->obj_components.empty() and this->filename.empty()) [[unlikely]] { + throw InternalError{"Namespace with object components requires filename component"}; } } +Namespace::Namespace(const std::vector &dir_components, + const std::string &filename, + const std::vector &obj_components) : + dir_components{dir_components}, + filename{filename}, + obj_components{obj_components} { +} -Namespace::Namespace(const Namespace &other, const std::string &addend) - : +Namespace::Namespace(const Namespace &other, + const std::string &obj_addend) : Namespace{other} { - - for (const auto &component : util::split(addend, '.')) { - this->components.push_back(component); + if (this->is_dir()) { + throw InternalError{"Cannot add object components to directory namespace"}; } -} + this->obj_components.push_back(obj_addend); +} void Namespace::pop_last() { if (this->empty()) { throw InternalError{"popping from empty namespace"}; } - this->components.pop_back(); -} + if (not this->obj_components.empty()) { + this->obj_components.pop_back(); + } + else if (not this->filename.empty()) { + this->filename.clear(); + } + else { + this->dir_components.pop_back(); + } +} bool Namespace::empty() const { - return this->components.empty(); + return this->dir_components.empty() + and this->filename.empty() + and this->obj_components.empty(); } - fqon_t Namespace::combine(const IDToken &name, size_t skip) const { Namespace combined{*this}; @@ -57,30 +72,101 @@ fqon_t Namespace::combine(const IDToken &name, size_t skip) const { for (auto &part : name.get_components()) { if (skip > 0) { skip -= 1; - } else { - combined.components.push_back(part.get()); + } + else { + combined.obj_components.push_back(part.get()); } } return combined.to_fqon(); } +bool Namespace::is_dir() const { + return (this->filename.empty() and this->obj_components.empty()); +} + +bool Namespace::is_file() const { + return not this->filename.empty() and this->obj_components.empty(); +} + +bool Namespace::is_obj() const { + return not this->obj_components.empty(); +} + +bool Namespace::is_nested_obj() const { + return this->obj_components.size() > 1; +} + +const std::vector &Namespace::get_dir_components() const { + return this->dir_components; +} + +const std::string &Namespace::get_filename() const { + return this->filename; +} + +const std::vector &Namespace::get_obj_components() const { + return this->obj_components; +} + +std::string Namespace::to_dirpath() const { + return util::strjoin( + "/", + this->dir_components, + [](const auto &in) -> const std::string & { + return in; + }); +} -// TODO: nested objects have components within one file. -// separating via / is wrong for them. -std::string Namespace::to_filename() const { +std::string Namespace::to_filepath() const { std::string ret = util::strjoin( "/", - this->components, - [] (const auto &in) -> const std::string& { + this->dir_components, + [](const auto &in) -> const std::string & { return in; + }); + + if (not this->filename.empty()) { + ret += "/" + this->filename + extension; + } + + return ret; +} + +fqon_t Namespace::to_fqon() const { + std::string ret; + if (not this->dir_components.empty()) { + ret += util::strjoin( + ".", + this->dir_components, + [](const auto &in) -> const std::string & { + return in; + }); + } + + if (not this->filename.empty()) { + if (not ret.empty()) { + ret += "."; } - ); + ret += this->filename; + } + + if (not this->obj_components.empty()) { + ret += "." + util::strjoin(".", this->obj_components); + } - ret += extension; return ret; } +std::string Namespace::str() const { + return this->to_fqon(); +} + +bool Namespace::operator==(const Namespace &other) const { + return this->dir_components == other.dir_components + and this->filename == other.filename + and this->obj_components == other.obj_components; +} Namespace Namespace::from_filename(const std::string &filename) { if (not util::ends_with(filename, extension)) { @@ -94,48 +180,23 @@ Namespace Namespace::from_filename(const std::string &filename) { throw APIError{"there's too many dots in the path"}; } - // sanitize the filename + // strip file extension + std::string namespace_name = filename.substr(0, filename.size() - extension.size()); + + // sanitize the filename (remove empty components) // TODO: Do this via a file API - std::string namespace_name; - char prev_char; - char cur_char; - // condition strips off file extension - for (size_t i = 0; i < filename.size() - extension.size(); ++i) { - cur_char = filename[i]; - - // slashes get replaced with dots - if (cur_char == '/') { - // strip multiple slashes - if (prev_char == '/') { - continue; - } - namespace_name += '.'; + std::vector components{}; + for (auto &comp : util::split(namespace_name, '/')) { + if (not comp.empty()) { + components.push_back(comp); } - // normal chars get copied - else { - namespace_name += cur_char; - } - - prev_char = cur_char; } - // the fqon_t constructor. - return Namespace{namespace_name}; -} - - -fqon_t Namespace::to_fqon() const { - return util::strjoin(".", this->components); -} + std::vector dir_components{components.begin(), components.end() - 1}; + std::string file_component = components.back(); - -std::string Namespace::str() const { - return this->to_fqon(); -} - - -bool Namespace::operator ==(const Namespace &other) const { - return this->components == other.components; + // the fqon_t constructor. + return Namespace{std::move(dir_components), std::move(file_component)}; } } // namespace nyan @@ -143,11 +204,18 @@ bool Namespace::operator ==(const Namespace &other) const { namespace std { -size_t hash::operator ()(const nyan::Namespace &ns) const { +size_t hash::operator()(const nyan::Namespace &ns) const { size_t ret = 0; - for (auto &component : ns.components) { + for (auto &component : ns.dir_components) { + ret = nyan::util::hash_combine(ret, std::hash{}(component)); + } + + ret = nyan::util::hash_combine(ret, std::hash{}(ns.filename)); + + for (auto &component : ns.obj_components) { ret = nyan::util::hash_combine(ret, std::hash{}(component)); } + return ret; } diff --git a/nyan/namespace.h b/nyan/namespace.h index 241fd27..f752034 100644 --- a/nyan/namespace.h +++ b/nyan/namespace.h @@ -1,4 +1,4 @@ -// Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #pragma once #include @@ -11,60 +11,132 @@ namespace nyan { class IDToken; - +/** + * Identifier of a namespace, i.e. a directory, filename, or (nested) object. + */ class Namespace { friend struct std::hash; public: - explicit Namespace(const fqon_t &token); - explicit Namespace(const IDToken &token); - Namespace(const Namespace &other, const std::string &addend); + /** + * Create a namespace identifier. + * + * @param dir_components Directory path components. + * @param filename Filename component. + * @param obj_components Object components. + */ + Namespace(std::vector &&dir_components = {}, + std::string &&filename = "", + std::vector &&obj_components = {}); + Namespace(const std::vector &dir_components = {}, + const std::string &filename = "", + const std::vector &obj_components = {}); + Namespace(const Namespace &other, const std::string &obj_addend); - virtual ~Namespace() = default; + ~Namespace() = default; + /** + * Pop the last component from the namespace ID. + */ void pop_last(); + + /** + * Check if the namespace ID is empty. + * + * @return true if the namespace ID is empty, else false. + */ bool empty() const; /** - * Append the given object/member reference to the namespace identifier to - * get its identifier. + * Append the given object name to the namespace identifier and get + * the resulting identifier * - * @param name IDToken with an object/member reference. - * @param skip Number of components at the start of @p name to be skipped. + * @param name IDToken with an object reference. + * @param skip Number of components at the start of \p name to be skipped. * - * @return Identifier of the object/member. + * @return fqon of the object. */ - fqon_t combine(const IDToken &name, size_t skip=0) const; + fqon_t combine(const IDToken &name, size_t skip = 0) const; /** - * Get a (relative) path to a filename for the namespace. + * Check if the namespace ID refers to a directory. * - * @return String representation of the path. Uses '/' as path - * component separator. + * @return true if the namespace ID is a directory, else false. */ - std::string to_filename() const; + bool is_dir() const; /** - * Create a namespace from a given filename. Performs a sanity - * check on the filename. + * Check if the namespace ID refers to a file. * - * @param filename Name of a file, including the extension. + * @return true if the namespace ID is a file, else false. + */ + bool is_file() const; + + /** + * Check if the namespace ID refers to a nyan object. * - * @return Namespace for the filename. + * @return true if the namespace ID is an object, else false. */ - static Namespace from_filename(const std::string &filename); + bool is_obj() const; + + /** + * Check if the namespace ID refers to a nested nyan object. + * + * @return true if the namespace ID is a nested object, else false. + */ + bool is_nested_obj() const; /** - * Get the identifier of the namespace. + * Get the directory path components of the namespace ID. * - * @return Identifier of the namespace. + * @return Directory path components. + */ + const std::vector &get_dir_components() const; + + /** + * Get the filename component of the namespace ID. + * + * @return Filename component. + */ + const std::string &get_filename() const; + + /** + * Get the object components of the namespace ID. + * + * @return Object components. + */ + const std::vector &get_obj_components() const; + + /** + * Get the directory path from the directory components inside the + * namespace. + * + * @return Directory path from the namespace. + */ + std::string to_dirpath() const; + + /** + * Get the file path from the directory and filename components inside + * the namespace. + * + * If the namespace ID refers to an object, this is the path to the file + * containing the object. + * + * @return File path from the namespace. + */ + std::string to_filepath() const; + + /** + * Get the fqon for this namespace ID. + * + * @return fqon identifier. */ fqon_t to_fqon() const; /** - * Get a string representation of the namespace. + * Get a string representation of this namespace. * - * @return String representation of the namespace. + * @return String representation of this namespace. */ std::string str() const; @@ -73,10 +145,33 @@ class Namespace { * * @return true if the namespaces are equal, else false. */ - bool operator ==(const Namespace &other) const; + bool operator==(const Namespace &other) const; + + /** + * Create a namespace from a given filename. Performs a sanity + * check on the filename. + * + * @param filename Name of a file, including the extension. + * + * @return Namespace for the filename. + */ + static Namespace from_filename(const std::string &filename); -protected: - std::vector components; +private: + /** + * Directory path components. + */ + std::vector dir_components; + + /** + * Filename component. + */ + std::string filename; + + /** + * Object components. + */ + std::vector obj_components; }; } // namespace nyan @@ -85,6 +180,6 @@ class Namespace { namespace std { template <> struct hash { - size_t operator ()(const nyan::Namespace &ns) const; + size_t operator()(const nyan::Namespace &ns) const; }; } // namespace std diff --git a/nyan/namespace_finder.cpp b/nyan/namespace_finder.cpp index d292198..38c698c 100644 --- a/nyan/namespace_finder.cpp +++ b/nyan/namespace_finder.cpp @@ -1,4 +1,4 @@ -// Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. +// Copyright 2017-2023 the nyan authors, LGPLv3+. See copying.md for legal info. #include "namespace_finder.h" @@ -10,8 +10,7 @@ namespace nyan { -NamespaceFinder::NamespaceFinder(AST &&ast) - : +NamespaceFinder::NamespaceFinder(AST &&ast) : ast{std::move(ast)} {} @@ -22,7 +21,6 @@ void NamespaceFinder::add_import(const Namespace &import) { void NamespaceFinder::add_alias(const Token &alias, const Namespace &destination) { - const std::string &search = alias.get(); if (this->aliases.find(search) != std::end(this->aliases)) { @@ -34,8 +32,9 @@ void NamespaceFinder::add_alias(const Token &alias, bool NamespaceFinder::check_conflict(const std::string &name) const { - return (this->aliases.find(name) != std::end(this->aliases) or - this->imports.find(Namespace{name}) != std::end(this->imports)); + return (this->aliases.find(name) != std::end(this->aliases) // aliases + // importing files from the root directory can be a problem too + or this->imports.find(Namespace{std::vector(), name}) != std::end(this->imports)); } @@ -54,14 +53,13 @@ fqon_t NamespaceFinder::expand_alias(const IDToken &name) const { } // no alias found. basically return the input name. - return Namespace{name}.to_fqon(); + return name.to_fqon(); } fqon_t NamespaceFinder::find(const Namespace ¤t, const IDToken &search, const MetaInfo &typedb) const { - if (unlikely(not search.exists())) { throw InternalError{"tried to find namespace for empty id"}; } @@ -101,11 +99,11 @@ const AST &NamespaceFinder::get_ast() const { std::string NamespaceFinder::str() const { std::ostringstream builder; builder << "NamespaceFinder knows:" << std::endl - << "= aliases:" << std::endl; + << "= aliases:" << std::endl; for (auto &it : this->aliases) { builder << " * " << it.first - << " => " << it.second.str() << std::endl; + << " => " << it.second.str() << std::endl; } builder << "= imports:" << std::endl; diff --git a/nyan/namespace_finder.h b/nyan/namespace_finder.h index 3d44c11..1bdc03b 100644 --- a/nyan/namespace_finder.h +++ b/nyan/namespace_finder.h @@ -43,7 +43,7 @@ class NamespaceFinder { /** * Check if a name conflicts wth other names in the namespace, i.e. the - * name is already used. + * name is already used by an alias or an import. * * @return true if the name is already used, else false. */ diff --git a/nyan/object_info.cpp b/nyan/object_info.cpp index 167824a..d53a2a2 100644 --- a/nyan/object_info.cpp +++ b/nyan/object_info.cpp @@ -5,16 +5,17 @@ #include #include "lang_error.h" -#include "util.h" #include "patch_info.h" #include "state.h" +#include "util.h" namespace nyan { -ObjectInfo::ObjectInfo(const Location &location) - : +ObjectInfo::ObjectInfo(const Location &location, + const Namespace &ns) : location{location}, + ns{ns}, initial_patch{false} {} @@ -22,10 +23,13 @@ const Location &ObjectInfo::get_location() const { return this->location; } +const Namespace &ObjectInfo::get_namespace() const { + return this->ns; +} + MemberInfo &ObjectInfo::add_member(const memberid_t &name, MemberInfo &&member) { - // copy the location so it's still valid if the insert fails. Location loc = member.get_location(); @@ -34,8 +38,7 @@ MemberInfo &ObjectInfo::add_member(const memberid_t &name, throw LangError{ loc, "member already in this object", - {{ret.first->second.get_location(), "first defined here"}} - }; + {{ret.first->second.get_location(), "first defined here"}}}; } return ret.first->second; @@ -135,7 +138,8 @@ std::string ObjectInfo::str() const { for (auto &change : this->inheritance_change) { if (not liststart) { builder << ", "; - } else { + } + else { liststart = false; } diff --git a/nyan/object_info.h b/nyan/object_info.h index e86cb2d..81287cb 100644 --- a/nyan/object_info.h +++ b/nyan/object_info.h @@ -9,6 +9,7 @@ #include "inheritance_change.h" #include "location.h" #include "member_info.h" +#include "namespace.h" #include "ops.h" @@ -26,7 +27,8 @@ class ObjectInfo { public: using member_info_t = std::unordered_map; - explicit ObjectInfo(const Location &location); + explicit ObjectInfo(const Location &location, + const Namespace &ns); ~ObjectInfo() = default; /** @@ -36,6 +38,13 @@ class ObjectInfo { */ const Location &get_location() const; + /** + * Get the namespace of the object. + * + * @return Object namespace. + */ + const Namespace &get_namespace() const; + /** * Add metadata information for a member. * @@ -168,6 +177,11 @@ class ObjectInfo { */ Location location; + /** + * Namespace of the object. + */ + Namespace ns; + /** * Determines whether this object was defined as a patch. * It is one when it was declared with . From fafc2ced48d4916d3079c748930b044ba78d8bdc Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 2 Jul 2023 17:16:26 +0200 Subject: [PATCH 7/9] readme: update ci information and remove irc channel --- README.md | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index ac301eb..524bd7f 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ properly represent the enormous complexity of storing the data for [openage](htt The main focus is *readability* and *moddability*. [![github stars](https://img.shields.io/github/stars/SFTtech/nyan.svg)](https://github.com/SFTtech/nyan/stargazers) -[![#sfttech on Freenode](https://img.shields.io/badge/chat-on%20freenode-brightgreen)](https://webchat.freenode.net/?channels=sfttech) +[![#sfttech on matrix.org](/doc/assets/matrix.svg)](https://matrix.to/#/#sfttech:matrix.org) The foundation of **nyan**: @@ -123,19 +123,21 @@ Current State of the Project ---------------------------- `nyan` is fully functional and can be used in your project. +Please make us aware of your needs/experiences in our chat! -There's some features left to implement, but those only -cover special use cases: +We try to keep the API stable, but there's still some unknowns and need-to-change features. -* [ ] Inverse patch generation -* [ ] Subobject set specializations -* [ ] Callback trigger when a value changes -* [ ] Member name qualifications for name conflict resolving +Please submit bugs and feature requests (and patches) on [GitHub](https://github.com/SFTtech/nyan)! Dependencies, Building and Running ---------------------------------- +Operating System | Build status +--------------------|-------------- +Debian Sid | [Todo: Kevin #11](https://github.com/SFTtech/kevin/issues/11) + + - How do I get this to install on my box? - See [doc/building.md](doc/building.md). @@ -148,12 +150,6 @@ All of those observations are intended, not bugs. To get rid of them, recompile with `--dont-segfault --shut-up --new-girlfriend`. -Operating System | Build status ---------------------|-------------- -Debian Sid | [Todo: Kevin #11](https://github.com/SFTtech/kevin/issues/11) -MacOSX 10.14 | [![Build Status](https://travis-ci.org/SFTtech/nyan.svg?branch=master)](https://travis-ci.org/SFTtech/nyan) -Windows 10 - x64 | [![Build status](https://ci.appveyor.com/api/projects/status/6t1yonx5fu7dejs0/branch/master?svg=true)](https://ci.appveyor.com/project/simonsan/nyan-c53id/branch/master) - If this still does not help, try the [contact section](#contact) or the [bug tracker](https://github.com/SFTtech/nyan/issues). @@ -176,10 +172,9 @@ Contact ------- If you have the desire to perform semi-human interaction, -join our **Matrix** or **IRC** chatroom! +join our **Matrix** chatroom! * [`#sfttech:matrix.org`](https://riot.im/app/#/room/#sfttech:matrix.org) -* [`irc.freenode.net #sfttech`](https://webchat.freenode.net/?channels=sfttech) For ideas, problems, ..., use the [issue tracker](https://github.com/SFTtech/nyan/issues)! From c780d817ab646c1e9f4013f39398b7ae57af6db5 Mon Sep 17 00:00:00 2001 From: Jonas Jelten Date: Sun, 2 Jul 2023 17:17:11 +0200 Subject: [PATCH 8/9] namespace: use auto-string conversion for path generation --- nyan/namespace.cpp | 12 ++++-------- nyan/util.h | 7 +++---- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/nyan/namespace.cpp b/nyan/namespace.cpp index cc28bb3..a6accde 100644 --- a/nyan/namespace.cpp +++ b/nyan/namespace.cpp @@ -112,19 +112,15 @@ const std::vector &Namespace::get_obj_components() const { std::string Namespace::to_dirpath() const { return util::strjoin( "/", - this->dir_components, - [](const auto &in) -> const std::string & { - return in; - }); + this->dir_components + ); } std::string Namespace::to_filepath() const { std::string ret = util::strjoin( "/", - this->dir_components, - [](const auto &in) -> const std::string & { - return in; - }); + this->dir_components + ); if (not this->filename.empty()) { ret += "/" + this->filename + extension; diff --git a/nyan/util.h b/nyan/util.h index 9dc49e5..c3dd67f 100644 --- a/nyan/util.h +++ b/nyan/util.h @@ -117,14 +117,13 @@ strjoin( func=&stream_container_elem ) { - bool delim_active = false; - for (auto &entry : container) { - if (delim_active) { + for (bool use_delim = false; auto &entry : container) { + if (use_delim) { builder << delim; } func(builder, entry); - delim_active = true; + use_delim = true; } return builder; From bdc752b04e741741ebe245bdb4ccdc0ffafa6359 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sun, 2 Jul 2023 18:35:31 +0200 Subject: [PATCH 9/9] release: Version 0.3.0. --- nyan_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nyan_version b/nyan_version index 0ea3a94..0d91a54 100644 --- a/nyan_version +++ b/nyan_version @@ -1 +1 @@ -0.2.0 +0.3.0