├── nyan_version ├── doc ├── format_version ├── building.md └── README.md ├── test ├── imported.nyan ├── stuff │ └── file.nyan ├── diamond.nyan └── test.nyan ├── nyan ├── util │ ├── flags.cpp │ └── flags.h ├── value │ ├── container_types.cpp │ ├── set_base.cpp │ ├── container.cpp │ ├── container_types.h │ ├── none.h │ ├── file.h │ ├── object.h │ ├── set.h │ ├── boolean.h │ ├── orderedset.h │ ├── text.h │ ├── value_holder.cpp │ ├── none.cpp │ ├── object.cpp │ ├── text.cpp │ ├── file.cpp │ ├── set.cpp │ ├── orderedset.cpp │ ├── boolean.cpp │ ├── value_holder.h │ ├── dict.h │ └── number.h ├── object_notifier_types.cpp ├── config.cpp ├── curve.cpp ├── concept.cpp ├── datastructure │ └── orderedset.cpp ├── inheritance_change.cpp ├── patch_info.cpp ├── lexer │ ├── lexer.cpp │ ├── lexer.h │ ├── bracket.h │ ├── bracket.cpp │ ├── impl.h │ └── flex.lpp ├── concept.h ├── object_notifier_types.h ├── patch_info.h ├── nyan.h ├── compiler.h ├── object_notifier.cpp ├── inheritance_change.h ├── object_history.cpp ├── token_stream.cpp ├── config.h ├── member_info.cpp ├── nyan_tool.h ├── ops.cpp ├── change_tracker.cpp ├── file.cpp ├── api_error.cpp ├── parser.cpp ├── object_history.h ├── token_stream.h ├── object_notifier.h ├── c3.h ├── location.cpp ├── value_token.h ├── token.cpp ├── change_tracker.h ├── api_error.h ├── id_token.h ├── member.h ├── lang_error.h ├── meta_info.cpp ├── file.h ├── member_info.h ├── value_token.cpp ├── id_token.cpp ├── namespace_finder.h ├── parser.h ├── location.h ├── lang_error.cpp ├── object_state.h ├── state.h ├── ops.h ├── member.cpp ├── state.cpp ├── namespace_finder.cpp ├── meta_info.h ├── view.h ├── object_state.cpp ├── CMakeLists.txt ├── curve.h ├── object_info.cpp ├── state_history.h ├── basic_type.cpp ├── namespace.h ├── token.h └── state_history.cpp ├── kevinfile ├── extra └── syntax_highlighting │ ├── README.md │ └── kate │ └── nyan.xml ├── .travis.yml ├── .gitignore ├── copying.md └── .clang-format /nyan_version: -------------------------------------------------------------------------------- 1 | 0.3.0 2 | -------------------------------------------------------------------------------- /doc/format_version: -------------------------------------------------------------------------------- 1 | 2 2 | -------------------------------------------------------------------------------- /test/imported.nyan: -------------------------------------------------------------------------------- 1 | !version 1 2 | 3 | ImportedObject(): 4 | it_works : int = 42 5 | -------------------------------------------------------------------------------- /nyan/util/flags.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "flags.h" 4 | -------------------------------------------------------------------------------- /nyan/value/container_types.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "container_types.h" 4 | -------------------------------------------------------------------------------- /test/stuff/file.nyan: -------------------------------------------------------------------------------- 1 | !version 1 2 | 3 | MoreData(): 4 | pass 5 | 6 | import imported 7 | import imported as bla 8 | import stuff.file as self 9 | -------------------------------------------------------------------------------- /nyan/object_notifier_types.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2019 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "object_notifier_types.h" 4 | -------------------------------------------------------------------------------- /nyan/config.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2017 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "config.h" 4 | 5 | namespace nyan { 6 | } // namespace nyan 7 | -------------------------------------------------------------------------------- /nyan/curve.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "curve.h" 4 | 5 | 6 | namespace nyan { 7 | 8 | } // namespace nyan 9 | -------------------------------------------------------------------------------- /nyan/value/set_base.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2017 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "set_base.h" 4 | 5 | 6 | namespace nyan { 7 | 8 | } // namespace nyan 9 | -------------------------------------------------------------------------------- /nyan/value/container.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2017 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "container.h" 4 | 5 | 6 | namespace nyan { 7 | 8 | 9 | } // namespace nyan 10 | -------------------------------------------------------------------------------- /nyan/concept.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2025-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "concept.h" 4 | 5 | 6 | namespace nyan { 7 | 8 | // this file is intentionally empty 9 | 10 | } 11 | -------------------------------------------------------------------------------- /nyan/datastructure/orderedset.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "orderedset.h" 4 | 5 | namespace nyan::datastructure { 6 | 7 | 8 | } // namespace nyan::datastructure 9 | -------------------------------------------------------------------------------- /test/diamond.nyan: -------------------------------------------------------------------------------- 1 | !version 1 2 | 3 | a(): 4 | member : int = 0 5 | 6 | b(a): 7 | member += 1 8 | 9 | c(a): 10 | member += 2 11 | 12 | d(b, c): 13 | member += 4 14 | 15 | 16 | pa(): 17 | member += 8 18 | 19 | pb(): 20 | newmember : int = 0 21 | member += 16 22 | -------------------------------------------------------------------------------- /kevinfile: -------------------------------------------------------------------------------- 1 | # nyan kevin control file 2 | # 3 | # kevin CI source code: https://github.com/SFTtech/kevin 4 | # 5 | 6 | configure: 7 | - env: compiler=g++ (? if job == "debian" ?) 8 | - env: compiler=clang++ (? if job == "debian-clang" ?) 9 | mkdir build 10 | cd build && cmake .. -DCMAKE_CXX_COMPILER=${compiler} 11 | 12 | build: configure 13 | - cwd: build/ 14 | make -j$(nproc) 15 | 16 | test: build 17 | - cwd: build 18 | ./nyan/nyancat --test-parser -f ../test/test.nyan 19 | -------------------------------------------------------------------------------- /nyan/inheritance_change.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "inheritance_change.h" 4 | 5 | namespace nyan { 6 | 7 | InheritanceChange::InheritanceChange(inher_change_t type, fqon_t &&target) : 8 | type{type}, 9 | target{std::move(target)} {} 10 | 11 | 12 | inher_change_t InheritanceChange::get_type() const { 13 | return this->type; 14 | } 15 | 16 | 17 | const fqon_t &InheritanceChange::get_target() const { 18 | return this->target; 19 | } 20 | 21 | } // namespace nyan 22 | -------------------------------------------------------------------------------- /extra/syntax_highlighting/README.md: -------------------------------------------------------------------------------- 1 | # Syntax highlighting 2 | 3 | Syntax highlighting for nyan can be added to text editors and IDEs of your choice. 4 | 5 | ## Kate 6 | 7 | * copy `kate/nyan.xml` into the folder `~/.local/share/org.kde.syntax-highlighting/syntax` 8 | * restart Kate 9 | 10 | ## VS Code 11 | 12 | * Download our extension from the [VS Code marketplace](https://marketplace.visualstudio.com/items?itemName=SFTtech.nyan) 13 | 14 | The extension is maintained in its own Github repository [nyan-vscode](https://github.com/SFTtech/nyan-vscode). 15 | -------------------------------------------------------------------------------- /nyan/patch_info.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "patch_info.h" 4 | 5 | #include 6 | 7 | #include "error.h" 8 | #include "util.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | PatchInfo::PatchInfo(fqon_t &&target) : 14 | target{std::move(target)} {} 15 | 16 | 17 | const fqon_t &PatchInfo::get_target() const { 18 | return this->target; 19 | } 20 | 21 | 22 | std::string PatchInfo::str() const { 23 | std::ostringstream builder; 24 | 25 | builder << "<" << this->target << ">"; 26 | return builder.str(); 27 | } 28 | 29 | } // namespace nyan 30 | -------------------------------------------------------------------------------- /nyan/value/container_types.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "../datastructure/orderedset.h" 7 | #include "value_holder.h" 8 | 9 | 10 | namespace nyan { 11 | 12 | /** datatype used for (unordered) set storage */ 13 | using set_t = std::unordered_set; 14 | 15 | 16 | /** datatype used for ordered set storage */ 17 | using ordered_set_t = datastructure::OrderedSet; 18 | 19 | 20 | /** datatype used for dict storage */ 21 | using dict_t = std::unordered_map; 22 | 23 | } // namespace nyan 24 | -------------------------------------------------------------------------------- /nyan/lexer/lexer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "lexer.h" 4 | 5 | #include "impl.h" 6 | 7 | 8 | namespace nyan { 9 | 10 | Lexer::Lexer(const std::shared_ptr &file) : 11 | impl{std::make_unique(file)} { 12 | } 13 | 14 | Lexer::~Lexer() = default; 15 | 16 | // Return the token generated from the implementation. 17 | Token Lexer::get_next_token() { 18 | return this->impl->generate_token(); 19 | } 20 | 21 | 22 | LexerError::LexerError(const Location &location, 23 | const std::string &msg) : 24 | LangError{location, msg} {} 25 | 26 | } // namespace nyan 27 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | os: osx 2 | osx_image: xcode10.2 3 | language: cpp 4 | script: 5 | - brew update-reset && brew update 6 | - brew upgrade cmake 7 | - brew install flex 8 | - brew install make 9 | - brew install -cc=clang llvm@8 10 | - export PATH="/usr/local/opt/llvm@8/bin:$PATH:/usr/local/lib:/usr/local/opt/llvm/bin" 11 | - export PKG_CONFIG_PATH="$PKG_CONFIG_PATH:/usr/local/lib/pkgconfig:/usr/local/lib" 12 | - mkdir build && cd build 13 | - cmake -DCMAKE_CXX_COMPILER="clang++" .. 14 | - make && make test 15 | before_cache: 16 | - brew cleanup 17 | cache: 18 | directories: 19 | - /usr/local/Homebrew 20 | - $HOME/Library/Caches/Homebrew 21 | -------------------------------------------------------------------------------- /nyan/concept.h: -------------------------------------------------------------------------------- 1 | // Copyright 2025-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #pragma once 4 | 5 | #include 6 | 7 | 8 | namespace nyan { 9 | 10 | class Value; 11 | class Object; 12 | 13 | /** 14 | * Type that is a nyan value. 15 | */ 16 | template 17 | concept ValueLike = std::derived_from; 18 | 19 | /** 20 | * Type that is either a nyan value or object. 21 | * Object is not a value (ObjectValue is), but want to allow an 22 | * overloaded conversion for direct object access. 23 | */ 24 | template 25 | concept ValueOrObjectLike = std::is_same_v or ValueLike; 26 | 27 | } // namespace nyan 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # editor-specific files 2 | \#* 3 | .#* 4 | *~ 5 | .*.swp 6 | 7 | # ELF files 8 | *.o 9 | *.a 10 | *.so 11 | *.lo 12 | *.la 13 | *.Plo 14 | 15 | # python bytecode 16 | *.pyc 17 | *.pyo 18 | __pycache__ 19 | 20 | # workflow 21 | *.orig 22 | 23 | # build system 24 | /bin 25 | /.bin 26 | /build 27 | /compile_commands.json 28 | 29 | # debugging 30 | callgrind.out.* 31 | perf.data* 32 | .gdb_history 33 | 34 | # IDE stuff 35 | /.cache 36 | /.dir-locals.el 37 | 38 | # cmake in-source builds 39 | /DartConfiguration.tcl 40 | /Testing 41 | CTestTestfile.cmake 42 | CMakeFiles 43 | cmake_install.cmake 44 | CMakeCache.txt 45 | Makefile 46 | 47 | # code search 48 | /.agignore 49 | /.globalrc 50 | /GPATH 51 | /GRTAGS 52 | /GTAGS 53 | -------------------------------------------------------------------------------- /nyan/object_notifier_types.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2019 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "config.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | class ObjectState; 14 | 15 | /** 16 | * Object change notification callback type. 17 | * Is called with the change timestamp, 18 | * the updated object name, and the new nyan object state. 19 | * TODO: report the changed members (better as a separate type) 20 | * 21 | * @param t Time of update. 22 | * @param fqon Identifier of the updated object. 23 | * @param state New object state. 24 | */ 25 | using update_cb_t = std::function; 26 | 27 | } // namespace nyan 28 | -------------------------------------------------------------------------------- /nyan/patch_info.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "config.h" 7 | 8 | namespace nyan { 9 | 10 | /** 11 | * Information about a patch. 12 | */ 13 | class PatchInfo { 14 | public: 15 | explicit PatchInfo(fqon_t &&target); 16 | ~PatchInfo() = default; 17 | 18 | /** 19 | * Get the identifier of the patch target. 20 | * 21 | * @return Identifier of the patch target. 22 | */ 23 | const fqon_t &get_target() const; 24 | 25 | /** 26 | * Get the string representation of the metadata information. 27 | * 28 | * @return String representation of the metadata information. 29 | */ 30 | std::string str() const; 31 | 32 | protected: 33 | /** 34 | * Identifier of the patch target. 35 | */ 36 | fqon_t target; 37 | }; 38 | 39 | 40 | } // namespace nyan 41 | -------------------------------------------------------------------------------- /nyan/value/none.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include "value.h" 5 | 6 | 7 | namespace nyan { 8 | 9 | 10 | /** 11 | * Nyan value to store None (basically a "no value" placeholder). 12 | */ 13 | class None : public Value { 14 | public: 15 | None(); 16 | 17 | ValueHolder copy() const override; 18 | std::string str() const override; 19 | std::string repr() const override; 20 | size_t hash() const override; 21 | 22 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 23 | const BasicType &get_type() const override; 24 | 25 | /** the global None value */ 26 | static std::shared_ptr value; 27 | 28 | protected: 29 | bool apply_value(const Value &value, nyan_op operation) override; 30 | bool equals(const Value &other) const override; 31 | }; 32 | 33 | } // namespace nyan 34 | -------------------------------------------------------------------------------- /nyan/nyan.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #pragma once 4 | 5 | 6 | /** 7 | * @file 8 | * Main header file for nyan. 9 | */ 10 | 11 | #include "config.h" 12 | 13 | #include "ast.h" 14 | #include "database.h" 15 | #include "error.h" 16 | #include "file.h" 17 | #include "lexer/lexer.h" 18 | #include "member.h" 19 | #include "namespace.h" 20 | #include "object.h" 21 | #include "ops.h" 22 | #include "parser.h" 23 | #include "token.h" 24 | #include "type.h" 25 | #include "util.h" 26 | #include "value/container.h" 27 | #include "value/dict.h" 28 | #include "value/file.h" 29 | #include "value/number.h" 30 | #include "value/object.h" 31 | #include "value/orderedset.h" 32 | #include "value/set.h" 33 | #include "value/text.h" 34 | #include "value/value.h" 35 | #include "view.h" 36 | 37 | 38 | /** 39 | * The nyan engine and interpreter is defined in this namespace. 40 | */ 41 | namespace nyan { 42 | 43 | } // namespace nyan 44 | -------------------------------------------------------------------------------- /nyan/value/file.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | 7 | #include "value.h" 8 | 9 | 10 | namespace nyan { 11 | 12 | class IDToken; 13 | 14 | 15 | /** 16 | * Nyan value to store file names as nyan values. 17 | */ 18 | class Filename : public Value { 19 | public: 20 | Filename(const std::string &path); 21 | Filename(const IDToken &token); 22 | 23 | const std::string &get() const; 24 | 25 | ValueHolder copy() const override; 26 | std::string str() const override; 27 | std::string repr() const override; 28 | size_t hash() const override; 29 | 30 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 31 | const BasicType &get_type() const override; 32 | 33 | protected: 34 | bool apply_value(const Value &value, nyan_op operation) override; 35 | bool equals(const Value &other) const override; 36 | 37 | std::string path; 38 | }; 39 | 40 | } // namespace nyan 41 | -------------------------------------------------------------------------------- /nyan/compiler.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #pragma once 4 | 5 | /* 6 | * Branch prediction tuning. 7 | * The expression is expected to be true (=likely) or false (=unlikely). 8 | */ 9 | #if defined(__GNUC__) 10 | #define likely(x) __builtin_expect(!!(x), 1) 11 | #define unlikely(x) __builtin_expect(!!(x), 0) 12 | #else 13 | #define likely(x) (x) 14 | #define unlikely(x) (x) 15 | #endif 16 | 17 | 18 | /* 19 | * Software breakpoint for debugging. 20 | */ 21 | #ifdef _WIN32 22 | #define BREAKPOINT __debugbreak() 23 | #else 24 | #include 25 | #define BREAKPOINT raise(SIGTRAP) 26 | #endif 27 | 28 | 29 | /* 30 | * shared library symbol export declarations 31 | */ 32 | #if defined(_WIN32) 33 | #if defined(nyan_EXPORTS) 34 | #define NYANAPI __declspec(dllexport) // library is built 35 | #else 36 | #define NYANAPI __declspec(dllimport) // library is used 37 | #endif 38 | #else 39 | #define NYANAPI __attribute__((visibility("default"))) 40 | #endif 41 | -------------------------------------------------------------------------------- /nyan/object_notifier.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "object_notifier.h" 4 | 5 | #include "view.h" 6 | 7 | 8 | namespace nyan { 9 | 10 | ObjectNotifierHandle::ObjectNotifierHandle(const update_cb_t &func) : 11 | func{func} {} 12 | 13 | 14 | void ObjectNotifierHandle::fire(order_t t, const fqon_t &fqon, const ObjectState &state) const { 15 | this->func(t, fqon, state); 16 | } 17 | 18 | 19 | ObjectNotifier::ObjectNotifier(const fqon_t &fqon, 20 | const update_cb_t &func, 21 | const std::shared_ptr &view) : 22 | fqon{fqon}, 23 | view{view}, 24 | handle{std::make_shared(func)} {} 25 | 26 | 27 | ObjectNotifier::~ObjectNotifier() { 28 | this->view->deregister_notifier(this->fqon, this->handle); 29 | } 30 | 31 | 32 | const std::shared_ptr &ObjectNotifier::get_handle() const { 33 | return this->handle; 34 | } 35 | 36 | 37 | } // namespace nyan 38 | -------------------------------------------------------------------------------- /nyan/value/object.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include "../config.h" 5 | #include "value.h" 6 | 7 | 8 | namespace nyan { 9 | 10 | /** 11 | * Nyan value to store object references. 12 | * They are stored just by remembering the fully-qualified object name. 13 | */ 14 | class ObjectValue : public Value { 15 | public: 16 | ObjectValue(const fqon_t &name); 17 | 18 | ValueHolder copy() const override; 19 | std::string str() const override; 20 | std::string repr() const override; 21 | size_t hash() const override; 22 | 23 | /** return the stored fqon */ 24 | const fqon_t &get_name() const; 25 | 26 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 27 | const BasicType &get_type() const override; 28 | 29 | protected: 30 | bool apply_value(const Value &value, nyan_op operation) override; 31 | bool equals(const Value &other) const override; 32 | 33 | fqon_t name; 34 | }; 35 | 36 | } // namespace nyan 37 | -------------------------------------------------------------------------------- /nyan/value/set.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | 7 | #include "../ops.h" 8 | #include "container_types.h" 9 | #include "set_base.h" 10 | 11 | 12 | namespace nyan { 13 | 14 | 15 | /** 16 | * Nyan value to store a unordered set of things. 17 | */ 18 | class Set 19 | : public SetBase { 20 | // fetch the constructors 21 | using SetBase::SetBase; 22 | 23 | public: 24 | Set(); 25 | Set(std::vector &&values); 26 | 27 | std::string str() const override; 28 | std::string repr() const override; 29 | 30 | ValueHolder copy() const override; 31 | 32 | bool add(const ValueHolder &value) override; 33 | bool contains(const ValueHolder &value) const override; 34 | bool remove(const ValueHolder &value) override; 35 | 36 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 37 | const BasicType &get_type() const override; 38 | }; 39 | 40 | } // namespace nyan 41 | -------------------------------------------------------------------------------- /nyan/inheritance_change.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include "config.h" 5 | #include "ops.h" 6 | 7 | 8 | namespace nyan { 9 | 10 | /** 11 | * Runtime inheritance change. 12 | */ 13 | class InheritanceChange { 14 | public: 15 | InheritanceChange(inher_change_t type, fqon_t &&target); 16 | 17 | /** 18 | * Get the type of inheritance change, i.e.whether the new parent is appended to 19 | * the front or the back of the linearization. 20 | * 21 | * @return The inheritance change type. 22 | */ 23 | inher_change_t get_type() const; 24 | 25 | /** 26 | * Get the object to which the inheritance change is applied. 27 | * 28 | * @return Identifier of the target object. 29 | */ 30 | const fqon_t &get_target() const; 31 | 32 | protected: 33 | /** 34 | * Inheritance change type. 35 | */ 36 | inher_change_t type; 37 | 38 | /** 39 | * Identifier of the target object. 40 | */ 41 | fqon_t target; 42 | }; 43 | 44 | 45 | } // namespace nyan 46 | -------------------------------------------------------------------------------- /nyan/value/boolean.h: -------------------------------------------------------------------------------- 1 | // Copyright 2018-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include "../id_token.h" 6 | #include "value.h" 7 | 8 | 9 | namespace nyan { 10 | 11 | /** 12 | * Nyan value to store a boolean value (true/false). 13 | */ 14 | class Boolean : public Value { 15 | public: 16 | Boolean(const bool &value); 17 | Boolean(const IDToken &token); 18 | 19 | ValueHolder copy() const override; 20 | std::string str() const override; 21 | std::string repr() const override; 22 | size_t hash() const override; 23 | 24 | bool get() const { 25 | return *this; 26 | } 27 | 28 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 29 | const BasicType &get_type() const override; 30 | 31 | operator bool() const { 32 | return this->value; 33 | } 34 | 35 | protected: 36 | bool apply_value(const Value &value, nyan_op operation) override; 37 | bool equals(const Value &other) const override; 38 | 39 | bool value; 40 | }; 41 | 42 | } // namespace nyan 43 | -------------------------------------------------------------------------------- /nyan/object_history.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "object_history.h" 4 | 5 | #include "compiler.h" 6 | 7 | 8 | namespace nyan { 9 | 10 | 11 | void ObjectHistory::insert_change(const order_t time) { 12 | auto it = this->changes.lower_bound(time); 13 | 14 | // remove all newer entries 15 | this->changes.erase(it, std::end(this->changes)); 16 | 17 | auto ret = this->changes.insert(time); 18 | if (unlikely(ret.second == false)) { 19 | throw InternalError{"did not insert change point, it existed before"}; 20 | } 21 | } 22 | 23 | 24 | std::optional ObjectHistory::last_change_before(order_t t) const { 25 | // get the iterator to the first element greater than t 26 | auto it = this->changes.upper_bound(t); 27 | if (it == std::begin(this->changes)) { 28 | // the requested ordering point is not in this history 29 | return {}; 30 | } 31 | 32 | // go one back, this is the item we're looking for 33 | --it; 34 | 35 | return *it; 36 | } 37 | 38 | } // namespace nyan 39 | -------------------------------------------------------------------------------- /nyan/value/orderedset.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include "container_types.h" 6 | #include "set_base.h" 7 | 8 | #include "../ops.h" 9 | #include "value_holder.h" 10 | 11 | 12 | namespace nyan { 13 | 14 | class Value; 15 | 16 | 17 | /** 18 | * Nyan value to store an ordered set of things. 19 | */ 20 | class OrderedSet 21 | : public SetBase { 22 | // fetch the constructors 23 | using SetBase::SetBase; 24 | 25 | public: 26 | OrderedSet(); 27 | OrderedSet(std::vector &&values); 28 | 29 | std::string str() const override; 30 | std::string repr() const override; 31 | 32 | ValueHolder copy() const override; 33 | 34 | bool add(const ValueHolder &value) override; 35 | bool contains(const ValueHolder &value) const override; 36 | bool remove(const ValueHolder &value) override; 37 | 38 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 39 | const BasicType &get_type() const override; 40 | }; 41 | 42 | } // namespace nyan 43 | -------------------------------------------------------------------------------- /nyan/lexer/lexer.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | 7 | #include "../lang_error.h" 8 | #include "../token.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | class File; 14 | 15 | namespace lexer { 16 | class Impl; 17 | } // namespace lexer 18 | 19 | 20 | class Lexer { 21 | public: 22 | /** 23 | * Create a lexer for the given file. 24 | */ 25 | Lexer(const std::shared_ptr &file); 26 | virtual ~Lexer(); 27 | 28 | // no moves and copies 29 | Lexer(Lexer &&other) = delete; 30 | Lexer(const Lexer &other) = delete; 31 | Lexer &operator=(Lexer &&other) = delete; 32 | Lexer &operator=(const Lexer &other) = delete; 33 | 34 | /** 35 | * Return the next available token. 36 | */ 37 | Token get_next_token(); 38 | 39 | protected: 40 | /** Lexer internal implementation */ 41 | std::unique_ptr impl; 42 | }; 43 | 44 | 45 | /** 46 | * Exception for lexer problems. 47 | */ 48 | class LexerError : public LangError { 49 | public: 50 | LexerError(const Location &location, const std::string &msg); 51 | }; 52 | 53 | 54 | } // namespace nyan 55 | -------------------------------------------------------------------------------- /nyan/value/text.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | 7 | #include "value.h" 8 | 9 | 10 | namespace nyan { 11 | 12 | class IDToken; 13 | 14 | /** 15 | * Nyan value to store text. 16 | */ 17 | class Text : public Value { 18 | public: 19 | Text(const std::string &value); 20 | Text(const IDToken &token); 21 | 22 | ValueHolder copy() const override; 23 | std::string str() const override; 24 | std::string repr() const override; 25 | size_t hash() const override; 26 | 27 | const std::string &get() const { 28 | return *this; 29 | } 30 | 31 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 32 | const BasicType &get_type() const override; 33 | 34 | operator const std::string &() const { 35 | return this->value; 36 | } 37 | 38 | operator const char *() const { 39 | return this->value.c_str(); 40 | } 41 | 42 | protected: 43 | bool apply_value(const Value &value, nyan_op operation) override; 44 | bool equals(const Value &other) const override; 45 | 46 | std::string value; 47 | }; 48 | 49 | } // namespace nyan 50 | -------------------------------------------------------------------------------- /nyan/token_stream.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "token_stream.h" 4 | 5 | #include 6 | 7 | #include "token.h" 8 | 9 | 10 | namespace nyan { 11 | 12 | TokenStream::TokenStream(const TokenStream::container_t &container) : 13 | container{container}, 14 | iter{std::begin(container)} {} 15 | 16 | 17 | TokenStream::~TokenStream() = default; 18 | 19 | 20 | const TokenStream::tok_t *TokenStream::next() { 21 | const tok_t *ret; 22 | 23 | if (not this->full()) { 24 | throw InternalError{"requested item from empty list"}; 25 | } 26 | 27 | ret = &(*this->iter); 28 | 29 | // std::cout << "tok: " << ret->str() << std::endl; 30 | 31 | this->iter = std::next(this->iter); 32 | return ret; 33 | } 34 | 35 | 36 | bool TokenStream::full() const { 37 | return this->iter != std::end(this->container); 38 | } 39 | 40 | 41 | bool TokenStream::empty() const { 42 | return not this->full(); 43 | } 44 | 45 | 46 | void TokenStream::reinsert_last() { 47 | if (this->iter == std::begin(this->container)) { 48 | throw InternalError{"requested reinsert of unavailable token"}; 49 | } 50 | 51 | this->iter = std::prev(this->iter); 52 | } 53 | 54 | } // namespace nyan 55 | -------------------------------------------------------------------------------- /nyan/config.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #pragma once 4 | 5 | #ifdef _MSC_VER 6 | // Allow using alternative operator representation with non-conforming compiler 7 | #include 8 | #endif 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | namespace nyan { 16 | 17 | /** number of spaces per indent **/ 18 | constexpr int SPACES_PER_INDENT = 4; 19 | 20 | /** ordering type */ 21 | using order_t = uint64_t; 22 | 23 | /** starting point of order */ 24 | constexpr const order_t DEFAULT_T = 0; 25 | 26 | /** the maximum representable value of order_t is always the "latest" value */ 27 | constexpr const order_t LATEST_T = std::numeric_limits::max(); 28 | 29 | /** fully-qualified object name */ 30 | using fqon_t = std::string; 31 | 32 | /** fully-qualified namespace name */ 33 | using fqnn_t = fqon_t; 34 | 35 | /** member name identifier type */ 36 | using memberid_t = std::string; 37 | 38 | /** member and override nesting depth type */ 39 | using override_depth_t = unsigned; 40 | 41 | /** type used for nyan::Int values */ 42 | using value_int_t = int64_t; 43 | 44 | /** type used for nyan::Float values */ 45 | using value_float_t = double; 46 | 47 | } // namespace nyan 48 | -------------------------------------------------------------------------------- /nyan/member_info.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #include "member_info.h" 3 | 4 | #include 5 | 6 | #include "type.h" 7 | 8 | 9 | namespace nyan { 10 | 11 | MemberInfo::MemberInfo(const Location &location) : 12 | location{location}, 13 | initial_def{false} {} 14 | 15 | 16 | Type &MemberInfo::set_type(std::shared_ptr &&type, bool initial) { 17 | this->initial_def = initial; 18 | this->type = std::move(type); 19 | return *this->type.get(); 20 | } 21 | 22 | 23 | Type &MemberInfo::set_type(const std::shared_ptr &type, bool initial) { 24 | this->initial_def = initial; 25 | this->type = type; 26 | return *this->type.get(); 27 | } 28 | 29 | 30 | const std::shared_ptr &MemberInfo::get_type() const { 31 | return this->type; 32 | } 33 | 34 | 35 | const Location &MemberInfo::get_location() const { 36 | return this->location; 37 | } 38 | 39 | 40 | bool MemberInfo::is_initial_def() const { 41 | return this->initial_def; 42 | } 43 | 44 | 45 | std::string MemberInfo::str() const { 46 | std::ostringstream builder; 47 | 48 | if (this->type) { 49 | builder << this->type->str(); 50 | } 51 | else { 52 | builder << "[no type]"; 53 | } 54 | 55 | return builder.str(); 56 | } 57 | 58 | } // namespace nyan 59 | -------------------------------------------------------------------------------- /nyan/nyan_tool.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | #include 7 | 8 | namespace nyan { 9 | 10 | /** 11 | * boolean flags to be set by cmdline options 12 | */ 13 | enum class option_flag { 14 | ECHO, 15 | TEST_PARSER 16 | }; 17 | 18 | /** 19 | * string arguments to be set by cmdline options 20 | */ 21 | enum class option_param { 22 | FILE 23 | }; 24 | 25 | using flags_t = std::unordered_map; 26 | using params_t = std::unordered_map; 27 | 28 | 29 | /** 30 | * Run the nyan tool. 31 | */ 32 | int run(flags_t, params_t); 33 | 34 | /** 35 | * Display the tool help. 36 | */ 37 | void help(); 38 | 39 | 40 | } // namespace nyan 41 | 42 | namespace std { 43 | 44 | /** 45 | * Hash for the option_flag enum class. Fak u C++! 46 | */ 47 | template <> 48 | struct hash { 49 | size_t operator()(const nyan::option_flag &x) const { 50 | return static_cast(x); 51 | } 52 | }; 53 | 54 | /** 55 | * Hash for the option_param enum class. Fak u C++! 56 | */ 57 | template <> 58 | struct hash { 59 | size_t operator()(const nyan::option_param &x) const { 60 | return static_cast(x); 61 | } 62 | }; 63 | 64 | } // namespace std 65 | -------------------------------------------------------------------------------- /nyan/ops.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "ops.h" 4 | 5 | #include "ast.h" 6 | #include "token.h" 7 | 8 | 9 | namespace nyan { 10 | 11 | const std::unordered_set no_nyan_ops; 12 | 13 | 14 | nyan_op op_from_string(const std::string &str) { 15 | static const std::unordered_map str_to_op{ 16 | {"=", nyan_op::ASSIGN}, 17 | {"+", nyan_op::ADD}, 18 | {"-", nyan_op::SUBTRACT}, 19 | {"*", nyan_op::MULTIPLY}, 20 | {"/", nyan_op::DIVIDE}, 21 | {"+=", nyan_op::ADD_ASSIGN}, 22 | {"-=", nyan_op::SUBTRACT_ASSIGN}, 23 | {"*=", nyan_op::MULTIPLY_ASSIGN}, 24 | {"/=", nyan_op::DIVIDE_ASSIGN}, 25 | {"|=", nyan_op::UNION_ASSIGN}, 26 | {"&=", nyan_op::INTERSECT_ASSIGN}, 27 | }; 28 | 29 | auto it = str_to_op.find(str); 30 | 31 | if (it == std::end(str_to_op)) { 32 | return nyan_op::INVALID; 33 | } 34 | else { 35 | return it->second; 36 | } 37 | } 38 | 39 | 40 | nyan_op op_from_token(const Token &token) { 41 | if (token.type == token_type::OPERATOR) { 42 | return op_from_string(token.get()); 43 | } 44 | else { 45 | throw ASTError("expected operator, but got", token); 46 | } 47 | } 48 | 49 | 50 | Operator::Operator(const Token &token) : 51 | op{op_from_token(token)} {} 52 | 53 | 54 | const nyan_op &Operator::get() { 55 | return this->op; 56 | } 57 | 58 | 59 | } // namespace nyan 60 | -------------------------------------------------------------------------------- /nyan/change_tracker.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "change_tracker.h" 4 | 5 | 6 | namespace nyan { 7 | 8 | void ObjectChanges::add_parent(const fqon_t &obj) { 9 | this->new_parents.push_back(obj); 10 | } 11 | 12 | 13 | const std::vector &ObjectChanges::get_new_parents() const { 14 | return this->new_parents; 15 | } 16 | 17 | 18 | bool ObjectChanges::parents_update_required() const { 19 | return this->new_parents.size() > 0; 20 | } 21 | 22 | 23 | ObjectChanges &ChangeTracker::track_patch(const fqon_t &target_name) { 24 | // if existing, return the object change tracker 25 | // else: create a new one. 26 | auto it = this->changes.find(target_name); 27 | if (it == std::end(this->changes)) { 28 | auto it_new_tracker = this->changes.emplace(target_name, ObjectChanges{}).first; 29 | return it_new_tracker->second; 30 | } 31 | else { 32 | return it->second; 33 | } 34 | } 35 | 36 | 37 | const std::unordered_map &ChangeTracker::get_object_changes() const { 38 | return this->changes; 39 | } 40 | 41 | 42 | std::unordered_set ChangeTracker::get_changed_objects() const { 43 | std::unordered_set ret; 44 | ret.reserve(this->changes.size()); 45 | 46 | for (auto &it : this->changes) { 47 | ret.insert(it.first); 48 | } 49 | 50 | return ret; 51 | } 52 | 53 | 54 | } // namespace nyan 55 | -------------------------------------------------------------------------------- /nyan/file.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "file.h" 4 | 5 | #include "error.h" 6 | #include "util.h" 7 | 8 | 9 | namespace nyan { 10 | 11 | 12 | File::File(const std::string &virtual_name, std::string &&data) : 13 | name{virtual_name}, 14 | data{std::move(data)} { 15 | this->extract_lines(); 16 | } 17 | 18 | 19 | File::File(const std::string &path) : 20 | File{path, util::read_file(path)} { 21 | // util::read_file throws a FileReadError if unsuccessful. 22 | } 23 | 24 | 25 | void File::extract_lines() { 26 | this->line_ends = {std::string::npos}; 27 | 28 | for (size_t i = 0; i < this->data.size(); i++) { 29 | if (this->data[i] == '\n') { 30 | this->line_ends.push_back(i); 31 | } 32 | } 33 | this->line_ends.push_back(data.size()); 34 | } 35 | 36 | 37 | const std::string &File::get_name() const { 38 | return this->name; 39 | } 40 | 41 | 42 | const std::string &File::get_content() const { 43 | return this->data; 44 | } 45 | 46 | 47 | std::string File::get_line(size_t n) const { 48 | size_t begin = this->line_ends[n - 1] + 1; 49 | size_t len = this->line_ends[n] - begin; 50 | return this->data.substr(begin, len); 51 | } 52 | 53 | 54 | const char *File::c_str() const { 55 | return this->data.c_str(); 56 | } 57 | 58 | 59 | size_t File::size() const { 60 | return this->data.size(); 61 | } 62 | 63 | 64 | } // namespace nyan 65 | -------------------------------------------------------------------------------- /nyan/api_error.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "api_error.h" 4 | 5 | #include 6 | 7 | 8 | namespace nyan { 9 | 10 | APIError::APIError(const std::string &msg) : 11 | Error{msg} {} 12 | 13 | 14 | InvalidObjectError::InvalidObjectError() : 15 | APIError("uninitialized object was used") {} 16 | 17 | 18 | MemberTypeError::MemberTypeError(const fqon_t &objname, 19 | const memberid_t &member, 20 | const std::string &real_type, 21 | const std::string &wrong_type) : 22 | APIError{(static_cast( 23 | std::ostringstream{} << "type mismatch for member " << objname + "." << member 24 | << ": tried to convert real type " << real_type << " to " << wrong_type)) 25 | .str()}, 26 | objname{objname}, 27 | member{member}, 28 | real_type{real_type}, 29 | wrong_type{wrong_type} {} 30 | 31 | 32 | ObjectNotFoundError::ObjectNotFoundError(const fqon_t &obj_name) : 33 | APIError{"object not found: " + obj_name}, 34 | objname{obj_name} {} 35 | 36 | 37 | MemberNotFoundError::MemberNotFoundError(const fqon_t &obj_name, 38 | const memberid_t &member_name) : 39 | APIError{"Could not find member " + obj_name + "." + member_name}, 40 | objname{obj_name}, 41 | name{member_name} {} 42 | 43 | } // namespace nyan 44 | -------------------------------------------------------------------------------- /nyan/value/value_holder.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "value_holder.h" 4 | 5 | #include "value.h" 6 | 7 | 8 | namespace nyan { 9 | 10 | ValueHolder::ValueHolder() = default; 11 | 12 | 13 | ValueHolder::ValueHolder(std::shared_ptr &&value) : 14 | value{std::move(value)} {} 15 | 16 | 17 | ValueHolder::ValueHolder(const std::shared_ptr &value) : 18 | value{value} {} 19 | 20 | 21 | ValueHolder &ValueHolder::operator=(const std::shared_ptr &value) { 22 | this->value = value; 23 | return *this; 24 | } 25 | 26 | 27 | const std::shared_ptr &ValueHolder::get_ptr() const { 28 | return this->value; 29 | } 30 | 31 | 32 | bool ValueHolder::exists() const { 33 | return this->value.get() != nullptr; 34 | } 35 | 36 | 37 | Value &ValueHolder::operator*() const { 38 | return *this->value; 39 | } 40 | 41 | 42 | Value *ValueHolder::operator->() const { 43 | return this->value.get(); 44 | } 45 | 46 | 47 | bool ValueHolder::operator==(const ValueHolder &other) const { 48 | return (*this->value == *other.value); 49 | } 50 | 51 | 52 | bool ValueHolder::operator!=(const ValueHolder &other) const { 53 | return (*this->value != *other.value); 54 | } 55 | 56 | 57 | } // namespace nyan 58 | 59 | 60 | namespace std { 61 | 62 | size_t hash::operator()(const nyan::ValueHolder &val) const { 63 | return hash{}(*val); 64 | } 65 | 66 | } // namespace std 67 | -------------------------------------------------------------------------------- /nyan/parser.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "parser.h" 4 | 5 | #include "ast.h" 6 | #include "compiler.h" 7 | #include "database.h" 8 | #include "file.h" 9 | #include "lexer/lexer.h" 10 | #include "member.h" 11 | #include "object.h" 12 | #include "token.h" 13 | #include "type.h" 14 | #include "util.h" 15 | #include "value/number.h" 16 | #include "value/orderedset.h" 17 | #include "value/set.h" 18 | #include "value/text.h" 19 | #include "value/value.h" 20 | 21 | 22 | namespace nyan { 23 | 24 | Parser::Parser() = default; 25 | 26 | 27 | AST Parser::parse(const std::shared_ptr &file) { 28 | // If you are some parser junkie and I trigger your rage mode now, 29 | // feel free to rewrite the parser or use a tool like bison. 30 | 31 | // tokenize input 32 | std::vector tokens = this->tokenize(file); 33 | 34 | // create ast from tokens 35 | AST ast = this->create_ast(tokens); 36 | 37 | return ast; 38 | } 39 | 40 | 41 | std::vector Parser::tokenize(const std::shared_ptr &file) const { 42 | Lexer lexer{file}; 43 | 44 | std::vector ret; 45 | 46 | while (true) { 47 | Token token = lexer.get_next_token(); 48 | bool end = (token.type == token_type::ENDFILE); 49 | 50 | ret.push_back(std::move(token)); 51 | 52 | if (end) { 53 | break; 54 | } 55 | } 56 | 57 | return ret; 58 | } 59 | 60 | 61 | AST Parser::create_ast(const std::vector &tokens) const { 62 | TokenStream token_iter{tokens}; 63 | AST root{token_iter}; 64 | return root; 65 | } 66 | 67 | } // namespace nyan 68 | -------------------------------------------------------------------------------- /nyan/object_history.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "config.h" 11 | #include "curve.h" 12 | 13 | 14 | namespace nyan { 15 | 16 | 17 | /** 18 | * Cached information about an object. 19 | * Speeds up information retrieval. 20 | */ 21 | class ObjectHistory { 22 | public: 23 | /** 24 | * Insert a new change record for this object. 25 | * This only updates the change history, 26 | * not the linearizations or the child tracking. 27 | * 28 | * @param t Time of insertion. 29 | */ 30 | void insert_change(const order_t t); 31 | 32 | /** 33 | * Get the time of the last change before a given time. 34 | * 35 | * @param t Search changes before this point in time 36 | * 37 | * @return Time of the last change if there is one, else empty std::optional. 38 | */ 39 | std::optional last_change_before(order_t t) const; 40 | 41 | // TODO: curve for value cache: memberid_t => curve 42 | 43 | /** 44 | * Stores the parent linearization of this object over time. 45 | */ 46 | Curve> linearizations; 47 | 48 | /** 49 | * Stores the direct children an object has over time. 50 | */ 51 | Curve> children; 52 | 53 | protected: 54 | /** 55 | * History of order points where this object was modified. 56 | * This is used to quickly find the matching order for an 57 | * object state in the state history. 58 | */ 59 | std::set changes; 60 | }; 61 | 62 | 63 | } // namespace nyan 64 | -------------------------------------------------------------------------------- /nyan/token_stream.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | 7 | 8 | namespace nyan { 9 | 10 | class Token; 11 | 12 | /** 13 | * Python-yield like iterator for a token stream. 14 | * You can fetch the next value until nothing is left. 15 | * 16 | * The passed container is stored as reference only, 17 | * so it must be kept owned in the outside. 18 | */ 19 | class TokenStream { 20 | public: 21 | using tok_t = Token; 22 | using container_t = std::vector; 23 | 24 | TokenStream(const container_t &container); 25 | 26 | ~TokenStream(); 27 | 28 | /** 29 | * Advance one step in the stream and get the pointer to 30 | * the token the streamnow points to. 31 | * 32 | * @return Next token in the stream. 33 | */ 34 | const tok_t *next(); 35 | 36 | /** 37 | * Check if the end of the stream has been reached, i.e. there 38 | * are no more tokens after the current token. 39 | * 40 | * @return true if there are no more tokens in the stream, else false. 41 | */ 42 | bool full() const; 43 | 44 | /** 45 | * Check if there are tokens left in the stream. 46 | * 47 | * @return true if there are more tokens in the stream, else false. 48 | */ 49 | bool empty() const; 50 | 51 | /** 52 | * Reinserts the token previously returned by next(). 53 | */ 54 | void reinsert_last(); 55 | 56 | protected: 57 | /** 58 | * List of tokens in the stream. 59 | */ 60 | const container_t &container; 61 | 62 | /** 63 | * Iterator used for advancing/regressing in the stream. 64 | */ 65 | container_t::const_iterator iter; 66 | }; 67 | 68 | } // namespace nyan 69 | -------------------------------------------------------------------------------- /nyan/object_notifier.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "object_notifier_types.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | class View; 14 | 15 | /** 16 | * Notification callback object modification. 17 | */ 18 | class ObjectNotifierHandle { 19 | public: 20 | ObjectNotifierHandle(const update_cb_t &func); 21 | 22 | /** 23 | * Calls the user provided function of the notifier. 24 | * 25 | * @param t Time of update. 26 | * @param fqon Identifier of the updated object. 27 | * @param state New object state. 28 | */ 29 | void fire(order_t t, const fqon_t &fqon, const ObjectState &state) const; 30 | 31 | protected: 32 | /** 33 | * The user function which is called when the object is changed. 34 | */ 35 | update_cb_t func; 36 | }; 37 | 38 | 39 | class ObjectNotifier { 40 | public: 41 | ObjectNotifier(const fqon_t &fqon, 42 | const update_cb_t &func, 43 | const std::shared_ptr &view); 44 | ~ObjectNotifier(); 45 | 46 | /** 47 | * Get the callback handle for the object notifier. 48 | * 49 | * @return Shared pointer to the ObjectNotifierHandle. 50 | */ 51 | const std::shared_ptr &get_handle() const; 52 | 53 | protected: 54 | /** 55 | * Which object the notifier is for. 56 | */ 57 | fqon_t fqon; 58 | 59 | /** 60 | * View this notifier is active in. 61 | */ 62 | std::shared_ptr view; 63 | 64 | /** 65 | * Stores the actual callback handle. 66 | */ 67 | std::shared_ptr handle; 68 | }; 69 | 70 | 71 | } // namespace nyan 72 | -------------------------------------------------------------------------------- /nyan/c3.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "config.h" 9 | #include "error.h" 10 | 11 | 12 | namespace nyan { 13 | 14 | class ObjectState; 15 | 16 | 17 | /** 18 | * Function to fetch an object state. 19 | */ 20 | using objstate_fetch_t = std::function; 21 | 22 | 23 | /** 24 | * Implements the C3 multi inheritance linearization algorithm 25 | * to bring the parents of an object into the "right" order. 26 | * Calls linearize_recurse(). 27 | * 28 | * @param name Identifier of the object that is linearized. 29 | * @param get_obj Function to retrive the ObjectState of the object. 30 | * 31 | * @return A C3 linearization of the object's parents. 32 | */ 33 | std::vector linearize(const fqon_t &name, const objstate_fetch_t &get_obj); 34 | 35 | 36 | /** 37 | * Recursive walk for the c3 linearization implememtation. 38 | * 39 | * @param name Identifier of the object that is linearized. 40 | * @param get_obj Function to retrive the ObjectState of the object. 41 | * @param seen Set of objects that have already been found. Should be empty on initial call. 42 | * 43 | * @return A C3 linearization of the object's parents. 44 | */ 45 | std::vector 46 | linearize_recurse(const fqon_t &name, 47 | const objstate_fetch_t &get_obj, 48 | std::unordered_set *seen); 49 | 50 | 51 | /** 52 | * Exceptions in the c3 linearization progress. 53 | */ 54 | class C3Error : public Error { 55 | public: 56 | C3Error(const std::string &msg); 57 | }; 58 | 59 | 60 | } // namespace nyan 61 | -------------------------------------------------------------------------------- /nyan/value/none.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "none.h" 4 | 5 | #include 6 | 7 | namespace nyan { 8 | 9 | 10 | // global none value 11 | std::shared_ptr None::value = std::make_shared(); 12 | 13 | 14 | None::None() = default; 15 | 16 | 17 | ValueHolder None::copy() const { 18 | return ValueHolder{None::value}; 19 | } 20 | 21 | 22 | bool None::apply_value(const Value & /**value*/, nyan_op /**operation*/) { 23 | throw InternalError{"None can't get an applied value - assign Value directly to member instead"}; 24 | } 25 | 26 | 27 | std::string None::str() const { 28 | return "None"; 29 | } 30 | 31 | 32 | std::string None::repr() const { 33 | return this->str(); 34 | } 35 | 36 | 37 | size_t None::hash() const { 38 | return std::hash{}("nyan_None"); 39 | } 40 | 41 | 42 | bool None::equals(const Value & /*other*/) const { 43 | // none always equals none, 44 | // and `other` is ensured to be none by the `Value::operator==` check, 45 | // which then calls this `equals` member method. 46 | return true; 47 | } 48 | 49 | 50 | const std::unordered_set &None::allowed_operations(const Type &with_type) const { 51 | const static std::unordered_set ops{ 52 | nyan_op::ASSIGN, 53 | }; 54 | 55 | // None can only be assigned to types with optional modifier flag 56 | if (not with_type.has_modifier(modifier_t::OPTIONAL)) { 57 | return no_nyan_ops; 58 | } 59 | 60 | return ops; 61 | } 62 | 63 | 64 | const BasicType &None::get_type() const { 65 | constexpr static BasicType type{ 66 | primitive_t::NONE, 67 | composite_t::SINGLE, 68 | }; 69 | 70 | return type; 71 | } 72 | 73 | 74 | } // namespace nyan 75 | -------------------------------------------------------------------------------- /nyan/lexer/bracket.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include "../token.h" 6 | 7 | 8 | namespace nyan::lexer { 9 | 10 | 11 | class Bracket { 12 | public: 13 | Bracket(token_type type, int indent); 14 | 15 | /** This bracket is directly followed by a newline. */ 16 | void doesnt_hang(int hanging_indent); 17 | 18 | /** Was this bracket not directly followed by a newline? */ 19 | bool is_hanging() const; 20 | 21 | /** Does this bracket match the given token type? */ 22 | bool matches(token_type type) const; 23 | 24 | /** Return the expected content indentation level. */ 25 | int get_content_indent() const; 26 | 27 | /** Return the expected closing bracket indent level. */ 28 | std::string get_closing_indent() const; 29 | 30 | /** Check if the closing indent is ok. */ 31 | bool closing_indent_ok(int indent) const; 32 | 33 | /** Return the visual representation of the expected bracket */ 34 | const char *matching_type_str() const; 35 | 36 | /** convert the token type to bracket type */ 37 | static bracket_type to_type(token_type token); 38 | 39 | protected: 40 | /** expected closing bracket type */ 41 | token_type expected_match() const; 42 | 43 | /** 44 | * Indentation level of the line this bracket was in. 45 | */ 46 | int indentation; 47 | 48 | /** 49 | * Type of this opening bracket. 50 | */ 51 | bracket_type type; 52 | 53 | /** 54 | * true if the indentation mode is "hanging", 55 | * that is, if a continuation must happen at the 56 | * indent level of this opening bracket. 57 | * The expected indentation level of contained content is stored 58 | * in the `indentation` member. 59 | */ 60 | bool hanging; 61 | }; 62 | 63 | } // namespace nyan::lexer 64 | -------------------------------------------------------------------------------- /nyan/location.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "location.h" 4 | 5 | #include "file.h" 6 | #include "id_token.h" 7 | #include "token.h" 8 | 9 | 10 | namespace nyan { 11 | 12 | 13 | Location::Location(const Token &token) : 14 | Location{token.location} {} 15 | 16 | 17 | Location::Location(const IDToken &token) : 18 | Location{token.get_start_location()} { 19 | // use the full id length as location length 20 | this->length = token.get_length(); 21 | } 22 | 23 | 24 | Location::Location(const std::shared_ptr &file, 25 | int line, 26 | int line_offset, 27 | int length) : 28 | file{file}, 29 | line{line}, 30 | line_offset{line_offset}, 31 | length{length} {} 32 | 33 | 34 | Location::Location(const std::string &custom) : 35 | _is_builtin{true}, 36 | msg{custom} {} 37 | 38 | 39 | bool Location::is_builtin() const { 40 | return this->_is_builtin; 41 | } 42 | 43 | const std::string &Location::get_msg() const { 44 | return this->msg; 45 | } 46 | 47 | int Location::get_line() const { 48 | return this->line; 49 | } 50 | 51 | 52 | int Location::get_line_offset() const { 53 | return this->line_offset; 54 | } 55 | 56 | int Location::get_length() const { 57 | return this->length; 58 | } 59 | 60 | 61 | std::string Location::get_line_content() const { 62 | if (this->_is_builtin) { 63 | return this->msg; 64 | } 65 | return this->file->get_line(this->get_line()); 66 | } 67 | 68 | const std::shared_ptr &Location::get_file() const { 69 | return this->file; 70 | } 71 | 72 | 73 | void Location::str(std::ostringstream &builder) const { 74 | if (this->_is_builtin) { 75 | builder << "[native call]: "; 76 | return; 77 | } 78 | 79 | builder << this->file->get_name() << ":" 80 | << this->line << ":" 81 | << this->line_offset << ": "; 82 | } 83 | 84 | } // namespace nyan 85 | -------------------------------------------------------------------------------- /doc/building.md: -------------------------------------------------------------------------------- 1 | # Building nyan 2 | 3 | This project requires a C++20 compiler (e.g gcc >= 10 or clang >= 10) and 4 | uses `flex` and `cmake`. 5 | 6 | 7 | ## Dependencies 8 | 9 | ###### Ubuntu 20.04 or later 10 | 11 | ``` 12 | sudo apt-get update 13 | sudo apt-get install cmake flex make 14 | ``` 15 | 16 | For gcc: `sudo apt-get install gcc g++` 17 | 18 | For clang: `sudo apt-get install clang` 19 | 20 | 21 | ###### Archlinux 22 | 23 | ``` 24 | sudo pacman -Syu --needed cmake flex make clang gcc 25 | ``` 26 | 27 | ###### Gentoo 28 | 29 | ``` 30 | sudo emerge -avt cmake flex make 31 | ``` 32 | 33 | ###### Windows 34 | 35 | - [CMake](https://cmake.org/download/) 36 | - [Visual Studio 2017 Community edition](https://www.visualstudio.com/downloads/) 37 | - [flex](https://sourceforge.net/projects/winflexbison/) 38 | - The path to win_flex.exe needs to be added to the PATH environment variable 39 | 40 | ## Clone this repository 41 | 42 | ``` 43 | git clone https://github.com/SFTtech/nyan.git 44 | ``` 45 | 46 | ## Build 47 | 48 | This project is built like every standard [cmake project](http://lmgtfy.com/?q=building+a+cmake+project). It boils down to: 49 | 50 | ``` 51 | cd nyan 52 | mkdir build 53 | cd build 54 | cmake .. 55 | make -j$(nproc) 56 | ``` 57 | 58 | _Note:_ on windows the last command is 59 | ```powershell 60 | cmake --build . --config RelWithDebInfo 61 | ``` 62 | 63 | _Note:_ if nyan can't find flex add `-DFLEX_EXECUTABLE=path/to/win_flex.exe` to the cmake configure command. 64 | 65 | 66 | `cmake` registers the project in the [user package registry](https://cmake.org/cmake/help/latest/manual/cmake-packages.7.html#user-package-registry). 67 | Other projects can therefore easily find `nyan` **without** installing it 68 | (no `sudo make install` needed). 69 | 70 | `find_package(nyan CONFIG REQUIRED)` will directly provide `nyan::nyan` as a 71 | target to link to (with its include directories etc). 72 | -------------------------------------------------------------------------------- /nyan/value/object.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "object.h" 4 | 5 | #include 6 | 7 | #include "../error.h" 8 | #include "../token.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | ObjectValue::ObjectValue(const fqon_t &name) : 14 | name{name} {} 15 | 16 | 17 | ValueHolder ObjectValue::copy() const { 18 | return {std::make_shared(*this)}; 19 | } 20 | 21 | 22 | bool ObjectValue::apply_value(const Value &value, nyan_op operation) { 23 | const ObjectValue &change = dynamic_cast(value); 24 | 25 | switch (operation) { 26 | case nyan_op::ASSIGN: 27 | this->name = change.name; 28 | break; 29 | 30 | default: 31 | throw InternalError{"unknown operation requested"}; 32 | } 33 | 34 | return true; 35 | } 36 | 37 | 38 | std::string ObjectValue::str() const { 39 | return this->name; 40 | } 41 | 42 | 43 | std::string ObjectValue::repr() const { 44 | return this->str(); 45 | } 46 | 47 | 48 | size_t ObjectValue::hash() const { 49 | return std::hash{}(this->name); 50 | } 51 | 52 | 53 | const fqon_t &ObjectValue::get_name() const { 54 | return this->name; 55 | } 56 | 57 | 58 | bool ObjectValue::equals(const Value &other) const { 59 | auto &other_val = dynamic_cast(other); 60 | return this->name == other_val.name; 61 | } 62 | 63 | 64 | const std::unordered_set &ObjectValue::allowed_operations(const Type &with_type) const { 65 | const static std::unordered_set ops{ 66 | nyan_op::ASSIGN, 67 | }; 68 | 69 | switch (with_type.get_primitive_type()) { 70 | case primitive_t::OBJECT: 71 | case primitive_t::NONE: 72 | return ops; 73 | 74 | default: 75 | return no_nyan_ops; 76 | } 77 | } 78 | 79 | 80 | const BasicType &ObjectValue::get_type() const { 81 | constexpr static BasicType type{ 82 | primitive_t::OBJECT, 83 | composite_t::SINGLE, 84 | }; 85 | 86 | return type; 87 | } 88 | 89 | 90 | } // namespace nyan 91 | -------------------------------------------------------------------------------- /nyan/util/flags.h: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | namespace nyan::util { 11 | 12 | /** 13 | * Wrapper for an enum so we can use it as bitset. 14 | * 15 | * Example: 16 | * enum class lol { 17 | * red, 18 | * green, 19 | * yellow 20 | * }; 21 | * 22 | * Flags lolflags; 23 | * lolflags.set(lol::red); 24 | * lolflags[lol::red] == true; 25 | */ 26 | template 27 | requires std::is_enum_v && requires { 28 | { T::size }; 29 | } 30 | class Flags { 31 | public: 32 | Flags &set(T e, bool value = true) noexcept { 33 | this->bits.set(underlying(e), value); 34 | return *this; 35 | } 36 | 37 | Flags &reset(T e) noexcept { 38 | this->set(e, false); 39 | return *this; 40 | } 41 | 42 | Flags &reset() noexcept { 43 | this->bits.reset(); 44 | return *this; 45 | } 46 | 47 | // TODO: maybe add operator&=,|=,^=,~ 48 | 49 | [[nodiscard]] bool all() const noexcept { 50 | return this->bits.all(); 51 | } 52 | 53 | [[nodiscard]] bool any() const noexcept { 54 | return this->bits.any(); 55 | } 56 | 57 | [[nodiscard]] bool none() const noexcept { 58 | return this->bits.none(); 59 | } 60 | 61 | [[nodiscard]] constexpr std::size_t size() const noexcept { 62 | return this->bits.size(); 63 | } 64 | 65 | [[nodiscard]] std::size_t count() const noexcept { 66 | return this->bits.count(); 67 | } 68 | 69 | constexpr bool operator[](T e) const { 70 | return this->bits[underlying(e)]; 71 | } 72 | 73 | private: 74 | using enum_val_t = typename std::make_unsigned_t>; 75 | 76 | /** 77 | * convert enum entry to underlying value. 78 | */ 79 | static constexpr enum_val_t underlying(T e) { 80 | return static_cast(e); 81 | } 82 | 83 | private: 84 | /** 85 | * Bitset with as many entries as the enum has. 86 | */ 87 | std::bitset bits; 88 | }; 89 | 90 | } // namespace nyan::util 91 | -------------------------------------------------------------------------------- /nyan/value_token.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "token.h" 9 | #include "type.h" 10 | 11 | 12 | namespace nyan { 13 | 14 | 15 | /** 16 | * Stores a value. Values can consist of multiple IDTokens. 17 | */ 18 | class ValueToken { 19 | public: 20 | ValueToken() = default; 21 | 22 | /** 23 | * Simple constructor for a single value that is not in a container. 24 | */ 25 | ValueToken(const IDToken &token); 26 | 27 | /** 28 | * Constructor for value tokens in a container. 29 | */ 30 | ValueToken(composite_t type, 31 | std::vector &tokens); 32 | 33 | /** 34 | * Get the string representation of this ValueToken. 35 | * 36 | * @return String representation formatted in nyan language notation. 37 | */ 38 | std::string str() const; 39 | 40 | /** 41 | * Check if this ValueToken is empty. 42 | * 43 | * @return true if the ValueToken has more than one IDToken, else false. 44 | */ 45 | bool exists() const; 46 | 47 | /** 48 | * Get the starting location of this ValueToken in a file. 49 | * 50 | * @return Location of this ValueToken. 51 | */ 52 | const Location &get_start_location() const; 53 | 54 | /** 55 | * Get the character length of this ValueToken. 56 | * 57 | * @return Length of this ValueToken. 58 | */ 59 | size_t get_length() const; 60 | 61 | /** 62 | * Get the list of IDTokens in this ValueToken. 63 | * 64 | * @return List of IDTokens in this ValueToken. 65 | */ 66 | const std::vector &get_value() const; 67 | 68 | protected: 69 | /** 70 | * Get the type of container that this ValueToken is stored in. 71 | * 72 | * @return Container type of this ValueToken. 73 | */ 74 | const composite_t &get_container_type() const; 75 | 76 | /** 77 | * Container type where the value is tored in. 78 | */ 79 | composite_t container_type; 80 | 81 | /** 82 | * Components in the token. 83 | */ 84 | std::vector tokens; 85 | }; 86 | 87 | 88 | } // namespace nyan 89 | -------------------------------------------------------------------------------- /nyan/token.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "token.h" 4 | 5 | #include 6 | 7 | #include "error.h" 8 | #include "file.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | 14 | Token::Token(const std::shared_ptr &file, 15 | int line, 16 | int line_offset, 17 | int length, 18 | token_type type) : 19 | location{file, line, line_offset, length}, 20 | type{type} {} 21 | 22 | 23 | Token::Token(const std::shared_ptr &file, 24 | int line, 25 | int line_offset, 26 | int length, 27 | token_type type, 28 | const std::string &value) : 29 | location{file, line, line_offset, length}, 30 | type{type}, 31 | value{value} {} 32 | 33 | 34 | Token::Token() : 35 | type{token_type::INVALID} {} 36 | 37 | 38 | const std::string &Token::get() const { 39 | return this->value; 40 | } 41 | 42 | 43 | std::string Token::str() const { 44 | std::ostringstream builder; 45 | builder << "(" << this->location.get_line() << ":" 46 | << this->location.get_line_offset() << ": " 47 | << token_type_str(this->type); 48 | if (this->value.size() > 0) { 49 | builder << " '" << this->value << "'"; 50 | } 51 | builder << ")"; 52 | return builder.str(); 53 | } 54 | 55 | 56 | bool Token::exists() const { 57 | return this->get().size() > 0; 58 | } 59 | 60 | 61 | bool Token::is_endmarker() const { 62 | switch (this->type) { 63 | case token_type::ENDFILE: 64 | case token_type::ENDLINE: 65 | return true; 66 | 67 | case token_type::INVALID: 68 | throw InternalError{"invalid token used"}; 69 | 70 | default: 71 | return false; 72 | } 73 | } 74 | 75 | 76 | bool Token::is_content() const { 77 | switch (this->type) { 78 | case token_type::FLOAT: 79 | case token_type::ID: 80 | case token_type::INF: 81 | case token_type::INT: 82 | case token_type::STRING: 83 | return true; 84 | 85 | case token_type::INVALID: 86 | throw InternalError{"invalid token used"}; 87 | 88 | default: 89 | return false; 90 | } 91 | } 92 | 93 | } // namespace nyan 94 | -------------------------------------------------------------------------------- /nyan/change_tracker.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "config.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | 14 | /** 15 | * Change tracking for a single object. 16 | */ 17 | class ObjectChanges { 18 | public: 19 | /** 20 | * Track an object as a new parent. 21 | * 22 | * @param obj Identifier of the object. 23 | */ 24 | void add_parent(const fqon_t &obj); 25 | 26 | /** 27 | * Retrieve the list of new parents. 28 | * 29 | * @return The list of new parents. 30 | */ 31 | const std::vector &get_new_parents() const; 32 | 33 | /** 34 | * Check if the parents were updated by the change. 35 | * 36 | * @return true if size of new_parents is > 1, else false. 37 | */ 38 | bool parents_update_required() const; 39 | 40 | protected: 41 | /** 42 | * List of new parents. 43 | */ 44 | std::vector new_parents; 45 | }; 46 | 47 | 48 | /** 49 | * Collects what changes have been done in a transaction. 50 | * Then this info is used to invalidate caches. 51 | */ 52 | class ChangeTracker { 53 | public: 54 | /** 55 | * Get the ObjectChanges for an object targeted by a patch 56 | * from the changes map or create a new one if there doesn't 57 | * exist one yet. 58 | * 59 | * @param target_name Identifier of the target object. 60 | * 61 | * @return An ObjectChanges tracker. 62 | */ 63 | ObjectChanges &track_patch(const fqon_t &target_name); 64 | 65 | /** 66 | * Retrieve the map with all ObjectChanges by object. 67 | * 68 | * @return A map of ObjectChanges by object. 69 | */ 70 | const std::unordered_map &get_object_changes() const; 71 | 72 | /** 73 | * Get all objects whose ObjectChanges are tracked in this tracker. 74 | * 75 | * @return A set of object identifiers. 76 | */ 77 | std::unordered_set get_changed_objects() const; 78 | 79 | protected: 80 | /** 81 | * Map of ObjectChanges by object. 82 | */ 83 | std::unordered_map changes; 84 | }; 85 | 86 | } // namespace nyan 87 | -------------------------------------------------------------------------------- /nyan/api_error.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include "error.h" 5 | 6 | 7 | namespace nyan { 8 | 9 | /** 10 | * Error thrown when the API user most likely did something wrong. 11 | */ 12 | class APIError : public Error { 13 | public: 14 | APIError(const std::string &msg); 15 | }; 16 | 17 | 18 | /** 19 | * An Object was not initialized properly. 20 | * This happens when you used the default constructor without 21 | * updating the Object from a View. 22 | */ 23 | class InvalidObjectError : public APIError { 24 | public: 25 | InvalidObjectError(); 26 | }; 27 | 28 | 29 | /** 30 | * The type of a member was queried wrongly. 31 | */ 32 | class MemberTypeError : public APIError { 33 | public: 34 | MemberTypeError(const fqon_t &objname, 35 | const memberid_t &member, 36 | const std::string &real_type, 37 | const std::string &wrong_type); 38 | 39 | protected: 40 | /** 41 | * Name (identifier) of the object the member is part of. 42 | */ 43 | fqon_t objname; 44 | 45 | /** 46 | * Name (identifier) of the member. 47 | */ 48 | memberid_t member; 49 | 50 | /** 51 | * Type that the member should have assigned. 52 | */ 53 | std::string real_type; 54 | 55 | /** 56 | * Type that the member has actually assigned. 57 | */ 58 | std::string wrong_type; 59 | }; 60 | 61 | 62 | /** 63 | * An object queried over the API is not found. 64 | */ 65 | class ObjectNotFoundError : public APIError { 66 | public: 67 | ObjectNotFoundError(const fqon_t &objname); 68 | 69 | protected: 70 | /** 71 | * Name (identifier) of the object. 72 | */ 73 | fqon_t objname; 74 | }; 75 | 76 | 77 | /** 78 | * An object member queried over the API is not found. 79 | */ 80 | class MemberNotFoundError : public APIError { 81 | public: 82 | MemberNotFoundError(const fqon_t &objname, 83 | const memberid_t &membername); 84 | 85 | protected: 86 | /** 87 | * Name (identifier) of the object the member is part of. 88 | */ 89 | fqon_t objname; 90 | 91 | /** 92 | * Name (identifier) of the member. 93 | */ 94 | memberid_t name; 95 | }; 96 | 97 | } // namespace nyan 98 | -------------------------------------------------------------------------------- /nyan/id_token.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "token.h" 9 | #include "token_stream.h" 10 | 11 | 12 | namespace nyan { 13 | 14 | 15 | /** 16 | * String tokens, used as identifiers, can be concatenated by dots sometimes. 17 | * Used for e.g. namespace references, or ambiguous members of parent objects. 18 | * This multi-token groups those. 19 | */ 20 | class IDToken { 21 | public: 22 | IDToken() = default; 23 | IDToken(const Token &first, TokenStream &tokens); 24 | 25 | /** 26 | * Get the string representation of this IDToken. 27 | * 28 | * @return String representation formatted in nyan language notation. 29 | */ 30 | std::string str() const; 31 | 32 | /** 33 | * Check if this IDToken is empty. 34 | * 35 | * @return true if the IDToken has more than one ID, else false. 36 | */ 37 | bool exists() const; 38 | 39 | /** 40 | * Get the type of the IDToken's content. 41 | * 42 | * @return Type of the first ID in this IDToken or token_type::INVALID if it doesn't exist. 43 | */ 44 | token_type get_type() const; 45 | 46 | /** 47 | * Get the starting location of this IDToken in a file. 48 | * 49 | * @return Location of this IDToken. 50 | */ 51 | const Location &get_start_location() const; 52 | 53 | /** 54 | * Get the character length of this IDToken. 55 | * 56 | * @return Length of this IDToken. 57 | */ 58 | size_t get_length() const; 59 | 60 | /** 61 | * Get the list of IDs in this IDToken. 62 | * 63 | * @return A list of Tokens in this IDToken. 64 | */ 65 | const std::vector &get_components() const; 66 | 67 | /** 68 | * Get the string representation of the first ID in this IDToken. 69 | * 70 | * @return String representation of the first ID formatted in nyan language notation. 71 | */ 72 | const std::string &get_first() const; 73 | 74 | /** 75 | * Get an fqon from an IDToken. 76 | * 77 | * @return Fqon for IDToken. 78 | */ 79 | fqon_t to_fqon() const; 80 | 81 | protected: 82 | /** 83 | * List of IDs defining the IDToken. 84 | */ 85 | std::vector ids; 86 | }; 87 | 88 | 89 | } // namespace nyan 90 | -------------------------------------------------------------------------------- /nyan/member.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "ops.h" 11 | #include "type.h" 12 | #include "value/value.h" 13 | 14 | namespace nyan { 15 | 16 | 17 | /** 18 | * Stores a member of a Object. 19 | * Also responsible for validating applied operators. 20 | */ 21 | class Member { 22 | public: 23 | /** 24 | * Member with value. 25 | */ 26 | Member(override_depth_t depth, 27 | nyan_op operation, 28 | Type declared_type, 29 | ValueHolder &&value); 30 | 31 | Member(const Member &other); 32 | Member(Member &&other) noexcept; 33 | Member &operator=(const Member &other); 34 | Member &operator=(Member &&other) noexcept; 35 | 36 | ~Member() = default; 37 | 38 | /** 39 | * Get the operation performed by this member. 40 | * 41 | * @return Operation of the member. 42 | */ 43 | nyan_op get_operation() const; 44 | 45 | /** 46 | * Get the value stored in this member. 47 | * 48 | * @return Value of the member. 49 | */ 50 | const Value &get_value() const; 51 | 52 | /** 53 | * Apply another member, using its operation, to this member. 54 | * 55 | * @param change Member applied to this member. 56 | * 57 | * @return if the change application was successful 58 | */ 59 | bool apply(const Member &change); 60 | 61 | /** 62 | * Get the string representation of this member's initialization part, 63 | * i.e. operation and value. 64 | * 65 | * @return String containing the member initialization in nyan format. 66 | */ 67 | std::string str() const; 68 | 69 | protected: 70 | /** 71 | * Number of @ chars before the operation, 72 | * those define the override depth when applying the patch. 73 | */ 74 | override_depth_t override_depth = 0; 75 | 76 | /** 77 | * Operation specified for this member. 78 | */ 79 | nyan_op operation = nyan_op::INVALID; 80 | 81 | /** 82 | * Type from the member declaration. 83 | */ 84 | Type declared_type; 85 | 86 | /** 87 | * Value stored in this member. 88 | * Must match the `declared_type`. 89 | */ 90 | ValueHolder value; 91 | }; 92 | 93 | 94 | } // namespace nyan 95 | -------------------------------------------------------------------------------- /nyan/lang_error.h: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include "error.h" 6 | #include "location.h" 7 | 8 | 9 | namespace nyan { 10 | 11 | 12 | /** 13 | * Exception class to capture problems with files, 14 | * for that, it stores line number and line offset. 15 | */ 16 | class LangError : public Error { 17 | public: 18 | LangError(const Location &location, const std::string &msg, std::vector> &&reasons = {}); 19 | 20 | /** 21 | * String representation of this error. 22 | * 23 | * @return String representation of this error. 24 | */ 25 | std::string str() const override; 26 | 27 | /** 28 | * Get a string that visualizes the error in the output using 29 | * the location and reasons for the error. 30 | * 31 | * @return String containing a pretty error message. 32 | */ 33 | virtual std::string show_problem_origin() const; 34 | 35 | protected: 36 | /** 37 | * Location of the error in a file. 38 | */ 39 | Location location; 40 | 41 | /** 42 | * Map of reasons for the error by location in the file. 43 | */ 44 | std::vector> reasons; 45 | }; 46 | 47 | 48 | /** 49 | * Thrown when encountering type problems in nyan code. 50 | */ 51 | class TypeError : public LangError { 52 | public: 53 | TypeError(const Location &location, const std::string &msg); 54 | 55 | virtual ~TypeError() = default; 56 | }; 57 | 58 | 59 | /** 60 | * Exception for name access problems and naming conflicts. 61 | */ 62 | class NameError : public LangError { 63 | public: 64 | NameError(const Location &location, 65 | const std::string &msg, 66 | const std::string &name = ""); 67 | 68 | /** 69 | * String representation of this error. 70 | * 71 | * @return String representation of this error. 72 | */ 73 | std::string str() const override; 74 | 75 | protected: 76 | /** 77 | * Name that the entitycausing this error conflicts with. 78 | */ 79 | std::string name; 80 | }; 81 | 82 | 83 | /** 84 | * Tokenize failure exception. 85 | */ 86 | class TokenizeError : public LangError { 87 | public: 88 | TokenizeError(const Location &location, 89 | const std::string &msg); 90 | }; 91 | 92 | } // namespace nyan 93 | -------------------------------------------------------------------------------- /nyan/meta_info.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "meta_info.h" 4 | 5 | #include 6 | #include 7 | 8 | #include "lang_error.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | ObjectInfo &MetaInfo::add_object(const fqon_t &name, ObjectInfo &&obj_info) { 14 | // copy location so we can use it after obj was moved. 15 | Location loc = obj_info.get_location(); 16 | 17 | auto ret = this->object_info.insert({name, std::move(obj_info)}); 18 | if (ret.second == false) { 19 | throw LangError{ 20 | loc, 21 | "object already defined", 22 | {{ret.first->second.get_location(), "first defined here"}}}; 23 | } 24 | 25 | return ret.first->second; 26 | } 27 | 28 | 29 | const MetaInfo::obj_info_t &MetaInfo::get_objects() const { 30 | return this->object_info; 31 | } 32 | 33 | 34 | ObjectInfo *MetaInfo::get_object(const fqon_t &name) { 35 | return const_cast(std::as_const(*this).get_object(name)); 36 | } 37 | 38 | 39 | const ObjectInfo *MetaInfo::get_object(const fqon_t &name) const { 40 | auto it = this->object_info.find(name); 41 | if (it == std::end(this->object_info)) { 42 | return nullptr; 43 | } 44 | return &it->second; 45 | } 46 | 47 | 48 | bool MetaInfo::has_object(const fqon_t &name) const { 49 | return this->object_info.count(name) == 1; 50 | } 51 | 52 | Namespace &MetaInfo::add_namespace(const Namespace &ns) { 53 | auto ret = this->namespaces.insert({fqnn_t(ns.to_fqon()), ns}); 54 | 55 | return ret.first->second; 56 | } 57 | 58 | Namespace *MetaInfo::get_namespace(const fqnn_t &name) { 59 | return const_cast(std::as_const(*this).get_namespace(name)); 60 | } 61 | 62 | const Namespace *MetaInfo::get_namespace(const fqnn_t &name) const { 63 | auto it = this->namespaces.find(name); 64 | if (it == std::end(this->namespaces)) { 65 | return nullptr; 66 | } 67 | return &it->second; 68 | } 69 | 70 | bool MetaInfo::has_namespace(const fqnn_t &name) const { 71 | return this->namespaces.contains(name); 72 | } 73 | 74 | std::string MetaInfo::str() const { 75 | std::ostringstream builder; 76 | 77 | for (auto &it : this->get_objects()) { 78 | builder << it.first << " -> " << it.second.str() << std::endl; 79 | } 80 | 81 | return builder.str(); 82 | } 83 | 84 | } // namespace nyan 85 | -------------------------------------------------------------------------------- /nyan/file.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | #include 7 | 8 | 9 | namespace nyan { 10 | 11 | 12 | /** 13 | * Represents a nyan data file. 14 | */ 15 | class File { 16 | public: 17 | File(const std::string &path); 18 | File(const std::string &virtual_name, std::string &&data); 19 | 20 | // moving allowed 21 | File(File &&other) noexcept = default; 22 | File &operator=(File &&other) noexcept = default; 23 | 24 | // no copies 25 | File(const File &other) = delete; 26 | File &operator=(const File &other) = delete; 27 | 28 | virtual ~File() = default; 29 | 30 | /** 31 | * Return the file name. 32 | * 33 | * @return String containing the filename. 34 | */ 35 | const std::string &get_name() const; 36 | 37 | /** 38 | * Return the file content. 39 | * 40 | * @return String containing the file's content. 41 | */ 42 | const std::string &get_content() const; 43 | 44 | /** 45 | * Return the given line number of the file. 46 | * Starts at line 1. *buhuuuuu* *sob* *mrrrmmuu* *whimper* 47 | * 48 | * @param n Line number. 49 | * 50 | * @return String containing the content of line n. 51 | */ 52 | std::string get_line(size_t n) const; 53 | 54 | /** 55 | * Return the number of lines in the file. 56 | * It will always have at least one line. 57 | * The empty file has one line of length 0. 58 | * 59 | * @return Number of lines in the file. 60 | */ 61 | size_t get_line_count() const; 62 | 63 | /** 64 | * Return a c string of the file content. 65 | * 66 | * @return Char array containing the filename. 67 | */ 68 | const char *c_str() const; 69 | 70 | /** 71 | * Return the size of the file content. 72 | * 73 | * @return Size of the file content string (number of characters). 74 | */ 75 | size_t size() const; 76 | 77 | protected: 78 | /** 79 | * Create line_ends entries from the file content. 80 | */ 81 | void extract_lines(); 82 | 83 | /** 84 | * Name of the file. 85 | */ 86 | std::string name; 87 | 88 | /** 89 | * Content of the file. 90 | */ 91 | std::string data; 92 | 93 | /** 94 | * Stores the offsets of line endings in the file content. 95 | */ 96 | std::vector line_ends; 97 | }; 98 | 99 | } // namespace nyan 100 | -------------------------------------------------------------------------------- /nyan/member_info.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "location.h" 7 | 8 | 9 | namespace nyan { 10 | 11 | class Type; 12 | 13 | 14 | /** 15 | * Stores information for a member of an Object. 16 | */ 17 | class MemberInfo { 18 | public: 19 | explicit MemberInfo(const Location &location); 20 | ~MemberInfo() = default; 21 | 22 | /** 23 | * Set the type of this member. Moves the shared pointer storing the 24 | * type. 25 | * 26 | * @param type Shared pointer to the Type. 27 | * @param initial Set to true if the member defined the type, false if the member is a 28 | * patch member or inherited. 29 | * 30 | * @return Type of the member. 31 | */ 32 | Type &set_type(std::shared_ptr &&type, bool initial); 33 | 34 | /** 35 | * Set the type of this member. Copies the shared pointer storing the 36 | * type. 37 | * 38 | * @param type Shared pointer to the Type. 39 | * @param initial Set to true if the member defined the type, false if the 40 | * member is a patch member or inherited. 41 | * 42 | * @return Type of the member. 43 | */ 44 | Type &set_type(const std::shared_ptr &type, bool initial); 45 | 46 | /** 47 | * Returns the type of this member. 48 | * 49 | * @return Type of the member. 50 | */ 51 | const std::shared_ptr &get_type() const; 52 | 53 | /** 54 | * Get the position of this member in a file. 55 | * 56 | * @return Location of the member. 57 | */ 58 | const Location &get_location() const; 59 | 60 | /** 61 | * Checks if this member contains the initial type definition, i.e. it 62 | * is not a patch member or inherited. 63 | * 64 | * @return true if the member is the initial definition, else false. 65 | */ 66 | bool is_initial_def() const; 67 | 68 | /** 69 | * Get the string representation of this member's declaration. 70 | * 71 | * @return String containing the member declaration in nyan format. 72 | */ 73 | std::string str() const; 74 | 75 | protected: 76 | /** 77 | * Location where the member was defined. 78 | */ 79 | Location location; 80 | 81 | /** 82 | * Determines whether this member definition is the initial one. 83 | */ 84 | bool initial_def; 85 | 86 | /** 87 | * Type of the member. 88 | */ 89 | std::shared_ptr type; 90 | }; 91 | 92 | 93 | } // namespace nyan 94 | -------------------------------------------------------------------------------- /nyan/value/text.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "text.h" 4 | 5 | #include 6 | 7 | #include "../compiler.h" 8 | #include "../id_token.h" 9 | #include "../lang_error.h" 10 | #include "../token.h" 11 | #include "../util.h" 12 | 13 | 14 | namespace nyan { 15 | 16 | Text::Text(const std::string &value) : 17 | value{value} {} 18 | 19 | 20 | Text::Text(const IDToken &token) { 21 | if (unlikely(token.get_type() != token_type::STRING)) { 22 | throw LangError{ 23 | token, 24 | "invalid value for text"}; 25 | } 26 | 27 | // strip the quotes 28 | this->value = token.get_first().substr(1, token.get_first().size() - 2); 29 | } 30 | 31 | 32 | ValueHolder Text::copy() const { 33 | return {std::make_shared(*this)}; 34 | } 35 | 36 | 37 | bool Text::apply_value(const Value &value, nyan_op operation) { 38 | const Text &change = dynamic_cast(value); 39 | 40 | switch (operation) { 41 | case nyan_op::ASSIGN: 42 | this->value = change.value; 43 | break; 44 | 45 | case nyan_op::ADD_ASSIGN: 46 | this->value += change.value; 47 | break; 48 | 49 | default: 50 | throw InternalError{"unknown operation requested"}; 51 | } 52 | 53 | return true; 54 | } 55 | 56 | 57 | std::string Text::str() const { 58 | return "\"" + this->value + "\""; 59 | } 60 | 61 | 62 | std::string Text::repr() const { 63 | return this->str(); 64 | } 65 | 66 | 67 | size_t Text::hash() const { 68 | return std::hash{}(this->value); 69 | } 70 | 71 | 72 | bool Text::equals(const Value &other) const { 73 | auto &other_val = dynamic_cast(other); 74 | return this->value == other_val.value; 75 | } 76 | 77 | 78 | const std::unordered_set &Text::allowed_operations(const Type &with_type) const { 79 | const static std::unordered_set ops{ 80 | nyan_op::ASSIGN, 81 | nyan_op::ADD_ASSIGN, 82 | }; 83 | 84 | switch (with_type.get_primitive_type()) { 85 | case primitive_t::TEXT: 86 | return ops; 87 | 88 | default: 89 | return no_nyan_ops; 90 | } 91 | } 92 | 93 | 94 | const BasicType &Text::get_type() const { 95 | constexpr static BasicType type{ 96 | primitive_t::TEXT, 97 | composite_t::SINGLE, 98 | }; 99 | 100 | return type; 101 | } 102 | 103 | 104 | } // namespace nyan 105 | -------------------------------------------------------------------------------- /nyan/value/file.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "file.h" 4 | 5 | #include 6 | 7 | #include "../compiler.h" 8 | #include "../id_token.h" 9 | #include "../lang_error.h" 10 | #include "../util.h" 11 | 12 | 13 | namespace nyan { 14 | 15 | Filename::Filename(const std::string &path) : 16 | path{path} { 17 | // TODO relative path resolution 18 | } 19 | 20 | 21 | Filename::Filename(const IDToken &token) { 22 | if (unlikely(token.get_type() != token_type::STRING)) { 23 | throw LangError{ 24 | token, 25 | "invalid value for filename"}; 26 | } 27 | 28 | // strip the quotes 29 | this->path = token.get_first().substr(1, token.get_first().size() - 2); 30 | } 31 | 32 | 33 | const std::string &Filename::get() const { 34 | return this->path; 35 | } 36 | 37 | 38 | ValueHolder Filename::copy() const { 39 | return {std::make_shared(*this)}; 40 | } 41 | 42 | 43 | bool Filename::apply_value(const Value &value, nyan_op operation) { 44 | const Filename &change = dynamic_cast(value); 45 | 46 | // TODO: relative path resolution 47 | 48 | switch (operation) { 49 | case nyan_op::ASSIGN: 50 | this->path = change.path; 51 | break; 52 | 53 | default: 54 | throw Error{"unknown operation requested"}; 55 | } 56 | 57 | return true; 58 | } 59 | 60 | 61 | std::string Filename::str() const { 62 | return "\"" + this->path + "\""; 63 | } 64 | 65 | 66 | std::string Filename::repr() const { 67 | return this->str(); 68 | } 69 | 70 | 71 | size_t Filename::hash() const { 72 | return std::hash{}(this->path); 73 | } 74 | 75 | 76 | bool Filename::equals(const Value &other) const { 77 | auto &other_val = dynamic_cast(other); 78 | return this->path == other_val.path; 79 | } 80 | 81 | 82 | const std::unordered_set &Filename::allowed_operations(const Type &with_type) const { 83 | const static std::unordered_set ops{ 84 | nyan_op::ASSIGN, 85 | }; 86 | 87 | switch (with_type.get_primitive_type()) { 88 | case primitive_t::FILENAME: 89 | return ops; 90 | 91 | default: 92 | return no_nyan_ops; 93 | } 94 | } 95 | 96 | 97 | const BasicType &Filename::get_type() const { 98 | constexpr static BasicType type{ 99 | primitive_t::FILENAME, 100 | composite_t::SINGLE, 101 | }; 102 | 103 | return type; 104 | } 105 | 106 | 107 | } // namespace nyan 108 | -------------------------------------------------------------------------------- /nyan/value_token.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #include "value_token.h" 3 | 4 | #include 5 | 6 | #include "ast.h" 7 | #include "compiler.h" 8 | #include "error.h" 9 | #include "location.h" 10 | 11 | namespace nyan { 12 | 13 | 14 | ValueToken::ValueToken(const IDToken &token) : 15 | container_type{composite_t::SINGLE} { 16 | this->tokens.push_back(token); 17 | } 18 | 19 | 20 | ValueToken::ValueToken(composite_t type, 21 | std::vector &tokens) : 22 | tokens{tokens} { 23 | const static std::unordered_set container_types{ 24 | composite_t::SET, 25 | composite_t::ORDEREDSET, 26 | composite_t::DICT}; 27 | 28 | if (container_types.find(type) == container_types.end()) { 29 | throw InternalError{"unknown container value type"}; 30 | } 31 | 32 | this->container_type = type; 33 | } 34 | 35 | 36 | std::string ValueToken::str() const { 37 | switch (this->container_type) { 38 | case composite_t::SINGLE: 39 | case composite_t::SET: 40 | case composite_t::ORDEREDSET: 41 | return this->tokens.at(0).str(); 42 | 43 | case composite_t::DICT: 44 | return this->tokens.at(0).str() + ": " + this->tokens.at(1).str(); 45 | 46 | default: 47 | throw InternalError{"unknown container value type"}; 48 | }; 49 | } 50 | 51 | 52 | bool ValueToken::exists() const { 53 | return this->tokens.size() > 0; 54 | } 55 | 56 | 57 | const Location &ValueToken::get_start_location() const { 58 | if (unlikely(not this->exists())) { 59 | throw InternalError{"this ValueToken doesn't exist, but you queried its location"}; 60 | } 61 | 62 | return this->tokens.at(0).get_start_location(); 63 | } 64 | 65 | 66 | size_t ValueToken::get_length() const { 67 | if (not this->exists()) { 68 | return 0; 69 | } 70 | 71 | switch (this->container_type) { 72 | case composite_t::SINGLE: 73 | case composite_t::SET: 74 | case composite_t::ORDEREDSET: 75 | return this->tokens.at(0).get_length(); 76 | 77 | case composite_t::DICT: 78 | // key token length + value token length + separating ": " length 79 | return this->tokens.at(0).get_length() + this->tokens.at(1).get_length() + 2; 80 | 81 | default: 82 | throw InternalError{"unknown container value type"}; 83 | }; 84 | } 85 | 86 | 87 | const std::vector &ValueToken::get_value() const { 88 | return this->tokens; 89 | } 90 | 91 | 92 | const composite_t &ValueToken::get_container_type() const { 93 | return this->container_type; 94 | } 95 | 96 | 97 | } // namespace nyan 98 | -------------------------------------------------------------------------------- /nyan/id_token.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #include "id_token.h" 3 | 4 | #include 5 | 6 | #include "ast.h" 7 | #include "compiler.h" 8 | #include "error.h" 9 | #include "location.h" 10 | #include "util.h" 11 | 12 | namespace nyan { 13 | 14 | 15 | IDToken::IDToken(const Token &first, 16 | TokenStream &tokens) { 17 | this->ids.push_back(first); 18 | 19 | auto token = tokens.next(); 20 | while (token->type == token_type::DOT) { 21 | token = tokens.next(); 22 | if (unlikely(token->type != token_type::ID)) { 23 | throw ASTError{"expected identifier after a dot, encountered", *token}; 24 | } 25 | this->ids.push_back(*token); 26 | token = tokens.next(); 27 | } 28 | 29 | tokens.reinsert_last(); 30 | } 31 | 32 | 33 | std::string IDToken::str() const { 34 | return util::strjoin( 35 | ".", 36 | this->ids, 37 | [](const Token &tok) -> auto & { 38 | return tok.get(); 39 | }); 40 | } 41 | 42 | 43 | bool IDToken::exists() const { 44 | return this->ids.size() > 0; 45 | } 46 | 47 | 48 | token_type IDToken::get_type() const { 49 | if (unlikely(not this->exists())) { 50 | return token_type::INVALID; 51 | } 52 | else { 53 | return this->ids.at(0).type; 54 | } 55 | } 56 | 57 | 58 | const Location &IDToken::get_start_location() const { 59 | if (unlikely(not this->exists())) { 60 | throw InternalError{ 61 | "this IDToken doesn't exist, but you queried its location"}; 62 | } 63 | 64 | return this->ids.at(0).location; 65 | } 66 | 67 | 68 | size_t IDToken::get_length() const { 69 | if (not this->exists()) { 70 | return 0; 71 | } 72 | 73 | size_t len = 0; 74 | for (auto &tok : this->ids) { 75 | // there's separating . in between each id 76 | len += tok.location.get_length() + 1; 77 | } 78 | 79 | // there's no trailing . in an id 80 | len -= 1; 81 | 82 | return len; 83 | } 84 | 85 | 86 | const std::vector &IDToken::get_components() const { 87 | return this->ids; 88 | } 89 | 90 | 91 | const std::string &IDToken::get_first() const { 92 | if (unlikely(not this->exists())) { 93 | throw InternalError{"element of non-existing IDToken requested"}; 94 | } 95 | 96 | return this->ids[0].get(); 97 | } 98 | 99 | fqon_t IDToken::to_fqon() const { 100 | return util::strjoin(".", 101 | this->ids, 102 | [](const auto &in) -> const std::string & { 103 | return in.get(); 104 | }); 105 | } 106 | 107 | } // namespace nyan 108 | -------------------------------------------------------------------------------- /test/test.nyan: -------------------------------------------------------------------------------- 1 | # test nyan file 2 | !version 1 3 | !arg 1 2 3 "Test" 4 | 5 | Dummy(): 6 | ... 7 | 8 | First(): 9 | # a first object! 10 | wat : abstract(First) = Second 11 | wat2 : abstract(children(First)) = Second 12 | wat3 : optional(First) = Second 13 | wat4 : set(children(First)) = {Second} 14 | member : int = 15 15 | nice_member : bool = True 16 | test : text = "rofl lol" 17 | bla : file = "wtf.h4x" 18 | blub : int = inf 19 | blob : float = -inf 20 | 21 | FirstPatch(): 22 | member += 3 23 | blub += 3 24 | blob -= 3 25 | wat3 = None 26 | 27 | Second(First): 28 | member *= 5.5 29 | First.nice_member = False 30 | # nap : int 31 | wat3 = None 32 | 33 | NestingBase(First): 34 | 35 | setmember : set(int) = {1,2,3,4, 36 | 5,6,7,} 37 | 38 | member = 2 39 | 40 | SomeChild(Dummy): 41 | pass 42 | 43 | AnotherChild(SomeChild): 44 | setmember : set(int) = { 45 | 1,2,3, 46 | 4,5,6 47 | } 48 | 49 | anothermember : set(int) = {1,2, 50 | 3,4,5} 51 | 52 | Fourth(First): 53 | guenther : int = 1337 54 | truth : bool = True 55 | 56 | Fifth(Fourth): 57 | guenther /= 100 58 | truth &= False 59 | 60 | Third(First): 61 | rolf : int = 42 62 | dieter : set(First) = {First, Second, Fourth} 63 | 64 | Test(Second): 65 | member += 1337 66 | gschicht : int = 0 67 | 68 | TestChild(Test): 69 | member -= 10 70 | 71 | NewParent(Second): 72 | member -= 337 73 | new_value : int = 0 74 | 75 | Patch[NewParent+](): 76 | gschicht = 235 77 | member += 1 78 | 79 | PatchPatch(): 80 | member += 19 81 | 82 | SetTest(): 83 | member : set(int) = {1, 1, 2, 3, 1} 84 | # o{} for orderedset 85 | orderedmember : orderedset(int) = o{1, 2, 3, 1, 2, 3, 2} 86 | 87 | SetPatch(): 88 | member |= o{3,4,4} 89 | orderedmember += o{4} 90 | 91 | DictTest(): 92 | dictmember : dict(int, text) = {2: "two", 4: "four"} 93 | dictmember2 : optional(dict(text, int)) = None 94 | dictmember3 : optional(dict(text, int)) = {"two": 2, "four": 4} 95 | 96 | # importing! 97 | import imported as imp 98 | import stuff.file 99 | 100 | import test 101 | 102 | UseImported(imp.ImportedObject): 103 | it_works += 10 104 | 105 | Bla(): 106 | AnotherTest(): 107 | ... 108 | 109 | Test(): 110 | ... 111 | ... 112 | -------------------------------------------------------------------------------- /nyan/value/set.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "set.h" 4 | 5 | #include "../error.h" 6 | #include "../util.h" 7 | 8 | 9 | namespace nyan { 10 | 11 | Set::Set() = default; 12 | 13 | 14 | Set::Set(std::vector &&values) { 15 | for (auto &value : values) { 16 | this->values.insert(std::move(value)); 17 | } 18 | } 19 | 20 | 21 | ValueHolder Set::copy() const { 22 | return {std::make_shared(*this)}; 23 | } 24 | 25 | 26 | bool Set::add(const ValueHolder &value) { 27 | return std::get<1>(this->values.insert(value)); 28 | } 29 | 30 | 31 | bool Set::contains(const ValueHolder &value) const { 32 | return (this->values.find(value) != std::end(this->values)); 33 | } 34 | 35 | 36 | bool Set::remove(const ValueHolder &value) { 37 | return (1 == this->values.erase(value)); 38 | } 39 | 40 | 41 | std::string Set::str() const { 42 | // same as repr(), except we use str(). 43 | 44 | std::ostringstream builder; 45 | builder << "{"; 46 | builder << util::strjoin( 47 | ", ", this->values, [](const auto &val) { 48 | return val->str(); 49 | }); 50 | builder << "}"; 51 | 52 | return builder.str(); 53 | } 54 | 55 | 56 | std::string Set::repr() const { 57 | // same as str(), except we use repr(). 58 | 59 | std::ostringstream builder; 60 | builder << "{"; 61 | builder << util::strjoin( 62 | ", ", this->values, [](const auto &val) { 63 | return val->repr(); 64 | }); 65 | builder << "}"; 66 | return builder.str(); 67 | } 68 | 69 | 70 | const std::unordered_set &Set::allowed_operations(const Type &with_type) const { 71 | const static std::unordered_set set_ops{ 72 | nyan_op::ASSIGN, 73 | nyan_op::ADD_ASSIGN, 74 | nyan_op::UNION_ASSIGN, 75 | nyan_op::SUBTRACT_ASSIGN, 76 | nyan_op::INTERSECT_ASSIGN, 77 | }; 78 | 79 | const static std::unordered_set orderedset_ops{ 80 | nyan_op::SUBTRACT_ASSIGN, 81 | nyan_op::INTERSECT_ASSIGN, 82 | }; 83 | 84 | if (not with_type.is_container()) { 85 | return no_nyan_ops; 86 | } 87 | 88 | switch (with_type.get_composite_type()) { 89 | case composite_t::SET: 90 | return set_ops; 91 | 92 | case composite_t::ORDEREDSET: 93 | return orderedset_ops; 94 | 95 | default: 96 | return no_nyan_ops; 97 | } 98 | } 99 | 100 | 101 | const BasicType &Set::get_type() const { 102 | constexpr static BasicType type{ 103 | primitive_t::CONTAINER, 104 | composite_t::SET, 105 | }; 106 | 107 | return type; 108 | } 109 | 110 | } // namespace nyan 111 | -------------------------------------------------------------------------------- /nyan/value/orderedset.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "orderedset.h" 4 | 5 | #include "../error.h" 6 | #include "../util.h" 7 | 8 | 9 | namespace nyan { 10 | 11 | OrderedSet::OrderedSet() = default; 12 | 13 | 14 | OrderedSet::OrderedSet(std::vector &&values) { 15 | for (auto &value : values) { 16 | this->values.insert(std::move(value)); 17 | } 18 | } 19 | 20 | 21 | ValueHolder OrderedSet::copy() const { 22 | return {std::make_shared(*this)}; 23 | } 24 | 25 | 26 | bool OrderedSet::add(const ValueHolder &value) { 27 | return this->values.insert(value); 28 | } 29 | 30 | 31 | bool OrderedSet::contains(const ValueHolder &value) const { 32 | return this->values.contains(value); 33 | } 34 | 35 | 36 | bool OrderedSet::remove(const ValueHolder &value) { 37 | return (this->values.erase(value) == 1); 38 | } 39 | 40 | 41 | std::string OrderedSet::str() const { 42 | std::ostringstream builder; 43 | builder << "o{"; 44 | builder << util::strjoin( 45 | ", ", this->values, [](const auto &val) { 46 | return val->str(); 47 | }); 48 | builder << "}"; 49 | 50 | return builder.str(); 51 | } 52 | 53 | 54 | std::string OrderedSet::repr() const { 55 | std::ostringstream builder; 56 | builder << "o{"; 57 | builder << util::strjoin( 58 | ", ", this->values, [](const auto &val) { 59 | return val->repr(); 60 | }); 61 | builder << "}"; 62 | return builder.str(); 63 | } 64 | 65 | 66 | const std::unordered_set &OrderedSet::allowed_operations(const Type &with_type) const { 67 | const static std::unordered_set orderedset_ops{ 68 | nyan_op::ASSIGN, 69 | nyan_op::ADD_ASSIGN, 70 | nyan_op::SUBTRACT_ASSIGN, 71 | nyan_op::INTERSECT_ASSIGN, 72 | }; 73 | 74 | const static std::unordered_set set_ops{ 75 | nyan_op::ASSIGN, 76 | nyan_op::ADD_ASSIGN, 77 | nyan_op::UNION_ASSIGN, 78 | nyan_op::SUBTRACT_ASSIGN, 79 | nyan_op::INTERSECT_ASSIGN, 80 | }; 81 | 82 | if (not with_type.is_container()) { 83 | return no_nyan_ops; 84 | } 85 | 86 | switch (with_type.get_composite_type()) { 87 | case composite_t::ORDEREDSET: 88 | return orderedset_ops; 89 | 90 | case composite_t::SET: 91 | return set_ops; 92 | 93 | default: 94 | return no_nyan_ops; 95 | } 96 | } 97 | 98 | 99 | const BasicType &OrderedSet::get_type() const { 100 | constexpr static BasicType type{ 101 | primitive_t::CONTAINER, 102 | composite_t::ORDEREDSET, 103 | }; 104 | 105 | return type; 106 | } 107 | 108 | } // namespace nyan 109 | -------------------------------------------------------------------------------- /nyan/lexer/bracket.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "bracket.h" 4 | 5 | 6 | namespace nyan::lexer { 7 | 8 | 9 | Bracket::Bracket(token_type ttype, int indent) : 10 | indentation{indent}, 11 | type{this->to_type(ttype)}, 12 | hanging{true} {} 13 | 14 | 15 | void Bracket::doesnt_hang(int new_indent) { 16 | this->hanging = false; 17 | this->indentation = new_indent; 18 | } 19 | 20 | 21 | bool Bracket::is_hanging() const { 22 | return this->hanging; 23 | } 24 | 25 | 26 | bool Bracket::matches(token_type type) const { 27 | return type == this->expected_match(); 28 | } 29 | 30 | 31 | int Bracket::get_content_indent() const { 32 | if (this->is_hanging()) { 33 | // hanging brackets store their expected indent level 34 | return this->indentation; 35 | } 36 | else { 37 | // wrapped brackets store their base indent level 38 | return this->indentation + SPACES_PER_INDENT; 39 | } 40 | } 41 | 42 | 43 | bool Bracket::closing_indent_ok(int indent) const { 44 | if (this->is_hanging()) { 45 | // hanging indent requires the closing bracket to be 46 | // closed after the opening bracket column 47 | return this->indentation <= indent; 48 | } 49 | else { 50 | return this->indentation == indent; 51 | } 52 | } 53 | 54 | 55 | std::string Bracket::get_closing_indent() const { 56 | if (this->is_hanging()) { 57 | std::ostringstream builder; 58 | builder << "at least " 59 | << this->indentation; 60 | return builder.str(); 61 | } 62 | else { 63 | return std::to_string(this->indentation); 64 | } 65 | } 66 | 67 | 68 | const char *Bracket::matching_type_str() const { 69 | return token_type_str(this->expected_match()); 70 | } 71 | 72 | 73 | bracket_type Bracket::to_type(token_type token) { 74 | switch (token) { 75 | case token_type::LPAREN: 76 | case token_type::RPAREN: 77 | return bracket_type::PAREN; 78 | case token_type::LANGLE: 79 | case token_type::RANGLE: 80 | return bracket_type::ANGLE; 81 | case token_type::LBRACKET: 82 | case token_type::RBRACKET: 83 | return bracket_type::BRACKET; 84 | case token_type::LBRACE: 85 | case token_type::RBRACE: 86 | return bracket_type::BRACE; 87 | default: 88 | throw InternalError{"tried to convert non-bracket token to bracket"}; 89 | } 90 | } 91 | 92 | 93 | token_type Bracket::expected_match() const { 94 | switch (this->type) { 95 | case bracket_type::PAREN: 96 | return token_type::RPAREN; 97 | case bracket_type::ANGLE: 98 | return token_type::RANGLE; 99 | case bracket_type::BRACKET: 100 | return token_type::RBRACKET; 101 | case bracket_type::BRACE: 102 | return token_type::RBRACE; 103 | default: 104 | throw InternalError{"unknown bracket type"}; 105 | } 106 | } 107 | 108 | 109 | } // namespace nyan::lexer 110 | -------------------------------------------------------------------------------- /nyan/namespace_finder.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ast.h" 10 | #include "namespace.h" 11 | 12 | 13 | namespace nyan { 14 | 15 | class NamespaceFinder { 16 | /** 17 | * Lookup map to find the namespace for an alias. 18 | */ 19 | using namespace_alias_t = std::unordered_map; 20 | 21 | /** 22 | * In the current namespace, namespaces in here were imported. 23 | */ 24 | using namespace_available_t = std::unordered_set; 25 | 26 | public: 27 | NamespaceFinder(AST &&ast); 28 | 29 | /** 30 | * Imports another namespace into the finder. 31 | * 32 | * @param ns Namespace that should be imported. 33 | */ 34 | void add_import(const Namespace &ns); 35 | 36 | /** 37 | * Imports another namespace into the finder by using an alias. 38 | * 39 | * @param alias Token with the name of the alias. 40 | * @param ns Namespace that should be imported. 41 | */ 42 | void add_alias(const Token &alias, const Namespace &destination); 43 | 44 | /** 45 | * Check if a name conflicts wth other names in the namespace, i.e. the 46 | * name is already used by an alias or an import. 47 | * 48 | * @return true if the name is already used, else false. 49 | */ 50 | bool check_conflict(const std::string &name) const; 51 | 52 | /** 53 | * Get the identifier of a namespace associated with an alias. 54 | * 55 | * @param name IDToken with an alias as its first component. 56 | * 57 | * @return Identifier of the namespace. 58 | */ 59 | fqon_t expand_alias(const IDToken &name) const; 60 | 61 | /** 62 | * Search for the object/member identifier of an object/member reference 63 | * in a given namespace. 64 | * 65 | * @param name IDToken with an object/member reference. 66 | * 67 | * @return Identifier of the object/member. 68 | */ 69 | fqon_t find(const Namespace ¤t, 70 | const IDToken &search, 71 | const MetaInfo &typedb) const; 72 | 73 | /** 74 | * Get the AST (abstract syntax tree) of this namespace. 75 | * 76 | * @return AST of this namespace. 77 | */ 78 | const AST &get_ast() const; 79 | 80 | /** 81 | * Get the string representation of this namespace. 82 | * 83 | * @return String representation of this namespace. 84 | */ 85 | std::string str() const; 86 | 87 | public: 88 | /** 89 | * Abstract syntax tree of the namespace of this finder. 90 | */ 91 | AST ast; 92 | 93 | /** 94 | * Directly imported namespaces. 95 | */ 96 | namespace_available_t imports; 97 | 98 | /** 99 | * Namespaces imported by alias. 100 | */ 101 | namespace_alias_t aliases; 102 | }; 103 | 104 | } // namespace nyan 105 | -------------------------------------------------------------------------------- /nyan/value/boolean.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "boolean.h" 4 | 5 | #include 6 | 7 | #include "../compiler.h" 8 | #include "../lang_error.h" 9 | #include "../token.h" 10 | #include "../util.h" 11 | 12 | 13 | namespace nyan { 14 | 15 | Boolean::Boolean(const bool &value) : 16 | value{value} {} 17 | 18 | 19 | Boolean::Boolean(const IDToken &token) { 20 | if (unlikely(token.get_type() != token_type::ID)) { 21 | throw LangError{ 22 | token, 23 | "invalid value for boolean"}; 24 | } 25 | 26 | const std::string &token_value = token.get_first(); 27 | 28 | if (token_value == "True") { 29 | this->value = true; 30 | } 31 | else if (token_value == "False") { 32 | this->value = false; 33 | } 34 | else { 35 | throw LangError{ 36 | token, 37 | "unknown boolean value (did you use 'True' and 'False'?)"}; 38 | } 39 | } 40 | 41 | 42 | ValueHolder Boolean::copy() const { 43 | return ValueHolder{ 44 | std::make_shared(dynamic_cast(*this))}; 45 | } 46 | 47 | 48 | bool Boolean::apply_value(const Value &value, nyan_op operation) { 49 | const Boolean &change = dynamic_cast(value); 50 | 51 | switch (operation) { 52 | case nyan_op::ASSIGN: 53 | this->value = change.value; 54 | break; 55 | 56 | case nyan_op::UNION_ASSIGN: 57 | this->value |= change.value; 58 | break; 59 | 60 | case nyan_op::INTERSECT_ASSIGN: 61 | this->value &= change.value; 62 | break; 63 | 64 | default: 65 | throw Error{"unknown operation requested"}; 66 | } 67 | 68 | return true; 69 | } 70 | 71 | 72 | std::string Boolean::str() const { 73 | if (this->value) { 74 | return "True"; 75 | } 76 | else { 77 | return "False"; 78 | } 79 | } 80 | 81 | 82 | std::string Boolean::repr() const { 83 | return this->str(); 84 | } 85 | 86 | 87 | size_t Boolean::hash() const { 88 | return std::hash{}(this->value); 89 | } 90 | 91 | 92 | bool Boolean::equals(const Value &other) const { 93 | auto &other_val = dynamic_cast(other); 94 | return this->value == other_val.value; 95 | } 96 | 97 | 98 | const std::unordered_set &Boolean::allowed_operations(const Type &with_type) const { 99 | const static std::unordered_set ops{ 100 | nyan_op::ASSIGN, 101 | nyan_op::UNION_ASSIGN, 102 | nyan_op::INTERSECT_ASSIGN, 103 | }; 104 | 105 | switch (with_type.get_primitive_type()) { 106 | case primitive_t::BOOLEAN: 107 | return ops; 108 | 109 | default: 110 | return no_nyan_ops; 111 | } 112 | } 113 | 114 | 115 | const BasicType &Boolean::get_type() const { 116 | constexpr static BasicType type{ 117 | primitive_t::BOOLEAN, 118 | composite_t::SINGLE, 119 | }; 120 | 121 | return type; 122 | } 123 | 124 | 125 | } // namespace nyan 126 | -------------------------------------------------------------------------------- /nyan/parser.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include "ast.h" 8 | #include "token.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | class Database; 14 | class File; 15 | class Member; 16 | class Object; 17 | 18 | /** 19 | * The parser for nyan. 20 | */ 21 | class Parser { 22 | public: 23 | Parser(); 24 | virtual ~Parser() = default; 25 | 26 | /** 27 | * Parse a nyan file and return its AST (abstact syntax tree). 28 | * 29 | * @param file Shared pointer to a nyan data file. 30 | * 31 | * @return AST of the nyan file. 32 | */ 33 | AST parse(const std::shared_ptr &file); 34 | 35 | protected: 36 | /** 37 | * Get the token stream of a nyan file. 38 | * 39 | * @param file Shared pointer to a nyan data file. 40 | * 41 | * @return List of tokens in the file. 42 | */ 43 | std::vector tokenize(const std::shared_ptr &file) const; 44 | 45 | /** 46 | * Create an AST (abstact syntax tree) from a token list. 47 | * 48 | * @param tokens List of tokens. 49 | * 50 | * @return AST of the token list. 51 | */ 52 | AST create_ast(const std::vector &tokens) const; 53 | 54 | #if 0 55 | /** 56 | * Create nyan objects and place them in the database 57 | * (which was specified in the constructor) 58 | */ 59 | std::vector create_objects(const AST &ast); 60 | 61 | /** 62 | * Add the object inheritance to an object to be constructed 63 | * from that AST part. 64 | */ 65 | void add_inheritance(Object *obj, const ASTObject &astobj) const; 66 | 67 | /** 68 | * Add the patch target objects from the AST to the object. 69 | */ 70 | void add_patch_targets(Object *obj, const ASTObject &astobj) const ; 71 | 72 | /** 73 | * Determine the types of members, optionally consult parent objects 74 | * in the database to get the type. 75 | */ 76 | std::unordered_map member_type_creation(Object *obj, const ASTObject &astobj) const; 77 | 78 | /** 79 | * Create member entries which can then be stored in an object. 80 | */ 81 | std::vector> create_members(Object *obj, const ASTObject &astobj, std::unordered_map &member_types) const; 82 | 83 | /** 84 | * Create a Value from an AST member value. 85 | * Check if the ast value can be assigned (with the given operation) 86 | * to the member type determined already. 87 | */ 88 | ValueContainer create_member_value(const Type *member_type, const ASTMemberValue &astmembervalue) const; 89 | 90 | /** 91 | * Create a Value from a single value token. 92 | */ 93 | ValueContainer value_from_value_token(const Token &value_token) const; 94 | 95 | /** 96 | * Store the inheritance modifications of a patch. 97 | */ 98 | void inheritance_mod(Object *obj, const ASTObject &astobj) const ; 99 | #endif 100 | }; 101 | 102 | } // namespace nyan 103 | -------------------------------------------------------------------------------- /nyan/location.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | namespace nyan { 9 | 10 | class File; 11 | class Token; 12 | class IDToken; 13 | 14 | 15 | /** 16 | * Location of some data in nyan. 17 | * Used to display error messages for positions in the file. 18 | */ 19 | class Location { 20 | public: 21 | Location() = default; 22 | Location(const Token &token); 23 | Location(const IDToken &token); 24 | Location(const std::shared_ptr &file, 25 | int line, 26 | int line_offset, 27 | int length = 0); 28 | explicit Location(const std::string &custom); 29 | 30 | ~Location() = default; 31 | 32 | /** 33 | * Checks if the location is a built-in nyan location. 34 | * 35 | * @return true if the location is built-in, else false. 36 | */ 37 | bool is_builtin() const; 38 | 39 | /** 40 | * Get the message for the location. 41 | * 42 | * @return String containing the message. 43 | */ 44 | const std::string &get_msg() const; 45 | 46 | /** 47 | * Get the line index of the location in the file. 48 | * 49 | * @return Line index in the file (starting from 1). 50 | */ 51 | int get_line() const; 52 | 53 | /** 54 | * Get the line offset of the location in its line. 55 | * 56 | * @return Line offset in the line. 57 | */ 58 | int get_line_offset() const; 59 | 60 | /** 61 | * Get the content length of the location in the file. 62 | * 63 | * @return Number of characters in the location content. 64 | */ 65 | int get_length() const; 66 | 67 | /** 68 | * Get the line of the location in the file. 69 | * 70 | * @return String containing the contents of the line. 71 | */ 72 | std::string get_line_content() const; 73 | 74 | /** 75 | * Get the file for the location. 76 | * 77 | * @return File handle. Can be \p nullptr. 78 | */ 79 | const std::shared_ptr &get_file() const; 80 | 81 | /** 82 | * Append the string representation of the location to a given output stream. 83 | * 84 | * @param builder Output stream the string representation is appended to. 85 | */ 86 | void str(std::ostringstream &builder) const; 87 | 88 | protected: 89 | /** 90 | * if true, this location does not point to a file, 91 | * instead it describes some built-in location of nyan itself. 92 | */ 93 | bool _is_builtin = false; 94 | 95 | /** 96 | * Shared pointer to the file of the location. 97 | */ 98 | std::shared_ptr file; 99 | 100 | /** 101 | * Line index in the file (starting from 1). 102 | */ 103 | int line; 104 | 105 | /** 106 | * Line offset in its line. 107 | */ 108 | int line_offset; 109 | 110 | /** 111 | * Length of the content the location associates with. 112 | */ 113 | int length; 114 | 115 | /** 116 | * Message for built-in locations. 117 | */ 118 | std::string msg; 119 | }; 120 | 121 | } // namespace nyan 122 | -------------------------------------------------------------------------------- /nyan/lang_error.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2019-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "lang_error.h" 4 | 5 | #include 6 | #include 7 | 8 | #include "util.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | using namespace std::string_literals; 14 | 15 | LangError::LangError(const Location &location, 16 | const std::string &msg, 17 | std::vector> &&reasons) : 18 | Error{msg}, 19 | location{location}, 20 | reasons{std::move(reasons)} {} 21 | 22 | 23 | std::string LangError::str() const { 24 | std::ostringstream builder; 25 | 26 | builder << "\x1b[1m"; 27 | 28 | this->location.str(builder); 29 | 30 | builder << "\x1b[31;1merror:\x1b[39;49m " << this->msg 31 | << "\x1b[0m"; 32 | 33 | return builder.str(); 34 | } 35 | 36 | 37 | static void visualize_location(std::ostringstream &builder, const Location &location) { 38 | size_t offset = location.get_line_offset(); 39 | size_t length = location.get_length(); 40 | 41 | if (length > 0) { 42 | length -= 1; 43 | } 44 | else { 45 | length = 0; 46 | } 47 | 48 | builder << location.get_line_content() << std::endl 49 | << std::string(offset, ' ') << "\x1b[36;1m^" 50 | << std::string(length, '~') << "\x1b[m"; 51 | } 52 | 53 | 54 | std::string LangError::show_problem_origin() const { 55 | std::ostringstream builder; 56 | 57 | if (this->location.is_builtin()) { 58 | builder << this->location.get_msg(); 59 | } 60 | else { 61 | visualize_location(builder, this->location); 62 | } 63 | 64 | for (const auto &reason : this->reasons) { 65 | const Location &loc = reason.first; 66 | const std::string &msg = reason.second; 67 | 68 | builder << std::endl 69 | << "\x1b[1m"; 70 | 71 | loc.str(builder); 72 | 73 | builder << "\x1b[30;1mnote:\x1b[39;49m " 74 | << msg 75 | << "\x1b[0m" << std::endl; 76 | 77 | visualize_location(builder, loc); 78 | builder << std::endl; 79 | } 80 | 81 | return builder.str(); 82 | } 83 | 84 | 85 | TypeError::TypeError(const Location &location, const std::string &msg) : 86 | LangError{location, msg} {} 87 | 88 | 89 | NameError::NameError(const Location &location, 90 | const std::string &msg, 91 | const std::string &name) : 92 | LangError{location, msg}, 93 | name{name} {} 94 | 95 | 96 | std::string NameError::str() const { 97 | std::ostringstream builder; 98 | builder << "\x1b[1m"; 99 | 100 | this->location.str(builder); 101 | 102 | builder << "\x1b[31;1mname error:\x1b[39;49m "; 103 | builder << this->msg; 104 | 105 | if (not this->name.empty()) { 106 | builder << ": '" << this->name << "'"; 107 | } 108 | 109 | builder << "\x1b[0m"; 110 | 111 | return builder.str(); 112 | } 113 | 114 | 115 | TokenizeError::TokenizeError(const Location &location, 116 | const std::string &msg) : 117 | LangError{location, msg} {} 118 | 119 | } // namespace nyan 120 | -------------------------------------------------------------------------------- /nyan/lexer/impl.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include "../lang_error.h" 8 | #include "bracket.h" 9 | 10 | 11 | namespace nyan::lexer { 12 | 13 | /// Interface with flex generated lexer. 14 | class Impl { 15 | public: 16 | explicit Impl(const std::shared_ptr &file); 17 | 18 | ~Impl(); 19 | 20 | /** No copies. No moves. */ 21 | Impl(const Impl &other) = delete; 22 | Impl(Impl &&other) = delete; 23 | const Impl &operator=(const Impl &other) = delete; 24 | Impl &&operator=(Impl &&other) = delete; 25 | 26 | /** Produce a token by reading the input. */ 27 | Token generate_token(); 28 | 29 | /** @name FlexInterfaceMethods 30 | * Methods used by the flex generated lexer. 31 | */ 32 | ///@{ 33 | 34 | /** Advance the line position by match length. */ 35 | void advance_linepos(); 36 | 37 | /** 38 | * Try to read `max_size` bytes to `buffer`. 39 | * Return number of bytes read. 40 | */ 41 | int read_input(char *buffer, int max_size); 42 | 43 | /** 44 | * Create a token with correct text position and value. 45 | * Add the token to the queue. 46 | */ 47 | void token(token_type type); 48 | 49 | /** Tokenize error was encountered. */ 50 | TokenizeError error(const std::string &msg); 51 | 52 | /** Emit line ending token for current position. */ 53 | void endline(); 54 | 55 | /** Generate indentation tokens based on given depth. */ 56 | void handle_indent(int depth); 57 | 58 | ///@} 59 | 60 | protected: 61 | /** 62 | * Indentation enforcement in parens requires to track 63 | * the open and closing parens `(<[{}]>)`. 64 | */ 65 | void track_brackets(token_type type, int token_start); 66 | 67 | /** Input file used for tokenization. */ 68 | std::shared_ptr file; 69 | 70 | /** String stream which is fed into the lexer. */ 71 | std::istringstream input; 72 | 73 | /** Available tokens. */ 74 | std::queue tokens; 75 | 76 | /** The indentation level of the previous line. */ 77 | int previous_indent = 0; 78 | 79 | /** The bracket stack remembers current open positions of `(<[{}]>)`. */ 80 | std::stack brackets; 81 | 82 | /** 83 | * Set to true when a opening bracket was encountered. 84 | * If it is true and the next token is a newline, the bracket is hanging. 85 | * It will be set to false when the token after a opening 86 | * bracket was processed. 87 | */ 88 | bool possibly_hanging = false; 89 | 90 | /** 91 | * True when the indentation in brackets doesn't match. 92 | * This is only the case when for a closing bracket. 93 | */ 94 | bool bracketcloseindent_expected = false; 95 | 96 | /** The default line positon at the very beginning of one line. */ 97 | static constexpr int linepos_start = 0; 98 | 99 | /** Current position in a line. */ 100 | int linepos = linepos_start; 101 | 102 | /** yyscan_t object: pointer to flex generated lexer */ 103 | void *scanner{nullptr}; 104 | }; 105 | 106 | } // namespace nyan::lexer 107 | -------------------------------------------------------------------------------- /extra/syntax_highlighting/kate/nyan.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | import 6 | as 7 | 8 | 9 | int 10 | float 11 | text 12 | file 13 | bool 14 | 15 | 16 | set 17 | orderedset 18 | dict 19 | 20 | 21 | True 22 | False 23 | inf 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /doc/README.md: -------------------------------------------------------------------------------- 1 | # nyan 2 | 3 | ## WTF? 4 | 5 | **nyan** is a strongly typed hierarchical key-value database with patch 6 | functionality and inheritance. 7 | 8 | 9 | ## Design idea 10 | 11 | [openage](https://github.com/SFTtech/openage) requires a very complex data 12 | storage to represent the hierarchy of its objects. Research and technology 13 | affects numerous units, civilization bonuses, monk conversions and all that 14 | with the goal to be ultimatively moddable by the community: 15 | 16 | Current data representation formats make this nearly impossible to 17 | accomplish. Readability problems or huge lexical overhead led us to 18 | design a language crafted for our needs. 19 | 20 | Enter **nyan**, which is our approach to store data in a new way™. 21 | 22 | 23 | ## Core Principles 24 | 25 | * Human-readable language 26 | * More or less compact (readability > memory) 27 | * General-purpose data definition + database features 28 | * Changing data with patches at runtime 29 | * Moddability of defined data 30 | * Portable 31 | * Object-oriented 32 | * Typesafe 33 | 34 | 35 | ## Srsly? 36 | 37 | Let's create a new unit with a mod: a japanese tentacle monster. 38 | 39 | ``` python 40 | TentacleMonster(Unit): 41 | name = "Splortsch" 42 | hp = 2000 43 | 44 | Creation(): 45 | creates += {TentacleMonster} 46 | 47 | TentacleMod(Mod): 48 | name = "Add the allmighty tentacle monster to your holy army" 49 | patches = {Creation} 50 | ``` 51 | 52 | Things like `Unit` and `Mod` are provided by the game engine, 53 | `TownCenter` is provided by the base game data. 54 | 55 | When the engine activates the mod, your town center can create the new unit. 56 | 57 | 58 | ## Why nyan? 59 | 60 | * nyan allows easy modding 61 | * Data packs ship configuration data and game content as `.nyan` files 62 | * Modpacks can change and extend existing information easily, by applying data "patches" 63 | * Patches are applied whenever the `libnyan` user decides when or where to do so 64 | * nyan is typesafe 65 | * The type of a member is stored when declaring it 66 | * The only things nyan can do: Hierarchical data declaration and patches 67 | * No member type casts 68 | * Only allowed operators for a member type can be called 69 | * nyan is invented here™ 70 | * we can change the specification to our needs whenever we want 71 | 72 | ## Specification 73 | 74 | A full specification is provided [here](nyan_specification.md). 75 | 76 | ## Integration into a Game Engine 77 | 78 | * Some `.nyan` files are shipped with the game engine 79 | * They describe things the engine is capable of, basically the mod API 80 | * That way, the engine can be sure that things exist 81 | * The engine can access all nyan file contents with type safety 82 | * The data files of the game then extend and change the API `nyan::Object`s 83 | 84 | * The nyan database provides a C++ API used by the game engine 85 | * Can parse `.nyan` files and add all information to the database 86 | * Provides hooks so the engine can react on internal changes 87 | 88 | * Data does not contain any executable code but can specify function names 89 | and parameters. The game engine is responsible for calling those 90 | functions or redirecting to custom scripts 91 | -------------------------------------------------------------------------------- /nyan/object_state.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "member.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | class ObjectChanges; 14 | class ObjectInfo; 15 | 16 | 17 | /** 18 | * Single object state storage. 19 | */ 20 | class ObjectState { 21 | friend class Database; 22 | 23 | public: 24 | /** 25 | * Creation of an initial object state. 26 | */ 27 | ObjectState(std::deque &&parents); 28 | 29 | /** 30 | * Apply changes to this object with a patch. 31 | * 32 | * @param mod Shared pointer to the patch modifying this object. 33 | * @param mod_info Metadata information object of the patch. 34 | * @param tracker Tracker for changes to this object. 35 | */ 36 | void apply(const std::shared_ptr &mod, 37 | const ObjectInfo &mod_info, 38 | ObjectChanges &tracker); 39 | 40 | /** 41 | * Copy this object state. 42 | * 43 | * @return Shared pointer to the copy of the object state. 44 | */ 45 | std::shared_ptr copy() const; 46 | 47 | /** 48 | * Get the parents of this object. 49 | * 50 | * @return Double-ended queue with identifiers for the parent objects. 51 | */ 52 | const std::deque &get_parents() const; 53 | 54 | /** 55 | * Check if the object has a member with a given identifier. 56 | * 57 | * @param name Identifier of the member. 58 | * 59 | * @return true if the object has a member with the identifier, else false. 60 | */ 61 | bool has(const memberid_t &name) const; 62 | 63 | /** 64 | * Get the pointer to a member with a given identifier. 65 | * 66 | * @param name Identifier of the member. 67 | * 68 | * @return Pointer to the member if the object has this member, else nullptr. 69 | */ 70 | Member *get(const memberid_t &name); 71 | 72 | /** 73 | * Get the pointer to a member with a given identifier. 74 | * 75 | * @param name Identifier of the member. 76 | * 77 | * @return Pointer to the member object if the object has this member, else nullptr. 78 | */ 79 | const Member *get(const memberid_t &name) const; 80 | 81 | /** 82 | * Get the members of this object. 83 | * 84 | * @return Map of the member objects by member identifer. 85 | */ 86 | const std::unordered_map &get_members() const; 87 | 88 | /** 89 | * Get the string representation of this object state. 90 | * 91 | * @return String representation of this object state. 92 | */ 93 | std::string str() const; 94 | 95 | private: 96 | /** 97 | * Replace the member map. Used for creating initial object states. 98 | * 99 | * @param members Map of the member objects by member identifer. 100 | */ 101 | void set_members(std::unordered_map &&members); 102 | 103 | /** 104 | * Parent objects. 105 | */ 106 | std::deque parents; 107 | 108 | /** 109 | * Member objects storage. 110 | */ 111 | std::unordered_map members; 112 | 113 | // The object location is stored in the metainfo-database. 114 | }; 115 | 116 | } // namespace nyan 117 | -------------------------------------------------------------------------------- /nyan/state.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "config.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | class ObjectState; 14 | class View; 15 | 16 | 17 | /** 18 | * Database state for some point in time. 19 | * Contains ObjectStates. 20 | * Has a reference to the previous state. 21 | */ 22 | class State { 23 | public: 24 | State(const std::shared_ptr &previous_state); 25 | 26 | State(); 27 | 28 | /** 29 | * Get an object state for a given object identifier name. 30 | * 31 | * @param fqon Identifier of the object. 32 | * 33 | * @return Shared pointer to the object state if it exists, else nullptr. 34 | */ 35 | const std::shared_ptr *get(const fqon_t &fqon) const; 36 | 37 | /** 38 | * Add an object state to the database state. This can only be done for the initial 39 | * state, i.e. there's no previous state. Why? The database must be filled 40 | * at some point. 41 | * 42 | * @param fqon Identifier of the object. 43 | * @param obj Shared pointer to the state of the object. 44 | * 45 | * @return ObjectState that was added to the state. 46 | */ 47 | ObjectState &add_object(const fqon_t &name, std::shared_ptr &&obj); 48 | 49 | /** 50 | * Add and potentially replace the object states of this state by 51 | * object states from another database state. 52 | * 53 | * @param Shared pointer to the other database state. 54 | */ 55 | void update(std::shared_ptr &&obj); 56 | 57 | /** 58 | * Copy an object state from an origin view to this state. 59 | * If it is in this state already, don't copy it. 60 | * 61 | * @param name Identifier of the object. 62 | * @param t Time for the object state is retrieved. 63 | * @param origin View from which the object state is copied. 64 | * 65 | * @return Shared pointer to the object state. 66 | */ 67 | const std::shared_ptr ©_object(const fqon_t &name, 68 | order_t t, 69 | std::shared_ptr &origin); 70 | 71 | /** 72 | * Get the previous database state. 73 | * 74 | * @return Shared pointer to the previous database state, 75 | * nullptr if this is the initial state. 76 | */ 77 | const std::shared_ptr &get_previous_state() const; 78 | 79 | /** 80 | * Return the object states stored in this state. 81 | * 82 | * @return Map of shared pointers to object states by object identifier. 83 | */ 84 | const std::unordered_map> & 85 | get_objects() const; 86 | 87 | /** 88 | * Get the string representation of this state. 89 | * 90 | * @return String representation of this state. 91 | */ 92 | std::string str() const; 93 | 94 | private: 95 | /** 96 | * Object states in the database state. 97 | */ 98 | std::unordered_map> objects; 99 | 100 | /** 101 | * Previous state. 102 | */ 103 | std::shared_ptr previous_state; 104 | }; 105 | 106 | } // namespace nyan 107 | -------------------------------------------------------------------------------- /nyan/ops.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "error.h" 9 | 10 | namespace nyan { 11 | 12 | class Token; 13 | 14 | /** 15 | * Operation identifiers for all builtin member types 16 | */ 17 | enum class nyan_op { 18 | INVALID, 19 | ADD, 20 | ADD_ASSIGN, 21 | ASSIGN, 22 | DIVIDE, 23 | DIVIDE_ASSIGN, 24 | INTERSECT_ASSIGN, 25 | MULTIPLY, 26 | MULTIPLY_ASSIGN, 27 | SUBTRACT, 28 | SUBTRACT_ASSIGN, 29 | UNION_ASSIGN, 30 | }; 31 | 32 | 33 | /** 34 | * Inheritance modification operations. 35 | */ 36 | enum class inher_change_t { 37 | ADD_FRONT, 38 | ADD_BACK 39 | }; 40 | 41 | 42 | /** 43 | * Convenience variable to be used whenever no operation is allowed. 44 | * It comes in handy as the allowance sets are const static function 45 | * variables normally, and the function returns a reference. 46 | * As this variable exists globally, we can return a reference 47 | * without redefining an empty set again and again. 48 | */ 49 | extern const std::unordered_set no_nyan_ops; 50 | 51 | 52 | /** 53 | * Get a string representation of a nyan operation. 54 | * 55 | * @param op Nyan operation. 56 | * 57 | * @return Char array with the string representation of the nyan operation. 58 | */ 59 | constexpr const char *op_to_string(nyan_op op) { 60 | switch (op) { 61 | case nyan_op::ADD: 62 | return "+"; 63 | case nyan_op::ADD_ASSIGN: 64 | return "+="; 65 | case nyan_op::ASSIGN: 66 | return "="; 67 | case nyan_op::DIVIDE: 68 | return "/"; 69 | case nyan_op::DIVIDE_ASSIGN: 70 | return "/="; 71 | case nyan_op::INTERSECT_ASSIGN: 72 | return "&="; 73 | case nyan_op::INVALID: 74 | return "=INVALID="; 75 | case nyan_op::MULTIPLY: 76 | return "*"; 77 | case nyan_op::MULTIPLY_ASSIGN: 78 | return "*="; 79 | case nyan_op::SUBTRACT: 80 | return "-"; 81 | case nyan_op::SUBTRACT_ASSIGN: 82 | return "-="; 83 | case nyan_op::UNION_ASSIGN: 84 | return "|="; 85 | } 86 | return "unhandled nyan_op"; 87 | } 88 | 89 | 90 | /** 91 | * Get a nyan operation from a string of an operator. 92 | * 93 | * @param str String with an operator. 94 | * 95 | * @return A nyan operation. 96 | */ 97 | nyan_op op_from_string(const std::string &str); 98 | 99 | 100 | /** 101 | * Get a nyan operation from a token of an operator. 102 | * 103 | * @param str Token with an operator string. 104 | * 105 | * @return A nyan operation. 106 | */ 107 | nyan_op op_from_token(const Token &token); 108 | 109 | 110 | /** 111 | * A nyan operator, to be created from either a token 112 | * 113 | * TODO: What is this? 114 | */ 115 | class Operator { 116 | public: 117 | Operator(const Token &token); 118 | virtual ~Operator() = default; 119 | 120 | const nyan_op &get(); 121 | 122 | protected: 123 | const nyan_op op; 124 | }; 125 | 126 | 127 | } // namespace nyan 128 | 129 | namespace std { 130 | 131 | /** 132 | * Hash for the nyan_op enum class. Thanks C++! 133 | */ 134 | template <> 135 | struct hash { 136 | size_t operator()(const nyan::nyan_op &x) const { 137 | return static_cast(x); 138 | } 139 | }; 140 | 141 | } // namespace std 142 | -------------------------------------------------------------------------------- /nyan/member.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "member.h" 4 | 5 | #include 6 | 7 | #include "compiler.h" 8 | #include "util.h" 9 | #include "value/none.h" 10 | 11 | 12 | namespace nyan { 13 | 14 | 15 | Member::Member(override_depth_t depth, 16 | nyan_op operation, 17 | Type declared_type, 18 | ValueHolder &&value) : 19 | override_depth{depth}, 20 | operation{operation}, 21 | declared_type{declared_type}, 22 | value{std::move(value)} {} 23 | 24 | 25 | Member::Member(const Member &other) : 26 | override_depth{other.override_depth}, 27 | operation{other.operation}, 28 | declared_type{other.declared_type}, 29 | value{other.value->copy()} {} 30 | 31 | 32 | Member::Member(Member &&other) noexcept 33 | : 34 | override_depth{std::move(other.override_depth)}, 35 | operation{std::move(other.operation)}, 36 | declared_type{std::move(other.declared_type)}, 37 | value{std::move(other.value)} {} 38 | 39 | 40 | Member &Member::operator=(const Member &other) { 41 | *this = Member{other}; 42 | return *this; 43 | } 44 | 45 | 46 | Member &Member::operator=(Member &&other) noexcept { 47 | this->override_depth = std::move(other.override_depth); 48 | this->operation = std::move(other.operation); 49 | this->value = std::move(other.value); 50 | return *this; 51 | } 52 | 53 | 54 | nyan_op Member::get_operation() const { 55 | return this->operation; 56 | } 57 | 58 | 59 | const Value &Member::get_value() const { 60 | if (unlikely(not this->value.exists())) { 61 | throw InternalError{"fetched nonexisting value of member"}; 62 | } 63 | 64 | return *this->value; 65 | } 66 | 67 | 68 | bool Member::apply(const Member &change) { 69 | // if the change requests an operator overwrite, apply it. 70 | if (change.override_depth > 0) { 71 | // TODO: need to revisit this... 72 | throw InternalError{"operator overrides not really implemented"}; 73 | 74 | this->override_depth = change.override_depth - 1; 75 | this->operation = change.get_operation(); 76 | // TODO: None can't be copied! 77 | this->value = change.get_value().copy(); 78 | return true; 79 | } 80 | 81 | // TODO: if there's more types that require a value override 82 | // maybe enhance Value::apply and check which type level accepts given operator with value type 83 | auto &change_value = change.value.get_ptr(); 84 | 85 | if (util::isinstance(change_value.get())) { 86 | // nyan_op::ASSIGN was validated during type check already 87 | this->value = change_value; 88 | } 89 | else if (util::isinstance(*this->value)) { 90 | if (change.get_operation() == nyan_op::ASSIGN) { 91 | this->value = change_value; 92 | } 93 | // else swallow all operations 94 | } 95 | else { 96 | // keep operator as-is and modify the value. 97 | return this->value->apply(change); 98 | } 99 | return true; 100 | } 101 | 102 | 103 | std::string Member::str() const { 104 | std::ostringstream builder; 105 | 106 | if (this->operation != nyan_op::INVALID) { 107 | builder << op_to_string(this->operation); 108 | } 109 | 110 | if (this->value.exists()) { 111 | builder << " " << this->value->repr(); 112 | } 113 | 114 | return std::move(builder).str(); 115 | } 116 | 117 | } // namespace nyan 118 | -------------------------------------------------------------------------------- /nyan/value/value_holder.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | 6 | #include "../api_error.h" 7 | #include "../concept.h" 8 | #include "../util.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | class Value; 14 | 15 | 16 | /** 17 | * Wrapper class to hold values by shared pointer. 18 | * Used to redirect the hashing and comparison function inside the ptr. 19 | * The shared_ptr must be initialized when using ValueHolder. 20 | */ 21 | class ValueHolder { 22 | public: 23 | ValueHolder(); 24 | ValueHolder(std::shared_ptr &&value); 25 | ValueHolder(const std::shared_ptr &value); 26 | 27 | /** 28 | * Assign a new value to the holder. 29 | */ 30 | ValueHolder &operator=(const std::shared_ptr &value); 31 | 32 | /** 33 | * Get the shared pointer to the value wrapped by this holder. 34 | * 35 | * @return Shared pointer to this holder's value. 36 | */ 37 | const std::shared_ptr &get_ptr() const; 38 | 39 | /** 40 | * Get a shared pointer to the value stored by this holder. 41 | * 42 | * Auto-converts the value to type T, which must be a nyan::Value type. 43 | * 44 | * @tparam T Type of the value to retrieve. 45 | * 46 | * @return Value stored by this holder. 47 | * 48 | * @throws InternalError if the value is not of type T. 49 | */ 50 | template 51 | const std::shared_ptr get_value_ptr() const; 52 | 53 | /** 54 | * Check if this holder points to a value. 55 | * 56 | * @return true if \p this->value is not nullptr, else false. 57 | */ 58 | bool exists() const; 59 | 60 | /** 61 | * Get the value stored at this holder's pointer. 62 | * 63 | * @return Value stored at this holder's pointer. 64 | */ 65 | Value &operator*() const; 66 | 67 | /** 68 | * Get the shared pointer of this ValueHolder. 69 | * 70 | * @return Shared pointer to this holder's value. 71 | */ 72 | Value *operator->() const; 73 | 74 | /** 75 | * Compare two ValueHolders for equality. 76 | * 77 | * @return true if the values stored by the holders are equal, else false. 78 | */ 79 | bool operator==(const ValueHolder &other) const; 80 | 81 | /** 82 | * Compare two ValueHolders for inequality. 83 | * 84 | * @return true if the values stored by the holders are not equal, else false. 85 | */ 86 | bool operator!=(const ValueHolder &other) const; 87 | 88 | protected: 89 | /** 90 | * Shared pointer to the wrapped value. 91 | */ 92 | std::shared_ptr value; 93 | }; 94 | 95 | 96 | template 97 | const std::shared_ptr ValueHolder::get_value_ptr() const { 98 | auto ret = std::dynamic_pointer_cast(this->value); 99 | 100 | if (not ret) { 101 | throw APIError{"ValueHolder does not contain a value of type " 102 | + util::typestring() + ", but got " 103 | + util::typestring(this->value.get())}; 104 | } 105 | 106 | return ret; 107 | } 108 | 109 | } // namespace nyan 110 | 111 | 112 | namespace std { 113 | 114 | /** 115 | * Hashing for ValueHolders. 116 | * Relays the hash to the internally stored value. 117 | */ 118 | template <> 119 | struct hash { 120 | size_t operator()(const nyan::ValueHolder &val) const; 121 | }; 122 | 123 | } // namespace std 124 | -------------------------------------------------------------------------------- /nyan/state.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "state.h" 4 | 5 | #include 6 | 7 | #include "compiler.h" 8 | #include "error.h" 9 | #include "object_state.h" 10 | #include "util.h" 11 | #include "view.h" 12 | 13 | 14 | namespace nyan { 15 | 16 | State::State(const std::shared_ptr &previous_state) : 17 | previous_state{previous_state} {} 18 | 19 | 20 | State::State() : 21 | previous_state{nullptr} {} 22 | 23 | 24 | const std::shared_ptr *State::get(const fqon_t &fqon) const { 25 | auto it = this->objects.find(fqon); 26 | if (it != std::end(this->objects)) { 27 | return &it->second; 28 | } 29 | else { 30 | return nullptr; 31 | } 32 | } 33 | 34 | 35 | ObjectState &State::add_object(const fqon_t &name, std::shared_ptr &&obj) { 36 | if (unlikely(this->previous_state != nullptr)) { 37 | throw InternalError{"can't add new object in state that is not initial."}; 38 | } 39 | 40 | auto ins = this->objects.insert({name, std::move(obj)}); 41 | 42 | if (not ins.second) { 43 | throw InternalError{"added an already-known object to the state!"}; 44 | } 45 | 46 | return *ins.first->second; 47 | } 48 | 49 | 50 | void State::update(std::shared_ptr &&source_state) { 51 | // update this state with all objects from the source state 52 | // -> move all objects from the sourcestate into this one. 53 | for (auto &it : source_state.get()->objects) { 54 | auto search = this->objects.find(it.first); 55 | if (search != std::end(this->objects)) { 56 | search->second = std::move(it.second); 57 | } 58 | else { 59 | this->objects.insert( 60 | {std::move(it.first), std::move(it.second)}); 61 | } 62 | } 63 | } 64 | 65 | 66 | const std::shared_ptr &State::copy_object(const fqon_t &name, 67 | order_t t, 68 | std::shared_ptr &origin) { 69 | // last known state of the object 70 | const std::shared_ptr &source = origin->get_raw(name, t); 71 | 72 | if (not source) { 73 | throw InternalError{"object copy source not found"}; 74 | } 75 | 76 | // check if the object already is in this state 77 | auto it = this->objects.find(name); 78 | if (it == std::end(this->objects)) { 79 | // if not, copy the source object into this state 80 | auto it_new_object = this->objects.emplace(name, source->copy()).first; 81 | return it_new_object->second; 82 | } 83 | else { 84 | // else, no need to copy, the object is already in this state 85 | return it->second; 86 | } 87 | } 88 | 89 | 90 | const std::shared_ptr &State::get_previous_state() const { 91 | return this->previous_state; 92 | } 93 | 94 | 95 | const std::unordered_map> & 96 | State::get_objects() const { 97 | return this->objects; 98 | } 99 | 100 | 101 | std::string State::str() const { 102 | std::ostringstream builder; 103 | 104 | builder << "State:" << std::endl; 105 | 106 | size_t i = 0; 107 | for (auto &it : this->objects) { 108 | builder << "object " << i << ":" << std::endl 109 | << it.first << " => " << it.second->str() << std::endl; 110 | i += 1; 111 | } 112 | 113 | return builder.str(); 114 | } 115 | 116 | } // namespace nyan 117 | -------------------------------------------------------------------------------- /nyan/namespace_finder.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "namespace_finder.h" 4 | 5 | #include 6 | 7 | #include "compiler.h" 8 | #include "meta_info.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | NamespaceFinder::NamespaceFinder(AST &&ast) : 14 | ast{std::move(ast)} {} 15 | 16 | 17 | void NamespaceFinder::add_import(const Namespace &import) { 18 | this->imports.insert(import); 19 | } 20 | 21 | 22 | void NamespaceFinder::add_alias(const Token &alias, 23 | const Namespace &destination) { 24 | const std::string &search = alias.get(); 25 | 26 | if (this->aliases.find(search) != std::end(this->aliases)) { 27 | throw NameError{alias, "redefinition of namespace alias", search}; 28 | } 29 | 30 | this->aliases.insert({search, destination}); 31 | } 32 | 33 | 34 | bool NamespaceFinder::check_conflict(const std::string &name) const { 35 | return ( 36 | // aliases must be unique 37 | this->aliases.find(name) != std::end(this->aliases) 38 | // importing files from the root directory can be a problem too 39 | or this->imports.find(Namespace{std::vector(), name}) != std::end(this->imports)); 40 | } 41 | 42 | 43 | fqon_t NamespaceFinder::expand_alias(const IDToken &name) const { 44 | if (unlikely(not name.exists())) { 45 | throw InternalError{"tried expanding alias on empty id token"}; 46 | } 47 | 48 | // only the first component can be an alias. 49 | const std::string &first = name.get_components()[0].get(); 50 | 51 | auto it = this->aliases.find(first); 52 | if (it != std::end(this->aliases)) { 53 | // alias found. now expand it. 54 | return it->second.combine(name, 1); 55 | } 56 | 57 | // no alias found. basically return the input name. 58 | return name.to_fqon(); 59 | } 60 | 61 | 62 | fqon_t NamespaceFinder::find(const Namespace ¤t, 63 | const IDToken &search, 64 | const MetaInfo &typedb) const { 65 | if (unlikely(not search.exists())) { 66 | throw InternalError{"tried to find namespace for empty id"}; 67 | } 68 | 69 | Namespace search_base{current}; 70 | 71 | fqon_t result; 72 | // go towards the root namespace 73 | // to search for the matching object name 74 | while (true) { 75 | result = search_base.combine(search); 76 | if (typedb.has_object(result)) { 77 | return result; 78 | } 79 | 80 | // if the search base is exhausted, do alias expansion. 81 | if (search_base.empty()) { 82 | result = this->expand_alias(search); 83 | 84 | if (not typedb.has_object(result)) { 85 | throw NameError{search, "unknown name", search.str()}; 86 | } 87 | 88 | return result; 89 | } 90 | 91 | search_base.pop_last(); 92 | } 93 | } 94 | 95 | 96 | const AST &NamespaceFinder::get_ast() const { 97 | return this->ast; 98 | } 99 | 100 | 101 | std::string NamespaceFinder::str() const { 102 | std::ostringstream builder; 103 | builder << "NamespaceFinder knows:" << std::endl 104 | << "= aliases:" << std::endl; 105 | 106 | for (auto &it : this->aliases) { 107 | builder << " * " << it.first 108 | << " => " << it.second.str() << std::endl; 109 | } 110 | 111 | builder << "= imports:" << std::endl; 112 | 113 | for (auto &it : this->imports) { 114 | builder << " * " << it.str() << std::endl; 115 | } 116 | 117 | return builder.str(); 118 | } 119 | 120 | } // namespace nyan 121 | -------------------------------------------------------------------------------- /nyan/lexer/flex.lpp: -------------------------------------------------------------------------------- 1 | %{ 2 | // Copyright 2016-2018 the nyan authors, LGPLv3+. See copying.md for legal info. 3 | 4 | /** 5 | * @file 6 | * nyan language lexer 7 | */ 8 | 9 | #include 10 | 11 | #include "impl.h" 12 | 13 | using namespace std::string_literals; 14 | 15 | // Downcast `this` into our custom implementation 16 | #define impl static_cast(yyextra) 17 | 18 | // read characters from the input feed 19 | #define YY_INPUT(buf, result, max_size) \ 20 | { result = impl->read_input(buf, max_size); } 21 | 22 | // before matching a rule, advance the line position by length of current token 23 | #define YY_USER_ACTION impl->advance_linepos(); 24 | 25 | %} 26 | 27 | 28 | %option reentrant 29 | %option warn nodefault 30 | %option yylineno 31 | %option nounistd 32 | %option noinput nounput noyywrap 33 | %option never-interactive 34 | 35 | 36 | digit [0-9] 37 | id [A-Za-z_][A-Za-z0-9_]* 38 | operator [-+*/|%&]=?|= 39 | int (-|0[xX])?{digit}+ 40 | float -?({digit}+\.{digit}*|{digit}*\.{digit}+) 41 | inf -?inf 42 | 43 | %% 44 | 45 | [ ]*#.* { /* ignore trailing comments */ } 46 | [ \r] { /* ignore single space characters */ } 47 | "\f" { yylineno -= 1; } 48 | "\n" { impl->endline(); } 49 | 50 | ^[ ]*[^ \n]+ { /* indent */ 51 | int depth = 0; 52 | for(; depth < yyleng && yytext[depth] == ' '; ++depth); 53 | impl->handle_indent(depth); 54 | yyless(depth); 55 | } 56 | 57 | \"(\\.|[^\\"])*\" { impl->token(nyan::token_type::STRING); } 58 | \'(\\.|[^\\'])*\' { impl->token(nyan::token_type::STRING); } 59 | 60 | "(" { impl->token(nyan::token_type::LPAREN); } 61 | ")" { impl->token(nyan::token_type::RPAREN); } 62 | "<" { impl->token(nyan::token_type::LANGLE); } 63 | ">" { impl->token(nyan::token_type::RANGLE); } 64 | "[" { impl->token(nyan::token_type::LBRACKET); } 65 | "]" { impl->token(nyan::token_type::RBRACKET); } 66 | "{" { impl->token(nyan::token_type::LBRACE); } 67 | "}" { impl->token(nyan::token_type::RBRACE); } 68 | "@" { impl->token(nyan::token_type::AT); } 69 | "!" { impl->token(nyan::token_type::BANG); } 70 | 71 | "pass" { impl->token(nyan::token_type::PASS); } 72 | "..." { impl->token(nyan::token_type::ELLIPSIS); } 73 | "import" { impl->token(nyan::token_type::IMPORT); } 74 | "from" { impl->token(nyan::token_type::FROM); } 75 | "as" { impl->token(nyan::token_type::AS); } 76 | {operator} { impl->token(nyan::token_type::OPERATOR); } 77 | {inf} { impl->token(nyan::token_type::INF); } 78 | {int} { impl->token(nyan::token_type::INT); } 79 | {float} { impl->token(nyan::token_type::FLOAT); } 80 | {id} { impl->token(nyan::token_type::ID); } 81 | ":" { impl->token(nyan::token_type::COLON); } 82 | "," { impl->token(nyan::token_type::COMMA); } 83 | "." { impl->token(nyan::token_type::DOT); } 84 | 85 | <> { impl->token(nyan::token_type::ENDFILE); yyterminate(); } 86 | 87 | "\t" { throw impl->error("tab character"); } 88 | . { throw impl->error("invalid char: "s + yytext); } 89 | 90 | %% 91 | -------------------------------------------------------------------------------- /nyan/meta_info.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "config.h" 9 | #include "namespace.h" 10 | #include "object_info.h" 11 | 12 | 13 | namespace nyan { 14 | 15 | /** 16 | * Nyan database metainformation. 17 | * Used for type-checking etc. 18 | */ 19 | class MetaInfo { 20 | public: 21 | using obj_info_t = std::unordered_map; 22 | using ns_info_t = std::unordered_map; 23 | 24 | MetaInfo() = default; 25 | ~MetaInfo() = default; 26 | 27 | /** 28 | * Add metadata information for an object. 29 | * 30 | * @param name Identifier of the object. 31 | * @param obj_info ObjectInfo with metadata information. 32 | * 33 | * @return The stored metadata information object. 34 | */ 35 | ObjectInfo &add_object(const fqon_t &name, ObjectInfo &&obj_info); 36 | 37 | /** 38 | * Get the all metadata information objects for objects 39 | * stored in the database. 40 | * 41 | * @return Map of metadata information objects by object identifier. 42 | */ 43 | const obj_info_t &get_objects() const; 44 | 45 | /** 46 | * Get the the metadata information object for an object. 47 | * 48 | * @param name Identifier of the object. 49 | * 50 | * @return ObjectInfo with metadata information if the object is 51 | * in the database, else nullptr. 52 | */ 53 | ObjectInfo *get_object(const fqon_t &name); 54 | 55 | /** 56 | * Get the the metadata information object for an object. 57 | * 58 | * @param name Identifier of the object. 59 | * 60 | * @return ObjectInfo with metadata information if the object is 61 | * in the database, else nullptr. 62 | */ 63 | const ObjectInfo *get_object(const fqon_t &name) const; 64 | 65 | /** 66 | * Check if an object is in the database. 67 | * 68 | * @param name Identifier of the object. 69 | * 70 | * @return true if the object is in the database, else false. 71 | */ 72 | bool has_object(const fqon_t &name) const; 73 | 74 | /** 75 | * Add a namespace to the database. 76 | * 77 | * @param ns Namespace to add. 78 | * 79 | * @return The stored namespace. 80 | */ 81 | Namespace &add_namespace(const Namespace &ns); 82 | 83 | /** 84 | * Get a namespace from the database. 85 | * 86 | * @param name Identifier of the namespace. 87 | * 88 | * @return The stored namespace. 89 | */ 90 | Namespace *get_namespace(const fqnn_t &name); 91 | 92 | /** 93 | * Get a namespace from the database. 94 | * 95 | * @param name Identifier of the namespace. 96 | * 97 | * @return The stored namespace. 98 | */ 99 | const Namespace *get_namespace(const fqnn_t &name) const; 100 | 101 | /** 102 | * Check if a namespace is in the database. 103 | * 104 | * @param name Identifier of the namespace. 105 | * 106 | * @return true if the namespace is in the database, else false. 107 | */ 108 | bool has_namespace(const fqnn_t &name) const; 109 | 110 | /** 111 | * Get a string representation of all metadata information objects. 112 | * 113 | * @return String representation of all metadata information objects. 114 | */ 115 | std::string str() const; 116 | 117 | protected: 118 | /** 119 | * Location and type information for the objects. 120 | * This is for displaying error messages and line information. 121 | */ 122 | obj_info_t object_info; 123 | 124 | /** 125 | * Namespaces loaded in the database. 126 | */ 127 | ns_info_t namespaces; 128 | }; 129 | 130 | } // namespace nyan 131 | -------------------------------------------------------------------------------- /nyan/value/dict.h: -------------------------------------------------------------------------------- 1 | // Copyright 2020-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | 7 | #include "../api_error.h" 8 | #include "../compiler.h" 9 | #include "../util.h" 10 | #include "container.h" 11 | #include "container_types.h" 12 | #include "value.h" 13 | 14 | 15 | namespace nyan { 16 | 17 | 18 | /** 19 | * Nyan value to store a dict/map/assocated array of things. 20 | */ 21 | class Dict : public Value { 22 | public: 23 | using value_storage = dict_t; 24 | using key_type = typename value_storage::key_type; 25 | using value_type = typename value_storage::mapped_type; 26 | using element_type = typename value_storage::value_type; 27 | using value_const_iterator = typename value_storage::const_iterator; 28 | 29 | using iterator = ContainerIterator>; 30 | using const_iterator = ContainerIterator>; 31 | 32 | using holder_iterator = ContainerIterator; 33 | using holder_const_iterator = ContainerIterator; 34 | 35 | Dict(); 36 | Dict(std::unordered_map &&values); 37 | 38 | 39 | size_t hash() const override { 40 | throw APIError{"Dicts are not hashable."}; 41 | } 42 | 43 | 44 | size_t size() const { 45 | return this->values.size(); 46 | } 47 | 48 | 49 | void clear() { 50 | this->values.clear(); 51 | } 52 | 53 | 54 | const value_storage &get() const { 55 | return this->values; 56 | } 57 | 58 | iterator begin() { 59 | throw Error{ 60 | "Dicts are only const-iterable. " 61 | "make it const by using e.g. " 62 | "for (auto &it = std::as_const(dict))"}; 63 | } 64 | 65 | iterator end() { 66 | // also throw the error above. 67 | return this->begin(); 68 | } 69 | 70 | 71 | holder_iterator values_begin() { 72 | throw Error{ 73 | "Dict values holders are not non-const-iterable."}; 74 | } 75 | 76 | 77 | holder_iterator values_end() { 78 | // also throw the error above. 79 | return this->values_begin(); 80 | } 81 | 82 | 83 | /** 84 | * Get an iterator to the underlying dict storage. 85 | * Contrary to the above, this will allow to get the 86 | * ValueHolders. 87 | */ 88 | holder_const_iterator values_begin() const { 89 | auto real_iterator = std::make_unique< 90 | DefaultIterator>( 92 | std::begin(this->values)); 93 | 94 | return holder_const_iterator{std::move(real_iterator)}; 95 | } 96 | 97 | /** 98 | * Iterator to end of the underlying storage. 99 | */ 100 | holder_const_iterator values_end() const { 101 | auto real_iterator = std::make_unique< 102 | DefaultIterator>( 104 | std::end(this->values)); 105 | 106 | return holder_const_iterator{std::move(real_iterator)}; 107 | } 108 | 109 | 110 | bool add(const element_type &value); 111 | bool contains(const key_type &value) const; 112 | bool remove(const key_type &value); 113 | 114 | ValueHolder copy() const override; 115 | std::string str() const override; 116 | std::string repr() const override; 117 | 118 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 119 | const BasicType &get_type() const override; 120 | 121 | protected: 122 | bool apply_value(const Value &value, nyan_op operation) override; 123 | 124 | 125 | bool equals(const Value &other) const override { 126 | auto &other_val = dynamic_cast(other); 127 | 128 | return values == other_val.values; 129 | } 130 | 131 | /** 132 | * Dict value storage (this is an unordered map). 133 | */ 134 | value_storage values; 135 | }; 136 | 137 | } // namespace nyan 138 | -------------------------------------------------------------------------------- /copying.md: -------------------------------------------------------------------------------- 1 | Any file in this project that doesn't state otherwise, and isn't listed as an 2 | exception below, is Copyright 2016-2017 the nyan authors, and licensed 3 | under the terms of the GNU Lesser General Public License Version 3, or 4 | (at your option) any later version ("LGPL3+"). 5 | A copy of the license can be found in [legal/LGPLv3](/legal/LGPLv3). 6 | For completeness, a copy of the GPLv3 is included at [legal/GPLv3](/legal/GPLv3). 7 | 8 | _the nyan authors_ are: 9 | 10 | | Full name | aliases | E-Mail | 11 | |-----------------------------|-----------------------------|---------------------------------------| 12 | | Jonas Jelten | TheJJ | jj@sft.mx | 13 | | Markus Otto | zuntrax | otto@fs.tum.de | 14 | | Michael Enßlin | mic_e | michael@ensslin.cc | 15 | | Andre Kupka | freakout | kupka@in.tum.de | 16 | | Tushar Maheshwari | tusharpm | tushar27192@gmail.com | 17 | | Andrew Thompson | mrwerdo | mrwerdo331@me.com | 18 | 19 | If you're a first-time commiter, add yourself to the above list. This is not 20 | just for legal reasons, but also to keep an overview of all those nicknames. 21 | 22 | If your info is missing, wrong, or you want it to be removed for whatever 23 | reason, please contact us. 24 | 25 | A full list of all nyan authors ("contributors") can also be determined 26 | from the VCS, e.g. via `git shortlog -sne`, or conveniently looked up on 27 | [the GitHub web interface](https://github.com/SFTtech/nyan/graphs/contributors). 28 | 29 | Details on individual authorships of files can be obtained via the VCS, 30 | e.g. via `git blame`, or the GitHub web interface. 31 | 32 | This program is distributed in the hope that it will be useful, 33 | but WITHOUT ANY WARRANTY; without even the implied warranty of 34 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 35 | GNU Lesser General Public License Version 3 for more details. 36 | 37 | If you wish to include a file from nyan in your project, make sure to 38 | include all required legal info. The easiest way to do this would probably 39 | be to include a copy of this file (`copying.md`), and to leave the file's 40 | copyright header untouched. 41 | 42 | Per-file license header guidelines: 43 | 44 | In addition to this file, to prevent legal caveats, every source file *must* 45 | include a header. 46 | 47 | **nyan-native** source files, that is, files that were created by 48 | _the nyan authors_, require the following one-line header, preferrably in 49 | the first line, as a comment: 50 | 51 | Copyright 20XX-20YY the nyan authors, LGPLv3+. See copying.md for legal info. 52 | 53 | `20XX` is the year when the file was created, and `20YY` is the year when 54 | the file was last edited. When editing a file, make sure the 55 | last-modification year is still correct. 56 | 57 | 58 | Notes about this file: 59 | 60 | This file originates from [openage](https://openage.sft.mx/). 61 | It was adapted for nyan and its license. 62 | 63 | I (zuntrax) and neither mic_e are lawyers. This is an open-source 64 | project, we're doing this for fun. People convinced me that this legal 65 | shit must be done, so I did it, even though I'd rather have spent the 66 | time on useful parts of the project. If you see any legal issues, 67 | feel free to contact me. I, personally, despise in-sourcefile legal 68 | text blocks. They're a pest, and unlike many others, I don't simply 69 | accept them because "that is what everybody does". Thus, I worked out 70 | the minimal 1-line text above, which should be free of legal caveats, 71 | and a reasonable compromise. I'd be happy to see it used in other 72 | projects; you're free to use this file (`copying.md`) as a template 73 | for your project's legal documentation. 74 | -------------------------------------------------------------------------------- /nyan/view.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2025 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "curve.h" 10 | #include "object.h" 11 | #include "state_history.h" 12 | #include "transaction.h" 13 | 14 | 15 | namespace nyan { 16 | 17 | class Database; 18 | class ObjectState; 19 | class ObjectNotifier; 20 | class ObjectNotifierHandle; 21 | class State; 22 | 23 | 24 | /** 25 | * Database state view. 26 | */ 27 | class View : public std::enable_shared_from_this { 28 | friend class Transaction; 29 | 30 | public: 31 | View(const std::shared_ptr &database); 32 | 33 | Object get_object(const fqon_t &fqon); 34 | const std::shared_ptr get_object_ptr(const fqon_t &fqon); 35 | 36 | const std::shared_ptr &get_raw(const fqon_t &fqon, order_t t = LATEST_T) const; 37 | 38 | const ObjectInfo &get_info(const fqon_t &fqon) const; 39 | 40 | Transaction new_transaction(order_t t = DEFAULT_T); 41 | 42 | std::shared_ptr new_child(); 43 | 44 | // TODO: replace by deregistering child when it is destroyed 45 | void cleanup_stale_children(); 46 | 47 | const Database &get_database() const; 48 | 49 | const std::vector &get_linearization(const fqon_t &fqon, order_t t = LATEST_T) const; 50 | 51 | /** 52 | * Get the direct ancestor children of an object. 53 | * Does not step further down than one inheritance level. 54 | */ 55 | const std::unordered_set &get_obj_children(const fqon_t &fqon, order_t t = LATEST_T) const; 56 | 57 | /** 58 | * Get all ancestor children of an object including the transitive onces. 59 | */ 60 | std::unordered_set get_obj_children_all(const fqon_t &fqon, order_t t = LATEST_T) const; 61 | 62 | /** 63 | * Register a function that is called whenever the given object or any of its parents 64 | * change a value. 65 | * You need to keep the returned ObjectNotifier alive, because when it is deconstructed, 66 | * the callback will be deregistered. 67 | */ 68 | std::shared_ptr create_notifier(const fqon_t &fqon, const update_cb_t &callback); 69 | 70 | void deregister_notifier(const fqon_t &fqon, 71 | const std::shared_ptr ¬ifier); 72 | 73 | /** 74 | * Drop all state later than given time. 75 | * This drops child tracking, value caches, linearizations. 76 | * Also deletes then-unchanged objects histories. 77 | */ 78 | // TODO void reset_from(order_t t=DEFAULT_T); 79 | 80 | 81 | /** 82 | * Call the notifications for the given objects. 83 | */ 84 | void fire_notifications(const std::unordered_set &changed_objs, 85 | order_t t) const; 86 | 87 | 88 | protected: 89 | const std::vector> &get_children(); 90 | 91 | void gather_obj_children(std::unordered_set &target, 92 | const fqon_t &obj, 93 | order_t t) const; 94 | 95 | StateHistory &get_state_history(); 96 | 97 | void add_child(const std::shared_ptr &view); 98 | 99 | /** 100 | * Database used if the state curve has no information about 101 | * the queried object at all. 102 | */ 103 | std::shared_ptr database; 104 | 105 | /** 106 | * Data storage over time. 107 | */ 108 | StateHistory state; 109 | 110 | /** 111 | * Child views. Used to propagate down patches. 112 | */ 113 | std::vector> children; 114 | 115 | /** 116 | * If this view is a child of another view, this pointer 117 | * can bring us back to the parent. 118 | */ 119 | std::weak_ptr parent_view; 120 | 121 | /** 122 | * Registered event notification callbacks. 123 | */ 124 | std::unordered_map>> notifiers; 125 | 126 | // TODO: track transactions and then use tracking to 127 | // check for transaction modificationconflicts 128 | // beware the child views so that conflicts in them are detected as well 129 | }; 130 | 131 | } // namespace nyan 132 | -------------------------------------------------------------------------------- /nyan/object_state.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "object_state.h" 4 | 5 | #include 6 | #include 7 | 8 | #include "change_tracker.h" 9 | #include "compiler.h" 10 | #include "object_info.h" 11 | #include "util.h" 12 | 13 | 14 | namespace nyan { 15 | 16 | 17 | ObjectState::ObjectState(std::deque &&parents) : 18 | parents{std::move(parents)} {} 19 | 20 | 21 | void ObjectState::apply(const std::shared_ptr &mod, 22 | const ObjectInfo &mod_info, 23 | ObjectChanges &tracker) { 24 | const auto &inher_changes = mod_info.get_inheritance_change(); 25 | if (inher_changes.size() > 0) { 26 | for (auto &change : inher_changes) { 27 | // check if the object already has the parent 28 | auto it = std::find(std::begin(this->parents), 29 | std::end(this->parents), 30 | change.get_target()); 31 | bool parent_exists = it != std::end(this->parents); 32 | 33 | // only add the parent if it does not exist. 34 | // maybe we may want to relocate it in the future? 35 | if (not parent_exists) { 36 | switch (change.get_type()) { 37 | case inher_change_t::ADD_FRONT: 38 | this->parents.push_front(change.get_target()); 39 | tracker.add_parent(change.get_target()); 40 | break; 41 | case inher_change_t::ADD_BACK: 42 | this->parents.push_back(change.get_target()); 43 | tracker.add_parent(change.get_target()); 44 | break; 45 | default: 46 | throw InternalError{"unsupported inheritance change type"}; 47 | } 48 | } 49 | } 50 | } 51 | 52 | // change each member in this object by the member of the patch. 53 | // other->members: maps memberid_t name => Member 54 | for (auto &it : mod->members) { 55 | auto search = this->members.find(it.first); 56 | if (search == std::end(this->members)) { 57 | // copy the member from the modification object, 58 | // if it is a patch. 59 | // that way a child object without the member 60 | // can get the modifications. 61 | if (likely(mod_info.is_patch())) { 62 | this->members.emplace(it.first, it.second); 63 | } 64 | else { 65 | throw InternalError{ 66 | "a non-patch tried to change a nonexisting member"}; 67 | } 68 | } 69 | else { 70 | search->second.apply(it.second); 71 | } 72 | 73 | // TODO optimization: we could now calculate the resulting value! 74 | // TODO: invalidate value cache with the change tracker 75 | } 76 | } 77 | 78 | 79 | std::shared_ptr ObjectState::copy() const { 80 | return std::make_shared(*this); 81 | } 82 | 83 | 84 | const std::deque &ObjectState::get_parents() const { 85 | return this->parents; 86 | } 87 | 88 | 89 | bool ObjectState::has(const memberid_t &name) const { 90 | return this->members.find(name) != std::end(this->members); 91 | } 92 | 93 | 94 | Member *ObjectState::get(const memberid_t &name) { 95 | auto it = this->members.find(name); 96 | if (it == std::end(this->members)) { 97 | return nullptr; 98 | } 99 | return &it->second; 100 | } 101 | 102 | 103 | // Thanks C++, always redundancy free! 104 | const Member *ObjectState::get(const memberid_t &name) const { 105 | auto it = this->members.find(name); 106 | if (it == std::end(this->members)) { 107 | return nullptr; 108 | } 109 | return &it->second; 110 | } 111 | 112 | 113 | const std::unordered_map &ObjectState::get_members() const { 114 | return this->members; 115 | } 116 | 117 | 118 | std::string ObjectState::str() const { 119 | std::ostringstream builder; 120 | 121 | builder << "ObjectState(" 122 | << util::strjoin(", ", this->parents) 123 | << ")" 124 | << std::endl; 125 | 126 | if (this->members.size() == 0) { 127 | builder << " [no members]" << std::endl; 128 | } 129 | 130 | for (auto &it : this->members) { 131 | builder << " " << it.first 132 | << " -> " << it.second.str() << std::endl; 133 | } 134 | 135 | return builder.str(); 136 | } 137 | 138 | 139 | void ObjectState::set_members(std::unordered_map &&members) { 140 | this->members = std::move(members); 141 | } 142 | 143 | } // namespace nyan 144 | -------------------------------------------------------------------------------- /nyan/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # source file configuration 2 | # for the resulting nyan library 3 | 4 | if (APPLE) 5 | execute_process(COMMAND brew --prefix flex 6 | OUTPUT_VARIABLE BREW_FLEX_HOME 7 | OUTPUT_STRIP_TRAILING_WHITESPACE) 8 | if (EXISTS "${BREW_FLEX_HOME}/bin/flex") 9 | message(STATUS "Found homebrew flex, using it instead.") 10 | set(FLEX_EXECUTABLE "${BREW_FLEX_HOME}/bin/flex") 11 | endif () 12 | endif () 13 | 14 | find_package(FLEX 2.6 REQUIRED) 15 | 16 | set(nyanl_cpp "${CMAKE_CURRENT_BINARY_DIR}/flex.gen.cpp") 17 | set(nyanl_h "${CMAKE_CURRENT_BINARY_DIR}/flex.gen.h") 18 | set(nyanl_lpp "${CMAKE_CURRENT_SOURCE_DIR}/lexer/flex.lpp") 19 | 20 | add_custom_command(OUTPUT ${nyanl_cpp} ${nyanl_h} 21 | COMMAND ${FLEX_EXECUTABLE} -Ca --header-file=${nyanl_h} -o ${nyanl_cpp} ${nyanl_lpp} 22 | VERBATIM 23 | DEPENDS ${nyanl_lpp} 24 | COMMENT "[flex] Generating scanner with flex ${FLEX_VERSION}" 25 | ) 26 | 27 | add_library(nyan SHARED 28 | api_error.cpp 29 | ast.cpp 30 | basic_type.cpp 31 | c3.cpp 32 | change_tracker.cpp 33 | concept.cpp 34 | config.cpp 35 | curve.cpp 36 | database.cpp 37 | datastructure/orderedset.cpp 38 | error.cpp 39 | file.cpp 40 | id_token.cpp 41 | inheritance_change.cpp 42 | ${nyanl_cpp} 43 | lang_error.cpp 44 | lexer/bracket.cpp 45 | lexer/impl.cpp 46 | lexer/lexer.cpp 47 | location.cpp 48 | member.cpp 49 | member_info.cpp 50 | meta_info.cpp 51 | namespace.cpp 52 | namespace_finder.cpp 53 | object.cpp 54 | object_history.cpp 55 | object_info.cpp 56 | object_notifier.cpp 57 | object_notifier_types.cpp 58 | object_state.cpp 59 | ops.cpp 60 | parser.cpp 61 | patch_info.cpp 62 | state.cpp 63 | state_history.cpp 64 | token.cpp 65 | token_stream.cpp 66 | transaction.cpp 67 | type.cpp 68 | util.cpp 69 | util/flags.cpp 70 | value_token.cpp 71 | value/boolean.cpp 72 | value/container_types.cpp 73 | value/container.cpp 74 | value/dict.cpp 75 | value/file.cpp 76 | value/none.cpp 77 | value/number.cpp 78 | value/object.cpp 79 | value/orderedset.cpp 80 | value/set.cpp 81 | value/set_base.cpp 82 | value/text.cpp 83 | value/value.cpp 84 | value/value_holder.cpp 85 | view.cpp 86 | ) 87 | add_library(nyan::nyan ALIAS nyan) 88 | 89 | if(UNIX) 90 | if("${CMAKE_SYSTEM_NAME}" MATCHES "^(Free|Net|Open)BSD|DragonFly") 91 | find_library(EXECINFO_LIBRARY execinfo) 92 | target_link_libraries(nyan ${CMAKE_DL_LIBS} ${EXECINFO_LIBRARY}) 93 | else() 94 | target_link_libraries(nyan ${CMAKE_DL_LIBS}) 95 | endif() 96 | 97 | if(NOT APPLE) 98 | # for ld on macOS: "The default is to treat undefined symbols as errors." 99 | set_target_properties(nyan PROPERTIES LINK_FLAGS "-Wl,--no-undefined") 100 | endif() 101 | endif() 102 | 103 | if(WIN32 AND (NOT WINDOWS_STORE)) 104 | set_target_properties(nyan PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS ON) 105 | target_link_libraries(nyan PRIVATE DbgHelp) 106 | endif() 107 | 108 | set_target_properties(nyan PROPERTIES 109 | VERSION ${nyan_VERSION} 110 | SOVERSION 1 111 | INTERFACE_nyan_MAJOR_VERSION 1 112 | COMPATIBLE_INTERFACE_STRING nyan_MAJOR_VERSION 113 | ) 114 | 115 | # C++ standard requirement 116 | target_compile_features(nyan 117 | PUBLIC 118 | cxx_std_17 119 | ) 120 | 121 | # binaries 122 | install( 123 | TARGETS nyan 124 | EXPORT ${nyan_exports_name} 125 | ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} 126 | LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} 127 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} 128 | ) 129 | 130 | # install headers 131 | install(DIRECTORY 132 | ./ 133 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}/nyan" 134 | COMPONENT Devel 135 | FILES_MATCHING PATTERN "*.h" 136 | ) 137 | 138 | 139 | # include directory specification 140 | # depending if nyan was built or installed, 141 | # projects using libnyan will automatically include the correct dir. 142 | target_include_directories(nyan 143 | PUBLIC 144 | $ 145 | $ 146 | PRIVATE 147 | ${CMAKE_CURRENT_SOURCE_DIR}/lexer 148 | ${CMAKE_CURRENT_BINARY_DIR} 149 | ) 150 | 151 | 152 | # the nyan tool 153 | add_executable(nyancat 154 | nyan_tool.cpp 155 | ) 156 | target_link_libraries(nyancat nyan) 157 | install( 158 | TARGETS nyancat 159 | EXPORT ${nyan_exports_name} 160 | DESTINATION ${CMAKE_INSTALL_BINDIR} 161 | ) 162 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | # SFT codestyle 3 | # Tab indent + space alignment 4 | # see documentation in doc/code_style/ for details and explainations. 5 | Language: Cpp 6 | AccessModifierOffset: -4 7 | AlignAfterOpenBracket: Align 8 | AlignArrayOfStructures: None 9 | AlignConsecutiveAssignments: false 10 | AlignConsecutiveBitFields: false 11 | AlignConsecutiveDeclarations: false 12 | AlignConsecutiveMacros: false 13 | AlignEscapedNewlines: DontAlign 14 | AlignOperands: Align 15 | AlignTrailingComments: 16 | Kind: Leave 17 | AllowAllArgumentsOnNextLine: true 18 | AllowAllParametersOfDeclarationOnNextLine: true 19 | AllowShortBlocksOnASingleLine: Never 20 | AllowShortCaseLabelsOnASingleLine: false 21 | AllowShortEnumsOnASingleLine: false 22 | AllowShortFunctionsOnASingleLine: Empty 23 | AllowShortIfStatementsOnASingleLine: Never 24 | AllowShortLambdasOnASingleLine: All 25 | AllowShortLoopsOnASingleLine: false 26 | AlwaysBreakAfterDefinitionReturnType: None 27 | AlwaysBreakAfterReturnType: None 28 | AlwaysBreakBeforeMultilineStrings: false 29 | AlwaysBreakTemplateDeclarations: Yes 30 | BinPackArguments: false 31 | BinPackParameters: false 32 | BraceWrapping: 33 | AfterCaseLabel: false 34 | AfterClass: false 35 | AfterControlStatement: Never 36 | AfterEnum: false 37 | AfterExternBlock: false 38 | AfterFunction: false 39 | AfterNamespace: false 40 | AfterObjCDeclaration: false 41 | AfterStruct: false 42 | AfterUnion: false 43 | BeforeCatch: true 44 | BeforeElse: true 45 | BeforeLambdaBody: false 46 | BeforeWhile: true 47 | IndentBraces: false 48 | SplitEmptyFunction: false 49 | SplitEmptyNamespace: false 50 | SplitEmptyRecord: false 51 | BreakAfterJavaFieldAnnotations: true 52 | BreakBeforeBinaryOperators: NonAssignment 53 | BreakBeforeBraces: Custom 54 | BreakBeforeInheritanceComma: false 55 | BreakBeforeTernaryOperators: false 56 | BreakConstructorInitializers: AfterColon 57 | BreakInheritanceList: BeforeComma 58 | BreakStringLiterals: false 59 | # clang-format 19 BreakTemplateDeclarations: Yes 60 | ColumnLimit: 0 61 | CompactNamespaces: false 62 | ConstructorInitializerAllOnOneLineOrOnePerLine: false 63 | ConstructorInitializerIndentWidth: 4 64 | ContinuationIndentWidth: 4 65 | Cpp11BracedListStyle: true 66 | DeriveLineEnding: true 67 | DerivePointerAlignment: false 68 | DisableFormat: false 69 | ExperimentalAutoDetectBinPacking: false 70 | FixNamespaceComments: true 71 | ForEachMacros: 72 | - foreach 73 | - Q_FOREACH 74 | - BOOST_FOREACH 75 | IncludeBlocks: Preserve 76 | IncludeCategories: 77 | - Regex: '.*' 78 | Priority: 3 79 | SortPriority: 0 80 | IncludeIsMainRegex: '' 81 | IncludeIsMainSourceRegex: '' 82 | IndentCaseBlocks: false 83 | IndentCaseLabels: false 84 | IndentExternBlock: NoIndent 85 | IndentGotoLabels: false 86 | IndentPPDirectives: BeforeHash 87 | IndentWidth: 4 88 | IndentWrappedFunctionNames: false 89 | InsertNewlineAtEOF: true 90 | InsertTrailingCommas: Wrapped 91 | KeepEmptyLinesAtTheStartOfBlocks: false 92 | MacroBlockBegin: '' 93 | MacroBlockEnd: '' 94 | MaxEmptyLinesToKeep: 2 95 | NamespaceIndentation: None 96 | PenaltyBreakAssignment: 2 97 | PenaltyBreakBeforeFirstCallParameter: 1 98 | PenaltyBreakComment: 300 99 | PenaltyBreakFirstLessLess: 120 100 | PenaltyBreakString: 1000 101 | PenaltyBreakTemplateDeclaration: 10 102 | PenaltyExcessCharacter: 1000000 103 | PenaltyReturnTypeOnItsOwnLine: 200 104 | PointerAlignment: Right 105 | ReflowComments: Always 106 | SortIncludes: CaseInsensitive 107 | SortUsingDeclarations: true 108 | SpaceAfterCStyleCast: false 109 | SpaceAfterLogicalNot: false 110 | SpaceAfterTemplateKeyword: true 111 | SpaceBeforeAssignmentOperators: true 112 | SpaceBeforeCaseColon: false 113 | SpaceBeforeCpp11BracedList: false 114 | SpaceBeforeCtorInitializerColon: true 115 | SpaceBeforeInheritanceColon: true 116 | SpaceBeforeParens: ControlStatements 117 | SpaceBeforeRangeBasedForLoopColon: true 118 | SpaceBeforeSquareBrackets: false 119 | SpaceInEmptyBlock: false 120 | SpaceInEmptyParentheses: false 121 | SpacesBeforeTrailingComments: 1 122 | SpacesInAngles: Never 123 | SpacesInCStyleCastParentheses: false 124 | SpacesInConditionalStatement: false 125 | SpacesInContainerLiterals: false 126 | SpacesInParentheses: false 127 | SpacesInSquareBrackets: false 128 | Standard: Latest 129 | TabWidth: 4 130 | UseCRLF: false 131 | UseTab: AlignWithSpaces 132 | ... 133 | -------------------------------------------------------------------------------- /nyan/curve.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #pragma once 4 | 5 | #include 6 | #include 7 | 8 | #include "compiler.h" 9 | #include "config.h" 10 | #include "error.h" 11 | #include "util.h" 12 | 13 | 14 | namespace nyan { 15 | 16 | template 17 | class Curve { 18 | public: 19 | using container_t = std::map; 20 | 21 | using fallback_t = std::function; 22 | 23 | 24 | Curve() {} 25 | 26 | #ifdef CURVE_FALLBACK_FUNCTION 27 | Curve(const fallback_t &func) : 28 | fallback{func} {} 29 | 30 | /** 31 | * Fallback function, used if a time before the first 32 | * entry is used. 33 | */ 34 | fallback_t fallback; 35 | #endif 36 | 37 | // TODO: maybe add variants of the functions below 38 | // which also return the time the keyframe belongs to. 39 | 40 | /** 41 | * Get the first value after a given point in time. 42 | * 43 | * @param time The point in time after which the method searches for a value. 44 | * 45 | * @return The first value that can be found after 'time'. 46 | */ 47 | const T &at(const order_t time) const { 48 | // search for element which is greater than time 49 | auto it = this->container.upper_bound(time); 50 | if (it == std::begin(this->container)) { 51 | #ifdef CURVE_FALLBACK_FUNCTION 52 | if (likely(this->fallback)) { 53 | return this->fallback(time); 54 | } 55 | else { 56 | #endif 57 | throw InternalError{ 58 | "requested time lower than first curve entry"}; 59 | #ifdef CURVE_FALLBACK_FUNCTION 60 | } 61 | #endif 62 | } 63 | 64 | // go one back, so it's less or equal the requested time. 65 | --it; 66 | return it->second; 67 | } 68 | 69 | /** 70 | * Like `at`, but returns nullptr if no keyframe was found. 71 | * 72 | * @param time The point in time after which the method searches for a value. 73 | * 74 | * @return The first value that can be found after 'time' if one exists, else nullptr. 75 | */ 76 | const T *at_find(const order_t time) const { 77 | auto it = this->container.upper_bound(time); 78 | if (it == std::begin(this->container)) { 79 | return nullptr; 80 | } 81 | --it; 82 | return &it->second; 83 | } 84 | 85 | /** 86 | * Get the value at the exact time. 87 | * 88 | * @param time The point in time at which the value should be retrieved. 89 | * 90 | * @return Value at the given time if it exists, else nullptr. 91 | */ 92 | const T *at_exact(const order_t time) const { 93 | auto it = this->container.find(time); 94 | if (it == std::end(this->container)) { 95 | return nullptr; 96 | } 97 | 98 | return &it->second; 99 | } 100 | 101 | /** 102 | * Get the first value before a given point in time. 103 | * 104 | * @param time The point in time before which the method searches for a value. 105 | * 106 | * @return The first value that can be found before 'time'. 107 | */ 108 | const T &before(const order_t time) const { 109 | // search for element which is not less than the given time. 110 | auto it = this->container.lower_bound(time); 111 | if (it == std::begin(this->container)) { 112 | throw InternalError{"curve has no previous keyframe"}; 113 | } 114 | 115 | // go one back, so it's less than the requested time. 116 | --it; 117 | return it->second; 118 | } 119 | 120 | /** 121 | * Check if no values are stored in the curve. 122 | * 123 | * @return true if the value container is empty, else false. 124 | */ 125 | bool empty() const { 126 | return this->container.empty(); 127 | } 128 | 129 | /** 130 | * Insert a new keyframe with a value into the curve. 131 | * 132 | * @param time The point in time at which the value is inserted. 133 | * @param value Value that is inserted. 134 | * 135 | * @return The inserted value. 136 | */ 137 | T &insert_drop(const order_t time, T &&value) { 138 | auto it = this->container.lower_bound(time); 139 | 140 | // remove all elements greater or equal the requested time 141 | this->container.erase(it, std::end(this->container)); 142 | 143 | // insert the new keyframe 144 | auto ret = this->container.insert({time, std::move(value)}); 145 | if (unlikely(ret.second == false)) { 146 | throw InternalError{"did not insert value, it existed before"}; 147 | } 148 | 149 | return ret.first->second; 150 | } 151 | 152 | protected: 153 | /** 154 | * Keyframes of the curve, stored as a map of values by time. 155 | */ 156 | container_t container; 157 | }; 158 | 159 | } // namespace nyan 160 | -------------------------------------------------------------------------------- /nyan/object_info.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "object_info.h" 4 | 5 | #include 6 | 7 | #include "lang_error.h" 8 | #include "patch_info.h" 9 | #include "state.h" 10 | #include "util.h" 11 | 12 | 13 | namespace nyan { 14 | 15 | ObjectInfo::ObjectInfo(const Location &location, 16 | const Namespace &ns) : 17 | location{location}, 18 | ns{ns}, 19 | initial_patch{false} {} 20 | 21 | 22 | const Location &ObjectInfo::get_location() const { 23 | return this->location; 24 | } 25 | 26 | const Namespace &ObjectInfo::get_namespace() const { 27 | return this->ns; 28 | } 29 | 30 | 31 | MemberInfo &ObjectInfo::add_member(const memberid_t &name, 32 | MemberInfo &&member) { 33 | // copy the location so it's still valid if the insert fails. 34 | Location loc = member.get_location(); 35 | 36 | auto ret = this->member_info.insert({name, std::move(member)}); 37 | if (ret.second == false) { 38 | throw LangError{ 39 | loc, 40 | "member already in this object", 41 | {{ret.first->second.get_location(), "first defined here"}}}; 42 | } 43 | 44 | return ret.first->second; 45 | } 46 | 47 | 48 | ObjectInfo::member_info_t &ObjectInfo::get_members() { 49 | return this->member_info; 50 | } 51 | 52 | const ObjectInfo::member_info_t &ObjectInfo::get_members() const { 53 | return this->member_info; 54 | } 55 | 56 | 57 | const MemberInfo *ObjectInfo::get_member(const memberid_t &name) const { 58 | auto it = this->member_info.find(name); 59 | 60 | if (it == std::end(this->member_info)) { 61 | return nullptr; 62 | } 63 | 64 | return &it->second; 65 | } 66 | 67 | 68 | bool ObjectInfo::is_patch() const { 69 | return this->patch_info.get() != nullptr; 70 | } 71 | 72 | 73 | bool ObjectInfo::is_initial_patch() const { 74 | return this->initial_patch; 75 | } 76 | 77 | 78 | PatchInfo &ObjectInfo::add_patch(const std::shared_ptr &info, bool initial) { 79 | this->initial_patch = initial; 80 | this->patch_info = info; 81 | return *this->patch_info.get(); 82 | } 83 | 84 | 85 | const std::shared_ptr &ObjectInfo::get_patch() const { 86 | return this->patch_info; 87 | } 88 | 89 | 90 | void ObjectInfo::add_inheritance_change(InheritanceChange &&change) { 91 | this->inheritance_change.push_back(std::move(change)); 92 | } 93 | 94 | 95 | const std::vector &ObjectInfo::get_inheritance_change() const { 96 | return this->inheritance_change; 97 | } 98 | 99 | 100 | void ObjectInfo::set_linearization(std::vector &&lin) { 101 | this->initial_linearization = std::move(lin); 102 | } 103 | 104 | 105 | const std::vector &ObjectInfo::get_linearization() const { 106 | return this->initial_linearization; 107 | } 108 | 109 | 110 | void ObjectInfo::add_children(std::unordered_set &&children) { 111 | this->initial_children.insert(children.begin(), children.end()); 112 | } 113 | 114 | 115 | void ObjectInfo::set_children(std::unordered_set &&children) { 116 | this->initial_children = std::move(children); 117 | } 118 | 119 | 120 | const std::unordered_set &ObjectInfo::get_children() const { 121 | return this->initial_children; 122 | } 123 | 124 | 125 | std::string ObjectInfo::str() const { 126 | std::ostringstream builder; 127 | 128 | builder << "ObjectInfo"; 129 | 130 | if (this->is_patch()) { 131 | builder << " " << this->patch_info->str(); 132 | } 133 | 134 | if (this->inheritance_change.size() > 0) { 135 | builder << " ["; 136 | 137 | bool liststart = true; 138 | for (auto &change : this->inheritance_change) { 139 | if (not liststart) { 140 | builder << ", "; 141 | } 142 | else { 143 | liststart = false; 144 | } 145 | 146 | switch (change.get_type()) { 147 | case inher_change_t::ADD_FRONT: 148 | builder << change.get_target() << "+"; 149 | break; 150 | 151 | case inher_change_t::ADD_BACK: 152 | builder << "+" << change.get_target(); 153 | break; 154 | 155 | default: 156 | throw InternalError{"unknown inheritance change type"}; 157 | } 158 | } 159 | 160 | builder << "]"; 161 | } 162 | 163 | builder << ":" << std::endl; 164 | 165 | if (this->member_info.size() == 0) { 166 | builder << " [no members]" << std::endl; 167 | } 168 | 169 | for (auto &it : this->member_info) { 170 | const auto &memberid = it.first; 171 | const auto &memberinfo = it.second; 172 | 173 | builder << " -> " << memberid; 174 | builder << " : " << memberinfo.str() << std::endl; 175 | } 176 | 177 | return builder.str(); 178 | } 179 | 180 | } // namespace nyan 181 | -------------------------------------------------------------------------------- /nyan/state_history.h: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "config.h" 9 | #include "object_history.h" 10 | 11 | 12 | namespace nyan { 13 | 14 | class Database; 15 | class MetaInfo; 16 | class ObjectState; 17 | class State; 18 | 19 | 20 | /** 21 | * Object state history tracking. 22 | */ 23 | class StateHistory { 24 | public: 25 | StateHistory(const std::shared_ptr &base); 26 | 27 | /** 28 | * Get the latest database state at or after a given time. 29 | * 30 | * @param t Time for which the state is retrieved. 31 | * 32 | * @return Shared pointer to State at time \p t if it exists, else the next state after that. 33 | */ 34 | const std::shared_ptr &get_state(order_t t) const; 35 | 36 | /** 37 | * Get the latest database state before a given time. 38 | * 39 | * @param t Time for which the state is retrieved. 40 | * 41 | * @return Shared pointer to State before time \p t . 42 | */ 43 | const std::shared_ptr &get_state_before(order_t t) const; 44 | 45 | /** 46 | * Get the database state at a given time. 47 | * 48 | * @param t Time for which the state is retrieved. 49 | * 50 | * @return Shared pointer to State at time \p t if it exists, else nullptr.. 51 | */ 52 | const std::shared_ptr *get_state_exact(order_t t) const; 53 | 54 | /** 55 | * Get an object state at a given time. 56 | * 57 | * @param fqon Identifier of the object. 58 | * @param t Time for which the object state is retrieved. 59 | * 60 | * @return Shared pointer to the ObjectState at time \p t if 61 | * it exists, else the next state before that. 62 | */ 63 | const std::shared_ptr *get_obj_state(const fqon_t &fqon, order_t t) const; 64 | 65 | /** 66 | * Record all changes of a new state in the history. 67 | * 68 | * @param new_state New state in the database. 69 | * @param t Time of insertion. 70 | */ 71 | void insert(std::shared_ptr &&new_state, order_t t); 72 | 73 | /** 74 | * Record a change to the linearization of an object in its history. 75 | * 76 | * @param ins New linearization of the object. The first element in 77 | * the list is also the identifier of the object. 78 | * @param t Time of insertion. 79 | */ 80 | void insert_linearization(std::vector &&ins, order_t t); 81 | 82 | /** 83 | * Get the linearization of an object at a given time. 84 | * 85 | * @param obj Identifier of the object. 86 | * @param t Time for which the object linearization is retrieved. 87 | * @param meta_info Metadata information of the database. 88 | * 89 | * @return C3 linearization of the object. 90 | */ 91 | const std::vector &get_linearization(const fqon_t &obj, order_t t, const MetaInfo &meta_info) const; 92 | 93 | /** 94 | * Record a change to the children of an object in its history. 95 | * 96 | * @param obj Identifier of the object. 97 | * @param ins New children of the object. 98 | * @param t Time of insertion. 99 | */ 100 | void insert_children(const fqon_t &obj, std::unordered_set &&ins, order_t t); 101 | 102 | /** 103 | * Get the children of an object at a given time. 104 | * 105 | * @param obj Identifier of the object. 106 | * @param t Time for which the object children are retrieved. 107 | * @param meta_info Metadata information of the database. 108 | * 109 | * @return List of children of the object. 110 | */ 111 | const std::unordered_set &get_children(const fqon_t &obj, order_t t, const MetaInfo &meta_info) const; 112 | 113 | protected: 114 | /** 115 | * Get the object history an an object in the database. 116 | * 117 | * @param obj Identifier of the object. 118 | * 119 | * @return Pointer to the ObjectHistory of the object if it exists, else nullptr. 120 | */ 121 | ObjectHistory *get_obj_history(const fqon_t &obj); 122 | 123 | /** 124 | * Get the object history an an object in the database. 125 | * 126 | * @param obj Identifier of the object. 127 | * 128 | * @return Pointer to the ObjectHistory of the object if it exists, else nullptr. 129 | */ 130 | const ObjectHistory *get_obj_history(const fqon_t &obj) const; 131 | 132 | /** 133 | * Get the object history an an object in the database or create it if 134 | * it doesn't exist. 135 | * 136 | * @param obj Identifier of the object. 137 | * 138 | * @return Pointer to the ObjectHistory of the object. 139 | */ 140 | ObjectHistory &get_create_obj_history(const fqon_t &obj); 141 | 142 | /** 143 | * Storage of states over time. 144 | */ 145 | Curve> history; 146 | 147 | /** 148 | * Information history for each object. 149 | * Optimizes searches in the history. 150 | */ 151 | std::unordered_map object_obj_hists; 152 | }; 153 | 154 | 155 | } // namespace nyan 156 | -------------------------------------------------------------------------------- /nyan/basic_type.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "basic_type.h" 4 | 5 | #include "ast.h" 6 | #include "error.h" 7 | #include "id_token.h" 8 | #include "token.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | bool BasicType::is_object() const { 14 | return (this->primitive_type == primitive_t::OBJECT); 15 | } 16 | 17 | bool BasicType::is_fundamental() const { 18 | switch (this->primitive_type) { 19 | case primitive_t::BOOLEAN: 20 | case primitive_t::TEXT: 21 | case primitive_t::FILENAME: 22 | case primitive_t::INT: 23 | case primitive_t::FLOAT: 24 | case primitive_t::NONE: 25 | return true; 26 | case primitive_t::OBJECT: 27 | case primitive_t::CONTAINER: 28 | case primitive_t::MODIFIER: 29 | return false; 30 | } 31 | 32 | throw InternalError{"unknown primitive type"}; 33 | } 34 | 35 | 36 | bool BasicType::is_composite() const { 37 | return (this->composite_type != composite_t::SINGLE); 38 | } 39 | 40 | 41 | bool BasicType::is_container() const { 42 | switch (this->composite_type) { 43 | case composite_t::SET: 44 | case composite_t::ORDEREDSET: 45 | case composite_t::DICT: 46 | return true; 47 | case composite_t::SINGLE: 48 | case composite_t::ABSTRACT: 49 | case composite_t::CHILDREN: 50 | case composite_t::OPTIONAL: 51 | return false; 52 | } 53 | 54 | throw InternalError{"unknown composite type"}; 55 | } 56 | 57 | 58 | bool BasicType::is_modifier() const { 59 | switch (this->composite_type) { 60 | case composite_t::ABSTRACT: 61 | case composite_t::CHILDREN: 62 | case composite_t::OPTIONAL: 63 | return true; 64 | case composite_t::SINGLE: 65 | case composite_t::SET: 66 | case composite_t::ORDEREDSET: 67 | case composite_t::DICT: 68 | return false; 69 | } 70 | 71 | throw InternalError{"unknown composite type"}; 72 | } 73 | 74 | 75 | size_t BasicType::expected_nested_types() const { 76 | if (this->is_fundamental()) { 77 | return 0; 78 | } 79 | 80 | switch (this->composite_type) { 81 | // containers 82 | case composite_t::SET: 83 | case composite_t::ORDEREDSET: 84 | return 1; 85 | case composite_t::DICT: 86 | return 2; 87 | 88 | // modifiers 89 | case composite_t::ABSTRACT: 90 | case composite_t::CHILDREN: 91 | case composite_t::OPTIONAL: 92 | return 1; 93 | 94 | // else, primitive value 95 | case composite_t::SINGLE: 96 | return 0; 97 | 98 | default: 99 | throw Error{"unhandled composite type"}; 100 | } 101 | } 102 | 103 | 104 | bool BasicType::operator==(const BasicType &other) const { 105 | return (this->primitive_type == other.primitive_type and this->composite_type == other.composite_type); 106 | } 107 | 108 | 109 | std::string BasicType::str() const { 110 | if (this->is_fundamental()) { 111 | return type_to_string(this->primitive_type); 112 | } 113 | else if (this->is_composite()) { 114 | return composite_type_to_string(this->composite_type); 115 | } 116 | 117 | throw InternalError{"basic string neither fundamental nor composite"}; 118 | } 119 | 120 | 121 | // textual type conversion for the type definition in a member 122 | BasicType BasicType::from_type_token(const IDToken &tok) { 123 | // TODO: replace those lookup maps with a constexpr compiletime map. 124 | 125 | // primitive type name map 126 | static const std::unordered_map primitive_types = { 127 | {"bool", primitive_t::BOOLEAN}, 128 | {"text", primitive_t::TEXT}, 129 | {"file", primitive_t::FILENAME}, 130 | {"int", primitive_t::INT}, 131 | {"float", primitive_t::FLOAT}}; 132 | 133 | // container type name map 134 | static const std::unordered_map container_types = { 135 | {"set", composite_t::SET}, 136 | {"orderedset", composite_t::ORDEREDSET}, 137 | {"dict", composite_t::DICT}}; 138 | 139 | // modifier type name map 140 | static const std::unordered_map modifiers = { 141 | {"abstract", composite_t::ABSTRACT}, 142 | {"children", composite_t::CHILDREN}, 143 | {"optional", composite_t::OPTIONAL}}; 144 | 145 | primitive_t type = primitive_t::OBJECT; 146 | composite_t composite_type = composite_t::SINGLE; 147 | 148 | switch (tok.get_type()) { 149 | // type names are always identifiers: 150 | case token_type::ID: { 151 | auto it0 = primitive_types.find(tok.get_first()); 152 | if (it0 != std::end(primitive_types)) { 153 | type = it0->second; 154 | break; 155 | } 156 | 157 | auto it1 = container_types.find(tok.get_first()); 158 | if (it1 != std::end(container_types)) { 159 | type = primitive_t::CONTAINER; 160 | composite_type = it1->second; 161 | break; 162 | } 163 | 164 | auto it2 = modifiers.find(tok.get_first()); 165 | if (it2 != std::end(modifiers)) { 166 | type = primitive_t::MODIFIER; 167 | composite_type = it2->second; 168 | } 169 | break; 170 | } 171 | default: 172 | throw ASTError{"expected some type name but there is", tok}; 173 | } 174 | 175 | return BasicType{type, composite_type}; 176 | } 177 | 178 | } // namespace nyan 179 | -------------------------------------------------------------------------------- /nyan/namespace.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2023 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | #include 5 | #include 6 | 7 | #include "config.h" 8 | 9 | 10 | namespace nyan { 11 | 12 | class IDToken; 13 | 14 | /** 15 | * Identifier of a namespace, i.e. a directory, filename, or (nested) object. 16 | */ 17 | class Namespace { 18 | friend struct std::hash; 19 | 20 | public: 21 | /** 22 | * Create a namespace identifier. 23 | * 24 | * @param dir_components Directory path components. 25 | * @param filename Filename component. 26 | * @param obj_components Object components. 27 | */ 28 | Namespace(std::vector &&dir_components = {}, 29 | std::string &&filename = "", 30 | std::vector &&obj_components = {}); 31 | Namespace(const std::vector &dir_components = {}, 32 | const std::string &filename = "", 33 | const std::vector &obj_components = {}); 34 | Namespace(const Namespace &other, const std::string &obj_addend); 35 | 36 | ~Namespace() = default; 37 | 38 | /** 39 | * Pop the last component from the namespace ID. 40 | */ 41 | void pop_last(); 42 | 43 | /** 44 | * Check if the namespace ID is empty. 45 | * 46 | * @return true if the namespace ID is empty, else false. 47 | */ 48 | bool empty() const; 49 | 50 | /** 51 | * Append the given object name to the namespace identifier and get 52 | * the resulting identifier 53 | * 54 | * @param name IDToken with an object reference. 55 | * @param skip Number of components at the start of \p name to be skipped. 56 | * 57 | * @return fqon of the object. 58 | */ 59 | fqon_t combine(const IDToken &name, size_t skip = 0) const; 60 | 61 | /** 62 | * Check if the namespace ID refers to a directory. 63 | * 64 | * @return true if the namespace ID is a directory, else false. 65 | */ 66 | bool is_dir() const; 67 | 68 | /** 69 | * Check if the namespace ID refers to a file. 70 | * 71 | * @return true if the namespace ID is a file, else false. 72 | */ 73 | bool is_file() const; 74 | 75 | /** 76 | * Check if the namespace ID refers to a nyan object. 77 | * 78 | * @return true if the namespace ID is an object, else false. 79 | */ 80 | bool is_obj() const; 81 | 82 | /** 83 | * Check if the namespace ID refers to a nested nyan object. 84 | * 85 | * @return true if the namespace ID is a nested object, else false. 86 | */ 87 | bool is_nested_obj() const; 88 | 89 | /** 90 | * Get the directory path components of the namespace ID. 91 | * 92 | * @return Directory path components. 93 | */ 94 | const std::vector &get_dir_components() const; 95 | 96 | /** 97 | * Get the filename component of the namespace ID. 98 | * 99 | * @return Filename component. 100 | */ 101 | const std::string &get_filename() const; 102 | 103 | /** 104 | * Get the object components of the namespace ID. 105 | * 106 | * @return Object components. 107 | */ 108 | const std::vector &get_obj_components() const; 109 | 110 | /** 111 | * Get the directory path from the directory components inside the 112 | * namespace. 113 | * 114 | * @return Directory path from the namespace. 115 | */ 116 | std::string to_dirpath() const; 117 | 118 | /** 119 | * Get the file path from the directory and filename components inside 120 | * the namespace. 121 | * 122 | * If the namespace ID refers to an object, this is the path to the file 123 | * containing the object. 124 | * 125 | * @return File path from the namespace. 126 | */ 127 | std::string to_filepath() const; 128 | 129 | /** 130 | * Get the fqon for this namespace ID. 131 | * 132 | * @return fqon identifier. 133 | */ 134 | fqon_t to_fqon() const; 135 | 136 | /** 137 | * Get a string representation of this namespace. 138 | * 139 | * @return String representation of this namespace. 140 | */ 141 | std::string str() const; 142 | 143 | /** 144 | * Checks if this namespace is equal to a given namespace. 145 | * 146 | * @return true if the namespaces are equal, else false. 147 | */ 148 | bool operator==(const Namespace &other) const; 149 | 150 | /** 151 | * Create a namespace from a given filename. Performs a sanity 152 | * check on the filename. 153 | * 154 | * @param filename Name of a file, including the extension. 155 | * 156 | * @return Namespace for the filename. 157 | */ 158 | static Namespace from_filename(const std::string &filename); 159 | 160 | private: 161 | /** 162 | * Directory path components. 163 | */ 164 | std::vector dir_components; 165 | 166 | /** 167 | * Filename component. 168 | */ 169 | std::string filename; 170 | 171 | /** 172 | * Object components. 173 | */ 174 | std::vector obj_components; 175 | }; 176 | 177 | } // namespace nyan 178 | 179 | 180 | namespace std { 181 | template <> 182 | struct hash { 183 | size_t operator()(const nyan::Namespace &ns) const; 184 | }; 185 | } // namespace std 186 | -------------------------------------------------------------------------------- /nyan/value/number.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | #include 7 | 8 | #include "value.h" 9 | 10 | #include "../config.h" 11 | 12 | 13 | namespace nyan { 14 | 15 | class IDToken; 16 | 17 | class NumberBase : public Value { 18 | template 19 | friend class Number; 20 | 21 | public: 22 | virtual ~NumberBase() = default; 23 | 24 | /** Checks if the value is positive or negative infinity. */ 25 | virtual bool is_infinite() const = 0; 26 | 27 | /** Checks if the value is positive infinity */ 28 | virtual bool is_infinite_positive() const = 0; 29 | 30 | /** is this number zero? */ 31 | virtual bool is_zero() const = 0; 32 | 33 | /** Check if the number is positive. */ 34 | virtual bool is_positive() const = 0; 35 | 36 | protected: 37 | /** 38 | * get this number as float 39 | * does _not_ do infinity conversions. 40 | */ 41 | virtual value_float_t as_float() const = 0; 42 | 43 | /** 44 | * get this number as int 45 | * does _not_ do infinity conversions. 46 | */ 47 | virtual value_int_t as_int() const = 0; 48 | }; 49 | 50 | 51 | /** 52 | * Nyan value to store a number. 53 | */ 54 | template 55 | class Number : public NumberBase { 56 | public: 57 | /** the actual number is stored as a configurable type */ 58 | using storage_type = T; 59 | 60 | /** 61 | * result of an infinity calculation. 62 | * this and other mean the current operand values. 63 | */ 64 | enum class infinity_action { 65 | THIS, //!< keep the value of this number 66 | OTHER, //!< take the value of the other number 67 | INF_POS, //!< set this to positive infinite 68 | INF_NEG, //!< set this to negative infinite 69 | ZERO, //!< set this to zero 70 | }; 71 | 72 | Number(const IDToken &token); 73 | Number(T value) : 74 | value{value} {} 75 | 76 | ValueHolder copy() const override { 77 | return {std::make_shared(*this)}; 78 | } 79 | 80 | std::string str() const override { 81 | return std::to_string(this->value); 82 | } 83 | 84 | std::string repr() const override { 85 | return this->str(); 86 | } 87 | 88 | size_t hash() const override { 89 | return std::hash{}(this->value); 90 | } 91 | 92 | T get() const { 93 | return *this; 94 | } 95 | 96 | /** 97 | * Checks if the value is positive or negative infinity. 98 | */ 99 | bool is_infinite() const override; 100 | 101 | /** 102 | * Checks if the value is positive infinity. 103 | */ 104 | bool is_infinite_positive() const override; 105 | 106 | /** 107 | * Check if the number is zero. 108 | */ 109 | bool is_zero() const override; 110 | 111 | /** 112 | * Check if the number is positive. 113 | */ 114 | bool is_positive() const override; 115 | 116 | /** 117 | * Get positive infinity value for this number. 118 | */ 119 | constexpr static T infinite_pos(); 120 | 121 | /** 122 | * Get negative infinity value for this number. 123 | */ 124 | constexpr static T infinite_neg(); 125 | 126 | /** 127 | * Calculates the value that is assigned when one of the operands 128 | * of apply_value() is infinity. This is Maybe {pos_inf, neg_inf, this, other}. 129 | * In case the result is NaN, return Nothing. 130 | */ 131 | std::optional handle_infinity(const NumberBase &other, nyan_op operation); 132 | 133 | const std::unordered_set &allowed_operations(const Type &with_type) const override; 134 | const BasicType &get_type() const override; 135 | 136 | operator T() const { 137 | return this->value; 138 | } 139 | 140 | protected: 141 | bool apply_value(const Value &value, nyan_op operation) override; 142 | bool equals(const Value &other) const override { 143 | auto &other_val = dynamic_cast(other); 144 | return this->value == other_val.value; 145 | } 146 | 147 | /** get this number as float */ 148 | value_float_t as_float() const override; 149 | 150 | /** get this number as int */ 151 | value_int_t as_int() const override; 152 | 153 | 154 | /** 155 | * Actual numerical value. 156 | */ 157 | T value; 158 | }; 159 | 160 | 161 | /** 162 | * Integer storage. 163 | */ 164 | using Int = Number; 165 | 166 | 167 | /** 168 | * Floating point data type. 169 | */ 170 | using Float = Number; 171 | 172 | 173 | template <> 174 | constexpr Float::storage_type 175 | Number::infinite_pos() { 176 | return std::numeric_limits::infinity(); 177 | } 178 | 179 | template <> 180 | constexpr Float::storage_type 181 | Number::infinite_neg() { 182 | return -std::numeric_limits::infinity(); 183 | } 184 | 185 | 186 | template <> 187 | constexpr Int::storage_type 188 | Number::infinite_pos() { 189 | return std::numeric_limits::max(); 190 | } 191 | 192 | template <> 193 | constexpr Int::storage_type 194 | Number::infinite_neg() { 195 | return std::numeric_limits::min(); 196 | } 197 | 198 | 199 | } // namespace nyan 200 | -------------------------------------------------------------------------------- /nyan/token.h: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2021 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | #pragma once 3 | 4 | 5 | #include 6 | 7 | #include "error.h" 8 | #include "location.h" 9 | 10 | namespace nyan { 11 | 12 | class File; 13 | 14 | /** 15 | * Available token types. 16 | */ 17 | enum class token_type { 18 | AS, 19 | AT, 20 | BANG, 21 | COLON, 22 | COMMA, 23 | DEDENT, 24 | DOT, 25 | ENDFILE, 26 | ENDLINE, 27 | ELLIPSIS, 28 | FLOAT, 29 | FROM, 30 | ID, 31 | IMPORT, 32 | INDENT, 33 | INF, 34 | INT, 35 | INVALID, 36 | LANGLE, 37 | LBRACE, 38 | LBRACKET, 39 | LPAREN, 40 | OPERATOR, 41 | PASS, 42 | RANGLE, 43 | RBRACE, 44 | RBRACKET, 45 | RPAREN, 46 | STRING, 47 | }; 48 | 49 | /** 50 | * Known bracket types. 51 | */ 52 | enum class bracket_type { 53 | PAREN, 54 | ANGLE, 55 | BRACKET, 56 | BRACE, 57 | }; 58 | 59 | 60 | /** 61 | * Get the string representation of a given token type. 62 | * 63 | * @param type Token type. 64 | * 65 | * @return String representation of the token type. 66 | */ 67 | constexpr const char *token_type_str(token_type type) { 68 | using namespace std::string_literals; 69 | 70 | switch (type) { 71 | case token_type::AS: 72 | return "as"; 73 | case token_type::AT: 74 | return "@"; 75 | case token_type::BANG: 76 | return "!"; 77 | case token_type::COLON: 78 | return "colon"; 79 | case token_type::COMMA: 80 | return "comma"; 81 | case token_type::DEDENT: 82 | return "dedentation"; 83 | case token_type::DOT: 84 | return "dot"; 85 | case token_type::ELLIPSIS: 86 | return "ellipsis"; 87 | case token_type::ENDFILE: 88 | return "end of file"; 89 | case token_type::ENDLINE: 90 | return "end of line"; 91 | case token_type::FLOAT: 92 | return "float"; 93 | case token_type::FROM: 94 | return "from"; 95 | case token_type::ID: 96 | return "identifier"; 97 | case token_type::IMPORT: 98 | return "import"; 99 | case token_type::INDENT: 100 | return "indentation"; 101 | case token_type::INF: 102 | return "inf"; 103 | case token_type::INT: 104 | return "int"; 105 | case token_type::INVALID: 106 | return "invalid"; 107 | case token_type::LANGLE: 108 | return "'<'"; 109 | case token_type::LBRACE: 110 | return "'{'"; 111 | case token_type::LBRACKET: 112 | return "'['"; 113 | case token_type::LPAREN: 114 | return "'('"; 115 | case token_type::OPERATOR: 116 | return "operator"; 117 | case token_type::PASS: 118 | return "pass"; 119 | case token_type::RANGLE: 120 | return "'>'"; 121 | case token_type::RBRACE: 122 | return "'}'"; 123 | case token_type::RBRACKET: 124 | return "']'"; 125 | case token_type::RPAREN: 126 | return "')'"; 127 | case token_type::STRING: 128 | return "string"; 129 | } 130 | 131 | return "unhandled token_type"; 132 | } 133 | 134 | 135 | /** 136 | * Check if the given token type requires a payload storage. 137 | * If not, then the token type is already enough information. 138 | * 139 | * @param type Token type. 140 | * 141 | * @return true if the token type requires a payload, else false. 142 | */ 143 | constexpr bool token_needs_payload(token_type type) { 144 | switch (type) { 145 | case token_type::FLOAT: 146 | case token_type::ID: 147 | case token_type::INF: 148 | case token_type::INT: 149 | case token_type::OPERATOR: 150 | case token_type::STRING: 151 | return true; 152 | default: 153 | return false; 154 | } 155 | } 156 | 157 | 158 | /** 159 | * Tokens are generated by the nyan lexer. 160 | */ 161 | class Token { 162 | public: 163 | Token(); 164 | Token(const std::shared_ptr &file, 165 | int line, 166 | int line_offset, 167 | int length, 168 | token_type type); 169 | Token(const std::shared_ptr &file, 170 | int line, 171 | int line_offset, 172 | int length, 173 | token_type type, 174 | const std::string &value); 175 | ~Token() = default; 176 | 177 | /** 178 | * Check if the token has a payload. 179 | * 180 | * @return true if a payload exists, else false. 181 | */ 182 | bool exists() const; 183 | 184 | /** 185 | * Check if the token is an endmarker (EOL or EOF). 186 | * 187 | * @return true if the token is an endmarker, else false. 188 | */ 189 | bool is_endmarker() const; 190 | 191 | /** 192 | * Check if the token type is used for storing content. 193 | * This can be member values or object references. 194 | * 195 | * @return true if the token type is used for content, else false. 196 | */ 197 | bool is_content() const; 198 | 199 | /** 200 | * Get the token payload. 201 | * 202 | * @return String of the token payload. 203 | */ 204 | const std::string &get() const; 205 | 206 | /** 207 | * Get the string representation of the token. 208 | * 209 | * @return String representation of the token. 210 | */ 211 | std::string str() const; 212 | 213 | /** 214 | * Location of the token in a file. 215 | */ 216 | Location location; 217 | 218 | /** 219 | * Token type. 220 | */ 221 | token_type type; 222 | 223 | protected: 224 | /** 225 | * Token payload. 226 | */ 227 | std::string value; 228 | }; 229 | 230 | } // namespace nyan 231 | -------------------------------------------------------------------------------- /nyan/state_history.cpp: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2020 the nyan authors, LGPLv3+. See copying.md for legal info. 2 | 3 | #include "state_history.h" 4 | 5 | #include "compiler.h" 6 | #include "database.h" 7 | #include "meta_info.h" 8 | #include "state.h" 9 | 10 | 11 | namespace nyan { 12 | 13 | StateHistory::StateHistory(const std::shared_ptr &base) { 14 | // create new empty state to work on at the beginning. 15 | this->insert( 16 | std::make_shared(base->get_state()), 17 | DEFAULT_T); 18 | } 19 | 20 | const std::shared_ptr &StateHistory::get_state(order_t t) const { 21 | return this->history.at(t); 22 | } 23 | 24 | 25 | const std::shared_ptr &StateHistory::get_state_before(order_t t) const { 26 | return this->history.at(t)->get_previous_state(); 27 | } 28 | 29 | 30 | const std::shared_ptr *StateHistory::get_state_exact(order_t t) const { 31 | return this->history.at_exact(t); 32 | } 33 | 34 | 35 | const std::shared_ptr *StateHistory::get_obj_state(const fqon_t &fqon, order_t t) const { 36 | // get the object history 37 | const ObjectHistory *obj_history = this->get_obj_history(fqon); 38 | 39 | // object isn't recorded in this state history 40 | if (obj_history == nullptr) { 41 | return nullptr; 42 | } 43 | 44 | std::optional order = obj_history->last_change_before(t); 45 | 46 | if (not order) { 47 | // the change is earlier than what is recorded in this history. 48 | return nullptr; 49 | } 50 | 51 | // now we know the time in history the object was changed 52 | const std::shared_ptr *state = this->history.at_exact(*order); 53 | if (unlikely(state == nullptr)) { 54 | throw InternalError{"no history record at change point"}; 55 | } 56 | 57 | const std::shared_ptr *obj_state = (*state)->get(fqon); 58 | if (unlikely(state == nullptr)) { 59 | throw InternalError{"object state not found at change point"}; 60 | } 61 | 62 | return obj_state; 63 | } 64 | 65 | 66 | void StateHistory::insert(std::shared_ptr &&new_state, order_t t) { 67 | // record the changes. 68 | for (const auto &it : new_state->get_objects()) { 69 | ObjectHistory &obj_history = this->get_create_obj_history(it.first); 70 | obj_history.insert_change(t); 71 | } 72 | 73 | // drop all later changes 74 | this->history.insert_drop(t, std::move(new_state)); 75 | } 76 | 77 | 78 | void StateHistory::insert_linearization(std::vector &&ins, order_t t) { 79 | const auto &obj = ins.at(0); 80 | 81 | this->get_create_obj_history(obj).linearizations.insert_drop(t, std::move(ins)); 82 | } 83 | 84 | 85 | const std::vector & 86 | StateHistory::get_linearization(const fqon_t &obj, order_t t, const MetaInfo &meta_info) const { 87 | const ObjectHistory *obj_hist = this->get_obj_history(obj); 88 | if (obj_hist != nullptr) { 89 | if (not obj_hist->linearizations.empty()) { 90 | auto ret = obj_hist->linearizations.at_find(t); 91 | 92 | if (ret != nullptr) { 93 | return *ret; 94 | } 95 | } 96 | } 97 | 98 | // otherwise, the lin is only stored in the database. 99 | const ObjectInfo *obj_info = meta_info.get_object(obj); 100 | if (unlikely(obj_info == nullptr)) { 101 | throw InternalError{"object not found in metainfo"}; 102 | } 103 | 104 | return obj_info->get_linearization(); 105 | } 106 | 107 | 108 | void StateHistory::insert_children(const fqon_t &obj, 109 | std::unordered_set &&ins, 110 | order_t t) { 111 | this->get_create_obj_history(obj).children.insert_drop(t, std::move(ins)); 112 | } 113 | 114 | 115 | const std::unordered_set & 116 | StateHistory::get_children(const fqon_t &obj, order_t t, const MetaInfo &meta_info) const { 117 | // first try the obj_history 118 | const ObjectHistory *obj_hist = this->get_obj_history(obj); 119 | if (obj_hist != nullptr) { 120 | if (not obj_hist->children.empty()) { 121 | auto ret = obj_hist->children.at_find(t); 122 | 123 | if (ret != nullptr) { 124 | return *ret; 125 | } 126 | } 127 | } 128 | 129 | // otherwise, the lin is only stored in the database. 130 | const ObjectInfo *obj_info = meta_info.get_object(obj); 131 | if (unlikely(obj_info == nullptr)) { 132 | throw InternalError{"object not found in metainfo"}; 133 | } 134 | 135 | return obj_info->get_children(); 136 | } 137 | 138 | 139 | ObjectHistory *StateHistory::get_obj_history(const fqon_t &obj) { 140 | auto it = this->object_obj_hists.find(obj); 141 | if (it != std::end(this->object_obj_hists)) { 142 | return &it->second; 143 | } 144 | else { 145 | return nullptr; 146 | } 147 | } 148 | 149 | 150 | const ObjectHistory *StateHistory::get_obj_history(const fqon_t &obj) const { 151 | auto it = this->object_obj_hists.find(obj); 152 | if (it != std::end(this->object_obj_hists)) { 153 | return &it->second; 154 | } 155 | else { 156 | return nullptr; 157 | } 158 | } 159 | 160 | 161 | ObjectHistory &StateHistory::get_create_obj_history(const fqon_t &obj) { 162 | auto it = this->object_obj_hists.find(obj); 163 | if (it != std::end(this->object_obj_hists)) { 164 | return it->second; 165 | } 166 | else { 167 | // create new obj_history entry. 168 | auto it_new_entry = this->object_obj_hists.emplace(obj, ObjectHistory{}).first; 169 | return it_new_entry->second; 170 | } 171 | } 172 | 173 | } // namespace nyan 174 | --------------------------------------------------------------------------------