diff --git a/.clang-format b/.clang-format
index 4d990a5..7f8e865 100644
--- a/.clang-format
+++ b/.clang-format
@@ -1,66 +1,211 @@
-# Generated from CLion C/C++ Code Style settings
+---
BasedOnStyle: LLVM
AccessModifierOffset: -2
-AlignAfterOpenBracket: Align
-AlignConsecutiveAssignments: None
+AlignAfterOpenBracket: BlockIndent
+AlignArrayOfStructures: None
+AlignConsecutiveAssignments:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveBitFields:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveDeclarations:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignConsecutiveMacros:
+ Enabled: false
+ AcrossEmptyLines: false
+ AcrossComments: false
+ AlignCompound: false
+ PadOperators: false
+AlignEscapedNewlines: Left
AlignOperands: Align
-AllowAllArgumentsOnNextLine: false
-AllowAllConstructorInitializersOnNextLine: false
-AllowAllParametersOfDeclarationOnNextLine: false
-AllowShortBlocksOnASingleLine: Always
-AllowShortCaseLabelsOnASingleLine: false
+AlignTrailingComments: true
+AllowAllArgumentsOnNextLine: true
+AllowAllConstructorInitializersOnNextLine: true
+AllowAllParametersOfDeclarationOnNextLine: true
+AllowShortBlocksOnASingleLine: Never
+AllowShortCaseLabelsOnASingleLine: true
+AllowShortEnumsOnASingleLine: true
AllowShortFunctionsOnASingleLine: All
-AllowShortIfStatementsOnASingleLine: Always
+AllowShortIfStatementsOnASingleLine: AllIfsAndElse
AllowShortLambdasOnASingleLine: All
-AllowShortLoopsOnASingleLine: true
+AllowShortLoopsOnASingleLine: false
+AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
+AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
-BreakBeforeBraces: Custom
+AttributeMacros:
+ - __capability
+BinPackArguments: false
+BinPackParameters: false
+BitFieldColonSpacing: Both
BraceWrapping:
- AfterCaseLabel: false
- AfterClass: false
- AfterControlStatement: Never
+ AfterCaseLabel: true
+ AfterClass: true
+ AfterControlStatement: Always
AfterEnum: false
AfterFunction: false
AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
AfterUnion: false
+ AfterExternBlock: false
BeforeCatch: false
BeforeElse: false
+ BeforeLambdaBody: false
+ BeforeWhile: false
IndentBraces: false
- SplitEmptyFunction: false
+ SplitEmptyFunction: true
SplitEmptyRecord: true
+ SplitEmptyNamespace: true
+BreakAfterJavaFieldAnnotations: false
BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Attach
+BreakBeforeConceptDeclarations: Always
BreakBeforeTernaryOperators: true
-BreakConstructorInitializers: BeforeColon
+BreakConstructorInitializers: AfterColon
BreakInheritanceList: BeforeColon
-ColumnLimit: 0
+BreakStringLiterals: true
+ColumnLimit: 120
+CommentPragmas: "^ IWYU pragma:"
CompactNamespaces: false
-ContinuationIndentWidth: 4
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+ConstructorInitializerIndentWidth: 2
+ContinuationIndentWidth: 2
+Cpp11BracedListStyle: false
+DeriveLineEnding: true
+DerivePointerAlignment: false
+DisableFormat: false
+EmptyLineAfterAccessModifier: Never
+EmptyLineBeforeAccessModifier: LogicalBlock
+ExperimentalAutoDetectBinPacking: false
+FixNamespaceComments: false
+ForEachMacros:
+ - foreach
+ - Q_FOREACH
+ - BOOST_FOREACH
+IfMacros:
+ - KJ_IF_MAYBE
+IncludeBlocks: Preserve
+IncludeCategories:
+ - Regex: ^"(llvm|llvm-c|clang|clang-c)/
+ Priority: 2
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: ^(<|"(gtest|gmock|isl|json)/)
+ Priority: 3
+ SortPriority: 0
+ CaseSensitive: false
+ - Regex: .*
+ Priority: 1
+ SortPriority: 0
+ CaseSensitive: false
+IncludeIsMainRegex: (Test)?$
+IncludeIsMainSourceRegex: ""
+IndentAccessModifiers: false
+IndentCaseBlocks: true
IndentCaseLabels: true
+IndentExternBlock: AfterExternBlock
+IndentGotoLabels: true
IndentPPDirectives: None
+IndentRequiresClause: true
IndentWidth: 2
+IndentWrappedFunctionNames: false
+InsertBraces: false
+InsertTrailingCommas: None
+JavaScriptQuotes: Leave
+JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
-MaxEmptyLinesToKeep: 2
+LambdaBodyIndentation: Signature
+Language: Cpp
+MacroBlockBegin: ""
+MacroBlockEnd: ""
+MaxEmptyLinesToKeep: 1
NamespaceIndentation: All
+ObjCBinPackProtocolList: Auto
+ObjCBlockIndentWidth: 2
+ObjCBreakBeforeNestedBlockParam: true
ObjCSpaceAfterProperty: false
ObjCSpaceBeforeProtocolList: true
+PPIndentWidth: -1
+PackConstructorInitializers: Never
+PenaltyBreakAssignment: 2
+PenaltyBreakBeforeFirstCallParameter: 19
+PenaltyBreakComment: 300
+PenaltyBreakFirstLessLess: 120
+PenaltyBreakOpenParenthesis: 0
+PenaltyBreakString: 1000
+PenaltyBreakTemplateDeclaration: 10
+PenaltyExcessCharacter: 1000000
+PenaltyIndentedWhitespace: 0
+PenaltyReturnTypeOnItsOwnLine: 60
PointerAlignment: Left
-ReflowComments: false
+QualifierAlignment: Leave
+ReferenceAlignment: Pointer
+ReflowComments: true
+RemoveBracesLLVM: false
+RequiresClausePosition: OwnLine
+SeparateDefinitionBlocks: Leave
+ShortNamespaceLines: 1
+SortIncludes: Never
+SortJavaStaticImport: Before
+SortUsingDeclarations: true
SpaceAfterCStyleCast: true
SpaceAfterLogicalNot: false
-SpaceAfterTemplateKeyword: false
+SpaceAfterTemplateKeyword: true
+SpaceAroundPointerQualifiers: Default
SpaceBeforeAssignmentOperators: true
+SpaceBeforeCaseColon: false
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
-SpaceBeforeRangeBasedForLoopColon: false
+SpaceBeforeParensOptions:
+ AfterControlStatements: true
+ AfterForeachMacros: true
+ AfterFunctionDeclarationName: false
+ AfterFunctionDefinitionName: false
+ AfterIfMacros: true
+ AfterOverloadedOperator: false
+ AfterRequiresInClause: false
+ AfterRequiresInExpression: false
+ BeforeNonEmptyParentheses: false
+SpaceBeforeRangeBasedForLoopColon: true
+SpaceBeforeSquareBrackets: false
+SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
-SpacesBeforeTrailingComments: 0
-SpacesInAngles: false
+SpacesBeforeTrailingComments: 1
+SpacesInAngles: Never
SpacesInCStyleCastParentheses: false
-SpacesInContainerLiterals: false
+SpacesInConditionalStatement: false
+SpacesInContainerLiterals: true
+SpacesInLineCommentPrefix:
+ Minimum: 1
+ Maximum: -1
SpacesInParentheses: false
SpacesInSquareBrackets: false
+Standard: Latest
+StatementAttributeLikeMacros:
+ - Q_EMIT
+StatementMacros:
+ - Q_UNUSED
+ - QT_REQUIRE_VERSION
TabWidth: 2
-UseTab: Always
+UseCRLF: false
+UseTab: ForContinuationAndIndentation
+WhitespaceSensitiveMacros:
+ - STRINGIZE
+ - PP_STRINGIZE
+ - BOOST_PP_STRINGIZE
+ - NS_SWIFT_NAME
+ - CF_SWIFT_NAME
\ No newline at end of file
diff --git a/.cproject b/.cproject
new file mode 100644
index 0000000..7891ac7
--- /dev/null
+++ b/.cproject
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.docs/Designs/Language.drawio b/.docs/Designs/Language.drawio
new file mode 100644
index 0000000..88238fd
--- /dev/null
+++ b/.docs/Designs/Language.drawio
@@ -0,0 +1,428 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.docs/Gallery/Oscript.gif b/.docs/Gallery/Oscript.gif
new file mode 100644
index 0000000..932a66f
Binary files /dev/null and b/.docs/Gallery/Oscript.gif differ
diff --git a/.docs/Gallery/RayTracer.png b/.docs/Gallery/RayTracer.png
new file mode 100644
index 0000000..1fe76b2
Binary files /dev/null and b/.docs/Gallery/RayTracer.png differ
diff --git a/.docs/Gallery/RayTracerLua.png b/.docs/Gallery/RayTracerLua.png
new file mode 100644
index 0000000..f49bbd0
Binary files /dev/null and b/.docs/Gallery/RayTracerLua.png differ
diff --git a/.docs/Gallery/Sketch3D.gif b/.docs/Gallery/Sketch3D.gif
new file mode 100644
index 0000000..890b1f3
Binary files /dev/null and b/.docs/Gallery/Sketch3D.gif differ
diff --git a/.docs/Gallery/oscript.txt b/.docs/Gallery/oscript.txt
new file mode 100644
index 0000000..72b43e1
--- /dev/null
+++ b/.docs/Gallery/oscript.txt
@@ -0,0 +1,16 @@
+method myLogFunction(value) {
+ print value;
+}
+
+myLogFunction("hello");
+
+var i = 10;
+
+if (i == 10) {
+ while (i > 0) {
+ print i;
+ i = i - 1;
+ }
+} else {
+ print "still doin' heavy calculations...";
+}
diff --git a/.github/workflows/WindowsPlatform.yml b/.github/workflows/WindowsPlatform.yml
new file mode 100644
index 0000000..760ad45
--- /dev/null
+++ b/.github/workflows/WindowsPlatform.yml
@@ -0,0 +1,17 @@
+name: Windows
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+jobs:
+ build:
+ runs-on: self-hosted
+
+ steps:
+ - uses: actions/checkout@v3
+
+ #- name: Build
+ #run: echo hello
diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml
new file mode 100644
index 0000000..be2d4e7
--- /dev/null
+++ b/.github/workflows/cmake-single-platform.yml
@@ -0,0 +1,49 @@
+# This starter workflow is for a CMake project running on a single platform. There is a different starter workflow if you need cross-platform coverage.
+# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-multi-platform.yml
+name: CMake on a single platform
+
+on:
+ push:
+ branches: [ "master" ]
+ pull_request:
+ branches: [ "master" ]
+
+env:
+ # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
+ BUILD_TYPE: Release
+
+jobs:
+ build:
+ # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
+ # You can convert this to a matrix build if you need cross-platform coverage.
+ # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Sutup Dependencies
+ run: |
+ git submodule update --init --recursive
+ sudo apt-get update
+ sudo apt-get install python3 python-is-python3
+ sudo apt-get install -y libx11-dev libgl1-mesa-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
+ sudo apt-get install -y clang-15
+ sudo apt-get install -y libasound2-dev libglew-dev
+ sudo apt-get install -y portaudio19-dev
+
+ - name: Configure CMake
+ # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
+ # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
+ run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+
+ - name: Build
+ # Build your program with the given configuration
+ run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
+
+ - name: Test
+ working-directory: ${{github.workspace}}/build
+ # Execute tests defined by the CMake configuration.
+ # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
+ run: ctest -C ${{env.BUILD_TYPE}}
+
diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml
index 0e85c3d..4041169 100644
--- a/.github/workflows/cmake.yml
+++ b/.github/workflows/cmake.yml
@@ -18,25 +18,47 @@ jobs:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v3
- - name: Install LLVM
- run: sudo apt-get install -y llvm
-
- - name: Set LLVM Toolchain
- run: |
- sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 10
- sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 20
+ - name: Setup
+ shell: bash
+ run: |
+ # If your submodules are configured to use SSH instead of HTTPS please uncomment the following line
+ # git config --global url."https://github.com/".insteadOf "git@github.com:"
+ auth_header="$(git config --local --get http.https://github.com/.extraheader)"
- - name: Configure CMake
- run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+ git submodule sync --recursive
+ git -c "http.extraheader=$auth_header" -c protocol.version=2 submodule update --init --force --recursive --depth=1
+
+ sudo apt-get update
+ sudo apt-get install python3 python-is-python3
+ sudo apt-get install -y libx11-dev libgl1-mesa-dev libxrandr-dev libxinerama-dev libxcursor-dev libxi-dev
+ sudo apt-get install -y clang-15
+ sudo apt-get install -y libasound2-dev libglew-dev
+ sudo apt-get install -y portaudio19-dev
+ #cd Externals/glew/
+ #make extensions
+ sudo apt update
+
+ #- name: Set LLVM Toolchain
+ #run: |
+ # sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/g++ 10
+ # sudo update-alternatives --install /usr/bin/c++ c++ /usr/bin/clang++ 20
+
+ - name: Configure CMake
+ run: |
+ gcc -v
+ clang++-15 -v
+ CC=clang-15 CXX=clang++-15 cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
+
+ - name: Build
+ run: CC=clang CXX=clang++ cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} -v -j
+
+ - name: Test
+ working-directory: ${{github.workspace}}/build
+ # Execute tests defined by the CMake configuration.
+ # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
+ run: ctest -j -C ${{env.BUILD_TYPE}}
- - name: Build
- run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- - name: Test
- working-directory: ${{github.workspace}}/build
- # Execute tests defined by the CMake configuration.
- # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
- run: ctest -C ${{env.BUILD_TYPE}}
diff --git a/.gitignore b/.gitignore
index b0247cb..5493878 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,11 @@
*tmp*
bin
build*
+*build*
lib
install
.vscode
+out
+.vs
+*.bkp
+*.$*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..9d1d1de
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,21 @@
+[submodule "Externals/imgui"]
+ path = Externals/imgui
+ url = https://github.com/elushaX/imgui.git
+[submodule "Externals/lua"]
+ path = Externals/lua
+ url = https://github.com/elushaX/lua.git
+[submodule "Externals/unittest-cpp"]
+ path = Externals/unittest-cpp
+ url = https://github.com/elushaX/unittest-cpp.git
+[submodule "Externals/glfw"]
+ path = Externals/glfw
+ url = https://github.com/elushaX/glfw.git
+[submodule "Externals/nanovg"]
+ path = Externals/nanovg
+ url = https://github.com/elushaX/nanovg.git
+[submodule "Externals/lalr"]
+ path = Externals/lalr
+ url = https://github.com/elushaX/lalr.git
+[submodule "Externals/asio"]
+ path = Externals/asio
+ url = https://github.com/elushaX/asio.git
diff --git a/.project b/.project
new file mode 100644
index 0000000..4dcf3c3
--- /dev/null
+++ b/.project
@@ -0,0 +1,20 @@
+
+
+ Modules
+
+
+
+
+
+ org.eclipse.cdt.core.cBuilder
+ clean,full,incremental,
+
+
+
+
+
+ org.eclipse.cdt.core.cnature
+ org.eclipse.cdt.core.ccnature
+ org.eclipse.cdt.cmake.core.cmakeNature
+
+
diff --git a/Allocators/CMakeLists.txt b/.wip/Language/CMakeLists.txt
similarity index 54%
rename from Allocators/CMakeLists.txt
rename to .wip/Language/CMakeLists.txt
index fae102e..2ac316c 100644
--- a/Allocators/CMakeLists.txt
+++ b/.wip/Language/CMakeLists.txt
@@ -3,20 +3,18 @@ cmake_minimum_required(VERSION 3.2)
set(CMAKE_CXX_STANDARD 23)
-project(Allocator)
+project(Language)
### ---------------------- Static Library --------------------- ###
-file(GLOB SOURCES "./private/*.cpp")
-file(GLOB HEADERS "./public/*.hpp")
+file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
+file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp")
add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
-target_include_directories(${PROJECT_NAME} PUBLIC ./public/)
-target_link_libraries(${PROJECT_NAME} PUBLIC Utils)
+target_include_directories(${PROJECT_NAME} PUBLIC public/)
+target_link_libraries(${PROJECT_NAME} PUBLIC Strings)
### -------------------------- Tests -------------------------- ###
enable_testing()
file(GLOB TEST_SOURCES "./tests/*.cpp")
add_executable(${PROJECT_NAME}Tests ${TEST_SOURCES})
-target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} Utils)
-add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
-
-install(TARGETS ${PROJECT_NAME} LIBRARY DESTINATION ${CMAKE_INSTALL_PREFIX}/${PROJECT_NAME}/lib)
\ No newline at end of file
+target_link_libraries(${PROJECT_NAME}Tests ${PROJECT_NAME} UnitTest++)
+add_test(NAME ${PROJECT_NAME}Tests COMMAND ${PROJECT_NAME}Tests)
\ No newline at end of file
diff --git a/.wip/Language/private/Grammar.cpp b/.wip/Language/private/Grammar.cpp
new file mode 100644
index 0000000..d6acecf
--- /dev/null
+++ b/.wip/Language/private/Grammar.cpp
@@ -0,0 +1,36 @@
+
+#include "Grammar.hpp"
+
+using namespace tp;
+
+ContextFreeGrammar::Arg::Arg(const String& id, bool terminal, bool epsilon) {
+ mId = id;
+ mIsTerminal = terminal;
+ mIsEpsilon = epsilon;
+}
+
+const String& ContextFreeGrammar::Arg::getId() const { return mId; }
+
+bool ContextFreeGrammar::Arg::operator==(const Arg& in) const {
+ return (mId == in.mId) && (mIsEpsilon == in.mIsEpsilon) && (mIsTerminal == in.mIsTerminal);
+}
+
+ContextFreeGrammar::Rule::Rule(const String& id, const InitialierList& args) {
+ mId = id;
+ mArgs = args;
+}
+
+bool ContextFreeGrammar::Rule::operator==(const Rule& in) const { return (mId == in.mId) && (mArgs == in.mArgs); }
+
+bool ContextFreeGrammar::Rule::isProductive() const {
+ for (auto arg : mArgs) {
+ if (arg->getId() == mId) return false;
+ }
+ return true;
+}
+
+void ContextFreeGrammar::addRule(const Rule& rule) { mRules.append(rule); }
+
+void ContextFreeGrammar::addRule(const String& id, const InitialierList& args) { addRule(Rule(id, args)); }
+
+void ContextFreeGrammar::setStart(const String& startRule) { mStartTerminal = startRule; }
\ No newline at end of file
diff --git a/.wip/Language/private/LanguageCommon.cpp b/.wip/Language/private/LanguageCommon.cpp
new file mode 100644
index 0000000..147536d
--- /dev/null
+++ b/.wip/Language/private/LanguageCommon.cpp
@@ -0,0 +1,8 @@
+
+#include "LanguageCommon.hpp"
+#include "Strings.hpp"
+
+using namespace tp;
+
+static ModuleManifest* sModuleDependencies[] = { &gModuleStrings, nullptr };
+ModuleManifest tp::gModuleLanguage = ModuleManifest("Language", nullptr, nullptr, sModuleDependencies);
diff --git a/.wip/Language/public/Automata.hpp b/.wip/Language/public/Automata.hpp
new file mode 100644
index 0000000..0e059b9
--- /dev/null
+++ b/.wip/Language/public/Automata.hpp
@@ -0,0 +1,249 @@
+
+#pragma once
+
+#include "Utils.hpp"
+#include "List.hpp"
+#include "Map.hpp"
+#include "Tree.hpp"
+
+namespace tp {
+
+ // Non-Deterministic Finite-State Automata
+ template
+ class FiniteStateAutomation {
+ public:
+ struct State;
+
+ public:
+ class Transition {
+ friend FiniteStateAutomation;
+
+ public:
+ enum Type { ANY, EPSILON, SYMBOL };
+
+ public:
+ Transition(Type type, State* state, tAlphabetType symbol = tAlphabetType()) {
+ mState = state;
+ mType = type;
+ mSymbol = symbol;
+ }
+
+ [[nodiscard]] bool isTransition(const tAlphabetType& symbol) const {
+ return (mType == ANY || mType == EPSILON) || (mSymbol == symbol);
+ }
+
+ [[nodiscard]] bool doesConsumes(const tAlphabetType& symbol) const {
+ return (mType == ANY || (mType == SYMBOL && mSymbol == symbol));
+ }
+
+ [[nodiscard]] bool isEpsilon() const { return mType == EPSILON; }
+
+ const State* getState() const { return mState; }
+ const tAlphabetType& getSymbol() const { return mSymbol; }
+
+ private:
+ State* mState = nullptr;
+ Type mType;
+ tAlphabetType mSymbol;
+ };
+
+ class State {
+ friend FiniteStateAutomation;
+
+ public:
+ State() = default;
+
+ public:
+ void setValue(const tStateType& stateValue) { mStateVal = stateValue; }
+ void setAcceptance(bool isAccepting) { mIsAccepting = isAccepting; }
+ [[nodiscard]] bool isAccepting() const { return mIsAccepting; }
+ const tStateType& getStateVal() const { return mStateVal; }
+ [[nodiscard]] const Buffer* getTransitions() const { return &mTransitions; }
+
+ private:
+ Buffer mTransitions{};
+ tStateType mStateVal = tStateType();
+ bool mIsAccepting = false;
+ };
+
+ private:
+ List mStates;
+ State* mStartState = nullptr;
+ Range mAlphabetRange = { ENV_UALNI_MAX, ENV_UALNI_MIN };
+
+ public:
+ FiniteStateAutomation() = default;
+
+ State* addState(const tStateType& state, bool accepting) {
+ auto node = mStates.newNode();
+ node->data.mIsAccepting = accepting;
+ node->data.mStateVal = state;
+ mStates.pushBack(node);
+ return &node->data;
+ }
+
+ void addTransition(State* from, State* to, const tAlphabetType& symbol) {
+ from->mTransitions.append(Transition(Transition::SYMBOL, to, symbol));
+ if (mAlphabetRange.mBegin < ualni(symbol)) mAlphabetRange.mBegin = ualni(symbol);
+ if (mAlphabetRange.mEnd > ualni(symbol)) mAlphabetRange.mEnd = ualni(symbol);
+ }
+
+ void addEpsilonTransition(State* from, State* to) { from->mTransitions.append(Transition(Transition::SYMBOL, to)); }
+
+ void addAnyTransition(State* from, State* to) { from->mTransitions.append(Transition(Transition::ANY, to)); }
+
+ void setStartState(State* start) { mStartState = start; }
+
+ [[nodiscard]] State* getStartState() const { return mStartState; }
+
+ [[nodiscard]] bool isValid() const {
+ if (!mStartState) {
+ return false;
+ }
+ return true;
+ }
+
+ [[nodiscard]] ualni numStates() const { return mStates.length(); }
+
+ [[nodiscard]] const List* getStates() const { return &mStates; }
+
+ [[nodiscard]] Range getAlphabetRange() const { return mAlphabetRange; }
+
+ private:
+ typedef AvlTree, bool> StatesSet;
+
+ // Expands initial set with states that are reachable from initial set with no input consumption (E-transitions)
+ static void expandSet(StatesSet& set) {
+ List workingSet;
+
+ set.forEach([&](AvlNumericKey& key, bool) { workingSet.pushBack(key.val); });
+
+ while (workingSet.length()) {
+ auto first = workingSet.first()->data;
+ set.insert(first, {});
+
+ for (auto transition : first->mTransitions) {
+ if (!transition->isEpsilon()) continue;
+ if (set.find(transition->mState)) continue;
+ workingSet.pushBack(transition->mState);
+ }
+
+ workingSet.popFront();
+ }
+ }
+
+ // States that are reachable from initial set with symbol transition
+ static void findMoveSet(StatesSet& from, StatesSet& moveSet, tAlphabetType symbol) {
+ from.forEach([&](AvlNumericKey& key, bool) {
+ for (auto transition : key.val->mTransitions) {
+ if (transition->isEpsilon()) continue;
+ if (!transition->isTransition(symbol)) continue;
+ if (moveSet.find(transition->mState)) continue;
+ moveSet.insert(transition->mState, {});
+ }
+ });
+ }
+
+ public:
+ bool makeDeterministic() {
+ if (!isValid()) return false;
+
+ struct GroupKey {
+ const StatesSet* group;
+ static ualni hash(GroupKey key) { return 0; }
+ bool operator==(const GroupKey& key) const { return false; }
+ };
+
+ struct GroupInfo {
+ StatesSet* group = nullptr;
+ AvlTree, tAlphabetType> transitions;
+ State* newState = nullptr;
+ bool accepting = false;
+ tStateType stateVal = tStateType();
+ };
+
+ Buffer groups = { {} };
+ Map groupInfos;
+
+ groups.first().insert(getStartState(), false);
+
+ expandSet(groups.first());
+
+ groupInfos.put({ &groups.first() }, { &groups.first() });
+
+ // 1) find new states
+ List workingSet;
+ workingSet.pushBack(&groups.first());
+
+ while (workingSet.length()) {
+ StatesSet* group = workingSet.first()->data;
+ GroupInfo* info = &groupInfos.get({ group });
+
+ for (auto symbol : getAlphabetRange()) {
+
+ // calculate new possible state
+ StatesSet potentialGroup;
+
+ findMoveSet(*group, potentialGroup, tAlphabetType(symbol));
+ expandSet(potentialGroup);
+
+ if (!potentialGroup.size()) continue;
+
+ // find existing or create group
+ StatesSet* targetGroup = nullptr;
+ auto iter = groupInfos.presents({ &potentialGroup });
+ if (iter) {
+ targetGroup = groupInfos.getSlotVal(iter).group;
+ } else {
+ targetGroup = &groups.append(potentialGroup);
+ groupInfos.put({ targetGroup }, { targetGroup });
+ workingSet.pushBack(targetGroup);
+ }
+
+ // assert transition is added
+ info->transitions.insert(targetGroup, tAlphabetType(symbol));
+ }
+
+ workingSet.popFront();
+ }
+
+ // 2) find new states termination values
+ for (auto group : groupInfos) {
+ GroupInfo* info = &group->val;
+ ualni accepting = 0;
+
+ info->group->forEach([&](AvlNumericKey& key, bool) {
+ if (key.val->mIsAccepting) {
+ accepting++;
+ info->accepting = true;
+ info->stateVal = key.val->mStateVal;
+ }
+ });
+
+ if (!accepting) {
+ info->accepting = false;
+ info->stateVal = info->group->head()->key.val->mStateVal;
+ }
+ }
+
+ // 3) transfer
+ mStates.removeAll();
+
+ // create states
+ for (auto group : groupInfos) {
+ group->val.newState = addState(group->val.stateVal, group->val.accepting);
+ }
+
+ // create transitions
+ for (auto group : groupInfos) {
+ auto functor = [&](AvlNumericKey targetGroupKey, tAlphabetType symbol) {
+ GroupInfo* targetGroup = &groupInfos.get({ (StatesSet*) targetGroupKey.val });
+ addTransition(group->val.newState, targetGroup->newState, symbol);
+ };
+ group->val.transitions.forEach(functor);
+ }
+
+ return true;
+ }
+ };
+}
\ No newline at end of file
diff --git a/.wip/Language/public/ContextFreeAutomata.hpp b/.wip/Language/public/ContextFreeAutomata.hpp
new file mode 100644
index 0000000..9c4f698
--- /dev/null
+++ b/.wip/Language/public/ContextFreeAutomata.hpp
@@ -0,0 +1,137 @@
+
+#pragma once
+
+#include "Strings.hpp"
+#include "Automata.hpp"
+#include "Buffer2D.hpp"
+
+namespace tp {
+
+ template
+ class ContextFreeAutomata {
+
+ struct Action {
+ enum Type { SHIFT, REDUCE, TRAP } type = TRAP;
+ ualni num = 0; // state to shift (shift action) or pop count (reduce action)
+ };
+
+ public:
+ struct StackItem {
+ ualni state = 0;
+ tAlphabetType symbol;
+ Buffer leafs;
+ };
+
+ struct AcceptResult {
+ bool accepted = false;
+ ualni advancedIdx = 0;
+ const StackItem* ast = nullptr;
+ };
+
+ public:
+ ContextFreeAutomata() = default;
+
+ AcceptResult accept(const tAlphabetType* stream, ualni size) {
+ mCurrentState = mStartState;
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+
+ ualni advancedIdx = 0;
+ while (advancedIdx < size) {
+ const tAlphabetType& symbol = *(stream + advancedIdx);
+
+ if (!(symbol >= mRange.mBegin && symbol < mRange.mEnd)) {
+ return { false, advancedIdx, nullptr };
+ }
+
+ const Action& action = mTable.get({ ualni(symbol - mRange.mBegin), mCurrentState });
+
+ if (action.type == Action::TRAP) {
+ return { false, advancedIdx, nullptr };
+ }
+
+ if (action.type == Action::SHIFT) {
+ mStack.last()->symbol = symbol;
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+ mCurrentState = action.num;
+ }
+
+ if (mTable.get({ 0, mCurrentState }).type == Action::REDUCE) {
+ StackItem* newItem = &mItems.append(StackItem{});
+ for (auto iter : Range(action.num)) {
+ newItem->leafs.append(mStack.last());
+ mCurrentState = mStack.last()->state;
+ mStack.pop();
+ }
+
+ if (!mStack.size()) {
+ if (advancedIdx == size) {
+ return { true, advancedIdx, newItem };
+ } else {
+ return { false, advancedIdx, {} };
+ }
+ }
+
+ mStack.append(&mItems.append({ mCurrentState, {}, {} }));
+ }
+
+ advancedIdx++;
+ }
+
+ return { false, advancedIdx, nullptr };
+ }
+
+ public:
+ typedef FiniteStateAutomation Automata;
+ typedef Automata::State AutomataState;
+
+ void construct(const Automata& automata) {
+ mRange = automata.getAlphabetRange();
+
+ const ualni numStates = automata.numStates();
+ const ualni numSymbols = mRange.idxDiff();
+
+ mTable.reserve({ numSymbols, numStates });
+ mTable.assign(Action{ Action::TRAP, 0 });
+
+ Map states;
+ ualni stateIndex = 0;
+ for (auto state : *automata.getStates()) {
+ states.put(&state.data(), { stateIndex });
+ stateIndex++;
+ }
+
+ stateIndex = 0;
+ for (auto state : *automata.getStates()) {
+ if (&state.data() == automata.getStartState()) {
+ mStartState = stateIndex;
+ }
+
+ if (state->isAccepting()) {
+ ASSERT(state->getTransitions()->size() == 0)
+ for (auto symbolIndex : Range(numSymbols)) {
+ mTable.set({ stateIndex, symbolIndex }, { Action::REDUCE, state->getStateVal().numArgs() });
+ }
+ } else {
+ for (auto transition : *state->getTransitions()) {
+ ualni symbolIndex = ualni(transition->getSymbol()) - mRange.mBegin;
+ ualni targetStateIndex = states.get(transition->getState());
+ mTable.set({ stateIndex, symbolIndex }, { Action::SHIFT, targetStateIndex });
+ }
+ }
+
+ stateIndex++;
+ }
+ }
+
+ private:
+ Buffer2D mTable;
+
+ Buffer mItems;
+ Buffer mStack;
+
+ ualni mStartState = 0;
+ ualni mCurrentState = 0;
+
+ Range mRange;
+ };
+}
diff --git a/.wip/Language/public/ContextFreeCompiler.hpp b/.wip/Language/public/ContextFreeCompiler.hpp
new file mode 100644
index 0000000..db05b61
--- /dev/null
+++ b/.wip/Language/public/ContextFreeCompiler.hpp
@@ -0,0 +1,181 @@
+
+#pragma once
+
+#include "Automata.hpp"
+#include "Grammar.hpp"
+
+namespace tp {
+
+ class ContextFreeCompiler {
+ public:
+ struct SymbolVal {
+ SymbolVal() = default;
+
+ SymbolVal(ualni aId, ualni aStart, ualni aLen) {
+ id = aId;
+ start = aStart;
+ len = aLen;
+ };
+
+ SymbolVal(ualni val) { id = val; }
+
+ operator ualni() const { return id; }
+
+ bool operator==(const SymbolVal& in) const { return in.id == id; }
+ SymbolVal& operator=(const SymbolVal& in) = default;
+
+ ualni id = 0;
+ ualni start = 0;
+ ualni len = 0;
+ };
+
+ struct Item {
+ const ContextFreeGrammar::Rule* mRule = nullptr;
+ ualni mAdvanceIdx = 0;
+ ualni numArgs() const { return 0; }
+ };
+
+ struct Symbol {
+ String mId;
+ bool mIsTerminal = false;
+ };
+
+ private:
+ struct NonTerminal {
+ Buffer rules;
+ Map references;
+ Map referencing;
+
+ public:
+ [[nodiscard]] bool isProductive() const {
+ for (auto rule : rules) {
+ if (rule->isProductive()) return true;
+ }
+ return false;
+ }
+
+ [[nodiscard]] bool isLooped(Map& processed, const String& id) const {
+ for (auto ref : referencing) {
+ if (processed.presents(ref->key)) return true;
+ }
+ processed.put(id, {});
+ for (auto ref : referencing) {
+ if (ref->val->isLooped(processed, ref->key)) return true;
+ }
+ return false;
+ }
+ };
+
+ public:
+ bool compile(const ContextFreeGrammar& grammar, FiniteStateAutomation& automata) {
+ if (!init(grammar)) return false;
+ return true;
+ }
+
+ [[nodiscard]] const Buffer* getSymbols() const { return &mSymbols; }
+ [[nodiscard]] SymbolVal getSymbolId(const String& name) const { return mSymbolLookup.get(name); }
+
+ private:
+ bool init(const ContextFreeGrammar& grammar) {
+
+ if (!grammar.getRules()->size()) {
+ return false;
+ }
+
+ for (auto rule : *grammar.getRules()) {
+ if (!rule->getArgs()->size()) {
+ return false;
+ }
+ }
+
+ findNonTerminals(grammar);
+
+ for (auto nonTerminal : mNonTerminals) {
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isTerminal() || arg->isEpsilon()) continue;
+
+ if (!mNonTerminals.presents(arg->getId())) {
+ printf("Referenced non-terminal '%s' is not defined\n", arg->getId().read());
+ return false;
+ }
+ }
+ }
+ }
+
+ findAllReferences(grammar);
+
+ for (auto nonTerminal : mNonTerminals) {
+ if (!nonTerminal->val.references.size() && nonTerminal->key != grammar.getStartTerminal()) {
+ printf("Non-terminal '%s' is defined but not used\n", nonTerminal->key.read());
+ return false;
+ }
+ }
+
+ for (auto nonTerminal : mNonTerminals) {
+ if (!nonTerminal->val.isProductive()) {
+ printf("Non-terminal '%s' is not productive\n", nonTerminal->val.rules.first()->getId().read());
+ return false;
+ }
+ }
+
+ Map processed;
+ if (mNonTerminals.get(grammar.getStartTerminal()).isLooped(processed, grammar.getStartTerminal())) {
+ printf("Note that grammar is looped.\n");
+ return false;
+ }
+
+ initSymbols(grammar);
+
+ return true;
+ }
+
+ void findNonTerminals(const ContextFreeGrammar& grammar) {
+ for (auto rule : *grammar.getRules()) {
+ if (!mNonTerminals.presents(rule->getId())) {
+ mNonTerminals.put(rule->getId(), {});
+ }
+ auto nonTerminal = &mNonTerminals.get(rule->getId());
+ nonTerminal->rules.append(&rule.data());
+ }
+ }
+
+ void findAllReferences(const ContextFreeGrammar& grammar) {
+ for (auto nonTerminal : mNonTerminals) {
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isTerminal() || arg->isEpsilon()) continue;
+
+ NonTerminal* reference = &mNonTerminals.get(arg->getId());
+ nonTerminal->val.referencing.put(arg->getId(), reference);
+ reference->references.put(nonTerminal->key, &nonTerminal->val);
+ }
+ }
+ }
+ }
+
+ void initSymbols(const ContextFreeGrammar& grammar) {
+ for (auto nonTerminal : mNonTerminals) {
+ mSymbols.append({ nonTerminal->key, false });
+ mSymbolLookup.put(nonTerminal->key, SymbolVal(mSymbols.size() - 1));
+
+ for (auto rule : nonTerminal->val.rules) {
+ for (auto arg : *rule->getArgs()) {
+ if (arg->isEpsilon() || arg->isTerminal()) continue;
+ if (mTerminals.presents(arg->getId())) continue;
+ mTerminals.put(arg->getId(), {});
+
+ mSymbols.append({ nonTerminal->key, true });
+ mSymbolLookup.put(nonTerminal->key, SymbolVal(mSymbols.size() - 1));
+ }
+ }
+ }
+ }
+
+ private:
+ Map mNonTerminals;
+ Map mTerminals;
+ Buffer mSymbols;
+ Map mSymbolLookup;
+ };
+}
diff --git a/.wip/Language/public/Grammar.hpp b/.wip/Language/public/Grammar.hpp
new file mode 100644
index 0000000..df1fba5
--- /dev/null
+++ b/.wip/Language/public/Grammar.hpp
@@ -0,0 +1,224 @@
+
+#pragma once
+
+#include "Buffer.hpp"
+#include "LanguageCommon.hpp"
+#include "Map.hpp"
+#include "Strings.hpp"
+
+namespace tp {
+
+ class ContextFreeGrammar {
+ public:
+ struct Arg {
+ friend class Rule;
+
+ public:
+ Arg() = default;
+ explicit Arg(const String& id, bool terminal = true, bool epsilon = false);
+
+ public:
+ bool operator==(const Arg& in) const;
+ [[nodiscard]] const String& getId() const;
+ [[nodiscard]] bool isTerminal() const { return mIsTerminal; }
+ [[nodiscard]] bool isEpsilon() const { return mIsEpsilon; }
+
+ private:
+ String mId;
+ bool mIsTerminal = false;
+ bool mIsEpsilon = false;
+ };
+
+ class Rule {
+ public:
+ Rule() = default;
+ Rule(const String& id, const InitialierList& args);
+
+ public:
+ bool operator==(const Rule& in) const;
+ [[nodiscard]] bool isProductive() const;
+ [[nodiscard]] const String& getId() const { return mId; }
+ [[nodiscard]] const Buffer* getArgs() const { return &mArgs; }
+
+ private:
+ String mId;
+ Buffer mArgs;
+ };
+
+ public:
+ ContextFreeGrammar() = default;
+
+ public:
+ void addRule(const Rule& rule);
+ void addRule(const String& id, const InitialierList& args);
+ void setStart(const String& startRule);
+ [[nodiscard]] const Buffer* getRules() const { return &mRules; }
+ [[nodiscard]] const String& getStartTerminal() const { return mStartTerminal; }
+
+ public:
+ Buffer mRules;
+ String mStartTerminal;
+ bool mIsLooped = false;
+ };
+
+ template
+ class RegularGrammar {
+ public:
+ struct Node {
+ enum Type {
+ NONE,
+ ANY,
+ OR,
+ IF,
+ CLASS,
+ COMPOUND,
+ REPEAT,
+ VAL,
+ } mType = NONE;
+
+ explicit Node(Type type) :
+ mType(type) {}
+
+ virtual ~Node() = default;
+ };
+
+ class ValueNode : public Node {
+ public:
+ explicit ValueNode(tAlphabetType val) :
+ mVal(val),
+ Node(Node::VAL) {}
+
+ ~ValueNode() override = default;
+
+ public:
+ tAlphabetType mVal;
+ };
+
+ class CompoundNode : public Node {
+ public:
+ CompoundNode() :
+ Node(Node::COMPOUND) {}
+
+ CompoundNode(const InitialierList& nodes) :
+ Node(Node::COMPOUND) {
+ mSequence = nodes;
+ }
+
+ ~CompoundNode() override {
+ for (auto iter : mSequence) {
+ delete iter.data();
+ }
+ mSequence.clear();
+ }
+
+ public:
+ Buffer mSequence;
+ };
+
+ class AlternationNode : public Node {
+ public:
+ AlternationNode() :
+ Node(Node::OR) {}
+
+ AlternationNode(const Node* a, const Node* b) :
+ Node(Node::OR) {
+ mFirst = a;
+ mSecond = b;
+ }
+
+ ~AlternationNode() override {
+ delete mFirst;
+ delete mSecond;
+ }
+
+ public:
+ const Node* mFirst = nullptr;
+ const Node* mSecond = nullptr;
+ };
+
+ class IfNode : public Node {
+ public:
+ IfNode() :
+ Node(Node::IF) {}
+
+ explicit IfNode(const Node* a) :
+ Node(Node::IF) {
+ mNode = a;
+ }
+
+ ~IfNode() override { delete mNode; }
+
+ public:
+ const Node* mNode = nullptr;
+ };
+
+ class AnyNode : public Node {
+ public:
+ AnyNode() :
+ Node(Node::ANY) {}
+
+ ~AnyNode() override = default;
+ };
+
+ class RepetitionNode : public Node {
+ public:
+ RepetitionNode() :
+ Node(Node::REPEAT) {}
+
+ explicit RepetitionNode(const Node* rep, bool plus = false) :
+ Node(Node::REPEAT) {
+ mNode = rep;
+ mPlus = plus;
+ }
+
+ ~RepetitionNode() override { delete mNode; }
+
+ public:
+ Node* mNode = nullptr;
+ bool mPlus = false;
+ };
+
+ class ClassNode : public Node {
+ public:
+ ClassNode() :
+ Node(Node::CLASS) {}
+
+ explicit ClassNode(const Buffer>& ranges, bool exclude = false) :
+ Node(Node::CLASS) {
+ mExclude = exclude;
+ mRanges = ranges;
+ }
+
+ ~ClassNode() override { mRanges.removeAll(); }
+
+ public:
+ Buffer> mRanges;
+ bool mExclude = false;
+ };
+
+ public:
+ RegularGrammar() = default;
+
+ ~RegularGrammar() {
+ for (auto rule : mRules) {
+ delete rule->t1;
+ }
+ }
+
+ void addRule(const Node* node, tTokType id) { mRules.append({ node, id }); }
+
+ public:
+ const Node* seq(const InitialierList& nodes) { return new CompoundNode(nodes); }
+ const Node* val(tAlphabetType in) { return new ValueNode(in); }
+ const Node* alt(const Node* a, const Node* b) { return new AlternationNode(a, b); }
+ const Node* may(const Node* a) { return new IfNode(a); }
+ const Node* any() { return new AnyNode(); }
+ const Node* rep(const Node* rep, bool plus = false) { return new RepetitionNode(rep, plus); }
+ const Node* ranges(const Buffer>& ranges, bool exclude = false) {
+ return new ClassNode(ranges, exclude);
+ }
+
+ public:
+ Buffer> mRules;
+ };
+}
diff --git a/.wip/Language/public/LanguageCommon.hpp b/.wip/Language/public/LanguageCommon.hpp
new file mode 100644
index 0000000..0429431
--- /dev/null
+++ b/.wip/Language/public/LanguageCommon.hpp
@@ -0,0 +1,7 @@
+#pragma once
+
+#include "Module.hpp"
+
+namespace tp {
+ extern ModuleManifest gModuleLanguage;
+}
diff --git a/.wip/Language/public/Parser.hpp b/.wip/Language/public/Parser.hpp
new file mode 100644
index 0000000..10985ae
--- /dev/null
+++ b/.wip/Language/public/Parser.hpp
@@ -0,0 +1,107 @@
+
+#pragma once
+
+#include "RegularCompiler.hpp"
+#include "RegularAutomata.hpp"
+
+#include "ContextFreeCompiler.hpp"
+#include "ContextFreeAutomata.hpp"
+
+namespace tp {
+
+ template
+ class Parser {
+
+ typedef RegularGrammar RegularGrammar;
+ typedef RegularCompiler RegularCompiler;
+ typedef FiniteStateAutomation RegularGraph;
+ typedef RegularAutomata RegularAutomata;
+
+ // ContextFreeGrammar;
+ // ContextFreeCompiler;
+ typedef FiniteStateAutomation ContextFreeGraph;
+ typedef ContextFreeAutomata ContextFreeAutomata;
+
+ public:
+ struct ParseResult {
+ bool accepted = false;
+ const ContextFreeAutomata::StackItem* ast = nullptr;
+ };
+
+ public:
+ Parser() = default;
+
+ public:
+ bool compileTables(
+ const ContextFreeGrammar& cfGrammar,
+ const RegularGrammar& reGrammar,
+ const Map& contextFreeToRegular
+ ) {
+ // Compile Regular Grammar
+ {
+ RegularGraph graph;
+ RegularCompiler compiler;
+ compiler.compile(graph, reGrammar);
+ graph.makeDeterministic();
+ mRegularAutomata.construct(graph);
+ }
+
+ // compile context free grammar
+ {
+ ContextFreeGraph graph;
+ ContextFreeCompiler compiler;
+ compiler.compile(cfGrammar, graph);
+ graph.makeDeterministic();
+ mContextFreeAutomata.construct(graph);
+
+ // make glue
+ for (auto symbol : *compiler.getSymbols()) {
+ auto symbolId = compiler.getSymbolId(symbol->mId);
+ if (symbol->mIsTerminal) {
+ auto iter = contextFreeToRegular.presents(symbol->mId);
+ if (!iter) return false;
+ mGrammarGlue.put(contextFreeToRegular.getSlotVal(iter), symbolId);
+ } else {
+ mAstNames.put(symbolId, symbol->mId);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ ParseResult parse(const tAlphabetType* sentence, ualni sentenceLength) {
+ // get tokens stream
+ Buffer tokens;
+
+ const tAlphabetType* sentenceIter = sentence;
+ ualni lengthIter = sentenceLength;
+
+ while (lengthIter) {
+ auto result = mRegularAutomata.accept(sentenceIter, lengthIter);
+
+ if (!result.accepted) {
+ return { false, nullptr };
+ }
+
+ tokens.append(ContextFreeCompiler::SymbolVal(
+ mGrammarGlue.get(result.state), ualni(sentenceIter - sentence), result.advancedIdx
+ ));
+
+ sentenceIter += result.advancedIdx;
+ lengthIter -= result.advancedIdx;
+ }
+
+ ContextFreeAutomata::AcceptResult result = mContextFreeAutomata.accept(tokens.getBuff(), tokens.size());
+ return { result.accepted, result.ast };
+ }
+
+ public:
+ // save load compiled tables
+ RegularAutomata mRegularAutomata;
+ ContextFreeAutomata mContextFreeAutomata;
+
+ Map mGrammarGlue;
+ Map mAstNames;
+ };
+}
diff --git a/.wip/Language/public/RegularAutomata.hpp b/.wip/Language/public/RegularAutomata.hpp
new file mode 100644
index 0000000..501dada
--- /dev/null
+++ b/.wip/Language/public/RegularAutomata.hpp
@@ -0,0 +1,101 @@
+
+#pragma once
+
+#include "Strings.hpp"
+#include "Automata.hpp"
+#include "Buffer2D.hpp"
+
+namespace tp {
+
+ template
+ class RegularAutomata {
+ public:
+ struct AcceptResult {
+ bool accepted = false;
+ ualni advancedIdx = 0;
+ tStateType state = tStateType();
+ };
+
+ public:
+ RegularAutomata() = default;
+
+ AcceptResult accept(const tAlphabetType* stream, ualni size) {
+ mCurrentState = mStartState;
+
+ ualni advancedIdx = 0;
+
+ while (advancedIdx < size) {
+ const tAlphabetType& symbol = *(stream + advancedIdx);
+
+ if (!(symbol >= mSymbolRange.mBegin && symbol < mSymbolRange.mEnd)) {
+ return { false, advancedIdx, {} };
+ }
+
+ mCurrentState = mTable.get({ (ualni) (symbol - mSymbolRange.mBegin), mCurrentState });
+
+ if (mCurrentState == mStates.size()) {
+ return { false, advancedIdx, {} };
+ }
+
+ if (mStates[mCurrentState].first) {
+ return { true, advancedIdx, mStates[mCurrentState].second };
+ }
+
+ advancedIdx++;
+ }
+
+ return { false, advancedIdx, {} };
+ }
+
+ public:
+ void construct(const FiniteStateAutomation& automata) {
+ const auto range = automata.getAlphabetRange();
+ mSymbolRange = { tAlphabetType(range.mBegin), tAlphabetType(range.mEnd) };
+
+ auto range_len = ualni(mSymbolRange.mEnd - mSymbolRange.mBegin);
+ auto sizeX = range_len ? range_len : 1;
+ auto sizeY = (ualni) (automata.numStates());
+
+ mTable.reserve({ sizeX, sizeY });
+ mTable.assign(automata.numStates());
+ mStates.reserve(sizeY);
+
+ ualni idx = 0;
+ for (auto state : *automata.getStates()) {
+ mStates[idx] = { state->isAccepting(), state->getStateVal() };
+ idx++;
+ }
+
+ idx = 0;
+ for (auto state : *automata.getStates()) {
+ if (&state.data() == automata.getStartState()) {
+ mStartState = mCurrentState = idx;
+ }
+ idx++;
+ }
+
+ ualni stateIdx = 0;
+ for (auto state : *automata.getStates()) {
+ for (auto transition : *state->getTransitions()) {
+ ualni stateIdx2 = 0;
+ for (auto state2 : *automata.getStates()) {
+ if (transition->getState() == &state2.data()) break;
+ stateIdx2++;
+ }
+ auto const code = transition->getSymbol();
+ mTable.set({ (ualni) (code - mSymbolRange.mBegin), (ualni) stateIdx }, stateIdx2);
+ }
+ stateIdx++;
+ }
+ }
+
+ private:
+ Buffer2D mTable;
+ Buffer> mStates;
+
+ ualni mCurrentState = 0;
+ ualni mStartState = 0;
+
+ Range mSymbolRange = { 0, 0 };
+ };
+}
diff --git a/.wip/Language/public/RegularCompiler.hpp b/.wip/Language/public/RegularCompiler.hpp
new file mode 100644
index 0000000..a27f464
--- /dev/null
+++ b/.wip/Language/public/RegularCompiler.hpp
@@ -0,0 +1,208 @@
+
+#pragma once
+
+#include "Grammar.hpp"
+#include "Automata.hpp"
+
+namespace tp {
+
+ template
+ class RegularCompiler {
+
+ typedef FiniteStateAutomation Graph;
+ typedef typename Graph::State Vertex;
+ typedef RegularGrammar Grammar;
+
+ struct Node {
+ Vertex* left = nullptr;
+ Vertex* right = nullptr;
+ };
+
+ private:
+ Graph* mGraph = nullptr;
+
+ public:
+ struct CompileError {
+ uhalni mRuleIndex = 0;
+ tStateType mRuleState;
+ const char* description = nullptr;
+ [[nodiscard]] bool isError() const { return description; }
+ };
+
+ CompileError mError;
+
+ void compile(Graph& graph, const tAlphabetType* regex, tStateType state) {
+ mGraph = &graph;
+ compileUtil(regex, state);
+ }
+
+ void compile(Graph& aGraph, const Grammar& grammar) {
+ mGraph = &aGraph;
+
+ auto left = addVertex();
+ auto right = addVertex();
+
+ halni idx = 0;
+ for (auto rule : grammar.mRules) {
+
+ auto node = compileUtil(rule.data().first, rule.data().second);
+
+ if (!(node.left && node.right)) {
+ mError.mRuleIndex = idx;
+ return;
+ }
+
+ transitionAny(left, node.left);
+ transitionAny(node.right, right);
+
+ idx++;
+ }
+
+ mGraph->setStartState(left);
+ }
+
+ private:
+ Node compileUtil(const Grammar::Node* astNode, tStateType state) {
+
+ auto node = compileNode(astNode, nullptr, nullptr);
+
+ node.right->setValue(state);
+ node.right->setAcceptance(true);
+
+ mGraph->setStartState(node.left);
+
+ return node;
+ }
+
+ Node compileVal(Grammar::ValueNode* val, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+ transitionVal(left, right, val->mVal);
+ return { left, right };
+ }
+
+ Node compileAlternation(const Grammar::AlternationNode* alt, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto first_node = compileNode(alt->mFirst, aLeft, aRight);
+ auto second_node = compileNode(alt->mSecond);
+ transitionAny(first_node.left, second_node.left);
+ transitionAny(second_node.right, first_node.right);
+ return first_node;
+ }
+
+ Node compileAny(const Grammar::AnyNode*, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+ transitionAny(left, right, true);
+ return { left, right };
+ }
+
+ Node compileRepeat(const Grammar::RepetitionNode* repeat, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ if (repeat->mPlus) {
+ auto middle = addVertex();
+
+ auto left_node = compileNode(repeat->mNode, aLeft, middle);
+
+ auto right_node = compileNode(repeat->mNode, middle, aRight);
+ transitionAny(right_node.right, right_node.left);
+ transitionAny(right_node.left, right_node.right);
+
+ return { left_node.left, right_node.right };
+ } else {
+ auto node = compileNode(repeat->mNode, aLeft, aRight);
+ transitionAny(node.right, node.left);
+ transitionAny(node.left, node.right);
+ return node;
+ }
+ }
+
+ Node compileIf(const Grammar::IfNode* ifNode, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto node = compileNode(ifNode->mNode, aLeft, aRight);
+ transitionAny(node.left, node.right);
+ return node;
+ }
+
+ Node compileClass(const Grammar::ClassNode* node, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ auto left = aLeft ? aLeft : addVertex();
+ auto right = aRight ? aRight : addVertex();
+
+ if (node->mRanges.size() == 1) {
+ auto const& range = node->mRanges.first();
+ transitionRange(left, right, { ualni(range.mBegin), ualni(range.mEnd) }, node->mExclude);
+ return { left, right };
+ }
+
+ for (auto range : node->mRanges) {
+ auto middle = addVertex();
+ transitionRange(left, middle, { ualni(range->mBegin), ualni(range->mEnd) }, node->mExclude);
+ transitionAny(middle, right);
+ }
+ return { left, right };
+ }
+
+ Node compileCompound(const Grammar::CompoundNode* compound, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ Vertex* left = nullptr;
+ Vertex* rigth = nullptr;
+
+ ualni idx = 0;
+ for (auto child : compound->mSequence) {
+ auto pass_left = idx == 0 ? aLeft : rigth;
+ auto pass_right = idx == compound->mSequence.size() - 1 ? aRight : nullptr;
+ auto node = compileNode(child.data(), pass_left, pass_right);
+ if (!left) left = node.left;
+ rigth = node.right;
+ idx++;
+ }
+
+ return { left, rigth };
+ }
+
+ Node compileNode(const Grammar::Node* node, Vertex* aLeft = nullptr, Vertex* aRight = nullptr) {
+ switch (node->mType) {
+ case Grammar::Node::CLASS: return compileClass((typename Grammar::ClassNode*) node, aLeft, aRight);
+ case Grammar::Node::COMPOUND: return compileCompound((typename Grammar::CompoundNode*) node, aLeft, aRight);
+ case Grammar::Node::IF: return compileIf((typename Grammar::IfNode*) node, aLeft, aRight);
+ case Grammar::Node::REPEAT: return compileRepeat((typename Grammar::RepetitionNode*) node, aLeft, aRight);
+ case Grammar::Node::ANY: return compileAny((typename Grammar::AnyNode*) node, aLeft, aRight);
+ case Grammar::Node::OR: return compileAlternation((typename Grammar::AlternationNode*) node, aLeft, aRight);
+ case Grammar::Node::VAL: return compileVal((typename Grammar::ValueNode*) node, aLeft, aRight);
+ case Grammar::Node::NONE: break;
+ }
+ ASSERT(0)
+ return {};
+ }
+
+ void transitionAny(Vertex* from, Vertex* to, bool consumes = false) {
+ for (auto symbol : Range(tMinSymbol, tMaxSymbol)) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ void transitionVal(Vertex* from, Vertex* to, tAlphabetType val) { mGraph->addTransition(from, to, val); }
+
+ void transitionRange(Vertex* from, Vertex* to, Range range, bool exclude) {
+ if (exclude) {
+ Range first = { tMinSymbol, range.mBegin - 1 };
+ Range second = { range.mEnd + 1, tMaxSymbol };
+
+ if (first.valid()) {
+ for (auto symbol : first) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ if (second.valid()) {
+ for (auto symbol : second) {
+ transitionVal(from, to, symbol);
+ }
+ }
+
+ } else {
+ for (auto symbol : range) {
+ transitionVal(from, to, symbol);
+ }
+ }
+ }
+
+ Vertex* addVertex() { return mGraph->addState(tInTransition, false); }
+ };
+}
diff --git a/.wip/Language/public/SimpleParser.hpp b/.wip/Language/public/SimpleParser.hpp
new file mode 100644
index 0000000..ad1dc6e
--- /dev/null
+++ b/.wip/Language/public/SimpleParser.hpp
@@ -0,0 +1,69 @@
+#pragma once
+
+#include "Parser.hpp"
+
+namespace tp {
+
+ // Gives ability to express grammar in the Unified Format as sentence
+ template
+ class SimpleParser {
+ enum UGTokens : alni { InTransition = -1, TestSeq };
+
+ typedef Parser UGParser;
+ typedef Parser UserParser;
+
+ public:
+ SimpleParser() {
+ // Grammar for unified grammar format sentence that tables compiled from
+
+ // Define Context-Free grammar
+ ContextFreeGrammar contextFreeGrammar;
+ {
+ // use existing CF grammar interface
+ contextFreeGrammar.addRule("a", { ContextFreeGrammar::Arg("") });
+ contextFreeGrammar.setStart("a");
+ }
+
+ // Define Regular grammar
+ RegularGrammar regularGrammar;
+ {
+ // this is basically ast from existing tokenizer
+ regularGrammar.addRule(regularGrammar.seq({ regularGrammar.val('a'), regularGrammar.val('b') }), TestSeq);
+ }
+
+ Map terminalsMap;
+ terminalsMap.put("TestSeq", TestSeq);
+
+ mUnifiedGrammarParser.compileTables(contextFreeGrammar, regularGrammar, terminalsMap);
+ }
+
+ public:
+ void compileTables(const tAlphabetType* grammar, ualni grammarLength) {
+ mUnifiedGrammarParser.parse(grammar, grammarLength);
+
+ // compile each ast into RegularGrammar and ContextFree Grammar api instructions
+ ContextFreeGrammar userContextFreeGrammar;
+ RegularGrammar userRegularGrammar;
+
+ // ...
+ // split ast into RE and CF part
+ // generate and execute grammar api commands
+ // use existing tokenizer code to create RE transition matrix
+ // ...
+ // compile tables from user grammar
+
+ Map terminalsMap;
+ terminalsMap.put("TestSeq", 0);
+
+ mUserParser.compileTables(userContextFreeGrammar, userRegularGrammar, terminalsMap);
+ }
+
+ UserParser::ParseResult parse(const tAlphabetType* grammar, ualni grammarLength) {
+ return mUserParser.parse(grammar, grammarLength);
+ }
+
+ private:
+ UGParser mUnifiedGrammarParser;
+ UserParser mUserParser;
+ };
+}
diff --git a/.wip/Language/tests/Test.hpp b/.wip/Language/tests/Test.hpp
new file mode 100644
index 0000000..f0b25f1
--- /dev/null
+++ b/.wip/Language/tests/Test.hpp
@@ -0,0 +1,43 @@
+#pragma once
+
+// #include "SimpleParser.hpp"
+
+const char* gGrammar = R"(
+# Grammar in the CF-RE United Format (Defined by language module)
+
+Rules : {
+ ScopeList : ScopeList Scope | Scope ;
+ Scope : \ScopeBegin StatementList \ScopeEnd;
+ StatementList : StatementList Statement \StatementEnd;
+ StatementList : Statement \StatementEnd;
+ Statement : \StatementBody;
+}
+
+Terminals : {
+ Space : " " | "\t" | "\n" | "\r";
+ ScopeBegin : "{";
+ ScopeEnd : "}";
+ StatementEnd : ";";
+ StatementBody : "a" | "b";
+}
+
+Start : Scope;
+Ignore : Space;
+)";
+
+const char* gSentence = R"(
+{}
+
+{ }
+
+{
+a;
+ a ; a ;
+
+ }
+
+ {
+a;
+ a;
+}
+)";
\ No newline at end of file
diff --git a/.wip/Language/tests/Tests.cpp b/.wip/Language/tests/Tests.cpp
new file mode 100644
index 0000000..ce4beb2
--- /dev/null
+++ b/.wip/Language/tests/Tests.cpp
@@ -0,0 +1,45 @@
+
+#include "Test.hpp"
+
+/*
+using namespace tp;
+
+void testAutomation() {
+
+ FiniteStateAutomation automata;
+
+ auto start = automata.addState(0, false);
+ auto end = automata.addState(1, true);
+
+ automata.addTransition(start, end, 'a');
+
+ automata.setStartState(start);
+
+ automata.makeDeterministic();
+}
+
+void test() {
+ auto parser = SimpleParser();
+
+ parser.compileTables(gGrammar, String::Logic::calcLength(gGrammar));
+ auto result = parser.parse(gSentence, String::Logic::calcLength(gSentence));
+}
+
+int main() {
+
+ tp::ModuleManifest* deps[] = { &tp::gModuleLanguage, nullptr };
+ tp::ModuleManifest testModule("Test", nullptr, nullptr, deps);
+
+ if (!testModule.initialize()) {
+ return 1;
+ }
+
+ testAutomation();
+ test();
+
+ testModule.deinitialize();
+}
+
+*/
+
+int main() { return 0; }
\ No newline at end of file
diff --git a/3DEditor/CMakeLists.txt b/3DEditor/CMakeLists.txt
new file mode 100644
index 0000000..e11e387
--- /dev/null
+++ b/3DEditor/CMakeLists.txt
@@ -0,0 +1,22 @@
+project(3DEditor)
+
+### ---------------------- Externals --------------------- ###
+set(BINDINGS_INCLUDE ../Externals/glfw/include ../Externals)
+set(BINDINGS_LIBS glfw Imgui)
+
+### ---------------------- Static Library --------------------- ###
+file(GLOB SOURCES "./private/*.cpp" "./private/*/*.cpp")
+file(GLOB HEADERS "./public/*.hpp" "./public/*/*.hpp")
+
+add_library(${PROJECT_NAME} STATIC ${SOURCES} ${HEADERS})
+
+target_include_directories(${PROJECT_NAME} PUBLIC ./public/ ${BINDINGS_INCLUDE} ./ext/)
+target_link_libraries(${PROJECT_NAME} PUBLIC Graphics Connection Widgets Math RasterRender)
+target_link_libraries(${PROJECT_NAME} PUBLIC ${BINDINGS_LIBS})
+
+### -------------------------- Applications -------------------------- ###
+add_executable(3DEditorApp ./applications/Entry.cpp ./applications/SceneLoad.cpp)
+target_link_libraries(3DEditorApp ${PROJECT_NAME} Lua ImageIO)
+
+file(COPY "rsc" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
+file(COPY "rsc/Font.ttf" DESTINATION "${CMAKE_BINARY_DIR}/${PROJECT_NAME}/")
diff --git a/3DEditor/applications/Entry.cpp b/3DEditor/applications/Entry.cpp
new file mode 100644
index 0000000..fb1e37e
--- /dev/null
+++ b/3DEditor/applications/Entry.cpp
@@ -0,0 +1,42 @@
+
+#include "EditorWidget.hpp"
+
+#include "GraphicApplication.hpp"
+
+using namespace tp;
+
+bool loadMeshes(tp::Scene& scene, const std::string& objetsPath);
+
+class EditorGUI : public Application {
+public:
+ EditorGUI() {
+ Vec2F renderResolution = { 1000, 1000 };
+ auto canvas = this->mGraphics->getCanvas();
+ // mGui = new EditorWidget(canvas, &geometry, renderResolution);
+
+ mGui = new ShortcutsTest();
+
+ loadMeshes(geometry, "rsc/scene.obj");
+
+ geometry.mCamera.lookAtPoint({ 0, 0, 0 }, { 3, 3, 2 }, { 0, 0, 1 });
+ }
+
+ ~EditorGUI() override { delete mGui; }
+
+ void processFrame(EventHandler* eventHandler) override {
+
+ auto rec = RectF({ 0, 0 }, mWindow->getSize());
+ mGui->proc(*eventHandler, rec, rec);
+ }
+
+ void drawFrame(Canvas* canvas) override { mGui->draw(*canvas); }
+
+private:
+ Scene geometry;
+ ShortcutsTest* mGui;
+};
+
+int main() {
+ EditorGUI gui;
+ gui.run();
+}
diff --git a/3DEditor/applications/SceneLoad.cpp b/3DEditor/applications/SceneLoad.cpp
new file mode 100644
index 0000000..bc03e0c
--- /dev/null
+++ b/3DEditor/applications/SceneLoad.cpp
@@ -0,0 +1,41 @@
+#include "Scene.hpp"
+
+#include "obj/OBJ_Loader.h"
+#include
+
+bool loadMeshes(tp::Scene& scene, const std::string& objetsPath) {
+ using namespace tp;
+
+ objl::Loader Loader;
+
+ if (!Loader.LoadFile(objetsPath.c_str())) {
+ std::cout << "Failed to Load File. May have failed to find it or it was not an .obj file.\n";
+ return false;
+ }
+
+ for (auto& curMesh : Loader.LoadedMeshes) {
+ scene.mObjects.append(Object());
+
+ auto object = &scene.mObjects.last();
+
+ for (auto& vertex : curMesh.Vertices) {
+ // printf("{ %f, %f, %f }, \n", vertex.Position.X, vertex.Position.Y, vertex.Position.Z);
+ object->mTopology.Points.append(Vec3F{ vertex.Position.X, vertex.Position.Y, vertex.Position.Z });
+ object->mTopology.Normals.append(Vec3F{ vertex.Normal.X, vertex.Normal.Y, vertex.Normal.Z });
+ }
+
+ for (int j = 0; j < curMesh.Indices.size(); j += 3) {
+ uint idx1 = (int) curMesh.Indices[j];
+ uint idx2 = (int) curMesh.Indices[j + 1];
+ uint idx3 = (int) curMesh.Indices[j + 2];
+ // printf("{ %i, %i, %i },\n", idx1, idx2, idx3);
+ object->mTopology.Indexes.append({ idx1, idx2, idx3 });
+ }
+
+ if (object->mTopology.Normals.size() != object->mTopology.Points.size()) {
+ printf("Logic error loading normals\n");
+ }
+ }
+
+ return scene.mObjects.size();
+}
diff --git a/3DEditor/private/Render.cpp b/3DEditor/private/Render.cpp
new file mode 100644
index 0000000..ec98be5
--- /dev/null
+++ b/3DEditor/private/Render.cpp
@@ -0,0 +1,110 @@
+#include "Render.hpp"
+
+#include "GraphicsApi.hpp"
+
+using namespace tp;
+
+class ObjectBuffers {
+public:
+ ObjectBuffers(Object* object) {
+ mObject = (object);
+
+ auto& buff = mObject->mTopology.Points;
+ auto& indices = mObject->mTopology.Indexes;
+
+ glGenVertexArrays(1, &VAO);
+ glGenBuffers(1, &VBO);
+ glGenBuffers(1, &EBO);
+
+ glBindVertexArray(VAO);
+
+ glBindBuffer(GL_ARRAY_BUFFER, VBO);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(Vec3F) * buff.size(), buff.getBuff(), GL_STATIC_DRAW);
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(Vec3) * indices.size(), indices.getBuff(), GL_STATIC_DRAW);
+
+ glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), nullptr);
+ glEnableVertexAttribArray(0);
+
+ // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glBindVertexArray(0);
+ }
+
+ Object* mObject = nullptr;
+
+ GLuint VAO = 0;
+ GLuint VBO = 0;
+ GLuint EBO = 0;
+
+ void drawCall() {
+ auto& indices = mObject->mTopology.Indexes;
+
+ glBindVertexArray(VAO);
+
+ // glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
+ glDrawElements(GL_TRIANGLES, indices.size() * 3, GL_UNSIGNED_INT, nullptr);
+
+ // glDrawArrays(GL_TRIANGLES, 0, sizeof(buffer) / (2 * sizeof(float)));
+
+ // glBindVertexArray(0);
+ }
+
+ ~ObjectBuffers() {
+ glDeleteBuffers(1, &VBO);
+ glDeleteVertexArrays(1, &VAO);
+ }
+};
+
+Render::Render(Vec2F renderResolution) :
+ mRenderBuffer(renderResolution) {
+
+ mDefaultShader.load("rsc/shaders/default.vert", nullptr, "rsc/shaders/default.frag", true);
+}
+
+Render::~Render() {}
+
+uint4 Render::getRenderBuffer() { return mRenderBuffer.texId(); }
+
+Vec2F Render::getBufferSize() { return mRenderBuffer.getSize(); }
+
+void Render::render(const Scene& geometry, Vec2F size) {
+
+ for (auto object : geometry.mObjects) {
+ if (!object->mBuffers) {
+ object->mBuffers = std::make_shared(&object.data());
+ }
+ }
+
+ mRenderBuffer.mClearCol = { 0.0f, 0.0f, 0.0f, 0.f };
+
+ mRenderBuffer.beginDraw();
+ mRenderBuffer.clear();
+
+ mDefaultShader.bind();
+
+ Mat4F cameraMat = geometry.mCamera.calculateTransformationMatrix();
+
+ glEnable(GL_DEPTH_TEST);
+
+ for (auto object : geometry.mObjects) {
+
+ static auto origin = (GLint) mDefaultShader.getu("Origin");
+ static auto basis = (GLint) mDefaultShader.getu("Basis");
+ static auto camera = (GLint) mDefaultShader.getu("Camera");
+
+ Mat4F basisMat;
+ Vec4F originPoint;
+
+ glUniform4fv(origin, 1, &originPoint[0]);
+ glUniformMatrix4fv(basis, 1, false, &basisMat[0][0]);
+ glUniformMatrix4fv(camera, 1, true, &cameraMat[0][0]);
+
+ object->mBuffers->drawCall();
+ }
+
+ mDefaultShader.unbind();
+
+ mRenderBuffer.endDraw();
+}
diff --git a/3DEditor/public/EditorWidget.hpp b/3DEditor/public/EditorWidget.hpp
new file mode 100644
index 0000000..cabd207
--- /dev/null
+++ b/3DEditor/public/EditorWidget.hpp
@@ -0,0 +1,104 @@
+#include "Widgets.hpp"
+#include "Render.hpp"
+
+namespace tp {
+
+ template
+ class ShortcutsTest : public Widget {
+ public:
+ ShortcutsTest() { this->createConfig("ShortcutsTest"); }
+
+ void action(const Events&) {
+ //
+ }
+
+ void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
+ this->mArea = aArea;
+ this->mVisible = areaParent.isOverlap(aArea);
+ if (!this->mVisible) return;
+ }
+
+ void draw(Canvas& canvas) override {
+ if (!this->mVisible) return;
+ canvas.rect(this->mArea, this->getColor("Base"));
+ }
+
+ void populateConfig() override {
+ this->addColor("Base", "Base");
+
+ this->addOperator("OperatorName", { this, [](void* self, const Events& events) {
+ ((ShortcutsTest*) self)->action(events);
+ } });
+
+ this->getShortcuts("OperatorName").append({ { "Alt", "Hold" }, { "Mouse1", "Hold" } });
+ this->getShortcuts("OperatorName").append({ { "Alt", "Hold" }, { "Mouse1", "Hold" } });
+ }
+ };
+
+ template
+ class ViewportWidget : public Widget {
+ public:
+ explicit ViewportWidget(Canvas* canvas, Scene* geometry, Vec2F renderResolution) :
+ mRender(renderResolution) {
+ this->createConfig("ViewportWidget");
+
+ mImage = canvas->createImageFromTextId(mRender.getRenderBuffer(), mRender.getBufferSize());
+ mGeometry = geometry;
+ mCanvas = canvas;
+ }
+
+ ~ViewportWidget() { mCanvas->deleteImageHandle(mImage); }
+
+ void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
+ this->mArea = aArea;
+ this->mVisible = areaParent.isOverlap(aArea);
+ if (!this->mVisible) return;
+
+ mGeometry->mCamera.rotate(0.01f, 0.0);
+ }
+
+ void draw(Canvas& canvas) override {
+ if (!this->mVisible) return;
+
+ mRender.render(*mGeometry, this->mArea.size);
+ canvas.drawImage(this->mArea, &mImage, PI);
+ }
+
+ public:
+ Render mRender;
+ Scene* mGeometry = nullptr;
+ Canvas* mCanvas = nullptr;
+ Canvas::ImageHandle mImage;
+ };
+
+ template
+ class EditorWidget : public Widget {
+ public:
+ EditorWidget(Canvas* canvas, Scene* geometry, Vec2F renderResolution) :
+ mViewport(canvas, geometry, renderResolution) {
+ this->createConfig("EditorWidget");
+ this->addColor("Base", "Base");
+ }
+
+ void proc(const Events& events, const RectF& areaParent, const RectF& aArea) override {
+ this->mArea = aArea;
+ this->mVisible = areaParent.isOverlap(aArea);
+ if (!this->mVisible) return;
+
+ mSplitView.proc(events, aArea, aArea);
+ mViewport.proc(events, aArea, mSplitView.getFirst());
+ }
+
+ void draw(Canvas& canvas) override {
+ if (!this->mVisible) return;
+
+ canvas.rect(this->mArea, this->getColor("Base"));
+ mSplitView.draw(canvas);
+ mViewport.draw(canvas);
+ }
+
+ public:
+ ViewportWidget mViewport;
+ SplitView mSplitView;
+ };
+}
\ No newline at end of file
diff --git a/3DEditor/public/Render.hpp b/3DEditor/public/Render.hpp
new file mode 100644
index 0000000..818ffe8
--- /dev/null
+++ b/3DEditor/public/Render.hpp
@@ -0,0 +1,23 @@
+#pragma once
+
+#include "Scene.hpp"
+#include "Rect.hpp"
+
+#include "FrameBuffer.hpp"
+#include "Shader.hpp"
+
+namespace tp {
+ class Render {
+ public:
+ explicit Render(Vec2F renderResolution);
+ ~Render();
+
+ void render(const Scene& geometry, Vec2F size);
+ uint4 getRenderBuffer();
+ Vec2F getBufferSize();
+
+ private:
+ RenderBuffer mRenderBuffer;
+ RenderShader mDefaultShader;
+ };
+}
\ No newline at end of file
diff --git a/3DEditor/public/Scene.hpp b/3DEditor/public/Scene.hpp
new file mode 100644
index 0000000..9b995ba
--- /dev/null
+++ b/3DEditor/public/Scene.hpp
@@ -0,0 +1,34 @@
+#pragma once
+
+#include "Topology.hpp"
+#include
+
+class ObjectBuffers;
+
+namespace tp {
+
+ class Object {
+ public:
+ Object() = default;
+
+ public:
+ Topology mTopology;
+ std::shared_ptr mBuffers;
+ };
+
+ struct PointLight {
+ Vec3F pos;
+ halnf fallOut = 1.f;
+ halnf intensity = 1.f;
+ };
+
+ class Scene {
+ public:
+ Scene() = default;
+
+ public:
+ Buffer