├── .dockerignore ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── bench.cpp ├── docs ├── logo.png └── typerunner-debugger.png ├── libs ├── .gitkeep ├── imgui-texteditor-fork │ ├── .gitignore │ ├── CONTRIBUTING │ ├── LICENSE │ ├── README.md │ ├── TextEditor.cpp │ └── TextEditor.h └── magic_enum │ └── magic_enum.hpp ├── main.cpp ├── package-lock.json ├── package.json ├── src ├── CMakeLists.txt ├── checker │ ├── check2.h │ ├── checks.h │ ├── compiler.h │ ├── debug.h │ ├── instructions.h │ ├── module2.h │ ├── pool_array.h │ ├── pool_single.h │ ├── types.h │ ├── types2.h │ ├── utils.h │ ├── vm.cpp │ ├── vm.h │ ├── vm2.cpp │ ├── vm2.h │ ├── vm2_utils.h │ └── vm3.h ├── core.cpp ├── core.h ├── diagnostic_messages.h ├── enum.h ├── factory.cpp ├── factory.h ├── fs.h ├── gui │ ├── CMakeLists.txt │ ├── app.h │ ├── debugger_main.cpp │ └── graph │ │ ├── LICENSE │ │ ├── crossing.hpp │ │ ├── cycle.hpp │ │ ├── graph.hpp │ │ ├── interface.hpp │ │ ├── layering.hpp │ │ ├── layout.hpp │ │ ├── positioning.hpp │ │ ├── report.hpp │ │ ├── router.hpp │ │ ├── subgraph.hpp │ │ ├── types.hpp │ │ ├── utils.hpp │ │ └── vec2.hpp ├── hash.h ├── node_test.cpp ├── node_test.h ├── parenthesizer.cpp ├── parenthesizer.h ├── parser2.cpp ├── parser2.h ├── path.cpp ├── path.h ├── pool_band.h ├── scanner.cpp ├── scanner.h ├── string.h ├── syntax_cursor.cpp ├── syntax_cursor.h ├── tests │ ├── CMakeLists.txt │ ├── test_bench.cpp │ ├── test_checker.cpp │ ├── test_core.cpp │ ├── test_hash.cpp │ ├── test_mem.cpp │ ├── test_newtypes.cpp │ ├── test_parser.cpp │ ├── test_pool_array.cpp │ ├── test_pool_band.cpp │ ├── test_pool_single.cpp │ ├── test_scanner.cpp │ ├── test_vm2.cpp │ ├── test_vm2_class.cpp │ ├── test_vm2_closure.cpp │ ├── test_vm2_function.cpp │ ├── test_vm2_union.cpp │ └── utils.h ├── types.cpp ├── types.h ├── utf.cpp ├── utf.h ├── utilities.cpp └── utilities.h └── tests ├── basic1.ts ├── basicError1.ts ├── bench.ts ├── big1.ts ├── big2.ts ├── function1.ts ├── generic2.ts ├── objectLiterals1.ts └── tutorial1.ts /.dockerignore: -------------------------------------------------------------------------------- 1 | .idea 2 | cmake-* 3 | build* 4 | Dockerfile -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | cmake-build-debug 2 | .idea 3 | *.tsbytecode -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libs/tracy"] 2 | path = libs/tracy 3 | url = git@github.com:wolfpld/tracy.git 4 | [submodule "libs/fmt"] 5 | path = libs/fmt 6 | url = git@github.com:fmtlib/fmt.git 7 | [submodule "libs/imgui"] 8 | path = libs/imgui 9 | url = git@github.com:ocornut/imgui.git 10 | [submodule "libs/asmjit"] 11 | path = libs/asmjit 12 | url = git@github.com:asmjit/asmjit.git 13 | [submodule "libs/doctest"] 14 | path = libs/doctest 15 | url = git@github.com:doctest/doctest.git 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | project(typescript) 3 | 4 | set(CMAKE_CXX_STANDARD 20) 5 | 6 | set(CMAKE_CXX_FLAGS "-Wno-error=enum-constexpr-conversion -Wno-unused-variable -Wno-switch -Wno-trigraphs") 7 | 8 | if(CMAKE_BUILD_TYPE STREQUAL "Release") 9 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O3 -ffast-math") 10 | endif() 11 | 12 | include_directories(libs/tracy/) 13 | include_directories(libs/fmt/include/) 14 | 15 | list(APPEND CMAKE_MODULE_PATH libs) 16 | 17 | add_subdirectory(libs/doctest) 18 | add_subdirectory(libs/tracy) 19 | add_subdirectory(libs/fmt) 20 | 21 | set(ASMJIT_STATIC TRUE) 22 | #add_subdirectory(libs/asmjit) 23 | 24 | # enable for profiling 25 | #add_definitions(-DTRACY_ENABLE) 26 | #link_libraries(Tracy::TracyClient) 27 | 28 | #include_directories(libs/asmjit/src) 29 | include_directories(libs/magic_enum) 30 | 31 | add_subdirectory(src) 32 | 33 | add_executable(typescript_main main.cpp) 34 | target_link_libraries(typescript_main typescript) 35 | 36 | add_executable(bench bench.cpp) 37 | target_link_libraries(bench typescript) -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bullseye 2 | 3 | RUN apt-get update && apt-get -y install gnupg wget 4 | RUN wget -O - https://apt.llvm.org/llvm-snapshot.gpg.key|apt-key add - 5 | RUN echo "deb http://apt.llvm.org/bullseye/ llvm-toolchain-bullseye-14 main" > /etc/apt/sources.list.d/clang.list 6 | 7 | # Install dependencies 8 | RUN apt-get -qq update && \ 9 | apt-get install -qqy --no-install-recommends \ 10 | clang-14 lldb-14 lld-14 ca-certificates \ 11 | autoconf automake cmake dpkg-dev file git make patch \ 12 | libc-dev libc++-dev libgcc-10-dev libstdc++-10-dev \ 13 | dirmngr gnupg2 lbzip2 wget xz-utils libtinfo5 && \ 14 | rm -rf /var/lib/apt/lists/* 15 | 16 | ADD . /typerunner 17 | WORKDIR /typerunner 18 | RUN mkdir build 19 | RUN cd build && cmake -DCMAKE_CXX_COMPILER=clang++-14 -DCMAKE_C_COMPILER=clang-14 -DCMAKE_BUILD_TYPE=Release .. 20 | RUN cd build && make bench typescript_main -j 8 21 | RUN ./build/bench tests/objectLiterals1.ts 22 | RUN ./build/typescript_main tests/objectLiterals1.ts -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Marc J. Schmidt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | xcode: 3 | cd xcode && cmake -D CMAKE_C_COMPILER="`xcrun -find cc`" -D CMAKE_CXX_COMPILER="`xcrun -find c++`" .. -GXcode -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # Todo 2 | 3 | | Type | Parser | Compiler | VM | 4 | |------------------|--------|----------|-----| 5 | | String | [X] | [X] | [X] | 6 | | Number | [X] | [X] | [X] | 7 | | Boolean | [X] | [X] | [X] | 8 | | BigInt | [X] | [X] | [X] | 9 | | String Literal | [X] | [X] | [X] | 10 | | Number Literal | [X] | [X] | [X] | 11 | | BigInt Literal | [X] | [X] | [X] | 12 | | Bool Literal | [X] | [X] | [X] | 13 | | Interfaces | [X] | [X] | 50% | 14 | | Object Literal | [X] | [X] | [X] | 15 | | Classes | [X] | 20% | 5% | 16 | | Union | [X] | [X] | [X] | 17 | | Intersection | [X] | [] | [] | 18 | | Functions | [X] | 50% | 30% | 19 | | Template Literal | [X] | 50% | 20% | 20 | 21 | - [] Optimiser Parser 22 | - [] Memory pool 23 | - [] Inference 24 | - [] Binary Expressions 25 | - [] Binary Expressions 26 | - [] Type narrowing 27 | - [] If-Else 28 | - [] While 29 | - [] Early Return 30 | - [] Type Guards 31 | - [] Type Assertions 32 | - [] Interface merging 33 | - [] Primitives (lazy) 34 | - [] Globals 35 | - [] Modules 36 | - [] Namespaces 37 | 38 | -------------------------------------------------------------------------------- /bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "./src/core.h" 6 | #include "./src/fs.h" 7 | #include "./src/parser2.h" 8 | #include "./src/checker/vm2.h" 9 | #include "./src/checker/module2.h" 10 | #include "./src/checker/debug.h" 11 | #include "./src/checker/compiler.h" 12 | 13 | using namespace tr; 14 | 15 | void compileAndRun(const string &code, const string &fileName) { 16 | ZoneScoped; 17 | auto iterations = 1000; 18 | auto cold = benchRun(iterations, [&] { 19 | checker::Compiler compiler; 20 | Parser parser; 21 | auto result = parser.parseSourceFile(fileName, code, types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 22 | auto program = compiler.compileSourceFile(result); 23 | auto bin = program.build(); 24 | auto module = make_shared(bin, fileName, code); 25 | vm2::run(module); 26 | }); 27 | 28 | checker::Compiler compiler; 29 | Parser parser; 30 | auto result = parser.parseSourceFile(fileName, code, types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 31 | auto program = compiler.compileSourceFile(result); 32 | auto bin = program.build(); 33 | auto module = make_shared(bin, fileName, code); 34 | auto warm = benchRun(iterations, [&] { 35 | module->clear(); 36 | vm2::run(module); 37 | }); 38 | 39 | std::cout << fmt::format("typerunner: {} iterations (it): cold {:.9f}ms/it, warm {:.9f}ms/it\n", iterations, cold.count() / iterations, warm.count() / iterations); 40 | } 41 | 42 | int main(int argc, char *argv[]) { 43 | ZoneScoped; 44 | std::string file; 45 | auto cwd = std::filesystem::current_path(); 46 | 47 | if (argc > 1) { 48 | file = cwd.string() + "/" + argv[1]; 49 | } else { 50 | file = cwd.string() + "/../tests/basic1.ts"; 51 | } 52 | 53 | if (!fileExists(file)) { 54 | std::cout << "File not found " << file << "\n"; 55 | return 4; 56 | } 57 | auto code = fileRead(file); 58 | auto relative = std::filesystem::relative(file, cwd); 59 | compileAndRun(code, relative.string()); 60 | return 0; 61 | } 62 | -------------------------------------------------------------------------------- /docs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcj/TypeRunner/21f9ce87a72fbfe011be9bfcf404f6e76bd74fe6/docs/logo.png -------------------------------------------------------------------------------- /docs/typerunner-debugger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcj/TypeRunner/21f9ce87a72fbfe011be9bfcf404f6e76bd74fe6/docs/typerunner-debugger.png -------------------------------------------------------------------------------- /libs/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marcj/TypeRunner/21f9ce87a72fbfe011be9bfcf404f6e76bd74fe6/libs/.gitkeep -------------------------------------------------------------------------------- /libs/imgui-texteditor-fork/.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | TextEditor.cpp.bak 34 | *.bak 35 | Save/TextEditor.h 36 | Save/TextEditor.cpp 37 | -------------------------------------------------------------------------------- /libs/imgui-texteditor-fork/CONTRIBUTING: -------------------------------------------------------------------------------- 1 | # Contributing 2 | Pull requests are welcome, feel free to contribute if you have implemented something which might be useful for the general audience of this little piece of software. Apparently, it became kind of a community project now. :) 3 | 4 | Whem contributing, please follow the following guidelines. I will keep it updated as we bump into something which worth doing better. 5 | - Try to follow the same coding and naming conventions you find in the source already. I know that everyone has its own preference/taste in coding, but please keep the source consistent in style. 6 | - Please submit to the 'dev' branch first for testing, and it will be merged to 'main' if it seems to work fine. I would like try keep 'master' in a good working condition, as more and more people are using it. 7 | - Please send your submissions in small, well defined requests, i. e. do not accumulate many unrelated changes in one large pull request. Keep your submissions as small as possible, it will make everyone's life easier. 8 | - Avoid using ImGui internal since it would make the source fragile against internal changes in ImGui. 9 | - Try to keep the perormance high within the render function. Try to avoid doing anything which leads to memory allocations (like using temporary std::string, std::vector variables), or complex algorithm. If you really have to, try to amortise it between frames. 10 | 11 | Thank you. :) 12 | -------------------------------------------------------------------------------- /libs/imgui-texteditor-fork/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 BalazsJako 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /libs/imgui-texteditor-fork/README.md: -------------------------------------------------------------------------------- 1 | # ImGuiColorTextEdit 2 | Syntax highlighting text editor for ImGui 3 | 4 | ![Screenshot](https://github.com/BalazsJako/ImGuiColorTextEdit/wiki/ImGuiTextEdit.png "Screenshot") 5 | 6 | Demo project: https://github.com/BalazsJako/ColorTextEditorDemo 7 | 8 | This started as my attempt to write a relatively simple widget which provides text editing functionality with syntax highlighting. Now there are other contributors who provide valuable additions. 9 | 10 | While it relies on Omar Cornut's https://github.com/ocornut/imgui, it does not follow the "pure" one widget - one function approach. Since the editor has to maintain a relatively complex and large internal state, it did not seem to be practical to try and enforce fully immediate mode. It stores its internal state in an object instance which is reused across frames. 11 | 12 | The code is (still) work in progress, please report if you find any issues. 13 | 14 | # Main features 15 | - approximates typical code editor look and feel (essential mouse/keyboard commands work - I mean, the commands _I_ normally use :)) 16 | - undo/redo 17 | - UTF-8 support 18 | - works with both fixed and variable-width fonts 19 | - extensible syntax highlighting for multiple languages 20 | - identifier declarations: a small piece of description can be associated with an identifier. The editor displays it in a tooltip when the mouse cursor is hovered over the identifier 21 | - error markers: the user can specify a list of error messages together the line of occurence, the editor will highligh the lines with red backround and display error message in a tooltip when the mouse cursor is hovered over the line 22 | - large files: there is no explicit limit set on file size or number of lines (below 2GB, performance is not affected when large files are loaded (except syntax coloring, see below) 23 | - color palette support: you can switch between different color palettes, or even define your own 24 | - whitespace indicators (TAB, space) 25 | 26 | # Known issues 27 | - syntax highligthing of most languages - except C/C++ - is based on std::regex, which is diasppointingly slow. Because of that, the highlighting process is amortized between multiple frames. C/C++ has a hand-written tokenizer which is much faster. 28 | 29 | Please post your screenshots if you find this little piece of software useful. :) 30 | 31 | # Contribute 32 | 33 | If you want to contribute, please refer to CONTRIBUTE file. 34 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "./src/core.h" 6 | #include "./src/fs.h" 7 | #include "./src/parser2.h" 8 | #include "./src/checker/vm2.h" 9 | #include "./src/checker/module2.h" 10 | #include "./src/checker/debug.h" 11 | #include "./src/checker/compiler.h" 12 | 13 | using namespace tr; 14 | 15 | void run(const string &bytecode, const string &code, const string &fileName) { 16 | ZoneScoped; 17 | auto module = std::make_shared(bytecode, fileName, code); 18 | bench(1, [&]{ 19 | vm2::run(module); 20 | module->printErrors(); 21 | }); 22 | } 23 | 24 | void compileAndRun(const string &code, const string &file, const string &fileName) { 25 | ZoneScoped; 26 | auto bytecodePath = file + ".tsb"; 27 | auto buffer = fileRead(file); 28 | checker::Compiler compiler; 29 | Parser parser; 30 | auto result = parser.parseSourceFile(file, buffer, types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 31 | auto program = compiler.compileSourceFile(result); 32 | auto bin = program.build(); 33 | fileWrite(bytecodePath, bin); 34 | std::filesystem::last_write_time(bytecodePath, std::filesystem::last_write_time(file)); 35 | checker::printBin(bin); 36 | auto module = make_shared(bin, fileName, code); 37 | vm2::run(module); 38 | module->printErrors(); 39 | } 40 | 41 | int main(int argc, char *argv[]) { 42 | ZoneScoped; 43 | std::string file; 44 | auto cwd = std::filesystem::current_path(); 45 | 46 | if (argc > 1) { 47 | file = cwd.string() + "/" + argv[1]; 48 | } else { 49 | file = cwd.string() + "/../tests/basic1.ts"; 50 | } 51 | 52 | if (!fileExists(file)) { 53 | std::cout << "File not found " << file << "\n"; 54 | return 4; 55 | } 56 | auto code = fileRead(file); 57 | auto bytecode = file + ".tsb"; 58 | auto relative = std::filesystem::relative(file, cwd); 59 | 60 | if (fileExists(bytecode) && std::filesystem::last_write_time(bytecode) == std::filesystem::last_write_time(file)) { 61 | run(fileRead(bytecode), code, relative.string()); 62 | } else { 63 | compileAndRun(code, file, relative.string()); 64 | } 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "typerunner", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.js", 6 | "files": [ 7 | "README.md" 8 | ], 9 | "directories": { 10 | "test": "tests" 11 | }, 12 | "scripts": { 13 | "bench": "ts-node tests/bench.ts" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/marcj/TypeRunner.git" 18 | }, 19 | "author": "", 20 | "bugs": { 21 | "url": "https://github.com/marcj/TypeRunner/issues" 22 | }, 23 | "homepage": "https://github.com/marcj/TypeRunner#readme", 24 | "devDependencies": { 25 | "ts-node": "^10.9.1", 26 | "typescript": "^4.7.4" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(typescript) 2 | 3 | #add_definitions(-DTRACY_ENABLE) 4 | 5 | #set(CMAKE_CXX_FLAGS "-Wall -Wextra -O3 -ffast-math -fsanitize=leak") 6 | #set(CMAKE_CXX_FLAGS "-Wall -Wextra -O2") 7 | 8 | add_subdirectory(tests) 9 | 10 | add_library(typescript utf.h utf.cpp core.h core.cpp utilities.h utilities.cpp node_test.h node_test.cpp 11 | parser2.h parser2.cpp types.h types.cpp path.h path.cpp 12 | factory.h factory.cpp parenthesizer.h parenthesizer.cpp scanner.h scanner.cpp 13 | checker/instructions.h checker/compiler.h checker/types.h checker/utils.h checker/checks.h checker/debug.h checker/vm2.cpp) 14 | # ${CMAKE_CURRENT_SOURCE_DIR}/../libs/tracy/TracyClient.cpp 15 | 16 | target_link_libraries(typescript fmt) 17 | #target_link_libraries(typescript asmjit::asmjit) 18 | 19 | add_subdirectory(gui) -------------------------------------------------------------------------------- /src/checker/debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "./instructions.h" 5 | #include "../core.h" 6 | #include "./utils.h" 7 | 8 | namespace tr::checker { 9 | using std::string_view; 10 | using tr::instructions::OP; 11 | 12 | struct PrintSubroutineOp { 13 | string text; 14 | unsigned int address; 15 | }; 16 | 17 | struct PrintSubroutine { 18 | string name; 19 | unsigned int address; 20 | vector operations; 21 | }; 22 | 23 | struct DebugSourceMapEntry { 24 | OP op; 25 | unsigned int bytecodePos; 26 | unsigned int sourcePos; 27 | unsigned int sourceEnd; 28 | }; 29 | 30 | struct DebugBinResult { 31 | vector operations; 32 | vector storages; 33 | vector subroutines; 34 | vector sourceMap; 35 | PrintSubroutine *activeSubroutine = nullptr; 36 | }; 37 | 38 | inline DebugBinResult parseBin(string_view bin, bool print = false) { 39 | const auto end = bin.size(); 40 | unsigned int storageEnd = 0; 41 | bool newSubRoutine = false; 42 | bool firstJump = false; 43 | bool newLine = false; 44 | DebugBinResult result; 45 | if (print) std::cout << fmt::format("Bin {} bytes: ", bin.size()); 46 | 47 | for (unsigned int i = 0; i < end; i++) { 48 | if (storageEnd) { 49 | while (i < storageEnd) { 50 | auto size = vm::readUint16(bin, i + 8); 51 | auto data = bin.substr(i + 8 + 2, size); 52 | if (print) std::cout << fmt::format("(Storage ({})\"{}\") ", size, data); 53 | result.storages.push_back(string(data)); 54 | i += 8 + 2 + size; 55 | } 56 | debug(""); 57 | storageEnd = 0; 58 | } 59 | 60 | if (newSubRoutine) { 61 | auto found = false; 62 | unsigned int j = 0; 63 | for (auto &&r: result.subroutines) { 64 | if (r.address == i) { 65 | if (print) std::cout << fmt::format("\n&{} {}(): ", j, r.name); 66 | result.activeSubroutine = &r; 67 | found = true; 68 | break; 69 | } 70 | j++; 71 | } 72 | if (!found) { 73 | if (print) std::cout << fmt::format("\nunknown!(): "); 74 | } 75 | newSubRoutine = false; 76 | } 77 | std::string params = ""; 78 | auto startI = i; 79 | auto op = (OP) bin[i]; 80 | 81 | switch (op) { 82 | case OP::TailCall: 83 | case OP::Call: { 84 | params += fmt::format(" &{}[{}]", vm::readUint32(bin, i + 1), vm::readUint16(bin, i + 5)); 85 | vm::eatParams(op, &i); 86 | break; 87 | } 88 | case OP::SourceMap: { 89 | auto size = vm::readUint32(bin, i + 1); 90 | auto start = i + 1; 91 | i += 4 + size; 92 | params += fmt::format(" {}->{} ({})", start, i, size / (4 * 3)); //each entry has 3x 4bytes (uint32) 93 | 94 | for (unsigned int j = start + 4; j < i; j += 4 * 3) { 95 | DebugSourceMapEntry sourceMapEntry{ 96 | .op = (OP)(bin[vm::readUint32(bin, j)]), 97 | .bytecodePos = vm::readUint32(bin, j), 98 | .sourcePos = vm::readUint32(bin, j + 4), 99 | .sourceEnd = vm::readUint32(bin, j + 8), 100 | }; 101 | result.sourceMap.push_back(sourceMapEntry); 102 | if (print) debug("Map [{}]{} to {}:{}", sourceMapEntry.bytecodePos, sourceMapEntry.op, sourceMapEntry.sourcePos, sourceMapEntry.sourceEnd); 103 | } 104 | break; 105 | } 106 | case OP::Subroutine: { 107 | auto nameAddress = vm::readUint32(bin, i + 1); 108 | auto address = vm::readUint32(bin, i + 5); 109 | string name = nameAddress ? string(vm::readStorage(bin, nameAddress + 8)) : ""; 110 | params += fmt::format(" {}[{}]", name, address); 111 | vm::eatParams(op, &i); 112 | result.subroutines.push_back({.name = name, .address = address}); 113 | break; 114 | } 115 | case OP::Jump: { 116 | auto address = vm::readInt32(bin, i + 1); 117 | params += fmt::format(" [{}, +{}]", startI + address, address); 118 | vm::eatParams(op, &i); 119 | if (!firstJump) storageEnd = address; 120 | if (firstJump) newLine = true; 121 | firstJump = true; 122 | break; 123 | } 124 | case OP::Main: { 125 | newSubRoutine = true; 126 | break; 127 | } 128 | case OP::Return: { 129 | newSubRoutine = true; 130 | break; 131 | } 132 | case OP::Distribute: { 133 | params += fmt::format(" &{} [{}, +{}]", vm::readUint16(bin, i + 1), startI + vm::readUint32(bin, i + 3), vm::readUint32(bin, i + 3)); 134 | vm::eatParams(op, &i); 135 | newLine = true; 136 | break; 137 | } 138 | case OP::JumpCondition: { 139 | params += fmt::format(" [{}]", startI + vm::readUint32(bin, i + 1)); 140 | vm::eatParams(op, &i); 141 | newLine = true; 142 | break; 143 | } 144 | case OP::CheckBody: 145 | case OP::InferBody: 146 | case OP::SelfCheck: 147 | case OP::Inline: 148 | case OP::Set: 149 | case OP::TypeArgumentDefault: { 150 | params += fmt::format(" &{}", vm::readUint32(bin, i + 1)); 151 | vm::eatParams(op, &i); 152 | break; 153 | } 154 | case OP::ClassRef: 155 | case OP::FunctionRef: { 156 | params += fmt::format(" &{} &{}", vm::readUint32(bin, i + 1), vm::readUint32(bin, i + 5)); 157 | vm::eatParams(op, &i); 158 | break; 159 | } 160 | case OP::New: 161 | case OP::Instantiate: 162 | case OP::InferTypeArguments: { 163 | params += fmt::format(" {}", vm::readUint16(bin, i + 1)); 164 | vm::eatParams(op, &i); 165 | break; 166 | } 167 | case OP::Error: { 168 | params += fmt::format(" {}", (instructions::ErrorCode)vm::readUint16(bin, i + 1)); 169 | vm::eatParams(op, &i); 170 | break; 171 | } 172 | case OP::CallExpression: { 173 | params += fmt::format(" {}", vm::readUint16(bin, i + 1)); 174 | vm::eatParams(op, &i); 175 | break; 176 | } 177 | case OP::Method: 178 | case OP::Function: 179 | case OP::Union: 180 | case OP::Tuple: 181 | case OP::TemplateLiteral: 182 | case OP::Class: 183 | case OP::ObjectLiteral: 184 | case OP::Slots: { 185 | params += fmt::format(" {}", vm::readUint16(bin, i + 1)); 186 | vm::eatParams(op, &i); 187 | break; 188 | } 189 | case OP::Loads: { 190 | params += fmt::format(" {} {}", vm::readUint16(bin, i + 1), vm::readUint16(bin, i + 3)); 191 | vm::eatParams(op, &i); 192 | break; 193 | } 194 | case OP::Parameter: 195 | case OP::NumberLiteral: 196 | case OP::BigIntLiteral: 197 | case OP::StringLiteral: { 198 | auto address = vm::readUint32(bin, i + 1); 199 | params += fmt::format(" \"{}\"", vm::readStorage(bin, address + 8)); 200 | vm::eatParams(op, &i); 201 | break; 202 | } 203 | } 204 | 205 | string text; 206 | if (params.empty()) { 207 | text = fmt::format("{}", op); 208 | } else { 209 | text = fmt::format("{}{}", op, params); 210 | } 211 | if (result.activeSubroutine) { 212 | result.activeSubroutine->operations.push_back({.text = text, .address = startI}); 213 | } else { 214 | result.operations.push_back(text); 215 | } 216 | if (print) { 217 | std::cout << "[" << startI << "](" << text << ") "; 218 | if (newLine) std::cout << "\n"; 219 | newLine = false; 220 | } 221 | } 222 | if (print) std::cout << "\n"; 223 | return result; 224 | } 225 | 226 | inline void printBin(string_view bin) { 227 | parseBin(bin, true); 228 | } 229 | } -------------------------------------------------------------------------------- /src/checker/instructions.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../enum.h" 4 | 5 | namespace tr::instructions { 6 | enum OP: int { 7 | Noop, 8 | Jump, //arbitrary jump, used at the beginning to jump over storage-data (storage-data's addresses are constant) 9 | Halt, 10 | SourceMap, //one parameter (size uint32). all subsequent bytes withing the given size is a map op:pos:end, each uint32 11 | Main, //marks end of meta-data section (subroutine metadata + storage data). after this the body section with all subroutine ops follow. 12 | 13 | Never, 14 | Any, 15 | Unknown, 16 | Void, //void is reserved word 17 | Object, 18 | 19 | String, 20 | Number, 21 | Boolean, 22 | BigInt, 23 | 24 | Symbol, 25 | Null, 26 | Undefined, 27 | 28 | StringLiteral, 29 | NumberLiteral, 30 | BigIntLiteral, 31 | True, 32 | False, 33 | 34 | Function, 35 | FunctionRef, //one parameter, the index of the subroutine of the function that needs to be instantiated 36 | ClassRef, 37 | 38 | Method, 39 | MethodSignature, 40 | 41 | Parameter, 42 | Initializer, 43 | 44 | Property, 45 | PropertySignature, 46 | 47 | Class, 48 | ObjectLiteral, 49 | IndexSignature, 50 | PropertyAccess, 51 | 52 | Array, 53 | Tuple, 54 | TupleMember, 55 | TupleNamedMember, //has one parameter, the name in the storage 56 | 57 | Optional, 58 | Readonly, 59 | Static, 60 | Rest, 61 | RestReuse, //in expressions like [...T, x] indicates that T can be stolen instead of copied 62 | 63 | Union, 64 | Intersection, 65 | 66 | Extends, //expected 2 entries on the stack 67 | Condition, //expected 3 entries on the stack 68 | JumpCondition, //expected 1 entry on the stack + two uint16 parameters 69 | 70 | CallExpression, //JS call expression, with 1 parameter (amount of parameters) 71 | Instantiate, //instantiates a type on the stack (FunctionRef for example), ExpressionWithTypeArguments 72 | StartInferTypeArguments, //calling generic function/class with only value arguments, infer type arguments from it 73 | InferTypeArguments, 74 | New, 75 | 76 | /** 77 | * Reserved new stack entries to be used as type variables. 78 | * 79 | * 1 parameter indicating how many stack entries will be reserved. 80 | */ 81 | Slots, 82 | 83 | /** 84 | * Self checks an expression, like variable, class, interface, type, etc. 85 | * TypeScript checks types even if they are never used. This SelfCheck makes sure all expression are visited at least once in main. 86 | */ 87 | SelfCheck, 88 | 89 | ///** 90 | // * Stack parameter. For each JS variable, JS function, as well as type variables (mapped-type variable for example). 91 | // * 92 | // * Parameters: 93 | // * 1. address on initial stack frame, which should contain its name as a string. 94 | // * 3. modifier: const 95 | // * 2. position in source code. necessary to determine if a reference is made to a const symbol before it was defined. 96 | // */ 97 | //Var, 98 | 99 | /** 100 | * Makes sure that in the current variable slot is a type placed if nothing was provided as parameter. 101 | * 102 | * For each type argument like here `T` a TypeArgument OP is generated. 103 | * Different to Var OP since Var does reserve an entry on the stack, 104 | * whereas TypeArgument ensures there is a stack entry if not already provided via call parameters.. 105 | * ```typescript 106 | * type A = T; 107 | * ``` 108 | */ 109 | TypeArgument, 110 | TypeArgumentDefault, //one parameter with the address of the subroutine of the default value 111 | 112 | TypeArgumentConstraint, //expects an entry (the constraint) on the stack 113 | 114 | TemplateLiteral, 115 | 116 | IndexAccess, 117 | Length, 118 | KeyOf, 119 | Infer, 120 | TypeOf, 121 | Widen, //literal to widened type, true -> boolean, '' => string, 32 => number, etc 122 | 123 | /** 124 | * Reserves a type variable on the stack, which contains a type object. Unknown as default. 125 | * 126 | * The address of it is known in the program and referenced directly. 127 | */ 128 | TypeVar, 129 | Loads, //LOAD from stack. pushes to the stack a referenced type in the stack. has 2 parameters: , frame is a negative offset to the frame, and index the index of the stack entry withing the referenced frame 130 | Assign, 131 | Dup, //Duplicates the current stack end 132 | Set, //narrows/Sets a new value for a subroutine (variables) 133 | SetAndPush, //narrows/Sets a new value for a subroutine (variables) 134 | Error, 135 | Pop, 136 | Inline, //Execute a subroutine on the same active frame 137 | 138 | //Frame, //creates a new stack frame 139 | //FrameEnd, 140 | Return, //end of a subroutine 141 | ReturnStatement, //used in inferring return types of functions 142 | 143 | Subroutine, 144 | Distribute, //calls a subroutine for each union member. one parameter (address to subroutine) 145 | Call, //call a subroutine and push the result on the stack 146 | TailCall, 147 | CheckBody, 148 | InferBody, 149 | UnwrapInferBody, 150 | }; 151 | 152 | enum class ErrorCode { 153 | CannotFind, //e.g. Cannot find name 'abc' 154 | }; 155 | 156 | //Max 8 bits, used in the bytecode 157 | enum SubroutineFlag: unsigned int { 158 | }; 159 | } 160 | 161 | 162 | template<> 163 | struct fmt::formatter: formatter { 164 | template 165 | auto format(tr::instructions::OP p, FormatContext &ctx) { 166 | return formatter::format(magic_enum::enum_name(p), ctx); 167 | } 168 | }; 169 | 170 | template<> 171 | struct fmt::formatter: formatter { 172 | template 173 | auto format(tr::instructions::ErrorCode p, FormatContext &ctx) { 174 | return formatter::format(magic_enum::enum_name(p), ctx); 175 | } 176 | }; 177 | -------------------------------------------------------------------------------- /src/checker/module2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "../core.h" 5 | #include "./utils.h" 6 | #include "./types2.h" 7 | #include "./instructions.h" 8 | #include "../utf.h" 9 | 10 | namespace tr::vm2 { 11 | using std::string; 12 | using std::string_view; 13 | using tr::instructions::OP; 14 | using tr::utf::eatWhitespace; 15 | 16 | struct ModuleSubroutine { 17 | string_view name; 18 | bool main = false; 19 | unsigned int address; 20 | unsigned int flags; 21 | bool exported = false; 22 | Type *result = nullptr; 23 | Type *narrowed = nullptr; //when control flow analysis sets a new value 24 | ModuleSubroutine(string_view name, unsigned int address, unsigned int flags, bool main): name(name), address(address), flags(flags), main(main) {} 25 | }; 26 | 27 | struct FoundSourceMap { 28 | unsigned int pos; 29 | unsigned int end; 30 | 31 | bool found() { 32 | return pos != 0 && end != 0; 33 | } 34 | }; 35 | 36 | /** 37 | * const v2: a = "as" 38 | * ^-----^ this is the identifier source map. 39 | * 40 | * This function makes sure map.pos starts at `v`, basically eating whitespace. 41 | */ 42 | inline void omitWhitespace(const string &code, FoundSourceMap &map) { 43 | map.pos = eatWhitespace(code, map.pos); 44 | } 45 | 46 | struct Module; 47 | 48 | struct DiagnosticMessage { 49 | string message; 50 | unsigned int ip; //ip of the node/OP 51 | Module * module = nullptr; 52 | DiagnosticMessage() {} 53 | explicit DiagnosticMessage(const string &message, int ip): message(message), ip(ip) {} 54 | }; 55 | 56 | struct FoundSourceLineCharacter { 57 | unsigned int line; 58 | unsigned int pos; 59 | unsigned int end; 60 | }; 61 | 62 | struct Module { 63 | const string bin; 64 | string fileName = "index.ts"; 65 | const string code = ""; //for diagnostic messages only 66 | 67 | vector subroutines; 68 | unsigned int sourceMapAddress; 69 | unsigned int sourceMapAddressEnd; 70 | 71 | vector errors; 72 | Module() {} 73 | 74 | Module(const string_view &bin, const string &fileName, const string &code): bin(bin), fileName(fileName), code(code) { 75 | } 76 | 77 | void clear() { 78 | errors.clear(); 79 | subroutines.clear(); 80 | } 81 | 82 | ModuleSubroutine *getSubroutine(unsigned int index) { 83 | return &subroutines[index]; 84 | } 85 | 86 | ModuleSubroutine *getMain() { 87 | return &subroutines.back(); 88 | } 89 | 90 | string findIdentifier(unsigned int ip) { 91 | auto map = findNormalizedMap(ip); 92 | if (!map.found()) return ""; 93 | return code.substr(map.pos, map.end - map.pos); 94 | } 95 | 96 | FoundSourceMap findMap(unsigned int ip) { 97 | unsigned int found = 0; 98 | for (unsigned int i = sourceMapAddress; i < sourceMapAddressEnd; i += 3 * 4) { 99 | auto mapIp = vm::readUint32(bin, i); 100 | if (mapIp == ip) { 101 | found = i; 102 | break; 103 | } 104 | // if (mapIp > ip) break; 105 | // found = i; 106 | } 107 | 108 | if (found) { 109 | return {vm::readUint32(bin, found + 4), vm::readUint32(bin, found + 8)}; 110 | } 111 | return {0, 0}; 112 | } 113 | 114 | FoundSourceMap findNormalizedMap(unsigned int ip) { 115 | auto map = findMap(ip); 116 | if (map.found()) omitWhitespace(code, map); 117 | return map; 118 | } 119 | 120 | /** 121 | * Converts FindSourceMap{x,y} to 122 | */ 123 | FoundSourceLineCharacter mapToLineCharacter(FoundSourceMap map) { 124 | unsigned int pos = 0; 125 | unsigned int line = 0; 126 | while (pos < map.pos) { 127 | std::size_t lineStart = code.find('\n', pos); 128 | if (lineStart == std::string::npos) { 129 | return {.line = line, .pos = map.pos - pos, .end = map.end - pos}; 130 | } else if (lineStart > map.pos) { 131 | //dont overshot 132 | break; 133 | } 134 | pos = lineStart + 1; 135 | line++; 136 | } 137 | return {.line = line, .pos = map.pos - pos, .end = map.end - pos}; 138 | } 139 | 140 | void printErrors() { 141 | for (auto &&e: errors) { 142 | if (e.ip) { 143 | auto map = findNormalizedMap(e.ip); 144 | 145 | if (map.found()) { 146 | auto lineStart = code.rfind('\n', map.pos); 147 | lineStart = lineStart == std::string::npos ? 0 : lineStart + 1; 148 | 149 | auto lineEnd = code.find('\n', map.end); 150 | if (lineEnd == std::string::npos) lineEnd = code.size(); 151 | std::cout << cyan << fileName << ":" << yellow << map.pos << ":" << map.end << reset << " - " << red << "error" << reset << " TS0000: " << e.message << "\n\n"; 152 | std::cout << "»" << code.substr(lineStart, lineEnd - lineStart - 1) << "\n"; 153 | auto space = map.pos - lineStart; 154 | std::cout << "»" << std::string(space, ' ') << red << std::string(map.end - map.pos, '~') << reset << "\n\n"; 155 | continue; 156 | } 157 | } 158 | std::cout << " " << e.message << "\n"; 159 | } 160 | std::cout << "Found " << errors.size() << " errors in " << fileName << "\n"; 161 | } 162 | }; 163 | 164 | inline void parseHeader(shared_ptr &module) { 165 | auto &bin = module->bin; 166 | auto end = bin.size(); 167 | bool main = true; 168 | for (unsigned int i = 0; i < end; i++) { 169 | const auto op = (OP) bin[i]; 170 | switch (op) { 171 | case OP::Jump: { 172 | i = vm::readUint32(bin, i + 1) - 1; //minus 1 because for's i++ 173 | break; 174 | } 175 | case OP::SourceMap: { 176 | unsigned int size = vm::readUint32(bin, i + 1); 177 | module->sourceMapAddress = i + 1 + 4; 178 | i += 4 + size; 179 | module->sourceMapAddressEnd = i; 180 | break; 181 | } 182 | case OP::Subroutine: { 183 | unsigned int nameAddress = vm::readUint32(bin, i + 1); 184 | auto name = nameAddress ? vm::readStorage(bin, nameAddress + 8) : ""; 185 | unsigned int address = vm::readUint32(bin, i + 5); 186 | unsigned int flags = vm::readUint32(bin, i + 5 + 4); 187 | vm::eatParams(op, &i); 188 | module->subroutines.push_back(ModuleSubroutine(name, address, flags, main)); 189 | main = false; 190 | break; 191 | } 192 | case OP::Main: { 193 | return; 194 | } 195 | } 196 | } 197 | 198 | throw std::runtime_error("No OP::Main found"); 199 | } 200 | } -------------------------------------------------------------------------------- /src/checker/pool_array.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "math.h" 9 | #include 10 | #include 11 | 12 | /** 13 | * Block is memory (Items + 1) * sizeof. Plus 1 because first entry is block header. 14 | * Block header points to next and previous block. 15 | */ 16 | template 17 | class PoolArray { 18 | public: 19 | unsigned int active = 0; 20 | 21 | union Slot { 22 | T element; 23 | struct Pointer { 24 | Slot *prev; 25 | Slot *next; 26 | } header; 27 | }; 28 | 29 | typedef char *data_pointer; 30 | typedef Slot slot_type; 31 | typedef Slot *slot_pointer; 32 | 33 | static_assert(BlockSize>=2 * sizeof(slot_type), "BlockSize too small."); 34 | static_assert(Items>=GCQueueSize, "Items need to be bigger than GCQueueSize"); 35 | 36 | struct Pool { 37 | Slot *currentBlock = nullptr; 38 | Slot *firstBlock = nullptr; 39 | 40 | Slot *currentSlot = nullptr; 41 | Slot *lastSlot = nullptr; 42 | Slot *freeSlot = nullptr; 43 | unsigned int blocks = 0; 44 | unsigned int slotSize; 45 | 46 | std::vector gcQueue; 47 | unsigned int gcQueued = 0; 48 | unsigned int gcQueueSize = 0; 49 | 50 | explicit Pool(unsigned int slotSize): slotSize(slotSize), gcQueueSize(ceil(GCQueueSize/slotSize)) { 51 | gcQueue.resize(gcQueueSize); 52 | } 53 | 54 | void deallocate(const std::span &span) { 55 | T *p = &span[0]; 56 | auto slot = reinterpret_cast(p); 57 | slot->header = {.prev = nullptr, .next = freeSlot}; 58 | freeSlot = slot; 59 | } 60 | 61 | void destruct(const std::span &span) { 62 | for (auto &&item: span) item.~T(); 63 | deallocate(span); 64 | } 65 | 66 | void gc(const std::span &span) { 67 | //flush queued items 68 | if (gcQueued>=gcQueueSize) gcFlush(); 69 | gcQueue[gcQueued++] = &span[0]; 70 | } 71 | 72 | void gcFlush() { 73 | for (unsigned int i = 0; i(gcQueue[i]); 79 | slot->header = {.prev = nullptr, .next = freeSlot}; 80 | freeSlot = slot; 81 | } 82 | gcQueued = 0; 83 | } 84 | }; 85 | 86 | constexpr static unsigned int poolAmount = 11; 87 | std::array pools = {Pool(1), Pool(2), Pool(4), Pool(8), Pool(16), Pool(32), Pool(64), Pool(128), Pool(256), Pool(512), Pool(1024)}; 88 | 89 | PoolArray() noexcept {} 90 | 91 | ~PoolArray() noexcept { 92 | for (auto &&pool: pools) { 93 | slot_pointer curr = pool.currentBlock; 94 | while (curr != nullptr) { 95 | slot_pointer prev = curr->header.prev; 96 | operator delete(reinterpret_cast(curr)); 97 | curr = prev; 98 | } 99 | } 100 | } 101 | 102 | unsigned int poolIndex(unsigned int size) { 103 | if (size>1024) size = 1024; 104 | return ceil(log2(size)); 105 | } 106 | 107 | Pool &getPool(unsigned int size) { 108 | return pools[poolIndex(size)]; 109 | } 110 | 111 | std::span allocate(unsigned int size) { 112 | auto &pool = getPool(size); 113 | active += size; 114 | if (pool.freeSlot != nullptr) { 115 | T *result = reinterpret_cast(pool.freeSlot); 116 | pool.freeSlot = pool.freeSlot->header.next; 117 | return {result, size}; 118 | } else { 119 | if (pool.currentSlot + pool.slotSize - 1>=pool.lastSlot) { 120 | allocateBlock(pool); 121 | } 122 | auto result = reinterpret_cast(pool.currentSlot + 1); 123 | pool.currentSlot += pool.slotSize; 124 | return {result, size}; 125 | } 126 | } 127 | 128 | void deallocate(const std::span &span) { 129 | active -= span.size(); 130 | auto &pool = getPool(span.size()); 131 | pool.deallocate(span); 132 | } 133 | 134 | std::span construct(unsigned int size) { 135 | auto span = allocate(size); 136 | for (auto &&item: span) new(&item) T(); 137 | return span; 138 | } 139 | 140 | void destruct(const std::span &span) { 141 | for (auto &&item: span) item.~T(); 142 | deallocate(span); 143 | } 144 | 145 | void clear() { 146 | active = 0; 147 | for (auto &&pool: pools) { 148 | pool.freeSlot = nullptr; 149 | pool.gcQueued = 0; 150 | if (pool.firstBlock) initializeBlock(pool, pool.firstBlock); 151 | } 152 | } 153 | 154 | void gc(const std::span &span) { 155 | auto &pool = getPool(span.size()); 156 | pool.gc(span); 157 | } 158 | 159 | private: 160 | 161 | void allocateBlock(Pool &pool) { 162 | if (pool.currentBlock && reinterpret_cast(pool.currentBlock)->header.next) { 163 | initializeBlock(pool, reinterpret_cast(pool.currentBlock)->header.next); 164 | } else { 165 | pool.blocks++; 166 | // Allocate space for the new block and store a pointer to the previous one 167 | data_pointer newBlock = reinterpret_cast(operator new(BlockSize)); 168 | reinterpret_cast(newBlock)->header = {.prev = pool.currentBlock, .next = nullptr}; 169 | setNextBlock(pool, reinterpret_cast(newBlock)); 170 | } 171 | } 172 | 173 | void setNextBlock(Pool &pool, slot_pointer nextBlock) { 174 | if (pool.currentBlock) pool.currentBlock->header.next = nextBlock; 175 | if (!pool.firstBlock) pool.firstBlock = nextBlock; 176 | initializeBlock(pool, nextBlock); 177 | } 178 | 179 | slot_pointer blockStartSlot(slot_pointer block) { 180 | auto blockPoint = reinterpret_cast(block); 181 | return reinterpret_cast(blockPoint + sizeof(slot_type)); 182 | } 183 | 184 | void initializeBlock(Pool &pool, slot_pointer nextBlock) { 185 | pool.currentBlock = nextBlock; 186 | pool.currentSlot = blockStartSlot(nextBlock); 187 | pool.lastSlot = reinterpret_cast(reinterpret_cast(nextBlock) + BlockSize - sizeof(slot_type) + 1); 188 | } 189 | }; -------------------------------------------------------------------------------- /src/checker/pool_single.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include "../core.h" 10 | 11 | /** 12 | * Block is memory (Items + 1) * sizeof. Plus 1 because first entry is block header. 13 | * Block header points to next and previous block. 14 | */ 15 | template 16 | class PoolSingle { 17 | public: 18 | typedef T value_type; 19 | typedef T *pointer; 20 | typedef T &reference; 21 | 22 | std::array gcQueue; 23 | unsigned int gcQueued = 0; 24 | 25 | union Slot { 26 | value_type element; 27 | struct Pointer { 28 | Slot *prev; 29 | Slot *next; 30 | } pointer; 31 | }; 32 | 33 | typedef char *data_pointer; 34 | typedef Slot slot_type; 35 | typedef Slot *slot_pointer; 36 | static_assert(BlockSize>=2 * sizeof(slot_type), "BlockSize too small."); 37 | 38 | ~PoolSingle() noexcept { 39 | slot_pointer curr = currentBlock; 40 | while (curr != nullptr) { 41 | slot_pointer prev = curr->pointer.prev; 42 | operator delete(reinterpret_cast(curr)); 43 | curr = prev; 44 | } 45 | } 46 | 47 | unsigned int active = 0; 48 | unsigned int blocks = 0; 49 | 50 | pointer allocate() { 51 | active++; 52 | if (freeSlot != nullptr) { 53 | pointer result = reinterpret_cast(freeSlot); 54 | freeSlot = freeSlot->pointer.next; 55 | return result; 56 | } else { 57 | if (currentSlot>=lastSlot) { 58 | allocateBlock(); 59 | } 60 | return reinterpret_cast(currentSlot++); 61 | } 62 | } 63 | 64 | void deallocate(pointer p) { 65 | if (p != nullptr) { 66 | active--; 67 | auto slot = reinterpret_cast(p); 68 | slot->pointer = {.prev = nullptr, .next = freeSlot}; 69 | freeSlot = slot; 70 | } 71 | } 72 | 73 | template 74 | pointer construct(Args &&... args) { 75 | pointer result = allocate(); 76 | new(result) T(std::forward(args)...); 77 | return result; 78 | } 79 | 80 | void destruct(T *p) { 81 | p->~T(); 82 | deallocate(p); 83 | } 84 | 85 | void clear() { 86 | active = 0; 87 | freeSlot = nullptr; 88 | gcQueued = 0; 89 | if (firstBlock) initializeBlock(firstBlock); 90 | } 91 | 92 | void gc(pointer p) { 93 | //flush queued items 94 | if (gcQueued>=GCQueueSize) gcFlush(); 95 | gcQueue[gcQueued++] = p; 96 | } 97 | 98 | void gcFlush() { 99 | for (unsigned int i = 0; i(currentBlock)->pointer.next) { 113 | initializeBlock(reinterpret_cast(currentBlock)->pointer.next); 114 | } else { 115 | blocks++; 116 | // Allocate space for the new block and store a pointer to the previous one 117 | data_pointer newBlock = reinterpret_cast(operator new(BlockSize)); 118 | reinterpret_cast(newBlock)->pointer = {.prev = currentBlock, .next = nullptr}; 119 | setNextBlock(reinterpret_cast(newBlock)); 120 | } 121 | } 122 | 123 | void setNextBlock(slot_pointer nextBlock) { 124 | if (currentBlock) currentBlock->pointer.next = nextBlock; 125 | if (!firstBlock) firstBlock = nextBlock; 126 | initializeBlock(nextBlock); 127 | } 128 | 129 | slot_pointer blockStartSlot(slot_pointer block) { 130 | auto blockPoint = reinterpret_cast(block); 131 | return reinterpret_cast(blockPoint + sizeof(slot_type)); 132 | } 133 | 134 | void initializeBlock(slot_pointer nextBlock) { 135 | currentBlock = nextBlock; 136 | currentSlot = blockStartSlot(nextBlock); 137 | lastSlot = reinterpret_cast(reinterpret_cast(nextBlock) + BlockSize - sizeof(slot_type) + 1); 138 | } 139 | }; -------------------------------------------------------------------------------- /src/checker/utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include "./instructions.h" 7 | 8 | namespace tr::vm { 9 | using std::vector; 10 | using std::string_view; 11 | 12 | inline uint64_t readUint64(const vector &bin, unsigned int offset) { 13 | return *(uint64_t *) (bin.data() + offset); 14 | } 15 | 16 | inline uint64_t readUint64(const string_view &bin, unsigned int offset) { 17 | return *(uint64_t *) (bin.data() + offset); 18 | } 19 | 20 | inline uint32_t readUint32(const vector &bin, unsigned int offset) { 21 | return *(uint32_t *) (bin.data() + offset); 22 | } 23 | 24 | inline uint32_t readUint32(const string_view &bin, unsigned int offset) { 25 | return *(uint32_t *) (bin.begin() + offset); 26 | } 27 | 28 | inline int32_t readInt32(const string_view &bin, unsigned int offset) { 29 | return *(int32_t *) (bin.begin() + offset); 30 | } 31 | 32 | inline void writeUint32(vector &bin, unsigned int offset, uint32_t value) { 33 | if (offset + 4>bin.size()) bin.resize(bin.size() + 4); 34 | *(uint32_t *) (bin.data() + offset) = value; 35 | } 36 | 37 | inline void writeInt32(vector &bin, unsigned int offset, int32_t value) { 38 | if (offset + 4>bin.size()) bin.resize(bin.size() + 4); 39 | *(int32_t *) (bin.data() + offset) = value; 40 | } 41 | 42 | inline void writeUint64(vector &bin, unsigned int offset, uint64_t value) { 43 | if (offset + 8>bin.size()) bin.resize(bin.size() + 8); 44 | *(uint64_t *) (bin.data() + offset) = value; 45 | } 46 | 47 | inline uint16_t readUint16(const vector &bin, unsigned int offset) { 48 | return *(uint16_t *) (bin.data() + offset); 49 | } 50 | 51 | inline uint16_t readUint16(const string_view &bin, unsigned int offset) { 52 | return *(uint16_t *) (bin.begin() + offset); 53 | } 54 | 55 | inline void writeUint16(vector &bin, unsigned int offset, uint16_t value) { 56 | if (offset + 2>bin.size()) bin.resize(bin.size() + 2); 57 | *(uint16_t *) (bin.data() + offset) = value; 58 | } 59 | 60 | inline string_view readStorage(const string_view &bin, const uint32_t offset) { 61 | const auto size = readUint16(bin, offset); 62 | return string_view(reinterpret_cast(bin.data() + offset + 2), size); 63 | } 64 | 65 | using tr::instructions::OP; 66 | inline void eatParams(OP op, unsigned int *i) { 67 | switch (op) { 68 | case OP::TailCall: 69 | case OP::Call: { 70 | *i += 6; 71 | break; 72 | } 73 | case OP::Subroutine: { 74 | *i += 4 + 4 + 1; 75 | break; 76 | } 77 | case OP::Main: { 78 | break; 79 | } 80 | case OP::Jump: { 81 | *i += 4; 82 | break; 83 | } 84 | case OP::JumpCondition: { 85 | *i += 4; 86 | break; 87 | } 88 | case OP::Distribute: { 89 | *i += 2 + 4; 90 | break; 91 | } 92 | case OP::Set: 93 | case OP::CheckBody: 94 | case OP::InferBody: 95 | case OP::SelfCheck: 96 | case OP::Inline: 97 | case OP::TypeArgumentDefault: { 98 | *i += 4; 99 | break; 100 | } 101 | case OP::ClassRef: 102 | case OP::FunctionRef: { 103 | *i += 4 + 4; 104 | break; 105 | } 106 | case OP::New: 107 | case OP::Instantiate: 108 | case OP::InferTypeArguments: { 109 | *i += 2; 110 | break; 111 | } 112 | case OP::Error: { 113 | *i += 2; 114 | break; 115 | } 116 | case OP::Method: 117 | case OP::Function: 118 | case OP::Union: 119 | case OP::Tuple: 120 | case OP::TemplateLiteral: 121 | case OP::Class: 122 | case OP::ObjectLiteral: 123 | case OP::Slots: 124 | case OP::CallExpression: { 125 | *i += 2; 126 | break; 127 | } 128 | case OP::Loads: { 129 | *i += 4; 130 | break; 131 | } 132 | case OP::Parameter: 133 | case OP::NumberLiteral: 134 | case OP::BigIntLiteral: 135 | case OP::StringLiteral: { 136 | *i += 4; 137 | break; 138 | } 139 | } 140 | } 141 | } -------------------------------------------------------------------------------- /src/checker/vm.cpp: -------------------------------------------------------------------------------- 1 | #include "./vm.h" 2 | 3 | namespace tr::vm { 4 | node stringToNum(VM &vm) { 5 | } 6 | } -------------------------------------------------------------------------------- /src/checker/vm2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "./pool_single.h" 5 | #include "./pool_array.h" 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "../core.h" 11 | #include "./utils.h" 12 | #include "./types2.h" 13 | #include "./module2.h" 14 | #include "./instructions.h" 15 | 16 | namespace tr::vm2 { 17 | using instructions::OP; 18 | using std::string_view; 19 | 20 | // constexpr auto memoryDefault = 4096 * 10; 21 | 22 | // struct TypeMemoryPool2 { 23 | // MemoryPool unknown; 24 | // MemoryPool never; 25 | // MemoryPool any; 26 | // MemoryPool literal; 27 | // MemoryPool objectLiteral; 28 | // MemoryPool unions; 29 | // MemoryPool tuple; 30 | // MemoryPool tupleMember; 31 | // MemoryPool propertySignature; 32 | // }; 33 | 34 | constexpr auto poolSize = 10000; 35 | inline PoolSingle pool; 36 | inline PoolSingle poolRef; 37 | inline PoolArray poolRefs; 38 | 39 | // The stack does not own Type 40 | inline std::array stack; 41 | inline unsigned int sp = 0; 42 | 43 | struct LoopHelper { 44 | TypeRef *current = nullptr; 45 | unsigned int ip = 0; 46 | unsigned int startSP = 0; 47 | unsigned int var1 = 0; 48 | LoopHelper *previous = nullptr; 49 | 50 | void set(unsigned int var1, TypeRef *typeRef) { 51 | this->var1 = var1; 52 | current = typeRef; 53 | } 54 | 55 | bool next() { 56 | if (!current) return false; 57 | stack[var1] = current->type; 58 | current = current->next; 59 | return true; 60 | } 61 | }; 62 | 63 | enum SubroutineFlag: uint16_t { 64 | InferBody = 1<<0, 65 | }; 66 | 67 | /** 68 | * For each active subroutine this object is created. 69 | */ 70 | struct ActiveSubroutine { 71 | Module *module; 72 | ModuleSubroutine *subroutine; 73 | unsigned int ip = 0; //current instruction pointer 74 | unsigned int depth = 0; 75 | 76 | unsigned int initialSp = 0; //initial stack pointer 77 | //the amount of registered variable slots on the stack. will be subtracted when doing popFrame() 78 | //type arguments of type functions and variables like for mapped types 79 | unsigned int variables = 0; 80 | vector variableIPs; //only used when stepper is active 81 | uint16_t typeArguments = 0; 82 | 83 | /** @see SubroutineFlag */ 84 | uint16_t flags = 0; 85 | LoopHelper *loop = nullptr; 86 | 87 | LoopHelper *createLoop(unsigned int var1, TypeRef *type); 88 | LoopHelper *createEmptyLoop(); 89 | 90 | void popLoop(); 91 | 92 | unsigned int size() { 93 | return sp - initialSp; 94 | } 95 | 96 | OP op() { 97 | return (OP) module->bin[ip]; 98 | } 99 | 100 | std::span pop(unsigned int size) { 101 | sp -= size; 102 | return {stack.data() + sp, size}; 103 | } 104 | uint32_t parseUint32() { 105 | auto val = vm::readUint32(module->bin, ip + 1); 106 | ip += 4; 107 | return val; 108 | } 109 | 110 | bool isMain() { 111 | return subroutine->main; 112 | } 113 | 114 | int32_t parseInt32() { 115 | auto val = vm::readInt32(module->bin, ip + 1); 116 | ip += 4; 117 | return val; 118 | } 119 | 120 | uint16_t parseUint16() { 121 | auto val = vm::readUint16(module->bin, ip + 1); 122 | ip += 2; 123 | return val; 124 | } 125 | }; 126 | 127 | template 128 | class StackPool { 129 | private: 130 | std::array values; 131 | unsigned int i; 132 | public: 133 | T *at(unsigned int pos) { 134 | return &values[pos]; 135 | } 136 | 137 | T *front() { 138 | return &values[i]; 139 | } 140 | 141 | unsigned int index() { 142 | return i; 143 | } 144 | 145 | unsigned int size() { 146 | return i + 1; 147 | } 148 | 149 | T *reset() { 150 | i = 0; 151 | return &values[0]; 152 | } 153 | 154 | T *push() { 155 | if (i>=Size) { 156 | throw std::runtime_error("Stack overflow"); 157 | } 158 | return &values[++i]; 159 | } 160 | 161 | T *pop() { 162 | if (i == 0) { 163 | throw std::runtime_error("Popped out of stack"); 164 | } 165 | return &values[--i]; 166 | } 167 | }; 168 | 169 | constexpr auto stackSize = 1024; 170 | //aka frames 171 | inline StackPool activeSubroutines; 172 | inline StackPool loops; 173 | 174 | inline bool stepper = false; 175 | inline ActiveSubroutine *subroutine = nullptr; 176 | 177 | void process(); 178 | 179 | void clear(shared_ptr &module); 180 | void prepare(shared_ptr &module); 181 | void drop(Type *type); 182 | void drop(std::span *types); 183 | void gc(std::span *types); 184 | void gc(Type *type); 185 | void gcFlush(); 186 | // Garbage collect whatever is left on the stack 187 | void gcStack(); 188 | void gcStackAndFlush(); 189 | 190 | Type *allocate(TypeKind kind, uint64_t hash = 0); 191 | std::span allocateRefs(unsigned int size); 192 | 193 | void addHashChild(Type *type, Type *child, unsigned int size); 194 | 195 | std::span popFrame(); 196 | 197 | static void run(shared_ptr module) { 198 | // profiler.clear(); 199 | // pool = MemoryPool(); 200 | // poolRef = MemoryPool(); 201 | pool.clear(); 202 | poolRef.clear(); 203 | poolRefs.clear(); 204 | 205 | sp = 0; 206 | loops.reset(); 207 | 208 | prepare(module); 209 | process(); 210 | } 211 | 212 | void call(shared_ptr &module, unsigned int index = 0, unsigned int arguments = 0); 213 | 214 | struct CStack { 215 | vector iterator; 216 | unsigned int i; 217 | unsigned int round; 218 | }; 219 | 220 | class CartesianProduct { 221 | vector stack; 222 | public: 223 | 224 | Type *current(CStack &s) { 225 | return s.iterator[s.i]; 226 | } 227 | 228 | bool next(CStack &s) { 229 | return (++s.i == s.iterator.size()) ? (s.i = 0, false) : true; 230 | } 231 | 232 | vector toGroup(Type *type) { 233 | if (type->kind == TypeKind::Boolean) { 234 | return {allocate(TypeKind::Literal)->setFlag(TypeFlag::True), allocate(TypeKind::Literal)->setFlag(TypeFlag::False)}; 235 | } else if (type->kind == TypeKind::Null) { 236 | return {allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "null")}; 237 | } else if (type->kind == TypeKind::Undefined) { 238 | return {allocate(TypeKind::Literal)->setLiteral(TypeFlag::StringLiteral, "undefined")}; 239 | // } else if (type->kind == TypeKind::templateLiteral) { 240 | // // //todo: this is wrong 241 | // // return type.types; 242 | // const result: Type[] = []; 243 | // for (const s of type.types) { 244 | // const g = this.toGroup(s); 245 | // result.push(...g); 246 | // } 247 | // 248 | // return result; 249 | } else if (type->kind == TypeKind::Union) { 250 | vector result; 251 | auto current = (TypeRef *) type->type; 252 | while (current) { 253 | auto g = toGroup(current->type); 254 | for (auto &&s: g) result.push_back(s); 255 | current = current->next; 256 | } 257 | 258 | return result; 259 | } else { 260 | return {type}; 261 | } 262 | } 263 | 264 | void add(Type *item) { 265 | stack.push_back({.iterator=toGroup(item), .i= 0, .round= 0}); 266 | } 267 | 268 | vector> calculate() { 269 | vector> result; 270 | 271 | outer: 272 | while (true) { 273 | vector row; 274 | for (auto &&s: stack) { 275 | auto item = current(s); 276 | if (item->kind == TypeKind::TemplateLiteral) { 277 | auto current = (TypeRef *) item->type; 278 | while (current) { 279 | row.push_back(current->type); 280 | current = current->next; 281 | } 282 | } else { 283 | row.push_back(item); 284 | } 285 | } 286 | result.push_back(row); 287 | 288 | for (unsigned int i = stack.size() - 1; i>=0; i--) { 289 | auto active = next(stack[i]); 290 | //when that i stack is active, continue in main loop 291 | if (active) goto outer; 292 | 293 | //i stack was rewinded. If it's the first, it means we are done 294 | if (i == 0) { 295 | goto done; 296 | } 297 | } 298 | break; 299 | } 300 | 301 | done: 302 | return result; 303 | } 304 | }; 305 | 306 | } -------------------------------------------------------------------------------- /src/checker/vm2_utils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "./types2.h" 5 | 6 | namespace ts::vm2 { 7 | // template 8 | // _GLIBCXX20_CONSTEXPR 9 | // _Function 10 | // for_each_child(void *start, _Function __f) 11 | // { 12 | // auto current = (TypeRef *)start; 13 | // while (current) { 14 | // __f(current->type); 15 | // current = current->next; 16 | // } 17 | // } 18 | 19 | } -------------------------------------------------------------------------------- /src/core.cpp: -------------------------------------------------------------------------------- 1 | #include "core.h" 2 | 3 | namespace tr::Debug { 4 | void asserts(bool v, std::string text) { 5 | if (!v) throw std::runtime_error("assert: " + text); 6 | } 7 | 8 | void fail(std::string text) { 9 | throw std::runtime_error("Fail: " + text); 10 | } 11 | } 12 | 13 | namespace tr { 14 | string substr(const string &str, int start, optional len) { 15 | if (start <= 0 && len && len < 0) return ""; 16 | if (!len || *len < 0 || len > str.size()) *len = str.size(); 17 | if (start < 0) start += str.length(); 18 | return str.substr(start, *len); 19 | } 20 | 21 | //compatible with JavaScript's String.substring 22 | string substring(const string &str, int start, optional end) { 23 | if (start < 0) start = 0; 24 | int len = str.size(); 25 | if (end) { 26 | if (*end < start) { 27 | len = start - *end; 28 | start = *end; 29 | } else { 30 | len = *end - start; 31 | } 32 | } 33 | if (len < 0) len = 0; 34 | if (start > str.size()) start = str.size(); 35 | return str.substr(start, len); 36 | } 37 | 38 | string replaceLeading(const string &text, const string &from, const string &to) { 39 | if (0 == text.find(from)) { 40 | string str = text; 41 | str.replace(0, from.length(), to); 42 | return str; 43 | } 44 | return text; 45 | } 46 | 47 | bool isTrue(optional b) { 48 | return b && *b; 49 | } 50 | bool isFalse(optional b) { 51 | return !b || !*b; 52 | } 53 | vector charToStringVector(vector chars) { 54 | vector s; 55 | for (auto &&c: chars) s.push_back(c); 56 | return s; 57 | } 58 | bool startsWith(const string &str, const string &suffix) { 59 | return str.find(suffix) == 0; 60 | } 61 | bool endsWith(const string &str, const string &suffix) { 62 | auto expectedPos = str.size() - suffix.size(); 63 | return expectedPos >= 0 && str.find(suffix, expectedPos) == expectedPos; 64 | } 65 | string join(const vector &vec, const char *delim) { 66 | std::stringstream res; 67 | copy(vec.begin(), vec.end(), std::ostream_iterator(res, delim)); 68 | return res.str(); 69 | } 70 | vector split(const string &s, const string &delimiter) { 71 | vector result; 72 | size_t last = 0; 73 | size_t next = 0; 74 | while ((next = s.find(delimiter, last)) != string::npos) { 75 | result.push_back(s.substr(last, next - last)); 76 | last = next + 1; 77 | } 78 | result.push_back(s.substr(last)); 79 | return result; 80 | } 81 | string replaceAll(const string &text, const string &from, const string &to) { 82 | if (from.empty()) return text; 83 | string str = text; 84 | size_t start_pos = 0; 85 | while ((start_pos = str.find(from, start_pos)) != std::string::npos) { 86 | str.replace(start_pos, from.length(), to); 87 | start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx' 88 | } 89 | return str; 90 | } 91 | string trimStringStart(string s) { 92 | while (s.compare(0, 1, " ") == 0) s.erase(s.begin()); 93 | return s; 94 | } 95 | string trimStringEnd(string s) { 96 | while (s.size() > 0 && s.compare(s.size() - 1, 1, " ") == 0) s.erase(s.end() - 1); 97 | return s; 98 | } 99 | } -------------------------------------------------------------------------------- /src/core.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define CALLBACK(name) [this](auto ...a) { return name(a...); } 17 | 18 | namespace tr::Debug { 19 | void asserts(bool v, std::string text = ""); 20 | void fail(std::string text = ""); 21 | } 22 | 23 | namespace tr { 24 | using std::string; 25 | using std::vector; 26 | using std::function; 27 | using std::optional; 28 | using std::shared_ptr; 29 | using std::replace; 30 | using std::unordered_map; 31 | using std::map; 32 | using std::cout; 33 | 34 | //compatible with JavaScript's String.substr 35 | string substr(const string &str, int start, optional len = {}); 36 | 37 | //compatible with JavaScript's String.substring 38 | string substring(const string &str, int start, optional end = {}); 39 | 40 | /** 41 | * Used for AST optional nodes that are manually memory managed. It's necessary to check if(node) to make sure not to access invalid memory. 42 | */ 43 | template 44 | using optionalNode = T*; 45 | 46 | /** 47 | * Used for AST nodes that are manually memory managed. 48 | */ 49 | template 50 | using node = T*; 51 | 52 | template 53 | T *reinterpret_node(node node) { 54 | return reinterpret_cast(node); 55 | } 56 | 57 | string replaceLeading(const string &text, const string &from, const string &to); 58 | 59 | /** 60 | * Returns the last element of an array if non-empty, `undefined` otherwise. 61 | */ 62 | template 63 | inline optional lastOrUndefined(vector &array) { 64 | if (array.empty()) return std::nullopt; 65 | return array.back(); 66 | } 67 | 68 | template 69 | inline T defaultTo(optional v, T def) { 70 | if (v) return *v; 71 | return def; 72 | } 73 | 74 | bool isTrue(optional b = {}); 75 | 76 | bool isFalse(optional b = {}); 77 | 78 | vector charToStringVector(vector chars); 79 | 80 | bool startsWith(const string &str, const string &suffix); 81 | 82 | bool endsWith(const string &str, const string &suffix); 83 | 84 | template 85 | inline vector append(vector &v, T item) { 86 | v.push_back(item); 87 | return v; 88 | } 89 | 90 | template 91 | inline vector slice(const vector &v, int start = 0, int end = 0) { 92 | if (!end) end = v.size(); 93 | return std::vector(v.begin() + start, v.begin() + end); 94 | } 95 | 96 | template 97 | inline optional> slice(const optional> &v, int start = 0, int end = 0) { 98 | if (!v) return std::nullopt; 99 | return slice(*v, start, end); 100 | } 101 | 102 | string join(const vector &vec, const char *delim); 103 | 104 | vector split(const string &s, const string &delimiter); 105 | 106 | string replaceAll(const string &text, const string &from, const string &to); 107 | 108 | // 109 | // template 110 | // using Optional = optional>; 111 | //// using Optional = variant>>; 112 | // //struct Optional: public optional> {}; 113 | // 114 | // template 115 | // bool empty(Optional v) { 116 | // // if (holds_alternative(v)) return false; 117 | // // auto opt = get>>(v); 118 | // // return opt.has_value(); 119 | // return v.has_value(); 120 | // } 121 | // 122 | // template 123 | // T &resolve(Optional v) { 124 | // // if (holds_alternative(v)) { 125 | // // return get(v); 126 | // // } 127 | // // auto opt = get>>(v); 128 | // // 129 | // // if (!opt.has_value()) throw runtime_error("Optional is empty"); 130 | // // 131 | // // return *(Node*)(&(opt.value().get())); 132 | // return *(T*)(&(v.value().get())); 133 | // } 134 | 135 | 136 | string trimStringStart(string s); 137 | 138 | string trimStringEnd(string s); 139 | 140 | template 141 | inline bool some(optional> array, optional> predicate) { 142 | //inline bool some(vector array, std::function predicate) { 143 | //inline bool some(optional> array) { 144 | if (array) { 145 | if (predicate) { 146 | for (auto &&v: (*array)) { 147 | if ((*predicate)(v)) { 148 | return true; 149 | } 150 | } 151 | } else { 152 | return (*array).size()>0; 153 | } 154 | } 155 | return false; 156 | } 157 | 158 | template 159 | inline bool some(optional> array) { 160 | return array && !array->empty(); 161 | } 162 | 163 | template 164 | inline bool some(vector array, std::function predicate) { 165 | return some(optional(array), optional(predicate)); 166 | } 167 | 168 | template 169 | inline void remove(vector &vector, const Func &filter) { 170 | auto new_end = std::remove_if(vector.begin(), vector.end(), filter); 171 | vector.erase(new_end, vector.end()); 172 | } 173 | 174 | template 175 | inline void remove(vector &vector, T item) { 176 | vector.erase(remove(vector.begin(), vector.end(), item), vector.end()); 177 | } 178 | 179 | template 180 | inline bool has(std::set&s, T 181 | item) { 182 | return s. 183 | find(item) 184 | != s. 185 | end(); 186 | } 187 | 188 | template 189 | inline bool has(vector &vector, T item) { 190 | return find(vector.begin(), vector.end(), item) != vector.end(); 191 | }; 192 | 193 | template 194 | inline bool has(const vector &vector, T item) { 195 | return find(vector.begin(), vector.end(), item) != vector.end(); 196 | }; 197 | 198 | template 199 | inline unordered_map combine( 200 | const unordered_map &map1, 201 | const unordered_map &map2 202 | ) { 203 | unordered_map res(map1); 204 | res.insert(map2.begin(), map2.end()); 205 | return res; 206 | } 207 | 208 | template 209 | inline unordered_map reverse( 210 | const unordered_map &map1 211 | ) { 212 | unordered_map res; 213 | for (auto &&i: map1) { 214 | res[i.second] = i.first; 215 | } 216 | return res; 217 | } 218 | 219 | template 220 | inline optional get(const unordered_map &m, K key) { 221 | auto v = m.find(key); 222 | if (v == m.end()) return std::nullopt; 223 | return v->second; 224 | } 225 | 226 | template 227 | inline optional set(unordered_map &m, K key, T v) { 228 | // m.insert_or_assign(key, v); 229 | throw std::runtime_error("not implemented"); 230 | std::cout<<"set map: "< 236 | inline bool has(unordered_map &map, T key) { 237 | return map.find(key) != map.end(); 238 | }; 239 | 240 | template 241 | inline bool has(map &map, T key) { 242 | return map.find(key) != map.end(); 243 | }; 244 | 245 | //template 246 | //inline void debug(T fmt, Args &&...args) { 247 | //// fmt::print(fmt, std::forward(args)...); 248 | // std::vformat(fmt, std::make_format_args(std::forward(args)...)); 249 | // std::cout<(args)...)<<"\n"; 250 | //// std::cout << fmt::format(fmt, args...) << "\n"; 251 | //} 252 | 253 | template 254 | inline void debug(fmt::format_string s, Args&&... args) 255 | { 256 | std::cout << fmt::format(s, std::forward(args)...); 257 | } 258 | 259 | inline std::chrono::duration benchRun(int iterations, const function &callback) { 260 | auto start = std::chrono::high_resolution_clock::now(); 261 | for (auto i = 0; i &callback) { 269 | auto took = benchRun(iterations, callback); 270 | std::cout< &callback) { 274 | bench("", iterations, callback); 275 | } 276 | 277 | inline std::string red; 278 | inline std::string green; 279 | inline std::string yellow; 280 | inline std::string cyan; 281 | inline std::string magenta; 282 | inline std::string reset; 283 | 284 | inline void enableColors() { 285 | red = "\033[0;31m"; 286 | green = "\033[1;32m"; 287 | yellow = "\033[1;33m"; 288 | cyan = "\033[0;36m"; 289 | magenta = "\033[0;35m"; 290 | reset = "\033[0m"; 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /src/enum.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define MAGIC_ENUM_RANGE_MIN 0 4 | #define MAGIC_ENUM_RANGE_MAX 512 5 | #include "magic_enum.hpp" -------------------------------------------------------------------------------- /src/fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | using std::string; 8 | using std::string_view; 9 | 10 | inline auto fileRead(const string &file) { 11 | std::ifstream t; 12 | t.open(file); 13 | t.seekg(0, std::ios::end); 14 | size_t size = t.tellg(); 15 | std::string buffer(size, ' '); 16 | t.seekg(0); 17 | t.read(&buffer[0], size); 18 | return std::move(buffer); 19 | } 20 | 21 | inline void fileWrite(const string &file, const string_view &content) { 22 | std::ofstream t; 23 | t.open(file); 24 | t << content; 25 | } 26 | 27 | inline bool fileExists(const string &file) { 28 | std::ifstream infile(file); 29 | return infile.good(); 30 | } -------------------------------------------------------------------------------- /src/gui/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(typescript) 2 | 3 | add_executable(typescript_debugger 4 | debugger_main.cpp 5 | app.h 6 | ../../libs/imgui/imgui.cpp 7 | ../../libs/imgui/imgui_demo.cpp 8 | ../../libs/imgui/imgui_draw.cpp 9 | ../../libs/imgui/imgui_widgets.cpp 10 | ../../libs/imgui/imgui_tables.cpp 11 | ../../libs/imgui/misc/cpp/imgui_stdlib.cpp 12 | ../../libs/imgui/backends/imgui_impl_sdl.cpp 13 | ../../libs/imgui/backends/imgui_impl_opengl3.cpp 14 | ../../libs/imgui-texteditor-fork/TextEditor.cpp 15 | ) 16 | 17 | target_include_directories( 18 | typescript_debugger 19 | PUBLIC ../../libs/imgui 20 | PUBLIC ../../libs/imgui-texteditor-fork 21 | PUBLIC /usr/local/include/ 22 | PUBLIC ../../libs/imgui/backends 23 | PUBLIC ../../libs/imgui/examples/libs/gl3w/ 24 | ) 25 | 26 | #if (TRACY_ENABLE) 27 | # target_sources( 28 | # typescript_debugger 29 | # PUBLIC ../../libs/imgui/examples/libs/gl3w/GL/gl3w.c 30 | # ) 31 | #endif() 32 | 33 | target_link_libraries(typescript_debugger typescript) 34 | 35 | find_package(OpenGL REQUIRED) 36 | find_package(SDL2 REQUIRED) 37 | 38 | target_include_directories( 39 | typescript_debugger 40 | PUBLIC ${SDL2_INCLUDE_DIRS} 41 | ) 42 | 43 | if (APPLE) 44 | target_link_libraries(typescript_debugger "-framework CoreFoundation") 45 | endif () 46 | 47 | target_link_libraries( 48 | typescript_debugger 49 | ${SDL2_LIBRARIES} 50 | ${OPENGL_LIBRARIES} 51 | ) -------------------------------------------------------------------------------- /src/gui/app.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "imgui.h" 4 | #include "imgui_impl_sdl.h" 5 | #include "imgui_impl_opengl3.h" 6 | #include 7 | #include 8 | #include 9 | 10 | #if defined(IMGUI_IMPL_OPENGL_ES2) 11 | #include 12 | #else 13 | #include 14 | #endif 15 | 16 | using namespace std; 17 | 18 | namespace tr::gui { 19 | class App { 20 | public: 21 | int displayWidth = 20; 22 | int displayHeight = 20; 23 | string title = "App"; 24 | }; 25 | 26 | SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); 27 | SDL_Window* window = nullptr; 28 | SDL_GLContext gl_context; 29 | 30 | bool running = true; 31 | App guiApp; 32 | function renderer; 33 | ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); 34 | ImFontConfig cfg; 35 | 36 | void mainLoop(void *arg) { 37 | // Poll and handle events (inputs, window resize, etc.) 38 | // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. 39 | // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy of the mouse data. 40 | // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your copy of the keyboard data. 41 | // Generally you may always pass all inputs to dear imgui, and hide them from your application based on those two flags. 42 | SDL_Event event; 43 | while (SDL_PollEvent(&event)) 44 | { 45 | ImGui_ImplSDL2_ProcessEvent(&event); 46 | if (event.type == SDL_QUIT) 47 | running = false; 48 | if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) 49 | running = false; 50 | } 51 | 52 | ImGuiIO &io = ImGui::GetIO(); 53 | guiApp.displayWidth = (int) io.DisplaySize.x; 54 | guiApp.displayHeight = (int) io.DisplaySize.y; 55 | // Start the Dear ImGui frame 56 | ImGui_ImplOpenGL3_NewFrame(); 57 | ImGui_ImplSDL2_NewFrame(); 58 | ImGui::NewFrame(); 59 | 60 | // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). 61 | //ImGui::ShowDemoWindow(&show_demo_window); 62 | renderer(); 63 | 64 | // Rendering 65 | ImGui::Render(); 66 | glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y); 67 | glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); 68 | glClear(GL_COLOR_BUFFER_BIT); 69 | ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); 70 | SDL_GL_SwapWindow(window); 71 | } 72 | 73 | void guiAppInit() { 74 | // Setup SDL 75 | // (Some versions of SDL before <2.0.10 appears to have performance/stalling issues on a minority of Windows systems, 76 | // depending on whether SDL_INIT_GAMECONTROLLER is enabled or disabled.. updating to latest version of SDL is recommended!) 77 | if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) 78 | { 79 | printf("Error: %s\n", SDL_GetError()); 80 | return; 81 | } 82 | 83 | // Decide GL+GLSL versions 84 | #if defined(IMGUI_IMPL_OPENGL_ES2) 85 | // GL ES 2.0 + GLSL 100 86 | const char* glsl_version = "#version 100"; 87 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); 88 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); 89 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); 90 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 91 | #elif defined(__APPLE__) 92 | // GL 3.2 Core + GLSL 150 93 | const char* glsl_version = "#version 150"; 94 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG); // Always required on Mac 95 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 96 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 97 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); 98 | #else 99 | // GL 3.0 + GLSL 130 100 | const char* glsl_version = "#version 130"; 101 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); 102 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); 103 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); 104 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); 105 | #endif 106 | 107 | // Create window with graphics context 108 | SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); 109 | SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); 110 | SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 8); 111 | window = SDL_CreateWindow(guiApp.title.c_str(), SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 1280, 720, window_flags); 112 | gl_context = SDL_GL_CreateContext(window); 113 | SDL_GL_MakeCurrent(window, gl_context); 114 | SDL_GL_SetSwapInterval(1); // Enable vsync 115 | 116 | // Setup Dear ImGui context 117 | IMGUI_CHECKVERSION(); 118 | ImGui::CreateContext(); 119 | ImGuiIO& io = ImGui::GetIO(); 120 | (void)io; 121 | //io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls 122 | //io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls 123 | 124 | auto scale = 2; 125 | cfg.SizePixels = 13.0f * scale; 126 | io.Fonts->AddFontDefault(&cfg); 127 | io.FontGlobalScale = 1.0f / scale; 128 | 129 | // Setup Dear ImGui style 130 | ImGui::StyleColorsDark(); 131 | //ImGui::StyleColorsClassic(); 132 | 133 | // Setup Platform/Renderer backends 134 | ImGui_ImplSDL2_InitForOpenGL(window, gl_context); 135 | ImGui_ImplOpenGL3_Init(glsl_version); 136 | 137 | // Load Fonts 138 | // - If no fonts are loaded, dear imgui will use the default font. You can also load multiple fonts and use ImGui::PushFont()/PopFont() to select them. 139 | // - AddFontFromFileTTF() will return the ImFont* so you can store it if you need to select the font among multiple. 140 | // - If the file cannot be loaded, the function will return NULL. Please handle those errors in your application (e.g. use an assertion, or display an error and quit). 141 | // - The fonts will be rasterized at a given size (w/ oversampling) and stored into a texture when calling ImFontAtlas::Build()/GetTexDataAsXXXX(), which ImGui_ImplXXXX_NewFrame below will call. 142 | // - Read 'docs/FONTS.md' for more instructions and details. 143 | // - Remember that in C/C++ if you want to include a backslash \ in a string literal you need to write a double backslash \\ ! 144 | //io.Fonts->AddFontDefault(); 145 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Roboto-Medium.ttf", 16.0f); 146 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f); 147 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/DroidSans.ttf", 16.0f); 148 | //io.Fonts->AddFontFromFileTTF("../../misc/fonts/ProggyTiny.ttf", 10.0f); 149 | //ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, NULL, io.Fonts->GetGlyphRangesJapanese()); 150 | //IM_ASSERT(font != NULL); 151 | } 152 | 153 | void guiAppRender(function r) { 154 | renderer = r; 155 | #if defined(__EMSCRIPTEN__) 156 | emscripten_set_main_loop_arg(mainLoop, nullptr, 0, true); 157 | #else 158 | while (running) { 159 | mainLoop(nullptr); 160 | } 161 | #endif 162 | } 163 | } -------------------------------------------------------------------------------- /src/gui/graph/LICENSE: -------------------------------------------------------------------------------- 1 | https://github.com/gml4gtk/demekgraph 2 | 3 | Copyright 2020 Miroslav Demek 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /src/gui/graph/crossing.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "layering.hpp" 9 | #include "utils.hpp" 10 | #include "report.hpp" 11 | 12 | namespace detail { 13 | 14 | // ---------------------------------------------------------------------------------------------- 15 | // ------------------------------- COUNTING CROSSINGS ----------------------------------------- 16 | // ---------------------------------------------------------------------------------------------- 17 | 18 | /** 19 | * Counts the number of crossings between edges indident on and 20 | * if would be to the left of . 21 | * Takes into account both the ingoing and outgoing edges. 22 | * 23 | * and need to be on the same layer 24 | */ 25 | int crossing_number(const hierarchy& h, vertex_t u, vertex_t v) { 26 | int count = 0; 27 | for (auto out_u : h.g.out_neighbours(u)) { 28 | for (auto out_v : h.g.out_neighbours(v)) { 29 | if (h.pos[out_v] < h.pos[out_u]) { 30 | ++count; 31 | } 32 | } 33 | } 34 | for (auto in_u : h.g.in_neighbours(u)) { 35 | for (auto in_v : h.g.in_neighbours(v)) { 36 | if (h.pos[in_v] < h.pos[in_u]) { 37 | ++count; 38 | } 39 | } 40 | } 41 | return count; 42 | } 43 | 44 | // counts the number of crossings between layers with index 'layer' and 'layer - 1' 45 | int count_layer_crossings(const hierarchy& h, int layer) { 46 | const std::vector& upper = h.layers[layer - 1]; 47 | int count = 0; 48 | 49 | for (int i = 0; i < upper.size() - 1; ++i) { 50 | for ( auto u : h.g.out_neighbours(upper[i]) ) { 51 | int j = h.pos[u]; 52 | for (int s = i + 1; s < upper.size(); ++s) { 53 | for ( auto v : h.g.out_neighbours(upper[s]) ) { 54 | int t = h.pos[v]; 55 | if ( (s > i && t < j) || (s < i && t > j) ) { 56 | ++count; 57 | } 58 | } 59 | } 60 | } 61 | } 62 | 63 | return count; 64 | } 65 | 66 | 67 | // counts the total number of crossings in the hierarchy 68 | int count_crossings(const hierarchy& h) { 69 | int count = 0; 70 | for (int i = 1; i < h.size(); ++i) { 71 | count += count_layer_crossings(h, i); 72 | } 73 | return count; 74 | } 75 | 76 | 77 | // ---------------------------------------------------------------------------------------------- 78 | // ------------------------------- CROSSING REDUCTION ----------------------------------------- 79 | // ---------------------------------------------------------------------------------------------- 80 | 81 | /** 82 | * Interface for a crossing reduction algorithm. 83 | * It reorders each layer of the hierarchy to avoid crossings. 84 | */ 85 | struct crossing_reduction { 86 | 87 | /** 88 | * Executes the algorithm. 89 | * It should leave h in a consistent state - ranking, layers and pos all agree with each other. 90 | */ 91 | virtual void run(hierarchy& h) = 0; 92 | virtual ~crossing_reduction() = default; 93 | }; 94 | 95 | 96 | /** 97 | * Barycentric heurictic for crossing reduction. 98 | * Vertices on each layer are order based on their barycenters - the average position of their neighbours. 99 | */ 100 | class barycentric_heuristic : public crossing_reduction { 101 | unsigned random_iters = 1; 102 | unsigned forgiveness = 7; 103 | bool trans = true; 104 | 105 | std::mt19937 mt; 106 | 107 | vertex_map best_order; 108 | int min_cross; 109 | 110 | public: 111 | barycentric_heuristic() = default; 112 | barycentric_heuristic(int rnd_iters, int max_fails, bool do_transpose) 113 | : random_iters(rnd_iters), forgiveness(max_fails), trans(do_transpose) {} 114 | 115 | void run(hierarchy& h) override { 116 | #ifdef REPORTING 117 | report::base = min_cross; 118 | report::random_runs = max_iters; 119 | report::forgivness = forgiveness; 120 | report::transpose = trans; 121 | report::random_final.clear(); 122 | report::iters = 0; 123 | #endif 124 | 125 | min_cross = init_order(h); 126 | best_order = h.pos; 127 | int base = min_cross; 128 | for (int i = 0; i < random_iters; ++i) { 129 | reduce(h, base); 130 | 131 | if (i != random_iters - 1) { 132 | for (auto& l : h.layers) { 133 | std::shuffle(l.begin(), l.end(), mt); 134 | } 135 | h.update_pos(); 136 | base = init_order(h); 137 | } 138 | } 139 | 140 | for (auto u : h.g.vertices()) { 141 | h.layer(u)[ best_order[u] ] = u; 142 | } 143 | h.update_pos(); 144 | 145 | #ifdef REPORTING 146 | report::final = min_cross; 147 | #endif 148 | } 149 | 150 | int init_order(hierarchy& h) { 151 | barycenter(h, 0); 152 | return count_crossings(h); 153 | } 154 | 155 | private: 156 | // attempts to reduce the number of crossings 157 | void reduce(hierarchy& h, int local_min) { 158 | auto local_order = h.pos; 159 | int fails = 0; 160 | 161 | for (int i = 0; ; ++i) { 162 | 163 | barycenter(h, i); 164 | 165 | if (trans) { 166 | #ifdef FAST 167 | fast_transpose(h); 168 | #else 169 | transpose(h); 170 | #endif 171 | } 172 | 173 | int cross = count_crossings(h); 174 | //std::cout << cross << "\n"; 175 | if (cross < local_min) { 176 | fails = 0; 177 | local_order = h.pos; 178 | local_min = cross; 179 | } else { 180 | fails++; 181 | } 182 | 183 | if (fails >= forgiveness) { 184 | break; 185 | } 186 | } 187 | 188 | if (local_min < min_cross) { 189 | best_order = local_order; 190 | min_cross = local_min; 191 | } 192 | 193 | #ifdef REPORTING 194 | report::iters += i; 195 | report::random_final.push_back(local_min); 196 | #endif 197 | } 198 | 199 | void barycenter(hierarchy& h, int i) { 200 | vertex_map weights(h.g); 201 | 202 | if (i % 2 == 0) { // top to bottom 203 | for (int j = 1; j < h.size(); ++j) { 204 | reorder_layer(h, weights, j, true); 205 | } 206 | } else { // from bottom up 207 | for (int j = h.size() - 2; j >= 0; --j) { 208 | reorder_layer(h, weights, j, false); 209 | } 210 | } 211 | } 212 | 213 | // reorders vertices on a layer 'i' based on their weights 214 | void reorder_layer(hierarchy& h, vertex_map& weights, int i, bool downward) { 215 | auto& layer = h.layers[i]; 216 | for (vertex_t u : layer) { 217 | weights[u] = weight( h.pos, u, downward ? h.g.in_neighbours(u) : h.g.out_neighbours(u) ); 218 | } 219 | std::sort(layer.begin(), layer.end(), [&weights] (const auto& u, const auto& v) { 220 | return weights[u] < weights[v]; 221 | }); 222 | 223 | h.update_pos(); 224 | } 225 | 226 | // calculates the weight of vertex as an average of the positions of its neighbour 227 | template 228 | float weight(const vertex_map& positions, vertex_t u, const T& neighbours) { 229 | unsigned count = 0; 230 | unsigned sum = 0; 231 | for (auto v : neighbours) { 232 | sum += positions[v]; 233 | count++; 234 | } 235 | if (count == 0) { 236 | return positions[u]; 237 | } 238 | return sum / (float)count; 239 | } 240 | 241 | // Heuristic for reducing crossings which repeatedly attempts to swap all ajacent vertices. 242 | void transpose(hierarchy& h) { 243 | bool improved = true; 244 | int k = 0; 245 | while (improved) { 246 | improved = false; 247 | 248 | for (auto& layer : h.layers) { 249 | for (int i = 0; i < layer.size() - 1; ++i) { 250 | int old = crossing_number(h, layer[i], layer[i + 1]); 251 | int next = crossing_number(h, layer[i + 1], layer[i]); 252 | 253 | if ( old > next ) { 254 | improved = true; 255 | h.swap(layer[i], layer[i + 1]); 256 | } 257 | } 258 | } 259 | k++; 260 | } 261 | } 262 | 263 | void fast_transpose(hierarchy& h) { 264 | //std::cout << h << "\n"; 265 | vertex_map eligible(h.g, true); 266 | 267 | bool improved = true; 268 | int iters = 0; 269 | while (improved) { 270 | iters++; 271 | improved = false; 272 | for (auto& layer : h.layers) { 273 | assert(layer.size() >= 1); 274 | for (int i = 0; i < layer.size() - 1; ++i) { 275 | if (eligible.at( layer[i] )) { 276 | int old = crossing_number(h, layer[i], layer[i + 1]); 277 | int next = crossing_number(h, layer[i + 1], layer[i]); 278 | int diff = old - next; 279 | 280 | if ( diff > 0 ) { 281 | improved = true; 282 | h.swap(layer[i], layer[i + 1]); 283 | 284 | if (i > 0) eligible.set( layer[i - 1], true ); 285 | eligible.set( layer[i + 1], true ); 286 | 287 | for (auto u : h.g.neighbours(layer[i])) { 288 | eligible.set( u, true ); 289 | } 290 | for (auto u : h.g.neighbours(layer[i+1])) { 291 | eligible.set( u, true ); 292 | } 293 | } 294 | eligible.set( layer[i], false ); 295 | } 296 | } 297 | } 298 | } 299 | } 300 | 301 | }; 302 | 303 | } // namespace detail -------------------------------------------------------------------------------- /src/gui/graph/cycle.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "subgraph.hpp" 7 | 8 | namespace detail { 9 | 10 | /** 11 | * Holds the edges that were reversed to remove cycles. 12 | * This struct contains the edges in their reversed form 13 | * i.e. if edge (u, v) cuased a cycle, it is saved as (v, u) 14 | */ 15 | struct feedback_set { 16 | edge_set reversed; 17 | edge_set removed; 18 | std::vector loops; 19 | }; 20 | 21 | 22 | /** 23 | * Interface for a cycle removal algorithm. 24 | */ 25 | struct cycle_removal { 26 | 27 | /** 28 | * Modifies the input graph by reversing certain edges to remove cycles. 29 | * 30 | * @return the reversed edges 31 | */ 32 | virtual feedback_set run(subgraph& g) = 0; 33 | 34 | virtual ~cycle_removal() = default; 35 | }; 36 | 37 | 38 | /** 39 | * Algorithm for removing cycles in a graph using depth first search. 40 | */ 41 | class dfs_removal : public cycle_removal { 42 | 43 | enum class state : char { done, in_progress, unvisited }; 44 | vertex_map marks; 45 | 46 | public: 47 | feedback_set run(subgraph& g) override { 48 | marks.init(g, state::unvisited); 49 | 50 | // find edges participating in cycles 51 | std::vector to_reverse; 52 | std::vector to_remove; 53 | for (auto u : g.vertices()) { 54 | if (marks[u] == state::unvisited) { 55 | dfs(g, u, to_reverse, to_remove); 56 | } 57 | } 58 | 59 | // remove or reverse the edges 60 | feedback_set reversed_edges; 61 | 62 | for (auto e : to_remove) { 63 | g.remove_edge(e); 64 | if (e.from == e.to) 65 | reversed_edges.loops.push_back(e.from); 66 | else 67 | reversed_edges.removed.insert(reversed(e)); 68 | } 69 | 70 | for (auto e : to_reverse) { 71 | g.remove_edge(e); 72 | g.add_edge(reversed(e)); 73 | reversed_edges.reversed.insert(reversed(e)); 74 | } 75 | 76 | return reversed_edges; 77 | } 78 | 79 | private: 80 | 81 | void dfs(subgraph& g, vertex_t u, std::vector& to_reverse, std::vector& to_remove) { 82 | marks[u] = state::in_progress; 83 | 84 | for (auto v : g.out_neighbours(u)) { 85 | if (u == v) { // a loop 86 | to_remove.push_back( {u, u} ); 87 | } else if (marks[v] == state::in_progress) { // there is a cycle 88 | if (g.has_edge(v, u)) { // two-cycle 89 | to_remove.push_back( {u, v} ); 90 | } else { // regular cycle 91 | to_reverse.push_back( {u, v} ); 92 | } 93 | } else if (marks[v] == state::unvisited) { 94 | dfs(g, v, to_reverse, to_remove); 95 | } 96 | } 97 | 98 | marks[u] = state::done; 99 | } 100 | }; 101 | 102 | } //namespace detail 103 | -------------------------------------------------------------------------------- /src/gui/graph/graph.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "utils.hpp" 9 | #include "types.hpp" 10 | 11 | /** 12 | * Basic clas for representing a directed graph. 13 | */ 14 | class graph { 15 | public: 16 | std::vector nodeSizes; 17 | 18 | /** 19 | * Add a new vertex to the graph. 20 | * 21 | * @return the identifier of the vertex 22 | */ 23 | vertex_t add_node(float size = 0) { 24 | nodeSizes.push_back(size); 25 | m_out_neighbours.emplace_back(); 26 | m_in_neighbours.emplace_back(); 27 | return m_out_neighbours.size() - 1; 28 | } 29 | 30 | /** 31 | * Add a new edge to the graph. 32 | * 33 | * The behavious is undefined if the same edge is added twice 34 | * or if an identifier other then one returned by add_node() is used. 35 | * 36 | * @param from the identifier of the starting vertex 37 | * @param to the identifier of the ending vertex 38 | * 39 | * @return a reference to the graph for chaining multiple calls 40 | */ 41 | graph& add_edge(vertex_t from, vertex_t to) { 42 | m_out_neighbours[from].push_back(to); 43 | m_in_neighbours[to].push_back(from); 44 | return *this; 45 | } 46 | 47 | /** 48 | * Get the number of vertices in the graph. 49 | * 50 | * @return the number of vertices 51 | */ 52 | unsigned size() const { return m_out_neighbours.size(); } 53 | 54 | /** 55 | * Get an immutable list of all successors. 56 | */ 57 | const std::vector& out_neighbours(vertex_t u) const { return m_out_neighbours[u]; } 58 | /** 59 | * Get an immutable list of all predecessors. 60 | */ 61 | const std::vector& in_neighbours(vertex_t u) const { return m_in_neighbours[u]; } 62 | 63 | /** 64 | * Get an implementation defined object which can be used for iterating through the vertices. 65 | * 66 | * The returned object can be used in a range for loop. 67 | * It provides begin() and end() methods which yield forward iterators for iterating through the vertices. 68 | */ 69 | range vertices() const { return range(0, size(), 1); } 70 | 71 | /** 72 | * Remove the given edge. 73 | * Slow operation - should be avoided if possible. 74 | */ 75 | void remove_edge(vertex_t from, vertex_t to) { 76 | remove_neighour(m_out_neighbours[from], to); 77 | remove_neighour(m_in_neighbours[to], from); 78 | } 79 | 80 | friend std::ostream& operator<<(std::ostream& out, const graph& g) { 81 | for (auto u : g.vertices()) { 82 | out << u << ": ["; 83 | 84 | const char* sep = ""; 85 | for (auto v : g.out_neighbours(u)) { 86 | out << sep << v; 87 | sep = ", "; 88 | } 89 | 90 | out << "]\n"; 91 | } 92 | return out; 93 | } 94 | 95 | private: 96 | std::vector< std::vector > m_out_neighbours; 97 | std::vector< std::vector > m_in_neighbours; 98 | 99 | void remove_neighour(std::vector& neighbours, vertex_t u) { 100 | auto it = std::find(neighbours.begin(), neighbours.end(), u); 101 | if (it != neighbours.end()) { 102 | neighbours.erase(it); 103 | } 104 | } 105 | 106 | }; 107 | 108 | /** 109 | * Builder class for creating graphs by hand. 110 | * 111 | * Should be used with care since the node identifiers are chosen by the caller. 112 | * The chosen identifiers should be a consecutive sequence of numbers [0, n-1] where n is the number of vertices in the graph. 113 | * Otherwise, the unused identifiers in the range [0, max_id] will be added into the graph as vertices 114 | * without any edges which is probably not what you want. 115 | * 116 | * For example if edges (0,2), (2,3), (0,3) are added, 117 | * the resulting graph will contain an aditional vertex with identifier 1. 118 | */ 119 | struct graph_builder { 120 | graph g; 121 | 122 | /** 123 | * Add a new edges with the given identifiers. 124 | */ 125 | graph_builder& add_edge(vertex_t u, vertex_t v) { 126 | add_vertex(u); 127 | add_vertex(v); 128 | g.add_edge(u, v); 129 | return *this; 130 | } 131 | 132 | /** 133 | * Get the resulting graph. 134 | */ 135 | graph build() { return g; } 136 | 137 | private: 138 | void add_vertex(vertex_t u) { 139 | while (u >= g.size()) { 140 | g.add_node(); 141 | } 142 | } 143 | }; 144 | -------------------------------------------------------------------------------- /src/gui/graph/interface.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "graph.hpp" 4 | #include "layout.hpp" 5 | #include "types.hpp" 6 | -------------------------------------------------------------------------------- /src/gui/graph/layout.hpp: -------------------------------------------------------------------------------- 1 | #ifndef LAYOUT_HPP 2 | #define LAYOUT_HPP 3 | 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include "interface.hpp" 10 | #include "subgraph.hpp" 11 | 12 | #include "cycle.hpp" 13 | #include "layering.hpp" 14 | #include "positioning.hpp" 15 | #include "crossing.hpp" 16 | #include "router.hpp" 17 | 18 | #ifdef CONTROL_CROSSING 19 | bool crossing_enabled = true; 20 | #endif 21 | 22 | 23 | class sugiyama_layout { 24 | public: 25 | sugiyama_layout(graph g) : g(g), original_vertex_count(g.size()) { build(); } 26 | 27 | sugiyama_layout(graph g, attributes attr) 28 | : g(g) 29 | , original_vertex_count(g.size()) 30 | , attrs(attr) { build(); } 31 | 32 | /** 33 | * Returns the positions and sizes of all the vertices in the graph. 34 | */ 35 | const std::vector& vertices() const { return nodes; } 36 | 37 | /** 38 | * Returns the control points for all the edges in the graph. 39 | */ 40 | const std::vector& edges() const { return paths; } 41 | 42 | float width() const { return size.x; } 43 | float height() const { return size.y; } 44 | vec2 dimensions() const { return size; } 45 | 46 | const attributes& attribs() const { return attrs; } 47 | 48 | private: 49 | graph g; 50 | unsigned original_vertex_count; 51 | 52 | detail::vertex_map boxes; 53 | 54 | // the final positions of vertices and control points of edges 55 | std::vector< node > nodes; 56 | std::vector< path > paths; 57 | vec2 size = { 0, 0 }; 58 | 59 | // attributes controling spacing 60 | attributes attrs; 61 | 62 | // algorithms for individual steps of sugiyama framework 63 | std::unique_ptr< detail::cycle_removal > cycle_module = 64 | std::make_unique< detail::dfs_removal >(); 65 | 66 | std::unique_ptr< detail::layering > layering_module = 67 | std::make_unique< detail::network_simplex_layering >(); 68 | 69 | std::unique_ptr< detail::crossing_reduction > crossing_module = 70 | std::make_unique< detail::barycentric_heuristic >(); 71 | 72 | std::unique_ptr< detail::positioning > positioning_module = 73 | std::make_unique< detail::fast_and_simple_positioning >(attrs, nodes, boxes); 74 | 75 | std::unique_ptr< detail::edge_router > routing_module = 76 | std::make_unique< detail::router >(nodes, paths, attrs); 77 | 78 | 79 | void build() { 80 | std::vector< detail::subgraph > subgraphs = detail::split(g); 81 | init_nodes(); 82 | 83 | vec2 start { 0, 0 }; 84 | for (auto& g : subgraphs) { 85 | vec2 dim = process_subgraph(g, start); 86 | start.x += dim.x + attrs.node_dist; 87 | size.x += dim.x + attrs.node_dist; 88 | size.y = std::max(size.y, dim.y); 89 | } 90 | 91 | size.x -= attrs.node_dist; 92 | nodes.resize(original_vertex_count); 93 | } 94 | 95 | 96 | vec2 process_subgraph(detail::subgraph& g, vec2 start) { 97 | 98 | auto reversed_edges = cycle_module->run(g); 99 | detail::hierarchy h = layering_module->run(g); 100 | 101 | auto long_edges = add_dummy_nodes(h); 102 | update_reversed_edges(reversed_edges, long_edges); 103 | update_dummy_nodes(); 104 | 105 | #ifdef CONTROL_CROSSING 106 | if (crossing_enabled) { 107 | crossing->run(h); 108 | } 109 | #else 110 | crossing_module->run(h); 111 | #endif 112 | enlarge_loop_boxes(reversed_edges); 113 | 114 | vec2 dimensions = positioning_module->run(h, start); 115 | 116 | routing_module->run(h, reversed_edges); 117 | 118 | return dimensions; 119 | } 120 | 121 | void update_dummy_nodes() { 122 | boxes.resize(g, { {0, 0}, { 0, 0} }); 123 | 124 | auto i = nodes.size(); 125 | nodes.resize(g.size()); 126 | for (; i < nodes.size(); ++i) { 127 | nodes[i].u = i; 128 | nodes[i].size = 0; 129 | } 130 | } 131 | 132 | 133 | void enlarge_loop_boxes(const detail::feedback_set& r) { 134 | for (auto u : r.loops) { 135 | boxes[u].size.x += attrs.loop_size; 136 | } 137 | } 138 | 139 | 140 | void init_nodes() { 141 | nodes.resize( g.size() ); 142 | boxes.resize( g ); 143 | for ( auto u : g.vertices() ) { 144 | nodes[u].u = u; 145 | nodes[u].size = g.nodeSizes[u];// || attrs.default_node_size; 146 | boxes[u] = { { 2*nodes[u].size, 2*nodes[u].size }, 147 | { nodes[u].size, nodes[u].size } }; 148 | } 149 | } 150 | 151 | 152 | /** 153 | * Checks if any of the reversed edges have been split into a path. 154 | * For each such edge (u, v) saves the first vertex on the path 155 | * from 'u' to 'v' instead of 'v'. 156 | * When reversing the path back, the rest of the path can be easily determined by folowing the dummy nodes 157 | * until the first non-dummy node is reached. 158 | */ 159 | void update_reversed_edges(detail::feedback_set& reversed_edges, const std::vector< detail::long_edge >& long_edges) { 160 | for (const auto& elem : long_edges) { 161 | if (reversed_edges.reversed.remove(elem.orig)) { 162 | reversed_edges.reversed.insert(elem.path[0], elem.path[1]); 163 | } else if (reversed_edges.removed.remove(elem.orig)) { 164 | reversed_edges.removed.insert(elem.path[0], elem.path[1]); 165 | } 166 | } 167 | } 168 | }; 169 | 170 | 171 | #endif 172 | -------------------------------------------------------------------------------- /src/gui/graph/report.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef REPORTING 4 | 5 | namespace report { 6 | 7 | // network simplex 8 | namespace simplex { 9 | inline int iters = 0; 10 | } 11 | 12 | //namespace crossing { 13 | // crossing stuff 14 | inline int random_runs = -1; 15 | inline int forgivness = -1; 16 | inline bool transpose = false; 17 | 18 | inline int base = 0; 19 | inline int final = 0; 20 | inline int iters = 0; 21 | inline std::vector random_final; 22 | //} 23 | } 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/gui/graph/subgraph.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "utils.hpp" 8 | #include "graph.hpp" 9 | 10 | 11 | namespace detail { 12 | 13 | struct edge { 14 | vertex_t from, to; 15 | }; 16 | 17 | inline bool operator==(edge lhs, edge rhs) { return lhs.to == rhs.to && lhs.from == rhs.from; } 18 | inline bool operator!=(edge lhs, edge rhs) { return !(lhs == rhs); } 19 | 20 | inline edge reversed(edge e) { return {e.to, e.from}; } 21 | 22 | inline std::ostream& operator<<(std::ostream& out, edge e) { 23 | out << "(" << e.from << ", " << e.to << ")"; 24 | return out; 25 | } 26 | 27 | // ---------------------------------------------------------------------------------------------- 28 | // -------------------------------------- SUBGRAPH -------------------------------------------- 29 | // ---------------------------------------------------------------------------------------------- 30 | 31 | 32 | /** 33 | * Subgraph of a given graph. 34 | * 35 | * Vertices which are added after the construction are called dummy vertices. 36 | * They can be used to distinguis between the vertices of the original graph and vertices which were added for "algorithmic" purpouses. 37 | */ 38 | class subgraph { 39 | graph& m_source; 40 | std::vector< vertex_t > m_vertices; 41 | vertex_t m_dummy_border; 42 | 43 | public: 44 | subgraph(graph& g, std::vector< vertex_t > vertices) 45 | : m_source(g) 46 | , m_vertices(std::move(vertices)) { 47 | if (!m_vertices.empty()) { 48 | m_dummy_border = 1 + *std::max_element(m_vertices.begin(), m_vertices.end()); 49 | } else { 50 | m_dummy_border = 0; 51 | } 52 | } 53 | 54 | unsigned size() const { return m_vertices.size(); } 55 | 56 | void add_edge(edge e) { m_source.add_edge(e.from, e.to); } 57 | void add_edge(vertex_t u, vertex_t v) { add_edge( { u, v } ); } 58 | 59 | vertex_t add_dummy() { 60 | auto u = m_source.add_node(); 61 | m_vertices.push_back(u); 62 | return u; 63 | } 64 | 65 | bool is_dummy(vertex_t u) const { return u >= m_dummy_border; } 66 | 67 | void remove_edge(edge e) { m_source.remove_edge(e.from, e.to); } 68 | void remove_edge(vertex_t u, vertex_t v) { remove_edge( { u, v } ); } 69 | 70 | bool has_edge(edge e) const { 71 | auto out = out_neighbours(e.from); 72 | return std::find(out.begin(), out.end(), e.to) != out.end(); 73 | } 74 | bool has_edge(vertex_t u, vertex_t v) const { return has_edge( { u, v } ); } 75 | 76 | const std::vector& out_neighbours(vertex_t u) const { return m_source.out_neighbours(u); } 77 | const std::vector& in_neighbours(vertex_t u) const { return m_source.in_neighbours(u); } 78 | chain_range< std::vector > neighbours(vertex_t u) const { return { out_neighbours(u), in_neighbours(u) }; } 79 | 80 | vertex_t out_neighbour(vertex_t u, int i) const { return m_source.out_neighbours(u)[i]; } 81 | vertex_t in_neighbour(vertex_t u, int i) const { return m_source.in_neighbours(u)[i]; } 82 | 83 | unsigned out_degree(vertex_t u) const { return m_source.out_neighbours(u).size(); } 84 | unsigned in_deree(vertex_t u) const { return m_source.in_neighbours(u).size(); } 85 | 86 | const std::vector& vertices() const { return m_vertices; } 87 | vertex_t vertex(int i) const { return m_vertices[i]; } 88 | }; 89 | 90 | 91 | inline std::ostream& operator<<(std::ostream& out, const subgraph& g) { 92 | for (auto u : g.vertices()) { 93 | out << u << ": {"; 94 | const char* sep = ""; 95 | for (auto v : g.out_neighbours(u)) { 96 | out << sep << v; 97 | sep = ", "; 98 | } 99 | out << "}\n"; 100 | } 101 | return out; 102 | } 103 | 104 | 105 | // ---------------------------------------------------------------------------------------------- 106 | // -------------------------------------- SPLITING -------------------------------------------- 107 | // ---------------------------------------------------------------------------------------------- 108 | 109 | /** 110 | * Recursively assign u and all vertices reachable from u in the underlying undirected graph to the same component. 111 | */ 112 | inline void split(const graph& g, std::vector& done, std::vector& component, vertex_t u) { 113 | done[u] = true; 114 | component.push_back(u); 115 | for (auto v : g.out_neighbours(u)) { 116 | if (!done[v]) { 117 | split(g, done, component, v); 118 | } 119 | } 120 | for (auto v : g.in_neighbours(u)) { 121 | if (!done[v]) { 122 | split(g, done, component, v); 123 | } 124 | } 125 | } 126 | 127 | 128 | /** 129 | * Split the given graph into connected components represented by subgrapgs. 130 | */ 131 | inline std::vector split(graph& g) { 132 | std::vector< std::vector > components; 133 | std::vector< bool > done(g.size(), false); 134 | 135 | for (auto u : g.vertices()) { 136 | if (!done[u]) { 137 | components.emplace_back(); 138 | split(g, done, components.back(), u); 139 | } 140 | } 141 | 142 | std::vector subgraphs; 143 | for (auto component : components) { 144 | subgraphs.emplace_back(g, component); 145 | } 146 | 147 | return subgraphs; 148 | } 149 | 150 | 151 | // ---------------------------------------------------------------------------------------------- 152 | // ----------------------------------- VERTEX MAP --------------------------------------------- 153 | // ---------------------------------------------------------------------------------------------- 154 | 155 | /** 156 | * Maps vertices to objects of type T 157 | */ 158 | template< typename T > 159 | struct vertex_map { 160 | std::vector< T > data; 161 | 162 | vertex_map() = default; 163 | 164 | vertex_map(const graph& g) : data(g.size(), T{}) {} 165 | vertex_map(const graph& g, T val) : data(g.size(), val) {} 166 | 167 | vertex_map(const subgraph& g) : vertex_map(g, T{}) {} 168 | vertex_map(const subgraph& g, T val) { 169 | for (auto u : g.vertices()) { 170 | if (u >= data.size()) { 171 | data.resize(u + 1); 172 | } 173 | data[u] = val; 174 | } 175 | } 176 | 177 | void resize(const graph& g) { data.resize(g.size()); } 178 | void resize(const graph& g, T val) { data.resize(g.size(), val); } 179 | void resize(const subgraph& g) { resize(g, T{}); } 180 | void resize(const subgraph& g, T val) { 181 | for (auto u : g.vertices()) { 182 | if (u >= data.size()) { 183 | data.resize(u + 1); 184 | data[u] = val; 185 | } 186 | } 187 | } 188 | 189 | void init(const subgraph& g, T val) { 190 | for (auto u : g.vertices()) { 191 | if (u >= data.size()) { 192 | data.resize(u + 1); 193 | } 194 | data[u] = val; 195 | } 196 | } 197 | 198 | // only to be used with T = bool, because the operator[] doesnt work :( 199 | T at(vertex_t u) const { return data[u]; } 200 | void set(vertex_t u, T val) { data[u] = val; } 201 | 202 | T& operator[](vertex_t u) { return data[u]; } 203 | const T& operator[](vertex_t u) const { return data[u]; } 204 | 205 | void insert(vertex_t u, const T& val) { 206 | add_vertex(u); 207 | data[u] = val; 208 | } 209 | void insert(vertex_t u) { insert(u, T{}); } 210 | 211 | void add_vertex(vertex_t u) { 212 | if (u >= data.size()) { 213 | data.resize(u + 1); 214 | } 215 | } 216 | 217 | bool contains(vertex_t u) const { 218 | return u < data.size(); 219 | } 220 | 221 | void clear() { data.clear(); } 222 | }; 223 | 224 | 225 | struct edge_set { 226 | vertex_map< std::vector > data; 227 | 228 | bool contains(edge e) const { return contains(e.from, e.to); } 229 | bool contains(vertex_t u, vertex_t v) const { 230 | return data.contains(u) && std::find(data[u].begin(), data[u].end(), v) != data[u].end(); 231 | } 232 | 233 | void insert(edge e) { insert(e.from, e.to); } 234 | void insert(vertex_t u, vertex_t v) { 235 | data.add_vertex(u); 236 | data[u].push_back(v); 237 | } 238 | 239 | bool remove(edge e) { return remove(e.from, e.to); } 240 | bool remove(vertex_t u, vertex_t v) { 241 | if (!data.contains(u)) 242 | return false; 243 | auto it = std::find(data[u].begin(), data[u].end(), v); 244 | if (it == data[u].end()) 245 | return false; 246 | data[u].erase(it); 247 | return true; 248 | } 249 | }; 250 | 251 | #ifdef DEBUG_LABELS 252 | std::map debug_labels; 253 | #endif 254 | 255 | 256 | } //namespace detail 257 | -------------------------------------------------------------------------------- /src/gui/graph/types.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "vec2.hpp" 7 | 8 | using vertex_t = unsigned; 9 | 10 | /** 11 | * Object representing a vertex in the final layout. 12 | */ 13 | struct node { 14 | vertex_t u; /**< the corresponding vertex identifier */ 15 | vec2 pos; /**< the position in space */ 16 | float size; /**< the radius */ 17 | }; 18 | 19 | inline float biggestSize(const std::vector &nodes, const std::vector &layer) { 20 | auto n = *std::max_element(layer.begin(), layer.end(), 21 | [&nodes](auto a, auto b) { 22 | return nodes[a].size < nodes[b].size; 23 | }); 24 | return nodes[n].size; 25 | } 26 | 27 | /** 28 | * Object representing an edge in the final layout. 29 | */ 30 | struct path { 31 | vertex_t from, to; /**< the vertex identifiers of endpoints of the corresponding edge */ 32 | std::vector points; /**< control points of the poly-line representing the edge */ 33 | bool bidirectional = false; /**< is the edge bidirectional? */ 34 | }; 35 | 36 | /** 37 | * Contains the parameters of the desired graph layout. 38 | */ 39 | struct attributes { 40 | float default_node_size = 15; /**< radius of all nodes */ 41 | float node_dist = 10; /**< minimum distance between borders of 2 nodes */ 42 | float layer_dist = 30; /**< minimum distance between borders of nodes in 2 different layers */ 43 | float loop_angle = 55; /**< angle determining the point on the node where a loop connects to it */ 44 | float loop_size = 15; /**< distance which the loop extends from the node*/ 45 | }; 46 | -------------------------------------------------------------------------------- /src/gui/graph/utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "vec2.hpp" 7 | #include "types.hpp" 8 | 9 | 10 | template 11 | T sgn(T val) { return ( T(0) < val ) - ( val < T(0) ); } 12 | 13 | 14 | template 15 | struct range { 16 | T r_start; 17 | T r_end; 18 | int step; 19 | 20 | range(T start, T end, int step = 1) : r_start(start), r_end(end), step(step) {} 21 | 22 | struct iterator { 23 | T val; 24 | int step; 25 | iterator(T val, int step) : val(val), step(step) {} 26 | 27 | T operator*() { return val; } 28 | friend bool operator==(const iterator& lhs, const iterator& rhs) { return lhs.val == rhs.val; } 29 | friend bool operator!=(const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); } 30 | iterator& operator++() { val += step; return *this; } 31 | }; 32 | 33 | iterator begin() const { return iterator(r_start, step); } 34 | iterator end() const { return iterator(r_end, step); } 35 | }; 36 | 37 | 38 | template 39 | struct chain_range { 40 | const T& first; 41 | const T& second; 42 | 43 | chain_range(const T& first, const T& second) : first(first), second(second) {} 44 | 45 | struct iterator { 46 | using It = typename T::const_iterator; 47 | It curr; 48 | It st_end; 49 | It nd_beg; 50 | 51 | iterator(It curr, It st_end, It nd_beg) 52 | : curr(curr) 53 | , st_end(st_end) 54 | , nd_beg(nd_beg) 55 | { 56 | if (curr == st_end) { 57 | this->curr = nd_beg; 58 | } 59 | } 60 | 61 | typename T::value_type operator*() { return *curr; } 62 | friend bool operator==(const iterator& lhs, const iterator& rhs) { return lhs.curr == rhs.curr; } 63 | friend bool operator!=(const iterator& lhs, const iterator& rhs) { return !(lhs == rhs); } 64 | iterator& operator++() { 65 | if (++curr == st_end) { 66 | curr = nd_beg; 67 | } 68 | return *this; 69 | } 70 | }; 71 | 72 | iterator begin() { 73 | return iterator( std::begin(first), std::end(first), std::begin(second) ); 74 | } 75 | iterator begin() const { 76 | return iterator( std::begin(first), std::end(first), std::begin(second) ); 77 | } 78 | iterator end() { 79 | return iterator( std::end(second), std::end(first), std::begin(second) ); 80 | } 81 | iterator end() const { 82 | return iterator( std::end(second), std::end(first), std::begin(second) ); 83 | } 84 | }; 85 | 86 | 87 | template 88 | std::ostream& operator<<(std::ostream& out, const std::vector& vec) { 89 | const char* sep = ""; 90 | for (const auto& x : vec) { 91 | out << sep << x; 92 | sep = ", "; 93 | } 94 | return out; 95 | } 96 | -------------------------------------------------------------------------------- /src/gui/graph/vec2.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct vec2 { 8 | float x, y; 9 | }; 10 | 11 | inline std::ostream& operator<<(std::ostream& out, vec2 v) { 12 | out << "[" << v.x << ", " << v.y << "]"; 13 | return out; 14 | } 15 | 16 | template 17 | inline vec2 operator*(T a, vec2 vec) { return { a*vec.x, a*vec.y}; } 18 | template 19 | inline vec2 operator*(vec2 vec, T a) { return { a*vec.x, a*vec.y}; } 20 | 21 | inline bool operator==(vec2 lhs, vec2 rhs) { return rhs.x == lhs.x && rhs.y == lhs.y; } 22 | inline bool operator!=(vec2 lhs, vec2 rhs) { return !(lhs == rhs); } 23 | 24 | inline vec2 operator+(vec2 lhs, vec2 rhs) { return { lhs.x + rhs.x, lhs.y + rhs.y }; } 25 | inline vec2& operator+=(vec2& lhs, vec2 rhs) { 26 | lhs = lhs + rhs; 27 | return lhs; 28 | } 29 | 30 | inline vec2 operator-(vec2 v) { return -1*v; } 31 | inline vec2 operator-(vec2 lhs, vec2 rhs) { return { lhs.x - rhs.x, lhs.y - rhs.y }; } 32 | inline vec2& operator-=(vec2& lhs, vec2 rhs) { 33 | lhs = lhs - rhs; 34 | return lhs; 35 | } 36 | 37 | inline float dot(vec2 u, vec2 v) { return u.x*v.x + u.y*v.y; } 38 | inline float cross(vec2 u, vec2 v) { 39 | return u.x*v.y - u.y*v.x; 40 | } 41 | 42 | inline float magnitude(vec2 v) { return std::sqrt( dot(v, v) ); } 43 | inline float distance(vec2 u, vec2 v) { return magnitude( v - u ); } 44 | 45 | inline vec2 normalized(vec2 v) { return { v.x/magnitude(v), v.y/magnitude(v) }; } 46 | 47 | inline float to_radians(float deg) { return (M_PI*deg)/180; } 48 | inline float to_degrees(float rad) { return (180*rad)/M_PI; } 49 | 50 | inline vec2 rotate(vec2 vec, float deg) { 51 | float rad = to_radians(deg); 52 | float sin = std::sin(rad); 53 | float cos = std::cos(rad); 54 | return { vec.x*cos + vec.y*sin, -vec.x*sin + vec.y*cos }; 55 | } 56 | 57 | float line_point_dist(vec2 from, vec2 to, vec2 p) { 58 | auto v = to - from; 59 | auto w = p - from; 60 | 61 | float c1 = dot(w,v); 62 | float c2 = dot(v,v); 63 | float t = c1 / c2; 64 | 65 | auto x = from + t*v; 66 | return distance(p, x); 67 | } 68 | 69 | // get the first interesection 70 | std::optional line_circle_intersection(vec2 from, vec2 to, vec2 center, float r) { 71 | auto d = to - from; 72 | auto f = from - center; 73 | 74 | auto a = dot(d, d); 75 | auto b = 2*dot(f, d); 76 | auto c = dot(f, f) - r*r; 77 | 78 | float discriminant = b*b - 4*a*c; 79 | if (discriminant < 0) { 80 | return std::nullopt; 81 | } 82 | 83 | discriminant = sqrt(discriminant); 84 | float t1 = (-b - discriminant)/(2*a); 85 | float t2 = (-b + discriminant)/(2*a); 86 | 87 | if (t1 >= 0 && t1 <= 1) { 88 | return { from + t1*d }; 89 | } 90 | 91 | if (t2 >= 0 && t2 <= 1) { 92 | return { from + t2*d }; 93 | } 94 | 95 | return std::nullopt; 96 | } 97 | -------------------------------------------------------------------------------- /src/hash.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace tr::hash { 8 | 9 | //https://github.com/ekpyron/xxhashct 10 | struct xxh64 { 11 | static constexpr uint64_t hash(const char *p, uint64_t len, uint64_t seed) { 12 | return finalize((len >= 32 ? h32bytes(p, len, seed) : seed + PRIME5) + len, p + (len & ~0x1F), len & 0x1F); 13 | } 14 | private: 15 | static constexpr uint64_t PRIME1 = 11400714785074694791ULL; 16 | static constexpr uint64_t PRIME2 = 14029467366897019727ULL; 17 | static constexpr uint64_t PRIME3 = 1609587929392839161ULL; 18 | static constexpr uint64_t PRIME4 = 9650029242287828579ULL; 19 | static constexpr uint64_t PRIME5 = 2870177450012600261ULL; 20 | 21 | static constexpr uint64_t rotl(uint64_t x, int r) { 22 | return ((x << r) | (x >> (64 - r))); 23 | } 24 | static constexpr uint64_t mix1(const uint64_t h, const uint64_t prime, int rshift) { 25 | return (h ^ (h >> rshift)) * prime; 26 | } 27 | static constexpr uint64_t mix2(const uint64_t p, const uint64_t v = 0) { 28 | return rotl(v + p * PRIME2, 31) * PRIME1; 29 | } 30 | static constexpr uint64_t mix3(const uint64_t h, const uint64_t v) { 31 | return (h ^ mix2(v)) * PRIME1 + PRIME4; 32 | } 33 | #ifdef XXH64_BIG_ENDIAN 34 | static constexpr uint32_t endian32 (const char *v) { 35 | return uint32_t(uint8_t(v[3]))|(uint32_t(uint8_t(v[2]))<<8) 36 | |(uint32_t(uint8_t(v[1]))<<16)|(uint32_t(uint8_t(v[0]))<<24); 37 | } 38 | 39 | static constexpr uint64_t endian64 (const char *v) 40 | { 41 | return uint64_t(uint8_t(v[7]))|(uint64_t(uint8_t(v[6]))<<8) 42 | |(uint64_t(uint8_t(v[5]))<<16)|(uint64_t(uint8_t(v[4]))<<24) 43 | |(uint64_t(uint8_t(v[3]))<<32)|(uint64_t(uint8_t(v[2]))<<40) 44 | |(uint64_t(uint8_t(v[1]))<<48)|(uint64_t(uint8_t(v[0]))<<56); 45 | } 46 | #else 47 | static constexpr uint32_t endian32(const char *v) { 48 | return uint32_t(uint8_t(v[0])) | (uint32_t(uint8_t(v[1])) << 8) 49 | | (uint32_t(uint8_t(v[2])) << 16) | (uint32_t(uint8_t(v[3])) << 24); 50 | } 51 | static constexpr uint64_t endian64(const char *v) { 52 | return uint64_t(uint8_t(v[0])) | (uint64_t(uint8_t(v[1])) << 8) 53 | | (uint64_t(uint8_t(v[2])) << 16) | (uint64_t(uint8_t(v[3])) << 24) 54 | | (uint64_t(uint8_t(v[4])) << 32) | (uint64_t(uint8_t(v[5])) << 40) 55 | | (uint64_t(uint8_t(v[6])) << 48) | (uint64_t(uint8_t(v[7])) << 56); 56 | } 57 | #endif 58 | static constexpr uint64_t fetch64(const char *p, const uint64_t v = 0) { 59 | return mix2(endian64(p), v); 60 | } 61 | static constexpr uint64_t fetch32(const char *p) { 62 | return uint64_t(endian32(p)) * PRIME1; 63 | } 64 | static constexpr uint64_t fetch8(const char *p) { 65 | return uint8_t(*p) * PRIME5; 66 | } 67 | static constexpr uint64_t finalize(const uint64_t h, const char *p, uint64_t len) { 68 | return (len >= 8) ? (finalize(rotl(h ^ fetch64(p), 27) * PRIME1 + PRIME4, p + 8, len - 8)) : 69 | ((len >= 4) ? (finalize(rotl(h ^ fetch32(p), 23) * PRIME2 + PRIME3, p + 4, len - 4)) : 70 | ((len > 0) ? (finalize(rotl(h ^ fetch8(p), 11) * PRIME1, p + 1, len - 1)) : 71 | (mix1(mix1(mix1(h, PRIME2, 33), PRIME3, 29), 1, 32)))); 72 | } 73 | static constexpr uint64_t h32bytes(const char *p, uint64_t len, const uint64_t v1, const uint64_t v2, const uint64_t v3, const uint64_t v4) { 74 | return (len >= 32) ? h32bytes(p + 32, len - 32, fetch64(p, v1), fetch64(p + 8, v2), fetch64(p + 16, v3), fetch64(p + 24, v4)) : 75 | mix3(mix3(mix3(mix3(rotl(v1, 1) + rotl(v2, 7) + rotl(v3, 12) + rotl(v4, 18), v1), v2), v3), v4); 76 | } 77 | static constexpr uint64_t h32bytes(const char *p, uint64_t len, const uint64_t seed) { 78 | return h32bytes(p, len, seed + PRIME1 + PRIME2, seed + PRIME2, seed, seed - PRIME1); 79 | } 80 | }; 81 | 82 | inline consteval size_t constLength(const char *str) { 83 | return (*str == 0) ? 0 : constLength(str + 1) + 1; 84 | } 85 | 86 | inline size_t length(const char *str) { 87 | return (*str == 0) ? 0 : length(str + 1) + 1; 88 | } 89 | 90 | inline consteval uint64_t const_hash(const char *input) { 91 | return xxh64::hash(input, constLength(input), 0); 92 | } 93 | 94 | // inline uint64_t const_hash(const std::string &input) { 95 | // return const_hash(input.c_str()); 96 | // } 97 | 98 | inline uint64_t runtime_hash(const std::string &input) { 99 | return xxh64::hash(input.c_str(), input.size(), 0); 100 | } 101 | 102 | inline uint64_t runtime_hash(const std::string_view &input) { 103 | return xxh64::hash(input.begin(), input.size(), 0); 104 | } 105 | 106 | inline uint64_t runtime_hash(const char * input) { 107 | return xxh64::hash(input, length(input), 0); 108 | } 109 | 110 | inline consteval uint64_t operator ""_hash(const char *s, size_t size) { 111 | return xxh64::hash(s, size, 0); 112 | } 113 | }; -------------------------------------------------------------------------------- /src/parser2.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "parser2.h" 3 | 4 | namespace tr { 5 | SyntaxKind Parser::token() { 6 | return currentToken; 7 | } 8 | 9 | SyntaxKind Parser::nextTokenWithoutCheck() { 10 | return currentToken = scanner.scan(); 11 | } 12 | 13 | optional Parser::parseErrorAtPosition(int start, int length, const shared_ptr &message, DiagnosticArg arg) { 14 | ZoneScoped; 15 | 16 | auto lastError = lastOrUndefined(parseDiagnostics); 17 | // Don't report another error if it would just be at the same position as the last error. 18 | if (!lastError || start != lastError->start) { 19 | auto d = createDetachedDiagnostic(fileName, start, length, message, {arg}); 20 | parseDiagnostics.push_back(d); 21 | return d; 22 | } 23 | 24 | // Mark that we've encountered an error. We'll set an appropriate bit on the next 25 | // node we finish so that it can't be reused incrementally. 26 | parseErrorBeforeNextFinishedNode = true; 27 | 28 | return nullopt; 29 | } 30 | 31 | node Parser::countNode(const node &node) { 32 | nodeCount++; 33 | return node; 34 | } 35 | } -------------------------------------------------------------------------------- /src/path.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "core.h" 6 | #include "utf.h" 7 | 8 | namespace tr { 9 | using std::string; 10 | using std::replace; 11 | using std::regex; 12 | using tr::utf::CharCode; 13 | 14 | constexpr static auto directorySeparator = "/"; 15 | constexpr static auto altDirectorySeparator = "\\"; 16 | constexpr static auto urlSchemeSeparator = "://"; 17 | static const regex relativePathSegmentRegExp("/(?:\\/\\/)|(?:^|\\/)\\.\\.?(?:$|\\/)/"); 18 | 19 | 20 | bool fileExtensionIs(const string &path, const string &extension); 21 | 22 | bool fileExtensionIsOneOf(const string &path, const vector &extensions); 23 | 24 | bool fileExtensionIsOneOf(const string &path, const vector &extensions); 25 | 26 | /** 27 | * Normalize path separators, converting `\` into `/`. 28 | */ 29 | string normalizeSlashes(const string &path); 30 | 31 | //// Path Parsing 32 | bool isVolumeCharacter(const CharCode &charCode); 33 | 34 | int getFileUrlVolumeSeparatorEnd(const string &url, int start); 35 | 36 | /** 37 | * Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files"). 38 | * If the root is part of a URL, the twos-complement of the root length is returned. 39 | */ 40 | int getEncodedRootLength(const string &path); 41 | 42 | /** 43 | * Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files"). 44 | * 45 | * For example: 46 | * ```ts 47 | * getRootLength("a") === 0 // "" 48 | * getRootLength("/") === 1 // "/" 49 | * getRootLength("c:") === 2 // "c:" 50 | * getRootLength("c:d") === 0 // "" 51 | * getRootLength("c:/") === 3 // "c:/" 52 | * getRootLength("c:\\") === 3 // "c:\\" 53 | * getRootLength("//server") === 7 // "//server" 54 | * getRootLength("//server/share") === 8 // "//server/" 55 | * getRootLength("\\\\server") === 7 // "\\\\server" 56 | * getRootLength("\\\\server\\share") === 8 // "\\\\server\\" 57 | * getRootLength("file:///path") === 8 // "file:///" 58 | * getRootLength("file:///c:") === 10 // "file:///c:" 59 | * getRootLength("file:///c:d") === 8 // "file:///" 60 | * getRootLength("file:///c:/path") === 11 // "file:///c:/" 61 | * getRootLength("file://server") === 13 // "file://server" 62 | * getRootLength("file://server/path") === 14 // "file://server/" 63 | * getRootLength("http://server") === 13 // "http://server" 64 | * getRootLength("http://server/path") === 14 // "http://server/" 65 | * ``` 66 | */ 67 | int getRootLength(const string &path); 68 | 69 | /** 70 | * Determines whether a charCode corresponds to `/` or `\`. 71 | */ 72 | bool isAnyDirectorySeparator(const CharCode &charCode); 73 | 74 | bool hasTrailingDirectorySeparator(const string &path); 75 | 76 | string ensureTrailingDirectorySeparator(const string &path); 77 | 78 | /** 79 | * Combines paths. If a path is absolute, it replaces any previous path. Relative paths are not simplified. 80 | * 81 | * ```ts 82 | * // Non-rooted 83 | * combinePaths("path", "to", "file.ext") === "path/to/file.ext" 84 | * combinePaths("path", "dir", "..", "to", "file.ext") === "path/dir/../to/file.ext" 85 | * // POSIX 86 | * combinePaths("/path", "to", "file.ext") === "/path/to/file.ext" 87 | * combinePaths("/path", "/to", "file.ext") === "/to/file.ext" 88 | * // DOS 89 | * combinePaths("c:/path", "to", "file.ext") === "c:/path/to/file.ext" 90 | * combinePaths("c:/path", "c:/to", "file.ext") === "c:/to/file.ext" 91 | * // URL 92 | * combinePaths("file:///path", "to", "file.ext") === "file:///path/to/file.ext" 93 | * combinePaths("file:///path", "file:///to", "file.ext") === "file:///to/file.ext" 94 | * ``` 95 | */ 96 | string combinePaths(string path, const vector &paths); 97 | 98 | vector pathComponents(const string &path, int rootLength); 99 | 100 | /** 101 | * Parse a path into an array containing a root component (at index 0) and zero or more path 102 | * components (at indices > 0). The result is not normalized. 103 | * If the path is relative, the root component is `""`. 104 | * If the path is absolute, the root component includes the first path separator (`/`). 105 | * 106 | * ```ts 107 | * // POSIX 108 | * getPathComponents("/path/to/file.ext") === ["/", "path", "to", "file.ext"] 109 | * getPathComponents("/path/to/") === ["/", "path", "to"] 110 | * getPathComponents("/") === ["/"] 111 | * // DOS 112 | * getPathComponents("c:/path/to/file.ext") === ["c:/", "path", "to", "file.ext"] 113 | * getPathComponents("c:/path/to/") === ["c:/", "path", "to"] 114 | * getPathComponents("c:/") === ["c:/"] 115 | * getPathComponents("c:") === ["c:"] 116 | * // URL 117 | * getPathComponents("http://typescriptlang.org/path/to/file.ext") === ["http://typescriptlang.org/", "path", "to", "file.ext"] 118 | * getPathComponents("http://typescriptlang.org/path/to/") === ["http://typescriptlang.org/", "path", "to"] 119 | * getPathComponents("http://typescriptlang.org/") === ["http://typescriptlang.org/"] 120 | * getPathComponents("http://typescriptlang.org") === ["http://typescriptlang.org"] 121 | * getPathComponents("file://server/path/to/file.ext") === ["file://server/", "path", "to", "file.ext"] 122 | * getPathComponents("file://server/path/to/") === ["file://server/", "path", "to"] 123 | * getPathComponents("file://server/") === ["file://server/"] 124 | * getPathComponents("file://server") === ["file://server"] 125 | * getPathComponents("file:///path/to/file.ext") === ["file:///", "path", "to", "file.ext"] 126 | * getPathComponents("file:///path/to/") === ["file:///", "path", "to"] 127 | * getPathComponents("file:///") === ["file:///"] 128 | * getPathComponents("file://") === ["file://"] 129 | */ 130 | vector getPathComponents(const string &path, const string ¤tDirectory = ""); 131 | 132 | /** 133 | * Formats a parsed path consisting of a root component (at index 0) and zero or more path 134 | * segments (at indices > 0). 135 | * 136 | * ```ts 137 | * getPathFromPathComponents(["/", "path", "to", "file.ext"]) === "/path/to/file.ext" 138 | * ``` 139 | */ 140 | string getPathFromPathComponents(const vector &pathComponents); 141 | 142 | /** 143 | * Reduce an array of path components to a more simplified path by navigating any 144 | * `"."` or `".."` entries in the path. 145 | */ 146 | vector reducePathComponents(const vector &components); 147 | 148 | string normalizePath(string &_path); 149 | } 150 | -------------------------------------------------------------------------------- /src/pool_band.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | class PoolBand { 11 | public: 12 | union Slot { 13 | struct Pointer { 14 | Slot *prev; 15 | Slot *next; 16 | } pointer; 17 | }; 18 | 19 | typedef Slot *slot_pointer; 20 | 21 | size_t blockSize; 22 | 23 | explicit PoolBand(size_t blockSize = 1024 * 100): blockSize(blockSize) {} 24 | 25 | ~PoolBand() noexcept { 26 | slot_pointer curr = currentBlock; 27 | while (curr != nullptr) { 28 | slot_pointer prev = curr->pointer.prev; 29 | operator delete(reinterpret_cast(curr)); 30 | curr = prev; 31 | } 32 | } 33 | 34 | unsigned int active = 0; 35 | unsigned int blocks = 0; 36 | 37 | template 38 | T *allocate() { 39 | active++; 40 | auto size = sizeof(T); 41 | //std::cout<<"allocate "<end) { 43 | allocateBlock(); 44 | } 45 | auto res = reinterpret_cast(currentSlot); 46 | currentSlot += size; 47 | return res; 48 | } 49 | 50 | template 51 | T *construct(Args &&... args) { 52 | auto result = allocate(); 53 | new(result) T(std::forward(args)...); 54 | return result; 55 | } 56 | 57 | template 58 | void destruct(T *p) { 59 | p->~T(); 60 | //deallocate(p); 61 | } 62 | 63 | void clear() { 64 | active = 0; 65 | if (firstBlock) initializeBlock(firstBlock); 66 | } 67 | private: 68 | slot_pointer currentBlock = nullptr; 69 | slot_pointer firstBlock = nullptr; 70 | 71 | char *currentSlot = nullptr; 72 | char *end = nullptr; 73 | 74 | void allocateBlock() { 75 | if (currentBlock && reinterpret_cast(currentBlock)->pointer.next) { 76 | initializeBlock(reinterpret_cast(currentBlock)->pointer.next); 77 | } else { 78 | blocks++; 79 | // Allocate space for the new block and store a pointer to the previous one 80 | auto newBlock = reinterpret_cast(operator new(sizeof(Slot) + blockSize)); 81 | reinterpret_cast(newBlock)->pointer = {.prev = currentBlock, .next = nullptr}; 82 | setNextBlock(reinterpret_cast(newBlock)); 83 | } 84 | } 85 | 86 | void setNextBlock(slot_pointer nextBlock) { 87 | if (currentBlock) currentBlock->pointer.next = nextBlock; 88 | if (!firstBlock) firstBlock = nextBlock; 89 | initializeBlock(nextBlock); 90 | } 91 | 92 | void initializeBlock(slot_pointer nextBlock) { 93 | currentBlock = nextBlock; 94 | currentSlot = reinterpret_cast(nextBlock) + sizeof(Slot); 95 | end = currentSlot + blockSize; 96 | } 97 | }; -------------------------------------------------------------------------------- /src/string.h: -------------------------------------------------------------------------------- 1 | /** 2 | * A lighter string replacement. Since most nodes like Identifier/StringLiteral/NumberLiteral etc 3 | * do not need std::string with all its features, it's enough if we either have a string_view (reference) 4 | * to the SourceFile::text, or have a static string wrapper around const char *. 5 | */ 6 | namespace tr { 7 | 8 | struct simple_string { 9 | const char *text; 10 | int size; 11 | }; 12 | 13 | } -------------------------------------------------------------------------------- /src/syntax_cursor.cpp: -------------------------------------------------------------------------------- 1 | #include "syntax_cursor.h" 2 | 3 | using namespace tr; 4 | 5 | Node SyntaxCursor::currentNode(int position) { 6 | // Only compute the current node if the position is different than the last time 7 | // we were asked. The parser commonly asks for the node at the same position 8 | // twice. Once to know if can read an appropriate list element at a certain point, 9 | // and then to actually read and consume the node. 10 | // if (position != lastQueriedPosition) { 11 | // // Much of the time the parser will need the very next node in the array that 12 | // // we just returned a node from.So just simply check for that case and move 13 | // // forward in the array instead of searching for the node again. 14 | // if (current && current->end == position && currentArrayIndex < (currentArray->length() - 1)) { 15 | // currentArrayIndex++; 16 | // current = ¤tArray->list[currentArrayIndex]; 17 | // } 18 | // 19 | // // If we don't have a node, or the node we have isn't in the right position, 20 | // // then try to find a viable node at the position requested. 21 | // if (!current || current->pos != position) { 22 | // findHighestListElementThatStartsAtPosition(position); 23 | // } 24 | // } 25 | 26 | // Cache this query so that we don't do any extra work if the parser calls back 27 | // into us. Note: this is very common as the parser will make pairs of calls like 28 | // 'isListElement -> parseListElement'. If we were unable to find a node when 29 | // called with 'isListElement', we don't want to redo the work when parseListElement 30 | // is called immediately after. 31 | lastQueriedPosition = position; 32 | 33 | // Either we don'd have a node, or we have a node at the position being asked for. 34 | // assert(!current || current->pos == position); 35 | return *current; 36 | } 37 | 38 | void SyntaxCursor::findHighestListElementThatStartsAtPosition(int position) { 39 | // Clear out any cached state about the last node we found. 40 | // currentArray = nullptr; 41 | // currentArrayIndex = InvalidPosition::Value; 42 | // current = nullptr; 43 | // 44 | // // Recurse into the source file to find the highest node at this position. 45 | // forEachChild(sourceFile, visitNode, visitArray); 46 | // return; 47 | // 48 | // function visitNode(node: Node) { 49 | // if (position >= node.pos && position < node.end) { 50 | // // Position was within this node. Keep searching deeper to find the node. 51 | // forEachChild(node, visitNode, visitArray); 52 | // 53 | // // don't proceed any further in the search. 54 | // return true; 55 | // } 56 | // 57 | // // position wasn't in this node, have to keep searching. 58 | // return false; 59 | // } 60 | // 61 | // function visitArray(array: NodeArray) { 62 | // if (position >= array.pos && position < array.end) { 63 | // // position was in this array. Search through this array to see if we find a 64 | // // viable element. 65 | // for (let i = 0; i < array.length; i++) { 66 | // const child = array[i]; 67 | // if (child) { 68 | // if (child.pos === position) { 69 | // // Found the right node. We're done. 70 | // currentArray = array; 71 | // currentArrayIndex = i; 72 | // current = child; 73 | // return true; 74 | // } 75 | // else { 76 | // if (child.pos < position && position < child.end) { 77 | // // Position in somewhere within this child. Search in it and 78 | // // stop searching in this array. 79 | // forEachChild(child, visitNode, visitArray); 80 | // return true; 81 | // } 82 | // } 83 | // } 84 | // } 85 | // } 86 | // 87 | // // position wasn't in this array, have to keep searching. 88 | // return false; 89 | // } 90 | } 91 | -------------------------------------------------------------------------------- /src/syntax_cursor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "types.h" 5 | 6 | using namespace std; 7 | 8 | namespace tr { 9 | 10 | enum InvalidPosition { 11 | Value = -1 12 | }; 13 | 14 | //not used 15 | // struct IncrementalNode : Node, IncrementalElement { 16 | // bool hasBeenIncrementallyParsed; 17 | // }; 18 | 19 | class SyntaxCursor { 20 | SourceFile *sourceFile; 21 | // NodeArray *currentArray; 22 | 23 | int currentArrayIndex = 0; 24 | 25 | // assert(currentArrayIndex < currentArray.length); 26 | Node *current; 27 | int lastQueriedPosition = InvalidPosition::Value; 28 | public: 29 | SyntaxCursor(SourceFile *sourceFile) : sourceFile(sourceFile) { 30 | // currentArray = &sourceFile->statements; 31 | // current = ¤tArray->list[currentArrayIndex]; 32 | } 33 | 34 | Node currentNode(int position); 35 | 36 | // Finds the highest element in the tree we can find that starts at the provided position. 37 | // The element must be a direct child of some node list in the tree. This way after we 38 | // return it, we can easily return its next sibling in the list. 39 | void findHighestListElementThatStartsAtPosition(int position); 40 | }; 41 | 42 | } // ts 43 | 44 | -------------------------------------------------------------------------------- /src/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | project(Tests) 2 | 3 | 4 | #Include(FetchContent) 5 | #FetchContent_Declare( 6 | # googletest 7 | # GIT_REPOSITORY https://github.com/google/googletest.git 8 | # GIT_TAG release-1.11.0 9 | #) 10 | ## For Windows: Prevent overriding the parent project's compiler/linker settings 11 | ##set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 12 | #FetchContent_MakeAvailable(googletest) 13 | # 14 | ##include_directories(${}) 15 | 16 | 17 | #project(Tests) 18 | # 19 | ##add_library(catch ../../../libs/Catch2/single_include/catch.hpp) 20 | ##set_target_properties(catch PROPERTIES LINKER_LANGUAGE CXX) 21 | # 22 | #Include(FetchContent) 23 | # 24 | ##FetchContent_Declare( 25 | ## Catch2 26 | ## GIT_REPOSITORY https://github.com/catchorg/Catch2.git 27 | ## GIT_TAG v3.0.1 28 | ##) 29 | ## 30 | ##FetchContent_MakeAvailable(Catch2) 31 | # 32 | #FetchContent_Declare( 33 | # googletest 34 | # GIT_REPOSITORY https://github.com/google/googletest.git 35 | # GIT_TAG release-1.11.0 36 | #) 37 | ## For Windows: Prevent overriding the parent project's compiler/linker settings 38 | #set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 39 | #FetchContent_MakeAvailable(googletest) 40 | # 41 | #include_directories(../../libs) 42 | # 43 | ## These tests can use the Catch2-provided main 44 | ##add_executable(tests test.cpp) 45 | ##link_libraries(PRIVATE Catch2::Catch2WithMain) 46 | # 47 | ##link_libraries(PRIVATE Catch2::Catch2WithMain) 48 | ##link_libraries(typescript) 49 | 50 | 51 | #Include(FetchContent) 52 | # 53 | #FetchContent_Declare( 54 | # Catch2 55 | # GIT_REPOSITORY https://github.com/catchorg/Catch2.git 56 | # GIT_TAG v2.13.9 # or a later release 57 | #) 58 | # 59 | #FetchContent_MakeAvailable(Catch2) 60 | 61 | file(GLOB TESTS test*.cpp) 62 | 63 | #add_executable(Tests_run test_core.cpp) 64 | #target_link_libraries(Tests_run gtest gtest_main typescript) 65 | 66 | foreach (file ${TESTS}) 67 | get_filename_component(name ${file} NAME_WE) 68 | MESSAGE("Test found typescript_${name}.") 69 | add_executable(typescript_${name} ${file}) 70 | 71 | # target_link_libraries(typescript_${name} PUBLIC Tracy::TracyClient) 72 | 73 | # target_link_libraries(typescript_${name} gtest_main) 74 | target_link_libraries(typescript_${name} PRIVATE doctest typescript) 75 | # target_link_libraries(typescript_${name} typescript) 76 | # target_link_libraries(typescript_${name} PRIVATE Catch2::Catch2WithMain) 77 | # target_link_libraries(typescript_${name} PRIVATE typescript) 78 | endforeach () 79 | -------------------------------------------------------------------------------- /src/tests/test_bench.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "../core.h" 5 | #include "../hash.h" 6 | #include "../checker/types.h" 7 | #include "../checker/vm.h" 8 | #include "../checker/vm2.h" 9 | #include "../checker/compiler.h" 10 | #include "../checker/debug.h" 11 | 12 | using namespace tr; 13 | using std::string; 14 | using std::make_shared; 15 | using std::string_view; 16 | 17 | struct SimpleType { 18 | string_view typeName; 19 | vm::TypeKind kind = vm::TypeKind::Never; 20 | 21 | //this is the OP position (instruction pointer) of the bytecode. sourcemap is necessary to map it to source positions. 22 | unsigned int ip{}; 23 | }; 24 | 25 | struct SimpleType2 { 26 | vm::TypeKind kind = vm::TypeKind::Never; 27 | }; 28 | 29 | TEST(bench, jit) { 30 | vm2::jitTest(); 31 | debug("called {}", vm2::called); 32 | 33 | bench("jit", 1000, [&] { 34 | vm2::called = 0; 35 | vm2::jitTest(); 36 | // EXPECT_EQ(vm2::called, 1800); 37 | }); 38 | 39 | auto fn = vm2::jitBuild(); 40 | vector vec; 41 | bench("jit prebuild", 1000, [&] { 42 | for (auto i = 0; i < 300*6; i++) { 43 | volatile auto a = new SimpleType(); 44 | } 45 | vm2::called = 0; 46 | // fn(); 47 | // EXPECT_EQ(vm2::called, 1800); 48 | }); 49 | 50 | bench("300*6 calls", 1000, [&] { 51 | vm2::called = 0; 52 | for (auto i = 0; i < 300; i++) { 53 | vm2::calledFunc(); 54 | vm2::calledFunc(); 55 | vm2::calledFunc(); 56 | vm2::calledFunc(); 57 | vm2::calledFunc(); 58 | vm2::calledFunc(); 59 | } 60 | EXPECT_EQ(vm2::called, 1800); 61 | }); 62 | 63 | bench("300*6 jump", 1000, [&] { 64 | vm2::called = 0; 65 | volatile auto i = 0; //stop optimisation 66 | volatile vm::TypeUnknown* t; 67 | 68 | inc: 69 | vm2::called++; 70 | // t = new vm::TypeUnknown; 71 | goto next; 72 | 73 | next: 74 | if (++i < 1800) goto inc; 75 | goto end; 76 | 77 | end: 78 | EXPECT_EQ(vm2::called, 1800); 79 | }); 80 | } 81 | 82 | TEST(bench, vm2) { 83 | checker::Program program; 84 | // program.pushOp(instructions::OP::Frame); 85 | for (auto i = 0; i < 300; i++) { 86 | program.pushOp(instructions::OP::Noop); //frame 87 | program.pushOp(instructions::OP::Noop); //string 88 | program.pushOp(instructions::OP::Noop); //property name 89 | 90 | program.pushOp(instructions::OP::Noop); //propertySignature 91 | program.pushOp(instructions::OP::Noop); //objectLiteral 92 | program.pushOp(instructions::OP::Noop); //TupleMember 93 | } 94 | program.pushOp(instructions::OP::Halt); 95 | 96 | auto bin = program.build(); 97 | checker::printBin(bin); 98 | 99 | auto module = make_shared(bin, "app", ""); 100 | bench("vm1 old, less cases", 1000, [&] { 101 | module->clear(); 102 | vm::VM vm; 103 | vm.run(module); 104 | }); 105 | bench("vm2 direct threaded", 1000, [&] { 106 | vm2::called = 0; 107 | module->clear(); 108 | vm2::run(module); 109 | EXPECT_EQ(vm2::called, 1800); 110 | }); 111 | 112 | bench("vm2 naive switch-case", 1000, [&] { 113 | vm2::called = 0; 114 | module->clear(); 115 | vm2::naive(module); 116 | // EXPECT_EQ(vm2::called, 1800); 117 | }); 118 | } 119 | 120 | TEST(bench, vm3) { 121 | auto jump = [] { 122 | 123 | //https://christopherschwaab.wordpress.com/2018/04/13/generating-a-threaded-arm-interpreter-with-templates/ 124 | 125 | // We're using the GNU C, labels-as-values extension here. 126 | void *prog[] = {&&PUSH,(void*)6, 127 | &&PUSH,(void*)7, 128 | &&MUL, &&HALT}; 129 | // vector program; 130 | // void **prog = prog; 131 | 132 | void **vPC = prog; 133 | void *stack[4], **sp = stack; 134 | 135 | goto **vPC++; 136 | PUSH: *sp++ = *vPC++; goto **vPC++; 137 | MUL: *(sp-1) = (void*)(*(long*)(sp-1) * *(long*)(sp-2)); 138 | --sp; 139 | goto **vPC++;; 140 | PRINT: //printf("%li\n", *(long*)sp--); goto **vPC++;; 141 | HALT: return; 142 | }; 143 | 144 | bench("jump", 1000, [&] { 145 | jump(); 146 | }); 147 | } 148 | 149 | TEST(bench, types) { 150 | bench("simple type", 1000, [&] { 151 | for (auto i = 0; i < 1000; i++) { 152 | volatile auto a = make_shared(); 153 | } 154 | }); 155 | 156 | bench("simple type array", 1000, [&] { 157 | auto simpleTypes = vector>(); 158 | simpleTypes.reserve(1000); 159 | for (auto i = 0; i < 1000; i++) { 160 | simpleTypes.emplace_back(); 161 | simpleTypes.pop_back(); 162 | } 163 | }); 164 | 165 | bench("simple type 2", 1000, [&] { 166 | volatile auto a = make_shared(); 167 | }); 168 | 169 | bench("simple type 3", 1000, [&] { 170 | volatile auto a = new SimpleType2; 171 | delete a; 172 | }); 173 | 174 | bench("simple type 4", 1000, [&] { 175 | volatile vm::TypeKind *a = new vm::TypeKind; 176 | *a = vm::TypeKind::Unknown; 177 | delete a; 178 | }); 179 | 180 | bench("base type", 1000, [&] { 181 | volatile auto a = make_shared(); 182 | }); 183 | 184 | bench("string type", 1000, [&] { 185 | for (auto i = 0; i < 1000; i++) { 186 | volatile auto a = make_shared(); 187 | } 188 | }); 189 | 190 | bench("string type array", 1000, [&] { 191 | auto simpleTypes = vector>(); 192 | simpleTypes.reserve(1000); 193 | for (auto i = 0; i < 1000; i++) { 194 | simpleTypes.emplace_back(); 195 | simpleTypes.pop_back(); 196 | } 197 | }); 198 | 199 | bench("string literal type ", 1000, [&] { 200 | for (auto i = 0; i < 1000; i++) { 201 | volatile auto a = make_shared("", vm::TypeLiteralType::String); 202 | } 203 | }); 204 | 205 | bench("string literal type array", 1000, [&] { 206 | auto simpleTypes = vector>(); 207 | simpleTypes.reserve(1000); 208 | for (auto i = 0; i < 1000; i++) { 209 | simpleTypes.emplace_back(); 210 | simpleTypes.pop_back(); 211 | } 212 | }); 213 | } 214 | 215 | TEST(bench, hashing) { 216 | string_view sv = "foo188"; 217 | string s = "foo188"; 218 | 219 | bench("hash from view", 100, [&] { 220 | tr::hash::runtime_hash(s); 221 | }); 222 | 223 | bench("hash from string", 100, [&] { 224 | tr::hash::runtime_hash(s); 225 | }); 226 | } 227 | -------------------------------------------------------------------------------- /src/tests/test_checker.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../parser2.h" 4 | #include "../checker/compiler.h" 5 | #include "../checker/vm.h" 6 | #include "../checker/debug.h" 7 | #include "../utils.h" 8 | 9 | using namespace tr; 10 | 11 | TEST(checker, program) { 12 | checker::Program program; 13 | 14 | program.pushOp(instructions::OP::Call); 15 | program.pushAddress(1); 16 | 17 | program.pushOp(instructions::OP::Var); 18 | program.pushAddress(2); 19 | 20 | program.pushOp(instructions::OP::Any); 21 | program.pushOp(instructions::OP::Assign); 22 | program.pushAddress(2); 23 | 24 | fmt::print("bytes {}", program.ops); 25 | } 26 | 27 | TEST(checker, type) { 28 | Parser parser; 29 | 30 | string code = R"( 31 | const v1: string = "abc"; 32 | const v2: number = 123; 33 | )"; 34 | 35 | testBench(code, 0); 36 | } 37 | 38 | TEST(checker, typeObject) { 39 | Parser parser; 40 | 41 | string code = R"( 42 | type CommandConfig = { id: string }; 43 | const commands: CommandConfig[] = [ 44 | {id: 'foo0'}, 45 | {id: false}, 46 | {id: false}, 47 | ]; 48 | )"; 49 | 50 | test(code, 2); 51 | } 52 | 53 | TEST(checker, typeError) { 54 | Parser parser; 55 | 56 | string code = R"( 57 | type a = T; const v2: a = "23"; 58 | )"; 59 | 60 | testBench(code, 1); 61 | } 62 | 63 | TEST(checker, typeUnion) { 64 | Parser parser; 65 | 66 | string code = R"( 67 | type a = number; 68 | type b = string | a; 69 | const v1: b = 'abc'; 70 | const v2: b = 23; 71 | const v3: b = true; 72 | )"; 73 | 74 | test(code, 1); 75 | } 76 | 77 | TEST(checker, typeGeneric) { 78 | string code = R"( 79 | type a = T; 80 | const v1: a = 34; 81 | const v2: a = "as"; 82 | )"; 83 | 84 | test(code, 1); 85 | } 86 | 87 | TEST(checker, typeGenericUnion) { 88 | Parser parser; 89 | 90 | string code = R"( 91 | type a = K | (T extends string ? 'yes' : 'no'); 92 | const v1: a = 'no'; 93 | const v2: a = 'yes'; 94 | const v3: a = true; 95 | const v4: a = 'yes'; 96 | const v5: a = 'nope'; 97 | )"; 98 | 99 | test(code); 100 | } 101 | 102 | TEST(checker, tuple) { 103 | Parser parser; 104 | 105 | string code = R"( 106 | type StringToNum = `${A['length']}` extends T ? A['length'] : StringToNum; 107 | const var1: StringToNum<'999'> = 1002; 108 | )"; 109 | 110 | testBench(code, 1); 111 | } 112 | 113 | TEST(checker, assign) { 114 | Parser parser; 115 | 116 | auto code = R"( 117 | const v: number | string = ''; 118 | //Assign to '', change to string 119 | 120 | v = 123; //Assign to 123, change to number 121 | )"; 122 | 123 | auto result = parser.parseSourceFile("app.ts", code, tr::types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 124 | 125 | checker::Compiler compiler; 126 | 127 | auto program = compiler.compileSourceFile(result); 128 | auto bin = program.build(); 129 | checker::printBin(bin); 130 | debug("done"); 131 | } 132 | 133 | TEST(checker, stackFrame) { 134 | Parser parser; 135 | 136 | auto code = R"( 137 | type a = string; 138 | 139 | type Generic = a | T; 140 | 141 | function another() { 142 | type a = ReturnType; 143 | } 144 | 145 | function print() { 146 | type b = a; 147 | type c = Generic; 148 | 149 | function p() { 150 | type d = Generic; 151 | } 152 | return c; 153 | } 154 | )"; 155 | 156 | auto result = parser.parseSourceFile("app.ts", code, tr::types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 157 | debug("done"); 158 | } 159 | 160 | TEST(checker, functionCall) { 161 | auto code = R"( 162 | const i: number = 123; 163 | 164 | function doIt(v: number) { 165 | } 166 | 167 | doIt(i); 168 | doIt('123'); 169 | )"; 170 | 171 | test(code); 172 | } 173 | 174 | TEST(checker, functionGenericCall) { 175 | auto code = R"( 176 | function doIt(v: T) { 177 | } 178 | doIt(23); 179 | )"; 180 | 181 | test(code, 0); 182 | } 183 | 184 | TEST(checker, controlFlow1) { 185 | auto code = R"( 186 | function boolFunc(t: true) {} 187 | let bool = true; 188 | boolFunc(bool); 189 | 190 | bool = false; 191 | boolFunc(bool); 192 | 193 | bool = Date.now() > 1000 ? true : false; 194 | boolFunc(bool); 195 | )"; 196 | 197 | testBench(code, 2); 198 | } 199 | 200 | TEST(checker, functionGenericExpressionCall) { 201 | auto code = R"( 202 | function doIt(v: T) { 203 | } 204 | 205 | const a = doIt; 206 | a(23); 207 | )"; 208 | 209 | testBench(code, 0); 210 | } 211 | 212 | TEST(checker, functionGenericCallBench) { 213 | //todo: implement inferring types from passed function arguments 214 | auto code = R"( 215 | // const i: number = 123; 216 | 217 | function doIt(v: T) { 218 | } 219 | 220 | doIt(23); 221 | // doIt('23'); 222 | // doIt<34>(i); 223 | // doIt(i); 224 | // doIt(i); 225 | // doIt('asd'); 226 | // doIt(); 227 | )"; 228 | 229 | testBench(code); 230 | } 231 | 232 | TEST(checker, objectLiteral1) { 233 | auto code = R"( 234 | type Person = {name: string, age: number} 235 | 236 | const a: Person = {name: 'Peter', age: 52}; 237 | const b: Person = {name: 'Peter', age: '52'}; 238 | )"; 239 | 240 | testBench(code); 241 | } 242 | 243 | TEST(checker, mapLiteralToClassConditional) { 244 | auto code = R"( 245 | class A {}; 246 | class B {}; 247 | class C {}; 248 | class D {}; 249 | 250 | type Resolve = 251 | T extends "a" ? A : 252 | T extends "b" ? B : 253 | T extends "c" ? C : 254 | T extends "d" ? D : 255 | never; 256 | 257 | type found = Resolve<"d">; 258 | )"; 259 | 260 | testBench(code); 261 | } 262 | TEST(checker, mapLiteralToClassMap) { 263 | auto code = R"( 264 | class A {} 265 | class B {} 266 | class C {} 267 | class D {} 268 | type ClassMap = { 269 | a: A, b: B, c: C, d: D 270 | } 271 | 272 | type Resolve = ClassMap[T]; 273 | 274 | type found = Resolve<"d">; 275 | )"; 276 | 277 | testBench(code); 278 | } 279 | 280 | TEST(checker, basic2) { 281 | Parser parser; 282 | 283 | auto code = R"( 284 | // i = 1; 285 | const i: number | string = 123; 286 | 287 | if ('number' === typeof i) { 288 | function print(v: string) { 289 | i = 3; 290 | const i = v; 291 | } 292 | } 293 | //type print = (v: string) => void; 294 | //const print = function(v: string) {} //both equivalent, except of the symbol scope 295 | 296 | i = "no"; 297 | print(i); 298 | )"; 299 | 300 | auto result = parser.parseSourceFile("app.ts", code, tr::types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 301 | debug("done"); 302 | } 303 | -------------------------------------------------------------------------------- /src/tests/test_hash.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../hash.h" 4 | 5 | using namespace std; 6 | using namespace tr::hash; 7 | 8 | TEST(hash, hash) { 9 | string_view sv = "foo188"; 10 | string s = "foo188"; 11 | 12 | // EXPECT_EQ("fo"_hash, 1842121476277988984UL); 13 | // EXPECT_EQ(const_hash("fo"), 1842121476277988984UL); 14 | // EXPECT_EQ(runtime_hash("fo"), 1842121476277988984UL); 15 | 16 | EXPECT_EQ("foo188"_hash, 3578094862220341077UL); 17 | EXPECT_EQ(runtime_hash(s), 3578094862220341077UL); 18 | EXPECT_EQ(runtime_hash(sv), 3578094862220341077UL); 19 | 20 | EXPECT_EQ(const_hash("foo188"), 3578094862220341077UL); 21 | EXPECT_EQ(runtime_hash("foo188"), 3578094862220341077UL); 22 | } 23 | -------------------------------------------------------------------------------- /src/tests/test_mem.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "../core.h" 4 | #include "../checker/compiler.h" 5 | #include 6 | #include 7 | 8 | using namespace tr; 9 | 10 | TEST(mem, unint) { 11 | std::vector bin; 12 | 13 | compiler::writeUint32(bin, 0, 1025); 14 | EXPECT_EQ(bin.size(), 4); 15 | 16 | compiler::writeUint32(bin, 4, 1026); 17 | EXPECT_EQ(bin.size(), 8); 18 | 19 | EXPECT_EQ(compiler::readUint32(bin, 0), 1025); 20 | EXPECT_EQ(compiler::readUint32(bin, 4), 1026); 21 | 22 | fmt::print("bin {}\n", bin); 23 | } 24 | -------------------------------------------------------------------------------- /src/tests/test_parser.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "../parser2.h" 5 | #include 6 | 7 | using namespace tr; 8 | 9 | TEST(parser, single) { 10 | Parser parser; 11 | 12 | auto code = "const i = 123;"; 13 | /** 14 | ConstKeyword 15 | WhitespaceTrivia 16 | Identifier 17 | WhitespaceTrivia 18 | EqualsToken 19 | WhitespaceTrivia 20 | NumericLiteral 21 | SemicolonToken 22 | EndOfFileToken 23 | */ 24 | 25 | auto result = parser.parseSourceFile("app.ts", code, tr::types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 26 | debug("done"); 27 | } 28 | 29 | TEST(parser, bench) { 30 | Parser parser; 31 | string code; 32 | 33 | for (int i = 0; i <100; i++) { 34 | code += string("const i").append(to_string(i)).append(" = 123;"); 35 | } 36 | 37 | usleep(100'000); 38 | 39 | bench(1, [&]{ 40 | auto result = parser.parseSourceFile("app.ts", code, tr::types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 41 | // auto sourceFile = parser.createSourceFile("app.ts", tr::types::ScriptTarget::Latest, ScriptKind::TS, false, make_shared(), make_shared(), 0, [](auto s) {}); 42 | }); 43 | fmt::print("parse {} bytes ", code.size()); 44 | 45 | usleep(100'000); 46 | } 47 | 48 | TEST(parser, bench2) { 49 | Parser parser; 50 | string code = R"( 51 | type Person = { name: string, age: number } 52 | 53 | type Student = { name: string, age: number, gpa: number } 54 | 55 | type FunctionContravarianceTest = (student: Student) => boolean 56 | 57 | const contravariance: FunctionContravarianceTest = (person: Person) => true 58 | 59 | type MyFn = (name: string) => { name: string} 60 | 61 | const woops: MyFn = (name: number) => ({ name }) 62 | 63 | type Union = string number boolean 64 | 65 | const arr: Union[] = ["hello", 420, false, (}] 66 | 67 | const fibonacci = (n: number): number => n <= 1 ? n : fibonacci(n - 1) + fibonacci(n - 2) 68 | 69 | type RedundantBigAssUnion = string | number string| number string number string| number| string number boolean boolean number 70 | 71 | const thisWorks: RedundantBigAssUnion[] = ["hello", 123] 72 | )"; 73 | 74 | usleep(100'000); 75 | 76 | bench(1, [&]{ 77 | auto result = parser.parseSourceFile("app.ts", code, tr::types::ScriptTarget::Latest, false, ScriptKind::TS, {}); 78 | // auto sourceFile = parser.createSourceFile("app.ts", tr::types::ScriptTarget::Latest, ScriptKind::TS, false, make_shared(), make_shared(), 0, [](auto s) {}); 79 | }); 80 | fmt::print("parse {} bytes ", code.size()); 81 | 82 | usleep(100'000); 83 | } -------------------------------------------------------------------------------- /src/tests/test_pool_array.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | 4 | #include 5 | #include "../checker/pool_array.h" 6 | #include "../core.h" 7 | #include 8 | 9 | struct Item { 10 | std::string_view title; 11 | unsigned int i; 12 | std::vector jopp; 13 | }; 14 | 15 | TEST_CASE("basic") { 16 | tr::debug("std::span = {}", sizeof(std::span)); 17 | } 18 | 19 | TEST_CASE("block creation") { 20 | PoolArray pool; 21 | REQUIRE(pool.getPool(1).blocks == 0); 22 | REQUIRE(pool.active == 0); 23 | 24 | auto p1 = pool.allocate(1); 25 | REQUIRE(pool.active == 1); 26 | REQUIRE(pool.getPool(1).blocks == 1); 27 | 28 | auto p2 = pool.allocate(1); 29 | REQUIRE(pool.getPool(1).blocks == 1); 30 | REQUIRE(pool.active == 2); 31 | 32 | pool.deallocate(p1); 33 | REQUIRE(pool.getPool(1).blocks == 1); 34 | REQUIRE(pool.active == 1); 35 | 36 | pool.deallocate(p2); 37 | REQUIRE(pool.getPool(1).blocks == 1); 38 | REQUIRE(pool.active == 0); 39 | 40 | auto p3 = pool.allocate(1); 41 | REQUIRE(pool.active == 1); 42 | REQUIRE(pool.getPool(1).blocks == 1); 43 | 44 | auto p4 = pool.allocate(1); 45 | REQUIRE(pool.getPool(1).blocks == 1); 46 | REQUIRE(pool.active == 2); 47 | } 48 | 49 | TEST_CASE("multi pool") { 50 | PoolArray pool; 51 | auto p1 = pool.construct(2); 52 | REQUIRE(pool.active == 2); 53 | REQUIRE(pool.getPool(2).blocks == 1); 54 | 55 | auto p2 = pool.construct(3); 56 | REQUIRE(pool.active == 5); 57 | REQUIRE(pool.getPool(2).blocks == 1); 58 | REQUIRE(pool.getPool(4).blocks == 1); 59 | 60 | pool.destruct(p2); 61 | REQUIRE(pool.active == 2); 62 | REQUIRE(pool.getPool(2).blocks == 1); 63 | REQUIRE(pool.getPool(4).blocks == 1); 64 | } 65 | 66 | TEST_CASE("mixed") { 67 | PoolArray pool; 68 | REQUIRE(pool.getPool(1).blocks == 0); 69 | REQUIRE(pool.active == 0); 70 | 71 | auto p1 = pool.allocate(1); 72 | REQUIRE(pool.getPool(1).blocks == 1); 73 | REQUIRE(pool.active == 1); 74 | 75 | auto p2 = pool.allocate(2); 76 | REQUIRE(pool.getPool(1).blocks == 1); 77 | REQUIRE(pool.active == 3); 78 | 79 | pool.deallocate(p1); 80 | REQUIRE(pool.getPool(1).blocks == 1); 81 | REQUIRE(pool.active == 2); 82 | 83 | pool.deallocate(p2); 84 | REQUIRE(pool.getPool(1).blocks == 1); 85 | REQUIRE(pool.active == 0); 86 | 87 | auto p3 = pool.allocate(1); 88 | REQUIRE(pool.getPool(1).blocks == 1); 89 | REQUIRE(pool.active == 1); 90 | 91 | pool.deallocate(p3); 92 | REQUIRE(pool.getPool(1).blocks == 1); 93 | REQUIRE(pool.active == 0); 94 | } 95 | 96 | TEST_CASE("allocator2") { 97 | PoolArray pool; 98 | auto p1 = pool.allocate(1); 99 | auto p2 = pool.allocate(1); 100 | REQUIRE(pool.active == 2); 101 | REQUIRE(pool.getPool(1).blocks == 1); 102 | 103 | auto p3 = pool.allocate(1); 104 | REQUIRE(pool.active == 3); 105 | REQUIRE(pool.getPool(1).blocks == 2); 106 | 107 | auto p4 = pool.allocate(1); 108 | REQUIRE(pool.active == 4); 109 | REQUIRE(pool.getPool(1).blocks == 2); 110 | 111 | auto p5 = pool.allocate(1); 112 | REQUIRE(pool.active == 5); 113 | REQUIRE(pool.getPool(1).blocks == 3); 114 | } 115 | 116 | TEST_CASE("allocator3") { 117 | PoolArray pool; 118 | { 119 | auto p1 = pool.allocate(1); 120 | auto p2 = pool.allocate(1); 121 | REQUIRE(pool.active == 2); 122 | REQUIRE(pool.getPool(1).blocks == 1); 123 | 124 | auto p3 = pool.allocate(1); 125 | REQUIRE(pool.active == 3); 126 | REQUIRE(pool.getPool(1).blocks == 2); 127 | 128 | auto p4 = pool.allocate(1); 129 | REQUIRE(pool.active == 4); 130 | REQUIRE(pool.getPool(1).blocks == 2); 131 | } 132 | 133 | { 134 | pool.clear(); 135 | auto p1 = pool.allocate(1); 136 | REQUIRE(pool.active == 1); 137 | REQUIRE(pool.getPool(1).blocks == 2); 138 | 139 | auto p2 = pool.allocate(1); 140 | REQUIRE(pool.active == 2); 141 | REQUIRE(pool.getPool(1).blocks == 2); 142 | 143 | //now block 2 should be reused 144 | auto p3 = pool.allocate(1); 145 | REQUIRE(pool.active == 3); 146 | REQUIRE(pool.getPool(1).blocks == 2); 147 | 148 | //now block 2 should be reused 149 | auto p4 = pool.allocate(1); 150 | REQUIRE(pool.active == 4); 151 | REQUIRE(pool.getPool(1).blocks == 2); 152 | 153 | //now block 3 should be created 154 | auto p5 = pool.allocate(1); 155 | REQUIRE(pool.active == 5); 156 | REQUIRE(pool.getPool(1).blocks == 3); 157 | } 158 | } 159 | 160 | TEST_CASE("allocator4") { 161 | PoolArray pool; 162 | pool.clear(); 163 | 164 | auto p1 = pool.construct(1); 165 | REQUIRE(p1[0].i == 0); 166 | REQUIRE(p1[0].title == ""); 167 | 168 | auto p2 = pool.construct(1); 169 | REQUIRE(p2[0].i == 0); 170 | REQUIRE(p2[0].title == ""); 171 | p2[0].i = 2; 172 | REQUIRE(p2[0].i == 2); 173 | 174 | pool.destruct(p2); 175 | 176 | auto p3 = pool.construct(1); 177 | REQUIRE(&p3[0] == &p2[0]); 178 | REQUIRE(p3[0].i == 0); 179 | REQUIRE(p3[0].title == ""); 180 | } 181 | 182 | TEST_CASE("allocator5") { 183 | PoolArray pool; 184 | pool.clear(); 185 | 186 | auto p1 = pool.construct(1); 187 | p1[0].i = 1; 188 | auto p2 = pool.construct(1); 189 | p2[0].i = 2; 190 | 191 | auto p3 = pool.construct(1); 192 | p3[0].i = 3; 193 | auto p4 = pool.construct(1); 194 | p4[0].i = 4; 195 | 196 | auto p5 = pool.construct(1); 197 | p5[0].i = 5; 198 | auto p6_ = pool.construct(1); 199 | pool.destruct(p6_); 200 | 201 | auto p6 = pool.construct(1); 202 | p6[0].i = 6; 203 | REQUIRE(pool.active == 6); 204 | REQUIRE(pool.getPool(1).blocks == 3); 205 | 206 | REQUIRE(p1[0].i == 1); 207 | REQUIRE(p2[0].i == 2); 208 | REQUIRE(p3[0].i == 3); 209 | REQUIRE(p4[0].i == 4); 210 | REQUIRE(p5[0].i == 5); 211 | REQUIRE(p6[0].i == 6); 212 | } -------------------------------------------------------------------------------- /src/tests/test_pool_band.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 3 | #include 4 | #include 5 | #include "../pool_band.h" 6 | #include 7 | 8 | struct Item { 9 | std::string_view title; 10 | std::vector jopp; 11 | unsigned int i; 12 | }; 13 | 14 | TEST_CASE("allocator1") { 15 | PoolBand pool(sizeof(Item) * 3); 16 | REQUIRE(pool.blocks == 0); 17 | REQUIRE(pool.active == 0); 18 | 19 | auto p1 = pool.allocate(); 20 | p1->title = "1"; 21 | p1->i = 1; 22 | REQUIRE(pool.active == 1); 23 | REQUIRE(pool.blocks == 1); 24 | 25 | auto p2 = pool.allocate(); 26 | p2->title = "2"; 27 | p2->i = 2; 28 | REQUIRE(pool.active == 2); 29 | REQUIRE(pool.blocks == 1); 30 | 31 | auto p3 = pool.allocate(); 32 | p3->title = "3"; 33 | p3->i = 3; 34 | REQUIRE(pool.active == 3); 35 | REQUIRE(pool.blocks == 1); 36 | 37 | auto p4 = pool.allocate(); 38 | p4->title = "4"; 39 | p4->i = 4; 40 | REQUIRE(pool.active == 4); 41 | REQUIRE(pool.blocks == 2); 42 | 43 | REQUIRE(p1->title == "1"); 44 | REQUIRE(p2->title == "2"); 45 | REQUIRE(p3->title == "3"); 46 | REQUIRE(p4->title == "4"); 47 | 48 | REQUIRE(p1->i == 1); 49 | REQUIRE(p2->i == 2); 50 | REQUIRE(p3->i == 3); 51 | REQUIRE(p4->i == 4); 52 | } 53 | 54 | //TEST_CASE("allocator2") { 55 | // PoolSingle pool; 56 | // auto p1 = pool.allocate(); 57 | // auto p2 = pool.allocate(); 58 | // REQUIRE(pool.active == 2); 59 | // REQUIRE(pool.blocks == 1); 60 | // 61 | // auto p3 = pool.allocate(); 62 | // REQUIRE(pool.active == 3); 63 | // REQUIRE(pool.blocks == 2); 64 | // 65 | // auto p4 = pool.allocate(); 66 | // REQUIRE(pool.active == 4); 67 | // REQUIRE(pool.blocks == 2); 68 | // 69 | // auto p5 = pool.allocate(); 70 | // REQUIRE(pool.active == 5); 71 | // REQUIRE(pool.blocks == 3); 72 | //} 73 | // 74 | //TEST_CASE("allocator3") { 75 | // PoolSingle pool; 76 | // { 77 | // auto p1 = pool.allocate(); 78 | // auto p2 = pool.allocate(); 79 | // REQUIRE(pool.active == 2); 80 | // REQUIRE(pool.blocks == 1); 81 | // 82 | // auto p3 = pool.allocate(); 83 | // REQUIRE(pool.active == 3); 84 | // REQUIRE(pool.blocks == 2); 85 | // 86 | // auto p4 = pool.allocate(); 87 | // REQUIRE(pool.active == 4); 88 | // REQUIRE(pool.blocks == 2); 89 | // } 90 | // 91 | // { 92 | // pool.clear(); 93 | // auto p1 = pool.allocate(); 94 | // REQUIRE(pool.active == 1); 95 | // REQUIRE(pool.blocks == 2); 96 | // 97 | // auto p2 = pool.allocate(); 98 | // REQUIRE(pool.active == 2); 99 | // REQUIRE(pool.blocks == 2); 100 | // 101 | // //now block 2 should be reused 102 | // auto p3 = pool.allocate(); 103 | // REQUIRE(pool.active == 3); 104 | // REQUIRE(pool.blocks == 2); 105 | // 106 | // //now block 2 should be reused 107 | // auto p4 = pool.allocate(); 108 | // REQUIRE(pool.active == 4); 109 | // REQUIRE(pool.blocks == 2); 110 | // 111 | // //now block 3 should be created 112 | // auto p5 = pool.allocate(); 113 | // REQUIRE(pool.active == 5); 114 | // REQUIRE(pool.blocks == 3); 115 | // } 116 | //} 117 | // 118 | //TEST_CASE("allocator4") { 119 | // PoolSingle pool; 120 | // pool.clear(); 121 | // 122 | // auto p1 = pool.newElement(); 123 | // REQUIRE(p1->i == 0); 124 | // REQUIRE(p1->title == ""); 125 | // 126 | // auto p2 = pool.newElement(); 127 | // REQUIRE(p2->i == 0); 128 | // REQUIRE(p2->title == ""); 129 | // p2->i = 2; 130 | // REQUIRE(p2->i == 2); 131 | // 132 | // pool.deleteElement(p2); 133 | // 134 | // auto p3 = pool.newElement(); 135 | // REQUIRE(p3 == p2); 136 | // REQUIRE(p3->i == 0); 137 | // REQUIRE(p3->title == ""); 138 | //} 139 | // 140 | //TEST_CASE("allocator5") { 141 | // PoolSingle pool; 142 | // pool.clear(); 143 | // 144 | // auto p1 = pool.newElement(); 145 | // p1->i = 1; 146 | // auto p2 = pool.newElement(); 147 | // p2->i = 2; 148 | // 149 | // auto p3 = pool.newElement(); 150 | // p3->i = 3; 151 | // auto p4 = pool.newElement(); 152 | // p4->i = 4; 153 | // 154 | // auto p5 = pool.newElement(); 155 | // p5->i = 5; 156 | // auto p6_ = pool.newElement(); 157 | // pool.deleteElement(p6_); 158 | // 159 | // auto p6 = pool.newElement(); 160 | // p6->i = 6; 161 | // REQUIRE(pool.active == 6); 162 | // REQUIRE(pool.blocks == 3); 163 | // 164 | // REQUIRE(p1->i == 1); 165 | // REQUIRE(p2->i == 2); 166 | // REQUIRE(p3->i == 3); 167 | // REQUIRE(p4->i == 4); 168 | // REQUIRE(p5->i == 5); 169 | // REQUIRE(p6->i == 6); 170 | //} -------------------------------------------------------------------------------- /src/tests/test_pool_single.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 3 | #include 4 | 5 | #include 6 | #include "../checker/pool_single.h" 7 | #include 8 | 9 | struct Item { 10 | std::string_view title; 11 | unsigned int i; 12 | std::vector jopp; 13 | }; 14 | 15 | TEST_CASE("allocator1") { 16 | PoolSingle pool; 17 | REQUIRE(pool.blocks == 0); 18 | REQUIRE(pool.active == 0); 19 | 20 | auto p1 = pool.allocate(); 21 | REQUIRE(pool.active == 1); 22 | REQUIRE(pool.blocks == 1); 23 | 24 | auto p2 = pool.allocate(); 25 | REQUIRE(pool.blocks == 1); 26 | REQUIRE(pool.active == 2); 27 | 28 | pool.deallocate(p1); 29 | REQUIRE(pool.blocks == 1); 30 | REQUIRE(pool.active == 1); 31 | 32 | pool.deallocate(p2); 33 | REQUIRE(pool.blocks == 1); 34 | REQUIRE(pool.active == 0); 35 | 36 | auto p3 = pool.allocate(); 37 | REQUIRE(pool.active == 1); 38 | REQUIRE(pool.blocks == 1); 39 | 40 | auto p4 = pool.allocate(); 41 | REQUIRE(pool.blocks == 1); 42 | REQUIRE(pool.active == 2); 43 | } 44 | 45 | TEST_CASE("allocator2") { 46 | PoolSingle pool; 47 | auto p1 = pool.allocate(); 48 | auto p2 = pool.allocate(); 49 | REQUIRE(pool.active == 2); 50 | REQUIRE(pool.blocks == 1); 51 | 52 | auto p3 = pool.allocate(); 53 | REQUIRE(pool.active == 3); 54 | REQUIRE(pool.blocks == 2); 55 | 56 | auto p4 = pool.allocate(); 57 | REQUIRE(pool.active == 4); 58 | REQUIRE(pool.blocks == 2); 59 | 60 | auto p5 = pool.allocate(); 61 | REQUIRE(pool.active == 5); 62 | REQUIRE(pool.blocks == 3); 63 | } 64 | 65 | TEST_CASE("allocator3") { 66 | PoolSingle pool; 67 | { 68 | auto p1 = pool.allocate(); 69 | auto p2 = pool.allocate(); 70 | REQUIRE(pool.active == 2); 71 | REQUIRE(pool.blocks == 1); 72 | 73 | auto p3 = pool.allocate(); 74 | REQUIRE(pool.active == 3); 75 | REQUIRE(pool.blocks == 2); 76 | 77 | auto p4 = pool.allocate(); 78 | REQUIRE(pool.active == 4); 79 | REQUIRE(pool.blocks == 2); 80 | } 81 | 82 | { 83 | pool.clear(); 84 | auto p1 = pool.allocate(); 85 | REQUIRE(pool.active == 1); 86 | REQUIRE(pool.blocks == 2); 87 | 88 | auto p2 = pool.allocate(); 89 | REQUIRE(pool.active == 2); 90 | REQUIRE(pool.blocks == 2); 91 | 92 | //now block 2 should be reused 93 | auto p3 = pool.allocate(); 94 | REQUIRE(pool.active == 3); 95 | REQUIRE(pool.blocks == 2); 96 | 97 | //now block 2 should be reused 98 | auto p4 = pool.allocate(); 99 | REQUIRE(pool.active == 4); 100 | REQUIRE(pool.blocks == 2); 101 | 102 | //now block 3 should be created 103 | auto p5 = pool.allocate(); 104 | REQUIRE(pool.active == 5); 105 | REQUIRE(pool.blocks == 3); 106 | } 107 | } 108 | 109 | TEST_CASE("allocator4") { 110 | PoolSingle pool; 111 | pool.clear(); 112 | 113 | auto p1 = pool.newElement(); 114 | REQUIRE(p1->i == 0); 115 | REQUIRE(p1->title == ""); 116 | 117 | auto p2 = pool.newElement(); 118 | REQUIRE(p2->i == 0); 119 | REQUIRE(p2->title == ""); 120 | p2->i = 2; 121 | REQUIRE(p2->i == 2); 122 | 123 | pool.deleteElement(p2); 124 | 125 | auto p3 = pool.newElement(); 126 | REQUIRE(p3 == p2); 127 | REQUIRE(p3->i == 0); 128 | REQUIRE(p3->title == ""); 129 | } 130 | 131 | TEST_CASE("allocator5") { 132 | PoolSingle pool; 133 | pool.clear(); 134 | 135 | auto p1 = pool.newElement(); 136 | p1->i = 1; 137 | auto p2 = pool.newElement(); 138 | p2->i = 2; 139 | 140 | auto p3 = pool.newElement(); 141 | p3->i = 3; 142 | auto p4 = pool.newElement(); 143 | p4->i = 4; 144 | 145 | auto p5 = pool.newElement(); 146 | p5->i = 5; 147 | auto p6_ = pool.newElement(); 148 | pool.deleteElement(p6_); 149 | 150 | auto p6 = pool.newElement(); 151 | p6->i = 6; 152 | REQUIRE(pool.active == 6); 153 | REQUIRE(pool.blocks == 3); 154 | 155 | REQUIRE(p1->i == 1); 156 | REQUIRE(p2->i == 2); 157 | REQUIRE(p3->i == 3); 158 | REQUIRE(p4->i == 4); 159 | REQUIRE(p5->i == 5); 160 | REQUIRE(p6->i == 6); 161 | } -------------------------------------------------------------------------------- /src/tests/test_scanner.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include "../scanner.h" 5 | 6 | using namespace tr; 7 | 8 | TEST(scanner, basisc) { 9 | 10 | auto start = std::chrono::high_resolution_clock::now(); 11 | Scanner scanner("const i = 123;const i2 = 123;const i3 = 123;const i4 = 123;const i5 = 123;const i6 = 123;const i7 = 123;const i8 = 123;"); 12 | 13 | auto i = 0; 14 | for (i = 0; i <10000; i++) { 15 | scanner.setText("const i = 123"); 16 | // scanner.setOnError([this](auto ...a) { scanError(a...); }); 17 | scanner.setScriptTarget(tr::types::ScriptTarget::Latest); 18 | scanner.setLanguageVariant(tr::types::LanguageVariant::Standard); 19 | 20 | while (scanner.scan() != tr::types::SyntaxKind::EndOfFileToken) { 21 | 22 | } 23 | 24 | scanner.clearCommentDirectives(); 25 | scanner.setText(""); 26 | scanner.setOnError(nullopt); 27 | } 28 | std::chrono::duration took = std::chrono::high_resolution_clock::now() - start; 29 | debug("scan {} took {}ms", i, took.count()); 30 | 31 | // std::cout << enum_name(scanner.scan()) << "\n"; 32 | // std::cout << enum_name(scanner.scan()) << "\n"; 33 | // std::cout << enum_name(scanner.scan()) << "\n"; 34 | // std::cout << enum_name(scanner.scan()) << "\n"; 35 | // std::cout << enum_name(scanner.scan()) << "\n"; 36 | // std::cout << enum_name(scanner.scan()) << "\n"; 37 | // std::cout << enum_name(scanner.scan()) << "\n"; 38 | // std::cout << enum_name(scanner.scan()) << "\n"; 39 | // std::cout << enum_name(scanner.scan()) << "\n"; 40 | } -------------------------------------------------------------------------------- /src/tests/test_vm2_class.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | #include 4 | 5 | #include "../core.h" 6 | #include "../hash.h" 7 | #include "../checker/compiler.h" 8 | #include "../checker/vm2.h" 9 | #include "./utils.h" 10 | 11 | using namespace tr; 12 | using namespace tr::vm2; 13 | 14 | TEST_CASE("class1") { 15 | string code = R"( 16 | class MyDate { 17 | static now(): number { 18 | return 0; 19 | } 20 | } 21 | const now: number = MyDate.now(); 22 | const now2: string = MyDate.now(); 23 | )"; 24 | test(code, 1); 25 | testBench(code, 1); 26 | } 27 | 28 | TEST_CASE(" class2") { 29 | string code = R"( 30 | class MyDate { 31 | now(): number { 32 | return 0; 33 | } 34 | } 35 | const now: number = new MyDate().now(); 36 | const now2: string = new MyDate().now(); 37 | )"; 38 | test(code, 1); 39 | testBench(code, 1); 40 | } 41 | 42 | TEST_CASE("class3") { 43 | string code = R"( 44 | class MyDate { 45 | now(): T { 46 | return 0; 47 | } 48 | } 49 | const now: number = new MyDate().now(); 50 | const now2: string = new MyDate().now(); 51 | const now3: number = new MyDate().now(); 52 | )"; 53 | test(code, 1); 54 | testBench(code, 1); 55 | } 56 | 57 | //todo: requires handling Constructor, ThisKeyword, ThisType 58 | // this['item'] relies on `item`, since we can't enforce the order in compile time, we convert all properties/methods to subroutines and call them however they are referenced, 59 | // and detect circular dependencies in the runtime. 60 | // How do we pass `this`? As first TypeArgument, or in a Slot? 61 | // `this` could change, e.g. `new (MyDate & {now: () => string})` 62 | // https://www.typescriptlang.org/play?#code/FAMwrgdgxgLglgewgAggCgJQC5VgLYBGApgE7IDewy1yJRMYJKADANzAC+wwUANgIYBnQcgCyATwAi-GEQpUaEBAHdMOCPmJlKNXbXqMW7XVwXUYCNchgALOCIBkFJauzJBMEnAgBzDvL0aOgYmazsRIWR+CHFjGlNdAHpEsPtkKCQAN1IYEVs5YMNrcQAHOQso5AATIhBSOirkJDk4GDNkAgErACUDJgAVUqIAHlt7AG0AchdJgF0APgDA-RCjdq5TZOKyyuQAXjEpGTknchccTH3Fjy9fLi2MiA9q4-3UImVD6Vl2HiRnghvCAfL7HTAAOgsmF+j2e51whFIbwI4Jc0O4sJgqBUACZ1JokQcUZ1+NCgA 63 | TEST_CASE("class4") { 64 | string code = R"( 65 | class MyDate { 66 | constructor(public item: T) {} 67 | now(): this['item'] { 68 | return this.item; 69 | } 70 | } 71 | const now: number = new MyDate(123).now(); 72 | const now2: string = new MyDate('123').now(); 73 | )"; 74 | test(code, 1); 75 | testBench(code, 1); 76 | } 77 | 78 | //todo: instance class from value arguments. does this work for functions already? -> no 79 | //todo: infer type arguments from constructor. Maybe put the constructor signature in its own subroutine 80 | // which we can call. Result would be that we have all type arguments 81 | TEST_CASE("class41") { 82 | string code = R"( 83 | class MyDate { 84 | constructor(public item: T) {} 85 | now(): T { 86 | return this.item; 87 | } 88 | } 89 | const now: number = new MyDate(123).now(); 90 | const now2: string = new MyDate('123').now(); 91 | )"; 92 | test(code, 0); 93 | testBench(code, 0); 94 | } 95 | 96 | //todo: Needs to resolve Base constructor and executes it to populate T 97 | TEST_CASE("class42") { 98 | string code = R"( 99 | class Base { 100 | constructor(public item: T) {} 101 | } 102 | 103 | class MyDate extends Base { 104 | now(): T { 105 | return this.item; 106 | } 107 | } 108 | const now: number = new MyDate(123).now(); 109 | const now2: string = new MyDate('123').now(); 110 | )"; 111 | test(code, 0); 112 | testBench(code, 0); 113 | } 114 | 115 | TEST_CASE("class5") { 116 | string code = R"( 117 | class Data { 118 | constructor(public item: T) {} 119 | } 120 | 121 | class MyDate extends Data { 122 | now(): this['item'] { 123 | return this.item; 124 | } 125 | } 126 | const now: number = new MyDate('123').now(); 127 | const now2: string = new MyDate('123').now(); 128 | )"; 129 | test(code, 1); 130 | testBench(code, 1); 131 | } 132 | 133 | TEST_CASE("class6") { 134 | string code = R"( 135 | class Data { 136 | constructor(public item: T) {} 137 | } 138 | 139 | class MyDate extends Data { 140 | public item: number; 141 | now(): this['item'] { 142 | return this.item; 143 | } 144 | } 145 | const now: number = new MyDate(123).now(); 146 | const now2: string = new MyDate('123').now(); 147 | )"; 148 | test(code, 1); 149 | testBench(code, 1); 150 | } 151 | -------------------------------------------------------------------------------- /src/tests/test_vm2_closure.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | #include 4 | 5 | #include "../core.h" 6 | #include "../hash.h" 7 | #include "../checker/compiler.h" 8 | #include "../checker/vm2.h" 9 | #include "./utils.h" 10 | 11 | using namespace tr; 12 | using namespace tr::vm2; 13 | 14 | TEST_CASE("closure1") { 15 | string code = R"( 16 | function test() { 17 | function b(): T { 18 | return {} as any; 19 | } 20 | 21 | return b; 22 | } 23 | 24 | const b: () => string = test(); 25 | const c: string = b(); 26 | const d: number = b(); 27 | )"; 28 | test(code, 1); 29 | //testBench(code, 1); 30 | } 31 | 32 | //todo: this breaks since we do not capture T and generate a FunctionRef since b is generic and needs to be instantiated. 33 | // FunctionRef needs to capture T and acts as closure. That's the solution right? 34 | TEST_CASE("closure2") { 35 | string code = R"( 36 | function test() { 37 | function b(v: K): K | T { 38 | return {} as any; 39 | } 40 | 41 | return b; 42 | } 43 | 44 | const b = test(); 45 | const c: string | number = b(3); 46 | const d: string = b('3'); 47 | )"; 48 | test(code, 1); 49 | //testBench(code, 1); 50 | } 51 | 52 | TEST_CASE("closure3") { 53 | string code = R"( 54 | function test() { 55 | function b(k: K): K & T { 56 | return {} as any; 57 | } 58 | 59 | return b; 60 | } 61 | 62 | const b = test(); 63 | )"; 64 | test(code, 0); 65 | //testBench(code, 1); 66 | } -------------------------------------------------------------------------------- /src/tests/test_vm2_function.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | #include 4 | 5 | #include "../core.h" 6 | #include "../hash.h" 7 | #include "../checker/compiler.h" 8 | #include "../checker/vm2.h" 9 | #include "./utils.h" 10 | 11 | using namespace tr; 12 | using namespace tr::vm2; 13 | 14 | TEST_CASE("function1") { 15 | string code = R"( 16 | function doIt(v: T) { 17 | } 18 | const a = doIt; 19 | a(23); 20 | )"; 21 | test(code, 0); 22 | testBench(code, 0); 23 | } 24 | 25 | TEST_CASE("function2") { 26 | string code = R"( 27 | function doIt(v: T) { 28 | } 29 | doIt(23); 30 | doIt<34>(33); 31 | )"; 32 | test(code, 1); 33 | testBench(code, 1); 34 | } 35 | 36 | TEST_CASE("function3") { 37 | ZoneScoped; 38 | string code = R"( 39 | function doIt() { 40 | return 1; 41 | } 42 | const var1: number = doIt(); 43 | const var2: string = doIt(); 44 | )"; 45 | test(code, 1); 46 | testBench(code, 1); 47 | } 48 | 49 | TEST_CASE("function4") { 50 | string code = R"( 51 | function doIt(v: number) { 52 | if (v == 1) return 'yes'; 53 | if (v == 2) return 'yes'; 54 | if (v == 3) return 'yes'; 55 | if (v == 4) return 'yes'; 56 | if (v == 5) return 'yes'; 57 | if (v == 6) return 'yes'; 58 | if (v == 7) return 'yes'; 59 | if (v == 8) return 'yes'; 60 | if (v == 9) return 'yes'; 61 | if (v == 10) return 'yes'; 62 | return 1; 63 | } 64 | const var1: number | string = doIt(0); 65 | const var2: number = doIt(0); 66 | )"; 67 | test(code, 1); 68 | testBench(code, 1); 69 | } 70 | 71 | TEST_CASE("function5") { 72 | string code = R"( 73 | function doIt(): string { 74 | return 1; 75 | } 76 | doIt(); 77 | )"; 78 | test(code, 1); 79 | testBench(code, 1); 80 | } 81 | 82 | //a function can have multiple definitions (overloading definitions where the last is ignored in finding a candidate). 83 | //we need to instantiate doIt with value argument 3, 84 | //basically converts to a tuple: [infer T extends number] so that rest parameters work out of the box. 85 | TEST_CASE("function6") { 86 | string code = R"( 87 | function doIt(v: T): T { 88 | } 89 | const a: 3 = doIt(3); 90 | const b: number = doIt(3); 91 | )"; 92 | test(code, 1); 93 | testBench(code, 1); 94 | } 95 | 96 | TEST_CASE("function7") { 97 | string code = R"( 98 | function doIt(v: T): T { 99 | } 100 | const a: number = doIt(3); 101 | const b: 3 = doIt(3); 102 | )"; 103 | test(code, 1); 104 | testBench(code, 1); 105 | } -------------------------------------------------------------------------------- /src/tests/test_vm2_union.cpp: -------------------------------------------------------------------------------- 1 | #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 2 | #include 3 | #include 4 | 5 | #include "../core.h" 6 | #include "../hash.h" 7 | #include "../checker/compiler.h" 8 | #include "../checker/vm2.h" 9 | #include "./utils.h" 10 | 11 | using namespace tr; 12 | using namespace tr::vm2; 13 | 14 | using std::string; 15 | using std::string_view; 16 | 17 | TEST_CASE("union1") { 18 | string code = R"( 19 | const v1: string | number = 'nope'; 20 | )"; 21 | testBench(code, 0); 22 | } 23 | 24 | TEST_CASE("bigUnion") { 25 | tr::checker::Program program; 26 | 27 | auto foos = 300; 28 | 29 | for (auto i = 0; iappend(to_string(i))); 32 | program.pushOp(OP::StringLiteral); 33 | program.pushStorage("a"); 34 | program.pushOp(OP::PropertySignature); 35 | program.pushOp(OP::ObjectLiteral); 36 | program.pushUint16(1); 37 | program.pushOp(OP::TupleMember); 38 | } 39 | program.pushOp(OP::Tuple); 40 | program.pushUint16(foos); 41 | 42 | for (auto i = 0; iappend(to_string(i))); 45 | } 46 | program.pushOp(OP::Union); 47 | program.pushUint16(foos); 48 | 49 | program.pushOp(OP::StringLiteral); 50 | program.pushStorage("a"); 51 | program.pushOp(OP::PropertySignature); 52 | program.pushOp(OP::ObjectLiteral); 53 | program.pushUint16(1); 54 | program.pushOp(OP::Array); 55 | 56 | program.pushOp(OP::Assign); 57 | program.pushOp(OP::Halt); 58 | 59 | auto module = std::make_shared(program.build(), "app.ts", ""); 60 | run(module); 61 | run(module); 62 | module->printErrors(); 63 | REQUIRE(module->errors.size() == 0); 64 | 65 | debug("pool.active = {}", tr::vm2::pool.active); 66 | debug("poolRef.active = {}", tr::vm2::poolRef.active); 67 | tr::vm2::clear(module); 68 | tr::vm2::gcStackAndFlush(); 69 | REQUIRE(tr::vm2::pool.active == 0); 70 | REQUIRE(tr::vm2::poolRef.active == 0); 71 | 72 | tr::bench("first", 1000, [&] { 73 | module->clear(); 74 | run(module); 75 | }); 76 | } 77 | -------------------------------------------------------------------------------- /src/tests/utils.h: -------------------------------------------------------------------------------- 1 | #include "../parser2.h" 2 | #include "../checker/compiler.h" 3 | #include "../checker/debug.h" 4 | #include "../checker/vm2.h" 5 | #include "Tracy.hpp" 6 | 7 | namespace tr { 8 | std::string compile(std::string code, bool print = true) { 9 | Parser parser; 10 | auto result = parser.parseSourceFile("app.ts", code, ScriptTarget::Latest, false, ScriptKind::TS, {}); 11 | checker::Compiler compiler; 12 | auto program = compiler.compileSourceFile(result); 13 | auto bin = program.build(); 14 | if (print) checker::printBin(bin); 15 | return bin; 16 | } 17 | 18 | shared_ptr test(string code, unsigned int expectedErrors = 0) { 19 | auto bin = compile(code); 20 | auto module = make_shared(bin, "app.ts", code); 21 | vm2::run(module); 22 | module->printErrors(); 23 | REQUIRE(expectedErrors == module->errors.size()); 24 | return module; 25 | } 26 | 27 | void testWarmBench(string code, int iterations = 1000) { 28 | auto bin = compile(code); 29 | auto module = make_shared(bin, "app.ts", code); 30 | auto warmTime = benchRun(iterations, [&module] { 31 | ZoneScoped; 32 | module->clear(); 33 | vm2::run(module); 34 | }); 35 | std::cout << fmt::format("{} iterations (it): warm {:.9f}ms/it", iterations, warmTime.count() / iterations); 36 | } 37 | 38 | void testBench(string code, unsigned int expectedErrors = 0, int iterations = 1000) { 39 | auto bin = compile(code); 40 | auto module = make_shared(bin, "app.ts", code); 41 | vm2::run(module); 42 | module->printErrors(); 43 | REQUIRE(expectedErrors == module->errors.size()); 44 | if (expectedErrors != module->errors.size()) return; 45 | 46 | auto warmTime = benchRun(iterations, [&module] { 47 | ZoneScoped; 48 | module->clear(); 49 | vm2::run(module); 50 | }); 51 | 52 | auto compileTime = benchRun(iterations, [&code] { 53 | compile(code, false); 54 | }); 55 | 56 | auto coldTime = benchRun(iterations, [&code] { 57 | auto module = make_shared(compile(code, false), "app.ts", code); 58 | vm2::run(module); 59 | }); 60 | 61 | std::cout << fmt::format("{} iterations (it): compile {:.9f}ms/it, cold {:.9f}ms/it, warm {:.9f}ms/it", iterations, compileTime.count() / iterations, coldTime.count() / iterations, warmTime.count() / iterations); 62 | } 63 | } -------------------------------------------------------------------------------- /src/types.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "types.h" 3 | 4 | namespace tr { 5 | // types::SyntaxKind BaseUnion::kind() { 6 | // return node->kind; 7 | // } 8 | // bool BaseUnion::empty() { 9 | // return node->kind == SyntaxKind::Unknown; 10 | // } 11 | // 12 | // BaseUnion::BaseUnion() { 13 | // node = make_shared(); 14 | // } 15 | types::DiagnosticMessage::DiagnosticMessage(int code, types::DiagnosticCategory category, const string_view &key, const string_view &message, bool reportsUnnecessary, bool reportsDeprecated, bool elidedInCompatabilityPyramid): code(code), category(category), key(key), message(message), reportsUnnecessary(reportsUnnecessary), reportsDeprecated(reportsDeprecated), elidedInCompatabilityPyramid(elidedInCompatabilityPyramid) {} 16 | 17 | bool VariableDeclaration::isConst() { 18 | if (auto a = to(getParent())) { 19 | return a->flags & (int) types::NodeFlags::Const; 20 | } 21 | return false; 22 | } 23 | 24 | void NodeArray::push(node item) { 25 | size++; 26 | if (last) { 27 | last->next = item; 28 | last = item; 29 | } else { 30 | head = item; 31 | last = item; 32 | } 33 | } 34 | 35 | NodeArrayIterator &NodeArrayIterator::operator++() { 36 | if (currentNode != nullptr) { 37 | this->currentNode = this->currentNode->next; 38 | } 39 | return *this; 40 | } 41 | } -------------------------------------------------------------------------------- /src/utf.cpp: -------------------------------------------------------------------------------- 1 | #include "utf.h" 2 | 3 | /** 4 | * Note that an arbitrary `charCodeAt(text, position+1)` does not work since the current code point might be longer than one byte. 5 | * We probably should introduction `int position, int offset` so that `charCodeAt(text, position, 1)` returns the correct unicode code point. 6 | */ 7 | tr::utf::CharCode tr::utf::charCodeAt(const std::string &text, int position, int *size) { 8 | //from - https://stackoverflow.com/a/40054802/979328 9 | int length = 1; 10 | int first = text[position]; 11 | if ((first & 0xf8) == 0xf0) length = 4; 12 | else if ((first & 0xf0) == 0xe0) length = 3; 13 | else if ((first & 0xe0) == 0xc0) length = 2; 14 | if ((unsigned long)(position + length) > text.length()) length = 1; 15 | 16 | if (size != nullptr) *size = length; 17 | 18 | //from http://www.zedwood.com/article/cpp-utf8-char-to-codepoint 19 | if (length < 1) return {-1, position}; 20 | unsigned char u0 = first; 21 | if (u0 >= 0 && u0 <= 127) return {u0, length}; 22 | if (length < 2) return {-1, position}; 23 | unsigned char u1 = text[position + 1]; 24 | if (u0 >= 192 && u0 <= 223) return {(u0 - 192) * 64 + (u1 - 128), length}; 25 | if (first == (const char) 0xed && (text[position + 1] & 0xa0) == 0xa0) 26 | return {-1, position}; //code points, 0xd800 to 0xdfff 27 | if (length < 3) return {-1, position}; 28 | unsigned char u2 = text[position + 2]; 29 | if (u0 >= 224 && u0 <= 239) return {(u0 - 224) * 4096 + (u1 - 128) * 64 + (u2 - 128), length}; 30 | if (length < 4) return {-1, position}; 31 | unsigned char u3 = text[position + 3]; 32 | if (u0 >= 240 && u0 <= 247) 33 | return {(u0 - 240) * 262144 + (u1 - 128) * 4096 + (u2 - 128) * 64 + (u3 - 128), length}; 34 | return {-1, position}; 35 | } 36 | 37 | std::string tr::utf::fromCharCode(int cp) { 38 | char c[5] = {0x00, 0x00, 0x00, 0x00, 0x00}; 39 | if (cp <= 0x7F) { c[0] = cp; } 40 | else if (cp <= 0x7FF) { 41 | c[0] = (cp >> 6) + 192; 42 | c[1] = (cp & 63) + 128; 43 | } else if (0xd800 <= cp && cp <= 0xdfff) {} //invalid block of utf8 44 | else if (cp <= 0xFFFF) { 45 | c[0] = (cp >> 12) + 224; 46 | c[1] = ((cp >> 6) & 63) + 128; 47 | c[2] = (cp & 63) + 128; 48 | } else if (cp <= 0x10FFFF) { 49 | c[0] = (cp >> 18) + 240; 50 | c[1] = ((cp >> 12) & 63) + 128; 51 | c[2] = ((cp >> 6) & 63) + 128; 52 | c[3] = (cp & 63) + 128; 53 | } 54 | return {c}; 55 | } -------------------------------------------------------------------------------- /src/utf.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | namespace tr::utf { 9 | enum CharacterCodes { 10 | nullCharacter = 0, 11 | maxAsciiCharacter = 0x7F, 12 | 13 | lineFeed = 0x0A, // \n 14 | carriageReturn = 0x0D, // \r 15 | lineSeparator = 0x2028, 16 | paragraphSeparator = 0x2029, 17 | nextLine = 0x0085, 18 | 19 | // Unicode 3.0 space characters 20 | space = 0x0020, // " " 21 | nonBreakingSpace = 0x00A0, // 22 | enQuad = 0x2000, 23 | emQuad = 0x2001, 24 | enSpace = 0x2002, 25 | emSpace = 0x2003, 26 | threePerEmSpace = 0x2004, 27 | fourPerEmSpace = 0x2005, 28 | sixPerEmSpace = 0x2006, 29 | figureSpace = 0x2007, 30 | punctuationSpace = 0x2008, 31 | thinSpace = 0x2009, 32 | hairSpace = 0x200A, 33 | zeroWidthSpace = 0x200B, 34 | narrowNoBreakSpace = 0x202F, 35 | ideographicSpace = 0x3000, 36 | mathematicalSpace = 0x205F, 37 | ogham = 0x1680, 38 | 39 | _ = 0x5F, 40 | $ = 0x24, 41 | 42 | _0 = 0x30, 43 | _1 = 0x31, 44 | _2 = 0x32, 45 | _3 = 0x33, 46 | _4 = 0x34, 47 | _5 = 0x35, 48 | _6 = 0x36, 49 | _7 = 0x37, 50 | _8 = 0x38, 51 | _9 = 0x39, 52 | 53 | a = 0x61, 54 | b = 0x62, 55 | c = 0x63, 56 | d = 0x64, 57 | e = 0x65, 58 | f = 0x66, 59 | g = 0x67, 60 | h = 0x68, 61 | i = 0x69, 62 | j = 0x6A, 63 | k = 0x6B, 64 | l = 0x6C, 65 | m = 0x6D, 66 | n = 0x6E, 67 | o = 0x6F, 68 | p = 0x70, 69 | q = 0x71, 70 | r = 0x72, 71 | s = 0x73, 72 | t = 0x74, 73 | u = 0x75, 74 | v = 0x76, 75 | w = 0x77, 76 | x = 0x78, 77 | y = 0x79, 78 | z = 0x7A, 79 | 80 | A = 0x41, 81 | B = 0x42, 82 | C = 0x43, 83 | D = 0x44, 84 | E = 0x45, 85 | F = 0x46, 86 | G = 0x47, 87 | H = 0x48, 88 | I = 0x49, 89 | J = 0x4A, 90 | K = 0x4B, 91 | L = 0x4C, 92 | M = 0x4D, 93 | N = 0x4E, 94 | O = 0x4F, 95 | P = 0x50, 96 | Q = 0x51, 97 | R = 0x52, 98 | S = 0x53, 99 | T = 0x54, 100 | U = 0x55, 101 | V = 0x56, 102 | W = 0x57, 103 | X = 0x58, 104 | Y = 0x59, 105 | Z = 0x5a, 106 | 107 | ampersand = 0x26, // & 108 | asterisk = 0x2A, // * 109 | at = 0x40, // @ 110 | backslash = 0x5C, // \ 111 | // backtick = 0x60, // ` 112 | backtick = 0x60, // ` 113 | bar = 0x7C, // | 114 | caret = 0x5E, // ^ 115 | closeBrace = 0x7D, // } 116 | closeBracket = 0x5D, // ] 117 | closeParen = 0x29, // ) 118 | colon = 0x3A, // : 119 | comma = 0x2C, // , 120 | dot = 0x2E, // . 121 | doubleQuote = 0x22, // " 122 | equals = 0x3D, // = 123 | exclamation = 0x21, // ! 124 | greaterThan = 0x3E, // > 125 | hash = 0x23, // # 126 | lessThan = 0x3C, // < 127 | minus = 0x2D, // - 128 | openBrace = 0x7B, // { 129 | openBracket = 0x5B, // [ 130 | openParen = 0x28, // ( 131 | percent = 0x25, // % 132 | plus = 0x2B, // + 133 | question = 0x3F, // ? 134 | semicolon = 0x3B, // ; 135 | singleQuote = 0x27, // ' 136 | slash = 0x2F, // / 137 | tilde = 0x7E, // ~ 138 | 139 | backspace = 0x08, // \b 140 | formFeed = 0x0C, // \f 141 | byteOrderMark = 0xFEFF, 142 | tab = 0x09, // \t 143 | verticalTab = 0x0B, // \v 144 | }; 145 | 146 | // std::wstring utf16From(std::string text); 147 | 148 | struct CharCode { 149 | int code; //unicode code point 150 | int length; 151 | }; 152 | 153 | // Updates size if non-nullptr is given 154 | CharCode charCodeAt(const std::string &text, int position, int *size = nullptr); 155 | 156 | std::string fromCharCode(int cp); 157 | 158 | inline bool isWhiteSpaceSingleLine(const CharCode &ch) { 159 | // Note: nextLine is in the Zs space, and should be considered to be a whitespace. 160 | // It is explicitly not a line-break as it isn't in the exact set specified by EcmaScript. 161 | return ch.code == CharacterCodes::space || 162 | ch.code == CharacterCodes::tab || 163 | ch.code == CharacterCodes::verticalTab || 164 | ch.code == CharacterCodes::formFeed || 165 | ch.code == CharacterCodes::nonBreakingSpace || 166 | ch.code == CharacterCodes::nextLine || 167 | ch.code == CharacterCodes::ogham || 168 | (ch.code >= CharacterCodes::enQuad && ch.code <= CharacterCodes::zeroWidthSpace) || 169 | ch.code == CharacterCodes::narrowNoBreakSpace || 170 | ch.code == CharacterCodes::mathematicalSpace || 171 | ch.code == CharacterCodes::ideographicSpace || 172 | ch.code == CharacterCodes::byteOrderMark; 173 | } 174 | 175 | inline bool isLineBreak(const CharCode &ch) { 176 | // ES5 7.3: 177 | // The ECMAScript line terminator characters are listed in Table 3. 178 | // Table 3: Line Terminator Characters 179 | // Code Unit Value Name Formal Name 180 | // \u000A Line Feed 181 | // \u000D Carriage Return 182 | // \u2028 Line separator 183 | // \u2029 Paragraph separator 184 | // Only the characters in Table 3 are treated as line terminators. Other new line or line 185 | // breaking characters are treated as white space but not as line terminators. 186 | 187 | return ch.code == CharacterCodes::lineFeed || 188 | ch.code == CharacterCodes::carriageReturn || 189 | ch.code == CharacterCodes::lineSeparator || 190 | ch.code == CharacterCodes::paragraphSeparator; 191 | } 192 | 193 | 194 | inline bool isWhiteSpaceLike(const CharCode &ch) { 195 | return isWhiteSpaceSingleLine(ch) || isLineBreak(ch); 196 | } 197 | 198 | inline unsigned int eatWhitespace(const std::string &text, unsigned int pos) { 199 | auto end = text.size(); 200 | while (pos < end) { 201 | auto charCode = charCodeAt(text, pos); 202 | if (!isWhiteSpaceLike(charCode)) break; 203 | pos += charCode.length; 204 | } 205 | return pos; 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /tests/basic1.ts: -------------------------------------------------------------------------------- 1 | const v1: string = "abc"; 2 | const v2: number = 123; -------------------------------------------------------------------------------- /tests/basicError1.ts: -------------------------------------------------------------------------------- 1 | const v1: string = "asd"; 2 | const v2: number = "23"; -------------------------------------------------------------------------------- /tests/bench.ts: -------------------------------------------------------------------------------- 1 | import {CompilerOptions, createCompilerHost, createProgram, getPreEmitDiagnostics} from "typescript"; 2 | import {readFileSync} from "fs"; 3 | import {execSync} from "child_process"; 4 | 5 | // const code = ` 6 | // function doIt(): string { 7 | // return 1; 8 | // } 9 | // doIt(); 10 | // `; 11 | const file = process.argv[2]; 12 | const code = readFileSync(file).toString('utf8'); 13 | 14 | console.log('file', file); 15 | console.log('code:'); 16 | console.log(code); 17 | console.log(); 18 | 19 | const options: CompilerOptions = { 20 | strict: true, 21 | skipLibCheck: true, 22 | types: [], 23 | typeRoots: [], 24 | lib: [], 25 | }; 26 | const host = createCompilerHost(options); 27 | host.readFile = (fileName) => { 28 | return code; 29 | } 30 | host.writeFile = () => { 31 | } 32 | 33 | const program = createProgram(['app.ts'], options, host); 34 | 35 | const iterations = 10; 36 | const start = Date.now(); 37 | for (let i = 0; i < iterations; i++) { 38 | const program = createProgram(['app.ts'], options, host); 39 | const diagnostics = getPreEmitDiagnostics(program); 40 | } 41 | const took = Date.now() - start; 42 | console.log('tsc: ', iterations, 'iterations took', took, 'ms.', took / iterations, 'ms/op'); 43 | 44 | execSync(__dirname + '/../cmake-build-release/bench ' + file, {stdio: 'inherit'}); 45 | -------------------------------------------------------------------------------- /tests/function1.ts: -------------------------------------------------------------------------------- 1 | function doIt(v: T) { 2 | } 3 | const a = doIt; 4 | a(23); -------------------------------------------------------------------------------- /tests/generic2.ts: -------------------------------------------------------------------------------- 1 | type StringToNum = `${A['length']}` extends T ? A['length'] : StringToNum; 2 | const var1: StringToNum<'999'> = 999; -------------------------------------------------------------------------------- /tests/objectLiterals1.ts: -------------------------------------------------------------------------------- 1 | type Person = {name: string, age: number} 2 | 3 | const a: Person = {name: 'Peter', age: 52}; 4 | const b: Person = {name: 'Peter', age: '52'}; 5 | -------------------------------------------------------------------------------- /tests/tutorial1.ts: -------------------------------------------------------------------------------- 1 | 2 | // Here you can see in real-time what branch the conditional type takes 3 | type isNumber = T extends number ? "yes" : "no"; 4 | const v2: isNumber = "yes"; 5 | 6 | // Here you can see that distributive conditional types 7 | // are executed for each union member 8 | type NoNumber = T extends number ? never : T; 9 | type Primitive = string | number | boolean; 10 | const v3: NoNumber = 34; 11 | 12 | --------------------------------------------------------------------------------